diff options
Diffstat (limited to 'drivers')
1943 files changed, 87758 insertions, 53574 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 694d5a70d6ce..c70d6e45dc10 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -134,8 +134,6 @@ source "drivers/staging/Kconfig" source "drivers/platform/Kconfig" -source "drivers/soc/Kconfig" - source "drivers/clk/Kconfig" source "drivers/hwspinlock/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 67d2334dc41e..527a6da8d539 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -50,7 +50,10 @@ obj-$(CONFIG_RESET_CONTROLLER) += reset/ obj-y += tty/ obj-y += char/ -# gpu/ comes after char for AGP vs DRM startup +# iommu/ comes before gpu as gpu are using iommu controllers +obj-$(CONFIG_IOMMU_SUPPORT) += iommu/ + +# gpu/ comes after char for AGP vs DRM startup and after iommu obj-y += gpu/ obj-$(CONFIG_CONNECTOR) += connector/ @@ -141,7 +144,6 @@ obj-y += clk/ obj-$(CONFIG_MAILBOX) += mailbox/ obj-$(CONFIG_HWSPINLOCK) += hwspinlock/ -obj-$(CONFIG_IOMMU_SUPPORT) += iommu/ obj-$(CONFIG_REMOTEPROC) += remoteproc/ obj-$(CONFIG_RPMSG) += rpmsg/ diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 8951cefb0a96..e6c3ddd92665 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -315,6 +315,12 @@ config ACPI_HOTPLUG_MEMORY To compile this driver as a module, choose M here: the module will be called acpi_memhotplug. +config ACPI_HOTPLUG_IOAPIC + bool + depends on PCI + depends on X86_IO_APIC + default y + config ACPI_SBS tristate "Smart Battery System" depends on X86 diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index f74317cc1ca9..b18cd2151ddb 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -40,7 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o acpi-y += pci_root.o pci_link.o pci_irq.o -acpi-y += acpi_lpss.o +acpi-y += acpi_lpss.o acpi_apd.o acpi-y += acpi_platform.o acpi-y += acpi_pnp.o acpi-y += int340x_thermal.o @@ -70,6 +70,7 @@ obj-$(CONFIG_ACPI_PROCESSOR) += processor.o obj-y += container.o obj-$(CONFIG_ACPI_THERMAL) += thermal.o obj-y += acpi_memhotplug.o +obj-$(CONFIG_ACPI_HOTPLUG_IOAPIC) += ioapic.o obj-$(CONFIG_ACPI_BATTERY) += battery.o obj-$(CONFIG_ACPI_SBS) += sbshc.o obj-$(CONFIG_ACPI_SBS) += sbs.o diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c new file mode 100644 index 000000000000..3984ea96e5f7 --- /dev/null +++ b/drivers/acpi/acpi_apd.c @@ -0,0 +1,150 @@ +/* + * AMD ACPI support for ACPI2platform device. + * + * Copyright (c) 2014,2015 AMD Corporation. + * Authors: Ken Xue <Ken.Xue@amd.com> + * Wu, Jeff <Jeff.Wu@amd.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/clk-provider.h> +#include <linux/platform_device.h> +#include <linux/pm_domain.h> +#include <linux/clkdev.h> +#include <linux/acpi.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/pm.h> + +#include "internal.h" + +ACPI_MODULE_NAME("acpi_apd"); +struct apd_private_data; + +/** + * ACPI_APD_SYSFS : add device attributes in sysfs + * ACPI_APD_PM : attach power domain to device + */ +#define ACPI_APD_SYSFS BIT(0) +#define ACPI_APD_PM BIT(1) + +/** + * struct apd_device_desc - a descriptor for apd device + * @flags: device flags like %ACPI_APD_SYSFS, %ACPI_APD_PM + * @fixed_clk_rate: fixed rate input clock source for acpi device; + * 0 means no fixed rate input clock source + * @setup: a hook routine to set device resource during create platform device + * + * Device description defined as acpi_device_id.driver_data + */ +struct apd_device_desc { + unsigned int flags; + unsigned int fixed_clk_rate; + int (*setup)(struct apd_private_data *pdata); +}; + +struct apd_private_data { + struct clk *clk; + struct acpi_device *adev; + const struct apd_device_desc *dev_desc; +}; + +#ifdef CONFIG_X86_AMD_PLATFORM_DEVICE +#define APD_ADDR(desc) ((unsigned long)&desc) + +static int acpi_apd_setup(struct apd_private_data *pdata) +{ + const struct apd_device_desc *dev_desc = pdata->dev_desc; + struct clk *clk = ERR_PTR(-ENODEV); + + if (dev_desc->fixed_clk_rate) { + clk = clk_register_fixed_rate(&pdata->adev->dev, + dev_name(&pdata->adev->dev), + NULL, CLK_IS_ROOT, + dev_desc->fixed_clk_rate); + clk_register_clkdev(clk, NULL, dev_name(&pdata->adev->dev)); + pdata->clk = clk; + } + + return 0; +} + +static struct apd_device_desc cz_i2c_desc = { + .setup = acpi_apd_setup, + .fixed_clk_rate = 133000000, +}; + +static struct apd_device_desc cz_uart_desc = { + .setup = acpi_apd_setup, + .fixed_clk_rate = 48000000, +}; + +#else + +#define APD_ADDR(desc) (0UL) + +#endif /* CONFIG_X86_AMD_PLATFORM_DEVICE */ + +/** +* Create platform device during acpi scan attach handle. +* Return value > 0 on success of creating device. +*/ +static int acpi_apd_create_device(struct acpi_device *adev, + const struct acpi_device_id *id) +{ + const struct apd_device_desc *dev_desc = (void *)id->driver_data; + struct apd_private_data *pdata; + struct platform_device *pdev; + int ret; + + if (!dev_desc) { + pdev = acpi_create_platform_device(adev); + return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1; + } + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + pdata->adev = adev; + pdata->dev_desc = dev_desc; + + if (dev_desc->setup) { + ret = dev_desc->setup(pdata); + if (ret) + goto err_out; + } + + adev->driver_data = pdata; + pdev = acpi_create_platform_device(adev); + if (!IS_ERR_OR_NULL(pdev)) + return 1; + + ret = PTR_ERR(pdev); + adev->driver_data = NULL; + + err_out: + kfree(pdata); + return ret; +} + +static const struct acpi_device_id acpi_apd_device_ids[] = { + /* Generic apd devices */ + { "AMD0010", APD_ADDR(cz_i2c_desc) }, + { "AMD0020", APD_ADDR(cz_uart_desc) }, + { "AMD0030", }, + { } +}; + +static struct acpi_scan_handler apd_handler = { + .ids = acpi_apd_device_ids, + .attach = acpi_apd_create_device, +}; + +void __init acpi_apd_init(void) +{ + acpi_scan_add_handler(&apd_handler); +} diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 4f3febf8a589..02e835f3cf8a 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -1,7 +1,7 @@ /* * ACPI support for Intel Lynxpoint LPSS. * - * Copyright (C) 2013, 2014, Intel Corporation + * Copyright (C) 2013, Intel Corporation * Authors: Mika Westerberg <mika.westerberg@linux.intel.com> * Rafael J. Wysocki <rafael.j.wysocki@intel.com> * @@ -60,8 +60,6 @@ ACPI_MODULE_NAME("acpi_lpss"); #define LPSS_CLK_DIVIDER BIT(2) #define LPSS_LTR BIT(3) #define LPSS_SAVE_CTX BIT(4) -#define LPSS_DEV_PROXY BIT(5) -#define LPSS_PROXY_REQ BIT(6) struct lpss_private_data; @@ -72,10 +70,8 @@ struct lpss_device_desc { void (*setup)(struct lpss_private_data *pdata); }; -static struct device *proxy_device; - static struct lpss_device_desc lpss_dma_desc = { - .flags = LPSS_CLK | LPSS_PROXY_REQ, + .flags = LPSS_CLK, }; struct lpss_private_data { @@ -129,7 +125,7 @@ static struct lpss_device_desc lpt_dev_desc = { }; static struct lpss_device_desc lpt_i2c_dev_desc = { - .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_LTR, + .flags = LPSS_CLK | LPSS_LTR, .prv_offset = 0x800, }; @@ -150,24 +146,22 @@ static struct lpss_device_desc byt_pwm_dev_desc = { }; static struct lpss_device_desc byt_uart_dev_desc = { - .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX | - LPSS_DEV_PROXY, + .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX, .prv_offset = 0x800, .setup = lpss_uart_setup, }; static struct lpss_device_desc byt_spi_dev_desc = { - .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX | - LPSS_DEV_PROXY, + .flags = LPSS_CLK | LPSS_CLK_GATE | LPSS_CLK_DIVIDER | LPSS_SAVE_CTX, .prv_offset = 0x400, }; static struct lpss_device_desc byt_sdio_dev_desc = { - .flags = LPSS_CLK | LPSS_DEV_PROXY, + .flags = LPSS_CLK, }; static struct lpss_device_desc byt_i2c_dev_desc = { - .flags = LPSS_CLK | LPSS_SAVE_CTX | LPSS_DEV_PROXY, + .flags = LPSS_CLK | LPSS_SAVE_CTX, .prv_offset = 0x800, .setup = byt_i2c_setup, }; @@ -313,7 +307,7 @@ static int acpi_lpss_create_device(struct acpi_device *adev, { struct lpss_device_desc *dev_desc; struct lpss_private_data *pdata; - struct resource_list_entry *rentry; + struct resource_entry *rentry; struct list_head resource_list; struct platform_device *pdev; int ret; @@ -333,13 +327,15 @@ static int acpi_lpss_create_device(struct acpi_device *adev, goto err_out; list_for_each_entry(rentry, &resource_list, node) - if (resource_type(&rentry->res) == IORESOURCE_MEM) { + if (resource_type(rentry->res) == IORESOURCE_MEM) { if (dev_desc->prv_size_override) pdata->mmio_size = dev_desc->prv_size_override; else - pdata->mmio_size = resource_size(&rentry->res); - pdata->mmio_base = ioremap(rentry->res.start, + pdata->mmio_size = resource_size(rentry->res); + pdata->mmio_base = ioremap(rentry->res->start, pdata->mmio_size); + if (!pdata->mmio_base) + goto err_out; break; } @@ -374,8 +370,6 @@ static int acpi_lpss_create_device(struct acpi_device *adev, adev->driver_data = pdata; pdev = acpi_create_platform_device(adev); if (!IS_ERR_OR_NULL(pdev)) { - if (!proxy_device && dev_desc->flags & LPSS_DEV_PROXY) - proxy_device = &pdev->dev; return 1; } @@ -600,14 +594,7 @@ static int acpi_lpss_runtime_suspend(struct device *dev) if (pdata->dev_desc->flags & LPSS_SAVE_CTX) acpi_lpss_save_ctx(dev, pdata); - ret = acpi_dev_runtime_suspend(dev); - if (ret) - return ret; - - if (pdata->dev_desc->flags & LPSS_PROXY_REQ && proxy_device) - return pm_runtime_put_sync_suspend(proxy_device); - - return 0; + return acpi_dev_runtime_suspend(dev); } static int acpi_lpss_runtime_resume(struct device *dev) @@ -615,12 +602,6 @@ static int acpi_lpss_runtime_resume(struct device *dev) struct lpss_private_data *pdata = acpi_driver_data(ACPI_COMPANION(dev)); int ret; - if (pdata->dev_desc->flags & LPSS_PROXY_REQ && proxy_device) { - ret = pm_runtime_get_sync(proxy_device); - if (ret) - return ret; - } - ret = acpi_dev_runtime_resume(dev); if (ret) return ret; diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index 23e2319ead41..ee28f4d15625 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -101,8 +101,8 @@ acpi_memory_get_resource(struct acpi_resource *resource, void *context) /* Can we combine the resource range information? */ if ((info->caching == address64.info.mem.caching) && (info->write_protect == address64.info.mem.write_protect) && - (info->start_addr + info->length == address64.minimum)) { - info->length += address64.address_length; + (info->start_addr + info->length == address64.address.minimum)) { + info->length += address64.address.address_length; return AE_OK; } } @@ -114,8 +114,8 @@ acpi_memory_get_resource(struct acpi_resource *resource, void *context) INIT_LIST_HEAD(&new->list); new->caching = address64.info.mem.caching; new->write_protect = address64.info.mem.write_protect; - new->start_addr = address64.minimum; - new->length = address64.address_length; + new->start_addr = address64.address.minimum; + new->length = address64.address.address_length; list_add_tail(&new->list, &mem_device->res_list); return AE_OK; diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 6ba8beb6b9d2..1284138e42ab 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -45,7 +45,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) struct platform_device *pdev = NULL; struct acpi_device *acpi_parent; struct platform_device_info pdevinfo; - struct resource_list_entry *rentry; + struct resource_entry *rentry; struct list_head resource_list; struct resource *resources = NULL; int count; @@ -71,7 +71,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev) } count = 0; list_for_each_entry(rentry, &resource_list, node) - resources[count++] = rentry->res; + resources[count++] = *rentry->res; acpi_dev_free_resource_list(&resource_list); } diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index 1fdf5e07a1c7..1020b1b53a17 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -170,7 +170,7 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr) acpi_status status; int ret; - if (pr->apic_id == -1) + if (pr->phys_id == -1) return -ENODEV; status = acpi_evaluate_integer(pr->handle, "_STA", NULL, &sta); @@ -180,13 +180,13 @@ static int acpi_processor_hotadd_init(struct acpi_processor *pr) cpu_maps_update_begin(); cpu_hotplug_begin(); - ret = acpi_map_lsapic(pr->handle, pr->apic_id, &pr->id); + ret = acpi_map_cpu(pr->handle, pr->phys_id, &pr->id); if (ret) goto out; ret = arch_register_cpu(pr->id); if (ret) { - acpi_unmap_lsapic(pr->id); + acpi_unmap_cpu(pr->id); goto out; } @@ -215,7 +215,7 @@ static int acpi_processor_get_info(struct acpi_device *device) union acpi_object object = { 0 }; struct acpi_buffer buffer = { sizeof(union acpi_object), &object }; struct acpi_processor *pr = acpi_driver_data(device); - int apic_id, cpu_index, device_declaration = 0; + int phys_id, cpu_index, device_declaration = 0; acpi_status status = AE_OK; static int cpu0_initialized; unsigned long long value; @@ -262,15 +262,18 @@ static int acpi_processor_get_info(struct acpi_device *device) pr->acpi_id = value; } - apic_id = acpi_get_apicid(pr->handle, device_declaration, pr->acpi_id); - if (apic_id < 0) - acpi_handle_debug(pr->handle, "failed to get CPU APIC ID.\n"); - pr->apic_id = apic_id; + phys_id = acpi_get_phys_id(pr->handle, device_declaration, pr->acpi_id); + if (phys_id < 0) + acpi_handle_debug(pr->handle, "failed to get CPU physical ID.\n"); + pr->phys_id = phys_id; - cpu_index = acpi_map_cpuid(pr->apic_id, pr->acpi_id); + cpu_index = acpi_map_cpuid(pr->phys_id, pr->acpi_id); if (!cpu0_initialized && !acpi_has_cpu_in_madt()) { cpu0_initialized = 1; - /* Handle UP system running SMP kernel, with no LAPIC in MADT */ + /* + * Handle UP system running SMP kernel, with no CPU + * entry in MADT + */ if ((cpu_index == -1) && (num_online_cpus() == 1)) cpu_index = 0; } @@ -458,7 +461,7 @@ static void acpi_processor_remove(struct acpi_device *device) /* Remove the CPU. */ arch_unregister_cpu(pr->id); - acpi_unmap_lsapic(pr->id); + acpi_unmap_cpu(pr->id); cpu_hotplug_done(); cpu_maps_update_done(); diff --git a/drivers/acpi/acpica/acapps.h b/drivers/acpi/acpica/acapps.h index 3d2c88289da9..d863016565b5 100644 --- a/drivers/acpi/acpica/acapps.h +++ b/drivers/acpi/acpica/acapps.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,7 +47,7 @@ /* Common info for tool signons */ #define ACPICA_NAME "Intel ACPI Component Architecture" -#define ACPICA_COPYRIGHT "Copyright (c) 2000 - 2014 Intel Corporation" +#define ACPICA_COPYRIGHT "Copyright (c) 2000 - 2015 Intel Corporation" #if ACPI_MACHINE_WIDTH == 64 #define ACPI_WIDTH "-64" diff --git a/drivers/acpi/acpica/accommon.h b/drivers/acpi/acpica/accommon.h index 6f1c616910ac..853aa2dbdb61 100644 --- a/drivers/acpi/acpica/accommon.h +++ b/drivers/acpi/acpica/accommon.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acdebug.h b/drivers/acpi/acpica/acdebug.h index 1d026ff1683f..4169bb87a996 100644 --- a/drivers/acpi/acpica/acdebug.h +++ b/drivers/acpi/acpica/acdebug.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acdispat.h b/drivers/acpi/acpica/acdispat.h index d3e2cc395d7f..408f04bcaab4 100644 --- a/drivers/acpi/acpica/acdispat.h +++ b/drivers/acpi/acpica/acdispat.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h index 7a7811a9fc26..228704b78657 100644 --- a/drivers/acpi/acpica/acevents.h +++ b/drivers/acpi/acpica/acevents.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -143,8 +143,6 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle, acpi_status acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback, void *context); -u8 acpi_ev_valid_gpe_event(struct acpi_gpe_event_info *gpe_event_info); - acpi_status acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info, struct acpi_gpe_block_info *gpe_block, void *context); diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 7f60582d0c8c..a165d25343e8 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h index c318d3e27893..196a55244559 100644 --- a/drivers/acpi/acpica/achware.h +++ b/drivers/acpi/acpica/achware.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acinterp.h b/drivers/acpi/acpica/acinterp.h index b01f71ce0523..1886bde54b5d 100644 --- a/drivers/acpi/acpica/acinterp.h +++ b/drivers/acpi/acpica/acinterp.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 680d23bbae7c..7add32e5d8c5 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acmacros.h b/drivers/acpi/acpica/acmacros.h index 4bceb11c7380..cf607fe69dbd 100644 --- a/drivers/acpi/acpica/acmacros.h +++ b/drivers/acpi/acpica/acmacros.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acnamesp.h b/drivers/acpi/acpica/acnamesp.h index ee1c040f321c..952fbe0b7231 100644 --- a/drivers/acpi/acpica/acnamesp.h +++ b/drivers/acpi/acpica/acnamesp.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h index 8abb393dafab..3e9720e1f34f 100644 --- a/drivers/acpi/acpica/acobject.h +++ b/drivers/acpi/acpica/acobject.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acopcode.h b/drivers/acpi/acpica/acopcode.h index dda0e6affcf1..a5f17de45ac6 100644 --- a/drivers/acpi/acpica/acopcode.h +++ b/drivers/acpi/acpica/acopcode.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acparser.h b/drivers/acpi/acpica/acparser.h index 6168b85463ed..74a390c6db16 100644 --- a/drivers/acpi/acpica/acparser.h +++ b/drivers/acpi/acpica/acparser.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acpredef.h b/drivers/acpi/acpica/acpredef.h index bd3908d26c4f..a972d11c97c9 100644 --- a/drivers/acpi/acpica/acpredef.h +++ b/drivers/acpi/acpica/acpredef.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acresrc.h b/drivers/acpi/acpica/acresrc.h index 4b008e8884a1..efc4c7124ccc 100644 --- a/drivers/acpi/acpica/acresrc.h +++ b/drivers/acpi/acpica/acresrc.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acstruct.h b/drivers/acpi/acpica/acstruct.h index cf7346110bd8..d14b547b7cd5 100644 --- a/drivers/acpi/acpica/acstruct.h +++ b/drivers/acpi/acpica/acstruct.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/actables.h b/drivers/acpi/acpica/actables.h index 1afe46e44dac..1c127a43017b 100644 --- a/drivers/acpi/acpica/actables.h +++ b/drivers/acpi/acpica/actables.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/acutils.h b/drivers/acpi/acpica/acutils.h index 486d342e74b6..c2f03e8774ad 100644 --- a/drivers/acpi/acpica/acutils.h +++ b/drivers/acpi/acpica/acutils.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/amlcode.h b/drivers/acpi/acpica/amlcode.h index 5908ccec6aea..3a95068fc119 100644 --- a/drivers/acpi/acpica/amlcode.h +++ b/drivers/acpi/acpica/amlcode.h @@ -7,7 +7,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/amlresrc.h b/drivers/acpi/acpica/amlresrc.h index 3a0beeb86ba5..ee0cdd60b93d 100644 --- a/drivers/acpi/acpica/amlresrc.h +++ b/drivers/acpi/acpica/amlresrc.h @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsargs.c b/drivers/acpi/acpica/dsargs.c index 720b1cdda711..3e6989738e85 100644 --- a/drivers/acpi/acpica/dsargs.c +++ b/drivers/acpi/acpica/dsargs.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dscontrol.c b/drivers/acpi/acpica/dscontrol.c index 8daf9de82b73..39da9da62bbf 100644 --- a/drivers/acpi/acpica/dscontrol.c +++ b/drivers/acpi/acpica/dscontrol.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsfield.c b/drivers/acpi/acpica/dsfield.c index c57666196672..43b40de90484 100644 --- a/drivers/acpi/acpica/dsfield.c +++ b/drivers/acpi/acpica/dsfield.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsinit.c b/drivers/acpi/acpica/dsinit.c index aee5e45f6d35..bbe74bcebbae 100644 --- a/drivers/acpi/acpica/dsinit.c +++ b/drivers/acpi/acpica/dsinit.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsmethod.c b/drivers/acpi/acpica/dsmethod.c index 3c7f7378b94d..d72565a3c646 100644 --- a/drivers/acpi/acpica/dsmethod.c +++ b/drivers/acpi/acpica/dsmethod.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsmthdat.c b/drivers/acpi/acpica/dsmthdat.c index b67522df01ac..2e4c42b377ec 100644 --- a/drivers/acpi/acpica/dsmthdat.c +++ b/drivers/acpi/acpica/dsmthdat.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsobject.c b/drivers/acpi/acpica/dsobject.c index a1e7e6b6fcf7..8a7b07b6adc8 100644 --- a/drivers/acpi/acpica/dsobject.c +++ b/drivers/acpi/acpica/dsobject.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c index 6c0759c0db47..77244182ff02 100644 --- a/drivers/acpi/acpica/dsopcode.c +++ b/drivers/acpi/acpica/dsopcode.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dsutils.c b/drivers/acpi/acpica/dsutils.c index 9f74795e2268..e5ff89bcb3f5 100644 --- a/drivers/acpi/acpica/dsutils.c +++ b/drivers/acpi/acpica/dsutils.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dswexec.c b/drivers/acpi/acpica/dswexec.c index f7f5107e754d..df54d46225cd 100644 --- a/drivers/acpi/acpica/dswexec.c +++ b/drivers/acpi/acpica/dswexec.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dswload.c b/drivers/acpi/acpica/dswload.c index 15623da26200..843942fb4be5 100644 --- a/drivers/acpi/acpica/dswload.c +++ b/drivers/acpi/acpica/dswload.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dswload2.c b/drivers/acpi/acpica/dswload2.c index 2ac28d297305..fcaa30c611fb 100644 --- a/drivers/acpi/acpica/dswload2.c +++ b/drivers/acpi/acpica/dswload2.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dswscope.c b/drivers/acpi/acpica/dswscope.c index 9d6e2c1de1f8..43b3ea40c0b6 100644 --- a/drivers/acpi/acpica/dswscope.c +++ b/drivers/acpi/acpica/dswscope.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/dswstate.c b/drivers/acpi/acpica/dswstate.c index 24f7d5ea678a..89ac2022465e 100644 --- a/drivers/acpi/acpica/dswstate.c +++ b/drivers/acpi/acpica/dswstate.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evevent.c b/drivers/acpi/acpica/evevent.c index c7bffff9ed32..bf6873f95e72 100644 --- a/drivers/acpi/acpica/evevent.c +++ b/drivers/acpi/acpica/evevent.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evglock.c b/drivers/acpi/acpica/evglock.c index 3393a73ca0d6..b78dc7c6d5d7 100644 --- a/drivers/acpi/acpica/evglock.c +++ b/drivers/acpi/acpica/evglock.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index aa70154cf4fa..5ed064e8673c 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -114,17 +114,6 @@ acpi_status acpi_ev_enable_gpe(struct acpi_gpe_event_info *gpe_event_info) ACPI_FUNCTION_TRACE(ev_enable_gpe); - /* - * We will only allow a GPE to be enabled if it has either an associated - * method (_Lxx/_Exx) or a handler, or is using the implicit notify - * feature. Otherwise, the GPE will be immediately disabled by - * acpi_ev_gpe_dispatch the first time it fires. - */ - if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == - ACPI_GPE_DISPATCH_NONE) { - return_ACPI_STATUS(AE_NO_HANDLER); - } - /* Clear the GPE (of stale events) */ status = acpi_hw_clear_gpe(gpe_event_info); @@ -339,7 +328,11 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info *gpe_xrupt_list) { acpi_status status; struct acpi_gpe_block_info *gpe_block; + struct acpi_namespace_node *gpe_device; struct acpi_gpe_register_info *gpe_register_info; + struct acpi_gpe_event_info *gpe_event_info; + u32 gpe_number; + struct acpi_gpe_handler_info *gpe_handler_info; u32 int_status = ACPI_INTERRUPT_NOT_HANDLED; u8 enabled_status_byte; u32 status_reg; @@ -367,6 +360,8 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info *gpe_xrupt_list) gpe_block = gpe_xrupt_list->gpe_block_list_head; while (gpe_block) { + gpe_device = gpe_block->node; + /* * Read all of the 8-bit GPE status and enable registers in this GPE * block, saving all of them. Find all currently active GP events. @@ -442,16 +437,68 @@ u32 acpi_ev_gpe_detect(struct acpi_gpe_xrupt_info *gpe_xrupt_list) /* Examine one GPE bit */ + gpe_event_info = + &gpe_block-> + event_info[((acpi_size) i * + ACPI_GPE_REGISTER_WIDTH) + j]; + gpe_number = + j + gpe_register_info->base_gpe_number; + if (enabled_status_byte & (1 << j)) { - /* - * Found an active GPE. Dispatch the event to a handler - * or method. - */ - int_status |= - acpi_ev_gpe_dispatch(gpe_block-> - node, - &gpe_block-> - event_info[((acpi_size) i * ACPI_GPE_REGISTER_WIDTH) + j], j + gpe_register_info->base_gpe_number); + + /* Invoke global event handler if present */ + + acpi_gpe_count++; + if (acpi_gbl_global_event_handler) { + acpi_gbl_global_event_handler + (ACPI_EVENT_TYPE_GPE, + gpe_device, gpe_number, + acpi_gbl_global_event_handler_context); + } + + /* Found an active GPE */ + + if (ACPI_GPE_DISPATCH_TYPE + (gpe_event_info->flags) == + ACPI_GPE_DISPATCH_RAW_HANDLER) { + + /* Dispatch the event to a raw handler */ + + gpe_handler_info = + gpe_event_info->dispatch. + handler; + + /* + * There is no protection around the namespace node + * and the GPE handler to ensure a safe destruction + * because: + * 1. The namespace node is expected to always + * exist after loading a table. + * 2. The GPE handler is expected to be flushed by + * acpi_os_wait_events_complete() before the + * destruction. + */ + acpi_os_release_lock + (acpi_gbl_gpe_lock, flags); + int_status |= + gpe_handler_info-> + address(gpe_device, + gpe_number, + gpe_handler_info-> + context); + flags = + acpi_os_acquire_lock + (acpi_gbl_gpe_lock); + } else { + /* + * Dispatch the event to a standard handler or + * method. + */ + int_status |= + acpi_ev_gpe_dispatch + (gpe_device, gpe_event_info, + gpe_number); + } } } } @@ -484,52 +531,15 @@ unlock_and_exit: static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) { struct acpi_gpe_event_info *gpe_event_info = context; - acpi_status status; - struct acpi_gpe_event_info *local_gpe_event_info; + acpi_status status = AE_OK; struct acpi_evaluate_info *info; struct acpi_gpe_notify_info *notify; ACPI_FUNCTION_TRACE(ev_asynch_execute_gpe_method); - /* Allocate a local GPE block */ - - local_gpe_event_info = - ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_gpe_event_info)); - if (!local_gpe_event_info) { - ACPI_EXCEPTION((AE_INFO, AE_NO_MEMORY, "while handling a GPE")); - return_VOID; - } - - status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); - if (ACPI_FAILURE(status)) { - ACPI_FREE(local_gpe_event_info); - return_VOID; - } - - /* Must revalidate the gpe_number/gpe_block */ - - if (!acpi_ev_valid_gpe_event(gpe_event_info)) { - status = acpi_ut_release_mutex(ACPI_MTX_EVENTS); - ACPI_FREE(local_gpe_event_info); - return_VOID; - } - - /* - * Take a snapshot of the GPE info for this level - we copy the info to - * prevent a race condition with remove_handler/remove_block. - */ - ACPI_MEMCPY(local_gpe_event_info, gpe_event_info, - sizeof(struct acpi_gpe_event_info)); - - status = acpi_ut_release_mutex(ACPI_MTX_EVENTS); - if (ACPI_FAILURE(status)) { - ACPI_FREE(local_gpe_event_info); - return_VOID; - } - /* Do the correct dispatch - normal method or implicit notify */ - switch (local_gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) { + switch (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags)) { case ACPI_GPE_DISPATCH_NOTIFY: /* * Implicit notify. @@ -542,7 +552,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) * June 2012: Expand implicit notify mechanism to support * notifies on multiple device objects. */ - notify = local_gpe_event_info->dispatch.notify_list; + notify = gpe_event_info->dispatch.notify_list; while (ACPI_SUCCESS(status) && notify) { status = acpi_ev_queue_notify_request(notify->device_node, @@ -566,7 +576,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) * _Lxx/_Exx control method that corresponds to this GPE */ info->prefix_node = - local_gpe_event_info->dispatch.method_node; + gpe_event_info->dispatch.method_node; info->flags = ACPI_IGNORE_RETURN_VALUE; status = acpi_ns_evaluate(info); @@ -576,25 +586,27 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) if (ACPI_FAILURE(status)) { ACPI_EXCEPTION((AE_INFO, status, "while evaluating GPE method [%4.4s]", - acpi_ut_get_node_name - (local_gpe_event_info->dispatch. - method_node))); + acpi_ut_get_node_name(gpe_event_info-> + dispatch. + method_node))); } break; default: - return_VOID; /* Should never happen */ + goto error_exit; /* Should never happen */ } /* Defer enabling of GPE until all notify handlers are done */ status = acpi_os_execute(OSL_NOTIFY_HANDLER, - acpi_ev_asynch_enable_gpe, - local_gpe_event_info); - if (ACPI_FAILURE(status)) { - ACPI_FREE(local_gpe_event_info); + acpi_ev_asynch_enable_gpe, gpe_event_info); + if (ACPI_SUCCESS(status)) { + return_VOID; } + +error_exit: + acpi_ev_asynch_enable_gpe(gpe_event_info); return_VOID; } @@ -622,7 +634,6 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_enable_gpe(void *context) (void)acpi_ev_finish_gpe(gpe_event_info); acpi_os_release_lock(acpi_gbl_gpe_lock, flags); - ACPI_FREE(gpe_event_info); return; } @@ -692,15 +703,6 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, ACPI_FUNCTION_TRACE(ev_gpe_dispatch); - /* Invoke global event handler if present */ - - acpi_gpe_count++; - if (acpi_gbl_global_event_handler) { - acpi_gbl_global_event_handler(ACPI_EVENT_TYPE_GPE, gpe_device, - gpe_number, - acpi_gbl_global_event_handler_context); - } - /* * Always disable the GPE so that it does not keep firing before * any asynchronous activity completes (either from the execution @@ -741,7 +743,7 @@ acpi_ev_gpe_dispatch(struct acpi_namespace_node *gpe_device, * If there is neither a handler nor a method, leave the GPE * disabled. */ - switch (gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) { + switch (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags)) { case ACPI_GPE_DISPATCH_HANDLER: /* Invoke the installed handler (at interrupt level) */ diff --git a/drivers/acpi/acpica/evgpeblk.c b/drivers/acpi/acpica/evgpeblk.c index d86699eea33c..e0f24c504513 100644 --- a/drivers/acpi/acpica/evgpeblk.c +++ b/drivers/acpi/acpica/evgpeblk.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -474,10 +474,12 @@ acpi_ev_initialize_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, * Ignore GPEs that have no corresponding _Lxx/_Exx method * and GPEs that are used to wake the system */ - if (((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == + if ((ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == ACPI_GPE_DISPATCH_NONE) - || ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) - == ACPI_GPE_DISPATCH_HANDLER) + || (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == + ACPI_GPE_DISPATCH_HANDLER) + || (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == + ACPI_GPE_DISPATCH_RAW_HANDLER) || (gpe_event_info->flags & ACPI_GPE_CAN_WAKE)) { continue; } diff --git a/drivers/acpi/acpica/evgpeinit.c b/drivers/acpi/acpica/evgpeinit.c index 7be928379879..8840296d5b20 100644 --- a/drivers/acpi/acpica/evgpeinit.c +++ b/drivers/acpi/acpica/evgpeinit.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -401,15 +401,17 @@ acpi_ev_match_gpe_method(acpi_handle obj_handle, return_ACPI_STATUS(AE_OK); } - if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == - ACPI_GPE_DISPATCH_HANDLER) { + if ((ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == + ACPI_GPE_DISPATCH_HANDLER) || + (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == + ACPI_GPE_DISPATCH_RAW_HANDLER)) { /* If there is already a handler, ignore this GPE method */ return_ACPI_STATUS(AE_OK); } - if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == + if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == ACPI_GPE_DISPATCH_METHOD) { /* * If there is already a method, ignore this method. But check diff --git a/drivers/acpi/acpica/evgpeutil.c b/drivers/acpi/acpica/evgpeutil.c index 17e4bbfdb096..3a958f3612fe 100644 --- a/drivers/acpi/acpica/evgpeutil.c +++ b/drivers/acpi/acpica/evgpeutil.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -108,53 +108,6 @@ unlock_and_exit: /******************************************************************************* * - * FUNCTION: acpi_ev_valid_gpe_event - * - * PARAMETERS: gpe_event_info - Info for this GPE - * - * RETURN: TRUE if the gpe_event is valid - * - * DESCRIPTION: Validate a GPE event. DO NOT CALL FROM INTERRUPT LEVEL. - * Should be called only when the GPE lists are semaphore locked - * and not subject to change. - * - ******************************************************************************/ - -u8 acpi_ev_valid_gpe_event(struct acpi_gpe_event_info *gpe_event_info) -{ - struct acpi_gpe_xrupt_info *gpe_xrupt_block; - struct acpi_gpe_block_info *gpe_block; - - ACPI_FUNCTION_ENTRY(); - - /* No need for spin lock since we are not changing any list elements */ - - /* Walk the GPE interrupt levels */ - - gpe_xrupt_block = acpi_gbl_gpe_xrupt_list_head; - while (gpe_xrupt_block) { - gpe_block = gpe_xrupt_block->gpe_block_list_head; - - /* Walk the GPE blocks on this interrupt level */ - - while (gpe_block) { - if ((&gpe_block->event_info[0] <= gpe_event_info) && - (&gpe_block->event_info[gpe_block->gpe_count] > - gpe_event_info)) { - return (TRUE); - } - - gpe_block = gpe_block->next; - } - - gpe_xrupt_block = gpe_xrupt_block->next; - } - - return (FALSE); -} - -/******************************************************************************* - * * FUNCTION: acpi_ev_get_gpe_device * * PARAMETERS: GPE_WALK_CALLBACK @@ -371,8 +324,10 @@ acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info, ACPI_GPE_REGISTER_WIDTH) + j]; - if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == - ACPI_GPE_DISPATCH_HANDLER) { + if ((ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == + ACPI_GPE_DISPATCH_HANDLER) || + (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == + ACPI_GPE_DISPATCH_RAW_HANDLER)) { /* Delete an installed handler block */ @@ -380,10 +335,8 @@ acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info, gpe_event_info->dispatch.handler = NULL; gpe_event_info->flags &= ~ACPI_GPE_DISPATCH_MASK; - } else - if ((gpe_event_info-> - flags & ACPI_GPE_DISPATCH_MASK) == - ACPI_GPE_DISPATCH_NOTIFY) { + } else if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) + == ACPI_GPE_DISPATCH_NOTIFY) { /* Delete the implicit notification device list */ diff --git a/drivers/acpi/acpica/evhandler.c b/drivers/acpi/acpica/evhandler.c index 78ac29351c9e..74e8595f5a2b 100644 --- a/drivers/acpi/acpica/evhandler.c +++ b/drivers/acpi/acpica/evhandler.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evmisc.c b/drivers/acpi/acpica/evmisc.c index 24ea3424981b..f7c9dfe7b990 100644 --- a/drivers/acpi/acpica/evmisc.c +++ b/drivers/acpi/acpica/evmisc.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c index 8eb8575e8c16..9abace3401f9 100644 --- a/drivers/acpi/acpica/evregion.c +++ b/drivers/acpi/acpica/evregion.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evrgnini.c b/drivers/acpi/acpica/evrgnini.c index 1b148a440d67..da323390bb70 100644 --- a/drivers/acpi/acpica/evrgnini.c +++ b/drivers/acpi/acpica/evrgnini.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evsci.c b/drivers/acpi/acpica/evsci.c index 29630e303829..0366703d2970 100644 --- a/drivers/acpi/acpica/evsci.c +++ b/drivers/acpi/acpica/evsci.c @@ -6,7 +6,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evxface.c b/drivers/acpi/acpica/evxface.c index 55a58f3ec8df..81f2d9e87fad 100644 --- a/drivers/acpi/acpica/evxface.c +++ b/drivers/acpi/acpica/evxface.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -51,6 +51,16 @@ #define _COMPONENT ACPI_EVENTS ACPI_MODULE_NAME("evxface") +#if (!ACPI_REDUCED_HARDWARE) +/* Local prototypes */ +static acpi_status +acpi_ev_install_gpe_handler(acpi_handle gpe_device, + u32 gpe_number, + u32 type, + u8 is_raw_handler, + acpi_gpe_handler address, void *context); + +#endif /******************************************************************************* @@ -76,6 +86,7 @@ ACPI_MODULE_NAME("evxface") * handlers. * ******************************************************************************/ + acpi_status acpi_install_notify_handler(acpi_handle device, u32 handler_type, @@ -717,32 +728,37 @@ ACPI_EXPORT_SYMBOL(acpi_remove_fixed_event_handler) /******************************************************************************* * - * FUNCTION: acpi_install_gpe_handler + * FUNCTION: acpi_ev_install_gpe_handler * * PARAMETERS: gpe_device - Namespace node for the GPE (NULL for FADT * defined GPEs) * gpe_number - The GPE number within the GPE block * type - Whether this GPE should be treated as an * edge- or level-triggered interrupt. + * is_raw_handler - Whether this GPE should be handled using + * the special GPE handler mode. * address - Address of the handler * context - Value passed to the handler on each GPE * * RETURN: Status * - * DESCRIPTION: Install a handler for a General Purpose Event. + * DESCRIPTION: Internal function to install a handler for a General Purpose + * Event. * ******************************************************************************/ -acpi_status -acpi_install_gpe_handler(acpi_handle gpe_device, - u32 gpe_number, - u32 type, acpi_gpe_handler address, void *context) +static acpi_status +acpi_ev_install_gpe_handler(acpi_handle gpe_device, + u32 gpe_number, + u32 type, + u8 is_raw_handler, + acpi_gpe_handler address, void *context) { struct acpi_gpe_event_info *gpe_event_info; struct acpi_gpe_handler_info *handler; acpi_status status; acpi_cpu_flags flags; - ACPI_FUNCTION_TRACE(acpi_install_gpe_handler); + ACPI_FUNCTION_TRACE(ev_install_gpe_handler); /* Parameter validation */ @@ -775,8 +791,10 @@ acpi_install_gpe_handler(acpi_handle gpe_device, /* Make sure that there isn't a handler there already */ - if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == - ACPI_GPE_DISPATCH_HANDLER) { + if ((ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == + ACPI_GPE_DISPATCH_HANDLER) || + (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == + ACPI_GPE_DISPATCH_RAW_HANDLER)) { status = AE_ALREADY_EXISTS; goto free_and_exit; } @@ -793,9 +811,10 @@ acpi_install_gpe_handler(acpi_handle gpe_device, * automatically during initialization, in which case it has to be * disabled now to avoid spurious execution of the handler. */ - if (((handler->original_flags & ACPI_GPE_DISPATCH_METHOD) || - (handler->original_flags & ACPI_GPE_DISPATCH_NOTIFY)) && - gpe_event_info->runtime_count) { + if (((ACPI_GPE_DISPATCH_TYPE(handler->original_flags) == + ACPI_GPE_DISPATCH_METHOD) || + (ACPI_GPE_DISPATCH_TYPE(handler->original_flags) == + ACPI_GPE_DISPATCH_NOTIFY)) && gpe_event_info->runtime_count) { handler->originally_enabled = TRUE; (void)acpi_ev_remove_gpe_reference(gpe_event_info); @@ -816,7 +835,10 @@ acpi_install_gpe_handler(acpi_handle gpe_device, gpe_event_info->flags &= ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); - gpe_event_info->flags |= (u8)(type | ACPI_GPE_DISPATCH_HANDLER); + gpe_event_info->flags |= + (u8)(type | + (is_raw_handler ? ACPI_GPE_DISPATCH_RAW_HANDLER : + ACPI_GPE_DISPATCH_HANDLER)); acpi_os_release_lock(acpi_gbl_gpe_lock, flags); @@ -830,10 +852,78 @@ free_and_exit: goto unlock_and_exit; } +/******************************************************************************* + * + * FUNCTION: acpi_install_gpe_handler + * + * PARAMETERS: gpe_device - Namespace node for the GPE (NULL for FADT + * defined GPEs) + * gpe_number - The GPE number within the GPE block + * type - Whether this GPE should be treated as an + * edge- or level-triggered interrupt. + * address - Address of the handler + * context - Value passed to the handler on each GPE + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for a General Purpose Event. + * + ******************************************************************************/ + +acpi_status +acpi_install_gpe_handler(acpi_handle gpe_device, + u32 gpe_number, + u32 type, acpi_gpe_handler address, void *context) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_install_gpe_handler); + + status = + acpi_ev_install_gpe_handler(gpe_device, gpe_number, type, FALSE, + address, context); + + return_ACPI_STATUS(status); +} + ACPI_EXPORT_SYMBOL(acpi_install_gpe_handler) /******************************************************************************* * + * FUNCTION: acpi_install_gpe_raw_handler + * + * PARAMETERS: gpe_device - Namespace node for the GPE (NULL for FADT + * defined GPEs) + * gpe_number - The GPE number within the GPE block + * type - Whether this GPE should be treated as an + * edge- or level-triggered interrupt. + * address - Address of the handler + * context - Value passed to the handler on each GPE + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for a General Purpose Event. + * + ******************************************************************************/ +acpi_status +acpi_install_gpe_raw_handler(acpi_handle gpe_device, + u32 gpe_number, + u32 type, acpi_gpe_handler address, void *context) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE(acpi_install_gpe_raw_handler); + + status = acpi_ev_install_gpe_handler(gpe_device, gpe_number, type, TRUE, + address, context); + + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_install_gpe_raw_handler) + +/******************************************************************************* + * * FUNCTION: acpi_remove_gpe_handler * * PARAMETERS: gpe_device - Namespace node for the GPE (NULL for FADT @@ -880,8 +970,10 @@ acpi_remove_gpe_handler(acpi_handle gpe_device, /* Make sure that a handler is indeed installed */ - if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) != - ACPI_GPE_DISPATCH_HANDLER) { + if ((ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) != + ACPI_GPE_DISPATCH_HANDLER) && + (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) != + ACPI_GPE_DISPATCH_RAW_HANDLER)) { status = AE_NOT_EXIST; goto unlock_and_exit; } @@ -896,6 +988,7 @@ acpi_remove_gpe_handler(acpi_handle gpe_device, /* Remove the handler */ handler = gpe_event_info->dispatch.handler; + gpe_event_info->dispatch.handler = NULL; /* Restore Method node (if any), set dispatch flags */ @@ -909,9 +1002,10 @@ acpi_remove_gpe_handler(acpi_handle gpe_device, * enabled, it should be enabled at this point to restore the * post-initialization configuration. */ - if (((handler->original_flags & ACPI_GPE_DISPATCH_METHOD) || - (handler->original_flags & ACPI_GPE_DISPATCH_NOTIFY)) && - handler->originally_enabled) { + if (((ACPI_GPE_DISPATCH_TYPE(handler->original_flags) == + ACPI_GPE_DISPATCH_METHOD) || + (ACPI_GPE_DISPATCH_TYPE(handler->original_flags) == + ACPI_GPE_DISPATCH_NOTIFY)) && handler->originally_enabled) { (void)acpi_ev_add_gpe_reference(gpe_event_info); } diff --git a/drivers/acpi/acpica/evxfevnt.c b/drivers/acpi/acpica/evxfevnt.c index bb8cbf5961bf..df06a23c4197 100644 --- a/drivers/acpi/acpica/evxfevnt.c +++ b/drivers/acpi/acpica/evxfevnt.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index e889a5304abd..70eb47e3d724 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -132,7 +132,7 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number) */ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); if (gpe_event_info) { - if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) != + if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) != ACPI_GPE_DISPATCH_NONE) { status = acpi_ev_add_gpe_reference(gpe_event_info); } else { @@ -183,6 +183,77 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number) ACPI_EXPORT_SYMBOL(acpi_disable_gpe) +/******************************************************************************* + * + * FUNCTION: acpi_set_gpe + * + * PARAMETERS: gpe_device - Parent GPE Device. NULL for GPE0/GPE1 + * gpe_number - GPE level within the GPE block + * action - ACPI_GPE_ENABLE or ACPI_GPE_DISABLE + * + * RETURN: Status + * + * DESCRIPTION: Enable or disable an individual GPE. This function bypasses + * the reference count mechanism used in the acpi_enable_gpe(), + * acpi_disable_gpe() interfaces. + * This API is typically used by the GPE raw handler mode driver + * to switch between the polling mode and the interrupt mode after + * the driver has enabled the GPE. + * The APIs should be invoked in this order: + * acpi_enable_gpe() <- Ensure the reference count > 0 + * acpi_set_gpe(ACPI_GPE_DISABLE) <- Enter polling mode + * acpi_set_gpe(ACPI_GPE_ENABLE) <- Leave polling mode + * acpi_disable_gpe() <- Decrease the reference count + * + * Note: If a GPE is shared by 2 silicon components, then both the drivers + * should support GPE polling mode or disabling the GPE for long period + * for one driver may break the other. So use it with care since all + * firmware _Lxx/_Exx handlers currently rely on the GPE interrupt mode. + * + ******************************************************************************/ +acpi_status acpi_set_gpe(acpi_handle gpe_device, u32 gpe_number, u8 action) +{ + struct acpi_gpe_event_info *gpe_event_info; + acpi_status status; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(acpi_set_gpe); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Perform the action */ + + switch (action) { + case ACPI_GPE_ENABLE: + + status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_ENABLE); + break; + + case ACPI_GPE_DISABLE: + + status = acpi_hw_low_set_gpe(gpe_event_info, ACPI_GPE_DISABLE); + break; + + default: + + status = AE_BAD_PARAMETER; + break; + } + +unlock_and_exit: + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_set_gpe) /******************************************************************************* * @@ -313,7 +384,7 @@ acpi_setup_gpe_for_wake(acpi_handle wake_device, * known as an "implicit notify". Note: The GPE is assumed to be * level-triggered (for windows compatibility). */ - if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == + if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == ACPI_GPE_DISPATCH_NONE) { /* * This is the first device for implicit notify on this GPE. @@ -327,7 +398,7 @@ acpi_setup_gpe_for_wake(acpi_handle wake_device, * If we already have an implicit notify on this GPE, add * this device to the notify list. */ - if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == + if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) == ACPI_GPE_DISPATCH_NOTIFY) { /* Ensure that the device is not already in the list */ @@ -530,6 +601,49 @@ unlock_and_exit: ACPI_EXPORT_SYMBOL(acpi_get_gpe_status) +/******************************************************************************* + * + * FUNCTION: acpi_finish_gpe + * + * PARAMETERS: gpe_device - Namespace node for the GPE Block + * (NULL for FADT defined GPEs) + * gpe_number - GPE level within the GPE block + * + * RETURN: Status + * + * DESCRIPTION: Clear and conditionally reenable a GPE. This completes the GPE + * processing. Intended for use by asynchronous host-installed + * GPE handlers. The GPE is only reenabled if the enable_for_run bit + * is set in the GPE info. + * + ******************************************************************************/ +acpi_status acpi_finish_gpe(acpi_handle gpe_device, u32 gpe_number) +{ + struct acpi_gpe_event_info *gpe_event_info; + acpi_status status; + acpi_cpu_flags flags; + + ACPI_FUNCTION_TRACE(acpi_finish_gpe); + + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + status = acpi_ev_finish_gpe(gpe_event_info); + +unlock_and_exit: + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS(status); +} + +ACPI_EXPORT_SYMBOL(acpi_finish_gpe) + /****************************************************************************** * * FUNCTION: acpi_disable_all_gpes @@ -604,7 +718,6 @@ ACPI_EXPORT_SYMBOL(acpi_enable_all_runtime_gpes) * all GPE blocks. * ******************************************************************************/ - acpi_status acpi_enable_all_wakeup_gpes(void) { acpi_status status; diff --git a/drivers/acpi/acpica/evxfregn.c b/drivers/acpi/acpica/evxfregn.c index 2d6f187939c7..f21afbab03f7 100644 --- a/drivers/acpi/acpica/evxfregn.c +++ b/drivers/acpi/acpica/evxfregn.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exconfig.c b/drivers/acpi/acpica/exconfig.c index 7d2949420db7..6e0df2b9d5a4 100644 --- a/drivers/acpi/acpica/exconfig.c +++ b/drivers/acpi/acpica/exconfig.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exconvrt.c b/drivers/acpi/acpica/exconvrt.c index c545386fee96..89a976b4ccf2 100644 --- a/drivers/acpi/acpica/exconvrt.c +++ b/drivers/acpi/acpica/exconvrt.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/excreate.c b/drivers/acpi/acpica/excreate.c index 95d23dabcfbb..aaeea4840aaa 100644 --- a/drivers/acpi/acpica/excreate.c +++ b/drivers/acpi/acpica/excreate.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exdebug.c b/drivers/acpi/acpica/exdebug.c index 6fbfad47518c..e67d0aca3fe6 100644 --- a/drivers/acpi/acpica/exdebug.c +++ b/drivers/acpi/acpica/exdebug.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exdump.c b/drivers/acpi/acpica/exdump.c index 0f23c3f2678e..7c213b6b6472 100644 --- a/drivers/acpi/acpica/exdump.c +++ b/drivers/acpi/acpica/exdump.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c index b994845ed359..c161dd974f74 100644 --- a/drivers/acpi/acpica/exfield.c +++ b/drivers/acpi/acpica/exfield.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exfldio.c b/drivers/acpi/acpica/exfldio.c index 1d1b27a96c5b..49479927e7f7 100644 --- a/drivers/acpi/acpica/exfldio.c +++ b/drivers/acpi/acpica/exfldio.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exmisc.c b/drivers/acpi/acpica/exmisc.c index 2207e624f538..b56fc9d6f48e 100644 --- a/drivers/acpi/acpica/exmisc.c +++ b/drivers/acpi/acpica/exmisc.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exmutex.c b/drivers/acpi/acpica/exmutex.c index b49ea2a95f4f..472030f2b5bb 100644 --- a/drivers/acpi/acpica/exmutex.c +++ b/drivers/acpi/acpica/exmutex.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exnames.c b/drivers/acpi/acpica/exnames.c index dbb03b544e8c..453b00c30177 100644 --- a/drivers/acpi/acpica/exnames.c +++ b/drivers/acpi/acpica/exnames.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exoparg1.c b/drivers/acpi/acpica/exoparg1.c index 1b8e94104407..77930683ab7d 100644 --- a/drivers/acpi/acpica/exoparg1.c +++ b/drivers/acpi/acpica/exoparg1.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exoparg2.c b/drivers/acpi/acpica/exoparg2.c index 2ede656ee26a..fcc618aa2061 100644 --- a/drivers/acpi/acpica/exoparg2.c +++ b/drivers/acpi/acpica/exoparg2.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exoparg3.c b/drivers/acpi/acpica/exoparg3.c index 363767cf01e5..b813fed95e56 100644 --- a/drivers/acpi/acpica/exoparg3.c +++ b/drivers/acpi/acpica/exoparg3.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exoparg6.c b/drivers/acpi/acpica/exoparg6.c index 29e9e99f7fe3..c930edda3f65 100644 --- a/drivers/acpi/acpica/exoparg6.c +++ b/drivers/acpi/acpica/exoparg6.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exprep.c b/drivers/acpi/acpica/exprep.c index 118e942005e5..4c2836dc825b 100644 --- a/drivers/acpi/acpica/exprep.c +++ b/drivers/acpi/acpica/exprep.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exregion.c b/drivers/acpi/acpica/exregion.c index cd5288a257a9..0fe188e238ef 100644 --- a/drivers/acpi/acpica/exregion.c +++ b/drivers/acpi/acpica/exregion.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exresnte.c b/drivers/acpi/acpica/exresnte.c index ab060261b43e..c7e3b929aa85 100644 --- a/drivers/acpi/acpica/exresnte.c +++ b/drivers/acpi/acpica/exresnte.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exresolv.c b/drivers/acpi/acpica/exresolv.c index 3cde553bcbe1..b6b7f3af29e4 100644 --- a/drivers/acpi/acpica/exresolv.c +++ b/drivers/acpi/acpica/exresolv.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exresop.c b/drivers/acpi/acpica/exresop.c index 3af8de3fcea4..d2964af9ad4d 100644 --- a/drivers/acpi/acpica/exresop.c +++ b/drivers/acpi/acpica/exresop.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exstore.c b/drivers/acpi/acpica/exstore.c index daf49f7ea311..a7eee2400ce0 100644 --- a/drivers/acpi/acpica/exstore.c +++ b/drivers/acpi/acpica/exstore.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exstoren.c b/drivers/acpi/acpica/exstoren.c index 04bd16c08f9e..3101607b4efe 100644 --- a/drivers/acpi/acpica/exstoren.c +++ b/drivers/acpi/acpica/exstoren.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exstorob.c b/drivers/acpi/acpica/exstorob.c index fd11018b0168..6fa3c8d8fc5f 100644 --- a/drivers/acpi/acpica/exstorob.c +++ b/drivers/acpi/acpica/exstorob.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exsystem.c b/drivers/acpi/acpica/exsystem.c index f7da64123ed5..05450656fe3d 100644 --- a/drivers/acpi/acpica/exsystem.c +++ b/drivers/acpi/acpica/exsystem.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/exutils.c b/drivers/acpi/acpica/exutils.c index d9d72dff2a76..3f4225e95d93 100644 --- a/drivers/acpi/acpica/exutils.c +++ b/drivers/acpi/acpica/exutils.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwacpi.c b/drivers/acpi/acpica/hwacpi.c index 1e66d960fc11..e5c5949f9081 100644 --- a/drivers/acpi/acpica/hwacpi.c +++ b/drivers/acpi/acpica/hwacpi.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwesleep.c b/drivers/acpi/acpica/hwesleep.c index 858fdd6be598..e5599f610808 100644 --- a/drivers/acpi/acpica/hwesleep.c +++ b/drivers/acpi/acpica/hwesleep.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwgpe.c b/drivers/acpi/acpica/hwgpe.c index 494027f5c067..84bc550f4f1d 100644 --- a/drivers/acpi/acpica/hwgpe.c +++ b/drivers/acpi/acpica/hwgpe.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -54,6 +54,10 @@ acpi_hw_enable_wakeup_gpe_block(struct acpi_gpe_xrupt_info *gpe_xrupt_info, struct acpi_gpe_block_info *gpe_block, void *context); +static acpi_status +acpi_hw_gpe_enable_write(u8 enable_mask, + struct acpi_gpe_register_info *gpe_register_info); + /****************************************************************************** * * FUNCTION: acpi_hw_get_gpe_register_bit @@ -146,7 +150,7 @@ acpi_hw_low_set_gpe(struct acpi_gpe_event_info *gpe_event_info, u32 action) status = acpi_hw_write(enable_mask, &gpe_register_info->enable_address); if (ACPI_SUCCESS(status) && (action & ACPI_GPE_SAVE_MASK)) { - gpe_register_info->enable_mask = enable_mask; + gpe_register_info->enable_mask = (u8)enable_mask; } return (status); } @@ -221,7 +225,7 @@ acpi_hw_get_gpe_status(struct acpi_gpe_event_info * gpe_event_info, /* GPE currently handled? */ - if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) != + if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) != ACPI_GPE_DISPATCH_NONE) { local_event_status |= ACPI_EVENT_FLAG_HAS_HANDLER; } diff --git a/drivers/acpi/acpica/hwpci.c b/drivers/acpi/acpica/hwpci.c index 6aade8e1d2a1..c5214dec4988 100644 --- a/drivers/acpi/acpica/hwpci.c +++ b/drivers/acpi/acpica/hwpci.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwregs.c b/drivers/acpi/acpica/hwregs.c index a4c34d2c556b..3cf77afd142c 100644 --- a/drivers/acpi/acpica/hwregs.c +++ b/drivers/acpi/acpica/hwregs.c @@ -6,7 +6,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwsleep.c b/drivers/acpi/acpica/hwsleep.c index d590693eb54e..7d21cae6d602 100644 --- a/drivers/acpi/acpica/hwsleep.c +++ b/drivers/acpi/acpica/hwsleep.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwtimer.c b/drivers/acpi/acpica/hwtimer.c index 76ab5c1a814e..675c709a300b 100644 --- a/drivers/acpi/acpica/hwtimer.c +++ b/drivers/acpi/acpica/hwtimer.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwvalid.c b/drivers/acpi/acpica/hwvalid.c index 6b919127cd9d..2bd33fe56cb3 100644 --- a/drivers/acpi/acpica/hwvalid.c +++ b/drivers/acpi/acpica/hwvalid.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwxface.c b/drivers/acpi/acpica/hwxface.c index 96d007df65ec..5f97468df8ff 100644 --- a/drivers/acpi/acpica/hwxface.c +++ b/drivers/acpi/acpica/hwxface.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/hwxfsleep.c b/drivers/acpi/acpica/hwxfsleep.c index 6921c7f3d208..3b3767698827 100644 --- a/drivers/acpi/acpica/hwxfsleep.c +++ b/drivers/acpi/acpica/hwxfsleep.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsaccess.c b/drivers/acpi/acpica/nsaccess.c index f1249e3463be..24fa19a76d70 100644 --- a/drivers/acpi/acpica/nsaccess.c +++ b/drivers/acpi/acpica/nsaccess.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsalloc.c b/drivers/acpi/acpica/nsalloc.c index 607eb9e5150d..e107f929d9cf 100644 --- a/drivers/acpi/acpica/nsalloc.c +++ b/drivers/acpi/acpica/nsalloc.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsarguments.c b/drivers/acpi/acpica/nsarguments.c index 80fcfc8c9c1b..5d347a71bd0b 100644 --- a/drivers/acpi/acpica/nsarguments.c +++ b/drivers/acpi/acpica/nsarguments.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsconvert.c b/drivers/acpi/acpica/nsconvert.c index b55642c4ee58..1a8b39c8d969 100644 --- a/drivers/acpi/acpica/nsconvert.c +++ b/drivers/acpi/acpica/nsconvert.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsdump.c b/drivers/acpi/acpica/nsdump.c index 3d88ef4a3e0d..80f097eb7381 100644 --- a/drivers/acpi/acpica/nsdump.c +++ b/drivers/acpi/acpica/nsdump.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsdumpdv.c b/drivers/acpi/acpica/nsdumpdv.c index 42d37109aa5d..7dc367e6fe09 100644 --- a/drivers/acpi/acpica/nsdumpdv.c +++ b/drivers/acpi/acpica/nsdumpdv.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nseval.c b/drivers/acpi/acpica/nseval.c index e634a05974db..7bcc68f57afa 100644 --- a/drivers/acpi/acpica/nseval.c +++ b/drivers/acpi/acpica/nseval.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsinit.c b/drivers/acpi/acpica/nsinit.c index a3fb7e4c0809..4a85c4517988 100644 --- a/drivers/acpi/acpica/nsinit.c +++ b/drivers/acpi/acpica/nsinit.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsload.c b/drivers/acpi/acpica/nsload.c index 7c9d0181f341..bd6cd4a81316 100644 --- a/drivers/acpi/acpica/nsload.c +++ b/drivers/acpi/acpica/nsload.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsnames.c b/drivers/acpi/acpica/nsnames.c index 7eee0a6f02f6..d293d9748036 100644 --- a/drivers/acpi/acpica/nsnames.c +++ b/drivers/acpi/acpica/nsnames.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsobject.c b/drivers/acpi/acpica/nsobject.c index a42ee9d6970d..677bc9330e64 100644 --- a/drivers/acpi/acpica/nsobject.c +++ b/drivers/acpi/acpica/nsobject.c @@ -6,7 +6,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsparse.c b/drivers/acpi/acpica/nsparse.c index e83cff31754b..c95a119767b5 100644 --- a/drivers/acpi/acpica/nsparse.c +++ b/drivers/acpi/acpica/nsparse.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nspredef.c b/drivers/acpi/acpica/nspredef.c index 392910ffbed9..0eb54315b4be 100644 --- a/drivers/acpi/acpica/nspredef.c +++ b/drivers/acpi/acpica/nspredef.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsprepkg.c b/drivers/acpi/acpica/nsprepkg.c index 1b13b921dda9..8b79958b7aca 100644 --- a/drivers/acpi/acpica/nsprepkg.c +++ b/drivers/acpi/acpica/nsprepkg.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsrepair.c b/drivers/acpi/acpica/nsrepair.c index 7e417aa5c91e..151fcd95ba84 100644 --- a/drivers/acpi/acpica/nsrepair.c +++ b/drivers/acpi/acpica/nsrepair.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsrepair2.c b/drivers/acpi/acpica/nsrepair2.c index b09e6bef72b8..c30672d23878 100644 --- a/drivers/acpi/acpica/nsrepair2.c +++ b/drivers/acpi/acpica/nsrepair2.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nssearch.c b/drivers/acpi/acpica/nssearch.c index af1cc42a8aa1..4a9d4a66016e 100644 --- a/drivers/acpi/acpica/nssearch.c +++ b/drivers/acpi/acpica/nssearch.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsutils.c b/drivers/acpi/acpica/nsutils.c index 4a5e3f5c0ff7..6ad02008c0c2 100644 --- a/drivers/acpi/acpica/nsutils.c +++ b/drivers/acpi/acpica/nsutils.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nswalk.c b/drivers/acpi/acpica/nswalk.c index 4758a1f2ce22..c68609a2bc1b 100644 --- a/drivers/acpi/acpica/nswalk.c +++ b/drivers/acpi/acpica/nswalk.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsxfeval.c b/drivers/acpi/acpica/nsxfeval.c index 4bd558bf10d2..b6030a2deee1 100644 --- a/drivers/acpi/acpica/nsxfeval.c +++ b/drivers/acpi/acpica/nsxfeval.c @@ -6,7 +6,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsxfname.c b/drivers/acpi/acpica/nsxfname.c index 8c6c11ce9760..d66c326485d8 100644 --- a/drivers/acpi/acpica/nsxfname.c +++ b/drivers/acpi/acpica/nsxfname.c @@ -6,7 +6,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/nsxfobj.c b/drivers/acpi/acpica/nsxfobj.c index dae9401be7a2..793383501f81 100644 --- a/drivers/acpi/acpica/nsxfobj.c +++ b/drivers/acpi/acpica/nsxfobj.c @@ -6,7 +6,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -53,50 +53,6 @@ ACPI_MODULE_NAME("nsxfobj") /******************************************************************************* * - * FUNCTION: acpi_get_id - * - * PARAMETERS: Handle - Handle of object whose id is desired - * ret_id - Where the id will be placed - * - * RETURN: Status - * - * DESCRIPTION: This routine returns the owner id associated with a handle - * - ******************************************************************************/ -acpi_status acpi_get_id(acpi_handle handle, acpi_owner_id * ret_id) -{ - struct acpi_namespace_node *node; - acpi_status status; - - /* Parameter Validation */ - - if (!ret_id) { - return (AE_BAD_PARAMETER); - } - - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { - return (status); - } - - /* Convert and validate the handle */ - - node = acpi_ns_validate_handle(handle); - if (!node) { - (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - return (AE_BAD_PARAMETER); - } - - *ret_id = node->owner_id; - - status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - return (status); -} - -ACPI_EXPORT_SYMBOL(acpi_get_id) - -/******************************************************************************* - * * FUNCTION: acpi_get_type * * PARAMETERS: handle - Handle of object whose type is desired diff --git a/drivers/acpi/acpica/psargs.c b/drivers/acpi/acpica/psargs.c index 314d314340ae..6d038770577b 100644 --- a/drivers/acpi/acpica/psargs.c +++ b/drivers/acpi/acpica/psargs.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psloop.c b/drivers/acpi/acpica/psloop.c index b058e2390fdd..90437227d790 100644 --- a/drivers/acpi/acpica/psloop.c +++ b/drivers/acpi/acpica/psloop.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psobject.c b/drivers/acpi/acpica/psobject.c index a6885077d59e..2f5ddd806c58 100644 --- a/drivers/acpi/acpica/psobject.c +++ b/drivers/acpi/acpica/psobject.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psopcode.c b/drivers/acpi/acpica/psopcode.c index 1755d2ac5656..1af4a405e351 100644 --- a/drivers/acpi/acpica/psopcode.c +++ b/drivers/acpi/acpica/psopcode.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psopinfo.c b/drivers/acpi/acpica/psopinfo.c index 0d8d37ffd04d..e18e7c47f482 100644 --- a/drivers/acpi/acpica/psopinfo.c +++ b/drivers/acpi/acpica/psopinfo.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psparse.c b/drivers/acpi/acpica/psparse.c index 6d27b597394e..a555f7f7b9a2 100644 --- a/drivers/acpi/acpica/psparse.c +++ b/drivers/acpi/acpica/psparse.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psscope.c b/drivers/acpi/acpica/psscope.c index 32d250feea21..9d669cc6cb62 100644 --- a/drivers/acpi/acpica/psscope.c +++ b/drivers/acpi/acpica/psscope.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/pstree.c b/drivers/acpi/acpica/pstree.c index 0b64181e7720..89984f30addc 100644 --- a/drivers/acpi/acpica/pstree.c +++ b/drivers/acpi/acpica/pstree.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psutils.c b/drivers/acpi/acpica/psutils.c index 3cd48802eede..960505ab409a 100644 --- a/drivers/acpi/acpica/psutils.c +++ b/drivers/acpi/acpica/psutils.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/pswalk.c b/drivers/acpi/acpica/pswalk.c index 9cb07e1e76d9..ba5f69171288 100644 --- a/drivers/acpi/acpica/pswalk.c +++ b/drivers/acpi/acpica/pswalk.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c index e135acaa5e1c..841a5ea06094 100644 --- a/drivers/acpi/acpica/psxface.c +++ b/drivers/acpi/acpica/psxface.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsaddr.c b/drivers/acpi/acpica/rsaddr.c index 916fd095ff34..66d406e8fe36 100644 --- a/drivers/acpi/acpica/rsaddr.c +++ b/drivers/acpi/acpica/rsaddr.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -74,7 +74,7 @@ struct acpi_rsconvert_info acpi_rs_convert_address16[5] = { * Address Translation Offset * Address Length */ - {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.address16.granularity), + {ACPI_RSC_MOVE16, ACPI_RS_OFFSET(data.address16.address.granularity), AML_OFFSET(address16.granularity), 5}, @@ -112,7 +112,7 @@ struct acpi_rsconvert_info acpi_rs_convert_address32[5] = { * Address Translation Offset * Address Length */ - {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.address32.granularity), + {ACPI_RSC_MOVE32, ACPI_RS_OFFSET(data.address32.address.granularity), AML_OFFSET(address32.granularity), 5}, @@ -150,7 +150,7 @@ struct acpi_rsconvert_info acpi_rs_convert_address64[5] = { * Address Translation Offset * Address Length */ - {ACPI_RSC_MOVE64, ACPI_RS_OFFSET(data.address64.granularity), + {ACPI_RSC_MOVE64, ACPI_RS_OFFSET(data.address64.address.granularity), AML_OFFSET(address64.granularity), 5}, @@ -194,7 +194,8 @@ struct acpi_rsconvert_info acpi_rs_convert_ext_address64[5] = { * Address Length * Type-Specific Attribute */ - {ACPI_RSC_MOVE64, ACPI_RS_OFFSET(data.ext_address64.granularity), + {ACPI_RSC_MOVE64, + ACPI_RS_OFFSET(data.ext_address64.address.granularity), AML_OFFSET(ext_address64.granularity), 6} }; diff --git a/drivers/acpi/acpica/rscalc.c b/drivers/acpi/acpica/rscalc.c index 689556744b03..cb739a694931 100644 --- a/drivers/acpi/acpica/rscalc.c +++ b/drivers/acpi/acpica/rscalc.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rscreate.c b/drivers/acpi/acpica/rscreate.c index 049d9c22a0f9..15434e4c9b34 100644 --- a/drivers/acpi/acpica/rscreate.c +++ b/drivers/acpi/acpica/rscreate.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsdump.c b/drivers/acpi/acpica/rsdump.c index c3c56b5a9788..1539394c8c52 100644 --- a/drivers/acpi/acpica/rsdump.c +++ b/drivers/acpi/acpica/rsdump.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsdumpinfo.c b/drivers/acpi/acpica/rsdumpinfo.c index 2f9332d5c973..b29d9ec63d1b 100644 --- a/drivers/acpi/acpica/rsdumpinfo.c +++ b/drivers/acpi/acpica/rsdumpinfo.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -183,15 +183,15 @@ struct acpi_rsdump_info acpi_rs_dump_address16[8] = { {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_address16), "16-Bit WORD Address Space", NULL}, {ACPI_RSD_ADDRESS, 0, NULL, NULL}, - {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(address16.granularity), "Granularity", - NULL}, - {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(address16.minimum), "Address Minimum", - NULL}, - {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(address16.maximum), "Address Maximum", - NULL}, - {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(address16.translation_offset), + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(address16.address.granularity), + "Granularity", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(address16.address.minimum), + "Address Minimum", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(address16.address.maximum), + "Address Maximum", NULL}, + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(address16.address.translation_offset), "Translation Offset", NULL}, - {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(address16.address_length), + {ACPI_RSD_UINT16, ACPI_RSD_OFFSET(address16.address.address_length), "Address Length", NULL}, {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET(address16.resource_source), NULL, NULL} }; @@ -200,15 +200,15 @@ struct acpi_rsdump_info acpi_rs_dump_address32[8] = { {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_address32), "32-Bit DWORD Address Space", NULL}, {ACPI_RSD_ADDRESS, 0, NULL, NULL}, - {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(address32.granularity), "Granularity", - NULL}, - {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(address32.minimum), "Address Minimum", - NULL}, - {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(address32.maximum), "Address Maximum", - NULL}, - {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(address32.translation_offset), + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(address32.address.granularity), + "Granularity", NULL}, + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(address32.address.minimum), + "Address Minimum", NULL}, + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(address32.address.maximum), + "Address Maximum", NULL}, + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(address32.address.translation_offset), "Translation Offset", NULL}, - {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(address32.address_length), + {ACPI_RSD_UINT32, ACPI_RSD_OFFSET(address32.address.address_length), "Address Length", NULL}, {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET(address32.resource_source), NULL, NULL} }; @@ -217,15 +217,15 @@ struct acpi_rsdump_info acpi_rs_dump_address64[8] = { {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_address64), "64-Bit QWORD Address Space", NULL}, {ACPI_RSD_ADDRESS, 0, NULL, NULL}, - {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(address64.granularity), "Granularity", - NULL}, - {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(address64.minimum), "Address Minimum", - NULL}, - {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(address64.maximum), "Address Maximum", - NULL}, - {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(address64.translation_offset), + {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(address64.address.granularity), + "Granularity", NULL}, + {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(address64.address.minimum), + "Address Minimum", NULL}, + {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(address64.address.maximum), + "Address Maximum", NULL}, + {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(address64.address.translation_offset), "Translation Offset", NULL}, - {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(address64.address_length), + {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(address64.address.address_length), "Address Length", NULL}, {ACPI_RSD_SOURCE, ACPI_RSD_OFFSET(address64.resource_source), NULL, NULL} }; @@ -234,15 +234,16 @@ struct acpi_rsdump_info acpi_rs_dump_ext_address64[8] = { {ACPI_RSD_TITLE, ACPI_RSD_TABLE_SIZE(acpi_rs_dump_ext_address64), "64-Bit Extended Address Space", NULL}, {ACPI_RSD_ADDRESS, 0, NULL, NULL}, - {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(ext_address64.granularity), + {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(ext_address64.address.granularity), "Granularity", NULL}, - {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(ext_address64.minimum), + {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(ext_address64.address.minimum), "Address Minimum", NULL}, - {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(ext_address64.maximum), + {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(ext_address64.address.maximum), "Address Maximum", NULL}, - {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(ext_address64.translation_offset), + {ACPI_RSD_UINT64, + ACPI_RSD_OFFSET(ext_address64.address.translation_offset), "Translation Offset", NULL}, - {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(ext_address64.address_length), + {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(ext_address64.address.address_length), "Address Length", NULL}, {ACPI_RSD_UINT64, ACPI_RSD_OFFSET(ext_address64.type_specific), "Type-Specific Attribute", NULL} diff --git a/drivers/acpi/acpica/rsinfo.c b/drivers/acpi/acpica/rsinfo.c index 9d3f8a9a24bd..edecfc675979 100644 --- a/drivers/acpi/acpica/rsinfo.c +++ b/drivers/acpi/acpica/rsinfo.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsio.c b/drivers/acpi/acpica/rsio.c index 19d64873290a..5adba018bab0 100644 --- a/drivers/acpi/acpica/rsio.c +++ b/drivers/acpi/acpica/rsio.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsirq.c b/drivers/acpi/acpica/rsirq.c index 3461f7db26df..07cfa70a475b 100644 --- a/drivers/acpi/acpica/rsirq.c +++ b/drivers/acpi/acpica/rsirq.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rslist.c b/drivers/acpi/acpica/rslist.c index 77291293af64..50d5be2ee062 100644 --- a/drivers/acpi/acpica/rslist.c +++ b/drivers/acpi/acpica/rslist.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsmemory.c b/drivers/acpi/acpica/rsmemory.c index eab4483ff5f8..c6b80862030e 100644 --- a/drivers/acpi/acpica/rsmemory.c +++ b/drivers/acpi/acpica/rsmemory.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsmisc.c b/drivers/acpi/acpica/rsmisc.c index 41eea4bc089c..1fe49d223663 100644 --- a/drivers/acpi/acpica/rsmisc.c +++ b/drivers/acpi/acpica/rsmisc.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsserial.c b/drivers/acpi/acpica/rsserial.c index 9e8407223d95..4c8c6fe6ea74 100644 --- a/drivers/acpi/acpica/rsserial.c +++ b/drivers/acpi/acpica/rsserial.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsutils.c b/drivers/acpi/acpica/rsutils.c index 897a5ceb0420..ece3cd60cc6a 100644 --- a/drivers/acpi/acpica/rsutils.c +++ b/drivers/acpi/acpica/rsutils.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/rsxface.c b/drivers/acpi/acpica/rsxface.c index 877ab9202133..8e6276df0226 100644 --- a/drivers/acpi/acpica/rsxface.c +++ b/drivers/acpi/acpica/rsxface.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -60,11 +60,11 @@ ACPI_MODULE_NAME("rsxface") ACPI_COPY_FIELD(out, in, min_address_fixed); \ ACPI_COPY_FIELD(out, in, max_address_fixed); \ ACPI_COPY_FIELD(out, in, info); \ - ACPI_COPY_FIELD(out, in, granularity); \ - ACPI_COPY_FIELD(out, in, minimum); \ - ACPI_COPY_FIELD(out, in, maximum); \ - ACPI_COPY_FIELD(out, in, translation_offset); \ - ACPI_COPY_FIELD(out, in, address_length); \ + ACPI_COPY_FIELD(out, in, address.granularity); \ + ACPI_COPY_FIELD(out, in, address.minimum); \ + ACPI_COPY_FIELD(out, in, address.maximum); \ + ACPI_COPY_FIELD(out, in, address.translation_offset); \ + ACPI_COPY_FIELD(out, in, address.address_length); \ ACPI_COPY_FIELD(out, in, resource_source); /* Local prototypes */ static acpi_status diff --git a/drivers/acpi/acpica/tbdata.c b/drivers/acpi/acpica/tbdata.c index f499c10ceb4a..6a144957aadd 100644 --- a/drivers/acpi/acpica/tbdata.c +++ b/drivers/acpi/acpica/tbdata.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c index 41519a958083..7d2486005e3f 100644 --- a/drivers/acpi/acpica/tbfadt.c +++ b/drivers/acpi/acpica/tbfadt.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/tbfind.c b/drivers/acpi/acpica/tbfind.c index cb947700206c..0b879fcfef67 100644 --- a/drivers/acpi/acpica/tbfind.c +++ b/drivers/acpi/acpica/tbfind.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/tbinstal.c b/drivers/acpi/acpica/tbinstal.c index 755b90c40ddf..9bad45e63a45 100644 --- a/drivers/acpi/acpica/tbinstal.c +++ b/drivers/acpi/acpica/tbinstal.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/tbprint.c b/drivers/acpi/acpica/tbprint.c index df3bb20ea325..ef16c06e5091 100644 --- a/drivers/acpi/acpica/tbprint.c +++ b/drivers/acpi/acpica/tbprint.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c index 6b1ca9991b90..6559a58439c5 100644 --- a/drivers/acpi/acpica/tbutils.c +++ b/drivers/acpi/acpica/tbutils.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/tbxface.c b/drivers/acpi/acpica/tbxface.c index 6482b0ded652..60e94f87f27a 100644 --- a/drivers/acpi/acpica/tbxface.c +++ b/drivers/acpi/acpica/tbxface.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -265,45 +265,6 @@ ACPI_EXPORT_SYMBOL(acpi_get_table_header) /******************************************************************************* * - * FUNCTION: acpi_unload_table_id - * - * PARAMETERS: id - Owner ID of the table to be removed. - * - * RETURN: Status - * - * DESCRIPTION: This routine is used to force the unload of a table (by id) - * - ******************************************************************************/ -acpi_status acpi_unload_table_id(acpi_owner_id id) -{ - int i; - acpi_status status = AE_NOT_EXIST; - - ACPI_FUNCTION_TRACE(acpi_unload_table_id); - - /* Find table in the global table list */ - for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) { - if (id != acpi_gbl_root_table_list.tables[i].owner_id) { - continue; - } - /* - * Delete all namespace objects owned by this table. Note that these - * objects can appear anywhere in the namespace by virtue of the AML - * "Scope" operator. Thus, we need to track ownership by an ID, not - * simply a position within the hierarchy - */ - acpi_tb_delete_namespace_by_owner(i); - status = acpi_tb_release_owner_id(i); - acpi_tb_set_table_loaded_flag(i, FALSE); - break; - } - return_ACPI_STATUS(status); -} - -ACPI_EXPORT_SYMBOL(acpi_unload_table_id) - -/******************************************************************************* - * * FUNCTION: acpi_get_table_with_size * * PARAMETERS: signature - ACPI signature of needed table diff --git a/drivers/acpi/acpica/tbxfload.c b/drivers/acpi/acpica/tbxfload.c index ab5308b81aa8..aadb3002a2dd 100644 --- a/drivers/acpi/acpica/tbxfload.c +++ b/drivers/acpi/acpica/tbxfload.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/tbxfroot.c b/drivers/acpi/acpica/tbxfroot.c index 43a54af2b548..eac52cf14f1a 100644 --- a/drivers/acpi/acpica/tbxfroot.c +++ b/drivers/acpi/acpica/tbxfroot.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utaddress.c b/drivers/acpi/acpica/utaddress.c index a1acec9d2ef3..1279f50da757 100644 --- a/drivers/acpi/acpica/utaddress.c +++ b/drivers/acpi/acpica/utaddress.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utalloc.c b/drivers/acpi/acpica/utalloc.c index efac83c606dc..61d8f6d186d1 100644 --- a/drivers/acpi/acpica/utalloc.c +++ b/drivers/acpi/acpica/utalloc.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utbuffer.c b/drivers/acpi/acpica/utbuffer.c index 038ea887f562..242bd071f007 100644 --- a/drivers/acpi/acpica/utbuffer.c +++ b/drivers/acpi/acpica/utbuffer.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utcache.c b/drivers/acpi/acpica/utcache.c index 78fde0aac487..eacc5eee362e 100644 --- a/drivers/acpi/acpica/utcache.c +++ b/drivers/acpi/acpica/utcache.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utcopy.c b/drivers/acpi/acpica/utcopy.c index ff601c0f7c7a..c37ec5035f4c 100644 --- a/drivers/acpi/acpica/utcopy.c +++ b/drivers/acpi/acpica/utcopy.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utdebug.c b/drivers/acpi/acpica/utdebug.c index e516254c63b2..57078e3ea9b7 100644 --- a/drivers/acpi/acpica/utdebug.c +++ b/drivers/acpi/acpica/utdebug.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utdecode.c b/drivers/acpi/acpica/utdecode.c index 40e923e675fc..988e23b7795c 100644 --- a/drivers/acpi/acpica/utdecode.c +++ b/drivers/acpi/acpica/utdecode.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utdelete.c b/drivers/acpi/acpica/utdelete.c index a3516de213fa..71fce389fd48 100644 --- a/drivers/acpi/acpica/utdelete.c +++ b/drivers/acpi/acpica/utdelete.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/uterror.c b/drivers/acpi/acpica/uterror.c index 8e544d4688cd..9ef80f2828e3 100644 --- a/drivers/acpi/acpica/uterror.c +++ b/drivers/acpi/acpica/uterror.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/uteval.c b/drivers/acpi/acpica/uteval.c index 8fed1482d228..6c738fa0cd42 100644 --- a/drivers/acpi/acpica/uteval.c +++ b/drivers/acpi/acpica/uteval.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utexcep.c b/drivers/acpi/acpica/utexcep.c index 0403dcaabaf2..743a0ae9fb17 100644 --- a/drivers/acpi/acpica/utexcep.c +++ b/drivers/acpi/acpica/utexcep.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utfileio.c b/drivers/acpi/acpica/utfileio.c index 4e263a8cc6f0..7e1168be39fa 100644 --- a/drivers/acpi/acpica/utfileio.c +++ b/drivers/acpi/acpica/utfileio.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utglobal.c b/drivers/acpi/acpica/utglobal.c index 77ceac715f28..5e8df9177da4 100644 --- a/drivers/acpi/acpica/utglobal.c +++ b/drivers/acpi/acpica/utglobal.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/uthex.c b/drivers/acpi/acpica/uthex.c index 9afa9441b183..aa448278ba28 100644 --- a/drivers/acpi/acpica/uthex.c +++ b/drivers/acpi/acpica/uthex.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utids.c b/drivers/acpi/acpica/utids.c index 4b12880e5b11..27431cfc1c44 100644 --- a/drivers/acpi/acpica/utids.c +++ b/drivers/acpi/acpica/utids.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utinit.c b/drivers/acpi/acpica/utinit.c index 77120ec9ea86..e402e07b4846 100644 --- a/drivers/acpi/acpica/utinit.c +++ b/drivers/acpi/acpica/utinit.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utlock.c b/drivers/acpi/acpica/utlock.c index dc6e96547f18..089f78bbd59b 100644 --- a/drivers/acpi/acpica/utlock.c +++ b/drivers/acpi/acpica/utlock.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utmath.c b/drivers/acpi/acpica/utmath.c index d44dee6ee10a..f9ff100f0159 100644 --- a/drivers/acpi/acpica/utmath.c +++ b/drivers/acpi/acpica/utmath.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utmisc.c b/drivers/acpi/acpica/utmisc.c index 2e2bb14e1099..56bbacd576f2 100644 --- a/drivers/acpi/acpica/utmisc.c +++ b/drivers/acpi/acpica/utmisc.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utmutex.c b/drivers/acpi/acpica/utmutex.c index 82717fff9ffc..37b8b58fcd56 100644 --- a/drivers/acpi/acpica/utmutex.c +++ b/drivers/acpi/acpica/utmutex.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utobject.c b/drivers/acpi/acpica/utobject.c index dfa9009bfc87..7d83efe1ea29 100644 --- a/drivers/acpi/acpica/utobject.c +++ b/drivers/acpi/acpica/utobject.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utosi.c b/drivers/acpi/acpica/utosi.c index 685766fc6ca8..574cd3118313 100644 --- a/drivers/acpi/acpica/utosi.c +++ b/drivers/acpi/acpica/utosi.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utownerid.c b/drivers/acpi/acpica/utownerid.c index 36bec57ebd23..2959217067cb 100644 --- a/drivers/acpi/acpica/utownerid.c +++ b/drivers/acpi/acpica/utownerid.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utpredef.c b/drivers/acpi/acpica/utpredef.c index db30caff130a..29e449935a82 100644 --- a/drivers/acpi/acpica/utpredef.c +++ b/drivers/acpi/acpica/utpredef.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utprint.c b/drivers/acpi/acpica/utprint.c index 0ce3f5a0dd67..82ca9142e10d 100644 --- a/drivers/acpi/acpica/utprint.c +++ b/drivers/acpi/acpica/utprint.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utresrc.c b/drivers/acpi/acpica/utresrc.c index bc1ff820c7dd..b3505dbc715e 100644 --- a/drivers/acpi/acpica/utresrc.c +++ b/drivers/acpi/acpica/utresrc.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utstate.c b/drivers/acpi/acpica/utstate.c index 1cc97a752c15..8274cc16edc3 100644 --- a/drivers/acpi/acpica/utstate.c +++ b/drivers/acpi/acpica/utstate.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utstring.c b/drivers/acpi/acpica/utstring.c index 6dc54b3c28b0..83b6c52490dc 100644 --- a/drivers/acpi/acpica/utstring.c +++ b/drivers/acpi/acpica/utstring.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/uttrack.c b/drivers/acpi/acpica/uttrack.c index 7d0ee969d781..130dd9f96f0f 100644 --- a/drivers/acpi/acpica/uttrack.c +++ b/drivers/acpi/acpica/uttrack.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utuuid.c b/drivers/acpi/acpica/utuuid.c index 4dc33130f134..c6149a212149 100644 --- a/drivers/acpi/acpica/utuuid.c +++ b/drivers/acpi/acpica/utuuid.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utxface.c b/drivers/acpi/acpica/utxface.c index 49c873c68756..0929187bdce0 100644 --- a/drivers/acpi/acpica/utxface.c +++ b/drivers/acpi/acpica/utxface.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utxferror.c b/drivers/acpi/acpica/utxferror.c index 88ef77f3cf88..306e785f9418 100644 --- a/drivers/acpi/acpica/utxferror.c +++ b/drivers/acpi/acpica/utxferror.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utxfinit.c b/drivers/acpi/acpica/utxfinit.c index b1fd6886e439..083a76891889 100644 --- a/drivers/acpi/acpica/utxfinit.c +++ b/drivers/acpi/acpica/utxfinit.c @@ -5,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/acpica/utxfmutex.c b/drivers/acpi/acpica/utxfmutex.c index 2a0f9e04d3a4..f2606af3364c 100644 --- a/drivers/acpi/acpica/utxfmutex.c +++ b/drivers/acpi/acpica/utxfmutex.c @@ -5,7 +5,7 @@ ******************************************************************************/ /* - * Copyright (C) 2000 - 2014, Intel Corp. + * Copyright (C) 2000 - 2015, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c index 2cd7bdd6c8b3..a85ac07f3da3 100644 --- a/drivers/acpi/apei/apei-base.c +++ b/drivers/acpi/apei/apei-base.c @@ -449,7 +449,7 @@ int apei_resources_sub(struct apei_resources *resources1, } EXPORT_SYMBOL_GPL(apei_resources_sub); -static int apei_get_nvs_callback(__u64 start, __u64 size, void *data) +static int apei_get_res_callback(__u64 start, __u64 size, void *data) { struct apei_resources *resources = data; return apei_res_add(&resources->iomem, start, size); @@ -457,7 +457,15 @@ static int apei_get_nvs_callback(__u64 start, __u64 size, void *data) static int apei_get_nvs_resources(struct apei_resources *resources) { - return acpi_nvs_for_each_region(apei_get_nvs_callback, resources); + return acpi_nvs_for_each_region(apei_get_res_callback, resources); +} + +int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size, + void *data), void *data); +static int apei_get_arch_resources(struct apei_resources *resources) + +{ + return arch_apei_filter_addr(apei_get_res_callback, resources); } /* @@ -470,7 +478,7 @@ int apei_resources_request(struct apei_resources *resources, { struct apei_res *res, *res_bak = NULL; struct resource *r; - struct apei_resources nvs_resources; + struct apei_resources nvs_resources, arch_res; int rc; rc = apei_resources_sub(resources, &apei_resources_all); @@ -485,10 +493,20 @@ int apei_resources_request(struct apei_resources *resources, apei_resources_init(&nvs_resources); rc = apei_get_nvs_resources(&nvs_resources); if (rc) - goto res_fini; + goto nvs_res_fini; rc = apei_resources_sub(resources, &nvs_resources); if (rc) - goto res_fini; + goto nvs_res_fini; + + if (arch_apei_filter_addr) { + apei_resources_init(&arch_res); + rc = apei_get_arch_resources(&arch_res); + if (rc) + goto arch_res_fini; + rc = apei_resources_sub(resources, &arch_res); + if (rc) + goto arch_res_fini; + } rc = -EINVAL; list_for_each_entry(res, &resources->iomem, list) { @@ -536,7 +554,9 @@ err_unmap_iomem: break; release_mem_region(res->start, res->end - res->start); } -res_fini: +arch_res_fini: + apei_resources_fini(&arch_res); +nvs_res_fini: apei_resources_fini(&nvs_resources); return rc; } diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index c2daa85fc9f7..735db11a9b00 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -257,7 +257,7 @@ int acpi_bus_init_power(struct acpi_device *device) device->power.state = ACPI_STATE_UNKNOWN; if (!acpi_device_is_present(device)) - return 0; + return -ENXIO; result = acpi_device_get_power(device, &state); if (result) @@ -1027,7 +1027,6 @@ EXPORT_SYMBOL_GPL(acpi_subsys_freeze); static struct dev_pm_domain acpi_general_pm_domain = { .ops = { -#ifdef CONFIG_PM .runtime_suspend = acpi_subsys_runtime_suspend, .runtime_resume = acpi_subsys_runtime_resume, #ifdef CONFIG_PM_SLEEP @@ -1041,7 +1040,6 @@ static struct dev_pm_domain acpi_general_pm_domain = { .poweroff_late = acpi_subsys_suspend_late, .restore_early = acpi_subsys_resume_early, #endif -#endif }, }; diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 1b5853f384e2..14d0c89ada2a 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1,8 +1,8 @@ /* - * ec.c - ACPI Embedded Controller Driver (v2.2) + * ec.c - ACPI Embedded Controller Driver (v3) * - * Copyright (C) 2001-2014 Intel Corporation - * Author: 2014 Lv Zheng <lv.zheng@intel.com> + * Copyright (C) 2001-2015 Intel Corporation + * Author: 2014, 2015 Lv Zheng <lv.zheng@intel.com> * 2006, 2007 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com> * 2006 Denis Sadykov <denis.m.sadykov@intel.com> * 2004 Luming Yu <luming.yu@intel.com> @@ -31,6 +31,7 @@ /* Uncomment next line to get verbose printout */ /* #define DEBUG */ +#define DEBUG_REF 0 #define pr_fmt(fmt) "ACPI : EC: " fmt #include <linux/kernel.h> @@ -71,20 +72,32 @@ enum ec_command { #define ACPI_EC_DELAY 500 /* Wait 500ms max. during EC ops */ #define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ #define ACPI_EC_MSI_UDELAY 550 /* Wait 550us for MSI EC */ +#define ACPI_EC_UDELAY_POLL 1000 /* Wait 1ms for EC transaction polling */ #define ACPI_EC_CLEAR_MAX 100 /* Maximum number of events to query * when trying to clear the EC */ enum { - EC_FLAGS_QUERY_PENDING, /* Query is pending */ - EC_FLAGS_GPE_STORM, /* GPE storm detected */ + EC_FLAGS_EVENT_ENABLED, /* Event is enabled */ + EC_FLAGS_EVENT_PENDING, /* Event is pending */ + EC_FLAGS_EVENT_DETECTED, /* Event is detected */ EC_FLAGS_HANDLERS_INSTALLED, /* Handlers for GPE and * OpReg are installed */ - EC_FLAGS_BLOCKED, /* Transactions are blocked */ + EC_FLAGS_STARTED, /* Driver is started */ + EC_FLAGS_STOPPED, /* Driver is stopped */ + EC_FLAGS_COMMAND_STORM, /* GPE storms occurred to the + * current command processing */ }; #define ACPI_EC_COMMAND_POLL 0x01 /* Available for command byte */ #define ACPI_EC_COMMAND_COMPLETE 0x02 /* Completed last byte */ +#define ec_debug_ref(ec, fmt, ...) \ + do { \ + if (DEBUG_REF) \ + pr_debug("%lu: " fmt, ec->reference_count, \ + ## __VA_ARGS__); \ + } while (0) + /* ec.c is compiled in acpi namespace so this shows up as acpi.ec_delay param */ static unsigned int ec_delay __read_mostly = ACPI_EC_DELAY; module_param(ec_delay, uint, 0644); @@ -105,6 +118,7 @@ struct acpi_ec_query_handler { acpi_handle handle; void *data; u8 query_bit; + struct kref kref; }; struct transaction { @@ -117,8 +131,12 @@ struct transaction { u8 wlen; u8 rlen; u8 flags; + unsigned long timestamp; }; +static int acpi_ec_query(struct acpi_ec *ec, u8 *data); +static void advance_transaction(struct acpi_ec *ec); + struct acpi_ec *boot_ec, *first_ec; EXPORT_SYMBOL(first_ec); @@ -129,7 +147,28 @@ static int EC_FLAGS_CLEAR_ON_RESUME; /* Needs acpi_ec_clear() on boot/resume */ static int EC_FLAGS_QUERY_HANDSHAKE; /* Needs QR_EC issued when SCI_EVT set */ /* -------------------------------------------------------------------------- - * Transaction Management + * Device Flags + * -------------------------------------------------------------------------- */ + +static bool acpi_ec_started(struct acpi_ec *ec) +{ + return test_bit(EC_FLAGS_STARTED, &ec->flags) && + !test_bit(EC_FLAGS_STOPPED, &ec->flags); +} + +static bool acpi_ec_flushed(struct acpi_ec *ec) +{ + return ec->reference_count == 1; +} + +static bool acpi_ec_has_pending_event(struct acpi_ec *ec) +{ + return test_bit(EC_FLAGS_EVENT_DETECTED, &ec->flags) || + test_bit(EC_FLAGS_EVENT_PENDING, &ec->flags); +} + +/* -------------------------------------------------------------------------- + * EC Registers * -------------------------------------------------------------------------- */ static inline u8 acpi_ec_read_status(struct acpi_ec *ec) @@ -151,6 +190,7 @@ static inline u8 acpi_ec_read_data(struct acpi_ec *ec) { u8 x = inb(ec->data_addr); + ec->curr->timestamp = jiffies; pr_debug("EC_DATA(R) = 0x%2.2x\n", x); return x; } @@ -159,12 +199,14 @@ static inline void acpi_ec_write_cmd(struct acpi_ec *ec, u8 command) { pr_debug("EC_SC(W) = 0x%2.2x\n", command); outb(command, ec->command_addr); + ec->curr->timestamp = jiffies; } static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) { pr_debug("EC_DATA(W) = 0x%2.2x\n", data); outb(data, ec->data_addr); + ec->curr->timestamp = jiffies; } #ifdef DEBUG @@ -188,6 +230,203 @@ static const char *acpi_ec_cmd_string(u8 cmd) #define acpi_ec_cmd_string(cmd) "UNDEF" #endif +/* -------------------------------------------------------------------------- + * GPE Registers + * -------------------------------------------------------------------------- */ + +static inline bool acpi_ec_is_gpe_raised(struct acpi_ec *ec) +{ + acpi_event_status gpe_status = 0; + + (void)acpi_get_gpe_status(NULL, ec->gpe, &gpe_status); + return (gpe_status & ACPI_EVENT_FLAG_SET) ? true : false; +} + +static inline void acpi_ec_enable_gpe(struct acpi_ec *ec, bool open) +{ + if (open) + acpi_enable_gpe(NULL, ec->gpe); + else { + BUG_ON(ec->reference_count < 1); + acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_ENABLE); + } + if (acpi_ec_is_gpe_raised(ec)) { + /* + * On some platforms, EN=1 writes cannot trigger GPE. So + * software need to manually trigger a pseudo GPE event on + * EN=1 writes. + */ + pr_debug("***** Polling quirk *****\n"); + advance_transaction(ec); + } +} + +static inline void acpi_ec_disable_gpe(struct acpi_ec *ec, bool close) +{ + if (close) + acpi_disable_gpe(NULL, ec->gpe); + else { + BUG_ON(ec->reference_count < 1); + acpi_set_gpe(NULL, ec->gpe, ACPI_GPE_DISABLE); + } +} + +static inline void acpi_ec_clear_gpe(struct acpi_ec *ec) +{ + /* + * GPE STS is a W1C register, which means: + * 1. Software can clear it without worrying about clearing other + * GPEs' STS bits when the hardware sets them in parallel. + * 2. As long as software can ensure only clearing it when it is + * set, hardware won't set it in parallel. + * So software can clear GPE in any contexts. + * Warning: do not move the check into advance_transaction() as the + * EC commands will be sent without GPE raised. + */ + if (!acpi_ec_is_gpe_raised(ec)) + return; + acpi_clear_gpe(NULL, ec->gpe); +} + +/* -------------------------------------------------------------------------- + * Transaction Management + * -------------------------------------------------------------------------- */ + +static void acpi_ec_submit_request(struct acpi_ec *ec) +{ + ec->reference_count++; + if (ec->reference_count == 1) + acpi_ec_enable_gpe(ec, true); +} + +static void acpi_ec_complete_request(struct acpi_ec *ec) +{ + bool flushed = false; + + ec->reference_count--; + if (ec->reference_count == 0) + acpi_ec_disable_gpe(ec, true); + flushed = acpi_ec_flushed(ec); + if (flushed) + wake_up(&ec->wait); +} + +static void acpi_ec_set_storm(struct acpi_ec *ec, u8 flag) +{ + if (!test_bit(flag, &ec->flags)) { + acpi_ec_disable_gpe(ec, false); + pr_debug("+++++ Polling enabled +++++\n"); + set_bit(flag, &ec->flags); + } +} + +static void acpi_ec_clear_storm(struct acpi_ec *ec, u8 flag) +{ + if (test_bit(flag, &ec->flags)) { + clear_bit(flag, &ec->flags); + acpi_ec_enable_gpe(ec, false); + pr_debug("+++++ Polling disabled +++++\n"); + } +} + +/* + * acpi_ec_submit_flushable_request() - Increase the reference count unless + * the flush operation is not in + * progress + * @ec: the EC device + * @allow_event: whether event should be handled + * + * This function must be used before taking a new action that should hold + * the reference count. If this function returns false, then the action + * must be discarded or it will prevent the flush operation from being + * completed. + * + * During flushing, QR_EC command need to pass this check when there is a + * pending event, so that the reference count held for the pending event + * can be decreased by the completion of the QR_EC command. + */ +static bool acpi_ec_submit_flushable_request(struct acpi_ec *ec, + bool allow_event) +{ + if (!acpi_ec_started(ec)) { + if (!allow_event || !acpi_ec_has_pending_event(ec)) + return false; + } + acpi_ec_submit_request(ec); + return true; +} + +static void acpi_ec_submit_event(struct acpi_ec *ec) +{ + if (!test_bit(EC_FLAGS_EVENT_DETECTED, &ec->flags) || + !test_bit(EC_FLAGS_EVENT_ENABLED, &ec->flags)) + return; + /* Hold reference for pending event */ + if (!acpi_ec_submit_flushable_request(ec, true)) + return; + ec_debug_ref(ec, "Increase event\n"); + if (!test_and_set_bit(EC_FLAGS_EVENT_PENDING, &ec->flags)) { + pr_debug("***** Event query started *****\n"); + schedule_work(&ec->work); + return; + } + acpi_ec_complete_request(ec); + ec_debug_ref(ec, "Decrease event\n"); +} + +static void acpi_ec_complete_event(struct acpi_ec *ec) +{ + if (ec->curr->command == ACPI_EC_COMMAND_QUERY) { + clear_bit(EC_FLAGS_EVENT_PENDING, &ec->flags); + pr_debug("***** Event query stopped *****\n"); + /* Unhold reference for pending event */ + acpi_ec_complete_request(ec); + ec_debug_ref(ec, "Decrease event\n"); + /* Check if there is another SCI_EVT detected */ + acpi_ec_submit_event(ec); + } +} + +static void acpi_ec_submit_detection(struct acpi_ec *ec) +{ + /* Hold reference for query submission */ + if (!acpi_ec_submit_flushable_request(ec, false)) + return; + ec_debug_ref(ec, "Increase query\n"); + if (!test_and_set_bit(EC_FLAGS_EVENT_DETECTED, &ec->flags)) { + pr_debug("***** Event detection blocked *****\n"); + acpi_ec_submit_event(ec); + return; + } + acpi_ec_complete_request(ec); + ec_debug_ref(ec, "Decrease query\n"); +} + +static void acpi_ec_complete_detection(struct acpi_ec *ec) +{ + if (ec->curr->command == ACPI_EC_COMMAND_QUERY) { + clear_bit(EC_FLAGS_EVENT_DETECTED, &ec->flags); + pr_debug("***** Event detetion unblocked *****\n"); + /* Unhold reference for query submission */ + acpi_ec_complete_request(ec); + ec_debug_ref(ec, "Decrease query\n"); + } +} + +static void acpi_ec_enable_event(struct acpi_ec *ec) +{ + unsigned long flags; + + spin_lock_irqsave(&ec->lock, flags); + set_bit(EC_FLAGS_EVENT_ENABLED, &ec->flags); + /* + * An event may be pending even with SCI_EVT=0, so QR_EC should + * always be issued right after started. + */ + acpi_ec_submit_detection(ec); + spin_unlock_irqrestore(&ec->lock, flags); +} + static int ec_transaction_completed(struct acpi_ec *ec) { unsigned long flags; @@ -200,7 +439,7 @@ static int ec_transaction_completed(struct acpi_ec *ec) return ret; } -static bool advance_transaction(struct acpi_ec *ec) +static void advance_transaction(struct acpi_ec *ec) { struct transaction *t; u8 status; @@ -208,6 +447,12 @@ static bool advance_transaction(struct acpi_ec *ec) pr_debug("===== %s (%d) =====\n", in_interrupt() ? "IRQ" : "TASK", smp_processor_id()); + /* + * By always clearing STS before handling all indications, we can + * ensure a hardware STS 0->1 change after this clearing can always + * trigger a GPE interrupt. + */ + acpi_ec_clear_gpe(ec); status = acpi_ec_read_status(ec); t = ec->curr; if (!t) @@ -223,6 +468,7 @@ static bool advance_transaction(struct acpi_ec *ec) t->rdata[t->ri++] = acpi_ec_read_data(ec); if (t->rlen == t->ri) { t->flags |= ACPI_EC_COMMAND_COMPLETE; + acpi_ec_complete_event(ec); if (t->command == ACPI_EC_COMMAND_QUERY) pr_debug("***** Command(%s) hardware completion *****\n", acpi_ec_cmd_string(t->command)); @@ -233,25 +479,29 @@ static bool advance_transaction(struct acpi_ec *ec) } else if (t->wlen == t->wi && (status & ACPI_EC_FLAG_IBF) == 0) { t->flags |= ACPI_EC_COMMAND_COMPLETE; + acpi_ec_complete_event(ec); wakeup = true; } - return wakeup; + goto out; } else { if (EC_FLAGS_QUERY_HANDSHAKE && !(status & ACPI_EC_FLAG_SCI) && (t->command == ACPI_EC_COMMAND_QUERY)) { t->flags |= ACPI_EC_COMMAND_POLL; + acpi_ec_complete_detection(ec); t->rdata[t->ri++] = 0x00; t->flags |= ACPI_EC_COMMAND_COMPLETE; + acpi_ec_complete_event(ec); pr_debug("***** Command(%s) software completion *****\n", acpi_ec_cmd_string(t->command)); wakeup = true; } else if ((status & ACPI_EC_FLAG_IBF) == 0) { acpi_ec_write_cmd(ec, t->command); t->flags |= ACPI_EC_COMMAND_POLL; + acpi_ec_complete_detection(ec); } else goto err; - return wakeup; + goto out; } err: /* @@ -259,28 +509,27 @@ err: * otherwise will take a not handled IRQ as a false one. */ if (!(status & ACPI_EC_FLAG_SCI)) { - if (in_interrupt() && t) - ++t->irq_count; + if (in_interrupt() && t) { + if (t->irq_count < ec_storm_threshold) + ++t->irq_count; + /* Allow triggering on 0 threshold */ + if (t->irq_count == ec_storm_threshold) + acpi_ec_set_storm(ec, EC_FLAGS_COMMAND_STORM); + } } - return wakeup; +out: + if (status & ACPI_EC_FLAG_SCI) + acpi_ec_submit_detection(ec); + if (wakeup && in_interrupt()) + wake_up(&ec->wait); } static void start_transaction(struct acpi_ec *ec) { ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0; ec->curr->flags = 0; - (void)advance_transaction(ec); -} - -static int acpi_ec_sync_query(struct acpi_ec *ec, u8 *data); - -static int ec_check_sci_sync(struct acpi_ec *ec, u8 state) -{ - if (state & ACPI_EC_FLAG_SCI) { - if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) - return acpi_ec_sync_query(ec, NULL); - } - return 0; + ec->curr->timestamp = jiffies; + advance_transaction(ec); } static int ec_poll(struct acpi_ec *ec) @@ -291,20 +540,25 @@ static int ec_poll(struct acpi_ec *ec) while (repeat--) { unsigned long delay = jiffies + msecs_to_jiffies(ec_delay); + unsigned long usecs = ACPI_EC_UDELAY_POLL; do { /* don't sleep with disabled interrupts */ if (EC_FLAGS_MSI || irqs_disabled()) { - udelay(ACPI_EC_MSI_UDELAY); + usecs = ACPI_EC_MSI_UDELAY; + udelay(usecs); if (ec_transaction_completed(ec)) return 0; } else { if (wait_event_timeout(ec->wait, ec_transaction_completed(ec), - msecs_to_jiffies(1))) + usecs_to_jiffies(usecs))) return 0; } spin_lock_irqsave(&ec->lock, flags); - (void)advance_transaction(ec); + if (time_after(jiffies, + ec->curr->timestamp + + usecs_to_jiffies(usecs))) + advance_transaction(ec); spin_unlock_irqrestore(&ec->lock, flags); } while (time_before(jiffies, delay)); pr_debug("controller reset, restart transaction\n"); @@ -325,21 +579,29 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, udelay(ACPI_EC_MSI_UDELAY); /* start transaction */ spin_lock_irqsave(&ec->lock, tmp); + /* Enable GPE for command processing (IBF=0/OBF=1) */ + if (!acpi_ec_submit_flushable_request(ec, true)) { + ret = -EINVAL; + goto unlock; + } + ec_debug_ref(ec, "Increase command\n"); /* following two actions should be kept atomic */ ec->curr = t; pr_debug("***** Command(%s) started *****\n", acpi_ec_cmd_string(t->command)); start_transaction(ec); - if (ec->curr->command == ACPI_EC_COMMAND_QUERY) { - clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); - pr_debug("***** Event stopped *****\n"); - } spin_unlock_irqrestore(&ec->lock, tmp); ret = ec_poll(ec); spin_lock_irqsave(&ec->lock, tmp); + if (t->irq_count == ec_storm_threshold) + acpi_ec_clear_storm(ec, EC_FLAGS_COMMAND_STORM); pr_debug("***** Command(%s) stopped *****\n", acpi_ec_cmd_string(t->command)); ec->curr = NULL; + /* Disable GPE for command processing (IBF=0/OBF=1) */ + acpi_ec_complete_request(ec); + ec_debug_ref(ec, "Decrease command\n"); +unlock: spin_unlock_irqrestore(&ec->lock, tmp); return ret; } @@ -354,10 +616,6 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) if (t->rdata) memset(t->rdata, 0, t->rlen); mutex_lock(&ec->mutex); - if (test_bit(EC_FLAGS_BLOCKED, &ec->flags)) { - status = -EINVAL; - goto unlock; - } if (ec->global_lock) { status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); if (ACPI_FAILURE(status)) { @@ -365,26 +623,11 @@ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t) goto unlock; } } - /* disable GPE during transaction if storm is detected */ - if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { - /* It has to be disabled, so that it doesn't trigger. */ - acpi_disable_gpe(NULL, ec->gpe); - } status = acpi_ec_transaction_unlocked(ec, t); - /* check if we received SCI during transaction */ - ec_check_sci_sync(ec, acpi_ec_read_status(ec)); - if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { + if (test_bit(EC_FLAGS_COMMAND_STORM, &ec->flags)) msleep(1); - /* It is safe to enable the GPE outside of the transaction. */ - acpi_enable_gpe(NULL, ec->gpe); - } else if (t->irq_count > ec_storm_threshold) { - pr_info("GPE storm detected(%d GPEs), " - "transactions will use polling mode\n", - t->irq_count); - set_bit(EC_FLAGS_GPE_STORM, &ec->flags); - } if (ec->global_lock) acpi_release_global_lock(glk); unlock: @@ -500,7 +743,7 @@ static void acpi_ec_clear(struct acpi_ec *ec) u8 value = 0; for (i = 0; i < ACPI_EC_CLEAR_MAX; i++) { - status = acpi_ec_sync_query(ec, &value); + status = acpi_ec_query(ec, &value); if (status || !value) break; } @@ -511,6 +754,57 @@ static void acpi_ec_clear(struct acpi_ec *ec) pr_info("%d stale EC events cleared\n", i); } +static void acpi_ec_start(struct acpi_ec *ec, bool resuming) +{ + unsigned long flags; + + spin_lock_irqsave(&ec->lock, flags); + if (!test_and_set_bit(EC_FLAGS_STARTED, &ec->flags)) { + pr_debug("+++++ Starting EC +++++\n"); + /* Enable GPE for event processing (SCI_EVT=1) */ + if (!resuming) { + acpi_ec_submit_request(ec); + ec_debug_ref(ec, "Increase driver\n"); + } + pr_info("+++++ EC started +++++\n"); + } + spin_unlock_irqrestore(&ec->lock, flags); +} + +static bool acpi_ec_stopped(struct acpi_ec *ec) +{ + unsigned long flags; + bool flushed; + + spin_lock_irqsave(&ec->lock, flags); + flushed = acpi_ec_flushed(ec); + spin_unlock_irqrestore(&ec->lock, flags); + return flushed; +} + +static void acpi_ec_stop(struct acpi_ec *ec, bool suspending) +{ + unsigned long flags; + + spin_lock_irqsave(&ec->lock, flags); + if (acpi_ec_started(ec)) { + pr_debug("+++++ Stopping EC +++++\n"); + set_bit(EC_FLAGS_STOPPED, &ec->flags); + spin_unlock_irqrestore(&ec->lock, flags); + wait_event(ec->wait, acpi_ec_stopped(ec)); + spin_lock_irqsave(&ec->lock, flags); + /* Disable GPE for event processing (SCI_EVT=1) */ + if (!suspending) { + acpi_ec_complete_request(ec); + ec_debug_ref(ec, "Decrease driver\n"); + } + clear_bit(EC_FLAGS_STARTED, &ec->flags); + clear_bit(EC_FLAGS_STOPPED, &ec->flags); + pr_info("+++++ EC stopped +++++\n"); + } + spin_unlock_irqrestore(&ec->lock, flags); +} + void acpi_ec_block_transactions(void) { struct acpi_ec *ec = first_ec; @@ -520,7 +814,7 @@ void acpi_ec_block_transactions(void) mutex_lock(&ec->mutex); /* Prevent transactions from being carried out */ - set_bit(EC_FLAGS_BLOCKED, &ec->flags); + acpi_ec_stop(ec, true); mutex_unlock(&ec->mutex); } @@ -531,14 +825,11 @@ void acpi_ec_unblock_transactions(void) if (!ec) return; - mutex_lock(&ec->mutex); /* Allow transactions to be carried out again */ - clear_bit(EC_FLAGS_BLOCKED, &ec->flags); + acpi_ec_start(ec, true); if (EC_FLAGS_CLEAR_ON_RESUME) acpi_ec_clear(ec); - - mutex_unlock(&ec->mutex); } void acpi_ec_unblock_transactions_early(void) @@ -548,36 +839,33 @@ void acpi_ec_unblock_transactions_early(void) * atomic context during wakeup, so we don't need to acquire the mutex). */ if (first_ec) - clear_bit(EC_FLAGS_BLOCKED, &first_ec->flags); + acpi_ec_start(first_ec, true); } -static int acpi_ec_query_unlocked(struct acpi_ec *ec, u8 *data) +/* -------------------------------------------------------------------------- + Event Management + -------------------------------------------------------------------------- */ +static struct acpi_ec_query_handler * +acpi_ec_get_query_handler(struct acpi_ec_query_handler *handler) { - int result; - u8 d; - struct transaction t = {.command = ACPI_EC_COMMAND_QUERY, - .wdata = NULL, .rdata = &d, - .wlen = 0, .rlen = 1}; + if (handler) + kref_get(&handler->kref); + return handler; +} - if (!ec || !data) - return -EINVAL; - /* - * Query the EC to find out which _Qxx method we need to evaluate. - * Note that successful completion of the query causes the ACPI_EC_SCI - * bit to be cleared (and thus clearing the interrupt source). - */ - result = acpi_ec_transaction_unlocked(ec, &t); - if (result) - return result; - if (!d) - return -ENODATA; - *data = d; - return 0; +static void acpi_ec_query_handler_release(struct kref *kref) +{ + struct acpi_ec_query_handler *handler = + container_of(kref, struct acpi_ec_query_handler, kref); + + kfree(handler); +} + +static void acpi_ec_put_query_handler(struct acpi_ec_query_handler *handler) +{ + kref_put(&handler->kref, acpi_ec_query_handler_release); } -/* -------------------------------------------------------------------------- - Event Management - -------------------------------------------------------------------------- */ int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, acpi_handle handle, acpi_ec_query_func func, void *data) @@ -593,6 +881,7 @@ int acpi_ec_add_query_handler(struct acpi_ec *ec, u8 query_bit, handler->func = func; handler->data = data; mutex_lock(&ec->mutex); + kref_init(&handler->kref); list_add(&handler->node, &ec->list); mutex_unlock(&ec->mutex); return 0; @@ -602,15 +891,18 @@ EXPORT_SYMBOL_GPL(acpi_ec_add_query_handler); void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit) { struct acpi_ec_query_handler *handler, *tmp; + LIST_HEAD(free_list); mutex_lock(&ec->mutex); list_for_each_entry_safe(handler, tmp, &ec->list, node) { if (query_bit == handler->query_bit) { - list_del(&handler->node); - kfree(handler); + list_del_init(&handler->node); + list_add(&handler->node, &free_list); } } mutex_unlock(&ec->mutex); + list_for_each_entry(handler, &free_list, node) + acpi_ec_put_query_handler(handler); } EXPORT_SYMBOL_GPL(acpi_ec_remove_query_handler); @@ -626,59 +918,58 @@ static void acpi_ec_run(void *cxt) else if (handler->handle) acpi_evaluate_object(handler->handle, NULL, NULL, NULL); pr_debug("##### Query(0x%02x) stopped #####\n", handler->query_bit); - kfree(handler); + acpi_ec_put_query_handler(handler); } -static int acpi_ec_sync_query(struct acpi_ec *ec, u8 *data) +static int acpi_ec_query(struct acpi_ec *ec, u8 *data) { u8 value = 0; - int status; - struct acpi_ec_query_handler *handler, *copy; + int result; + acpi_status status; + struct acpi_ec_query_handler *handler; + struct transaction t = {.command = ACPI_EC_COMMAND_QUERY, + .wdata = NULL, .rdata = &value, + .wlen = 0, .rlen = 1}; - status = acpi_ec_query_unlocked(ec, &value); + /* + * Query the EC to find out which _Qxx method we need to evaluate. + * Note that successful completion of the query causes the ACPI_EC_SCI + * bit to be cleared (and thus clearing the interrupt source). + */ + result = acpi_ec_transaction(ec, &t); + if (result) + return result; if (data) *data = value; - if (status) - return status; + if (!value) + return -ENODATA; + mutex_lock(&ec->mutex); list_for_each_entry(handler, &ec->list, node) { if (value == handler->query_bit) { /* have custom handler for this bit */ - copy = kmalloc(sizeof(*handler), GFP_KERNEL); - if (!copy) - return -ENOMEM; - memcpy(copy, handler, sizeof(*copy)); + handler = acpi_ec_get_query_handler(handler); pr_debug("##### Query(0x%02x) scheduled #####\n", handler->query_bit); - return acpi_os_execute((copy->func) ? + status = acpi_os_execute((handler->func) ? OSL_NOTIFY_HANDLER : OSL_GPE_HANDLER, - acpi_ec_run, copy); + acpi_ec_run, handler); + if (ACPI_FAILURE(status)) + result = -EBUSY; + break; } } - return 0; -} - -static void acpi_ec_gpe_query(void *ec_cxt) -{ - struct acpi_ec *ec = ec_cxt; - - if (!ec) - return; - mutex_lock(&ec->mutex); - acpi_ec_sync_query(ec, NULL); mutex_unlock(&ec->mutex); + return result; } -static int ec_check_sci(struct acpi_ec *ec, u8 state) +static void acpi_ec_gpe_poller(struct work_struct *work) { - if (state & ACPI_EC_FLAG_SCI) { - if (!test_and_set_bit(EC_FLAGS_QUERY_PENDING, &ec->flags)) { - pr_debug("***** Event started *****\n"); - return acpi_os_execute(OSL_NOTIFY_HANDLER, - acpi_ec_gpe_query, ec); - } - } - return 0; + struct acpi_ec *ec = container_of(work, struct acpi_ec, work); + + pr_debug("***** Event poller started *****\n"); + acpi_ec_query(ec, NULL); + pr_debug("***** Event poller stopped *****\n"); } static u32 acpi_ec_gpe_handler(acpi_handle gpe_device, @@ -688,11 +979,9 @@ static u32 acpi_ec_gpe_handler(acpi_handle gpe_device, struct acpi_ec *ec = data; spin_lock_irqsave(&ec->lock, flags); - if (advance_transaction(ec)) - wake_up(&ec->wait); + advance_transaction(ec); spin_unlock_irqrestore(&ec->lock, flags); - ec_check_sci(ec, acpi_ec_read_status(ec)); - return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE; + return ACPI_INTERRUPT_HANDLED; } /* -------------------------------------------------------------------------- @@ -750,11 +1039,11 @@ static struct acpi_ec *make_acpi_ec(void) if (!ec) return NULL; - ec->flags = 1 << EC_FLAGS_QUERY_PENDING; mutex_init(&ec->mutex); init_waitqueue_head(&ec->wait); INIT_LIST_HEAD(&ec->list); spin_lock_init(&ec->lock); + INIT_WORK(&ec->work, acpi_ec_gpe_poller); return ec; } @@ -810,13 +1099,13 @@ static int ec_install_handlers(struct acpi_ec *ec) if (test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags)) return 0; - status = acpi_install_gpe_handler(NULL, ec->gpe, + status = acpi_install_gpe_raw_handler(NULL, ec->gpe, ACPI_GPE_EDGE_TRIGGERED, &acpi_ec_gpe_handler, ec); if (ACPI_FAILURE(status)) return -ENODEV; - acpi_enable_gpe(NULL, ec->gpe); + acpi_ec_start(ec, false); status = acpi_install_address_space_handler(ec->handle, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler, @@ -831,7 +1120,7 @@ static int ec_install_handlers(struct acpi_ec *ec) pr_err("Fail in evaluating the _REG object" " of EC device. Broken bios is suspected.\n"); } else { - acpi_disable_gpe(NULL, ec->gpe); + acpi_ec_stop(ec, false); acpi_remove_gpe_handler(NULL, ec->gpe, &acpi_ec_gpe_handler); return -ENODEV; @@ -846,7 +1135,7 @@ static void ec_remove_handlers(struct acpi_ec *ec) { if (!test_bit(EC_FLAGS_HANDLERS_INSTALLED, &ec->flags)) return; - acpi_disable_gpe(NULL, ec->gpe); + acpi_ec_stop(ec, false); if (ACPI_FAILURE(acpi_remove_address_space_handler(ec->handle, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler))) pr_err("failed to remove space handler\n"); @@ -900,14 +1189,11 @@ static int acpi_ec_add(struct acpi_device *device) ret = ec_install_handlers(ec); /* EC is fully operational, allow queries */ - clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); + acpi_ec_enable_event(ec); /* Clear stale _Q events if hardware might require that */ - if (EC_FLAGS_CLEAR_ON_RESUME) { - mutex_lock(&ec->mutex); + if (EC_FLAGS_CLEAR_ON_RESUME) acpi_ec_clear(ec); - mutex_unlock(&ec->mutex); - } return ret; } diff --git a/drivers/acpi/event.c b/drivers/acpi/event.c index ef2d730734dc..e24ea4e796e4 100644 --- a/drivers/acpi/event.c +++ b/drivers/acpi/event.c @@ -100,7 +100,6 @@ int acpi_bus_generate_netlink_event(const char *device_class, struct acpi_genl_event *event; void *msg_header; int size; - int result; /* allocate memory */ size = nla_total_size(sizeof(struct acpi_genl_event)) + @@ -137,11 +136,7 @@ int acpi_bus_generate_netlink_event(const char *device_class, event->data = data; /* send multicast genetlink message */ - result = genlmsg_end(skb, msg_header); - if (result < 0) { - nlmsg_free(skb); - return result; - } + genlmsg_end(skb, msg_header); genlmsg_multicast(&acpi_event_genl_family, skb, 0, 0, GFP_ATOMIC); return 0; diff --git a/drivers/acpi/int340x_thermal.c b/drivers/acpi/int340x_thermal.c index a27d31d1ba24..9dcf83682e36 100644 --- a/drivers/acpi/int340x_thermal.c +++ b/drivers/acpi/int340x_thermal.c @@ -14,10 +14,10 @@ #include "internal.h" -#define DO_ENUMERATION 0x01 +#define INT3401_DEVICE 0X01 static const struct acpi_device_id int340x_thermal_device_ids[] = { - {"INT3400", DO_ENUMERATION }, - {"INT3401"}, + {"INT3400"}, + {"INT3401", INT3401_DEVICE}, {"INT3402"}, {"INT3403"}, {"INT3404"}, @@ -34,7 +34,10 @@ 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) - if (id->driver_data == DO_ENUMERATION) + acpi_create_platform_device(adev); +#elif defined(INTEL_SOC_DTS_THERMAL) || defined(INTEL_SOC_DTS_THERMAL_MODULE) + /* Intel SoC DTS thermal driver needs INT3401 to set IRQ descriptor */ + if (id->driver_data == INT3401_DEVICE) acpi_create_platform_device(adev); #endif return 1; diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 163e82f536fa..56b321aa2b1c 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -35,6 +35,13 @@ void acpi_int340x_thermal_init(void); int acpi_sysfs_init(void); void acpi_container_init(void); void acpi_memory_hotplug_init(void); +#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC +int acpi_ioapic_add(struct acpi_pci_root *root); +int acpi_ioapic_remove(struct acpi_pci_root *root); +#else +static inline int acpi_ioapic_add(struct acpi_pci_root *root) { return 0; } +static inline int acpi_ioapic_remove(struct acpi_pci_root *root) { return 0; } +#endif #ifdef CONFIG_ACPI_DOCK void register_dock_dependent_device(struct acpi_device *adev, acpi_handle dshandle); @@ -68,6 +75,8 @@ static inline void acpi_debugfs_init(void) { return; } #endif void acpi_lpss_init(void); +void acpi_apd_init(void); + acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src); bool acpi_queue_hotplug_work(struct work_struct *work); void acpi_device_hotplug(struct acpi_device *adev, u32 src); @@ -122,11 +131,13 @@ struct acpi_ec { unsigned long data_addr; unsigned long global_lock; unsigned long flags; + unsigned long reference_count; struct mutex mutex; wait_queue_head_t wait; struct list_head list; struct transaction *curr; spinlock_t lock; + struct work_struct work; }; extern struct acpi_ec *first_ec; diff --git a/drivers/acpi/ioapic.c b/drivers/acpi/ioapic.c new file mode 100644 index 000000000000..ccdc8db16bb8 --- /dev/null +++ b/drivers/acpi/ioapic.c @@ -0,0 +1,229 @@ +/* + * IOAPIC/IOxAPIC/IOSAPIC driver + * + * Copyright (C) 2009 Fujitsu Limited. + * (c) Copyright 2009 Hewlett-Packard Development Company, L.P. + * + * Copyright (C) 2014 Intel Corporation + * + * 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. + * + * Based on original drivers/pci/ioapic.c + * Yinghai Lu <yinghai@kernel.org> + * Jiang Liu <jiang.liu@intel.com> + */ + +/* + * This driver manages I/O APICs added by hotplug after boot. + * We try to claim all I/O APIC devices, but those present at boot were + * registered when we parsed the ACPI MADT. + */ + +#define pr_fmt(fmt) "ACPI : IOAPIC: " fmt + +#include <linux/slab.h> +#include <linux/acpi.h> +#include <linux/pci.h> +#include <acpi/acpi.h> + +struct acpi_pci_ioapic { + acpi_handle root_handle; + acpi_handle handle; + u32 gsi_base; + struct resource res; + struct pci_dev *pdev; + struct list_head list; +}; + +static LIST_HEAD(ioapic_list); +static DEFINE_MUTEX(ioapic_list_lock); + +static acpi_status setup_res(struct acpi_resource *acpi_res, void *data) +{ + struct resource *res = data; + struct resource_win win; + + res->flags = 0; + if (acpi_dev_filter_resource_type(acpi_res, IORESOURCE_MEM) == 0) + return AE_OK; + + if (!acpi_dev_resource_memory(acpi_res, res)) { + if (acpi_dev_resource_address_space(acpi_res, &win) || + acpi_dev_resource_ext_address_space(acpi_res, &win)) + *res = win.res; + } + if ((res->flags & IORESOURCE_PREFETCH) || + (res->flags & IORESOURCE_DISABLED)) + res->flags = 0; + + return AE_CTRL_TERMINATE; +} + +static bool acpi_is_ioapic(acpi_handle handle, char **type) +{ + acpi_status status; + struct acpi_device_info *info; + char *hid = NULL; + bool match = false; + + if (!acpi_has_method(handle, "_GSB")) + return false; + + status = acpi_get_object_info(handle, &info); + if (ACPI_SUCCESS(status)) { + if (info->valid & ACPI_VALID_HID) + hid = info->hardware_id.string; + if (hid) { + if (strcmp(hid, "ACPI0009") == 0) { + *type = "IOxAPIC"; + match = true; + } else if (strcmp(hid, "ACPI000A") == 0) { + *type = "IOAPIC"; + match = true; + } + } + kfree(info); + } + + return match; +} + +static acpi_status handle_ioapic_add(acpi_handle handle, u32 lvl, + void *context, void **rv) +{ + acpi_status status; + unsigned long long gsi_base; + struct acpi_pci_ioapic *ioapic; + struct pci_dev *dev = NULL; + struct resource *res = NULL; + char *type = NULL; + + if (!acpi_is_ioapic(handle, &type)) + return AE_OK; + + mutex_lock(&ioapic_list_lock); + list_for_each_entry(ioapic, &ioapic_list, list) + if (ioapic->handle == handle) { + mutex_unlock(&ioapic_list_lock); + return AE_OK; + } + + status = acpi_evaluate_integer(handle, "_GSB", NULL, &gsi_base); + if (ACPI_FAILURE(status)) { + acpi_handle_warn(handle, "failed to evaluate _GSB method\n"); + goto exit; + } + + ioapic = kzalloc(sizeof(*ioapic), GFP_KERNEL); + if (!ioapic) { + pr_err("cannot allocate memory for new IOAPIC\n"); + goto exit; + } else { + ioapic->root_handle = (acpi_handle)context; + ioapic->handle = handle; + ioapic->gsi_base = (u32)gsi_base; + INIT_LIST_HEAD(&ioapic->list); + } + + if (acpi_ioapic_registered(handle, (u32)gsi_base)) + goto done; + + dev = acpi_get_pci_dev(handle); + if (dev && pci_resource_len(dev, 0)) { + if (pci_enable_device(dev) < 0) + goto exit_put; + pci_set_master(dev); + if (pci_request_region(dev, 0, type)) + goto exit_disable; + res = &dev->resource[0]; + ioapic->pdev = dev; + } else { + pci_dev_put(dev); + dev = NULL; + + res = &ioapic->res; + acpi_walk_resources(handle, METHOD_NAME__CRS, setup_res, res); + if (res->flags == 0) { + acpi_handle_warn(handle, "failed to get resource\n"); + goto exit_free; + } else if (request_resource(&iomem_resource, res)) { + acpi_handle_warn(handle, "failed to insert resource\n"); + goto exit_free; + } + } + + if (acpi_register_ioapic(handle, res->start, (u32)gsi_base)) { + acpi_handle_warn(handle, "failed to register IOAPIC\n"); + goto exit_release; + } +done: + list_add(&ioapic->list, &ioapic_list); + mutex_unlock(&ioapic_list_lock); + + if (dev) + dev_info(&dev->dev, "%s at %pR, GSI %u\n", + type, res, (u32)gsi_base); + else + acpi_handle_info(handle, "%s at %pR, GSI %u\n", + type, res, (u32)gsi_base); + + return AE_OK; + +exit_release: + if (dev) + pci_release_region(dev, 0); + else + release_resource(res); +exit_disable: + if (dev) + pci_disable_device(dev); +exit_put: + pci_dev_put(dev); +exit_free: + kfree(ioapic); +exit: + mutex_unlock(&ioapic_list_lock); + *(acpi_status *)rv = AE_ERROR; + return AE_OK; +} + +int acpi_ioapic_add(struct acpi_pci_root *root) +{ + acpi_status status, retval = AE_OK; + + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, root->device->handle, + UINT_MAX, handle_ioapic_add, NULL, + root->device->handle, (void **)&retval); + + return ACPI_SUCCESS(status) && ACPI_SUCCESS(retval) ? 0 : -ENODEV; +} + +int acpi_ioapic_remove(struct acpi_pci_root *root) +{ + int retval = 0; + struct acpi_pci_ioapic *ioapic, *tmp; + + mutex_lock(&ioapic_list_lock); + list_for_each_entry_safe(ioapic, tmp, &ioapic_list, list) { + if (root->device->handle != ioapic->root_handle) + continue; + + if (acpi_unregister_ioapic(ioapic->handle, ioapic->gsi_base)) + retval = -EBUSY; + + if (ioapic->pdev) { + pci_release_region(ioapic->pdev, 0); + pci_disable_device(ioapic->pdev); + pci_dev_put(ioapic->pdev); + } else if (ioapic->res.flags && ioapic->res.parent) { + release_resource(&ioapic->res); + } + list_del(&ioapic->list); + kfree(ioapic); + } + mutex_unlock(&ioapic_list_lock); + + return retval; +} diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 24b5476449a1..1333cbdc3ea2 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -177,12 +177,7 @@ static int __init slit_valid(struct acpi_table_slit *slit) static int __init acpi_parse_slit(struct acpi_table_header *table) { - struct acpi_table_slit *slit; - - if (!table) - return -EINVAL; - - slit = (struct acpi_table_slit *)table; + struct acpi_table_slit *slit = (struct acpi_table_slit *)table; if (!slit_valid(slit)) { printk(KERN_INFO "ACPI: SLIT table looks invalid. Not used.\n"); @@ -260,11 +255,8 @@ acpi_parse_memory_affinity(struct acpi_subtable_header * header, static int __init acpi_parse_srat(struct acpi_table_header *table) { - struct acpi_table_srat *srat; - if (!table) - return -EINVAL; + struct acpi_table_srat *srat = (struct acpi_table_srat *)table; - srat = (struct acpi_table_srat *)table; acpi_srat_revision = srat->header.revision; /* Real work done in acpi_table_parse_srat below. */ diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 5277a0ee5704..e7f718d6918a 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -485,14 +485,6 @@ void acpi_pci_irq_disable(struct pci_dev *dev) if (!pin || !dev->irq_managed || dev->irq <= 0) return; - /* Keep IOAPIC pin configuration when suspending */ - if (dev->dev.power.is_prepared) - return; -#ifdef CONFIG_PM - if (dev->dev.power.runtime_status == RPM_SUSPENDING) - return; -#endif - entry = acpi_pci_irq_lookup(dev, pin); if (!entry) return; @@ -512,7 +504,7 @@ void acpi_pci_irq_disable(struct pci_dev *dev) dev_dbg(&dev->dev, "PCI INT %c disabled\n", pin_name(pin)); if (gsi >= 0) { acpi_unregister_gsi(gsi); - dev->irq = 0; dev->irq_managed = 0; + dev->irq = 0; } } diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index c6bcb8c719d8..68a5f712cd19 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -112,10 +112,10 @@ get_root_bridge_busnr_callback(struct acpi_resource *resource, void *data) if (ACPI_FAILURE(status)) return AE_OK; - if ((address.address_length > 0) && + if ((address.address.address_length > 0) && (address.resource_type == ACPI_BUS_NUMBER_RANGE)) { - res->start = address.minimum; - res->end = address.minimum + address.address_length - 1; + res->start = address.address.minimum; + res->end = address.address.minimum + address.address.address_length - 1; } return AE_OK; @@ -621,6 +621,7 @@ static int acpi_pci_root_add(struct acpi_device *device, if (hotadd) { pcibios_resource_survey_bus(root->bus); pci_assign_unassigned_root_bus_resources(root->bus); + acpi_ioapic_add(root); } pci_lock_rescan_remove(); @@ -644,6 +645,8 @@ static void acpi_pci_root_remove(struct acpi_device *device) pci_stop_root_bus(root->bus); + WARN_ON(acpi_ioapic_remove(root)); + device_set_run_wake(root->bus->bridge, false); pci_acpi_remove_bus_pm_notifier(device); diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 342942f90a10..7962651cdbd4 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -4,6 +4,10 @@ * * Alex Chiang <achiang@hp.com> * - Unified x86/ia64 implementations + * + * I/O APIC hotplug support + * Yinghai Lu <yinghai@kernel.org> + * Jiang Liu <jiang.liu@intel.com> */ #include <linux/export.h> #include <linux/acpi.h> @@ -12,6 +16,21 @@ #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME("processor_core"); +static struct acpi_table_madt *get_madt_table(void) +{ + static struct acpi_table_madt *madt; + static int read_madt; + + if (!read_madt) { + if (ACPI_FAILURE(acpi_get_table(ACPI_SIG_MADT, 0, + (struct acpi_table_header **)&madt))) + madt = NULL; + read_madt++; + } + + return madt; +} + static int map_lapic_id(struct acpi_subtable_header *entry, u32 acpi_id, int *apic_id) { @@ -67,19 +86,12 @@ static int map_lsapic_id(struct acpi_subtable_header *entry, static int map_madt_entry(int type, u32 acpi_id) { unsigned long madt_end, entry; - static struct acpi_table_madt *madt; - static int read_madt; - int apic_id = -1; - - if (!read_madt) { - if (ACPI_FAILURE(acpi_get_table(ACPI_SIG_MADT, 0, - (struct acpi_table_header **)&madt))) - madt = NULL; - read_madt++; - } + int phys_id = -1; /* CPU hardware ID */ + struct acpi_table_madt *madt; + madt = get_madt_table(); if (!madt) - return apic_id; + return phys_id; entry = (unsigned long)madt; madt_end = entry + madt->header.length; @@ -91,18 +103,18 @@ static int map_madt_entry(int type, u32 acpi_id) struct acpi_subtable_header *header = (struct acpi_subtable_header *)entry; if (header->type == ACPI_MADT_TYPE_LOCAL_APIC) { - if (!map_lapic_id(header, acpi_id, &apic_id)) + if (!map_lapic_id(header, acpi_id, &phys_id)) break; } else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC) { - if (!map_x2apic_id(header, type, acpi_id, &apic_id)) + if (!map_x2apic_id(header, type, acpi_id, &phys_id)) break; } else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) { - if (!map_lsapic_id(header, type, acpi_id, &apic_id)) + if (!map_lsapic_id(header, type, acpi_id, &phys_id)) break; } entry += header->length; } - return apic_id; + return phys_id; } static int map_mat_entry(acpi_handle handle, int type, u32 acpi_id) @@ -110,7 +122,7 @@ static int map_mat_entry(acpi_handle handle, int type, u32 acpi_id) struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; struct acpi_subtable_header *header; - int apic_id = -1; + int phys_id = -1; if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer))) goto exit; @@ -126,38 +138,38 @@ static int map_mat_entry(acpi_handle handle, int type, u32 acpi_id) header = (struct acpi_subtable_header *)obj->buffer.pointer; if (header->type == ACPI_MADT_TYPE_LOCAL_APIC) - map_lapic_id(header, acpi_id, &apic_id); + map_lapic_id(header, acpi_id, &phys_id); else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) - map_lsapic_id(header, type, acpi_id, &apic_id); + map_lsapic_id(header, type, acpi_id, &phys_id); else if (header->type == ACPI_MADT_TYPE_LOCAL_X2APIC) - map_x2apic_id(header, type, acpi_id, &apic_id); + map_x2apic_id(header, type, acpi_id, &phys_id); exit: kfree(buffer.pointer); - return apic_id; + return phys_id; } -int acpi_get_apicid(acpi_handle handle, int type, u32 acpi_id) +int acpi_get_phys_id(acpi_handle handle, int type, u32 acpi_id) { - int apic_id; + int phys_id; - apic_id = map_mat_entry(handle, type, acpi_id); - if (apic_id == -1) - apic_id = map_madt_entry(type, acpi_id); + phys_id = map_mat_entry(handle, type, acpi_id); + if (phys_id == -1) + phys_id = map_madt_entry(type, acpi_id); - return apic_id; + return phys_id; } -int acpi_map_cpuid(int apic_id, u32 acpi_id) +int acpi_map_cpuid(int phys_id, u32 acpi_id) { #ifdef CONFIG_SMP int i; #endif - if (apic_id == -1) { + if (phys_id == -1) { /* * On UP processor, there is no _MAT or MADT table. - * So above apic_id is always set to -1. + * So above phys_id is always set to -1. * * BIOS may define multiple CPU handles even for UP processor. * For example, @@ -170,7 +182,7 @@ int acpi_map_cpuid(int apic_id, u32 acpi_id) * Processor (CPU3, 0x03, 0x00000410, 0x06) {} * } * - * Ignores apic_id and always returns 0 for the processor + * Ignores phys_id and always returns 0 for the processor * handle with acpi id 0 if nr_cpu_ids is 1. * This should be the case if SMP tables are not found. * Return -1 for other CPU's handle. @@ -178,28 +190,121 @@ int acpi_map_cpuid(int apic_id, u32 acpi_id) if (nr_cpu_ids <= 1 && acpi_id == 0) return acpi_id; else - return apic_id; + return phys_id; } #ifdef CONFIG_SMP for_each_possible_cpu(i) { - if (cpu_physical_id(i) == apic_id) + if (cpu_physical_id(i) == phys_id) return i; } #else /* In UP kernel, only processor 0 is valid */ - if (apic_id == 0) - return apic_id; + if (phys_id == 0) + return phys_id; #endif return -1; } int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id) { - int apic_id; + int phys_id; - apic_id = acpi_get_apicid(handle, type, acpi_id); + phys_id = acpi_get_phys_id(handle, type, acpi_id); - return acpi_map_cpuid(apic_id, acpi_id); + return acpi_map_cpuid(phys_id, acpi_id); } EXPORT_SYMBOL_GPL(acpi_get_cpuid); + +#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC +static int get_ioapic_id(struct acpi_subtable_header *entry, u32 gsi_base, + u64 *phys_addr, int *ioapic_id) +{ + struct acpi_madt_io_apic *ioapic = (struct acpi_madt_io_apic *)entry; + + if (ioapic->global_irq_base != gsi_base) + return 0; + + *phys_addr = ioapic->address; + *ioapic_id = ioapic->id; + return 1; +} + +static int parse_madt_ioapic_entry(u32 gsi_base, u64 *phys_addr) +{ + struct acpi_subtable_header *hdr; + unsigned long madt_end, entry; + struct acpi_table_madt *madt; + int apic_id = -1; + + madt = get_madt_table(); + if (!madt) + return apic_id; + + entry = (unsigned long)madt; + madt_end = entry + madt->header.length; + + /* Parse all entries looking for a match. */ + entry += sizeof(struct acpi_table_madt); + while (entry + sizeof(struct acpi_subtable_header) < madt_end) { + hdr = (struct acpi_subtable_header *)entry; + if (hdr->type == ACPI_MADT_TYPE_IO_APIC && + get_ioapic_id(hdr, gsi_base, phys_addr, &apic_id)) + break; + else + entry += hdr->length; + } + + return apic_id; +} + +static int parse_mat_ioapic_entry(acpi_handle handle, u32 gsi_base, + u64 *phys_addr) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_subtable_header *header; + union acpi_object *obj; + int apic_id = -1; + + if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer))) + goto exit; + + if (!buffer.length || !buffer.pointer) + goto exit; + + obj = buffer.pointer; + if (obj->type != ACPI_TYPE_BUFFER || + obj->buffer.length < sizeof(struct acpi_subtable_header)) + goto exit; + + header = (struct acpi_subtable_header *)obj->buffer.pointer; + if (header->type == ACPI_MADT_TYPE_IO_APIC) + get_ioapic_id(header, gsi_base, phys_addr, &apic_id); + +exit: + kfree(buffer.pointer); + return apic_id; +} + +/** + * acpi_get_ioapic_id - Get IOAPIC ID and physical address matching @gsi_base + * @handle: ACPI object for IOAPIC device + * @gsi_base: GSI base to match with + * @phys_addr: Pointer to store physical address of matching IOAPIC record + * + * Walk resources returned by ACPI_MAT method, then ACPI MADT table, to search + * for an ACPI IOAPIC record matching @gsi_base. + * Return IOAPIC id and store physical address in @phys_addr if found a match, + * otherwise return <0. + */ +int acpi_get_ioapic_id(acpi_handle handle, u32 gsi_base, u64 *phys_addr) +{ + int apic_id; + + apic_id = parse_mat_ioapic_entry(handle, gsi_base, phys_addr); + if (apic_id == -1) + apic_id = parse_madt_ioapic_entry(gsi_base, phys_addr); + + return apic_id; +} +#endif /* CONFIG_ACPI_HOTPLUG_IOAPIC */ diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 87b704e41877..c256bd7fbd78 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -681,15 +681,13 @@ static int acpi_idle_bm_check(void) } /** - * acpi_idle_do_entry - a helper function that does C2 and C3 type entry + * acpi_idle_do_entry - enter idle state using the appropriate method * @cx: cstate data * * Caller disables interrupt before call and enables interrupt after return. */ -static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx) +static void acpi_idle_do_entry(struct acpi_processor_cx *cx) { - /* Don't trace irqs off for idle */ - stop_critical_timings(); if (cx->entry_method == ACPI_CSTATE_FFH) { /* Call into architectural FFH based C-state */ acpi_processor_ffh_cstate_enter(cx); @@ -703,38 +701,9 @@ static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx) gets asserted in time to freeze execution properly. */ inl(acpi_gbl_FADT.xpm_timer_block.address); } - start_critical_timings(); } /** - * acpi_idle_enter_c1 - enters an ACPI C1 state-type - * @dev: the target CPU - * @drv: cpuidle driver containing cpuidle state info - * @index: index of target state - * - * This is equivalent to the HALT instruction. - */ -static int acpi_idle_enter_c1(struct cpuidle_device *dev, - struct cpuidle_driver *drv, int index) -{ - struct acpi_processor *pr; - struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); - - pr = __this_cpu_read(processors); - - if (unlikely(!pr)) - return -EINVAL; - - lapic_timer_state_broadcast(pr, cx, 1); - acpi_idle_do_entry(cx); - - lapic_timer_state_broadcast(pr, cx, 0); - - return index; -} - - -/** * acpi_idle_play_dead - enters an ACPI state for long-term idle (i.e. off-lining) * @dev: the target CPU * @index: the index of suggested state @@ -761,47 +730,11 @@ static int acpi_idle_play_dead(struct cpuidle_device *dev, int index) return 0; } -/** - * acpi_idle_enter_simple - enters an ACPI state without BM handling - * @dev: the target CPU - * @drv: cpuidle driver with cpuidle state information - * @index: the index of suggested state - */ -static int acpi_idle_enter_simple(struct cpuidle_device *dev, - struct cpuidle_driver *drv, int index) +static bool acpi_idle_fallback_to_c1(struct acpi_processor *pr) { - struct acpi_processor *pr; - struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); - - pr = __this_cpu_read(processors); - - if (unlikely(!pr)) - return -EINVAL; - -#ifdef CONFIG_HOTPLUG_CPU - if ((cx->type != ACPI_STATE_C1) && (num_online_cpus() > 1) && - !pr->flags.has_cst && - !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED)) - return acpi_idle_enter_c1(dev, drv, CPUIDLE_DRIVER_STATE_START); -#endif - - /* - * Must be done before busmaster disable as we might need to - * access HPET ! - */ - lapic_timer_state_broadcast(pr, cx, 1); - - if (cx->type == ACPI_STATE_C3) - ACPI_FLUSH_CPU_CACHE(); - - /* Tell the scheduler that we are going deep-idle: */ - sched_clock_idle_sleep_event(); - acpi_idle_do_entry(cx); - - sched_clock_idle_wakeup_event(0); - - lapic_timer_state_broadcast(pr, cx, 0); - return index; + return IS_ENABLED(CONFIG_HOTPLUG_CPU) && num_online_cpus() > 1 && + !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED) && + !pr->flags.has_cst; } static int c3_cpu_count; @@ -809,44 +742,14 @@ static DEFINE_RAW_SPINLOCK(c3_lock); /** * acpi_idle_enter_bm - enters C3 with proper BM handling - * @dev: the target CPU - * @drv: cpuidle driver containing state data - * @index: the index of suggested state - * - * If BM is detected, the deepest non-C3 idle state is entered instead. + * @pr: Target processor + * @cx: Target state context */ -static int acpi_idle_enter_bm(struct cpuidle_device *dev, - struct cpuidle_driver *drv, int index) +static void acpi_idle_enter_bm(struct acpi_processor *pr, + struct acpi_processor_cx *cx) { - struct acpi_processor *pr; - struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); - - pr = __this_cpu_read(processors); - - if (unlikely(!pr)) - return -EINVAL; - -#ifdef CONFIG_HOTPLUG_CPU - if ((cx->type != ACPI_STATE_C1) && (num_online_cpus() > 1) && - !pr->flags.has_cst && - !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED)) - return acpi_idle_enter_c1(dev, drv, CPUIDLE_DRIVER_STATE_START); -#endif - - if (!cx->bm_sts_skip && acpi_idle_bm_check()) { - if (drv->safe_state_index >= 0) { - return drv->states[drv->safe_state_index].enter(dev, - drv, drv->safe_state_index); - } else { - acpi_safe_halt(); - return -EBUSY; - } - } - acpi_unlazy_tlb(smp_processor_id()); - /* Tell the scheduler that we are going deep-idle: */ - sched_clock_idle_sleep_event(); /* * Must be done before busmaster disable as we might need to * access HPET ! @@ -856,37 +759,71 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, /* * disable bus master * bm_check implies we need ARB_DIS - * !bm_check implies we need cache flush * bm_control implies whether we can do ARB_DIS * * That leaves a case where bm_check is set and bm_control is * not set. In that case we cannot do much, we enter C3 * without doing anything. */ - if (pr->flags.bm_check && pr->flags.bm_control) { + if (pr->flags.bm_control) { raw_spin_lock(&c3_lock); c3_cpu_count++; /* Disable bus master arbitration when all CPUs are in C3 */ if (c3_cpu_count == num_online_cpus()) acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 1); raw_spin_unlock(&c3_lock); - } else if (!pr->flags.bm_check) { - ACPI_FLUSH_CPU_CACHE(); } acpi_idle_do_entry(cx); /* Re-enable bus master arbitration */ - if (pr->flags.bm_check && pr->flags.bm_control) { + if (pr->flags.bm_control) { raw_spin_lock(&c3_lock); acpi_write_bit_register(ACPI_BITREG_ARB_DISABLE, 0); c3_cpu_count--; raw_spin_unlock(&c3_lock); } - sched_clock_idle_wakeup_event(0); + lapic_timer_state_broadcast(pr, cx, 0); +} + +static int acpi_idle_enter(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int index) +{ + struct acpi_processor_cx *cx = per_cpu(acpi_cstate[index], dev->cpu); + struct acpi_processor *pr; + + pr = __this_cpu_read(processors); + if (unlikely(!pr)) + return -EINVAL; + + if (cx->type != ACPI_STATE_C1) { + if (acpi_idle_fallback_to_c1(pr)) { + index = CPUIDLE_DRIVER_STATE_START; + cx = per_cpu(acpi_cstate[index], dev->cpu); + } else if (cx->type == ACPI_STATE_C3 && pr->flags.bm_check) { + if (cx->bm_sts_skip || !acpi_idle_bm_check()) { + acpi_idle_enter_bm(pr, cx); + return index; + } else if (drv->safe_state_index >= 0) { + index = drv->safe_state_index; + cx = per_cpu(acpi_cstate[index], dev->cpu); + } else { + acpi_safe_halt(); + return -EBUSY; + } + } + } + + lapic_timer_state_broadcast(pr, cx, 1); + + if (cx->type == ACPI_STATE_C3) + ACPI_FLUSH_CPU_CACHE(); + + acpi_idle_do_entry(cx); lapic_timer_state_broadcast(pr, cx, 0); + return index; } @@ -981,27 +918,12 @@ static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr) strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN); state->exit_latency = cx->latency; state->target_residency = cx->latency * latency_factor; + state->enter = acpi_idle_enter; state->flags = 0; - switch (cx->type) { - case ACPI_STATE_C1: - - state->enter = acpi_idle_enter_c1; - state->enter_dead = acpi_idle_play_dead; - drv->safe_state_index = count; - break; - - case ACPI_STATE_C2: - state->enter = acpi_idle_enter_simple; + if (cx->type == ACPI_STATE_C1 || cx->type == ACPI_STATE_C2) { state->enter_dead = acpi_idle_play_dead; drv->safe_state_index = count; - break; - - case ACPI_STATE_C3: - state->enter = pr->flags.bm_check ? - acpi_idle_enter_bm : - acpi_idle_enter_simple; - break; } count++; diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index 782a0d15c25f..4752b9939987 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -34,21 +34,34 @@ #define valid_IRQ(i) (true) #endif -static unsigned long acpi_dev_memresource_flags(u64 len, u8 write_protect, - bool window) +static bool acpi_dev_resource_len_valid(u64 start, u64 end, u64 len, bool io) { - unsigned long flags = IORESOURCE_MEM; + u64 reslen = end - start + 1; - if (len == 0) - flags |= IORESOURCE_DISABLED; + /* + * CHECKME: len might be required to check versus a minimum + * length as well. 1 for io is fine, but for memory it does + * not make any sense at all. + */ + if (len && reslen && reslen == len && start <= end) + return true; - if (write_protect == ACPI_READ_WRITE_MEMORY) - flags |= IORESOURCE_MEM_WRITEABLE; + pr_info("ACPI: invalid or unassigned resource %s [%016llx - %016llx] length [%016llx]\n", + io ? "io" : "mem", start, end, len); + + return false; +} + +static void acpi_dev_memresource_flags(struct resource *res, u64 len, + u8 write_protect) +{ + res->flags = IORESOURCE_MEM; - if (window) - flags |= IORESOURCE_WINDOW; + if (!acpi_dev_resource_len_valid(res->start, res->end, len, false)) + res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; - return flags; + if (write_protect == ACPI_READ_WRITE_MEMORY) + res->flags |= IORESOURCE_MEM_WRITEABLE; } static void acpi_dev_get_memresource(struct resource *res, u64 start, u64 len, @@ -56,7 +69,7 @@ static void acpi_dev_get_memresource(struct resource *res, u64 start, u64 len, { res->start = start; res->end = start + len - 1; - res->flags = acpi_dev_memresource_flags(len, write_protect, false); + acpi_dev_memresource_flags(res, len, write_protect); } /** @@ -67,6 +80,11 @@ static void acpi_dev_get_memresource(struct resource *res, u64 start, u64 len, * Check if the given ACPI resource object represents a memory resource and * if that's the case, use the information in it to populate the generic * resource object pointed to by @res. + * + * Return: + * 1) false with res->flags setting to zero: not the expected resource type + * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned resource + * 3) true: valid assigned resource */ bool acpi_dev_resource_memory(struct acpi_resource *ares, struct resource *res) { @@ -77,60 +95,52 @@ bool acpi_dev_resource_memory(struct acpi_resource *ares, struct resource *res) switch (ares->type) { case ACPI_RESOURCE_TYPE_MEMORY24: memory24 = &ares->data.memory24; - if (!memory24->minimum && !memory24->address_length) - return false; - acpi_dev_get_memresource(res, memory24->minimum, - memory24->address_length, + acpi_dev_get_memresource(res, memory24->minimum << 8, + memory24->address_length << 8, memory24->write_protect); break; case ACPI_RESOURCE_TYPE_MEMORY32: memory32 = &ares->data.memory32; - if (!memory32->minimum && !memory32->address_length) - return false; acpi_dev_get_memresource(res, memory32->minimum, memory32->address_length, memory32->write_protect); break; case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: fixed_memory32 = &ares->data.fixed_memory32; - if (!fixed_memory32->address && !fixed_memory32->address_length) - return false; acpi_dev_get_memresource(res, fixed_memory32->address, fixed_memory32->address_length, fixed_memory32->write_protect); break; default: + res->flags = 0; return false; } - return true; + + return !(res->flags & IORESOURCE_DISABLED); } EXPORT_SYMBOL_GPL(acpi_dev_resource_memory); -static unsigned int acpi_dev_ioresource_flags(u64 start, u64 end, u8 io_decode, - bool window) +static void acpi_dev_ioresource_flags(struct resource *res, u64 len, + u8 io_decode) { - int flags = IORESOURCE_IO; + res->flags = IORESOURCE_IO; - if (io_decode == ACPI_DECODE_16) - flags |= IORESOURCE_IO_16BIT_ADDR; + if (!acpi_dev_resource_len_valid(res->start, res->end, len, true)) + res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; - if (start > end || end >= 0x10003) - flags |= IORESOURCE_DISABLED; + if (res->end >= 0x10003) + res->flags |= IORESOURCE_DISABLED | IORESOURCE_UNSET; - if (window) - flags |= IORESOURCE_WINDOW; - - return flags; + if (io_decode == ACPI_DECODE_16) + res->flags |= IORESOURCE_IO_16BIT_ADDR; } static void acpi_dev_get_ioresource(struct resource *res, u64 start, u64 len, u8 io_decode) { - u64 end = start + len - 1; - res->start = start; - res->end = end; - res->flags = acpi_dev_ioresource_flags(start, end, io_decode, false); + res->end = start + len - 1; + acpi_dev_ioresource_flags(res, len, io_decode); } /** @@ -141,6 +151,11 @@ static void acpi_dev_get_ioresource(struct resource *res, u64 start, u64 len, * Check if the given ACPI resource object represents an I/O resource and * if that's the case, use the information in it to populate the generic * resource object pointed to by @res. + * + * Return: + * 1) false with res->flags setting to zero: not the expected resource type + * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned resource + * 3) true: valid assigned resource */ bool acpi_dev_resource_io(struct acpi_resource *ares, struct resource *res) { @@ -150,135 +165,143 @@ bool acpi_dev_resource_io(struct acpi_resource *ares, struct resource *res) switch (ares->type) { case ACPI_RESOURCE_TYPE_IO: io = &ares->data.io; - if (!io->minimum && !io->address_length) - return false; acpi_dev_get_ioresource(res, io->minimum, io->address_length, io->io_decode); break; case ACPI_RESOURCE_TYPE_FIXED_IO: fixed_io = &ares->data.fixed_io; - if (!fixed_io->address && !fixed_io->address_length) - return false; acpi_dev_get_ioresource(res, fixed_io->address, fixed_io->address_length, ACPI_DECODE_10); break; default: + res->flags = 0; return false; } - return true; + + return !(res->flags & IORESOURCE_DISABLED); } EXPORT_SYMBOL_GPL(acpi_dev_resource_io); -/** - * acpi_dev_resource_address_space - Extract ACPI address space information. - * @ares: Input ACPI resource object. - * @res: Output generic resource object. - * - * Check if the given ACPI resource object represents an address space resource - * and if that's the case, use the information in it to populate the generic - * resource object pointed to by @res. - */ -bool acpi_dev_resource_address_space(struct acpi_resource *ares, - struct resource *res) +static bool acpi_decode_space(struct resource_win *win, + struct acpi_resource_address *addr, + struct acpi_address64_attribute *attr) { - acpi_status status; - struct acpi_resource_address64 addr; - bool window; - u64 len; - u8 io_decode; + u8 iodec = attr->granularity == 0xfff ? ACPI_DECODE_10 : ACPI_DECODE_16; + bool wp = addr->info.mem.write_protect; + u64 len = attr->address_length; + struct resource *res = &win->res; - switch (ares->type) { - case ACPI_RESOURCE_TYPE_ADDRESS16: - case ACPI_RESOURCE_TYPE_ADDRESS32: - case ACPI_RESOURCE_TYPE_ADDRESS64: - break; - default: - return false; - } + /* + * Filter out invalid descriptor according to ACPI Spec 5.0, section + * 6.4.3.5 Address Space Resource Descriptors. + */ + if ((addr->min_address_fixed != addr->max_address_fixed && len) || + (addr->min_address_fixed && addr->max_address_fixed && !len)) + pr_debug("ACPI: Invalid address space min_addr_fix %d, max_addr_fix %d, len %llx\n", + addr->min_address_fixed, addr->max_address_fixed, len); - status = acpi_resource_to_address64(ares, &addr); - if (ACPI_FAILURE(status)) - return false; + res->start = attr->minimum; + res->end = attr->maximum; - res->start = addr.minimum; - res->end = addr.maximum; - window = addr.producer_consumer == ACPI_PRODUCER; + /* + * For bridges that translate addresses across the bridge, + * translation_offset is the offset that must be added to the + * address on the secondary side to obtain the address on the + * primary side. Non-bridge devices must list 0 for all Address + * Translation offset bits. + */ + if (addr->producer_consumer == ACPI_PRODUCER) { + res->start += attr->translation_offset; + res->end += attr->translation_offset; + } else if (attr->translation_offset) { + pr_debug("ACPI: translation_offset(%lld) is invalid for non-bridge device.\n", + attr->translation_offset); + } - switch(addr.resource_type) { + switch (addr->resource_type) { case ACPI_MEMORY_RANGE: - len = addr.maximum - addr.minimum + 1; - res->flags = acpi_dev_memresource_flags(len, - addr.info.mem.write_protect, - window); + acpi_dev_memresource_flags(res, len, wp); break; case ACPI_IO_RANGE: - io_decode = addr.granularity == 0xfff ? - ACPI_DECODE_10 : ACPI_DECODE_16; - res->flags = acpi_dev_ioresource_flags(addr.minimum, - addr.maximum, - io_decode, window); + acpi_dev_ioresource_flags(res, len, iodec); break; case ACPI_BUS_NUMBER_RANGE: res->flags = IORESOURCE_BUS; break; default: - res->flags = 0; + return false; } - return true; + win->offset = attr->translation_offset; + + if (addr->producer_consumer == ACPI_PRODUCER) + res->flags |= IORESOURCE_WINDOW; + + if (addr->info.mem.caching == ACPI_PREFETCHABLE_MEMORY) + res->flags |= IORESOURCE_PREFETCH; + + return !(res->flags & IORESOURCE_DISABLED); +} + +/** + * acpi_dev_resource_address_space - Extract ACPI address space information. + * @ares: Input ACPI resource object. + * @win: Output generic resource object. + * + * Check if the given ACPI resource object represents an address space resource + * and if that's the case, use the information in it to populate the generic + * resource object pointed to by @win. + * + * Return: + * 1) false with win->res.flags setting to zero: not the expected resource type + * 2) false with IORESOURCE_DISABLED in win->res.flags: valid unassigned + * resource + * 3) true: valid assigned resource + */ +bool acpi_dev_resource_address_space(struct acpi_resource *ares, + struct resource_win *win) +{ + struct acpi_resource_address64 addr; + + win->res.flags = 0; + if (ACPI_FAILURE(acpi_resource_to_address64(ares, &addr))) + return false; + + return acpi_decode_space(win, (struct acpi_resource_address *)&addr, + &addr.address); } EXPORT_SYMBOL_GPL(acpi_dev_resource_address_space); /** * acpi_dev_resource_ext_address_space - Extract ACPI address space information. * @ares: Input ACPI resource object. - * @res: Output generic resource object. + * @win: Output generic resource object. * * Check if the given ACPI resource object represents an extended address space * resource and if that's the case, use the information in it to populate the - * generic resource object pointed to by @res. + * generic resource object pointed to by @win. + * + * Return: + * 1) false with win->res.flags setting to zero: not the expected resource type + * 2) false with IORESOURCE_DISABLED in win->res.flags: valid unassigned + * resource + * 3) true: valid assigned resource */ bool acpi_dev_resource_ext_address_space(struct acpi_resource *ares, - struct resource *res) + struct resource_win *win) { struct acpi_resource_extended_address64 *ext_addr; - bool window; - u64 len; - u8 io_decode; + win->res.flags = 0; if (ares->type != ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64) return false; ext_addr = &ares->data.ext_address64; - res->start = ext_addr->minimum; - res->end = ext_addr->maximum; - window = ext_addr->producer_consumer == ACPI_PRODUCER; - - switch(ext_addr->resource_type) { - case ACPI_MEMORY_RANGE: - len = ext_addr->maximum - ext_addr->minimum + 1; - res->flags = acpi_dev_memresource_flags(len, - ext_addr->info.mem.write_protect, - window); - break; - case ACPI_IO_RANGE: - io_decode = ext_addr->granularity == 0xfff ? - ACPI_DECODE_10 : ACPI_DECODE_16; - res->flags = acpi_dev_ioresource_flags(ext_addr->minimum, - ext_addr->maximum, - io_decode, window); - break; - case ACPI_BUS_NUMBER_RANGE: - res->flags = IORESOURCE_BUS; - break; - default: - res->flags = 0; - } - - return true; + return acpi_decode_space(win, (struct acpi_resource_address *)ext_addr, + &ext_addr->address); } EXPORT_SYMBOL_GPL(acpi_dev_resource_ext_address_space); @@ -310,7 +333,7 @@ static void acpi_dev_irqresource_disabled(struct resource *res, u32 gsi) { res->start = gsi; res->end = gsi; - res->flags = IORESOURCE_IRQ | IORESOURCE_DISABLED; + res->flags = IORESOURCE_IRQ | IORESOURCE_DISABLED | IORESOURCE_UNSET; } static void acpi_dev_get_irqresource(struct resource *res, u32 gsi, @@ -369,6 +392,11 @@ static void acpi_dev_get_irqresource(struct resource *res, u32 gsi, * represented by the resource and populate the generic resource object pointed * to by @res accordingly. If the registration of the GSI is not successful, * IORESOURCE_DISABLED will be set it that object's flags. + * + * Return: + * 1) false with res->flags setting to zero: not the expected resource type + * 2) false with IORESOURCE_DISABLED in res->flags: valid unassigned resource + * 3) true: valid assigned resource */ bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index, struct resource *res) @@ -402,6 +430,7 @@ bool acpi_dev_resource_interrupt(struct acpi_resource *ares, int index, ext_irq->sharable, false); break; default: + res->flags = 0; return false; } @@ -415,12 +444,7 @@ EXPORT_SYMBOL_GPL(acpi_dev_resource_interrupt); */ void acpi_dev_free_resource_list(struct list_head *list) { - struct resource_list_entry *rentry, *re; - - list_for_each_entry_safe(rentry, re, list, node) { - list_del(&rentry->node); - kfree(rentry); - } + resource_list_free(list); } EXPORT_SYMBOL_GPL(acpi_dev_free_resource_list); @@ -432,18 +456,19 @@ struct res_proc_context { int error; }; -static acpi_status acpi_dev_new_resource_entry(struct resource *r, +static acpi_status acpi_dev_new_resource_entry(struct resource_win *win, struct res_proc_context *c) { - struct resource_list_entry *rentry; + struct resource_entry *rentry; - rentry = kmalloc(sizeof(*rentry), GFP_KERNEL); + rentry = resource_list_create_entry(NULL, 0); if (!rentry) { c->error = -ENOMEM; return AE_NO_MEMORY; } - rentry->res = *r; - list_add_tail(&rentry->node, c->list); + *rentry->res = win->res; + rentry->offset = win->offset; + resource_list_add_tail(rentry, c->list); c->count++; return AE_OK; } @@ -452,7 +477,8 @@ static acpi_status acpi_dev_process_resource(struct acpi_resource *ares, void *context) { struct res_proc_context *c = context; - struct resource r; + struct resource_win win; + struct resource *res = &win.res; int i; if (c->preproc) { @@ -467,18 +493,18 @@ static acpi_status acpi_dev_process_resource(struct acpi_resource *ares, } } - memset(&r, 0, sizeof(r)); + memset(&win, 0, sizeof(win)); - if (acpi_dev_resource_memory(ares, &r) - || acpi_dev_resource_io(ares, &r) - || acpi_dev_resource_address_space(ares, &r) - || acpi_dev_resource_ext_address_space(ares, &r)) - return acpi_dev_new_resource_entry(&r, c); + if (acpi_dev_resource_memory(ares, res) + || acpi_dev_resource_io(ares, res) + || acpi_dev_resource_address_space(ares, &win) + || acpi_dev_resource_ext_address_space(ares, &win)) + return acpi_dev_new_resource_entry(&win, c); - for (i = 0; acpi_dev_resource_interrupt(ares, i, &r); i++) { + for (i = 0; acpi_dev_resource_interrupt(ares, i, res); i++) { acpi_status status; - status = acpi_dev_new_resource_entry(&r, c); + status = acpi_dev_new_resource_entry(&win, c); if (ACPI_FAILURE(status)) return status; } @@ -503,7 +529,7 @@ static acpi_status acpi_dev_process_resource(struct acpi_resource *ares, * returned as the final error code. * * The resultant struct resource objects are put on the list pointed to by - * @list, that must be empty initially, as members of struct resource_list_entry + * @list, that must be empty initially, as members of struct resource_entry * objects. Callers of this routine should use %acpi_dev_free_resource_list() to * free that list. * @@ -538,3 +564,58 @@ int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list, return c.count; } EXPORT_SYMBOL_GPL(acpi_dev_get_resources); + +/** + * acpi_dev_filter_resource_type - Filter ACPI resource according to resource + * types + * @ares: Input ACPI resource object. + * @types: Valid resource types of IORESOURCE_XXX + * + * This is a hepler function to support acpi_dev_get_resources(), which filters + * ACPI resource objects according to resource types. + */ +int acpi_dev_filter_resource_type(struct acpi_resource *ares, + unsigned long types) +{ + unsigned long type = 0; + + switch (ares->type) { + case ACPI_RESOURCE_TYPE_MEMORY24: + case ACPI_RESOURCE_TYPE_MEMORY32: + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: + type = IORESOURCE_MEM; + break; + case ACPI_RESOURCE_TYPE_IO: + case ACPI_RESOURCE_TYPE_FIXED_IO: + type = IORESOURCE_IO; + break; + case ACPI_RESOURCE_TYPE_IRQ: + case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: + type = IORESOURCE_IRQ; + break; + case ACPI_RESOURCE_TYPE_DMA: + case ACPI_RESOURCE_TYPE_FIXED_DMA: + type = IORESOURCE_DMA; + break; + case ACPI_RESOURCE_TYPE_GENERIC_REGISTER: + type = IORESOURCE_REG; + break; + case ACPI_RESOURCE_TYPE_ADDRESS16: + case ACPI_RESOURCE_TYPE_ADDRESS32: + case ACPI_RESOURCE_TYPE_ADDRESS64: + case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: + if (ares->data.address.resource_type == ACPI_MEMORY_RANGE) + type = IORESOURCE_MEM; + else if (ares->data.address.resource_type == ACPI_IO_RANGE) + type = IORESOURCE_IO; + else if (ares->data.address.resource_type == + ACPI_BUS_NUMBER_RANGE) + type = IORESOURCE_BUS; + break; + default: + break; + } + + return (type & types) ? 0 : 1; +} +EXPORT_SYMBOL_GPL(acpi_dev_filter_resource_type); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 16914cc30882..bbca7830e18a 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1001,7 +1001,7 @@ static void acpi_free_power_resources_lists(struct acpi_device *device) if (device->wakeup.flags.valid) acpi_power_resources_list_free(&device->wakeup.resources); - if (!device->flags.power_manageable) + if (!device->power.flags.power_resources) return; for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) { @@ -1744,10 +1744,8 @@ static void acpi_bus_get_power_flags(struct acpi_device *device) device->power.flags.power_resources) device->power.states[ACPI_STATE_D3_COLD].flags.os_accessible = 1; - if (acpi_bus_init_power(device)) { - acpi_free_power_resources_lists(device); + if (acpi_bus_init_power(device)) device->flags.power_manageable = 0; - } } static void acpi_bus_get_flags(struct acpi_device *device) @@ -2371,13 +2369,18 @@ static void acpi_bus_attach(struct acpi_device *device) /* Skip devices that are not present. */ if (!acpi_device_is_present(device)) { device->flags.visited = false; + device->flags.power_manageable = 0; return; } if (device->handler) goto ok; if (!device->flags.initialized) { - acpi_bus_update_power(device, NULL); + device->flags.power_manageable = + device->power.states[ACPI_STATE_D0].flags.valid; + if (acpi_bus_init_power(device)) + device->flags.power_manageable = 0; + device->flags.initialized = true; } device->flags.visited = false; @@ -2541,6 +2544,7 @@ int __init acpi_scan_init(void) acpi_pci_link_init(); acpi_processor_init(); acpi_lpss_init(); + acpi_apd_init(); acpi_cmos_rtc_init(); acpi_container_init(); acpi_memory_hotplug_init(); diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index 8aa9254a387f..7f251dd1a687 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -321,7 +321,7 @@ static struct dmi_system_id acpisleep_dmi_table[] __initdata = { {}, }; -static void acpi_sleep_dmi_check(void) +static void __init acpi_sleep_dmi_check(void) { int year; diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index c72e79d2c5ad..88a4f99dd2a7 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -522,6 +522,34 @@ static struct dmi_system_id video_dmi_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_NAME, "370R4E/370R4V/370R5E/3570RE/370R5V"), }, }, + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1186097 */ + .callback = video_disable_native_backlight, + .ident = "SAMSUNG 3570R/370R/470R/450R/510R/4450RV", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "3570R/370R/470R/450R/510R/4450RV"), + }, + }, + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1094948 */ + .callback = video_disable_native_backlight, + .ident = "SAMSUNG 730U3E/740U3E", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"), + }, + }, + + { + /* https://bugzilla.redhat.com/show_bug.cgi?id=1163574 */ + .callback = video_disable_native_backlight, + .ident = "Dell XPS15 L521X", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "XPS L521X"), + }, + }, {} }; diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index a3a13605a9c4..5f601553b9b0 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -835,6 +835,7 @@ config PATA_AT32 config PATA_AT91 tristate "PATA support for AT91SAM9260" depends on ARM && SOC_AT91SAM9 + depends on !ARCH_MULTIPLATFORM help This option enables support for IDE devices on the Atmel AT91SAM9260 SoC. diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 49f1e6890587..33bb06e006c9 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -325,7 +325,6 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x9d05), board_ahci }, /* Sunrise Point-LP RAID */ { PCI_VDEVICE(INTEL, 0x9d07), board_ahci }, /* Sunrise Point-LP RAID */ { PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H AHCI */ - { PCI_VDEVICE(INTEL, 0xa103), board_ahci }, /* Sunrise Point-H RAID */ { PCI_VDEVICE(INTEL, 0xa105), board_ahci }, /* Sunrise Point-H RAID */ { PCI_VDEVICE(INTEL, 0xa107), board_ahci }, /* Sunrise Point-H RAID */ { PCI_VDEVICE(INTEL, 0xa10f), board_ahci }, /* Sunrise Point-H RAID */ diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 40f0e34f17af..71262e08648e 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -333,7 +333,7 @@ struct ahci_host_priv { u32 em_msg_type; /* EM message type */ bool got_runtime_pm; /* Did we do pm_runtime_get? */ struct clk *clks[AHCI_MAX_CLKS]; /* Optional */ - struct regulator *target_pwr; /* Optional */ + struct regulator **target_pwrs; /* Optional */ /* * If platform uses PHYs. There is a 1:1 relation between the port number and * the PHY position in this array. @@ -354,6 +354,10 @@ extern int ahci_ignore_sss; extern struct device_attribute *ahci_shost_attrs[]; extern struct device_attribute *ahci_sdev_attrs[]; +/* + * This must be instantiated by the edge drivers. Read the comments + * for ATA_BASE_SHT + */ #define AHCI_SHT(drv_name) \ ATA_NCQ_SHT(drv_name), \ .can_queue = AHCI_MAX_CMDS - 1, \ diff --git a/drivers/ata/ahci_da850.c b/drivers/ata/ahci_da850.c index ce8a7a6d6c7f..267a3d3e79f4 100644 --- a/drivers/ata/ahci_da850.c +++ b/drivers/ata/ahci_da850.c @@ -16,6 +16,8 @@ #include <linux/ahci_platform.h> #include "ahci.h" +#define DRV_NAME "ahci_da850" + /* SATA PHY Control Register offset from AHCI base */ #define SATA_P0PHYCR_REG 0x178 @@ -59,6 +61,10 @@ static const struct ata_port_info ahci_da850_port_info = { .port_ops = &ahci_platform_ops, }; +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT(DRV_NAME), +}; + static int ahci_da850_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -85,7 +91,8 @@ static int ahci_da850_probe(struct platform_device *pdev) da850_sata_init(dev, pwrdn_reg, hpriv->mmio); - rc = ahci_platform_init_host(pdev, hpriv, &ahci_da850_port_info); + rc = ahci_platform_init_host(pdev, hpriv, &ahci_da850_port_info, + &ahci_platform_sht); if (rc) goto disable_resources; @@ -102,7 +109,7 @@ static struct platform_driver ahci_da850_driver = { .probe = ahci_da850_probe, .remove = ata_platform_remove_one, .driver = { - .name = "ahci_da850", + .name = DRV_NAME, .pm = &ahci_da850_pm_ops, }, }; diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c index 35d51c59a370..3f3a7db208ae 100644 --- a/drivers/ata/ahci_imx.c +++ b/drivers/ata/ahci_imx.c @@ -28,6 +28,8 @@ #include <linux/libata.h> #include "ahci.h" +#define DRV_NAME "ahci-imx" + enum { /* Timer 1-ms Register */ IMX_TIMER1MS = 0x00e0, @@ -221,11 +223,9 @@ static int imx_sata_enable(struct ahci_host_priv *hpriv) if (imxpriv->no_device) return 0; - if (hpriv->target_pwr) { - ret = regulator_enable(hpriv->target_pwr); - if (ret) - return ret; - } + ret = ahci_platform_enable_regulators(hpriv); + if (ret) + return ret; ret = clk_prepare_enable(imxpriv->sata_ref_clk); if (ret < 0) @@ -270,8 +270,7 @@ static int imx_sata_enable(struct ahci_host_priv *hpriv) disable_clk: clk_disable_unprepare(imxpriv->sata_ref_clk); disable_regulator: - if (hpriv->target_pwr) - regulator_disable(hpriv->target_pwr); + ahci_platform_disable_regulators(hpriv); return ret; } @@ -291,8 +290,7 @@ static void imx_sata_disable(struct ahci_host_priv *hpriv) clk_disable_unprepare(imxpriv->sata_ref_clk); - if (hpriv->target_pwr) - regulator_disable(hpriv->target_pwr); + ahci_platform_disable_regulators(hpriv); } static void ahci_imx_error_handler(struct ata_port *ap) @@ -524,6 +522,10 @@ static u32 imx_ahci_parse_props(struct device *dev, return reg_value; } +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT(DRV_NAME), +}; + static int imx_ahci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -620,7 +622,8 @@ static int imx_ahci_probe(struct platform_device *pdev) reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000; writel(reg_val, hpriv->mmio + IMX_TIMER1MS); - ret = ahci_platform_init_host(pdev, hpriv, &ahci_imx_port_info); + ret = ahci_platform_init_host(pdev, hpriv, &ahci_imx_port_info, + &ahci_platform_sht); if (ret) goto disable_sata; @@ -678,7 +681,7 @@ static struct platform_driver imx_ahci_driver = { .probe = imx_ahci_probe, .remove = ata_platform_remove_one, .driver = { - .name = "ahci-imx", + .name = DRV_NAME, .of_match_table = imx_ahci_of_match, .pm = &ahci_imx_pm_ops, }, diff --git a/drivers/ata/ahci_mvebu.c b/drivers/ata/ahci_mvebu.c index 64bb08432b69..23716dd8a7ec 100644 --- a/drivers/ata/ahci_mvebu.c +++ b/drivers/ata/ahci_mvebu.c @@ -19,6 +19,8 @@ #include <linux/platform_device.h> #include "ahci.h" +#define DRV_NAME "ahci-mvebu" + #define AHCI_VENDOR_SPECIFIC_0_ADDR 0xa0 #define AHCI_VENDOR_SPECIFIC_0_DATA 0xa4 @@ -67,6 +69,10 @@ static const struct ata_port_info ahci_mvebu_port_info = { .port_ops = &ahci_platform_ops, }; +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT(DRV_NAME), +}; + static int ahci_mvebu_probe(struct platform_device *pdev) { struct ahci_host_priv *hpriv; @@ -88,7 +94,8 @@ static int ahci_mvebu_probe(struct platform_device *pdev) ahci_mvebu_mbus_config(hpriv, dram); ahci_mvebu_regret_option(hpriv); - rc = ahci_platform_init_host(pdev, hpriv, &ahci_mvebu_port_info); + rc = ahci_platform_init_host(pdev, hpriv, &ahci_mvebu_port_info, + &ahci_platform_sht); if (rc) goto disable_resources; @@ -114,7 +121,7 @@ static struct platform_driver ahci_mvebu_driver = { .probe = ahci_mvebu_probe, .remove = ata_platform_remove_one, .driver = { - .name = "ahci-mvebu", + .name = DRV_NAME, .of_match_table = ahci_mvebu_of_match, }, }; diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c index 18d539837045..78d6ae0b90c4 100644 --- a/drivers/ata/ahci_platform.c +++ b/drivers/ata/ahci_platform.c @@ -22,6 +22,8 @@ #include <linux/ahci_platform.h> #include "ahci.h" +#define DRV_NAME "ahci" + static const struct ata_port_info ahci_port_info = { .flags = AHCI_FLAG_COMMON, .pio_mask = ATA_PIO4, @@ -29,6 +31,10 @@ static const struct ata_port_info ahci_port_info = { .port_ops = &ahci_platform_ops, }; +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT(DRV_NAME), +}; + static int ahci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -46,7 +52,8 @@ static int ahci_probe(struct platform_device *pdev) if (of_device_is_compatible(dev->of_node, "hisilicon,hisi-ahci")) hpriv->flags |= AHCI_HFLAG_NO_FBS | AHCI_HFLAG_NO_NCQ; - rc = ahci_platform_init_host(pdev, hpriv, &ahci_port_info); + rc = ahci_platform_init_host(pdev, hpriv, &ahci_port_info, + &ahci_platform_sht); if (rc) goto disable_resources; @@ -75,7 +82,7 @@ static struct platform_driver ahci_driver = { .probe = ahci_probe, .remove = ata_platform_remove_one, .driver = { - .name = "ahci", + .name = DRV_NAME, .of_match_table = ahci_of_match, .pm = &ahci_pm_ops, }, diff --git a/drivers/ata/ahci_st.c b/drivers/ata/ahci_st.c index 2f9e8317cc16..bc971af262e7 100644 --- a/drivers/ata/ahci_st.c +++ b/drivers/ata/ahci_st.c @@ -23,6 +23,8 @@ #include "ahci.h" +#define DRV_NAME "st_ahci" + #define ST_AHCI_OOBR 0xbc #define ST_AHCI_OOBR_WE BIT(31) #define ST_AHCI_OOBR_CWMIN_SHIFT 24 @@ -140,6 +142,10 @@ static const struct ata_port_info st_ahci_port_info = { .port_ops = &st_ahci_port_ops, }; +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT(DRV_NAME), +}; + static int st_ahci_probe(struct platform_device *pdev) { struct st_ahci_drv_data *drv_data; @@ -166,7 +172,8 @@ static int st_ahci_probe(struct platform_device *pdev) if (err) return err; - err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info); + err = ahci_platform_init_host(pdev, hpriv, &st_ahci_port_info, + &ahci_platform_sht); if (err) { ahci_platform_disable_resources(hpriv); return err; @@ -229,7 +236,7 @@ MODULE_DEVICE_TABLE(of, st_ahci_match); static struct platform_driver st_ahci_driver = { .driver = { - .name = "st_ahci", + .name = DRV_NAME, .pm = &st_ahci_pm_ops, .of_match_table = of_match_ptr(st_ahci_match), }, diff --git a/drivers/ata/ahci_sunxi.c b/drivers/ata/ahci_sunxi.c index e2e0da539a2f..b26437430163 100644 --- a/drivers/ata/ahci_sunxi.c +++ b/drivers/ata/ahci_sunxi.c @@ -27,6 +27,8 @@ #include <linux/regulator/consumer.h> #include "ahci.h" +#define DRV_NAME "ahci-sunxi" + /* Insmod parameters */ static bool enable_pmp; module_param(enable_pmp, bool, 0); @@ -169,6 +171,10 @@ static const struct ata_port_info ahci_sunxi_port_info = { .port_ops = &ahci_platform_ops, }; +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT(DRV_NAME), +}; + static int ahci_sunxi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -200,7 +206,8 @@ static int ahci_sunxi_probe(struct platform_device *pdev) if (!enable_pmp) hpriv->flags |= AHCI_HFLAG_NO_PMP; - rc = ahci_platform_init_host(pdev, hpriv, &ahci_sunxi_port_info); + rc = ahci_platform_init_host(pdev, hpriv, &ahci_sunxi_port_info, + &ahci_platform_sht); if (rc) goto disable_resources; @@ -251,7 +258,7 @@ static struct platform_driver ahci_sunxi_driver = { .probe = ahci_sunxi_probe, .remove = ata_platform_remove_one, .driver = { - .name = "ahci-sunxi", + .name = DRV_NAME, .of_match_table = ahci_sunxi_of_match, .pm = &ahci_sunxi_pm_ops, }, diff --git a/drivers/ata/ahci_tegra.c b/drivers/ata/ahci_tegra.c index 032904402c95..3a62eb246d80 100644 --- a/drivers/ata/ahci_tegra.c +++ b/drivers/ata/ahci_tegra.c @@ -31,6 +31,8 @@ #include "ahci.h" +#define DRV_NAME "tegra-ahci" + #define SATA_CONFIGURATION_0 0x180 #define SATA_CONFIGURATION_EN_FPCI BIT(0) @@ -289,6 +291,10 @@ static const struct of_device_id tegra_ahci_of_match[] = { }; MODULE_DEVICE_TABLE(of, tegra_ahci_of_match); +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT(DRV_NAME), +}; + static int tegra_ahci_probe(struct platform_device *pdev) { struct ahci_host_priv *hpriv; @@ -354,7 +360,8 @@ static int tegra_ahci_probe(struct platform_device *pdev) if (ret) return ret; - ret = ahci_platform_init_host(pdev, hpriv, &ahci_tegra_port_info); + ret = ahci_platform_init_host(pdev, hpriv, &ahci_tegra_port_info, + &ahci_platform_sht); if (ret) goto deinit_controller; @@ -370,7 +377,7 @@ static struct platform_driver tegra_ahci_driver = { .probe = tegra_ahci_probe, .remove = ata_platform_remove_one, .driver = { - .name = "tegra-ahci", + .name = DRV_NAME, .of_match_table = tegra_ahci_of_match, }, /* LP0 suspend support not implemented */ diff --git a/drivers/ata/ahci_xgene.c b/drivers/ata/ahci_xgene.c index feeb8f1e2fe8..2e8bb603e447 100644 --- a/drivers/ata/ahci_xgene.c +++ b/drivers/ata/ahci_xgene.c @@ -30,6 +30,8 @@ #include <linux/phy/phy.h> #include "ahci.h" +#define DRV_NAME "xgene-ahci" + /* Max # of disk per a controller */ #define MAX_AHCI_CHN_PERCTR 2 @@ -85,6 +87,7 @@ struct xgene_ahci_context { struct ahci_host_priv *hpriv; struct device *dev; u8 last_cmd[MAX_AHCI_CHN_PERCTR]; /* tracking the last command issued*/ + u32 class[MAX_AHCI_CHN_PERCTR]; /* tracking the class of device */ void __iomem *csr_core; /* Core CSR address of IP */ void __iomem *csr_diag; /* Diag CSR address of IP */ void __iomem *csr_axi; /* AXI CSR address of IP */ @@ -105,17 +108,69 @@ static int xgene_ahci_init_memram(struct xgene_ahci_context *ctx) } /** + * xgene_ahci_poll_reg_val- Poll a register on a specific value. + * @ap : ATA port of interest. + * @reg : Register of interest. + * @val : Value to be attained. + * @interval : waiting interval for polling. + * @timeout : timeout for achieving the value. + */ +static int xgene_ahci_poll_reg_val(struct ata_port *ap, + void __iomem *reg, unsigned + int val, unsigned long interval, + unsigned long timeout) +{ + unsigned long deadline; + unsigned int tmp; + + tmp = ioread32(reg); + deadline = ata_deadline(jiffies, timeout); + + while (tmp != val && time_before(jiffies, deadline)) { + ata_msleep(ap, interval); + tmp = ioread32(reg); + } + + return tmp; +} + +/** * xgene_ahci_restart_engine - Restart the dma engine. * @ap : ATA port of interest * - * Restarts the dma engine inside the controller. + * Waits for completion of multiple commands and restarts + * the DMA engine inside the controller. */ static int xgene_ahci_restart_engine(struct ata_port *ap) { struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 fbs; + + /* + * In case of PMP multiple IDENTIFY DEVICE commands can be + * issued inside PxCI. So need to poll PxCI for the + * completion of outstanding IDENTIFY DEVICE commands before + * we restart the DMA engine. + */ + if (xgene_ahci_poll_reg_val(ap, port_mmio + + PORT_CMD_ISSUE, 0x0, 1, 100)) + return -EBUSY; ahci_stop_engine(ap); ahci_start_fis_rx(ap); + + /* + * Enable the PxFBS.FBS_EN bit as it + * gets cleared due to stopping the engine. + */ + if (pp->fbs_supported) { + fbs = readl(port_mmio + PORT_FBS); + writel(fbs | PORT_FBS_EN, port_mmio + PORT_FBS); + fbs = readl(port_mmio + PORT_FBS); + } + hpriv->start_engine(ap); return 0; @@ -129,6 +184,13 @@ static int xgene_ahci_restart_engine(struct ata_port *ap) * clear the BSY bit after receiving the PIO setup FIS. This results in the dma * state machine goes into the CMFatalErrorUpdate state and locks up. By * restarting the dma engine, it removes the controller out of lock up state. + * + * Due to H/W errata, the controller is unable to save the PMP + * field fetched from command header before sending the H2D FIS. + * When the device returns the PMP port field in the D2H FIS, there is + * a mismatch and results in command completion failure. The + * workaround is to write the pmp value to PxFBS.DEV field before issuing + * any command to PMP. */ static unsigned int xgene_ahci_qc_issue(struct ata_queued_cmd *qc) { @@ -136,8 +198,23 @@ static unsigned int xgene_ahci_qc_issue(struct ata_queued_cmd *qc) struct ahci_host_priv *hpriv = ap->host->private_data; struct xgene_ahci_context *ctx = hpriv->plat_data; int rc = 0; + u32 port_fbs; + void *port_mmio = ahci_port_base(ap); - if (unlikely(ctx->last_cmd[ap->port_no] == ATA_CMD_ID_ATA)) + /* + * Write the pmp value to PxFBS.DEV + * for case of Port Mulitplier. + */ + if (ctx->class[ap->port_no] == ATA_DEV_PMP) { + port_fbs = readl(port_mmio + PORT_FBS); + port_fbs &= ~PORT_FBS_DEV_MASK; + port_fbs |= qc->dev->link->pmp << PORT_FBS_DEV_OFFSET; + writel(port_fbs, port_mmio + PORT_FBS); + } + + if (unlikely((ctx->last_cmd[ap->port_no] == ATA_CMD_ID_ATA) || + (ctx->last_cmd[ap->port_no] == ATA_CMD_PACKET) || + (ctx->last_cmd[ap->port_no] == ATA_CMD_SMART))) xgene_ahci_restart_engine(ap); rc = ahci_qc_issue(qc); @@ -188,7 +265,7 @@ static unsigned int xgene_ahci_read_id(struct ata_device *dev, * * Clear reserved bit 8 (DEVSLP bit) as we don't support DEVSLP */ - id[ATA_ID_FEATURE_SUPP] &= ~(1 << 8); + id[ATA_ID_FEATURE_SUPP] &= cpu_to_le16(~(1 << 8)); return 0; } @@ -363,16 +440,119 @@ static void xgene_ahci_host_stop(struct ata_host *host) ahci_platform_disable_resources(hpriv); } +/** + * xgene_ahci_pmp_softreset - Issue the softreset to the drives connected + * to Port Multiplier. + * @link: link to reset + * @class: Return value to indicate class of device + * @deadline: deadline jiffies for the operation + * + * Due to H/W errata, the controller is unable to save the PMP + * field fetched from command header before sending the H2D FIS. + * When the device returns the PMP port field in the D2H FIS, there is + * a mismatch and results in command completion failure. The workaround + * is to write the pmp value to PxFBS.DEV field before issuing any command + * to PMP. + */ +static int xgene_ahci_pmp_softreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + int pmp = sata_srst_pmp(link); + struct ata_port *ap = link->ap; + u32 rc; + void *port_mmio = ahci_port_base(ap); + u32 port_fbs; + + /* + * Set PxFBS.DEV field with pmp + * value. + */ + port_fbs = readl(port_mmio + PORT_FBS); + port_fbs &= ~PORT_FBS_DEV_MASK; + port_fbs |= pmp << PORT_FBS_DEV_OFFSET; + writel(port_fbs, port_mmio + PORT_FBS); + + rc = ahci_do_softreset(link, class, pmp, deadline, ahci_check_ready); + + return rc; +} + +/** + * xgene_ahci_softreset - Issue the softreset to the drive. + * @link: link to reset + * @class: Return value to indicate class of device + * @deadline: deadline jiffies for the operation + * + * Due to H/W errata, the controller is unable to save the PMP + * field fetched from command header before sending the H2D FIS. + * When the device returns the PMP port field in the D2H FIS, there is + * a mismatch and results in command completion failure. The workaround + * is to write the pmp value to PxFBS.DEV field before issuing any command + * to PMP. Here is the algorithm to detect PMP : + * + * 1. Save the PxFBS value + * 2. Program PxFBS.DEV with pmp value send by framework. Framework sends + * 0xF for both PMP/NON-PMP initially + * 3. Issue softreset + * 4. If signature class is PMP goto 6 + * 5. restore the original PxFBS and goto 3 + * 6. return + */ +static int xgene_ahci_softreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) +{ + int pmp = sata_srst_pmp(link); + struct ata_port *ap = link->ap; + struct ahci_host_priv *hpriv = ap->host->private_data; + struct xgene_ahci_context *ctx = hpriv->plat_data; + void *port_mmio = ahci_port_base(ap); + u32 port_fbs; + u32 port_fbs_save; + u32 retry = 1; + u32 rc; + + port_fbs_save = readl(port_mmio + PORT_FBS); + + /* + * Set PxFBS.DEV field with pmp + * value. + */ + port_fbs = readl(port_mmio + PORT_FBS); + port_fbs &= ~PORT_FBS_DEV_MASK; + port_fbs |= pmp << PORT_FBS_DEV_OFFSET; + writel(port_fbs, port_mmio + PORT_FBS); + +softreset_retry: + rc = ahci_do_softreset(link, class, pmp, + deadline, ahci_check_ready); + + ctx->class[ap->port_no] = *class; + if (*class != ATA_DEV_PMP) { + /* + * Retry for normal drives without + * setting PxFBS.DEV field with pmp value. + */ + if (retry--) { + writel(port_fbs_save, port_mmio + PORT_FBS); + goto softreset_retry; + } + } + + return rc; +} + static struct ata_port_operations xgene_ahci_ops = { .inherits = &ahci_ops, .host_stop = xgene_ahci_host_stop, .hardreset = xgene_ahci_hardreset, .read_id = xgene_ahci_read_id, .qc_issue = xgene_ahci_qc_issue, + .softreset = xgene_ahci_softreset, + .pmp_softreset = xgene_ahci_pmp_softreset }; static const struct ata_port_info xgene_ahci_port_info = { - .flags = AHCI_FLAG_COMMON, + .flags = AHCI_FLAG_COMMON | ATA_FLAG_PMP, .pio_mask = ATA_PIO4, .udma_mask = ATA_UDMA6, .port_ops = &xgene_ahci_ops, @@ -444,6 +624,10 @@ static int xgene_ahci_mux_select(struct xgene_ahci_context *ctx) return val & CFG_SATA_ENET_SELECT_MASK ? -1 : 0; } +static struct scsi_host_template ahci_platform_sht = { + AHCI_SHT(DRV_NAME), +}; + static int xgene_ahci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -521,7 +705,8 @@ static int xgene_ahci_probe(struct platform_device *pdev) skip_clk_phy: hpriv->flags = AHCI_HFLAG_NO_PMP | AHCI_HFLAG_NO_NCQ; - rc = ahci_platform_init_host(pdev, hpriv, &xgene_ahci_port_info); + rc = ahci_platform_init_host(pdev, hpriv, &xgene_ahci_port_info, + &ahci_platform_sht); if (rc) goto disable_resources; @@ -543,7 +728,7 @@ static struct platform_driver xgene_ahci_driver = { .probe = xgene_ahci_probe, .remove = ata_platform_remove_one, .driver = { - .name = "xgene-ahci", + .name = DRV_NAME, .of_match_table = xgene_ahci_of_match, }, }; diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 97683e45ab04..61a9c07e0dff 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -2003,7 +2003,7 @@ static void ahci_set_aggressive_devslp(struct ata_port *ap, bool sleep) devslp = readl(port_mmio + PORT_DEVSLP); if (!(devslp & PORT_DEVSLP_DSP)) { - dev_err(ap->host->dev, "port does not support device sleep\n"); + dev_info(ap->host->dev, "port does not support device sleep\n"); return; } diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c index 0b03f9056692..d89305d289f6 100644 --- a/drivers/ata/libahci_platform.c +++ b/drivers/ata/libahci_platform.c @@ -24,6 +24,7 @@ #include <linux/ahci_platform.h> #include <linux/phy/phy.h> #include <linux/pm_runtime.h> +#include <linux/of_platform.h> #include "ahci.h" static void ahci_host_stop(struct ata_host *host); @@ -34,10 +35,6 @@ struct ata_port_operations ahci_platform_ops = { }; EXPORT_SYMBOL_GPL(ahci_platform_ops); -static struct scsi_host_template ahci_platform_sht = { - AHCI_SHT("ahci_platform"), -}; - /** * ahci_platform_enable_phys - Enable PHYs * @hpriv: host private area to store config values @@ -54,9 +51,6 @@ static int ahci_platform_enable_phys(struct ahci_host_priv *hpriv) int rc, i; for (i = 0; i < hpriv->nports; i++) { - if (!hpriv->phys[i]) - continue; - rc = phy_init(hpriv->phys[i]); if (rc) goto disable_phys; @@ -89,9 +83,6 @@ static void ahci_platform_disable_phys(struct ahci_host_priv *hpriv) int i; for (i = 0; i < hpriv->nports; i++) { - if (!hpriv->phys[i]) - continue; - phy_power_off(hpriv->phys[i]); phy_exit(hpriv->phys[i]); } @@ -144,6 +135,59 @@ void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); /** + * ahci_platform_enable_regulators - Enable regulators + * @hpriv: host private area to store config values + * + * This function enables all the regulators found in + * hpriv->target_pwrs, if any. If a regulator fails to be enabled, it + * disables all the regulators already enabled in reverse order and + * returns an error. + * + * RETURNS: + * 0 on success otherwise a negative error code + */ +int ahci_platform_enable_regulators(struct ahci_host_priv *hpriv) +{ + int rc, i; + + for (i = 0; i < hpriv->nports; i++) { + if (!hpriv->target_pwrs[i]) + continue; + + rc = regulator_enable(hpriv->target_pwrs[i]); + if (rc) + goto disable_target_pwrs; + } + + return 0; + +disable_target_pwrs: + while (--i >= 0) + if (hpriv->target_pwrs[i]) + regulator_disable(hpriv->target_pwrs[i]); + + return rc; +} +EXPORT_SYMBOL_GPL(ahci_platform_enable_regulators); + +/** + * ahci_platform_disable_regulators - Disable regulators + * @hpriv: host private area to store config values + * + * This function disables all regulators found in hpriv->target_pwrs. + */ +void ahci_platform_disable_regulators(struct ahci_host_priv *hpriv) +{ + int i; + + for (i = 0; i < hpriv->nports; i++) { + if (!hpriv->target_pwrs[i]) + continue; + regulator_disable(hpriv->target_pwrs[i]); + } +} +EXPORT_SYMBOL_GPL(ahci_platform_disable_regulators); +/** * ahci_platform_enable_resources - Enable platform resources * @hpriv: host private area to store config values * @@ -163,11 +207,9 @@ int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) { int rc; - if (hpriv->target_pwr) { - rc = regulator_enable(hpriv->target_pwr); - if (rc) - return rc; - } + rc = ahci_platform_enable_regulators(hpriv); + if (rc) + return rc; rc = ahci_platform_enable_clks(hpriv); if (rc) @@ -183,8 +225,8 @@ disable_clks: ahci_platform_disable_clks(hpriv); disable_regulator: - if (hpriv->target_pwr) - regulator_disable(hpriv->target_pwr); + ahci_platform_disable_regulators(hpriv); + return rc; } EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); @@ -205,8 +247,7 @@ void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) ahci_platform_disable_clks(hpriv); - if (hpriv->target_pwr) - regulator_disable(hpriv->target_pwr); + ahci_platform_disable_regulators(hpriv); } EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); @@ -222,6 +263,69 @@ static void ahci_platform_put_resources(struct device *dev, void *res) for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) clk_put(hpriv->clks[c]); + /* + * The regulators are tied to child node device and not to the + * SATA device itself. So we can't use devm for automatically + * releasing them. We have to do it manually here. + */ + for (c = 0; c < hpriv->nports; c++) + if (hpriv->target_pwrs && hpriv->target_pwrs[c]) + regulator_put(hpriv->target_pwrs[c]); + + kfree(hpriv->target_pwrs); +} + +static int ahci_platform_get_phy(struct ahci_host_priv *hpriv, u32 port, + struct device *dev, struct device_node *node) +{ + int rc; + + hpriv->phys[port] = devm_of_phy_get(dev, node, NULL); + + if (!IS_ERR(hpriv->phys[port])) + return 0; + + rc = PTR_ERR(hpriv->phys[port]); + switch (rc) { + case -ENOSYS: + /* No PHY support. Check if PHY is required. */ + if (of_find_property(node, "phys", NULL)) { + dev_err(dev, + "couldn't get PHY in node %s: ENOSYS\n", + node->name); + break; + } + case -ENODEV: + /* continue normally */ + hpriv->phys[port] = NULL; + rc = 0; + break; + + default: + dev_err(dev, + "couldn't get PHY in node %s: %d\n", + node->name, rc); + + break; + } + + return rc; +} + +static int ahci_platform_get_regulator(struct ahci_host_priv *hpriv, u32 port, + struct device *dev) +{ + struct regulator *target_pwr; + int rc = 0; + + target_pwr = regulator_get_optional(dev, "target"); + + if (!IS_ERR(target_pwr)) + hpriv->target_pwrs[port] = target_pwr; + else + rc = PTR_ERR(target_pwr); + + return rc; } /** @@ -246,7 +350,7 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev) struct ahci_host_priv *hpriv; struct clk *clk; struct device_node *child; - int i, enabled_ports = 0, rc = -ENOMEM; + int i, sz, enabled_ports = 0, rc = -ENOMEM, child_nodes; u32 mask_port_map = 0; if (!devres_open_group(dev, NULL, GFP_KERNEL)) @@ -267,14 +371,6 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev) goto err_out; } - hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); - if (IS_ERR(hpriv->target_pwr)) { - rc = PTR_ERR(hpriv->target_pwr); - if (rc == -EPROBE_DEFER) - goto err_out; - hpriv->target_pwr = NULL; - } - for (i = 0; i < AHCI_MAX_CLKS; i++) { /* * For now we must use clk_get(dev, NULL) for the first clock, @@ -296,19 +392,33 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev) hpriv->clks[i] = clk; } - hpriv->nports = of_get_child_count(dev->of_node); + hpriv->nports = child_nodes = of_get_child_count(dev->of_node); - if (hpriv->nports) { - hpriv->phys = devm_kzalloc(dev, - hpriv->nports * sizeof(*hpriv->phys), - GFP_KERNEL); - if (!hpriv->phys) { - rc = -ENOMEM; - goto err_out; - } + /* + * If no sub-node was found, we still need to set nports to + * one in order to be able to use the + * ahci_platform_[en|dis]able_[phys|regulators] functions. + */ + if (!child_nodes) + hpriv->nports = 1; + sz = hpriv->nports * sizeof(*hpriv->phys); + hpriv->phys = devm_kzalloc(dev, sz, GFP_KERNEL); + if (!hpriv->phys) { + rc = -ENOMEM; + goto err_out; + } + sz = hpriv->nports * sizeof(*hpriv->target_pwrs); + hpriv->target_pwrs = kzalloc(sz, GFP_KERNEL); + if (!hpriv->target_pwrs) { + rc = -ENOMEM; + goto err_out; + } + + if (child_nodes) { for_each_child_of_node(dev->of_node, child) { u32 port; + struct platform_device *port_dev __maybe_unused; if (!of_device_is_available(child)) continue; @@ -322,17 +432,24 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev) dev_warn(dev, "invalid port number %d\n", port); continue; } - mask_port_map |= BIT(port); - hpriv->phys[port] = devm_of_phy_get(dev, child, NULL); - if (IS_ERR(hpriv->phys[port])) { - rc = PTR_ERR(hpriv->phys[port]); - dev_err(dev, - "couldn't get PHY in node %s: %d\n", - child->name, rc); - goto err_out; +#ifdef CONFIG_OF_ADDRESS + of_platform_device_create(child, NULL, NULL); + + port_dev = of_find_device_by_node(child); + + if (port_dev) { + rc = ahci_platform_get_regulator(hpriv, port, + &port_dev->dev); + if (rc == -EPROBE_DEFER) + goto err_out; } +#endif + + rc = ahci_platform_get_phy(hpriv, port, dev, child); + if (rc) + goto err_out; enabled_ports++; } @@ -349,38 +466,14 @@ struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev) * If no sub-node was found, keep this for device tree * compatibility */ - struct phy *phy = devm_phy_get(dev, "sata-phy"); - if (!IS_ERR(phy)) { - hpriv->phys = devm_kzalloc(dev, sizeof(*hpriv->phys), - GFP_KERNEL); - if (!hpriv->phys) { - rc = -ENOMEM; - goto err_out; - } - - hpriv->phys[0] = phy; - hpriv->nports = 1; - } else { - rc = PTR_ERR(phy); - switch (rc) { - case -ENOSYS: - /* No PHY support. Check if PHY is required. */ - if (of_find_property(dev->of_node, "phys", NULL)) { - dev_err(dev, "couldn't get sata-phy: ENOSYS\n"); - goto err_out; - } - case -ENODEV: - /* continue normally */ - hpriv->phys = NULL; - break; - - default: - goto err_out; + rc = ahci_platform_get_phy(hpriv, 0, dev, dev->of_node); + if (rc) + goto err_out; - } - } + rc = ahci_platform_get_regulator(hpriv, 0, dev); + if (rc == -EPROBE_DEFER) + goto err_out; } - pm_runtime_enable(dev); pm_runtime_get_sync(dev); hpriv->got_runtime_pm = true; @@ -399,6 +492,7 @@ EXPORT_SYMBOL_GPL(ahci_platform_get_resources); * @pdev: platform device pointer for the host * @hpriv: ahci-host private data for the host * @pi_template: template for the ata_port_info to use + * @sht: scsi_host_template to use when registering * * This function does all the usual steps needed to bring up an * ahci-platform host, note any necessary resources (ie clks, phys, etc.) @@ -409,7 +503,8 @@ EXPORT_SYMBOL_GPL(ahci_platform_get_resources); */ int ahci_platform_init_host(struct platform_device *pdev, struct ahci_host_priv *hpriv, - const struct ata_port_info *pi_template) + const struct ata_port_info *pi_template, + struct scsi_host_template *sht) { struct device *dev = &pdev->dev; struct ata_port_info pi = *pi_template; @@ -493,7 +588,7 @@ int ahci_platform_init_host(struct platform_device *pdev, ahci_init_controller(host); ahci_print_info(host, "platform"); - return ahci_host_activate(host, irq, &ahci_platform_sht); + return ahci_host_activate(host, irq, sht); } EXPORT_SYMBOL_GPL(ahci_platform_init_host); diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 5c84fb5c3372..4b0d5e71858e 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -1752,33 +1752,6 @@ unsigned ata_exec_internal(struct ata_device *dev, } /** - * ata_do_simple_cmd - execute simple internal command - * @dev: Device to which the command is sent - * @cmd: Opcode to execute - * - * Execute a 'simple' command, that only consists of the opcode - * 'cmd' itself, without filling any other registers - * - * LOCKING: - * Kernel thread context (may sleep). - * - * RETURNS: - * Zero on success, AC_ERR_* mask on failure - */ -unsigned int ata_do_simple_cmd(struct ata_device *dev, u8 cmd) -{ - struct ata_taskfile tf; - - ata_tf_init(dev, &tf); - - tf.command = cmd; - tf.flags |= ATA_TFLAG_DEVICE; - tf.protocol = ATA_PROT_NODATA; - - return ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0); -} - -/** * ata_pio_need_iordy - check if iordy needed * @adev: ATA device * @@ -4233,10 +4206,33 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "PIONEER DVD-RW DVR-216D", NULL, ATA_HORKAGE_NOSETXFER }, /* devices that don't properly handle queued TRIM commands */ - { "Micron_M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, - { "Crucial_CT???M500SSD*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, - { "Micron_M550*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, - { "Crucial_CT*M550SSD*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, + { "Micron_M[56]*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | + ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "Crucial_CT*SSD*", NULL, ATA_HORKAGE_NO_NCQ_TRIM, }, + + /* + * As defined, the DRAT (Deterministic Read After Trim) and RZAT + * (Return Zero After Trim) flags in the ATA Command Set are + * unreliable in the sense that they only define what happens if + * the device successfully executed the DSM TRIM command. TRIM + * is only advisory, however, and the device is free to silently + * ignore all or parts of the request. + * + * Whitelist drives that are known to reliably return zeroes + * after TRIM. + */ + + /* + * The intel 510 drive has buggy DRAT/RZAT. Explicitly exclude + * that model before whitelisting all other intel SSDs. + */ + { "INTEL*SSDSC2MH*", NULL, 0, }, + + { "INTEL*SSD*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "SSD*INTEL*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "Samsung*SSD*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "SAMSUNG*SSD*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "ST[1248][0248]0[FH]*", NULL, ATA_HORKAGE_ZERO_AFTER_TRIM, }, /* * Some WD SATA-I drives spin up and down erratically when the link @@ -4748,7 +4744,10 @@ static struct ata_queued_cmd *ata_qc_new(struct ata_port *ap) return NULL; for (i = 0, tag = ap->last_tag + 1; i < max_queue; i++, tag++) { - tag = tag < max_queue ? tag : 0; + if (ap->flags & ATA_FLAG_LOWTAG) + tag = i; + else + tag = tag < max_queue ? tag : 0; /* the last tag is reserved for internal command. */ if (tag == ATA_TAG_INTERNAL) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 665fd2b18970..d2029a462e2c 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1635,7 +1635,6 @@ unsigned int atapi_eh_request_sense(struct ata_device *dev, DPRINTK("ATAPI request sense\n"); - /* FIXME: is this needed? */ memset(sense_buf, 0, SCSI_SENSE_BUFFERSIZE); /* initialize sense_buf with the error register, @@ -2389,6 +2388,7 @@ const char *ata_get_cmd_descript(u8 command) return NULL; } +EXPORT_SYMBOL_GPL(ata_get_cmd_descript); /** * ata_eh_link_report - report error handling to user diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index e364e86e84d7..280729325ebd 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1995,8 +1995,8 @@ static unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf) VPRINTK("ENTER\n"); - /* set scsi removeable (RMB) bit per ata bit */ - if (ata_id_removeable(args->id)) + /* set scsi removable (RMB) bit per ata bit */ + if (ata_id_removable(args->id)) hdr[1] |= (1 << 7); if (args->dev->class == ATA_DEV_ZAC) { @@ -2532,13 +2532,15 @@ static unsigned int ata_scsiop_read_cap(struct ata_scsi_args *args, u8 *rbuf) rbuf[15] = lowest_aligned; if (ata_id_has_trim(args->id)) { - rbuf[14] |= 0x80; /* TPE */ + rbuf[14] |= 0x80; /* LBPME */ - if (ata_id_has_zero_after_trim(args->id)) - rbuf[14] |= 0x40; /* TPRZ */ + if (ata_id_has_zero_after_trim(args->id) && + dev->horkage & ATA_HORKAGE_ZERO_AFTER_TRIM) { + ata_dev_info(dev, "Enabling discard_zeroes_data\n"); + rbuf[14] |= 0x40; /* LBPRZ */ + } } } - return 0; } diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index db90aa35cb71..2e86e3b85266 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -1333,7 +1333,19 @@ void ata_sff_flush_pio_task(struct ata_port *ap) DPRINTK("ENTER\n"); cancel_delayed_work_sync(&ap->sff_pio_task); + + /* + * We wanna reset the HSM state to IDLE. If we do so without + * grabbing the port lock, critical sections protected by it which + * expect the HSM state to stay stable may get surprised. For + * example, we may set IDLE in between the time + * __ata_sff_port_intr() checks for HSM_ST_IDLE and before it calls + * ata_sff_hsm_move() causing ata_sff_hsm_move() to BUG(). + */ + spin_lock_irq(ap->lock); ap->hsm_task_state = HSM_ST_IDLE; + spin_unlock_irq(ap->lock); + ap->sff_pio_task_link = NULL; if (ata_msg_ctl(ap)) diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 5f4e0cca56ec..82ebe263d2f1 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -76,7 +76,6 @@ extern unsigned ata_exec_internal_sg(struct ata_device *dev, struct ata_taskfile *tf, const u8 *cdb, int dma_dir, struct scatterlist *sg, unsigned int n_elem, unsigned long timeout); -extern unsigned int ata_do_simple_cmd(struct ata_device *dev, u8 cmd); extern int ata_wait_ready(struct ata_link *link, unsigned long deadline, int (*check_ready)(struct ata_link *link)); extern int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class, diff --git a/drivers/ata/pata_cs5530.c b/drivers/ata/pata_cs5530.c index 48ae4b434474..f9ca72e937ee 100644 --- a/drivers/ata/pata_cs5530.c +++ b/drivers/ata/pata_cs5530.c @@ -276,10 +276,8 @@ static int cs5530_init_chip(void) pci_dev_put(cs5530_0); return 0; fail_put: - if (master_0) - pci_dev_put(master_0); - if (cs5530_0) - pci_dev_put(cs5530_0); + pci_dev_put(master_0); + pci_dev_put(cs5530_0); return -ENODEV; } diff --git a/drivers/ata/pata_of_platform.c b/drivers/ata/pata_of_platform.c index dcc408abe171..b6b7af894d9d 100644 --- a/drivers/ata/pata_of_platform.c +++ b/drivers/ata/pata_of_platform.c @@ -16,6 +16,12 @@ #include <linux/ata_platform.h> #include <linux/libata.h> +#define DRV_NAME "pata_of_platform" + +static struct scsi_host_template pata_platform_sht = { + ATA_PIO_SHT(DRV_NAME), +}; + static int pata_of_platform_probe(struct platform_device *ofdev) { int ret; @@ -63,7 +69,7 @@ static int pata_of_platform_probe(struct platform_device *ofdev) pio_mask |= (1 << pio_mode) - 1; return __pata_platform_probe(&ofdev->dev, &io_res, &ctl_res, irq_res, - reg_shift, pio_mask); + reg_shift, pio_mask, &pata_platform_sht); } static struct of_device_id pata_of_platform_match[] = { @@ -74,7 +80,7 @@ MODULE_DEVICE_TABLE(of, pata_of_platform_match); static struct platform_driver pata_of_platform_driver = { .driver = { - .name = "pata_of_platform", + .name = DRV_NAME, .of_match_table = pata_of_platform_match, }, .probe = pata_of_platform_probe, diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c index 4d06a5cda987..dca8251b1aea 100644 --- a/drivers/ata/pata_pdc2027x.c +++ b/drivers/ata/pata_pdc2027x.c @@ -28,6 +28,7 @@ #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/device.h> +#include <linux/ktime.h> #include <scsi/scsi.h> #include <scsi/scsi_host.h> #include <scsi/scsi_cmnd.h> @@ -605,7 +606,7 @@ static long pdc_detect_pll_input_clock(struct ata_host *host) void __iomem *mmio_base = host->iomap[PDC_MMIO_BAR]; u32 scr; long start_count, end_count; - struct timeval start_time, end_time; + ktime_t start_time, end_time; long pll_clock, usec_elapsed; /* Start the test mode */ @@ -616,14 +617,14 @@ static long pdc_detect_pll_input_clock(struct ata_host *host) /* Read current counter value */ start_count = pdc_read_counter(host); - do_gettimeofday(&start_time); + start_time = ktime_get(); /* Let the counter run for 100 ms. */ mdelay(100); /* Read the counter values again */ end_count = pdc_read_counter(host); - do_gettimeofday(&end_time); + end_time = ktime_get(); /* Stop the test mode */ scr = ioread32(mmio_base + PDC_SYS_CTL); @@ -632,8 +633,7 @@ static long pdc_detect_pll_input_clock(struct ata_host *host) ioread32(mmio_base + PDC_SYS_CTL); /* flush */ /* calculate the input clock in Hz */ - usec_elapsed = (end_time.tv_sec - start_time.tv_sec) * 1000000 + - (end_time.tv_usec - start_time.tv_usec); + usec_elapsed = (long) ktime_us_delta(end_time, start_time); pll_clock = ((start_count - end_count) & 0x3fffffff) / 100 * (100000000 / usec_elapsed); diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c index 1eedfe46d7c8..c503ded87bb8 100644 --- a/drivers/ata/pata_platform.c +++ b/drivers/ata/pata_platform.c @@ -78,6 +78,7 @@ static void pata_platform_setup_port(struct ata_ioports *ioaddr, * @irq_res: Resource representing IRQ and its flags * @ioport_shift: I/O port shift * @__pio_mask: PIO mask + * @sht: scsi_host_template to use when registering * * Register a platform bus IDE interface. Such interfaces are PIO and we * assume do not support IRQ sharing. @@ -99,7 +100,8 @@ static void pata_platform_setup_port(struct ata_ioports *ioaddr, */ int __pata_platform_probe(struct device *dev, struct resource *io_res, struct resource *ctl_res, struct resource *irq_res, - unsigned int ioport_shift, int __pio_mask) + unsigned int ioport_shift, int __pio_mask, + struct scsi_host_template *sht) { struct ata_host *host; struct ata_port *ap; @@ -170,7 +172,7 @@ int __pata_platform_probe(struct device *dev, struct resource *io_res, /* activate */ return ata_host_activate(host, irq, irq ? ata_sff_interrupt : NULL, - irq_flags, &pata_platform_sht); + irq_flags, sht); } EXPORT_SYMBOL_GPL(__pata_platform_probe); @@ -216,7 +218,7 @@ static int pata_platform_probe(struct platform_device *pdev) return __pata_platform_probe(&pdev->dev, io_res, ctl_res, irq_res, pp_info ? pp_info->ioport_shift : 0, - pio_mask); + pio_mask, &pata_platform_sht); } static struct platform_driver pata_platform_driver = { diff --git a/drivers/ata/sata_dwc_460ex.c b/drivers/ata/sata_dwc_460ex.c index c7ddef89e7b0..fdb0f2879ea7 100644 --- a/drivers/ata/sata_dwc_460ex.c +++ b/drivers/ata/sata_dwc_460ex.c @@ -48,6 +48,18 @@ #define DRV_NAME "sata-dwc" #define DRV_VERSION "1.3" +#ifndef out_le32 +#define out_le32(a, v) __raw_writel(__cpu_to_le32(v), (void __iomem *)(a)) +#endif + +#ifndef in_le32 +#define in_le32(a) __le32_to_cpu(__raw_readl((void __iomem *)(a))) +#endif + +#ifndef NO_IRQ +#define NO_IRQ 0 +#endif + /* SATA DMA driver Globals */ #define DMA_NUM_CHANS 1 #define DMA_NUM_CHAN_REGS 8 @@ -273,7 +285,7 @@ struct sata_dwc_device { struct device *dev; /* generic device struct */ struct ata_probe_ent *pe; /* ptr to probe-ent */ struct ata_host *host; - u8 *reg_base; + u8 __iomem *reg_base; struct sata_dwc_regs *sata_dwc_regs; /* DW Synopsys SATA specific */ int irq_dma; }; @@ -323,7 +335,9 @@ struct sata_dwc_host_priv { struct device *dwc_dev; int dma_channel; }; -struct sata_dwc_host_priv host_pvt; + +static struct sata_dwc_host_priv host_pvt; + /* * Prototypes */ @@ -580,9 +594,9 @@ static int map_sg_to_lli(struct scatterlist *sg, int num_elems, sms_val = 0; dms_val = 1 + host_pvt.dma_channel; - dev_dbg(host_pvt.dwc_dev, "%s: sg=%p nelem=%d lli=%p dma_lli=0x%08x" - " dmadr=0x%08x\n", __func__, sg, num_elems, lli, (u32)dma_lli, - (u32)dmadr_addr); + dev_dbg(host_pvt.dwc_dev, + "%s: sg=%p nelem=%d lli=%p dma_lli=0x%pad dmadr=0x%p\n", + __func__, sg, num_elems, lli, &dma_lli, dmadr_addr); bl = get_burst_length_encode(AHB_DMA_BRST_DFLT); @@ -773,7 +787,7 @@ static void dma_dwc_exit(struct sata_dwc_device *hsdev) { dev_dbg(host_pvt.dwc_dev, "%s:\n", __func__); if (host_pvt.sata_dma_regs) { - iounmap(host_pvt.sata_dma_regs); + iounmap((void __iomem *)host_pvt.sata_dma_regs); host_pvt.sata_dma_regs = NULL; } @@ -797,7 +811,7 @@ static int dma_dwc_init(struct sata_dwc_device *hsdev, int irq) if (err) { dev_err(host_pvt.dwc_dev, "%s: dma_request_interrupts returns" " %d\n", __func__, err); - goto error_out; + return err; } /* Enabe DMA */ @@ -808,11 +822,6 @@ static int dma_dwc_init(struct sata_dwc_device *hsdev, int irq) sata_dma_regs); return 0; - -error_out: - dma_dwc_exit(hsdev); - - return err; } static int sata_dwc_scr_read(struct ata_link *link, unsigned int scr, u32 *val) @@ -823,7 +832,7 @@ static int sata_dwc_scr_read(struct ata_link *link, unsigned int scr, u32 *val) return -EINVAL; } - *val = in_le32((void *)link->ap->ioaddr.scr_addr + (scr * 4)); + *val = in_le32(link->ap->ioaddr.scr_addr + (scr * 4)); dev_dbg(link->ap->dev, "%s: id=%d reg=%d val=val=0x%08x\n", __func__, link->ap->print_id, scr, *val); @@ -839,21 +848,19 @@ static int sata_dwc_scr_write(struct ata_link *link, unsigned int scr, u32 val) __func__, scr); return -EINVAL; } - out_le32((void *)link->ap->ioaddr.scr_addr + (scr * 4), val); + out_le32(link->ap->ioaddr.scr_addr + (scr * 4), val); return 0; } static u32 core_scr_read(unsigned int scr) { - return in_le32((void __iomem *)(host_pvt.scr_addr_sstatus) +\ - (scr * 4)); + return in_le32(host_pvt.scr_addr_sstatus + (scr * 4)); } static void core_scr_write(unsigned int scr, u32 val) { - out_le32((void __iomem *)(host_pvt.scr_addr_sstatus) + (scr * 4), - val); + out_le32(host_pvt.scr_addr_sstatus + (scr * 4), val); } static void clear_serror(void) @@ -861,7 +868,6 @@ static void clear_serror(void) u32 val; val = core_scr_read(SCR_ERROR); core_scr_write(SCR_ERROR, val); - } static void clear_interrupt_bit(struct sata_dwc_device *hsdev, u32 bit) @@ -1261,24 +1267,24 @@ static void sata_dwc_enable_interrupts(struct sata_dwc_device *hsdev) static void sata_dwc_setup_port(struct ata_ioports *port, unsigned long base) { - port->cmd_addr = (void *)base + 0x00; - port->data_addr = (void *)base + 0x00; + port->cmd_addr = (void __iomem *)base + 0x00; + port->data_addr = (void __iomem *)base + 0x00; - port->error_addr = (void *)base + 0x04; - port->feature_addr = (void *)base + 0x04; + port->error_addr = (void __iomem *)base + 0x04; + port->feature_addr = (void __iomem *)base + 0x04; - port->nsect_addr = (void *)base + 0x08; + port->nsect_addr = (void __iomem *)base + 0x08; - port->lbal_addr = (void *)base + 0x0c; - port->lbam_addr = (void *)base + 0x10; - port->lbah_addr = (void *)base + 0x14; + port->lbal_addr = (void __iomem *)base + 0x0c; + port->lbam_addr = (void __iomem *)base + 0x10; + port->lbah_addr = (void __iomem *)base + 0x14; - port->device_addr = (void *)base + 0x18; - port->command_addr = (void *)base + 0x1c; - port->status_addr = (void *)base + 0x1c; + port->device_addr = (void __iomem *)base + 0x18; + port->command_addr = (void __iomem *)base + 0x1c; + port->status_addr = (void __iomem *)base + 0x1c; - port->altstatus_addr = (void *)base + 0x20; - port->ctl_addr = (void *)base + 0x20; + port->altstatus_addr = (void __iomem *)base + 0x20; + port->ctl_addr = (void __iomem *)base + 0x20; } /* @@ -1319,7 +1325,7 @@ static int sata_dwc_port_start(struct ata_port *ap) for (i = 0; i < SATA_DWC_QCMD_MAX; i++) hsdevp->cmd_issued[i] = SATA_DWC_CMD_ISSUED_NOT; - ap->bmdma_prd = 0; /* set these so libata doesn't use them */ + ap->bmdma_prd = NULL; /* set these so libata doesn't use them */ ap->bmdma_prd_dma = 0; /* @@ -1516,8 +1522,8 @@ static void sata_dwc_qc_prep_by_tag(struct ata_queued_cmd *qc, u8 tag) dma_chan = dma_dwc_xfer_setup(sg, qc->n_elem, hsdevp->llit[tag], hsdevp->llit_dma[tag], - (void *__iomem)(&hsdev->sata_dwc_regs->\ - dmadr), qc->dma_dir); + (void __iomem *)&hsdev->sata_dwc_regs->dmadr, + qc->dma_dir); if (dma_chan < 0) { dev_err(ap->dev, "%s: dma_dwc_xfer_setup returns err %d\n", __func__, dma_chan); @@ -1590,8 +1596,8 @@ static void sata_dwc_error_handler(struct ata_port *ap) ata_sff_error_handler(ap); } -int sata_dwc_hardreset(struct ata_link *link, unsigned int *class, - unsigned long deadline) +static int sata_dwc_hardreset(struct ata_link *link, unsigned int *class, + unsigned long deadline) { struct sata_dwc_device *hsdev = HSDEV_FROM_AP(link->ap); int ret; @@ -1623,7 +1629,7 @@ static struct scsi_host_template sata_dwc_sht = { * max of 1. This will get fixed in in a future release. */ .sg_tablesize = LIBATA_MAX_PRD, - .can_queue = ATA_DEF_QUEUE, /* ATA_MAX_QUEUE */ + /* .can_queue = ATA_MAX_QUEUE, */ .dma_boundary = ATA_DMA_BOUNDARY, }; @@ -1660,9 +1666,9 @@ static int sata_dwc_probe(struct platform_device *ofdev) struct sata_dwc_device *hsdev; u32 idr, versionr; char *ver = (char *)&versionr; - u8 *base = NULL; + u8 __iomem *base; int err = 0; - int irq, rc; + int irq; struct ata_host *host; struct ata_port_info pi = sata_dwc_port_info[0]; const struct ata_port_info *ppi[] = { &pi, NULL }; @@ -1670,12 +1676,12 @@ static int sata_dwc_probe(struct platform_device *ofdev) u32 dma_chan; /* Allocate DWC SATA device */ - hsdev = kzalloc(sizeof(*hsdev), GFP_KERNEL); - if (hsdev == NULL) { - dev_err(&ofdev->dev, "kmalloc failed for hsdev\n"); - err = -ENOMEM; - goto error; - } + host = ata_host_alloc_pinfo(&ofdev->dev, ppi, SATA_DWC_MAX_PORTS); + hsdev = devm_kzalloc(&ofdev->dev, sizeof(*hsdev), GFP_KERNEL); + if (!host || !hsdev) + return -ENOMEM; + + host->private_data = hsdev; if (of_property_read_u32(np, "dma-channel", &dma_chan)) { dev_warn(&ofdev->dev, "no dma-channel property set." @@ -1685,12 +1691,11 @@ static int sata_dwc_probe(struct platform_device *ofdev) host_pvt.dma_channel = dma_chan; /* Ioremap SATA registers */ - base = of_iomap(ofdev->dev.of_node, 0); + base = of_iomap(np, 0); if (!base) { dev_err(&ofdev->dev, "ioremap failed for SATA register" " address\n"); - err = -ENODEV; - goto error_kmalloc; + return -ENODEV; } hsdev->reg_base = base; dev_dbg(&ofdev->dev, "ioremap done for SATA register address\n"); @@ -1698,16 +1703,6 @@ static int sata_dwc_probe(struct platform_device *ofdev) /* Synopsys DWC SATA specific Registers */ hsdev->sata_dwc_regs = (void *__iomem)(base + SATA_DWC_REG_OFFSET); - /* Allocate and fill host */ - host = ata_host_alloc_pinfo(&ofdev->dev, ppi, SATA_DWC_MAX_PORTS); - if (!host) { - dev_err(&ofdev->dev, "ata_host_alloc_pinfo failed\n"); - err = -ENOMEM; - goto error_iomap; - } - - host->private_data = hsdev; - /* Setup port */ host->ports[0]->ioaddr.cmd_addr = base; host->ports[0]->ioaddr.scr_addr = base + SATA_DWC_SCR_OFFSET; @@ -1721,33 +1716,35 @@ static int sata_dwc_probe(struct platform_device *ofdev) idr, ver[0], ver[1], ver[2]); /* Get SATA DMA interrupt number */ - irq = irq_of_parse_and_map(ofdev->dev.of_node, 1); + irq = irq_of_parse_and_map(np, 1); if (irq == NO_IRQ) { dev_err(&ofdev->dev, "no SATA DMA irq\n"); err = -ENODEV; - goto error_out; + goto error_iomap; } /* Get physical SATA DMA register base address */ - host_pvt.sata_dma_regs = of_iomap(ofdev->dev.of_node, 1); + host_pvt.sata_dma_regs = (void *)of_iomap(np, 1); if (!(host_pvt.sata_dma_regs)) { dev_err(&ofdev->dev, "ioremap failed for AHBDMA register" " address\n"); err = -ENODEV; - goto error_out; + goto error_iomap; } /* Save dev for later use in dev_xxx() routines */ host_pvt.dwc_dev = &ofdev->dev; /* Initialize AHB DMAC */ - dma_dwc_init(hsdev, irq); + err = dma_dwc_init(hsdev, irq); + if (err) + goto error_dma_iomap; /* Enable SATA Interrupts */ sata_dwc_enable_interrupts(hsdev); /* Get SATA interrupt number */ - irq = irq_of_parse_and_map(ofdev->dev.of_node, 0); + irq = irq_of_parse_and_map(np, 0); if (irq == NO_IRQ) { dev_err(&ofdev->dev, "no SATA DMA irq\n"); err = -ENODEV; @@ -1759,9 +1756,8 @@ static int sata_dwc_probe(struct platform_device *ofdev) * device discovery process, invoking our port_start() handler & * error_handler() to execute a dummy Softreset EH session */ - rc = ata_host_activate(host, irq, sata_dwc_isr, 0, &sata_dwc_sht); - - if (rc != 0) + err = ata_host_activate(host, irq, sata_dwc_isr, 0, &sata_dwc_sht); + if (err) dev_err(&ofdev->dev, "failed to activate host"); dev_set_drvdata(&ofdev->dev, host); @@ -1770,12 +1766,10 @@ static int sata_dwc_probe(struct platform_device *ofdev) error_out: /* Free SATA DMA resources */ dma_dwc_exit(hsdev); - +error_dma_iomap: + iounmap((void __iomem *)host_pvt.sata_dma_regs); error_iomap: iounmap(base); -error_kmalloc: - kfree(hsdev); -error: return err; } @@ -1786,14 +1780,12 @@ static int sata_dwc_remove(struct platform_device *ofdev) struct sata_dwc_device *hsdev = host->private_data; ata_host_detach(host); - dev_set_drvdata(dev, NULL); /* Free SATA DMA resources */ dma_dwc_exit(hsdev); + iounmap((void __iomem *)host_pvt.sata_dma_regs); iounmap(hsdev->reg_base); - kfree(hsdev); - kfree(host); dev_dbg(&ofdev->dev, "done\n"); return 0; } diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index f9a0e34eb111..f8c33e3772b8 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -4185,8 +4185,7 @@ err: clk_disable_unprepare(hpriv->port_clks[port]); clk_put(hpriv->port_clks[port]); } - if (hpriv->port_phys[port]) - phy_power_off(hpriv->port_phys[port]); + phy_power_off(hpriv->port_phys[port]); } return rc; @@ -4216,8 +4215,7 @@ static int mv_platform_remove(struct platform_device *pdev) clk_disable_unprepare(hpriv->port_clks[port]); clk_put(hpriv->port_clks[port]); } - if (hpriv->port_phys[port]) - phy_power_off(hpriv->port_phys[port]); + phy_power_off(hpriv->port_phys[port]); } return 0; } diff --git a/drivers/ata/sata_rcar.c b/drivers/ata/sata_rcar.c index cb0d2e644af5..d49a5193b7de 100644 --- a/drivers/ata/sata_rcar.c +++ b/drivers/ata/sata_rcar.c @@ -2,8 +2,8 @@ * Renesas R-Car SATA driver * * Author: Vladimir Barinov <source@cogentembedded.com> - * Copyright (C) 2013 Cogent Embedded, Inc. - * Copyright (C) 2013 Renesas Solutions Corp. + * Copyright (C) 2013-2015 Cogent Embedded, Inc. + * Copyright (C) 2013-2015 Renesas Solutions Corp. * * 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 @@ -992,9 +992,30 @@ static int sata_rcar_resume(struct device *dev) return 0; } +static int sata_rcar_restore(struct device *dev) +{ + struct ata_host *host = dev_get_drvdata(dev); + struct sata_rcar_priv *priv = host->private_data; + + clk_prepare_enable(priv->clk); + + sata_rcar_setup_port(host); + + /* initialize host controller */ + sata_rcar_init_controller(host); + + ata_host_resume(host); + + return 0; +} + static const struct dev_pm_ops sata_rcar_pm_ops = { .suspend = sata_rcar_suspend, .resume = sata_rcar_resume, + .freeze = sata_rcar_suspend, + .thaw = sata_rcar_resume, + .poweroff = sata_rcar_suspend, + .restore = sata_rcar_restore, }; #endif diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index d81b20ddb527..ea655949023f 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -246,7 +246,7 @@ enum { /* host flags */ SIL24_COMMON_FLAGS = ATA_FLAG_SATA | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ | ATA_FLAG_ACPI_SATA | - ATA_FLAG_AN | ATA_FLAG_PMP, + ATA_FLAG_AN | ATA_FLAG_PMP | ATA_FLAG_LOWTAG, SIL24_FLAG_PCIX_IRQ_WOC = (1 << 24), /* IRQ loss errata on PCI-X */ IRQ_STAT_4PORTS = 0xf, diff --git a/drivers/atm/eni.c b/drivers/atm/eni.c index c7fab3ee14ee..6339efd32697 100644 --- a/drivers/atm/eni.c +++ b/drivers/atm/eni.c @@ -354,9 +354,9 @@ static int do_rx_dma(struct atm_vcc *vcc,struct sk_buff *skb, eni_vcc = ENI_VCC(vcc); paddr = 0; /* GCC, shut up */ if (skb) { - paddr = pci_map_single(eni_dev->pci_dev,skb->data,skb->len, - PCI_DMA_FROMDEVICE); - if (pci_dma_mapping_error(eni_dev->pci_dev, paddr)) + paddr = dma_map_single(&eni_dev->pci_dev->dev,skb->data,skb->len, + DMA_FROM_DEVICE); + if (dma_mapping_error(&eni_dev->pci_dev->dev, paddr)) goto dma_map_error; ENI_PRV_PADDR(skb) = paddr; if (paddr & 3) @@ -481,8 +481,8 @@ rx_enqueued++; trouble: if (paddr) - pci_unmap_single(eni_dev->pci_dev,paddr,skb->len, - PCI_DMA_FROMDEVICE); + dma_unmap_single(&eni_dev->pci_dev->dev,paddr,skb->len, + DMA_FROM_DEVICE); dma_map_error: if (skb) dev_kfree_skb_irq(skb); return -1; @@ -758,8 +758,8 @@ rx_dequeued++; } eni_vcc->rxing--; eni_vcc->rx_pos = ENI_PRV_POS(skb) & (eni_vcc->words-1); - pci_unmap_single(eni_dev->pci_dev,ENI_PRV_PADDR(skb),skb->len, - PCI_DMA_TODEVICE); + dma_unmap_single(&eni_dev->pci_dev->dev,ENI_PRV_PADDR(skb),skb->len, + DMA_TO_DEVICE); if (!skb->len) dev_kfree_skb_irq(skb); else { EVENT("pushing (len=%ld)\n",skb->len,0); @@ -1112,8 +1112,8 @@ DPRINTK("iovcnt = %d\n",skb_shinfo(skb)->nr_frags); vcc->dev->number); return enq_jam; } - paddr = pci_map_single(eni_dev->pci_dev,skb->data,skb->len, - PCI_DMA_TODEVICE); + paddr = dma_map_single(&eni_dev->pci_dev->dev,skb->data,skb->len, + DMA_TO_DEVICE); ENI_PRV_PADDR(skb) = paddr; /* prepare DMA queue entries */ j = 0; @@ -1226,8 +1226,8 @@ static void dequeue_tx(struct atm_dev *dev) break; } ENI_VCC(vcc)->txing -= ENI_PRV_SIZE(skb); - pci_unmap_single(eni_dev->pci_dev,ENI_PRV_PADDR(skb),skb->len, - PCI_DMA_TODEVICE); + dma_unmap_single(&eni_dev->pci_dev->dev,ENI_PRV_PADDR(skb),skb->len, + DMA_TO_DEVICE); if (vcc->pop) vcc->pop(vcc,skb); else dev_kfree_skb_irq(skb); atomic_inc(&vcc->stats->tx); @@ -2240,13 +2240,18 @@ static int eni_init_one(struct pci_dev *pci_dev, if (rc < 0) goto out; + rc = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(32)); + if (rc < 0) + goto out; + rc = -ENOMEM; eni_dev = kmalloc(sizeof(struct eni_dev), GFP_KERNEL); if (!eni_dev) goto err_disable; zero = &eni_dev->zero; - zero->addr = pci_alloc_consistent(pci_dev, ENI_ZEROES_SIZE, &zero->dma); + zero->addr = dma_alloc_coherent(&pci_dev->dev, + ENI_ZEROES_SIZE, &zero->dma, GFP_KERNEL); if (!zero->addr) goto err_kfree; @@ -2277,7 +2282,7 @@ err_eni_release: err_unregister: atm_dev_deregister(dev); err_free_consistent: - pci_free_consistent(pci_dev, ENI_ZEROES_SIZE, zero->addr, zero->dma); + dma_free_coherent(&pci_dev->dev, ENI_ZEROES_SIZE, zero->addr, zero->dma); err_kfree: kfree(eni_dev); err_disable: @@ -2302,7 +2307,7 @@ static void eni_remove_one(struct pci_dev *pdev) eni_do_release(dev); atm_dev_deregister(dev); - pci_free_consistent(pdev, ENI_ZEROES_SIZE, zero->addr, zero->dma); + dma_free_coherent(&pdev->dev, ENI_ZEROES_SIZE, zero->addr, zero->dma); kfree(ed); pci_disable_device(pdev); } diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c index d5d9eafbbfcf..75dde903b238 100644 --- a/drivers/atm/fore200e.c +++ b/drivers/atm/fore200e.c @@ -425,7 +425,7 @@ static void fore200e_pca_write(u32 val, volatile u32 __iomem *addr) static u32 fore200e_pca_dma_map(struct fore200e* fore200e, void* virt_addr, int size, int direction) { - u32 dma_addr = pci_map_single((struct pci_dev*)fore200e->bus_dev, virt_addr, size, direction); + u32 dma_addr = dma_map_single(&((struct pci_dev *) fore200e->bus_dev)->dev, virt_addr, size, direction); DPRINTK(3, "PCI DVMA mapping: virt_addr = 0x%p, size = %d, direction = %d, --> dma_addr = 0x%08x\n", virt_addr, size, direction, dma_addr); @@ -440,7 +440,7 @@ fore200e_pca_dma_unmap(struct fore200e* fore200e, u32 dma_addr, int size, int di DPRINTK(3, "PCI DVMA unmapping: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction); - pci_unmap_single((struct pci_dev*)fore200e->bus_dev, dma_addr, size, direction); + dma_unmap_single(&((struct pci_dev *) fore200e->bus_dev)->dev, dma_addr, size, direction); } @@ -449,7 +449,7 @@ fore200e_pca_dma_sync_for_cpu(struct fore200e* fore200e, u32 dma_addr, int size, { DPRINTK(3, "PCI DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction); - pci_dma_sync_single_for_cpu((struct pci_dev*)fore200e->bus_dev, dma_addr, size, direction); + dma_sync_single_for_cpu(&((struct pci_dev *) fore200e->bus_dev)->dev, dma_addr, size, direction); } static void @@ -457,7 +457,7 @@ fore200e_pca_dma_sync_for_device(struct fore200e* fore200e, u32 dma_addr, int si { DPRINTK(3, "PCI DVMA sync: dma_addr = 0x%08x, size = %d, direction = %d\n", dma_addr, size, direction); - pci_dma_sync_single_for_device((struct pci_dev*)fore200e->bus_dev, dma_addr, size, direction); + dma_sync_single_for_device(&((struct pci_dev *) fore200e->bus_dev)->dev, dma_addr, size, direction); } @@ -470,9 +470,10 @@ fore200e_pca_dma_chunk_alloc(struct fore200e* fore200e, struct chunk* chunk, { /* returned chunks are page-aligned */ chunk->alloc_size = size * nbr; - chunk->alloc_addr = pci_alloc_consistent((struct pci_dev*)fore200e->bus_dev, - chunk->alloc_size, - &chunk->dma_addr); + chunk->alloc_addr = dma_alloc_coherent(&((struct pci_dev *) fore200e->bus_dev)->dev, + chunk->alloc_size, + &chunk->dma_addr, + GFP_KERNEL); if ((chunk->alloc_addr == NULL) || (chunk->dma_addr == 0)) return -ENOMEM; @@ -488,7 +489,7 @@ fore200e_pca_dma_chunk_alloc(struct fore200e* fore200e, struct chunk* chunk, static void fore200e_pca_dma_chunk_free(struct fore200e* fore200e, struct chunk* chunk) { - pci_free_consistent((struct pci_dev*)fore200e->bus_dev, + dma_free_coherent(&((struct pci_dev *) fore200e->bus_dev)->dev, chunk->alloc_size, chunk->alloc_addr, chunk->dma_addr); @@ -2707,6 +2708,11 @@ static int fore200e_pca_detect(struct pci_dev *pci_dev, err = -EINVAL; goto out; } + + if (dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(32))) { + err = -EINVAL; + goto out; + } fore200e = kzalloc(sizeof(struct fore200e), GFP_KERNEL); if (fore200e == NULL) { diff --git a/drivers/atm/he.c b/drivers/atm/he.c index c39702bc279d..93dca2e73bf5 100644 --- a/drivers/atm/he.c +++ b/drivers/atm/he.c @@ -359,7 +359,7 @@ static int he_init_one(struct pci_dev *pci_dev, if (pci_enable_device(pci_dev)) return -EIO; - if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32)) != 0) { + if (dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(32)) != 0) { printk(KERN_WARNING "he: no suitable dma available\n"); err = -EIO; goto init_one_failure; @@ -533,9 +533,9 @@ static void he_init_tx_lbfp(struct he_dev *he_dev) static int he_init_tpdrq(struct he_dev *he_dev) { - he_dev->tpdrq_base = pci_zalloc_consistent(he_dev->pci_dev, - CONFIG_TPDRQ_SIZE * sizeof(struct he_tpdrq), - &he_dev->tpdrq_phys); + he_dev->tpdrq_base = dma_zalloc_coherent(&he_dev->pci_dev->dev, + CONFIG_TPDRQ_SIZE * sizeof(struct he_tpdrq), + &he_dev->tpdrq_phys, GFP_KERNEL); if (he_dev->tpdrq_base == NULL) { hprintk("failed to alloc tpdrq\n"); return -ENOMEM; @@ -796,16 +796,16 @@ static int he_init_group(struct he_dev *he_dev, int group) } /* large buffer pool */ - he_dev->rbpl_pool = pci_pool_create("rbpl", he_dev->pci_dev, + he_dev->rbpl_pool = dma_pool_create("rbpl", &he_dev->pci_dev->dev, CONFIG_RBPL_BUFSIZE, 64, 0); if (he_dev->rbpl_pool == NULL) { hprintk("unable to create rbpl pool\n"); goto out_free_rbpl_virt; } - he_dev->rbpl_base = pci_zalloc_consistent(he_dev->pci_dev, - CONFIG_RBPL_SIZE * sizeof(struct he_rbp), - &he_dev->rbpl_phys); + he_dev->rbpl_base = dma_zalloc_coherent(&he_dev->pci_dev->dev, + CONFIG_RBPL_SIZE * sizeof(struct he_rbp), + &he_dev->rbpl_phys, GFP_KERNEL); if (he_dev->rbpl_base == NULL) { hprintk("failed to alloc rbpl_base\n"); goto out_destroy_rbpl_pool; @@ -815,7 +815,7 @@ static int he_init_group(struct he_dev *he_dev, int group) for (i = 0; i < CONFIG_RBPL_SIZE; ++i) { - heb = pci_pool_alloc(he_dev->rbpl_pool, GFP_KERNEL|GFP_DMA, &mapping); + heb = dma_pool_alloc(he_dev->rbpl_pool, GFP_KERNEL, &mapping); if (!heb) goto out_free_rbpl; heb->mapping = mapping; @@ -842,9 +842,9 @@ static int he_init_group(struct he_dev *he_dev, int group) /* rx buffer ready queue */ - he_dev->rbrq_base = pci_zalloc_consistent(he_dev->pci_dev, - CONFIG_RBRQ_SIZE * sizeof(struct he_rbrq), - &he_dev->rbrq_phys); + he_dev->rbrq_base = dma_zalloc_coherent(&he_dev->pci_dev->dev, + CONFIG_RBRQ_SIZE * sizeof(struct he_rbrq), + &he_dev->rbrq_phys, GFP_KERNEL); if (he_dev->rbrq_base == NULL) { hprintk("failed to allocate rbrq\n"); goto out_free_rbpl; @@ -866,9 +866,9 @@ static int he_init_group(struct he_dev *he_dev, int group) /* tx buffer ready queue */ - he_dev->tbrq_base = pci_zalloc_consistent(he_dev->pci_dev, - CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq), - &he_dev->tbrq_phys); + he_dev->tbrq_base = dma_zalloc_coherent(&he_dev->pci_dev->dev, + CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq), + &he_dev->tbrq_phys, GFP_KERNEL); if (he_dev->tbrq_base == NULL) { hprintk("failed to allocate tbrq\n"); goto out_free_rbpq_base; @@ -884,18 +884,18 @@ static int he_init_group(struct he_dev *he_dev, int group) return 0; out_free_rbpq_base: - pci_free_consistent(he_dev->pci_dev, CONFIG_RBRQ_SIZE * - sizeof(struct he_rbrq), he_dev->rbrq_base, - he_dev->rbrq_phys); + dma_free_coherent(&he_dev->pci_dev->dev, CONFIG_RBRQ_SIZE * + sizeof(struct he_rbrq), he_dev->rbrq_base, + he_dev->rbrq_phys); out_free_rbpl: list_for_each_entry_safe(heb, next, &he_dev->rbpl_outstanding, entry) - pci_pool_free(he_dev->rbpl_pool, heb, heb->mapping); + dma_pool_free(he_dev->rbpl_pool, heb, heb->mapping); - pci_free_consistent(he_dev->pci_dev, CONFIG_RBPL_SIZE * - sizeof(struct he_rbp), he_dev->rbpl_base, - he_dev->rbpl_phys); + dma_free_coherent(&he_dev->pci_dev->dev, CONFIG_RBPL_SIZE * + sizeof(struct he_rbp), he_dev->rbpl_base, + he_dev->rbpl_phys); out_destroy_rbpl_pool: - pci_pool_destroy(he_dev->rbpl_pool); + dma_pool_destroy(he_dev->rbpl_pool); out_free_rbpl_virt: kfree(he_dev->rbpl_virt); out_free_rbpl_table: @@ -911,8 +911,11 @@ static int he_init_irq(struct he_dev *he_dev) /* 2.9.3.5 tail offset for each interrupt queue is located after the end of the interrupt queue */ - he_dev->irq_base = pci_alloc_consistent(he_dev->pci_dev, - (CONFIG_IRQ_SIZE+1) * sizeof(struct he_irq), &he_dev->irq_phys); + he_dev->irq_base = dma_zalloc_coherent(&he_dev->pci_dev->dev, + (CONFIG_IRQ_SIZE + 1) + * sizeof(struct he_irq), + &he_dev->irq_phys, + GFP_KERNEL); if (he_dev->irq_base == NULL) { hprintk("failed to allocate irq\n"); return -ENOMEM; @@ -1419,10 +1422,10 @@ static int he_start(struct atm_dev *dev) he_init_tpdrq(he_dev); - he_dev->tpd_pool = pci_pool_create("tpd", he_dev->pci_dev, - sizeof(struct he_tpd), TPD_ALIGNMENT, 0); + he_dev->tpd_pool = dma_pool_create("tpd", &he_dev->pci_dev->dev, + sizeof(struct he_tpd), TPD_ALIGNMENT, 0); if (he_dev->tpd_pool == NULL) { - hprintk("unable to create tpd pci_pool\n"); + hprintk("unable to create tpd dma_pool\n"); return -ENOMEM; } @@ -1459,9 +1462,9 @@ static int he_start(struct atm_dev *dev) /* host status page */ - he_dev->hsp = pci_zalloc_consistent(he_dev->pci_dev, - sizeof(struct he_hsp), - &he_dev->hsp_phys); + he_dev->hsp = dma_zalloc_coherent(&he_dev->pci_dev->dev, + sizeof(struct he_hsp), + &he_dev->hsp_phys, GFP_KERNEL); if (he_dev->hsp == NULL) { hprintk("failed to allocate host status page\n"); return -ENOMEM; @@ -1558,41 +1561,41 @@ he_stop(struct he_dev *he_dev) free_irq(he_dev->irq, he_dev); if (he_dev->irq_base) - pci_free_consistent(he_dev->pci_dev, (CONFIG_IRQ_SIZE+1) - * sizeof(struct he_irq), he_dev->irq_base, he_dev->irq_phys); + dma_free_coherent(&he_dev->pci_dev->dev, (CONFIG_IRQ_SIZE + 1) + * sizeof(struct he_irq), he_dev->irq_base, he_dev->irq_phys); if (he_dev->hsp) - pci_free_consistent(he_dev->pci_dev, sizeof(struct he_hsp), - he_dev->hsp, he_dev->hsp_phys); + dma_free_coherent(&he_dev->pci_dev->dev, sizeof(struct he_hsp), + he_dev->hsp, he_dev->hsp_phys); if (he_dev->rbpl_base) { list_for_each_entry_safe(heb, next, &he_dev->rbpl_outstanding, entry) - pci_pool_free(he_dev->rbpl_pool, heb, heb->mapping); + dma_pool_free(he_dev->rbpl_pool, heb, heb->mapping); - pci_free_consistent(he_dev->pci_dev, CONFIG_RBPL_SIZE - * sizeof(struct he_rbp), he_dev->rbpl_base, he_dev->rbpl_phys); + dma_free_coherent(&he_dev->pci_dev->dev, CONFIG_RBPL_SIZE + * sizeof(struct he_rbp), he_dev->rbpl_base, he_dev->rbpl_phys); } kfree(he_dev->rbpl_virt); kfree(he_dev->rbpl_table); if (he_dev->rbpl_pool) - pci_pool_destroy(he_dev->rbpl_pool); + dma_pool_destroy(he_dev->rbpl_pool); if (he_dev->rbrq_base) - pci_free_consistent(he_dev->pci_dev, CONFIG_RBRQ_SIZE * sizeof(struct he_rbrq), - he_dev->rbrq_base, he_dev->rbrq_phys); + dma_free_coherent(&he_dev->pci_dev->dev, CONFIG_RBRQ_SIZE * sizeof(struct he_rbrq), + he_dev->rbrq_base, he_dev->rbrq_phys); if (he_dev->tbrq_base) - pci_free_consistent(he_dev->pci_dev, CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq), - he_dev->tbrq_base, he_dev->tbrq_phys); + dma_free_coherent(&he_dev->pci_dev->dev, CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq), + he_dev->tbrq_base, he_dev->tbrq_phys); if (he_dev->tpdrq_base) - pci_free_consistent(he_dev->pci_dev, CONFIG_TBRQ_SIZE * sizeof(struct he_tbrq), - he_dev->tpdrq_base, he_dev->tpdrq_phys); + 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) - pci_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); @@ -1610,7 +1613,7 @@ __alloc_tpd(struct he_dev *he_dev) struct he_tpd *tpd; dma_addr_t mapping; - tpd = pci_pool_alloc(he_dev->tpd_pool, GFP_ATOMIC|GFP_DMA, &mapping); + tpd = dma_pool_alloc(he_dev->tpd_pool, GFP_ATOMIC, &mapping); if (tpd == NULL) return NULL; @@ -1681,7 +1684,7 @@ he_service_rbrq(struct he_dev *he_dev, int group) if (!RBRQ_HBUF_ERR(he_dev->rbrq_head)) { clear_bit(i, he_dev->rbpl_table); list_del(&heb->entry); - pci_pool_free(he_dev->rbpl_pool, heb, heb->mapping); + dma_pool_free(he_dev->rbpl_pool, heb, heb->mapping); } goto next_rbrq_entry; @@ -1774,7 +1777,7 @@ return_host_buffers: ++pdus_assembled; list_for_each_entry_safe(heb, next, &he_vcc->buffers, entry) - pci_pool_free(he_dev->rbpl_pool, heb, heb->mapping); + dma_pool_free(he_dev->rbpl_pool, heb, heb->mapping); INIT_LIST_HEAD(&he_vcc->buffers); he_vcc->pdu_len = 0; @@ -1843,10 +1846,10 @@ he_service_tbrq(struct he_dev *he_dev, int group) for (slot = 0; slot < TPD_MAXIOV; ++slot) { if (tpd->iovec[slot].addr) - pci_unmap_single(he_dev->pci_dev, + dma_unmap_single(&he_dev->pci_dev->dev, tpd->iovec[slot].addr, tpd->iovec[slot].len & TPD_LEN_MASK, - PCI_DMA_TODEVICE); + DMA_TO_DEVICE); if (tpd->iovec[slot].len & TPD_LST) break; @@ -1861,7 +1864,7 @@ he_service_tbrq(struct he_dev *he_dev, int group) next_tbrq_entry: if (tpd) - pci_pool_free(he_dev->tpd_pool, tpd, TPD_ADDR(tpd->status)); + dma_pool_free(he_dev->tpd_pool, tpd, TPD_ADDR(tpd->status)); he_dev->tbrq_head = (struct he_tbrq *) ((unsigned long) he_dev->tbrq_base | TBRQ_MASK(he_dev->tbrq_head + 1)); @@ -1905,7 +1908,7 @@ he_service_rbpl(struct he_dev *he_dev, int group) } he_dev->rbpl_hint = i + 1; - heb = pci_pool_alloc(he_dev->rbpl_pool, GFP_ATOMIC|GFP_DMA, &mapping); + heb = dma_pool_alloc(he_dev->rbpl_pool, GFP_ATOMIC, &mapping); if (!heb) break; heb->mapping = mapping; @@ -2084,10 +2087,10 @@ __enqueue_tpd(struct he_dev *he_dev, struct he_tpd *tpd, unsigned cid) */ for (slot = 0; slot < TPD_MAXIOV; ++slot) { if (tpd->iovec[slot].addr) - pci_unmap_single(he_dev->pci_dev, + dma_unmap_single(&he_dev->pci_dev->dev, tpd->iovec[slot].addr, tpd->iovec[slot].len & TPD_LEN_MASK, - PCI_DMA_TODEVICE); + DMA_TO_DEVICE); } if (tpd->skb) { if (tpd->vcc->pop) @@ -2096,7 +2099,7 @@ __enqueue_tpd(struct he_dev *he_dev, struct he_tpd *tpd, unsigned cid) dev_kfree_skb_any(tpd->skb); atomic_inc(&tpd->vcc->stats->tx_err); } - pci_pool_free(he_dev->tpd_pool, tpd, TPD_ADDR(tpd->status)); + dma_pool_free(he_dev->tpd_pool, tpd, TPD_ADDR(tpd->status)); return; } } @@ -2550,8 +2553,8 @@ he_send(struct atm_vcc *vcc, struct sk_buff *skb) } #ifdef USE_SCATTERGATHER - tpd->iovec[slot].addr = pci_map_single(he_dev->pci_dev, skb->data, - skb_headlen(skb), PCI_DMA_TODEVICE); + tpd->iovec[slot].addr = dma_map_single(&he_dev->pci_dev->dev, skb->data, + skb_headlen(skb), DMA_TO_DEVICE); tpd->iovec[slot].len = skb_headlen(skb); ++slot; @@ -2579,9 +2582,9 @@ he_send(struct atm_vcc *vcc, struct sk_buff *skb) slot = 0; } - tpd->iovec[slot].addr = pci_map_single(he_dev->pci_dev, + tpd->iovec[slot].addr = dma_map_single(&he_dev->pci_dev->dev, (void *) page_address(frag->page) + frag->page_offset, - frag->size, PCI_DMA_TODEVICE); + frag->size, DMA_TO_DEVICE); tpd->iovec[slot].len = frag->size; ++slot; @@ -2589,7 +2592,7 @@ he_send(struct atm_vcc *vcc, struct sk_buff *skb) tpd->iovec[slot - 1].len |= TPD_LST; #else - tpd->address0 = pci_map_single(he_dev->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE); + tpd->address0 = dma_map_single(&he_dev->pci_dev->dev, skb->data, skb->len, DMA_TO_DEVICE); tpd->length0 = skb->len | TPD_LST; #endif tpd->status |= TPD_INT; diff --git a/drivers/atm/he.h b/drivers/atm/he.h index 110a27d2ecfc..f3f53674ef3f 100644 --- a/drivers/atm/he.h +++ b/drivers/atm/he.h @@ -281,7 +281,7 @@ struct he_dev { int irq_peak; struct tasklet_struct tasklet; - struct pci_pool *tpd_pool; + struct dma_pool *tpd_pool; struct list_head outstanding_tpds; dma_addr_t tpdrq_phys; @@ -296,7 +296,7 @@ struct he_dev { struct he_buff **rbpl_virt; unsigned long *rbpl_table; unsigned long rbpl_hint; - struct pci_pool *rbpl_pool; + struct dma_pool *rbpl_pool; dma_addr_t rbpl_phys; struct he_rbp *rbpl_base, *rbpl_tail; struct list_head rbpl_outstanding; diff --git a/drivers/atm/horizon.c b/drivers/atm/horizon.c index 1dc0519333f2..527bbd595e37 100644 --- a/drivers/atm/horizon.c +++ b/drivers/atm/horizon.c @@ -458,12 +458,6 @@ static inline void update_tx_channel_config (hrz_dev * dev, short chan, u8 mode, return; } -static inline u16 query_tx_channel_config (hrz_dev * dev, short chan, u8 mode) { - wr_regw (dev, TX_CHANNEL_CONFIG_COMMAND_OFF, - chan * TX_CHANNEL_CONFIG_MULT | mode); - return rd_regw (dev, TX_CHANNEL_CONFIG_DATA_OFF); -} - /********** dump functions **********/ static inline void dump_skb (char * prefix, unsigned int vc, struct sk_buff * skb) { @@ -513,16 +507,6 @@ static inline void dump_framer (hrz_dev * dev) { /* RX channels are 10 bit integers, these fns are quite paranoid */ -static inline int channel_to_vpivci (const u16 channel, short * vpi, int * vci) { - unsigned short vci_bits = 10 - vpi_bits; - if ((channel & RX_CHANNEL_MASK) == channel) { - *vci = channel & ((~0)<<vci_bits); - *vpi = channel >> vci_bits; - return channel ? 0 : -EINVAL; - } - return -EINVAL; -} - static inline int vpivci_to_channel (u16 * channel, const short vpi, const int vci) { unsigned short vci_bits = 10 - vpi_bits; if (0 <= vpi && vpi < 1<<vpi_bits && 0 <= vci && vci < 1<<vci_bits) { @@ -1260,14 +1244,6 @@ static u32 rx_queue_entry_next (hrz_dev * dev) { return rx_queue_entry; } -/********** handle RX disabled by device **********/ - -static inline void rx_disabled_handler (hrz_dev * dev) { - wr_regw (dev, RX_CONFIG_OFF, rd_regw (dev, RX_CONFIG_OFF) | RX_ENABLE); - // count me please - PRINTK (KERN_WARNING, "RX was disabled!"); -} - /********** handle RX data received by device **********/ // called from IRQ handler diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c index 2b24ed056728..074616b39f4d 100644 --- a/drivers/atm/idt77252.c +++ b/drivers/atm/idt77252.c @@ -641,7 +641,8 @@ alloc_scq(struct idt77252_dev *card, int class) scq = kzalloc(sizeof(struct scq_info), GFP_KERNEL); if (!scq) return NULL; - scq->base = pci_zalloc_consistent(card->pcidev, SCQ_SIZE, &scq->paddr); + scq->base = dma_zalloc_coherent(&card->pcidev->dev, SCQ_SIZE, + &scq->paddr, GFP_KERNEL); if (scq->base == NULL) { kfree(scq); return NULL; @@ -669,12 +670,12 @@ free_scq(struct idt77252_dev *card, struct scq_info *scq) struct sk_buff *skb; struct atm_vcc *vcc; - pci_free_consistent(card->pcidev, SCQ_SIZE, - scq->base, scq->paddr); + dma_free_coherent(&card->pcidev->dev, SCQ_SIZE, + scq->base, scq->paddr); while ((skb = skb_dequeue(&scq->transmit))) { - pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb), - skb->len, PCI_DMA_TODEVICE); + dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), + skb->len, DMA_TO_DEVICE); vcc = ATM_SKB(skb)->vcc; if (vcc->pop) @@ -684,8 +685,8 @@ free_scq(struct idt77252_dev *card, struct scq_info *scq) } while ((skb = skb_dequeue(&scq->pending))) { - pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb), - skb->len, PCI_DMA_TODEVICE); + dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), + skb->len, DMA_TO_DEVICE); vcc = ATM_SKB(skb)->vcc; if (vcc->pop) @@ -800,8 +801,8 @@ drain_scq(struct idt77252_dev *card, struct vc_map *vc) if (skb) { TXPRINTK("%s: freeing skb at %p.\n", card->name, skb); - pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb), - skb->len, PCI_DMA_TODEVICE); + dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), + skb->len, DMA_TO_DEVICE); vcc = ATM_SKB(skb)->vcc; @@ -846,8 +847,8 @@ queue_skb(struct idt77252_dev *card, struct vc_map *vc, tbd = &IDT77252_PRV_TBD(skb); vcc = ATM_SKB(skb)->vcc; - IDT77252_PRV_PADDR(skb) = pci_map_single(card->pcidev, skb->data, - skb->len, PCI_DMA_TODEVICE); + IDT77252_PRV_PADDR(skb) = dma_map_single(&card->pcidev->dev, skb->data, + skb->len, DMA_TO_DEVICE); error = -EINVAL; @@ -924,8 +925,8 @@ done: return 0; errout: - pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb), - skb->len, PCI_DMA_TODEVICE); + dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), + skb->len, DMA_TO_DEVICE); return error; } @@ -970,8 +971,8 @@ init_rsq(struct idt77252_dev *card) { struct rsq_entry *rsqe; - card->rsq.base = pci_zalloc_consistent(card->pcidev, RSQSIZE, - &card->rsq.paddr); + card->rsq.base = dma_zalloc_coherent(&card->pcidev->dev, RSQSIZE, + &card->rsq.paddr, GFP_KERNEL); if (card->rsq.base == NULL) { printk("%s: can't allocate RSQ.\n", card->name); return -1; @@ -1001,8 +1002,8 @@ init_rsq(struct idt77252_dev *card) static void deinit_rsq(struct idt77252_dev *card) { - pci_free_consistent(card->pcidev, RSQSIZE, - card->rsq.base, card->rsq.paddr); + dma_free_coherent(&card->pcidev->dev, RSQSIZE, + card->rsq.base, card->rsq.paddr); } static void @@ -1057,9 +1058,9 @@ dequeue_rx(struct idt77252_dev *card, struct rsq_entry *rsqe) vcc = vc->rx_vcc; - pci_dma_sync_single_for_cpu(card->pcidev, IDT77252_PRV_PADDR(skb), - skb_end_pointer(skb) - skb->data, - PCI_DMA_FROMDEVICE); + dma_sync_single_for_cpu(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), + skb_end_pointer(skb) - skb->data, + DMA_FROM_DEVICE); if ((vcc->qos.aal == ATM_AAL0) || (vcc->qos.aal == ATM_AAL34)) { @@ -1180,9 +1181,9 @@ dequeue_rx(struct idt77252_dev *card, struct rsq_entry *rsqe) return; } - pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb), + dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), skb_end_pointer(skb) - skb->data, - PCI_DMA_FROMDEVICE); + DMA_FROM_DEVICE); sb_pool_remove(card, skb); skb_trim(skb, len); @@ -1254,9 +1255,9 @@ idt77252_rx_raw(struct idt77252_dev *card) head = IDT77252_PRV_PADDR(queue) + (queue->data - queue->head - 16); tail = readl(SAR_REG_RAWCT); - pci_dma_sync_single_for_cpu(card->pcidev, IDT77252_PRV_PADDR(queue), - skb_end_offset(queue) - 16, - PCI_DMA_FROMDEVICE); + dma_sync_single_for_cpu(&card->pcidev->dev, IDT77252_PRV_PADDR(queue), + skb_end_offset(queue) - 16, + DMA_FROM_DEVICE); while (head != tail) { unsigned int vpi, vci; @@ -1348,11 +1349,11 @@ drop: if (next) { card->raw_cell_head = next; queue = card->raw_cell_head; - pci_dma_sync_single_for_cpu(card->pcidev, - IDT77252_PRV_PADDR(queue), - (skb_end_pointer(queue) - - queue->data), - PCI_DMA_FROMDEVICE); + dma_sync_single_for_cpu(&card->pcidev->dev, + IDT77252_PRV_PADDR(queue), + (skb_end_pointer(queue) - + queue->data), + DMA_FROM_DEVICE); } else { card->raw_cell_head = NULL; printk("%s: raw cell queue overrun\n", @@ -1375,8 +1376,8 @@ init_tsq(struct idt77252_dev *card) { struct tsq_entry *tsqe; - card->tsq.base = pci_alloc_consistent(card->pcidev, RSQSIZE, - &card->tsq.paddr); + card->tsq.base = dma_alloc_coherent(&card->pcidev->dev, RSQSIZE, + &card->tsq.paddr, GFP_KERNEL); if (card->tsq.base == NULL) { printk("%s: can't allocate TSQ.\n", card->name); return -1; @@ -1398,8 +1399,8 @@ init_tsq(struct idt77252_dev *card) static void deinit_tsq(struct idt77252_dev *card) { - pci_free_consistent(card->pcidev, TSQSIZE, - card->tsq.base, card->tsq.paddr); + dma_free_coherent(&card->pcidev->dev, TSQSIZE, + card->tsq.base, card->tsq.paddr); } static void @@ -1861,9 +1862,9 @@ add_rx_skb(struct idt77252_dev *card, int queue, goto outfree; } - paddr = pci_map_single(card->pcidev, skb->data, + paddr = dma_map_single(&card->pcidev->dev, skb->data, skb_end_pointer(skb) - skb->data, - PCI_DMA_FROMDEVICE); + DMA_FROM_DEVICE); IDT77252_PRV_PADDR(skb) = paddr; if (push_rx_skb(card, skb, queue)) { @@ -1875,8 +1876,8 @@ add_rx_skb(struct idt77252_dev *card, int queue, return; outunmap: - pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb), - skb_end_pointer(skb) - skb->data, PCI_DMA_FROMDEVICE); + dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), + skb_end_pointer(skb) - skb->data, DMA_FROM_DEVICE); handle = IDT77252_PRV_POOL(skb); card->sbpool[POOL_QUEUE(handle)].skb[POOL_INDEX(handle)] = NULL; @@ -1892,15 +1893,15 @@ recycle_rx_skb(struct idt77252_dev *card, struct sk_buff *skb) u32 handle = IDT77252_PRV_POOL(skb); int err; - pci_dma_sync_single_for_device(card->pcidev, IDT77252_PRV_PADDR(skb), - skb_end_pointer(skb) - skb->data, - PCI_DMA_FROMDEVICE); + dma_sync_single_for_device(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), + skb_end_pointer(skb) - skb->data, + DMA_FROM_DEVICE); err = push_rx_skb(card, skb, POOL_QUEUE(handle)); if (err) { - pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb), + dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), skb_end_pointer(skb) - skb->data, - PCI_DMA_FROMDEVICE); + DMA_FROM_DEVICE); sb_pool_remove(card, skb); dev_kfree_skb(skb); } @@ -3058,11 +3059,11 @@ deinit_card(struct idt77252_dev *card) for (j = 0; j < FBQ_SIZE; j++) { skb = card->sbpool[i].skb[j]; if (skb) { - pci_unmap_single(card->pcidev, + dma_unmap_single(&card->pcidev->dev, IDT77252_PRV_PADDR(skb), (skb_end_pointer(skb) - skb->data), - PCI_DMA_FROMDEVICE); + DMA_FROM_DEVICE); card->sbpool[i].skb[j] = NULL; dev_kfree_skb(skb); } @@ -3076,8 +3077,8 @@ deinit_card(struct idt77252_dev *card) vfree(card->vcs); if (card->raw_cell_hnd) { - pci_free_consistent(card->pcidev, 2 * sizeof(u32), - card->raw_cell_hnd, card->raw_cell_paddr); + dma_free_coherent(&card->pcidev->dev, 2 * sizeof(u32), + card->raw_cell_hnd, card->raw_cell_paddr); } if (card->rsq.base) { @@ -3397,9 +3398,10 @@ static int init_card(struct atm_dev *dev) writel(0, SAR_REG_GP); /* Initialize RAW Cell Handle Register */ - card->raw_cell_hnd = pci_zalloc_consistent(card->pcidev, - 2 * sizeof(u32), - &card->raw_cell_paddr); + card->raw_cell_hnd = dma_zalloc_coherent(&card->pcidev->dev, + 2 * sizeof(u32), + &card->raw_cell_paddr, + GFP_KERNEL); if (!card->raw_cell_hnd) { printk("%s: memory allocation failure.\n", card->name); deinit_card(card); @@ -3611,6 +3613,11 @@ static int idt77252_init_one(struct pci_dev *pcidev, return err; } + if ((err = dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(32)))) { + printk("idt77252: can't enable DMA for PCI device at %s\n", pci_name(pcidev)); + return err; + } + card = kzalloc(sizeof(struct idt77252_dev), GFP_KERNEL); if (!card) { printk("idt77252-%d: can't allocate private data\n", index); diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c index 4217f29a85e0..924f8e26789d 100644 --- a/drivers/atm/iphase.c +++ b/drivers/atm/iphase.c @@ -1185,8 +1185,8 @@ static int rx_pkt(struct atm_dev *dev) /* Build the DLE structure */ wr_ptr = iadev->rx_dle_q.write; - wr_ptr->sys_pkt_addr = pci_map_single(iadev->pci, skb->data, - len, PCI_DMA_FROMDEVICE); + wr_ptr->sys_pkt_addr = dma_map_single(&iadev->pci->dev, skb->data, + len, DMA_FROM_DEVICE); wr_ptr->local_pkt_addr = buf_addr; wr_ptr->bytes = len; /* We don't know this do we ?? */ wr_ptr->mode = DMA_INT_ENABLE; @@ -1306,8 +1306,8 @@ static void rx_dle_intr(struct atm_dev *dev) u_short length; struct ia_vcc *ia_vcc; - pci_unmap_single(iadev->pci, iadev->rx_dle_q.write->sys_pkt_addr, - len, PCI_DMA_FROMDEVICE); + dma_unmap_single(&iadev->pci->dev, iadev->rx_dle_q.write->sys_pkt_addr, + len, DMA_FROM_DEVICE); /* no VCC related housekeeping done as yet. lets see */ vcc = ATM_SKB(skb)->vcc; if (!vcc) { @@ -1430,8 +1430,8 @@ static int rx_init(struct atm_dev *dev) // spin_lock_init(&iadev->rx_lock); /* Allocate 4k bytes - more aligned than needed (4k boundary) */ - dle_addr = pci_alloc_consistent(iadev->pci, DLE_TOTAL_SIZE, - &iadev->rx_dle_dma); + dle_addr = dma_alloc_coherent(&iadev->pci->dev, DLE_TOTAL_SIZE, + &iadev->rx_dle_dma, GFP_KERNEL); if (!dle_addr) { printk(KERN_ERR DEV_LABEL "can't allocate DLEs\n"); goto err_out; @@ -1631,8 +1631,8 @@ static int rx_init(struct atm_dev *dev) return 0; err_free_dle: - pci_free_consistent(iadev->pci, DLE_TOTAL_SIZE, iadev->rx_dle_q.start, - iadev->rx_dle_dma); + dma_free_coherent(&iadev->pci->dev, DLE_TOTAL_SIZE, iadev->rx_dle_q.start, + iadev->rx_dle_dma); err_out: return -ENOMEM; } @@ -1702,8 +1702,8 @@ static void tx_dle_intr(struct atm_dev *dev) /* Revenge of the 2 dle (skb + trailer) used in ia_pkt_tx() */ if (!((dle - iadev->tx_dle_q.start)%(2*sizeof(struct dle)))) { - pci_unmap_single(iadev->pci, dle->sys_pkt_addr, skb->len, - PCI_DMA_TODEVICE); + dma_unmap_single(&iadev->pci->dev, dle->sys_pkt_addr, skb->len, + DMA_TO_DEVICE); } vcc = ATM_SKB(skb)->vcc; if (!vcc) { @@ -1917,8 +1917,8 @@ static int tx_init(struct atm_dev *dev) readw(iadev->seg_reg+SEG_MASK_REG));) /* Allocate 4k (boundary aligned) bytes */ - dle_addr = pci_alloc_consistent(iadev->pci, DLE_TOTAL_SIZE, - &iadev->tx_dle_dma); + dle_addr = dma_alloc_coherent(&iadev->pci->dev, DLE_TOTAL_SIZE, + &iadev->tx_dle_dma, GFP_KERNEL); if (!dle_addr) { printk(KERN_ERR DEV_LABEL "can't allocate DLEs\n"); goto err_out; @@ -1989,8 +1989,10 @@ static int tx_init(struct atm_dev *dev) goto err_free_tx_bufs; } iadev->tx_buf[i].cpcs = cpcs; - iadev->tx_buf[i].dma_addr = pci_map_single(iadev->pci, - cpcs, sizeof(*cpcs), PCI_DMA_TODEVICE); + iadev->tx_buf[i].dma_addr = dma_map_single(&iadev->pci->dev, + cpcs, + sizeof(*cpcs), + DMA_TO_DEVICE); } iadev->desc_tbl = kmalloc(iadev->num_tx_desc * sizeof(struct desc_tbl_t), GFP_KERNEL); @@ -2198,14 +2200,14 @@ err_free_tx_bufs: while (--i >= 0) { struct cpcs_trailer_desc *desc = iadev->tx_buf + i; - pci_unmap_single(iadev->pci, desc->dma_addr, - sizeof(*desc->cpcs), PCI_DMA_TODEVICE); + dma_unmap_single(&iadev->pci->dev, desc->dma_addr, + sizeof(*desc->cpcs), DMA_TO_DEVICE); kfree(desc->cpcs); } kfree(iadev->tx_buf); err_free_dle: - pci_free_consistent(iadev->pci, DLE_TOTAL_SIZE, iadev->tx_dle_q.start, - iadev->tx_dle_dma); + dma_free_coherent(&iadev->pci->dev, DLE_TOTAL_SIZE, iadev->tx_dle_q.start, + iadev->tx_dle_dma); err_out: return -ENOMEM; } @@ -2476,20 +2478,20 @@ static void ia_free_tx(IADEV *iadev) for (i = 0; i < iadev->num_tx_desc; i++) { struct cpcs_trailer_desc *desc = iadev->tx_buf + i; - pci_unmap_single(iadev->pci, desc->dma_addr, - sizeof(*desc->cpcs), PCI_DMA_TODEVICE); + dma_unmap_single(&iadev->pci->dev, desc->dma_addr, + sizeof(*desc->cpcs), DMA_TO_DEVICE); kfree(desc->cpcs); } kfree(iadev->tx_buf); - pci_free_consistent(iadev->pci, DLE_TOTAL_SIZE, iadev->tx_dle_q.start, - iadev->tx_dle_dma); + dma_free_coherent(&iadev->pci->dev, DLE_TOTAL_SIZE, iadev->tx_dle_q.start, + iadev->tx_dle_dma); } static void ia_free_rx(IADEV *iadev) { kfree(iadev->rx_open); - pci_free_consistent(iadev->pci, DLE_TOTAL_SIZE, iadev->rx_dle_q.start, - iadev->rx_dle_dma); + dma_free_coherent(&iadev->pci->dev, DLE_TOTAL_SIZE, iadev->rx_dle_q.start, + iadev->rx_dle_dma); } static int ia_start(struct atm_dev *dev) @@ -3009,8 +3011,8 @@ static int ia_pkt_tx (struct atm_vcc *vcc, struct sk_buff *skb) { /* Build the DLE structure */ wr_ptr = iadev->tx_dle_q.write; memset((caddr_t)wr_ptr, 0, sizeof(*wr_ptr)); - wr_ptr->sys_pkt_addr = pci_map_single(iadev->pci, skb->data, - skb->len, PCI_DMA_TODEVICE); + wr_ptr->sys_pkt_addr = dma_map_single(&iadev->pci->dev, skb->data, + skb->len, DMA_TO_DEVICE); wr_ptr->local_pkt_addr = (buf_desc_ptr->buf_start_hi << 16) | buf_desc_ptr->buf_start_lo; /* wr_ptr->bytes = swap_byte_order(total_len); didn't seem to affect?? */ diff --git a/drivers/atm/lanai.c b/drivers/atm/lanai.c index 93eaf8d94492..ce43ae3e87b3 100644 --- a/drivers/atm/lanai.c +++ b/drivers/atm/lanai.c @@ -346,7 +346,8 @@ static void lanai_buf_allocate(struct lanai_buffer *buf, * everything, but the way the lanai uses DMA memory would * make that a terrific pain. This is much simpler. */ - buf->start = pci_alloc_consistent(pci, size, &buf->dmaaddr); + buf->start = dma_alloc_coherent(&pci->dev, + size, &buf->dmaaddr, GFP_KERNEL); if (buf->start != NULL) { /* Success */ /* Lanai requires 256-byte alignment of DMA bufs */ APRINTK((buf->dmaaddr & ~0xFFFFFF00) == 0, @@ -372,8 +373,8 @@ static void lanai_buf_deallocate(struct lanai_buffer *buf, struct pci_dev *pci) { if (buf->start != NULL) { - pci_free_consistent(pci, lanai_buf_size(buf), - buf->start, buf->dmaaddr); + dma_free_coherent(&pci->dev, lanai_buf_size(buf), + buf->start, buf->dmaaddr); buf->start = buf->end = buf->ptr = NULL; } } @@ -681,15 +682,6 @@ static inline int aal5_size(int size) return cells * 48; } -/* How many bytes can we send if we have "space" space, assuming we have - * to send full cells - */ -static inline int aal5_spacefor(int space) -{ - int cells = space / 48; - return cells * 48; -} - /* -------------------- FREE AN ATM SKB: */ static inline void lanai_free_skb(struct atm_vcc *atmvcc, struct sk_buff *skb) @@ -1953,12 +1945,7 @@ static int lanai_pci_start(struct lanai_dev *lanai) return -ENXIO; } pci_set_master(pci); - if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) != 0) { - printk(KERN_WARNING DEV_LABEL - "(itf %d): No suitable DMA available.\n", lanai->number); - return -EBUSY; - } - if (pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32)) != 0) { + if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32)) != 0) { printk(KERN_WARNING DEV_LABEL "(itf %d): No suitable DMA available.\n", lanai->number); return -EBUSY; diff --git a/drivers/atm/nicstar.c b/drivers/atm/nicstar.c index 9988ac98b6d8..b7e1cc0a97c8 100644 --- a/drivers/atm/nicstar.c +++ b/drivers/atm/nicstar.c @@ -252,10 +252,10 @@ static void nicstar_remove_one(struct pci_dev *pcidev) free_scq(card, card->scd2vc[j]->scq, card->scd2vc[j]->tx_vcc); } idr_destroy(&card->idr); - pci_free_consistent(card->pcidev, NS_RSQSIZE + NS_RSQ_ALIGNMENT, - card->rsq.org, card->rsq.dma); - pci_free_consistent(card->pcidev, NS_TSQSIZE + NS_TSQ_ALIGNMENT, - card->tsq.org, card->tsq.dma); + dma_free_coherent(&card->pcidev->dev, NS_RSQSIZE + NS_RSQ_ALIGNMENT, + card->rsq.org, card->rsq.dma); + dma_free_coherent(&card->pcidev->dev, NS_TSQSIZE + NS_TSQ_ALIGNMENT, + card->tsq.org, card->tsq.dma); free_irq(card->pcidev->irq, card); iounmap(card->membase); kfree(card); @@ -370,8 +370,7 @@ static int ns_init_card(int i, struct pci_dev *pcidev) ns_init_card_error(card, error); return error; } - if ((pci_set_dma_mask(pcidev, DMA_BIT_MASK(32)) != 0) || - (pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(32)) != 0)) { + if (dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(32)) != 0) { printk(KERN_WARNING "nicstar%d: No suitable DMA available.\n", i); error = 2; @@ -535,9 +534,9 @@ static int ns_init_card(int i, struct pci_dev *pcidev) writel(0x00000000, card->membase + VPM); /* Initialize TSQ */ - card->tsq.org = pci_alloc_consistent(card->pcidev, - NS_TSQSIZE + NS_TSQ_ALIGNMENT, - &card->tsq.dma); + card->tsq.org = dma_alloc_coherent(&card->pcidev->dev, + NS_TSQSIZE + NS_TSQ_ALIGNMENT, + &card->tsq.dma, GFP_KERNEL); if (card->tsq.org == NULL) { printk("nicstar%d: can't allocate TSQ.\n", i); error = 10; @@ -554,9 +553,9 @@ static int ns_init_card(int i, struct pci_dev *pcidev) PRINTK("nicstar%d: TSQ base at 0x%p.\n", i, card->tsq.base); /* Initialize RSQ */ - card->rsq.org = pci_alloc_consistent(card->pcidev, - NS_RSQSIZE + NS_RSQ_ALIGNMENT, - &card->rsq.dma); + card->rsq.org = dma_alloc_coherent(&card->pcidev->dev, + NS_RSQSIZE + NS_RSQ_ALIGNMENT, + &card->rsq.dma, GFP_KERNEL); if (card->rsq.org == NULL) { printk("nicstar%d: can't allocate RSQ.\n", i); error = 11; @@ -874,7 +873,8 @@ static scq_info *get_scq(ns_dev *card, int size, u32 scd) scq = kmalloc(sizeof(scq_info), GFP_KERNEL); if (!scq) return NULL; - scq->org = pci_alloc_consistent(card->pcidev, 2 * size, &scq->dma); + scq->org = dma_alloc_coherent(&card->pcidev->dev, + 2 * size, &scq->dma, GFP_KERNEL); if (!scq->org) { kfree(scq); return NULL; @@ -936,10 +936,10 @@ static void free_scq(ns_dev *card, scq_info *scq, struct atm_vcc *vcc) } } kfree(scq->skb); - pci_free_consistent(card->pcidev, - 2 * (scq->num_entries == VBR_SCQ_NUM_ENTRIES ? - VBR_SCQSIZE : CBR_SCQSIZE), - scq->org, scq->dma); + dma_free_coherent(&card->pcidev->dev, + 2 * (scq->num_entries == VBR_SCQ_NUM_ENTRIES ? + VBR_SCQSIZE : CBR_SCQSIZE), + scq->org, scq->dma); kfree(scq); } @@ -957,11 +957,11 @@ static void push_rxbufs(ns_dev * card, struct sk_buff *skb) handle2 = NULL; addr2 = 0; handle1 = skb; - addr1 = pci_map_single(card->pcidev, + addr1 = dma_map_single(&card->pcidev->dev, skb->data, (NS_PRV_BUFTYPE(skb) == BUF_SM ? NS_SMSKBSIZE : NS_LGSKBSIZE), - PCI_DMA_TODEVICE); + DMA_TO_DEVICE); NS_PRV_DMA(skb) = addr1; /* save so we can unmap later */ #ifdef GENERAL_DEBUG @@ -1670,8 +1670,8 @@ static int ns_send(struct atm_vcc *vcc, struct sk_buff *skb) ATM_SKB(skb)->vcc = vcc; - NS_PRV_DMA(skb) = pci_map_single(card->pcidev, skb->data, - skb->len, PCI_DMA_TODEVICE); + NS_PRV_DMA(skb) = dma_map_single(&card->pcidev->dev, skb->data, + skb->len, DMA_TO_DEVICE); if (vcc->qos.aal == ATM_AAL5) { buflen = (skb->len + 47 + 8) / 48 * 48; /* Multiple of 48 */ @@ -1930,10 +1930,10 @@ static void drain_scq(ns_dev * card, scq_info * scq, int pos) XPRINTK("nicstar%d: freeing skb at 0x%p (index %d).\n", card->index, skb, i); if (skb != NULL) { - pci_unmap_single(card->pcidev, + dma_unmap_single(&card->pcidev->dev, NS_PRV_DMA(skb), skb->len, - PCI_DMA_TODEVICE); + DMA_TO_DEVICE); vcc = ATM_SKB(skb)->vcc; if (vcc && vcc->pop != NULL) { vcc->pop(vcc, skb); @@ -1992,16 +1992,16 @@ static void dequeue_rx(ns_dev * card, ns_rsqe * rsqe) return; } idr_remove(&card->idr, id); - pci_dma_sync_single_for_cpu(card->pcidev, - NS_PRV_DMA(skb), - (NS_PRV_BUFTYPE(skb) == BUF_SM - ? NS_SMSKBSIZE : NS_LGSKBSIZE), - PCI_DMA_FROMDEVICE); - pci_unmap_single(card->pcidev, + dma_sync_single_for_cpu(&card->pcidev->dev, + NS_PRV_DMA(skb), + (NS_PRV_BUFTYPE(skb) == BUF_SM + ? NS_SMSKBSIZE : NS_LGSKBSIZE), + DMA_FROM_DEVICE); + dma_unmap_single(&card->pcidev->dev, NS_PRV_DMA(skb), (NS_PRV_BUFTYPE(skb) == BUF_SM ? NS_SMSKBSIZE : NS_LGSKBSIZE), - PCI_DMA_FROMDEVICE); + DMA_FROM_DEVICE); vpi = ns_rsqe_vpi(rsqe); vci = ns_rsqe_vci(rsqe); if (vpi >= 1UL << card->vpibits || vci >= 1UL << card->vcibits) { diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c index 21b0bc6a9c96..74e18b0a6d89 100644 --- a/drivers/atm/solos-pci.c +++ b/drivers/atm/solos-pci.c @@ -785,8 +785,8 @@ static void solos_bh(unsigned long card_arg) skb = card->rx_skb[port]; card->rx_skb[port] = NULL; - pci_unmap_single(card->dev, SKB_CB(skb)->dma_addr, - RX_DMA_SIZE, PCI_DMA_FROMDEVICE); + dma_unmap_single(&card->dev->dev, SKB_CB(skb)->dma_addr, + RX_DMA_SIZE, DMA_FROM_DEVICE); header = (void *)skb->data; size = le16_to_cpu(header->size); @@ -872,8 +872,8 @@ static void solos_bh(unsigned long card_arg) struct sk_buff *skb = alloc_skb(RX_DMA_SIZE, GFP_ATOMIC); if (skb) { SKB_CB(skb)->dma_addr = - pci_map_single(card->dev, skb->data, - RX_DMA_SIZE, PCI_DMA_FROMDEVICE); + dma_map_single(&card->dev->dev, skb->data, + RX_DMA_SIZE, DMA_FROM_DEVICE); iowrite32(SKB_CB(skb)->dma_addr, card->config_regs + RX_DMA_ADDR(port)); card->rx_skb[port] = skb; @@ -1069,8 +1069,8 @@ static uint32_t fpga_tx(struct solos_card *card) if (tx_pending & 1) { struct sk_buff *oldskb = card->tx_skb[port]; if (oldskb) { - pci_unmap_single(card->dev, SKB_CB(oldskb)->dma_addr, - oldskb->len, PCI_DMA_TODEVICE); + dma_unmap_single(&card->dev->dev, SKB_CB(oldskb)->dma_addr, + oldskb->len, DMA_TO_DEVICE); card->tx_skb[port] = NULL; } spin_lock(&card->tx_queue_lock); @@ -1089,8 +1089,8 @@ static uint32_t fpga_tx(struct solos_card *card) data = card->dma_bounce + (BUF_SIZE * port); memcpy(data, skb->data, skb->len); } - SKB_CB(skb)->dma_addr = pci_map_single(card->dev, data, - skb->len, PCI_DMA_TODEVICE); + SKB_CB(skb)->dma_addr = dma_map_single(&card->dev->dev, data, + skb->len, DMA_TO_DEVICE); card->tx_skb[port] = skb; iowrite32(SKB_CB(skb)->dma_addr, card->config_regs + TX_DMA_ADDR(port)); @@ -1210,7 +1210,7 @@ static int fpga_probe(struct pci_dev *dev, const struct pci_device_id *id) goto out; } - err = pci_set_dma_mask(dev, DMA_BIT_MASK(32)); + err = dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32)); if (err) { dev_warn(&dev->dev, "Failed to set 32-bit DMA mask\n"); goto out; @@ -1411,14 +1411,14 @@ static void atm_remove(struct solos_card *card) skb = card->rx_skb[i]; if (skb) { - pci_unmap_single(card->dev, SKB_CB(skb)->dma_addr, - RX_DMA_SIZE, PCI_DMA_FROMDEVICE); + dma_unmap_single(&card->dev->dev, SKB_CB(skb)->dma_addr, + RX_DMA_SIZE, DMA_FROM_DEVICE); dev_kfree_skb(skb); } skb = card->tx_skb[i]; if (skb) { - pci_unmap_single(card->dev, SKB_CB(skb)->dma_addr, - skb->len, PCI_DMA_TODEVICE); + dma_unmap_single(&card->dev->dev, SKB_CB(skb)->dma_addr, + skb->len, DMA_TO_DEVICE); dev_kfree_skb(skb); } while ((skb = skb_dequeue(&card->tx_queue[i]))) diff --git a/drivers/atm/zatm.c b/drivers/atm/zatm.c index 969c3c29000c..cecfb943762f 100644 --- a/drivers/atm/zatm.c +++ b/drivers/atm/zatm.c @@ -1306,19 +1306,20 @@ static int zatm_start(struct atm_dev *dev) if (!mbx_entries[i]) continue; - mbx = pci_alloc_consistent(pdev, 2*MBX_SIZE(i), &mbx_dma); + mbx = dma_alloc_coherent(&pdev->dev, + 2 * MBX_SIZE(i), &mbx_dma, GFP_KERNEL); if (!mbx) { error = -ENOMEM; goto out; } /* - * Alignment provided by pci_alloc_consistent() isn't enough + * Alignment provided by dma_alloc_coherent() isn't enough * for this device. */ if (((unsigned long)mbx ^ mbx_dma) & 0xffff) { printk(KERN_ERR DEV_LABEL "(itf %d): system " "bus incompatible with driver\n", dev->number); - pci_free_consistent(pdev, 2*MBX_SIZE(i), mbx, mbx_dma); + dma_free_coherent(&pdev->dev, 2*MBX_SIZE(i), mbx, mbx_dma); error = -ENODEV; goto out; } @@ -1354,9 +1355,9 @@ out_tx: kfree(zatm_dev->tx_map); out: while (i-- > 0) { - pci_free_consistent(pdev, 2*MBX_SIZE(i), - (void *)zatm_dev->mbx_start[i], - zatm_dev->mbx_dma[i]); + dma_free_coherent(&pdev->dev, 2 * MBX_SIZE(i), + (void *)zatm_dev->mbx_start[i], + zatm_dev->mbx_dma[i]); } free_irq(zatm_dev->irq, dev); goto done; @@ -1608,6 +1609,10 @@ static int zatm_init_one(struct pci_dev *pci_dev, if (ret < 0) goto out_disable; + ret = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(32)); + if (ret < 0) + goto out_disable; + zatm_dev->pci_dev = pci_dev; dev->dev_data = zatm_dev; zatm_dev->copper = (int)ent->driver_data; diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c index b0f138806bbc..f32b802b98f4 100644 --- a/drivers/base/power/common.c +++ b/drivers/base/power/common.c @@ -19,8 +19,8 @@ * @dev: Device to handle. * * If power.subsys_data is NULL, point it to a new object, otherwise increment - * its reference counter. Return 1 if a new object has been created, otherwise - * return 0 or error code. + * its reference counter. Return 0 if new object has been created or refcount + * increased, otherwise negative error code. */ int dev_pm_get_subsys_data(struct device *dev) { @@ -56,13 +56,11 @@ EXPORT_SYMBOL_GPL(dev_pm_get_subsys_data); * @dev: Device to handle. * * If the reference counter of power.subsys_data is zero after dropping the - * reference, power.subsys_data is removed. Return 1 if that happens or 0 - * otherwise. + * reference, power.subsys_data is removed. */ -int dev_pm_put_subsys_data(struct device *dev) +void dev_pm_put_subsys_data(struct device *dev) { struct pm_subsys_data *psd; - int ret = 1; spin_lock_irq(&dev->power.lock); @@ -70,18 +68,14 @@ int dev_pm_put_subsys_data(struct device *dev) if (!psd) goto out; - if (--psd->refcount == 0) { + if (--psd->refcount == 0) dev->power.subsys_data = NULL; - } else { + else psd = NULL; - ret = 0; - } out: spin_unlock_irq(&dev->power.lock); kfree(psd); - - return ret; } EXPORT_SYMBOL_GPL(dev_pm_put_subsys_data); diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 0d8780c04a5e..ba4abbe4693c 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -344,14 +344,7 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb, struct device *dev; gpd_data = container_of(nb, struct generic_pm_domain_data, nb); - - mutex_lock(&gpd_data->lock); dev = gpd_data->base.dev; - if (!dev) { - mutex_unlock(&gpd_data->lock); - return NOTIFY_DONE; - } - mutex_unlock(&gpd_data->lock); for (;;) { struct generic_pm_domain *genpd; @@ -1384,25 +1377,66 @@ EXPORT_SYMBOL_GPL(pm_genpd_syscore_poweron); #endif /* CONFIG_PM_SLEEP */ -static struct generic_pm_domain_data *__pm_genpd_alloc_dev_data(struct device *dev) +static struct generic_pm_domain_data *genpd_alloc_dev_data(struct device *dev, + struct generic_pm_domain *genpd, + struct gpd_timing_data *td) { struct generic_pm_domain_data *gpd_data; + int ret; + + ret = dev_pm_get_subsys_data(dev); + if (ret) + return ERR_PTR(ret); gpd_data = kzalloc(sizeof(*gpd_data), GFP_KERNEL); - if (!gpd_data) - return NULL; + if (!gpd_data) { + ret = -ENOMEM; + goto err_put; + } + + if (td) + gpd_data->td = *td; - mutex_init(&gpd_data->lock); + gpd_data->base.dev = dev; + gpd_data->need_restore = -1; + gpd_data->td.constraint_changed = true; + gpd_data->td.effective_constraint_ns = -1; gpd_data->nb.notifier_call = genpd_dev_pm_qos_notifier; - dev_pm_qos_add_notifier(dev, &gpd_data->nb); + + spin_lock_irq(&dev->power.lock); + + if (dev->power.subsys_data->domain_data) { + ret = -EINVAL; + goto err_free; + } + + dev->power.subsys_data->domain_data = &gpd_data->base; + dev->pm_domain = &genpd->domain; + + spin_unlock_irq(&dev->power.lock); + return gpd_data; + + err_free: + spin_unlock_irq(&dev->power.lock); + kfree(gpd_data); + err_put: + dev_pm_put_subsys_data(dev); + return ERR_PTR(ret); } -static void __pm_genpd_free_dev_data(struct device *dev, - struct generic_pm_domain_data *gpd_data) +static void genpd_free_dev_data(struct device *dev, + struct generic_pm_domain_data *gpd_data) { - dev_pm_qos_remove_notifier(dev, &gpd_data->nb); + spin_lock_irq(&dev->power.lock); + + dev->pm_domain = NULL; + dev->power.subsys_data->domain_data = NULL; + + spin_unlock_irq(&dev->power.lock); + kfree(gpd_data); + dev_pm_put_subsys_data(dev); } /** @@ -1414,8 +1448,7 @@ static void __pm_genpd_free_dev_data(struct device *dev, int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, struct gpd_timing_data *td) { - struct generic_pm_domain_data *gpd_data_new, *gpd_data = NULL; - struct pm_domain_data *pdd; + struct generic_pm_domain_data *gpd_data; int ret = 0; dev_dbg(dev, "%s()\n", __func__); @@ -1423,9 +1456,9 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev)) return -EINVAL; - gpd_data_new = __pm_genpd_alloc_dev_data(dev); - if (!gpd_data_new) - return -ENOMEM; + gpd_data = genpd_alloc_dev_data(dev, genpd, td); + if (IS_ERR(gpd_data)) + return PTR_ERR(gpd_data); genpd_acquire_lock(genpd); @@ -1434,50 +1467,22 @@ int __pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev, goto out; } - list_for_each_entry(pdd, &genpd->dev_list, list_node) - if (pdd->dev == dev) { - ret = -EINVAL; - goto out; - } - - ret = dev_pm_get_subsys_data(dev); + ret = genpd->attach_dev ? genpd->attach_dev(genpd, dev) : 0; if (ret) goto out; genpd->device_count++; genpd->max_off_time_changed = true; - spin_lock_irq(&dev->power.lock); - - dev->pm_domain = &genpd->domain; - if (dev->power.subsys_data->domain_data) { - gpd_data = to_gpd_data(dev->power.subsys_data->domain_data); - } else { - gpd_data = gpd_data_new; - dev->power.subsys_data->domain_data = &gpd_data->base; - } - gpd_data->refcount++; - if (td) - gpd_data->td = *td; - - spin_unlock_irq(&dev->power.lock); - - if (genpd->attach_dev) - genpd->attach_dev(genpd, dev); - - mutex_lock(&gpd_data->lock); - gpd_data->base.dev = dev; list_add_tail(&gpd_data->base.list_node, &genpd->dev_list); - gpd_data->need_restore = -1; - gpd_data->td.constraint_changed = true; - gpd_data->td.effective_constraint_ns = -1; - mutex_unlock(&gpd_data->lock); out: genpd_release_lock(genpd); - if (gpd_data != gpd_data_new) - __pm_genpd_free_dev_data(dev, gpd_data_new); + if (ret) + genpd_free_dev_data(dev, gpd_data); + else + dev_pm_qos_add_notifier(dev, &gpd_data->nb); return ret; } @@ -1504,7 +1509,6 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, { struct generic_pm_domain_data *gpd_data; struct pm_domain_data *pdd; - bool remove = false; int ret = 0; dev_dbg(dev, "%s()\n", __func__); @@ -1514,6 +1518,11 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, || pd_to_genpd(dev->pm_domain) != genpd) return -EINVAL; + /* The above validation also means we have existing domain_data. */ + pdd = dev->power.subsys_data->domain_data; + gpd_data = to_gpd_data(pdd); + dev_pm_qos_remove_notifier(dev, &gpd_data->nb); + genpd_acquire_lock(genpd); if (genpd->prepared_count > 0) { @@ -1527,58 +1536,22 @@ int pm_genpd_remove_device(struct generic_pm_domain *genpd, if (genpd->detach_dev) genpd->detach_dev(genpd, dev); - spin_lock_irq(&dev->power.lock); - - dev->pm_domain = NULL; - pdd = dev->power.subsys_data->domain_data; list_del_init(&pdd->list_node); - gpd_data = to_gpd_data(pdd); - if (--gpd_data->refcount == 0) { - dev->power.subsys_data->domain_data = NULL; - remove = true; - } - - spin_unlock_irq(&dev->power.lock); - - mutex_lock(&gpd_data->lock); - pdd->dev = NULL; - mutex_unlock(&gpd_data->lock); genpd_release_lock(genpd); - dev_pm_put_subsys_data(dev); - if (remove) - __pm_genpd_free_dev_data(dev, gpd_data); + genpd_free_dev_data(dev, gpd_data); return 0; out: genpd_release_lock(genpd); + dev_pm_qos_add_notifier(dev, &gpd_data->nb); return ret; } /** - * pm_genpd_dev_need_restore - Set/unset the device's "need restore" flag. - * @dev: Device to set/unset the flag for. - * @val: The new value of the device's "need restore" flag. - */ -void pm_genpd_dev_need_restore(struct device *dev, bool val) -{ - struct pm_subsys_data *psd; - unsigned long flags; - - spin_lock_irqsave(&dev->power.lock, flags); - - psd = dev_to_psd(dev); - if (psd && psd->domain_data) - to_gpd_data(psd->domain_data)->need_restore = val ? 1 : 0; - - spin_unlock_irqrestore(&dev->power.lock, flags); -} -EXPORT_SYMBOL_GPL(pm_genpd_dev_need_restore); - -/** * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain. * @genpd: Master PM domain to add the subdomain to. * @subdomain: Subdomain to be added. diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c index 106c69359306..15bf29974c31 100644 --- a/drivers/base/power/opp.c +++ b/drivers/base/power/opp.c @@ -117,20 +117,20 @@ do { \ } while (0) /** - * find_device_opp() - find device_opp struct using device pointer + * _find_device_opp() - find device_opp struct using device pointer * @dev: device pointer used to lookup device OPPs * * Search list of device OPPs for one containing matching device. Does a RCU * reader operation to grab the pointer needed. * - * Returns pointer to 'struct device_opp' if found, otherwise -ENODEV or + * Return: pointer to 'struct device_opp' if found, otherwise -ENODEV or * -EINVAL based on type of error. * * Locking: This function must be called under rcu_read_lock(). device_opp * is a RCU protected pointer. This means that device_opp is valid as long * as we are under RCU lock. */ -static struct device_opp *find_device_opp(struct device *dev) +static struct device_opp *_find_device_opp(struct device *dev) { struct device_opp *tmp_dev_opp, *dev_opp = ERR_PTR(-ENODEV); @@ -153,7 +153,7 @@ static struct device_opp *find_device_opp(struct device *dev) * dev_pm_opp_get_voltage() - Gets the voltage corresponding to an available opp * @opp: opp for which voltage has to be returned for * - * Return voltage in micro volt corresponding to the opp, else + * Return: voltage in micro volt corresponding to the opp, else * return 0 * * Locking: This function must be called under rcu_read_lock(). opp is a rcu @@ -169,6 +169,8 @@ unsigned long dev_pm_opp_get_voltage(struct dev_pm_opp *opp) struct dev_pm_opp *tmp_opp; unsigned long v = 0; + opp_rcu_lockdep_assert(); + tmp_opp = rcu_dereference(opp); if (unlikely(IS_ERR_OR_NULL(tmp_opp)) || !tmp_opp->available) pr_err("%s: Invalid parameters\n", __func__); @@ -183,7 +185,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_voltage); * dev_pm_opp_get_freq() - Gets the frequency corresponding to an available opp * @opp: opp for which frequency has to be returned for * - * Return frequency in hertz corresponding to the opp, else + * Return: frequency in hertz corresponding to the opp, else * return 0 * * Locking: This function must be called under rcu_read_lock(). opp is a rcu @@ -199,6 +201,8 @@ unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) struct dev_pm_opp *tmp_opp; unsigned long f = 0; + opp_rcu_lockdep_assert(); + tmp_opp = rcu_dereference(opp); if (unlikely(IS_ERR_OR_NULL(tmp_opp)) || !tmp_opp->available) pr_err("%s: Invalid parameters\n", __func__); @@ -213,7 +217,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); * dev_pm_opp_get_opp_count() - Get number of opps available in the opp list * @dev: device for which we do this operation * - * This function returns the number of available opps if there are any, + * Return: This function returns the number of available opps if there are any, * else returns 0 if none or the corresponding error value. * * Locking: This function takes rcu_read_lock(). @@ -226,7 +230,7 @@ int dev_pm_opp_get_opp_count(struct device *dev) rcu_read_lock(); - dev_opp = find_device_opp(dev); + dev_opp = _find_device_opp(dev); if (IS_ERR(dev_opp)) { count = PTR_ERR(dev_opp); dev_err(dev, "%s: device OPP not found (%d)\n", @@ -251,9 +255,9 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count); * @freq: frequency to search for * @available: true/false - match for available opp * - * Searches for exact match in the opp list and returns pointer to the matching - * opp if found, else returns ERR_PTR in case of error and should be handled - * using IS_ERR. Error return values can be: + * Return: Searches for exact match in the opp list and returns pointer to the + * matching opp if found, else returns ERR_PTR in case of error and should + * be handled using IS_ERR. Error return values can be: * EINVAL: for bad pointer * ERANGE: no match found for search * ENODEV: if device not found in list of registered devices @@ -280,7 +284,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, opp_rcu_lockdep_assert(); - dev_opp = find_device_opp(dev); + dev_opp = _find_device_opp(dev); if (IS_ERR(dev_opp)) { int r = PTR_ERR(dev_opp); dev_err(dev, "%s: device OPP not found (%d)\n", __func__, r); @@ -307,7 +311,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact); * Search for the matching ceil *available* OPP from a starting freq * for a device. * - * Returns matching *opp and refreshes *freq accordingly, else returns + * Return: matching *opp and refreshes *freq accordingly, else returns * ERR_PTR in case of error and should be handled using IS_ERR. Error return * values can be: * EINVAL: for bad pointer @@ -333,7 +337,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, return ERR_PTR(-EINVAL); } - dev_opp = find_device_opp(dev); + dev_opp = _find_device_opp(dev); if (IS_ERR(dev_opp)) return ERR_CAST(dev_opp); @@ -357,7 +361,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil); * Search for the matching floor *available* OPP from a starting freq * for a device. * - * Returns matching *opp and refreshes *freq accordingly, else returns + * Return: matching *opp and refreshes *freq accordingly, else returns * ERR_PTR in case of error and should be handled using IS_ERR. Error return * values can be: * EINVAL: for bad pointer @@ -383,7 +387,7 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, return ERR_PTR(-EINVAL); } - dev_opp = find_device_opp(dev); + dev_opp = _find_device_opp(dev); if (IS_ERR(dev_opp)) return ERR_CAST(dev_opp); @@ -403,7 +407,16 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor); -static struct device_opp *add_device_opp(struct device *dev) +/** + * _add_device_opp() - Allocate a new device OPP table + * @dev: device for which we do this operation + * + * New device node which uses OPPs - used when multiple devices with OPP tables + * are maintained. + * + * Return: valid device_opp pointer if success, else NULL. + */ +static struct device_opp *_add_device_opp(struct device *dev) { struct device_opp *dev_opp; @@ -424,8 +437,35 @@ static struct device_opp *add_device_opp(struct device *dev) return dev_opp; } -static int dev_pm_opp_add_dynamic(struct device *dev, unsigned long freq, - unsigned long u_volt, bool dynamic) +/** + * _opp_add_dynamic() - Allocate a dynamic OPP. + * @dev: device for which we do this operation + * @freq: Frequency in Hz for this OPP + * @u_volt: Voltage in uVolts for this OPP + * @dynamic: Dynamically added OPPs. + * + * This function adds an opp definition to the opp list and returns status. + * The opp is made available by default and it can be controlled using + * dev_pm_opp_enable/disable functions and may be removed by dev_pm_opp_remove. + * + * NOTE: "dynamic" parameter impacts OPPs added by the of_init_opp_table and + * freed by of_free_opp_table. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + * + * Return: + * 0 On success OR + * Duplicate OPPs (both freq and volt are same) and opp->available + * -EEXIST Freq are same and volt are different OR + * Duplicate OPPs (both freq and volt are same) and !opp->available + * -ENOMEM Memory allocation failure + */ +static int _opp_add_dynamic(struct device *dev, unsigned long freq, + long u_volt, bool dynamic) { struct device_opp *dev_opp = NULL; struct dev_pm_opp *opp, *new_opp; @@ -449,9 +489,9 @@ static int dev_pm_opp_add_dynamic(struct device *dev, unsigned long freq, new_opp->dynamic = dynamic; /* Check for existing list for 'dev' */ - dev_opp = find_device_opp(dev); + dev_opp = _find_device_opp(dev); if (IS_ERR(dev_opp)) { - dev_opp = add_device_opp(dev); + dev_opp = _add_device_opp(dev); if (!dev_opp) { ret = -ENOMEM; goto free_opp; @@ -519,34 +559,53 @@ free_opp: * mutex cannot be locked. * * Return: - * 0: On success OR + * 0 On success OR * Duplicate OPPs (both freq and volt are same) and opp->available - * -EEXIST: Freq are same and volt are different OR + * -EEXIST Freq are same and volt are different OR * Duplicate OPPs (both freq and volt are same) and !opp->available - * -ENOMEM: Memory allocation failure + * -ENOMEM Memory allocation failure */ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt) { - return dev_pm_opp_add_dynamic(dev, freq, u_volt, true); + return _opp_add_dynamic(dev, freq, u_volt, true); } EXPORT_SYMBOL_GPL(dev_pm_opp_add); -static void kfree_opp_rcu(struct rcu_head *head) +/** + * _kfree_opp_rcu() - Free OPP RCU handler + * @head: RCU head + */ +static void _kfree_opp_rcu(struct rcu_head *head) { struct dev_pm_opp *opp = container_of(head, struct dev_pm_opp, rcu_head); kfree_rcu(opp, rcu_head); } -static void kfree_device_rcu(struct rcu_head *head) +/** + * _kfree_device_rcu() - Free device_opp RCU handler + * @head: RCU head + */ +static void _kfree_device_rcu(struct rcu_head *head) { struct device_opp *device_opp = container_of(head, struct device_opp, rcu_head); kfree_rcu(device_opp, rcu_head); } -static void __dev_pm_opp_remove(struct device_opp *dev_opp, - struct dev_pm_opp *opp) +/** + * _opp_remove() - Remove an OPP from a table definition + * @dev_opp: points back to the device_opp struct this opp belongs to + * @opp: pointer to the OPP to remove + * + * This function removes an opp definition from the opp list. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * It is assumed that the caller holds required mutex for an RCU updater + * strategy. + */ +static void _opp_remove(struct device_opp *dev_opp, + struct dev_pm_opp *opp) { /* * Notify the changes in the availability of the operable @@ -554,12 +613,12 @@ static void __dev_pm_opp_remove(struct device_opp *dev_opp, */ srcu_notifier_call_chain(&dev_opp->srcu_head, OPP_EVENT_REMOVE, opp); list_del_rcu(&opp->node); - call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, kfree_opp_rcu); + call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); if (list_empty(&dev_opp->opp_list)) { list_del_rcu(&dev_opp->node); call_srcu(&dev_opp->srcu_head.srcu, &dev_opp->rcu_head, - kfree_device_rcu); + _kfree_device_rcu); } } @@ -569,6 +628,12 @@ static void __dev_pm_opp_remove(struct device_opp *dev_opp, * @freq: OPP to remove with matching 'freq' * * This function removes an opp from the opp list. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function internally uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. */ void dev_pm_opp_remove(struct device *dev, unsigned long freq) { @@ -579,7 +644,7 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq) /* Hold our list modification lock here */ mutex_lock(&dev_opp_list_lock); - dev_opp = find_device_opp(dev); + dev_opp = _find_device_opp(dev); if (IS_ERR(dev_opp)) goto unlock; @@ -596,14 +661,14 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq) goto unlock; } - __dev_pm_opp_remove(dev_opp, opp); + _opp_remove(dev_opp, opp); unlock: mutex_unlock(&dev_opp_list_lock); } EXPORT_SYMBOL_GPL(dev_pm_opp_remove); /** - * opp_set_availability() - helper to set the availability of an opp + * _opp_set_availability() - helper to set the availability of an opp * @dev: device for which we do this operation * @freq: OPP frequency to modify availability * @availability_req: availability status requested for this opp @@ -611,7 +676,7 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_remove); * Set the availability of an OPP with an RCU operation, opp_{enable,disable} * share a common logic which is isolated here. * - * Returns -EINVAL for bad pointers, -ENOMEM if no memory available for the + * Return: -EINVAL for bad pointers, -ENOMEM if no memory available for the * copy operation, returns 0 if no modifcation was done OR modification was * successful. * @@ -621,8 +686,8 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_remove); * that this function is *NOT* called under RCU protection or in contexts where * mutex locking or synchronize_rcu() blocking calls cannot be used. */ -static int opp_set_availability(struct device *dev, unsigned long freq, - bool availability_req) +static int _opp_set_availability(struct device *dev, unsigned long freq, + bool availability_req) { struct device_opp *dev_opp; struct dev_pm_opp *new_opp, *tmp_opp, *opp = ERR_PTR(-ENODEV); @@ -638,7 +703,7 @@ static int opp_set_availability(struct device *dev, unsigned long freq, mutex_lock(&dev_opp_list_lock); /* Find the device_opp */ - dev_opp = find_device_opp(dev); + dev_opp = _find_device_opp(dev); if (IS_ERR(dev_opp)) { r = PTR_ERR(dev_opp); dev_warn(dev, "%s: Device OPP not found (%d)\n", __func__, r); @@ -668,7 +733,7 @@ static int opp_set_availability(struct device *dev, unsigned long freq, list_replace_rcu(&opp->node, &new_opp->node); mutex_unlock(&dev_opp_list_lock); - call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, kfree_opp_rcu); + call_srcu(&dev_opp->srcu_head.srcu, &opp->rcu_head, _kfree_opp_rcu); /* Notify the change of the OPP availability */ if (availability_req) @@ -700,10 +765,14 @@ unlock: * integrity of the internal data structures. Callers should ensure that * this function is *NOT* called under RCU protection or in contexts where * 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 + * successful. */ int dev_pm_opp_enable(struct device *dev, unsigned long freq) { - return opp_set_availability(dev, freq, true); + return _opp_set_availability(dev, freq, true); } EXPORT_SYMBOL_GPL(dev_pm_opp_enable); @@ -722,26 +791,41 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_enable); * integrity of the internal data structures. Callers should ensure that * this function is *NOT* called under RCU protection or in contexts where * 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 + * successful. */ int dev_pm_opp_disable(struct device *dev, unsigned long freq) { - return opp_set_availability(dev, freq, false); + return _opp_set_availability(dev, freq, false); } EXPORT_SYMBOL_GPL(dev_pm_opp_disable); /** * dev_pm_opp_get_notifier() - find notifier_head of the device with opp * @dev: device pointer used to lookup device OPPs. + * + * Return: pointer to notifier head if found, otherwise -ENODEV or + * -EINVAL based on type of error casted as pointer. value must be checked + * with IS_ERR to determine valid pointer or error result. + * + * Locking: This function must be called under rcu_read_lock(). dev_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 srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev) { - struct device_opp *dev_opp = find_device_opp(dev); + struct device_opp *dev_opp = _find_device_opp(dev); if (IS_ERR(dev_opp)) return ERR_CAST(dev_opp); /* matching type */ return &dev_opp->srcu_head; } +EXPORT_SYMBOL_GPL(dev_pm_opp_get_notifier); #ifdef CONFIG_OF /** @@ -749,6 +833,22 @@ struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev) * @dev: device pointer used to lookup device OPPs. * * Register the initial OPP table with the OPP library for given device. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function indirectly uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. + * + * Return: + * 0 On success OR + * Duplicate OPPs (both freq and volt are same) and opp->available + * -EEXIST Freq are same and volt are different OR + * Duplicate OPPs (both freq and volt are same) and !opp->available + * -ENOMEM Memory allocation failure + * -ENODEV when 'operating-points' property is not found or is invalid data + * in device node. + * -ENODATA when empty 'operating-points' property is found */ int of_init_opp_table(struct device *dev) { @@ -777,7 +877,7 @@ int of_init_opp_table(struct device *dev) unsigned long freq = be32_to_cpup(val++) * 1000; unsigned long volt = be32_to_cpup(val++); - if (dev_pm_opp_add_dynamic(dev, freq, volt, false)) + if (_opp_add_dynamic(dev, freq, volt, false)) dev_warn(dev, "%s: Failed to add OPP %ld\n", __func__, freq); nr -= 2; @@ -792,6 +892,12 @@ EXPORT_SYMBOL_GPL(of_init_opp_table); * @dev: device pointer used to lookup device OPPs. * * Free OPPs created using static entries present in DT. + * + * Locking: The internal device_opp and opp structures are RCU protected. + * Hence this function indirectly uses RCU updater strategy with mutex locks + * to keep the integrity of the internal data structures. Callers should ensure + * that this function is *NOT* called under RCU protection or in contexts where + * mutex cannot be locked. */ void of_free_opp_table(struct device *dev) { @@ -799,7 +905,7 @@ void of_free_opp_table(struct device *dev) struct dev_pm_opp *opp, *tmp; /* Check for existing list for 'dev' */ - dev_opp = find_device_opp(dev); + dev_opp = _find_device_opp(dev); if (IS_ERR(dev_opp)) { int error = PTR_ERR(dev_opp); if (error != -ENODEV) @@ -816,7 +922,7 @@ void of_free_opp_table(struct device *dev) /* Free static OPPs */ list_for_each_entry_safe(opp, tmp, &dev_opp->opp_list, node) { if (!opp->dynamic) - __dev_pm_opp_remove(dev_opp, opp); + _opp_remove(dev_opp, opp); } mutex_unlock(&dev_opp_list_lock); diff --git a/drivers/base/power/qos.c b/drivers/base/power/qos.c index a8fe4c1a8d07..e56d538d039e 100644 --- a/drivers/base/power/qos.c +++ b/drivers/base/power/qos.c @@ -64,6 +64,8 @@ enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask) struct pm_qos_flags *pqf; s32 val; + lockdep_assert_held(&dev->power.lock); + if (IS_ERR_OR_NULL(qos)) return PM_QOS_FLAGS_UNDEFINED; @@ -104,6 +106,8 @@ EXPORT_SYMBOL_GPL(dev_pm_qos_flags); */ s32 __dev_pm_qos_read_value(struct device *dev) { + lockdep_assert_held(&dev->power.lock); + return IS_ERR_OR_NULL(dev->power.qos) ? 0 : pm_qos_read_value(&dev->power.qos->resume_latency); } diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 0da5865df5b1..beb8b27d4621 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -51,9 +51,11 @@ struct regmap_async { struct regmap { union { struct mutex mutex; - spinlock_t spinlock; + struct { + spinlock_t spinlock; + unsigned long spinlock_flags; + }; }; - unsigned long spinlock_flags; regmap_lock lock; regmap_unlock unlock; void *lock_arg; /* This is passed to lock/unlock functions */ @@ -233,6 +235,10 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, void regmap_async_complete_cb(struct regmap_async *async, int ret); +enum regmap_endian regmap_get_val_endian(struct device *dev, + const struct regmap_bus *bus, + const struct regmap_config *config); + extern struct regcache_ops regcache_rbtree_ops; extern struct regcache_ops regcache_lzo_ops; extern struct regcache_ops regcache_flat_ops; diff --git a/drivers/base/regmap/regmap-ac97.c b/drivers/base/regmap/regmap-ac97.c index e4c45d2299c1..8d304e2a943d 100644 --- a/drivers/base/regmap/regmap-ac97.c +++ b/drivers/base/regmap/regmap-ac97.c @@ -74,8 +74,8 @@ static int regmap_ac97_reg_write(void *context, unsigned int reg, } static const struct regmap_bus ac97_regmap_bus = { - .reg_write = regmap_ac97_reg_write, - .reg_read = regmap_ac97_reg_read, + .reg_write = regmap_ac97_reg_write, + .reg_read = regmap_ac97_reg_read, }; /** diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index 053150a7f9f2..4b76e33110a2 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -14,6 +14,7 @@ #include <linux/i2c.h> #include <linux/module.h> +#include "internal.h" static int regmap_smbus_byte_reg_read(void *context, unsigned int reg, unsigned int *val) @@ -87,6 +88,42 @@ static struct regmap_bus regmap_smbus_word = { .reg_read = regmap_smbus_word_reg_read, }; +static int regmap_smbus_word_read_swapped(void *context, unsigned int reg, + unsigned int *val) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + int ret; + + if (reg > 0xff) + return -EINVAL; + + ret = i2c_smbus_read_word_swapped(i2c, reg); + if (ret < 0) + return ret; + + *val = ret; + + return 0; +} + +static int regmap_smbus_word_write_swapped(void *context, unsigned int reg, + unsigned int val) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + + if (val > 0xffff || reg > 0xff) + return -EINVAL; + + return i2c_smbus_write_word_swapped(i2c, reg, val); +} + +static struct regmap_bus regmap_smbus_word_swapped = { + .reg_write = regmap_smbus_word_write_swapped, + .reg_read = regmap_smbus_word_read_swapped, +}; + static int regmap_i2c_write(void *context, const void *data, size_t count) { struct device *dev = context; @@ -180,7 +217,14 @@ static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c, else if (config->val_bits == 16 && config->reg_bits == 8 && i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_WORD_DATA)) - return ®map_smbus_word; + switch (regmap_get_val_endian(&i2c->dev, NULL, config)) { + case REGMAP_ENDIAN_LITTLE: + return ®map_smbus_word; + case REGMAP_ENDIAN_BIG: + return ®map_smbus_word_swapped; + default: /* everything else is not supported */ + break; + } else if (config->val_bits == 8 && config->reg_bits == 8 && i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index d2f8a818d200..f99b098ddabf 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -473,9 +473,9 @@ static enum regmap_endian regmap_get_reg_endian(const struct regmap_bus *bus, return REGMAP_ENDIAN_BIG; } -static enum regmap_endian regmap_get_val_endian(struct device *dev, - const struct regmap_bus *bus, - const struct regmap_config *config) +enum regmap_endian regmap_get_val_endian(struct device *dev, + const struct regmap_bus *bus, + const struct regmap_config *config) { struct device_node *np; enum regmap_endian endian; @@ -513,6 +513,7 @@ static enum regmap_endian regmap_get_val_endian(struct device *dev, /* Use this if no other value was found */ return REGMAP_ENDIAN_BIG; } +EXPORT_SYMBOL_GPL(regmap_get_val_endian); /** * regmap_init(): Initialise register map diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h index 314ae4032f3e..ac6c5fca906d 100644 --- a/drivers/bcma/bcma_private.h +++ b/drivers/bcma/bcma_private.h @@ -25,22 +25,18 @@ struct bcma_bus; bool bcma_wait_value(struct bcma_device *core, u16 reg, u32 mask, u32 value, int timeout); void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core); +void bcma_init_bus(struct bcma_bus *bus); int bcma_bus_register(struct bcma_bus *bus); void bcma_bus_unregister(struct bcma_bus *bus); -int __init bcma_bus_early_register(struct bcma_bus *bus, - struct bcma_device *core_cc, - struct bcma_device *core_mips); +int __init bcma_bus_early_register(struct bcma_bus *bus); #ifdef CONFIG_PM int bcma_bus_suspend(struct bcma_bus *bus); int bcma_bus_resume(struct bcma_bus *bus); #endif /* scan.c */ +void bcma_detect_chip(struct bcma_bus *bus); int bcma_bus_scan(struct bcma_bus *bus); -int __init bcma_bus_scan_early(struct bcma_bus *bus, - struct bcma_device_id *match, - struct bcma_device *core); -void bcma_init_bus(struct bcma_bus *bus); /* sprom.c */ int bcma_sprom_get(struct bcma_bus *bus); @@ -111,6 +107,14 @@ extern int bcma_chipco_watchdog_register(struct bcma_drv_cc *cc); #ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE bool bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc); void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc); +#else +static inline bool bcma_core_pci_is_in_hostmode(struct bcma_drv_pci *pc) +{ + return false; +} +static inline void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc) +{ +} #endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */ #ifdef CONFIG_BCMA_DRIVER_GPIO diff --git a/drivers/bcma/driver_chipcommon.c b/drivers/bcma/driver_chipcommon.c index 19f679667ca4..b7c8a8d4e6d1 100644 --- a/drivers/bcma/driver_chipcommon.c +++ b/drivers/bcma/driver_chipcommon.c @@ -79,7 +79,9 @@ static int bcma_chipco_watchdog_ticks_per_ms(struct bcma_drv_cc *cc) if (cc->capabilities & BCMA_CC_CAP_PMU) { if (bus->chipinfo.id == BCMA_CHIP_ID_BCM4706) - /* 4706 CC and PMU watchdogs are clocked at 1/4 of ALP clock */ + /* 4706 CC and PMU watchdogs are clocked at 1/4 of ALP + * clock + */ return bcma_chipco_get_alp_clock(cc) / 4000; else /* based on 32KHz ILP clock */ @@ -97,7 +99,8 @@ int bcma_chipco_watchdog_register(struct bcma_drv_cc *cc) wdt.driver_data = cc; wdt.timer_set = bcma_chipco_watchdog_timer_set_wdt; wdt.timer_set_ms = bcma_chipco_watchdog_timer_set_ms_wdt; - wdt.max_timer_ms = bcma_chipco_watchdog_get_max_timer(cc) / cc->ticks_per_ms; + wdt.max_timer_ms = + bcma_chipco_watchdog_get_max_timer(cc) / cc->ticks_per_ms; pdev = platform_device_register_data(NULL, "bcm47xx-wdt", cc->core->bus->num, &wdt, @@ -175,7 +178,6 @@ void bcma_core_chipcommon_init(struct bcma_drv_cc *cc) u32 bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks) { u32 maxt; - enum bcma_clkmode clkmode; maxt = bcma_chipco_watchdog_get_max_timer(cc); if (cc->capabilities & BCMA_CC_CAP_PMU) { @@ -185,8 +187,13 @@ u32 bcma_chipco_watchdog_timer_set(struct bcma_drv_cc *cc, u32 ticks) ticks = maxt; bcma_cc_write32(cc, BCMA_CC_PMU_WATCHDOG, ticks); } else { - clkmode = ticks ? BCMA_CLKMODE_FAST : BCMA_CLKMODE_DYNAMIC; - bcma_core_set_clockmode(cc->core, clkmode); + struct bcma_bus *bus = cc->core->bus; + + if (bus->chipinfo.id != BCMA_CHIP_ID_BCM4707 && + bus->chipinfo.id != BCMA_CHIP_ID_BCM53018) + bcma_core_set_clockmode(cc->core, + ticks ? BCMA_CLKMODE_FAST : BCMA_CLKMODE_DYNAMIC); + if (ticks > maxt) ticks = maxt; /* instant NMI */ @@ -335,7 +342,8 @@ void bcma_chipco_serial_init(struct bcma_drv_cc *cc) | BCMA_CC_CORECTL_UARTCLKEN); } } else { - bcma_err(cc->core->bus, "serial not supported on this device ccrev: 0x%x\n", ccrev); + bcma_err(cc->core->bus, "serial not supported on this device ccrev: 0x%x\n", + ccrev); return; } diff --git a/drivers/bcma/driver_pci.c b/drivers/bcma/driver_pci.c index 50329d1057ed..786666488a2d 100644 --- a/drivers/bcma/driver_pci.c +++ b/drivers/bcma/driver_pci.c @@ -145,6 +145,47 @@ static u16 bcma_pcie_mdio_writeread(struct bcma_drv_pci *pc, u16 device, } /************************************************** + * Early init. + **************************************************/ + +static void bcma_core_pci_fixcfg(struct bcma_drv_pci *pc) +{ + struct bcma_device *core = pc->core; + u16 val16, core_index; + uint regoff; + + regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_PI_OFFSET); + core_index = (u16)core->core_index; + + val16 = pcicore_read16(pc, regoff); + if (((val16 & BCMA_CORE_PCI_SPROM_PI_MASK) >> BCMA_CORE_PCI_SPROM_PI_SHIFT) + != core_index) { + val16 = (core_index << BCMA_CORE_PCI_SPROM_PI_SHIFT) | + (val16 & ~BCMA_CORE_PCI_SPROM_PI_MASK); + pcicore_write16(pc, regoff, val16); + } +} + +/* + * Apply some early fixes required before accessing SPROM. + * See also si_pci_fixcfg. + */ +void bcma_core_pci_early_init(struct bcma_drv_pci *pc) +{ + if (pc->early_setup_done) + return; + + pc->hostmode = bcma_core_pci_is_in_hostmode(pc); + if (pc->hostmode) + goto out; + + bcma_core_pci_fixcfg(pc); + +out: + pc->early_setup_done = true; +} + +/************************************************** * Workarounds. **************************************************/ @@ -175,24 +216,6 @@ static void bcma_pcicore_serdes_workaround(struct bcma_drv_pci *pc) tmp & ~BCMA_CORE_PCI_PLL_CTRL_FREQDET_EN); } -static void bcma_core_pci_fixcfg(struct bcma_drv_pci *pc) -{ - struct bcma_device *core = pc->core; - u16 val16, core_index; - uint regoff; - - regoff = BCMA_CORE_PCI_SPROM(BCMA_CORE_PCI_SPROM_PI_OFFSET); - core_index = (u16)core->core_index; - - val16 = pcicore_read16(pc, regoff); - if (((val16 & BCMA_CORE_PCI_SPROM_PI_MASK) >> BCMA_CORE_PCI_SPROM_PI_SHIFT) - != core_index) { - val16 = (core_index << BCMA_CORE_PCI_SPROM_PI_SHIFT) | - (val16 & ~BCMA_CORE_PCI_SPROM_PI_MASK); - pcicore_write16(pc, regoff, val16); - } -} - /* Fix MISC config to allow coming out of L2/L3-Ready state w/o PRST */ /* Needs to happen when coming out of 'standby'/'hibernate' */ static void bcma_core_pci_config_fixup(struct bcma_drv_pci *pc) @@ -216,7 +239,6 @@ static void bcma_core_pci_config_fixup(struct bcma_drv_pci *pc) static void bcma_core_pci_clientmode_init(struct bcma_drv_pci *pc) { - bcma_core_pci_fixcfg(pc); bcma_pcicore_serdes_workaround(pc); bcma_core_pci_config_fixup(pc); } @@ -226,13 +248,11 @@ void bcma_core_pci_init(struct bcma_drv_pci *pc) if (pc->setup_done) return; -#ifdef CONFIG_BCMA_DRIVER_PCI_HOSTMODE - pc->hostmode = bcma_core_pci_is_in_hostmode(pc); + bcma_core_pci_early_init(pc); + if (pc->hostmode) bcma_core_pci_hostmode_init(pc); -#endif /* CONFIG_BCMA_DRIVER_PCI_HOSTMODE */ - - if (!pc->hostmode) + else bcma_core_pci_clientmode_init(pc); } diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c index cd9161a8b3a1..53c6a8a58859 100644 --- a/drivers/bcma/host_pci.c +++ b/drivers/bcma/host_pci.c @@ -13,10 +13,12 @@ static void bcma_host_pci_switch_core(struct bcma_device *core) { + int win2 = core->bus->host_is_pcie2 ? + BCMA_PCIE2_BAR0_WIN2 : BCMA_PCI_BAR0_WIN2; + pci_write_config_dword(core->bus->host_pci, BCMA_PCI_BAR0_WIN, core->addr); - pci_write_config_dword(core->bus->host_pci, BCMA_PCI_BAR0_WIN2, - core->wrap); + pci_write_config_dword(core->bus->host_pci, win2, core->wrap); core->bus->mapped_core = core; bcma_debug(core->bus, "Switched to core: 0x%X\n", core->id.id); } diff --git a/drivers/bcma/host_soc.c b/drivers/bcma/host_soc.c index 335cbcfd945b..2dce34789329 100644 --- a/drivers/bcma/host_soc.c +++ b/drivers/bcma/host_soc.c @@ -193,7 +193,7 @@ int __init bcma_host_soc_init(struct bcma_soc *soc) int err; /* Scan bus and initialize it */ - err = bcma_bus_early_register(bus, &soc->core_cc, &soc->core_mips); + err = bcma_bus_early_register(bus); if (err) iounmap(bus->mmio); diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 534e1337766d..38bde6eab8a4 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -268,6 +268,18 @@ void bcma_prepare_core(struct bcma_bus *bus, struct bcma_device *core) } } +void bcma_init_bus(struct bcma_bus *bus) +{ + mutex_lock(&bcma_buses_mutex); + bus->num = bcma_bus_next_num++; + mutex_unlock(&bcma_buses_mutex); + + INIT_LIST_HEAD(&bus->cores); + bus->nr_cores = 0; + + bcma_detect_chip(bus); +} + static void bcma_register_core(struct bcma_bus *bus, struct bcma_device *core) { int err; @@ -356,12 +368,19 @@ static void bcma_unregister_cores(struct bcma_bus *bus) struct bcma_device *core, *tmp; list_for_each_entry_safe(core, tmp, &bus->cores, list) { + if (!core->dev_registered) + continue; list_del(&core->list); - if (core->dev_registered) - device_unregister(&core->dev); + device_unregister(&core->dev); } if (bus->hosttype == BCMA_HOSTTYPE_SOC) platform_device_unregister(bus->drv_cc.watchdog); + + /* Now noone uses internally-handled cores, we can free them */ + list_for_each_entry_safe(core, tmp, &bus->cores, list) { + list_del(&core->list); + kfree(core); + } } int bcma_bus_register(struct bcma_bus *bus) @@ -369,10 +388,6 @@ int bcma_bus_register(struct bcma_bus *bus) int err; struct bcma_device *core; - mutex_lock(&bcma_buses_mutex); - bus->num = bcma_bus_next_num++; - mutex_unlock(&bcma_buses_mutex); - /* Scan for devices (cores) */ err = bcma_bus_scan(bus); if (err) { @@ -387,6 +402,13 @@ int bcma_bus_register(struct bcma_bus *bus) bcma_core_chipcommon_early_init(&bus->drv_cc); } + /* Early init PCIE core */ + core = bcma_find_core(bus, BCMA_CORE_PCIE); + if (core) { + bus->drv_pci[0].core = core; + bcma_core_pci_early_init(&bus->drv_pci[0]); + } + /* Cores providing flash access go before SPROM init */ list_for_each_entry(core, &bus->cores, list) { if (bcma_is_core_needed_early(core->id.id)) @@ -459,7 +481,6 @@ int bcma_bus_register(struct bcma_bus *bus) void bcma_bus_unregister(struct bcma_bus *bus) { - struct bcma_device *cores[3]; int err; err = bcma_gpio_unregister(&bus->drv_cc); @@ -470,46 +491,23 @@ void bcma_bus_unregister(struct bcma_bus *bus) bcma_core_chipcommon_b_free(&bus->drv_cc_b); - cores[0] = bcma_find_core(bus, BCMA_CORE_MIPS_74K); - cores[1] = bcma_find_core(bus, BCMA_CORE_PCIE); - cores[2] = bcma_find_core(bus, BCMA_CORE_4706_MAC_GBIT_COMMON); - bcma_unregister_cores(bus); - - kfree(cores[2]); - kfree(cores[1]); - kfree(cores[0]); } -int __init bcma_bus_early_register(struct bcma_bus *bus, - struct bcma_device *core_cc, - struct bcma_device *core_mips) +/* + * This is a special version of bus registration function designed for SoCs. + * It scans bus and performs basic initialization of main cores only. + * Please note it requires memory allocation, however it won't try to sleep. + */ +int __init bcma_bus_early_register(struct bcma_bus *bus) { int err; struct bcma_device *core; - struct bcma_device_id match; - match.manuf = BCMA_MANUF_BCM; - match.id = bcma_cc_core_id(bus); - match.class = BCMA_CL_SIM; - match.rev = BCMA_ANY_REV; - - /* Scan for chip common core */ - err = bcma_bus_scan_early(bus, &match, core_cc); - if (err) { - bcma_err(bus, "Failed to scan for common core: %d\n", err); - return -1; - } - - match.manuf = BCMA_MANUF_MIPS; - match.id = BCMA_CORE_MIPS_74K; - match.class = BCMA_CL_SIM; - match.rev = BCMA_ANY_REV; - - /* Scan for mips core */ - err = bcma_bus_scan_early(bus, &match, core_mips); + /* Scan for devices (cores) */ + err = bcma_bus_scan(bus); if (err) { - bcma_err(bus, "Failed to scan for mips core: %d\n", err); + bcma_err(bus, "Failed to scan bus: %d\n", err); return -1; } diff --git a/drivers/bcma/scan.c b/drivers/bcma/scan.c index 917520776879..df806b9c5490 100644 --- a/drivers/bcma/scan.c +++ b/drivers/bcma/scan.c @@ -435,15 +435,12 @@ static int bcma_get_next_core(struct bcma_bus *bus, u32 __iomem **eromptr, return 0; } -void bcma_init_bus(struct bcma_bus *bus) +void bcma_detect_chip(struct bcma_bus *bus) { s32 tmp; struct bcma_chipinfo *chipinfo = &(bus->chipinfo); char chip_id[8]; - INIT_LIST_HEAD(&bus->cores); - bus->nr_cores = 0; - bcma_scan_switch_core(bus, BCMA_ADDR_BASE); tmp = bcma_scan_read32(bus, 0, BCMA_CC_ID); @@ -464,6 +461,10 @@ int bcma_bus_scan(struct bcma_bus *bus) int err, core_num = 0; + /* Skip if bus was already scanned (e.g. during early register) */ + if (bus->nr_cores) + return 0; + erombase = bcma_scan_read32(bus, 0, BCMA_CC_EROM); if (bus->hosttype == BCMA_HOSTTYPE_SOC) { eromptr = ioremap_nocache(erombase, BCMA_CORE_SIZE); @@ -522,61 +523,3 @@ out: return err; } - -int __init bcma_bus_scan_early(struct bcma_bus *bus, - struct bcma_device_id *match, - struct bcma_device *core) -{ - u32 erombase; - u32 __iomem *eromptr, *eromend; - - int err = -ENODEV; - int core_num = 0; - - erombase = bcma_scan_read32(bus, 0, BCMA_CC_EROM); - if (bus->hosttype == BCMA_HOSTTYPE_SOC) { - eromptr = ioremap_nocache(erombase, BCMA_CORE_SIZE); - if (!eromptr) - return -ENOMEM; - } else { - eromptr = bus->mmio; - } - - eromend = eromptr + BCMA_CORE_SIZE / sizeof(u32); - - bcma_scan_switch_core(bus, erombase); - - while (eromptr < eromend) { - memset(core, 0, sizeof(*core)); - INIT_LIST_HEAD(&core->list); - core->bus = bus; - - err = bcma_get_next_core(bus, &eromptr, match, core_num, core); - if (err == -ENODEV) { - core_num++; - continue; - } else if (err == -ENXIO) - continue; - else if (err == -ESPIPE) - break; - else if (err < 0) - goto out; - - core->core_index = core_num++; - bus->nr_cores++; - bcma_info(bus, "Core %d found: %s (manuf 0x%03X, id 0x%03X, rev 0x%02X, class 0x%X)\n", - core->core_index, bcma_device_name(&core->id), - core->id.manuf, core->id.id, core->id.rev, - core->id.class); - - list_add_tail(&core->list, &bus->cores); - err = 0; - break; - } - -out: - if (bus->hosttype == BCMA_HOSTTYPE_SOC) - iounmap(eromptr); - - return err; -} diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c index efb037f9c98a..206edd3ba668 100644 --- a/drivers/bcma/sprom.c +++ b/drivers/bcma/sprom.c @@ -579,7 +579,8 @@ int bcma_sprom_get(struct bcma_bus *bus) u16 offset = BCMA_CC_SPROM; u16 *sprom; size_t sprom_sizes[] = { SSB_SPROMSIZE_WORDS_R4, - SSB_SPROMSIZE_WORDS_R10, }; + SSB_SPROMSIZE_WORDS_R10, + SSB_SPROMSIZE_WORDS_R11, }; int i, err = 0; if (!bus->drv_cc.core) diff --git a/drivers/block/null_blk.c b/drivers/block/null_blk.c index ae9f615382f6..aa2224aa7caa 100644 --- a/drivers/block/null_blk.c +++ b/drivers/block/null_blk.c @@ -530,7 +530,7 @@ static int null_add_dev(void) goto out_cleanup_queues; nullb->q = blk_mq_init_queue(&nullb->tag_set); - if (!nullb->q) { + if (IS_ERR(nullb->q)) { rv = -ENOMEM; goto out_cleanup_tags; } diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c index b1d5d8797315..d826bf3e62c8 100644 --- a/drivers/block/nvme-core.c +++ b/drivers/block/nvme-core.c @@ -106,7 +106,7 @@ struct nvme_queue { dma_addr_t cq_dma_addr; u32 __iomem *q_db; u16 q_depth; - u16 cq_vector; + s16 cq_vector; u16 sq_head; u16 sq_tail; u16 cq_head; @@ -215,6 +215,7 @@ static void nvme_set_info(struct nvme_cmd_info *cmd, void *ctx, cmd->fn = handler; cmd->ctx = ctx; cmd->aborted = 0; + blk_mq_start_request(blk_mq_rq_from_pdu(cmd)); } /* Special values must be less than 0x1000 */ @@ -431,8 +432,13 @@ static void req_completion(struct nvme_queue *nvmeq, void *ctx, if (unlikely(status)) { if (!(status & NVME_SC_DNR || blk_noretry_request(req)) && (jiffies - req->start_time) < req->timeout) { + unsigned long flags; + blk_mq_requeue_request(req); - blk_mq_kick_requeue_list(req->q); + spin_lock_irqsave(req->q->queue_lock, flags); + if (!blk_queue_stopped(req->q)) + blk_mq_kick_requeue_list(req->q); + spin_unlock_irqrestore(req->q->queue_lock, flags); return; } req->errors = nvme_error_status(status); @@ -664,8 +670,6 @@ static int nvme_queue_rq(struct blk_mq_hw_ctx *hctx, } } - blk_mq_start_request(req); - nvme_set_info(cmd, iod, req_completion); spin_lock_irq(&nvmeq->q_lock); if (req->cmd_flags & REQ_DISCARD) @@ -835,6 +839,7 @@ static int nvme_submit_async_admin_req(struct nvme_dev *dev) if (IS_ERR(req)) return PTR_ERR(req); + req->cmd_flags |= REQ_NO_TIMEOUT; cmd_info = blk_mq_rq_to_pdu(req); nvme_set_info(cmd_info, req, async_req_completion); @@ -1016,14 +1021,19 @@ static void nvme_abort_req(struct request *req) struct nvme_command cmd; if (!nvmeq->qid || cmd_rq->aborted) { + unsigned long flags; + + spin_lock_irqsave(&dev_list_lock, flags); if (work_busy(&dev->reset_work)) - return; + goto out; list_del_init(&dev->node); dev_warn(&dev->pci_dev->dev, "I/O %d QID %d timeout, reset controller\n", req->tag, nvmeq->qid); dev->reset_workfn = nvme_reset_failed_dev; queue_work(nvme_workq, &dev->reset_work); + out: + spin_unlock_irqrestore(&dev_list_lock, flags); return; } @@ -1064,15 +1074,22 @@ static void nvme_cancel_queue_ios(struct blk_mq_hw_ctx *hctx, void *ctx; nvme_completion_fn fn; struct nvme_cmd_info *cmd; - static struct nvme_completion cqe = { - .status = cpu_to_le16(NVME_SC_ABORT_REQ << 1), - }; + struct nvme_completion cqe; + + if (!blk_mq_request_started(req)) + return; cmd = blk_mq_rq_to_pdu(req); if (cmd->ctx == CMD_CTX_CANCELLED) return; + if (blk_queue_dying(req->q)) + cqe.status = cpu_to_le16((NVME_SC_ABORT_REQ | NVME_SC_DNR) << 1); + else + cqe.status = cpu_to_le16(NVME_SC_ABORT_REQ << 1); + + dev_warn(nvmeq->q_dmadev, "Cancelling I/O %d QID %d\n", req->tag, nvmeq->qid); ctx = cancel_cmd_info(cmd, &fn); @@ -1084,17 +1101,29 @@ static enum blk_eh_timer_return nvme_timeout(struct request *req, bool reserved) struct nvme_cmd_info *cmd = blk_mq_rq_to_pdu(req); struct nvme_queue *nvmeq = cmd->nvmeq; - dev_warn(nvmeq->q_dmadev, "Timeout I/O %d QID %d\n", req->tag, - nvmeq->qid); - if (nvmeq->dev->initialized) - nvme_abort_req(req); - /* * The aborted req will be completed on receiving the abort req. * We enable the timer again. If hit twice, it'll cause a device reset, * as the device then is in a faulty state. */ - return BLK_EH_RESET_TIMER; + int ret = BLK_EH_RESET_TIMER; + + dev_warn(nvmeq->q_dmadev, "Timeout I/O %d QID %d\n", req->tag, + nvmeq->qid); + + spin_lock_irq(&nvmeq->q_lock); + if (!nvmeq->dev->initialized) { + /* + * Force cancelled command frees the request, which requires we + * return BLK_EH_NOT_HANDLED. + */ + nvme_cancel_queue_ios(nvmeq->hctx, req, nvmeq, reserved); + ret = BLK_EH_NOT_HANDLED; + } else + nvme_abort_req(req); + spin_unlock_irq(&nvmeq->q_lock); + + return ret; } static void nvme_free_queue(struct nvme_queue *nvmeq) @@ -1131,10 +1160,16 @@ static void nvme_free_queues(struct nvme_dev *dev, int lowest) */ static int nvme_suspend_queue(struct nvme_queue *nvmeq) { - int vector = nvmeq->dev->entry[nvmeq->cq_vector].vector; + int vector; spin_lock_irq(&nvmeq->q_lock); + if (nvmeq->cq_vector == -1) { + spin_unlock_irq(&nvmeq->q_lock); + return 1; + } + vector = nvmeq->dev->entry[nvmeq->cq_vector].vector; nvmeq->dev->online_queues--; + nvmeq->cq_vector = -1; spin_unlock_irq(&nvmeq->q_lock); irq_set_affinity_hint(vector, NULL); @@ -1169,11 +1204,13 @@ static void nvme_disable_queue(struct nvme_dev *dev, int qid) adapter_delete_sq(dev, qid); adapter_delete_cq(dev, qid); } + if (!qid && dev->admin_q) + blk_mq_freeze_queue_start(dev->admin_q); nvme_clear_queue(nvmeq); } static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid, - int depth, int vector) + int depth) { struct device *dmadev = &dev->pci_dev->dev; struct nvme_queue *nvmeq = kzalloc(sizeof(*nvmeq), GFP_KERNEL); @@ -1199,7 +1236,6 @@ static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid, nvmeq->cq_phase = 1; nvmeq->q_db = &dev->dbs[qid * 2 * dev->db_stride]; nvmeq->q_depth = depth; - nvmeq->cq_vector = vector; nvmeq->qid = qid; dev->queue_count++; dev->queues[qid] = nvmeq; @@ -1244,6 +1280,7 @@ static int nvme_create_queue(struct nvme_queue *nvmeq, int qid) struct nvme_dev *dev = nvmeq->dev; int result; + nvmeq->cq_vector = qid - 1; result = adapter_alloc_cq(dev, qid, nvmeq); if (result < 0) return result; @@ -1355,6 +1392,14 @@ static struct blk_mq_ops nvme_mq_ops = { .timeout = nvme_timeout, }; +static void nvme_dev_remove_admin(struct nvme_dev *dev) +{ + if (dev->admin_q && !blk_queue_dying(dev->admin_q)) { + blk_cleanup_queue(dev->admin_q); + blk_mq_free_tag_set(&dev->admin_tagset); + } +} + static int nvme_alloc_admin_tags(struct nvme_dev *dev) { if (!dev->admin_q) { @@ -1370,21 +1415,20 @@ static int nvme_alloc_admin_tags(struct nvme_dev *dev) return -ENOMEM; dev->admin_q = blk_mq_init_queue(&dev->admin_tagset); - if (!dev->admin_q) { + if (IS_ERR(dev->admin_q)) { blk_mq_free_tag_set(&dev->admin_tagset); return -ENOMEM; } - } + if (!blk_get_queue(dev->admin_q)) { + nvme_dev_remove_admin(dev); + return -ENODEV; + } + } else + blk_mq_unfreeze_queue(dev->admin_q); return 0; } -static void nvme_free_admin_tags(struct nvme_dev *dev) -{ - if (dev->admin_q) - blk_mq_free_tag_set(&dev->admin_tagset); -} - static int nvme_configure_admin_queue(struct nvme_dev *dev) { int result; @@ -1416,7 +1460,7 @@ static int nvme_configure_admin_queue(struct nvme_dev *dev) nvmeq = dev->queues[0]; if (!nvmeq) { - nvmeq = nvme_alloc_queue(dev, 0, NVME_AQ_DEPTH, 0); + nvmeq = nvme_alloc_queue(dev, 0, NVME_AQ_DEPTH); if (!nvmeq) return -ENOMEM; } @@ -1439,18 +1483,13 @@ static int nvme_configure_admin_queue(struct nvme_dev *dev) if (result) goto free_nvmeq; - result = nvme_alloc_admin_tags(dev); - if (result) - goto free_nvmeq; - + nvmeq->cq_vector = 0; result = queue_request_irq(dev, nvmeq, nvmeq->irqname); if (result) - goto free_tags; + goto free_nvmeq; return result; - free_tags: - nvme_free_admin_tags(dev); free_nvmeq: nvme_free_queues(dev, 0); return result; @@ -1944,7 +1983,7 @@ static void nvme_create_io_queues(struct nvme_dev *dev) unsigned i; for (i = dev->queue_count; i <= dev->max_qid; i++) - if (!nvme_alloc_queue(dev, i, dev->q_depth, i - 1)) + if (!nvme_alloc_queue(dev, i, dev->q_depth)) break; for (i = dev->online_queues; i <= dev->queue_count - 1; i++) @@ -2235,13 +2274,18 @@ static void nvme_wait_dq(struct nvme_delq_ctx *dq, struct nvme_dev *dev) break; if (!schedule_timeout(ADMIN_TIMEOUT) || fatal_signal_pending(current)) { + /* + * Disable the controller first since we can't trust it + * at this point, but leave the admin queue enabled + * until all queue deletion requests are flushed. + * FIXME: This may take a while if there are more h/w + * queues than admin tags. + */ set_current_state(TASK_RUNNING); - nvme_disable_ctrl(dev, readq(&dev->bar->cap)); - nvme_disable_queue(dev, 0); - - send_sig(SIGKILL, dq->worker->task, 1); + nvme_clear_queue(dev->queues[0]); flush_kthread_worker(dq->worker); + nvme_disable_queue(dev, 0); return; } } @@ -2318,7 +2362,6 @@ static void nvme_del_queue_start(struct kthread_work *work) { struct nvme_queue *nvmeq = container_of(work, struct nvme_queue, cmdinfo.work); - allow_signal(SIGKILL); if (nvme_delete_sq(nvmeq)) nvme_del_queue_end(nvmeq); } @@ -2376,6 +2419,34 @@ static void nvme_dev_list_remove(struct nvme_dev *dev) kthread_stop(tmp); } +static void nvme_freeze_queues(struct nvme_dev *dev) +{ + struct nvme_ns *ns; + + list_for_each_entry(ns, &dev->namespaces, list) { + blk_mq_freeze_queue_start(ns->queue); + + spin_lock(ns->queue->queue_lock); + queue_flag_set(QUEUE_FLAG_STOPPED, ns->queue); + spin_unlock(ns->queue->queue_lock); + + blk_mq_cancel_requeue_work(ns->queue); + blk_mq_stop_hw_queues(ns->queue); + } +} + +static void nvme_unfreeze_queues(struct nvme_dev *dev) +{ + struct nvme_ns *ns; + + list_for_each_entry(ns, &dev->namespaces, list) { + queue_flag_clear_unlocked(QUEUE_FLAG_STOPPED, ns->queue); + blk_mq_unfreeze_queue(ns->queue); + blk_mq_start_stopped_hw_queues(ns->queue, true); + blk_mq_kick_requeue_list(ns->queue); + } +} + static void nvme_dev_shutdown(struct nvme_dev *dev) { int i; @@ -2384,8 +2455,10 @@ static void nvme_dev_shutdown(struct nvme_dev *dev) dev->initialized = 0; nvme_dev_list_remove(dev); - if (dev->bar) + if (dev->bar) { + nvme_freeze_queues(dev); csts = readl(&dev->bar->csts); + } if (csts & NVME_CSTS_CFS || !(csts & NVME_CSTS_RDY)) { for (i = dev->queue_count - 1; i >= 0; i--) { struct nvme_queue *nvmeq = dev->queues[i]; @@ -2400,12 +2473,6 @@ static void nvme_dev_shutdown(struct nvme_dev *dev) nvme_dev_unmap(dev); } -static void nvme_dev_remove_admin(struct nvme_dev *dev) -{ - if (dev->admin_q && !blk_queue_dying(dev->admin_q)) - blk_cleanup_queue(dev->admin_q); -} - static void nvme_dev_remove(struct nvme_dev *dev) { struct nvme_ns *ns; @@ -2413,8 +2480,10 @@ static void nvme_dev_remove(struct nvme_dev *dev) list_for_each_entry(ns, &dev->namespaces, list) { if (ns->disk->flags & GENHD_FL_UP) del_gendisk(ns->disk); - if (!blk_queue_dying(ns->queue)) + if (!blk_queue_dying(ns->queue)) { + blk_mq_abort_requeue_list(ns->queue); blk_cleanup_queue(ns->queue); + } } } @@ -2495,6 +2564,7 @@ static void nvme_free_dev(struct kref *kref) nvme_free_namespaces(dev); nvme_release_instance(dev); blk_mq_free_tag_set(&dev->tagset); + blk_put_queue(dev->admin_q); kfree(dev->queues); kfree(dev->entry); kfree(dev); @@ -2591,15 +2661,20 @@ static int nvme_dev_start(struct nvme_dev *dev) } nvme_init_queue(dev->queues[0], 0); + result = nvme_alloc_admin_tags(dev); + if (result) + goto disable; result = nvme_setup_io_queues(dev); if (result) - goto disable; + goto free_tags; nvme_set_irq_hints(dev); return result; + free_tags: + nvme_dev_remove_admin(dev); disable: nvme_disable_queue(dev, 0); nvme_dev_list_remove(dev); @@ -2639,6 +2714,9 @@ static int nvme_dev_resume(struct nvme_dev *dev) dev->reset_workfn = nvme_remove_disks; queue_work(nvme_workq, &dev->reset_work); spin_unlock(&dev_list_lock); + } else { + nvme_unfreeze_queues(dev); + nvme_set_irq_hints(dev); } dev->initialized = 1; return 0; @@ -2776,11 +2854,10 @@ static void nvme_remove(struct pci_dev *pdev) pci_set_drvdata(pdev, NULL); flush_work(&dev->reset_work); misc_deregister(&dev->miscdev); - nvme_dev_remove(dev); nvme_dev_shutdown(dev); + nvme_dev_remove(dev); nvme_dev_remove_admin(dev); nvme_free_queues(dev, 0); - nvme_free_admin_tags(dev); nvme_release_prp_pools(dev); kref_put(&dev->kref, nvme_free_dev); } diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index 3ec85dfce124..8a86b62466f7 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c @@ -2098,32 +2098,26 @@ static void rbd_dev_parent_put(struct rbd_device *rbd_dev) * If an image has a non-zero parent overlap, get a reference to its * parent. * - * We must get the reference before checking for the overlap to - * coordinate properly with zeroing the parent overlap in - * rbd_dev_v2_parent_info() when an image gets flattened. We - * drop it again if there is no overlap. - * * Returns true if the rbd device has a parent with a non-zero * overlap and a reference for it was successfully taken, or * false otherwise. */ static bool rbd_dev_parent_get(struct rbd_device *rbd_dev) { - int counter; + int counter = 0; if (!rbd_dev->parent_spec) return false; - counter = atomic_inc_return_safe(&rbd_dev->parent_ref); - if (counter > 0 && rbd_dev->parent_overlap) - return true; - - /* Image was flattened, but parent is not yet torn down */ + down_read(&rbd_dev->header_rwsem); + if (rbd_dev->parent_overlap) + counter = atomic_inc_return_safe(&rbd_dev->parent_ref); + up_read(&rbd_dev->header_rwsem); if (counter < 0) rbd_warn(rbd_dev, "parent reference overflow"); - return false; + return counter > 0; } /* @@ -4239,7 +4233,6 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) */ if (rbd_dev->parent_overlap) { rbd_dev->parent_overlap = 0; - smp_mb(); rbd_dev_parent_put(rbd_dev); pr_info("%s: clone image has been flattened\n", rbd_dev->disk->disk_name); @@ -4285,7 +4278,6 @@ static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) * treat it specially. */ rbd_dev->parent_overlap = overlap; - smp_mb(); if (!overlap) { /* A null parent_spec indicates it's the initial probe */ @@ -5114,10 +5106,7 @@ static void rbd_dev_unprobe(struct rbd_device *rbd_dev) { struct rbd_image_header *header; - /* Drop parent reference unless it's already been done (or none) */ - - if (rbd_dev->parent_overlap) - rbd_dev_parent_put(rbd_dev); + rbd_dev_parent_put(rbd_dev); /* Free dynamic fields from the header, then zero it out */ diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 7ef7c098708f..cdfbd21e3597 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -638,7 +638,7 @@ static int virtblk_probe(struct virtio_device *vdev) goto out_put_disk; q = vblk->disk->queue = blk_mq_init_queue(&vblk->tag_set); - if (!q) { + if (IS_ERR(q)) { err = -ENOMEM; goto out_free_tags; } diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c index 63fc7f06a014..2a04d341e598 100644 --- a/drivers/block/xen-blkback/blkback.c +++ b/drivers/block/xen-blkback/blkback.c @@ -47,6 +47,7 @@ #include <asm/xen/hypervisor.h> #include <asm/xen/hypercall.h> #include <xen/balloon.h> +#include <xen/grant_table.h> #include "common.h" /* @@ -100,7 +101,7 @@ module_param(log_stats, int, 0644); #define BLKBACK_INVALID_HANDLE (~0) -/* Number of free pages to remove on each call to free_xenballooned_pages */ +/* Number of free pages to remove on each call to gnttab_free_pages */ #define NUM_BATCH_FREE_PAGES 10 static inline int get_free_page(struct xen_blkif *blkif, struct page **page) @@ -111,7 +112,7 @@ static inline int get_free_page(struct xen_blkif *blkif, struct page **page) if (list_empty(&blkif->free_pages)) { BUG_ON(blkif->free_pages_num != 0); spin_unlock_irqrestore(&blkif->free_pages_lock, flags); - return alloc_xenballooned_pages(1, page, false); + return gnttab_alloc_pages(1, page); } BUG_ON(blkif->free_pages_num == 0); page[0] = list_first_entry(&blkif->free_pages, struct page, lru); @@ -151,14 +152,14 @@ static inline void shrink_free_pagepool(struct xen_blkif *blkif, int num) blkif->free_pages_num--; if (++num_pages == NUM_BATCH_FREE_PAGES) { spin_unlock_irqrestore(&blkif->free_pages_lock, flags); - free_xenballooned_pages(num_pages, page); + gnttab_free_pages(num_pages, page); spin_lock_irqsave(&blkif->free_pages_lock, flags); num_pages = 0; } } spin_unlock_irqrestore(&blkif->free_pages_lock, flags); if (num_pages != 0) - free_xenballooned_pages(num_pages, page); + gnttab_free_pages(num_pages, page); } #define vaddr(page) ((unsigned long)pfn_to_kaddr(page_to_pfn(page))) @@ -262,6 +263,17 @@ static void put_persistent_gnt(struct xen_blkif *blkif, atomic_dec(&blkif->persistent_gnt_in_use); } +static void free_persistent_gnts_unmap_callback(int result, + struct gntab_unmap_queue_data *data) +{ + struct completion *c = data->data; + + /* BUG_ON used to reproduce existing behaviour, + but is this the best way to deal with this? */ + BUG_ON(result); + complete(c); +} + static void free_persistent_gnts(struct xen_blkif *blkif, struct rb_root *root, unsigned int num) { @@ -269,8 +281,17 @@ static void free_persistent_gnts(struct xen_blkif *blkif, struct rb_root *root, struct page *pages[BLKIF_MAX_SEGMENTS_PER_REQUEST]; struct persistent_gnt *persistent_gnt; struct rb_node *n; - int ret = 0; int segs_to_unmap = 0; + struct gntab_unmap_queue_data unmap_data; + struct completion unmap_completion; + + init_completion(&unmap_completion); + + unmap_data.data = &unmap_completion; + unmap_data.done = &free_persistent_gnts_unmap_callback; + unmap_data.pages = pages; + unmap_data.unmap_ops = unmap; + unmap_data.kunmap_ops = NULL; foreach_grant_safe(persistent_gnt, n, root, node) { BUG_ON(persistent_gnt->handle == @@ -285,9 +306,11 @@ static void free_persistent_gnts(struct xen_blkif *blkif, struct rb_root *root, if (++segs_to_unmap == BLKIF_MAX_SEGMENTS_PER_REQUEST || !rb_next(&persistent_gnt->node)) { - ret = gnttab_unmap_refs(unmap, NULL, pages, - segs_to_unmap); - BUG_ON(ret); + + unmap_data.count = segs_to_unmap; + gnttab_unmap_refs_async(&unmap_data); + wait_for_completion(&unmap_completion); + put_free_pages(blkif, pages, segs_to_unmap); segs_to_unmap = 0; } @@ -653,18 +676,14 @@ void xen_blkbk_free_caches(struct xen_blkif *blkif) shrink_free_pagepool(blkif, 0 /* All */); } -/* - * Unmap the grant references, and also remove the M2P over-rides - * used in the 'pending_req'. - */ -static void xen_blkbk_unmap(struct xen_blkif *blkif, - struct grant_page *pages[], - int num) +static unsigned int xen_blkbk_unmap_prepare( + struct xen_blkif *blkif, + struct grant_page **pages, + unsigned int num, + struct gnttab_unmap_grant_ref *unmap_ops, + struct page **unmap_pages) { - struct gnttab_unmap_grant_ref unmap[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - struct page *unmap_pages[BLKIF_MAX_SEGMENTS_PER_REQUEST]; unsigned int i, invcount = 0; - int ret; for (i = 0; i < num; i++) { if (pages[i]->persistent_gnt != NULL) { @@ -674,21 +693,95 @@ static void xen_blkbk_unmap(struct xen_blkif *blkif, if (pages[i]->handle == BLKBACK_INVALID_HANDLE) continue; unmap_pages[invcount] = pages[i]->page; - gnttab_set_unmap_op(&unmap[invcount], vaddr(pages[i]->page), + gnttab_set_unmap_op(&unmap_ops[invcount], vaddr(pages[i]->page), GNTMAP_host_map, pages[i]->handle); pages[i]->handle = BLKBACK_INVALID_HANDLE; - if (++invcount == BLKIF_MAX_SEGMENTS_PER_REQUEST) { - ret = gnttab_unmap_refs(unmap, NULL, unmap_pages, - invcount); + invcount++; + } + + return invcount; +} + +static void xen_blkbk_unmap_and_respond_callback(int result, struct gntab_unmap_queue_data *data) +{ + struct pending_req* pending_req = (struct pending_req*) (data->data); + struct xen_blkif *blkif = pending_req->blkif; + + /* BUG_ON used to reproduce existing behaviour, + but is this the best way to deal with this? */ + BUG_ON(result); + + put_free_pages(blkif, data->pages, data->count); + make_response(blkif, pending_req->id, + pending_req->operation, pending_req->status); + free_req(blkif, pending_req); + /* + * Make sure the request is freed before releasing blkif, + * or there could be a race between free_req and the + * cleanup done in xen_blkif_free during shutdown. + * + * NB: The fact that we might try to wake up pending_free_wq + * before drain_complete (in case there's a drain going on) + * it's not a problem with our current implementation + * because we can assure there's no thread waiting on + * pending_free_wq if there's a drain going on, but it has + * to be taken into account if the current model is changed. + */ + if (atomic_dec_and_test(&blkif->inflight) && atomic_read(&blkif->drain)) { + complete(&blkif->drain_complete); + } + xen_blkif_put(blkif); +} + +static void xen_blkbk_unmap_and_respond(struct pending_req *req) +{ + struct gntab_unmap_queue_data* work = &req->gnttab_unmap_data; + struct xen_blkif *blkif = req->blkif; + struct grant_page **pages = req->segments; + unsigned int invcount; + + invcount = xen_blkbk_unmap_prepare(blkif, pages, req->nr_pages, + req->unmap, req->unmap_pages); + + work->data = req; + work->done = xen_blkbk_unmap_and_respond_callback; + work->unmap_ops = req->unmap; + work->kunmap_ops = NULL; + work->pages = req->unmap_pages; + work->count = invcount; + + gnttab_unmap_refs_async(&req->gnttab_unmap_data); +} + + +/* + * Unmap the grant references. + * + * This could accumulate ops up to the batch size to reduce the number + * of hypercalls, but since this is only used in error paths there's + * no real need. + */ +static void xen_blkbk_unmap(struct xen_blkif *blkif, + struct grant_page *pages[], + int num) +{ + struct gnttab_unmap_grant_ref unmap[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + struct page *unmap_pages[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + unsigned int invcount = 0; + int ret; + + while (num) { + unsigned int batch = min(num, BLKIF_MAX_SEGMENTS_PER_REQUEST); + + invcount = xen_blkbk_unmap_prepare(blkif, pages, batch, + unmap, unmap_pages); + if (invcount) { + ret = gnttab_unmap_refs(unmap, NULL, unmap_pages, invcount); BUG_ON(ret); put_free_pages(blkif, unmap_pages, invcount); - invcount = 0; } - } - if (invcount) { - ret = gnttab_unmap_refs(unmap, NULL, unmap_pages, invcount); - BUG_ON(ret); - put_free_pages(blkif, unmap_pages, invcount); + pages += batch; + num -= batch; } } @@ -982,32 +1075,8 @@ static void __end_block_io_op(struct pending_req *pending_req, int error) * the grant references associated with 'request' and provide * the proper response on the ring. */ - if (atomic_dec_and_test(&pending_req->pendcnt)) { - struct xen_blkif *blkif = pending_req->blkif; - - xen_blkbk_unmap(blkif, - pending_req->segments, - pending_req->nr_pages); - make_response(blkif, pending_req->id, - pending_req->operation, pending_req->status); - free_req(blkif, pending_req); - /* - * Make sure the request is freed before releasing blkif, - * or there could be a race between free_req and the - * cleanup done in xen_blkif_free during shutdown. - * - * NB: The fact that we might try to wake up pending_free_wq - * before drain_complete (in case there's a drain going on) - * it's not a problem with our current implementation - * because we can assure there's no thread waiting on - * pending_free_wq if there's a drain going on, but it has - * to be taken into account if the current model is changed. - */ - if (atomic_dec_and_test(&blkif->inflight) && atomic_read(&blkif->drain)) { - complete(&blkif->drain_complete); - } - xen_blkif_put(blkif); - } + if (atomic_dec_and_test(&pending_req->pendcnt)) + xen_blkbk_unmap_and_respond(pending_req); } /* diff --git a/drivers/block/xen-blkback/common.h b/drivers/block/xen-blkback/common.h index f65b807e3236..cc90a840e616 100644 --- a/drivers/block/xen-blkback/common.h +++ b/drivers/block/xen-blkback/common.h @@ -350,6 +350,9 @@ struct pending_req { struct grant_page *indirect_pages[MAX_INDIRECT_PAGES]; struct seg_buf seg[MAX_INDIRECT_SEGMENTS]; struct bio *biolist[MAX_INDIRECT_SEGMENTS]; + struct gnttab_unmap_grant_ref unmap[MAX_INDIRECT_SEGMENTS]; + struct page *unmap_pages[MAX_INDIRECT_SEGMENTS]; + struct gntab_unmap_queue_data gnttab_unmap_data; }; diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 1ee27ac18de0..de4c8499cbac 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -108,6 +108,7 @@ static const struct usb_device_id ath3k_table[] = { { USB_DEVICE(0x13d3, 0x3393) }, { USB_DEVICE(0x13d3, 0x3402) }, { USB_DEVICE(0x13d3, 0x3408) }, + { USB_DEVICE(0x13d3, 0x3423) }, { USB_DEVICE(0x13d3, 0x3432) }, /* Atheros AR5BBU12 with sflash firmware */ @@ -162,6 +163,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = { { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU22 with sflash firmware */ @@ -174,6 +176,8 @@ static const struct usb_device_id ath3k_blist_tbl[] = { #define USB_REQ_DFU_DNLOAD 1 #define BULK_SIZE 4096 #define FW_HDR_SIZE 20 +#define TIMEGAP_USEC_MIN 50 +#define TIMEGAP_USEC_MAX 100 static int ath3k_load_firmware(struct usb_device *udev, const struct firmware *firmware) @@ -205,6 +209,9 @@ static int ath3k_load_firmware(struct usb_device *udev, pipe = usb_sndbulkpipe(udev, 0x02); while (count) { + /* workaround the compatibility issue with xHCI controller*/ + usleep_range(TIMEGAP_USEC_MIN, TIMEGAP_USEC_MAX); + size = min_t(uint, count, BULK_SIZE); memcpy(send_buf, firmware->data + sent, size); @@ -302,6 +309,9 @@ static int ath3k_load_fwfile(struct usb_device *udev, pipe = usb_sndbulkpipe(udev, 0x02); while (count) { + /* workaround the compatibility issue with xHCI controller*/ + usleep_range(TIMEGAP_USEC_MIN, TIMEGAP_USEC_MAX); + size = min_t(uint, count, BULK_SIZE); memcpy(send_buf, firmware->data + sent, size); diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index b2e7e94a6771..fcfb72e9e0ee 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -696,6 +696,8 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i hdev->flush = bfusb_flush; hdev->send = bfusb_send_frame; + set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks); + if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); hci_free_dev(hdev); diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h index 330f8f84928d..e75f8ee2512c 100644 --- a/drivers/bluetooth/btmrvl_drv.h +++ b/drivers/bluetooth/btmrvl_drv.h @@ -28,9 +28,9 @@ #define BTM_UPLD_SIZE 2312 /* Time to wait until Host Sleep state change in millisecond */ -#define WAIT_UNTIL_HS_STATE_CHANGED 5000 +#define WAIT_UNTIL_HS_STATE_CHANGED msecs_to_jiffies(5000) /* Time to wait for command response in millisecond */ -#define WAIT_UNTIL_CMD_RESP 5000 +#define WAIT_UNTIL_CMD_RESP msecs_to_jiffies(5000) enum rdwr_status { RDWR_STATUS_SUCCESS = 0, @@ -104,6 +104,7 @@ struct btmrvl_private { #ifdef CONFIG_DEBUG_FS void *debugfs_data; #endif + bool surprise_removed; }; #define MRVL_VENDOR_PKT 0xFE diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index 30939c993d94..413597789c61 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -178,6 +178,11 @@ static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode, struct sk_buff *skb; struct hci_command_hdr *hdr; + if (priv->surprise_removed) { + BT_ERR("Card is removed"); + return -EFAULT; + } + skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_ATOMIC); if (skb == NULL) { BT_ERR("No free skb"); @@ -202,10 +207,14 @@ static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode, wake_up_interruptible(&priv->main_thread.wait_q); if (!wait_event_interruptible_timeout(priv->adapter->cmd_wait_q, - priv->adapter->cmd_complete, - msecs_to_jiffies(WAIT_UNTIL_CMD_RESP))) + priv->adapter->cmd_complete || + priv->surprise_removed, + WAIT_UNTIL_CMD_RESP)) return -ETIMEDOUT; + if (priv->surprise_removed) + return -EFAULT; + return 0; } @@ -287,9 +296,10 @@ int btmrvl_enable_hs(struct btmrvl_private *priv) } ret = wait_event_interruptible_timeout(adapter->event_hs_wait_q, - adapter->hs_state, - msecs_to_jiffies(WAIT_UNTIL_HS_STATE_CHANGED)); - if (ret < 0) { + adapter->hs_state || + priv->surprise_removed, + WAIT_UNTIL_HS_STATE_CHANGED); + if (ret < 0 || priv->surprise_removed) { BT_ERR("event_hs_wait_q terminated (%d): %d,%d,%d", ret, adapter->hs_state, adapter->ps_state, adapter->wakeup_tries); @@ -538,8 +548,11 @@ static int btmrvl_check_device_tree(struct btmrvl_private *priv) static int btmrvl_setup(struct hci_dev *hdev) { struct btmrvl_private *priv = hci_get_drvdata(hdev); + int ret; - btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ); + ret = btmrvl_send_module_cfg_cmd(priv, MODULE_BRINGUP_REQ); + if (ret) + return ret; priv->btmrvl_dev.gpio_gap = 0xffff; @@ -597,7 +610,7 @@ static int btmrvl_service_main_thread(void *data) add_wait_queue(&thread->wait_q, &wait); set_current_state(TASK_INTERRUPTIBLE); - if (kthread_should_stop()) { + if (kthread_should_stop() || priv->surprise_removed) { BT_DBG("main_thread: break from main thread"); break; } @@ -616,6 +629,11 @@ static int btmrvl_service_main_thread(void *data) BT_DBG("main_thread woke up"); + if (kthread_should_stop() || priv->surprise_removed) { + BT_DBG("main_thread: break from main thread"); + break; + } + spin_lock_irqsave(&priv->driver_lock, flags); if (adapter->int_count) { adapter->int_count = 0; diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 0057c0b7a776..01d6da577eeb 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -573,7 +573,7 @@ static int btmrvl_sdio_download_fw_w_helper(struct btmrvl_sdio_card *card) offset += txlen; } while (true); - BT_DBG("FW download over, size %d bytes", offset); + BT_INFO("FW download over, size %d bytes", offset); ret = 0; @@ -798,6 +798,9 @@ static void btmrvl_sdio_interrupt(struct sdio_func *func) priv = card->priv; + if (priv->surprise_removed) + return; + if (card->reg->int_read_to_clear) ret = btmrvl_sdio_read_to_clear(card, &ireg); else @@ -1466,6 +1469,7 @@ static void btmrvl_sdio_remove(struct sdio_func *func) btmrvl_sdio_disable_host_int(card); } BT_DBG("unregester dev"); + card->priv->surprise_removed = true; btmrvl_sdio_unregister_dev(card); btmrvl_remove_card(card->priv); } diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 19cf2cf22e87..b87688881143 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -28,7 +28,7 @@ #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> -#define VERSION "0.6" +#define VERSION "0.7" static bool disable_scofix; static bool force_scofix; @@ -49,11 +49,17 @@ static struct usb_driver btusb_driver; #define BTUSB_INTEL_BOOT 0x200 #define BTUSB_BCM_PATCHRAM 0x400 #define BTUSB_MARVELL 0x800 +#define BTUSB_SWAVE 0x1000 +#define BTUSB_INTEL_NEW 0x2000 +#define BTUSB_AMP 0x4000 static const struct usb_device_id btusb_table[] = { /* Generic Bluetooth USB device */ { USB_DEVICE_INFO(0xe0, 0x01, 0x01) }, + /* Generic Bluetooth AMP device */ + { USB_DEVICE_INFO(0xe0, 0x01, 0x04), .driver_info = BTUSB_AMP }, + /* Apple-specific (Broadcom) devices */ { USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01) }, @@ -85,7 +91,7 @@ static const struct usb_device_id btusb_table[] = { { USB_DEVICE(0x05ac, 0x8281) }, /* AVM BlueFRITZ! USB v2.0 */ - { USB_DEVICE(0x057c, 0x3800) }, + { USB_DEVICE(0x057c, 0x3800), .driver_info = BTUSB_SWAVE }, /* Bluetooth Ultraport Module from IBM */ { USB_DEVICE(0x04bf, 0x030a) }, @@ -109,16 +115,24 @@ static const struct usb_device_id btusb_table[] = { { USB_DEVICE(0x13d3, 0x3404), .driver_info = BTUSB_BCM_PATCHRAM }, + /* Broadcom BCM20702B0 (Dynex/Insignia) */ + { USB_DEVICE(0x19ff, 0x0239), .driver_info = BTUSB_BCM_PATCHRAM }, + /* Foxconn - Hon Hai */ { USB_VENDOR_AND_INTERFACE_INFO(0x0489, 0xff, 0x01, 0x01), .driver_info = BTUSB_BCM_PATCHRAM }, + /* Lite-On Technology - Broadcom based */ + { USB_VENDOR_AND_INTERFACE_INFO(0x04ca, 0xff, 0x01, 0x01), + .driver_info = BTUSB_BCM_PATCHRAM }, + /* Broadcom devices with vendor specific id */ { USB_VENDOR_AND_INTERFACE_INFO(0x0a5c, 0xff, 0x01, 0x01), .driver_info = BTUSB_BCM_PATCHRAM }, /* ASUSTek Computer - Broadcom based */ - { USB_VENDOR_AND_INTERFACE_INFO(0x0b05, 0xff, 0x01, 0x01) }, + { USB_VENDOR_AND_INTERFACE_INFO(0x0b05, 0xff, 0x01, 0x01), + .driver_info = BTUSB_BCM_PATCHRAM }, /* Belkin F8065bf - Broadcom based */ { USB_VENDOR_AND_INTERFACE_INFO(0x050d, 0xff, 0x01, 0x01) }, @@ -188,6 +202,7 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU12 with sflash firmware */ @@ -237,6 +252,9 @@ static const struct usb_device_id blacklist_table[] = { /* CONWISE Technology based adapters with buggy SCO support */ { USB_DEVICE(0x0e5e, 0x6622), .driver_info = BTUSB_BROKEN_ISOC }, + /* Roper Class 1 Bluetooth Dongle (Silicon Wave based) */ + { USB_DEVICE(0x1300, 0x0001), .driver_info = BTUSB_SWAVE }, + /* Digianswer devices */ { USB_DEVICE(0x08fd, 0x0001), .driver_info = BTUSB_DIGIANSWER }, { USB_DEVICE(0x08fd, 0x0002), .driver_info = BTUSB_IGNORE }, @@ -249,13 +267,18 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x16d3, 0x0002), .driver_info = BTUSB_SNIFFER | BTUSB_BROKEN_ISOC }, - /* Intel Bluetooth device */ + /* Marvell Bluetooth devices */ + { USB_DEVICE(0x1286, 0x2044), .driver_info = BTUSB_MARVELL }, + { USB_DEVICE(0x1286, 0x2046), .driver_info = BTUSB_MARVELL }, + + /* Intel Bluetooth devices */ { USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL }, { USB_DEVICE(0x8087, 0x0a2a), .driver_info = BTUSB_INTEL }, + { USB_DEVICE(0x8087, 0x0a2b), .driver_info = BTUSB_INTEL_NEW }, - /* Marvell device */ - { USB_DEVICE(0x1286, 0x2044), .driver_info = BTUSB_MARVELL }, - { USB_DEVICE(0x1286, 0x2046), .driver_info = BTUSB_MARVELL }, + /* Other Intel Bluetooth devices */ + { USB_VENDOR_AND_INTERFACE_INFO(0x8087, 0xe0, 0x01, 0x01), + .driver_info = BTUSB_IGNORE }, { } /* Terminating entry */ }; @@ -267,6 +290,11 @@ static const struct usb_device_id blacklist_table[] = { #define BTUSB_ISOC_RUNNING 2 #define BTUSB_SUSPENDING 3 #define BTUSB_DID_ISO_RESUME 4 +#define BTUSB_BOOTLOADER 5 +#define BTUSB_DOWNLOADING 6 +#define BTUSB_FIRMWARE_LOADED 7 +#define BTUSB_FIRMWARE_FAILED 8 +#define BTUSB_BOOTING 9 struct btusb_data { struct hci_dev *hdev; @@ -300,14 +328,26 @@ struct btusb_data { struct usb_endpoint_descriptor *isoc_rx_ep; __u8 cmdreq_type; + __u8 cmdreq; unsigned int sco_num; int isoc_altsetting; int suspend_count; + int (*recv_event)(struct hci_dev *hdev, struct sk_buff *skb); int (*recv_bulk)(struct btusb_data *data, void *buffer, int count); }; +static int btusb_wait_on_bit_timeout(void *word, int bit, unsigned long timeout, + unsigned mode) +{ + might_sleep(); + if (!test_bit(bit, word)) + return 0; + return out_of_line_wait_on_bit_timeout(word, bit, bit_wait_timeout, + mode, timeout); +} + static inline void btusb_free_frags(struct btusb_data *data) { unsigned long flags; @@ -370,7 +410,7 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count) if (bt_cb(skb)->expect == 0) { /* Complete frame */ - hci_recv_frame(data->hdev, skb); + data->recv_event(data->hdev, skb); skb = NULL; } } @@ -952,7 +992,7 @@ static struct urb *alloc_ctrl_urb(struct hci_dev *hdev, struct sk_buff *skb) } dr->bRequestType = data->cmdreq_type; - dr->bRequest = 0; + dr->bRequest = data->cmdreq; dr->wIndex = 0; dr->wValue = 0; dr->wLength = __cpu_to_le16(skb->len); @@ -1290,6 +1330,26 @@ struct intel_version { u8 fw_patch_num; } __packed; +struct intel_boot_params { + __u8 status; + __u8 otp_format; + __u8 otp_content; + __u8 otp_patch; + __le16 dev_revid; + __u8 secure_boot; + __u8 key_from_hdr; + __u8 key_type; + __u8 otp_lock; + __u8 api_lock; + __u8 debug_lock; + bdaddr_t otp_bdaddr; + __u8 min_fw_build_nn; + __u8 min_fw_build_cw; + __u8 min_fw_build_yy; + __u8 limited_cce; + __u8 unlocked_state; +} __packed; + static const struct firmware *btusb_setup_intel_get_fw(struct hci_dev *hdev, struct intel_version *ver) { @@ -1698,6 +1758,562 @@ exit_mfg_deactivate: return 0; } +static int inject_cmd_complete(struct hci_dev *hdev, __u16 opcode) +{ + struct sk_buff *skb; + struct hci_event_hdr *hdr; + struct hci_ev_cmd_complete *evt; + + skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*evt) + 1, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + hdr = (struct hci_event_hdr *)skb_put(skb, sizeof(*hdr)); + hdr->evt = HCI_EV_CMD_COMPLETE; + hdr->plen = sizeof(*evt) + 1; + + evt = (struct hci_ev_cmd_complete *)skb_put(skb, sizeof(*evt)); + evt->ncmd = 0x01; + evt->opcode = cpu_to_le16(opcode); + + *skb_put(skb, 1) = 0x00; + + bt_cb(skb)->pkt_type = HCI_EVENT_PKT; + + return hci_recv_frame(hdev, skb); +} + +static int btusb_recv_bulk_intel(struct btusb_data *data, void *buffer, + int count) +{ + /* When the device is in bootloader mode, then it can send + * events via the bulk endpoint. These events are treated the + * same way as the ones received from the interrupt endpoint. + */ + if (test_bit(BTUSB_BOOTLOADER, &data->flags)) + return btusb_recv_intr(data, buffer, count); + + return btusb_recv_bulk(data, buffer, count); +} + +static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btusb_data *data = hci_get_drvdata(hdev); + + if (test_bit(BTUSB_BOOTLOADER, &data->flags)) { + struct hci_event_hdr *hdr = (void *)skb->data; + + /* When the firmware loading completes the device sends + * out a vendor specific event indicating the result of + * the firmware loading. + */ + if (skb->len == 7 && hdr->evt == 0xff && hdr->plen == 0x05 && + skb->data[2] == 0x06) { + if (skb->data[3] != 0x00) + test_bit(BTUSB_FIRMWARE_FAILED, &data->flags); + + if (test_and_clear_bit(BTUSB_DOWNLOADING, + &data->flags) && + test_bit(BTUSB_FIRMWARE_LOADED, &data->flags)) { + smp_mb__after_atomic(); + wake_up_bit(&data->flags, BTUSB_DOWNLOADING); + } + } + + /* When switching to the operational firmware the device + * sends a vendor specific event indicating that the bootup + * completed. + */ + if (skb->len == 9 && hdr->evt == 0xff && hdr->plen == 0x07 && + skb->data[2] == 0x02) { + if (test_and_clear_bit(BTUSB_BOOTING, &data->flags)) { + smp_mb__after_atomic(); + wake_up_bit(&data->flags, BTUSB_BOOTING); + } + } + } + + return hci_recv_frame(hdev, skb); +} + +static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct btusb_data *data = hci_get_drvdata(hdev); + struct urb *urb; + + 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)) { + struct hci_command_hdr *cmd = (void *)skb->data; + __u16 opcode = le16_to_cpu(cmd->opcode); + + /* When in bootloader mode and the command 0xfc09 + * is received, it needs to be send down the + * bulk endpoint. So allocate a bulk URB instead. + */ + if (opcode == 0xfc09) + urb = alloc_bulk_urb(hdev, skb); + else + urb = alloc_ctrl_urb(hdev, skb); + + /* When the 0xfc01 command is issued to boot into + * the operational firmware, it will actually not + * send a command complete event. To keep the flow + * control working inject that event here. + */ + if (opcode == 0xfc01) + inject_cmd_complete(hdev, opcode); + } else { + urb = alloc_ctrl_urb(hdev, skb); + } + if (IS_ERR(urb)) + return PTR_ERR(urb); + + hdev->stat.cmd_tx++; + return submit_or_queue_tx_urb(hdev, urb); + + case HCI_ACLDATA_PKT: + urb = alloc_bulk_urb(hdev, skb); + if (IS_ERR(urb)) + return PTR_ERR(urb); + + hdev->stat.acl_tx++; + return submit_or_queue_tx_urb(hdev, urb); + + case HCI_SCODATA_PKT: + if (hci_conn_num(hdev, SCO_LINK) < 1) + return -ENODEV; + + urb = alloc_isoc_urb(hdev, skb); + if (IS_ERR(urb)) + return PTR_ERR(urb); + + hdev->stat.sco_tx++; + return submit_tx_urb(hdev, urb); + } + + return -EILSEQ; +} + +static int btusb_intel_secure_send(struct hci_dev *hdev, u8 fragment_type, + u32 plen, const void *param) +{ + while (plen > 0) { + struct sk_buff *skb; + u8 cmd_param[253], fragment_len = (plen > 252) ? 252 : plen; + + cmd_param[0] = fragment_type; + memcpy(cmd_param + 1, param, fragment_len); + + skb = __hci_cmd_sync(hdev, 0xfc09, fragment_len + 1, + cmd_param, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + kfree_skb(skb); + + plen -= fragment_len; + param += fragment_len; + } + + return 0; +} + +static void btusb_intel_version_info(struct hci_dev *hdev, + struct intel_version *ver) +{ + const char *variant; + + switch (ver->fw_variant) { + case 0x06: + variant = "Bootloader"; + break; + case 0x23: + variant = "Firmware"; + break; + default: + return; + } + + BT_INFO("%s: %s revision %u.%u build %u week %u %u", hdev->name, + variant, ver->fw_revision >> 4, ver->fw_revision & 0x0f, + ver->fw_build_num, ver->fw_build_ww, 2000 + ver->fw_build_yy); +} + +static int btusb_setup_intel_new(struct hci_dev *hdev) +{ + static const u8 reset_param[] = { 0x00, 0x01, 0x00, 0x01, + 0x00, 0x08, 0x04, 0x00 }; + struct btusb_data *data = hci_get_drvdata(hdev); + struct sk_buff *skb; + struct intel_version *ver; + struct intel_boot_params *params; + const struct firmware *fw; + const u8 *fw_ptr; + char fwname[64]; + ktime_t calltime, delta, rettime; + unsigned long long duration; + int err; + + BT_DBG("%s", hdev->name); + + calltime = ktime_get(); + + /* Read the Intel version information to determine if the device + * is in bootloader mode or if it already has operational firmware + * loaded. + */ + 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)); + return PTR_ERR(skb); + } + + if (skb->len != sizeof(*ver)) { + BT_ERR("%s: Intel version event size mismatch", hdev->name); + 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); + err = -bt_to_errno(ver->status); + kfree_skb(skb); + return err; + } + + /* The hardware platform number has a fixed value of 0x37 and + * 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); + kfree_skb(skb); + return -EINVAL; + } + + /* At the moment only the hardware variant iBT 3.0 (LnP/SfP) is + * supported by this firmware loading method. This check has been + * put in place to ensure correct forward compatibility options + * when newer hardware variants come along. + */ + if (ver->hw_variant != 0x0b) { + BT_ERR("%s: Unsupported Intel hardware variant (%u)", + hdev->name, ver->hw_variant); + kfree_skb(skb); + return -EINVAL; + } + + btusb_intel_version_info(hdev, ver); + + /* The firmware variant determines if the device is in bootloader + * mode or is running operational firmware. The value 0x06 identifies + * the bootloader and the value 0x23 identifies the operational + * firmware. + * + * When the operational firmware is already present, then only + * the check for valid Bluetooth device address is needed. This + * determines if the device will be added as configured or + * unconfigured controller. + * + * It is not possible to use the Secure Boot Parameters in this + * case since that command is only available in bootloader mode. + */ + if (ver->fw_variant == 0x23) { + kfree_skb(skb); + clear_bit(BTUSB_BOOTLOADER, &data->flags); + btusb_check_bdaddr_intel(hdev); + return 0; + } + + /* If the device is not in bootloader mode, then the only possible + * 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); + kfree_skb(skb); + return -ENODEV; + } + + kfree_skb(skb); + + /* Read the secure boot parameters to identify the operating + * details of the bootloader. + */ + 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)); + return PTR_ERR(skb); + } + + if (skb->len != sizeof(*params)) { + BT_ERR("%s: Intel boot parameters size mismatch", hdev->name); + 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); + 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_INFO("%s: Secure boot is %s", hdev->name, + params->secure_boot ? "enabled" : "disabled"); + + BT_INFO("%s: Minimum firmware build %u week %u %u", hdev->name, + params->min_fw_build_nn, params->min_fw_build_cw, + 2000 + params->min_fw_build_yy); + + /* It is required that every single firmware fragment is acknowledged + * with a command complete event. If the boot parameters indicate + * 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); + kfree_skb(skb); + return -EINVAL; + } + + /* If the OTP has no valid Bluetooth device address, then there will + * 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); + set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); + } + + /* With this Intel bootloader only the hardware variant and device + * revision information are used to select the right firmware. + * + * Currently this bootloader support is limited to hardware variant + * iBT 3.0 (LnP/SfP) which is identified by the value 11 (0x0b). + */ + snprintf(fwname, sizeof(fwname), "intel/ibt-11-%u.sfi", + le16_to_cpu(params->dev_revid)); + + err = request_firmware(&fw, fwname, &hdev->dev); + if (err < 0) { + BT_ERR("%s: Failed to load Intel firmware file (%d)", + hdev->name, err); + kfree_skb(skb); + return err; + } + + BT_INFO("%s: Found device firmware: %s", hdev->name, fwname); + + kfree_skb(skb); + + if (fw->size < 644) { + BT_ERR("%s: Invalid size of firmware file (%zu)", + hdev->name, fw->size); + err = -EBADF; + goto done; + } + + set_bit(BTUSB_DOWNLOADING, &data->flags); + + /* Start the firmware download transaction with the Init fragment + * represented by the 128 bytes of CSS header. + */ + err = btusb_intel_secure_send(hdev, 0x00, 128, fw->data); + if (err < 0) { + BT_ERR("%s: Failed to send firmware header (%d)", + hdev->name, err); + goto done; + } + + /* Send the 256 bytes of public key information from the firmware + * as the PKey fragment. + */ + err = btusb_intel_secure_send(hdev, 0x03, 256, fw->data + 128); + if (err < 0) { + BT_ERR("%s: Failed to send firmware public key (%d)", + hdev->name, err); + goto done; + } + + /* Send the 256 bytes of signature information from the firmware + * as the Sign fragment. + */ + err = btusb_intel_secure_send(hdev, 0x02, 256, fw->data + 388); + if (err < 0) { + BT_ERR("%s: Failed to send firmware signature (%d)", + hdev->name, err); + goto done; + } + + fw_ptr = fw->data + 644; + + while (fw_ptr - fw->data < fw->size) { + struct hci_command_hdr *cmd = (void *)fw_ptr; + u8 cmd_len; + + cmd_len = sizeof(*cmd) + cmd->plen; + + /* Send each command from the firmware data buffer as + * a single Data fragment. + */ + err = btusb_intel_secure_send(hdev, 0x01, cmd_len, fw_ptr); + if (err < 0) { + BT_ERR("%s: Failed to send firmware data (%d)", + hdev->name, err); + goto done; + } + + fw_ptr += cmd_len; + } + + set_bit(BTUSB_FIRMWARE_LOADED, &data->flags); + + BT_INFO("%s: Waiting for firmware download to complete", hdev->name); + + /* Before switching the device into operational mode and with that + * booting the loaded firmware, wait for the bootloader notification + * that all fragments have been successfully received. + * + * When the event processing receives the notification, then the + * BTUSB_DOWNLOADING flag will be cleared. + * + * The firmware loading should not take longer than 5 seconds + * and thus just timeout if that happens and fail the setup + * of this device. + */ + err = btusb_wait_on_bit_timeout(&data->flags, BTUSB_DOWNLOADING, + msecs_to_jiffies(5000), + TASK_INTERRUPTIBLE); + if (err == 1) { + BT_ERR("%s: Firmware loading interrupted", hdev->name); + err = -EINTR; + goto done; + } + + if (err) { + BT_ERR("%s: Firmware loading timeout", hdev->name); + err = -ETIMEDOUT; + goto done; + } + + if (test_bit(BTUSB_FIRMWARE_FAILED, &data->flags)) { + BT_ERR("%s: Firmware loading failed", hdev->name); + err = -ENOEXEC; + goto done; + } + + rettime = ktime_get(); + 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); + +done: + release_firmware(fw); + + if (err < 0) + return err; + + calltime = ktime_get(); + + set_bit(BTUSB_BOOTING, &data->flags); + + skb = __hci_cmd_sync(hdev, 0xfc01, sizeof(reset_param), reset_param, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + kfree_skb(skb); + + /* The bootloader will not indicate when the device is ready. This + * is done by the operational firmware sending bootup notification. + * + * Booting into operational firmware should not take longer than + * 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); + + err = btusb_wait_on_bit_timeout(&data->flags, BTUSB_BOOTING, + msecs_to_jiffies(1000), + TASK_INTERRUPTIBLE); + + if (err == 1) { + BT_ERR("%s: Device boot interrupted", hdev->name); + return -EINTR; + } + + if (err) { + BT_ERR("%s: Device boot timeout", hdev->name); + return -ETIMEDOUT; + } + + rettime = ktime_get(); + 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); + + clear_bit(BTUSB_BOOTLOADER, &data->flags); + + return 0; +} + +static void btusb_hw_error_intel(struct hci_dev *hdev, u8 code) +{ + struct sk_buff *skb; + u8 type = 0x00; + + BT_ERR("%s: Hardware error 0x%2.2x", hdev->name, code); + + skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + BT_ERR("%s: Reset after hardware error failed (%ld)", + hdev->name, PTR_ERR(skb)); + return; + } + kfree_skb(skb); + + skb = __hci_cmd_sync(hdev, 0xfc22, 1, &type, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + BT_ERR("%s: Retrieving Intel exception info failed (%ld)", + hdev->name, PTR_ERR(skb)); + return; + } + + if (skb->len != 13) { + BT_ERR("%s: Exception info size mismatch", hdev->name); + kfree_skb(skb); + return; + } + + if (skb->data[0] != 0x00) { + BT_ERR("%s: Exception info command failure (%02x)", + hdev->name, skb->data[0]); + kfree_skb(skb); + return; + } + + BT_ERR("%s: Exception info %s", hdev->name, (char *)(skb->data + 1)); + + kfree_skb(skb); +} + static int btusb_set_bdaddr_intel(struct hci_dev *hdev, const bdaddr_t *bdaddr) { struct sk_buff *skb; @@ -1943,6 +2559,31 @@ static int btusb_set_bdaddr_bcm(struct hci_dev *hdev, const bdaddr_t *bdaddr) return 0; } +static int btusb_set_bdaddr_ath3012(struct hci_dev *hdev, + const bdaddr_t *bdaddr) +{ + struct sk_buff *skb; + u8 buf[10]; + long ret; + + buf[0] = 0x01; + buf[1] = 0x01; + buf[2] = 0x00; + buf[3] = sizeof(bdaddr_t); + memcpy(buf + 4, bdaddr, sizeof(bdaddr_t)); + + skb = __hci_cmd_sync(hdev, 0xfc0b, sizeof(buf), buf, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + BT_ERR("%s: Change address command failed (%ld)", + hdev->name, ret); + return ret; + } + kfree_skb(skb); + + return 0; +} + static int btusb_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -2003,7 +2644,13 @@ static int btusb_probe(struct usb_interface *intf, if (!data->intr_ep || !data->bulk_tx_ep || !data->bulk_rx_ep) return -ENODEV; - data->cmdreq_type = USB_TYPE_CLASS; + if (id->driver_info & BTUSB_AMP) { + data->cmdreq_type = USB_TYPE_CLASS | 0x01; + data->cmdreq = 0x2b; + } else { + data->cmdreq_type = USB_TYPE_CLASS; + data->cmdreq = 0x00; + } data->udev = interface_to_usbdev(intf); data->intf = intf; @@ -2019,7 +2666,14 @@ static int btusb_probe(struct usb_interface *intf, init_usb_anchor(&data->isoc_anchor); spin_lock_init(&data->rxlock); - data->recv_bulk = btusb_recv_bulk; + if (id->driver_info & BTUSB_INTEL_NEW) { + data->recv_event = btusb_recv_event_intel; + data->recv_bulk = btusb_recv_bulk_intel; + set_bit(BTUSB_BOOTLOADER, &data->flags); + } else { + data->recv_event = hci_recv_frame; + data->recv_bulk = btusb_recv_bulk; + } hdev = hci_alloc_dev(); if (!hdev) @@ -2028,6 +2682,11 @@ static int btusb_probe(struct usb_interface *intf, hdev->bus = HCI_USB; hci_set_drvdata(hdev, data); + if (id->driver_info & BTUSB_AMP) + hdev->dev_type = HCI_AMP; + else + hdev->dev_type = HCI_BREDR; + data->hdev = hdev; SET_HCIDEV_DEV(hdev, &intf->dev); @@ -2050,16 +2709,40 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_INTEL) { hdev->setup = btusb_setup_intel; hdev->set_bdaddr = btusb_set_bdaddr_intel; + set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); + } + + if (id->driver_info & BTUSB_INTEL_NEW) { + hdev->send = btusb_send_frame_intel; + hdev->setup = btusb_setup_intel_new; + hdev->hw_error = btusb_hw_error_intel; + hdev->set_bdaddr = btusb_set_bdaddr_intel; + set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); } if (id->driver_info & BTUSB_MARVELL) hdev->set_bdaddr = btusb_set_bdaddr_marvell; + if (id->driver_info & BTUSB_SWAVE) { + set_bit(HCI_QUIRK_FIXUP_INQUIRY_MODE, &hdev->quirks); + set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks); + } + if (id->driver_info & BTUSB_INTEL_BOOT) set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); - /* Interface numbers are hardcoded in the specification */ - data->isoc = usb_ifnum_to_if(data->udev, 1); + if (id->driver_info & BTUSB_ATH3012) { + hdev->set_bdaddr = btusb_set_bdaddr_ath3012; + set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); + } + + if (id->driver_info & BTUSB_AMP) { + /* AMP controllers do not support SCO packets */ + data->isoc = NULL; + } else { + /* Interface numbers are hardcoded in the specification */ + data->isoc = usb_ifnum_to_if(data->udev, 1); + } if (!reset) set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); @@ -2153,7 +2836,6 @@ static void btusb_disconnect(struct usb_interface *intf) else if (data->isoc) usb_driver_release_interface(&btusb_driver, data->isoc); - btusb_free_frags(data); hci_free_dev(hdev); } diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c index 860da40b78ef..0ce5e2d65a06 100644 --- a/drivers/bus/arm-cci.c +++ b/drivers/bus/arm-cci.c @@ -1312,6 +1312,9 @@ static int cci_probe(void) if (!np) return -ENODEV; + if (!of_device_is_available(np)) + return -ENODEV; + cci_config = of_match_node(arm_cci_matches, np)->data; if (!cci_config) return -ENODEV; diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index eb7682dc123b..81bf297f1034 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c @@ -210,12 +210,25 @@ static void mvebu_mbus_disable_window(struct mvebu_mbus_state *mbus, } /* Checks whether the given window number is available */ + +/* On Armada XP, 375 and 38x the MBus window 13 has the remap + * capability, like windows 0 to 7. However, the mvebu-mbus driver + * isn't currently taking into account this special case, which means + * that when window 13 is actually used, the remap registers are left + * to 0, making the device using this MBus window unavailable. The + * quick fix for stable is to not use window 13. A follow up patch + * will correctly handle this window. +*/ static int mvebu_mbus_window_is_free(struct mvebu_mbus_state *mbus, const int win) { void __iomem *addr = mbus->mbuswins_base + mbus->soc->win_cfg_offset(win); u32 ctrl = readl(addr + WIN_CTRL_OFF); + + if (win == 13) + return false; + return !(ctrl & WIN_CTRL_ENABLE); } diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c index d5d4cd82b9f7..5c0baa9ffc64 100644 --- a/drivers/char/hpet.c +++ b/drivers/char/hpet.c @@ -976,8 +976,8 @@ static acpi_status hpet_resources(struct acpi_resource *res, void *data) status = acpi_resource_to_address64(res, &addr); if (ACPI_SUCCESS(status)) { - hdp->hd_phys_address = addr.minimum; - hdp->hd_address = ioremap(addr.minimum, addr.address_length); + hdp->hd_phys_address = addr.address.minimum; + hdp->hd_address = ioremap(addr.address.minimum, addr.address.address_length); if (hpet_is_known(hdp)) { iounmap(hdp->hd_address); diff --git a/drivers/char/ipmi/ipmi_ssif.c b/drivers/char/ipmi/ipmi_ssif.c index fd5a5e85d7dc..982b96323f82 100644 --- a/drivers/char/ipmi/ipmi_ssif.c +++ b/drivers/char/ipmi/ipmi_ssif.c @@ -969,7 +969,8 @@ static void sender(void *send_info, do_gettimeofday(&t); pr_info("**Enqueue %02x %02x: %ld.%6.6ld\n", - msg->data[0], msg->data[1], t.tv_sec, t.tv_usec); + msg->data[0], msg->data[1], + (long) t.tv_sec, (long) t.tv_usec); } } diff --git a/drivers/char/random.c b/drivers/char/random.c index 04645c09fe5e..9cd6968e2f92 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -569,19 +569,19 @@ static void fast_mix(struct fast_pool *f) __u32 c = f->pool[2], d = f->pool[3]; a += b; c += d; - b = rol32(a, 6); d = rol32(c, 27); + b = rol32(b, 6); d = rol32(d, 27); d ^= a; b ^= c; a += b; c += d; - b = rol32(a, 16); d = rol32(c, 14); + b = rol32(b, 16); d = rol32(d, 14); d ^= a; b ^= c; a += b; c += d; - b = rol32(a, 6); d = rol32(c, 27); + b = rol32(b, 6); d = rol32(d, 27); d ^= a; b ^= c; a += b; c += d; - b = rol32(a, 16); d = rol32(c, 14); + b = rol32(b, 16); d = rol32(d, 14); d ^= a; b ^= c; f->pool[0] = a; f->pool[1] = b; diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 3f44f292d066..91f86131bb7a 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -13,6 +13,7 @@ config COMMON_CLK bool select HAVE_CLK_PREPARE select CLKDEV_LOOKUP + select SRCU ---help--- The common clock framework is a single definition of struct clk, useful across many platforms, as well as an diff --git a/drivers/clk/at91/clk-slow.c b/drivers/clk/at91/clk-slow.c index 32f7c1b36204..2f13bd5246b5 100644 --- a/drivers/clk/at91/clk-slow.c +++ b/drivers/clk/at91/clk-slow.c @@ -70,6 +70,7 @@ struct clk_sam9x5_slow { #define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw) +static struct clk *slow_clk; static int clk_slow_osc_prepare(struct clk_hw *hw) { @@ -357,6 +358,8 @@ at91_clk_register_sam9x5_slow(void __iomem *sckcr, clk = clk_register(NULL, &slowck->hw); if (IS_ERR(clk)) kfree(slowck); + else + slow_clk = clk; return clk; } @@ -433,6 +436,8 @@ at91_clk_register_sam9260_slow(struct at91_pmc *pmc, clk = clk_register(NULL, &slowck->hw); if (IS_ERR(clk)) kfree(slowck); + else + slow_clk = clk; return clk; } @@ -465,3 +470,25 @@ void __init of_at91sam9260_clk_slow_setup(struct device_node *np, of_clk_add_provider(np, of_clk_src_simple_get, clk); } + +/* + * FIXME: All slow clk users are not properly claiming it (get + prepare + + * enable) before using it. + * If all users properly claiming this clock decide that they don't need it + * anymore (or are removed), it is disabled while faulty users are still + * requiring it, and the system hangs. + * Prevent this clock from being disabled until all users are properly + * requesting it. + * Once this is done we should remove this function and the slow_clk variable. + */ +static int __init of_at91_clk_slow_retain(void) +{ + if (!slow_clk) + return 0; + + __clk_get(slow_clk); + clk_prepare_enable(slow_clk); + + return 0; +} +arch_initcall(of_at91_clk_slow_retain); diff --git a/drivers/clk/berlin/bg2q.c b/drivers/clk/berlin/bg2q.c index 21784e4eb3f0..440ef81ab15c 100644 --- a/drivers/clk/berlin/bg2q.c +++ b/drivers/clk/berlin/bg2q.c @@ -285,7 +285,6 @@ static const struct berlin2_gate_data bg2q_gates[] __initconst = { { "pbridge", "perif", 15, CLK_IGNORE_UNUSED }, { "sdio", "perif", 16, CLK_IGNORE_UNUSED }, { "nfc", "perif", 18 }, - { "smemc", "perif", 19 }, { "pcie", "perif", 22 }, }; diff --git a/drivers/clk/clk-ppc-corenet.c b/drivers/clk/clk-ppc-corenet.c index b6e6c85507a5..0a47d6f49cd6 100644 --- a/drivers/clk/clk-ppc-corenet.c +++ b/drivers/clk/clk-ppc-corenet.c @@ -291,7 +291,7 @@ static const struct of_device_id ppc_clk_ids[] __initconst = { {} }; -static struct platform_driver ppc_corenet_clk_driver __initdata = { +static struct platform_driver ppc_corenet_clk_driver = { .driver = { .name = "ppc_corenet_clock", .of_match_table = ppc_clk_ids, diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index f4963b7d4e17..d48ac71c6c8b 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1366,7 +1366,7 @@ static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate) new_rate = clk->ops->determine_rate(clk->hw, rate, &best_parent_rate, &parent_hw); - parent = parent_hw->clk; + parent = parent_hw ? parent_hw->clk : NULL; } else if (clk->ops->round_rate) { new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate); diff --git a/drivers/clk/rockchip/clk-cpu.c b/drivers/clk/rockchip/clk-cpu.c index 75c8c45ef728..8539c4fd34cc 100644 --- a/drivers/clk/rockchip/clk-cpu.c +++ b/drivers/clk/rockchip/clk-cpu.c @@ -124,10 +124,11 @@ static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk, { const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data; unsigned long alt_prate, alt_div; + unsigned long flags; alt_prate = clk_get_rate(cpuclk->alt_parent); - spin_lock(cpuclk->lock); + spin_lock_irqsave(cpuclk->lock, flags); /* * If the old parent clock speed is less than the clock speed @@ -164,7 +165,7 @@ static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk, cpuclk->reg_base + reg_data->core_reg); } - spin_unlock(cpuclk->lock); + spin_unlock_irqrestore(cpuclk->lock, flags); return 0; } @@ -173,6 +174,7 @@ static int rockchip_cpuclk_post_rate_change(struct rockchip_cpuclk *cpuclk, { const struct rockchip_cpuclk_reg_data *reg_data = cpuclk->reg_data; const struct rockchip_cpuclk_rate_table *rate; + unsigned long flags; rate = rockchip_get_cpuclk_settings(cpuclk, ndata->new_rate); if (!rate) { @@ -181,7 +183,7 @@ static int rockchip_cpuclk_post_rate_change(struct rockchip_cpuclk *cpuclk, return -EINVAL; } - spin_lock(cpuclk->lock); + spin_lock_irqsave(cpuclk->lock, flags); if (ndata->old_rate < ndata->new_rate) rockchip_cpuclk_set_dividers(cpuclk, rate); @@ -201,7 +203,7 @@ static int rockchip_cpuclk_post_rate_change(struct rockchip_cpuclk *cpuclk, if (ndata->old_rate > ndata->new_rate) rockchip_cpuclk_set_dividers(cpuclk, rate); - spin_unlock(cpuclk->lock); + spin_unlock_irqrestore(cpuclk->lock, flags); return 0; } diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index c54078960847..7eb684c50d42 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c @@ -210,6 +210,17 @@ PNAME(mux_sclk_hsadc_p) = { "hsadc_src", "hsadc_frac", "ext_hsadc" }; PNAME(mux_mac_p) = { "gpll", "dpll" }; PNAME(mux_sclk_macref_p) = { "mac_src", "ext_rmii" }; +static struct rockchip_pll_clock rk3066_pll_clks[] __initdata = { + [apll] = PLL(pll_rk3066, PLL_APLL, "apll", mux_pll_p, 0, RK2928_PLL_CON(0), + RK2928_MODE_CON, 0, 5, 0, rk3188_pll_rates), + [dpll] = PLL(pll_rk3066, PLL_DPLL, "dpll", mux_pll_p, 0, RK2928_PLL_CON(4), + RK2928_MODE_CON, 4, 4, 0, NULL), + [cpll] = PLL(pll_rk3066, PLL_CPLL, "cpll", mux_pll_p, 0, RK2928_PLL_CON(8), + RK2928_MODE_CON, 8, 6, ROCKCHIP_PLL_SYNC_RATE, rk3188_pll_rates), + [gpll] = PLL(pll_rk3066, PLL_GPLL, "gpll", mux_pll_p, 0, RK2928_PLL_CON(12), + RK2928_MODE_CON, 12, 7, ROCKCHIP_PLL_SYNC_RATE, rk3188_pll_rates), +}; + static struct rockchip_pll_clock rk3188_pll_clks[] __initdata = { [apll] = PLL(pll_rk3066, PLL_APLL, "apll", mux_pll_p, 0, RK2928_PLL_CON(0), RK2928_MODE_CON, 0, 6, 0, rk3188_pll_rates), @@ -427,11 +438,11 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = { /* hclk_peri gates */ GATE(0, "hclk_peri_axi_matrix", "hclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(4), 0, GFLAGS), GATE(0, "hclk_peri_ahb_arbi", "hclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(4), 6, GFLAGS), - GATE(0, "hclk_emem_peri", "hclk_peri", 0, RK2928_CLKGATE_CON(4), 7, GFLAGS), + GATE(0, "hclk_emem_peri", "hclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(4), 7, GFLAGS), GATE(HCLK_EMAC, "hclk_emac", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 0, GFLAGS), GATE(HCLK_NANDC0, "hclk_nandc0", "hclk_peri", 0, RK2928_CLKGATE_CON(5), 9, GFLAGS), - GATE(0, "hclk_usb_peri", "hclk_peri", 0, RK2928_CLKGATE_CON(4), 5, GFLAGS), - GATE(HCLK_OTG0, "hclk_usbotg0", "hclk_peri", 0, RK2928_CLKGATE_CON(5), 13, GFLAGS), + GATE(0, "hclk_usb_peri", "hclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(4), 5, GFLAGS), + GATE(HCLK_OTG0, "hclk_usbotg0", "hclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(5), 13, GFLAGS), GATE(HCLK_HSADC, "hclk_hsadc", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 5, GFLAGS), GATE(HCLK_PIDF, "hclk_pidfilter", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 6, GFLAGS), GATE(HCLK_SDMMC, "hclk_sdmmc", "hclk_peri", 0, RK2928_CLKGATE_CON(5), 10, GFLAGS), @@ -592,7 +603,8 @@ static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = { GATE(0, "hclk_cif1", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 6, GFLAGS), GATE(0, "hclk_hdmi", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 14, GFLAGS), - GATE(HCLK_OTG1, "hclk_usbotg1", "hclk_peri", 0, RK2928_CLKGATE_CON(5), 14, GFLAGS), + GATE(HCLK_OTG1, "hclk_usbotg1", "hclk_peri", CLK_IGNORE_UNUSED, + RK2928_CLKGATE_CON(5), 14, GFLAGS), GATE(0, "aclk_cif1", "aclk_vio1", 0, RK2928_CLKGATE_CON(6), 7, GFLAGS), @@ -680,7 +692,8 @@ static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = { GATE(0, "hclk_imem0", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 14, GFLAGS), GATE(0, "hclk_imem1", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 15, GFLAGS), - GATE(HCLK_OTG1, "hclk_usbotg1", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 3, GFLAGS), + GATE(HCLK_OTG1, "hclk_usbotg1", "hclk_peri", CLK_IGNORE_UNUSED, + RK2928_CLKGATE_CON(7), 3, GFLAGS), GATE(HCLK_HSIC, "hclk_hsic", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 4, GFLAGS), GATE(PCLK_TIMER3, "pclk_timer3", "pclk_cpu", 0, RK2928_CLKGATE_CON(7), 9, GFLAGS), @@ -735,8 +748,8 @@ static void __init rk3188_common_clk_init(struct device_node *np) static void __init rk3066a_clk_init(struct device_node *np) { rk3188_common_clk_init(np); - rockchip_clk_register_plls(rk3188_pll_clks, - ARRAY_SIZE(rk3188_pll_clks), + rockchip_clk_register_plls(rk3066_pll_clks, + ARRAY_SIZE(rk3066_pll_clks), RK3066_GRF_SOC_STATUS); rockchip_clk_register_branches(rk3066a_clk_branches, ARRAY_SIZE(rk3066a_clk_branches)); diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c index ac6be7c0132d..cbcddcc02475 100644 --- a/drivers/clk/rockchip/clk-rk3288.c +++ b/drivers/clk/rockchip/clk-rk3288.c @@ -145,20 +145,20 @@ struct rockchip_pll_rate_table rk3288_pll_rates[] = { } static struct rockchip_cpuclk_rate_table rk3288_cpuclk_rates[] __initdata = { - RK3288_CPUCLK_RATE(1800000000, 2, 4, 2, 4, 4), - RK3288_CPUCLK_RATE(1704000000, 2, 4, 2, 4, 4), - RK3288_CPUCLK_RATE(1608000000, 2, 4, 2, 4, 4), - RK3288_CPUCLK_RATE(1512000000, 2, 4, 2, 4, 4), - RK3288_CPUCLK_RATE(1416000000, 2, 4, 2, 4, 4), - RK3288_CPUCLK_RATE(1200000000, 2, 4, 2, 4, 4), - RK3288_CPUCLK_RATE(1008000000, 2, 4, 2, 4, 4), - RK3288_CPUCLK_RATE( 816000000, 2, 4, 2, 4, 4), - RK3288_CPUCLK_RATE( 696000000, 2, 4, 2, 4, 4), - RK3288_CPUCLK_RATE( 600000000, 2, 4, 2, 4, 4), - RK3288_CPUCLK_RATE( 408000000, 2, 4, 2, 4, 4), - RK3288_CPUCLK_RATE( 312000000, 2, 4, 2, 4, 4), - RK3288_CPUCLK_RATE( 216000000, 2, 4, 2, 4, 4), - RK3288_CPUCLK_RATE( 126000000, 2, 4, 2, 4, 4), + RK3288_CPUCLK_RATE(1800000000, 1, 3, 1, 3, 3), + RK3288_CPUCLK_RATE(1704000000, 1, 3, 1, 3, 3), + RK3288_CPUCLK_RATE(1608000000, 1, 3, 1, 3, 3), + RK3288_CPUCLK_RATE(1512000000, 1, 3, 1, 3, 3), + RK3288_CPUCLK_RATE(1416000000, 1, 3, 1, 3, 3), + RK3288_CPUCLK_RATE(1200000000, 1, 3, 1, 3, 3), + RK3288_CPUCLK_RATE(1008000000, 1, 3, 1, 3, 3), + RK3288_CPUCLK_RATE( 816000000, 1, 3, 1, 3, 3), + RK3288_CPUCLK_RATE( 696000000, 1, 3, 1, 3, 3), + RK3288_CPUCLK_RATE( 600000000, 1, 3, 1, 3, 3), + RK3288_CPUCLK_RATE( 408000000, 1, 3, 1, 3, 3), + RK3288_CPUCLK_RATE( 312000000, 1, 3, 1, 3, 3), + RK3288_CPUCLK_RATE( 216000000, 1, 3, 1, 3, 3), + RK3288_CPUCLK_RATE( 126000000, 1, 3, 1, 3, 3), }; static const struct rockchip_cpuclk_reg_data rk3288_cpuclk_data = { @@ -190,7 +190,7 @@ PNAME(mux_uart2_p) = { "uart2_src", "uart2_frac", "xin24m" }; PNAME(mux_uart3_p) = { "uart3_src", "uart3_frac", "xin24m" }; PNAME(mux_uart4_p) = { "uart4_src", "uart4_frac", "xin24m" }; PNAME(mux_cif_out_p) = { "cif_src", "xin24m" }; -PNAME(mux_macref_p) = { "mac_src", "ext_gmac" }; +PNAME(mux_mac_p) = { "mac_pll_src", "ext_gmac" }; PNAME(mux_hsadcout_p) = { "hsadc_src", "ext_hsadc" }; PNAME(mux_edp_24m_p) = { "ext_edp_24m", "xin24m" }; PNAME(mux_tspout_p) = { "cpll", "gpll", "npll", "xin27m" }; @@ -575,18 +575,18 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = { MUX(SCLK_UART4, "sclk_uart4", mux_uart4_p, 0, RK3288_CLKSEL_CON(3), 8, 2, MFLAGS), - COMPOSITE(0, "mac_src", mux_pll_src_npll_cpll_gpll_p, 0, + COMPOSITE(0, "mac_pll_src", mux_pll_src_npll_cpll_gpll_p, 0, RK3288_CLKSEL_CON(21), 0, 2, MFLAGS, 8, 5, DFLAGS, RK3288_CLKGATE_CON(2), 5, GFLAGS), - MUX(0, "macref", mux_macref_p, 0, + MUX(SCLK_MAC, "mac_clk", mux_mac_p, 0, RK3288_CLKSEL_CON(21), 4, 1, MFLAGS), - GATE(0, "sclk_macref_out", "macref", 0, + GATE(SCLK_MACREF_OUT, "sclk_macref_out", "mac_clk", 0, RK3288_CLKGATE_CON(5), 3, GFLAGS), - GATE(SCLK_MACREF, "sclk_macref", "macref", 0, + GATE(SCLK_MACREF, "sclk_macref", "mac_clk", 0, RK3288_CLKGATE_CON(5), 2, GFLAGS), - GATE(SCLK_MAC_RX, "sclk_mac_rx", "macref", 0, + GATE(SCLK_MAC_RX, "sclk_mac_rx", "mac_clk", 0, RK3288_CLKGATE_CON(5), 0, GFLAGS), - GATE(SCLK_MAC_TX, "sclk_mac_tx", "macref", 0, + GATE(SCLK_MAC_TX, "sclk_mac_tx", "mac_clk", 0, RK3288_CLKGATE_CON(5), 1, GFLAGS), COMPOSITE(0, "hsadc_src", mux_pll_src_cpll_gpll_p, 0, diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c index 095c1774592c..a3025e7ae35f 100644 --- a/drivers/clocksource/arm_arch_timer.c +++ b/drivers/clocksource/arm_arch_timer.c @@ -15,6 +15,7 @@ #include <linux/cpu.h> #include <linux/cpu_pm.h> #include <linux/clockchips.h> +#include <linux/clocksource.h> #include <linux/interrupt.h> #include <linux/of_irq.h> #include <linux/of_address.h> diff --git a/drivers/clocksource/bcm_kona_timer.c b/drivers/clocksource/bcm_kona_timer.c index 0595dc6c453e..f1e33d08dd83 100644 --- a/drivers/clocksource/bcm_kona_timer.c +++ b/drivers/clocksource/bcm_kona_timer.c @@ -68,9 +68,8 @@ static void kona_timer_disable_and_clear(void __iomem *base) } static void -kona_timer_get_counter(void *timer_base, uint32_t *msw, uint32_t *lsw) +kona_timer_get_counter(void __iomem *timer_base, uint32_t *msw, uint32_t *lsw) { - void __iomem *base = IOMEM(timer_base); int loop_limit = 4; /* @@ -86,9 +85,9 @@ kona_timer_get_counter(void *timer_base, uint32_t *msw, uint32_t *lsw) */ while (--loop_limit) { - *msw = readl(base + KONA_GPTIMER_STCHI_OFFSET); - *lsw = readl(base + KONA_GPTIMER_STCLO_OFFSET); - if (*msw == readl(base + KONA_GPTIMER_STCHI_OFFSET)) + *msw = readl(timer_base + KONA_GPTIMER_STCHI_OFFSET); + *lsw = readl(timer_base + KONA_GPTIMER_STCLO_OFFSET); + if (*msw == readl(timer_base + KONA_GPTIMER_STCHI_OFFSET)) break; } if (!loop_limit) { diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c index 9403061a2acc..83564c9cfdbe 100644 --- a/drivers/clocksource/exynos_mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -97,8 +97,8 @@ static void exynos4_mct_write(unsigned int value, unsigned long offset) writel_relaxed(value, reg_base + offset); if (likely(offset >= EXYNOS4_MCT_L_BASE(0))) { - stat_addr = (offset & ~EXYNOS4_MCT_L_MASK) + MCT_L_WSTAT_OFFSET; - switch (offset & EXYNOS4_MCT_L_MASK) { + stat_addr = (offset & EXYNOS4_MCT_L_MASK) + MCT_L_WSTAT_OFFSET; + switch (offset & ~EXYNOS4_MCT_L_MASK) { case MCT_L_TCON_OFFSET: mask = 1 << 3; /* L_TCON write status */ break; diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 0f665b8f2461..f150ca82bfaf 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -428,7 +428,7 @@ static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch, ced->features = CLOCK_EVT_FEAT_PERIODIC; ced->features |= CLOCK_EVT_FEAT_ONESHOT; ced->rating = 200; - ced->cpumask = cpumask_of(0); + ced->cpumask = cpu_possible_mask; ced->set_next_event = sh_tmu_clock_event_next; ced->set_mode = sh_tmu_clock_event_mode; ced->suspend = sh_tmu_clock_event_suspend; diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 29b2ef5a68b9..a171fef2c2b6 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -2,6 +2,7 @@ menu "CPU Frequency scaling" config CPU_FREQ bool "CPU Frequency scaling" + select SRCU help CPU Frequency scaling allows you to change the clock speed of CPUs on the fly. This is a nice method to save power, because diff --git a/drivers/cpufreq/Kconfig.x86 b/drivers/cpufreq/Kconfig.x86 index 89ae88f91895..c59bdcb83217 100644 --- a/drivers/cpufreq/Kconfig.x86 +++ b/drivers/cpufreq/Kconfig.x86 @@ -57,6 +57,16 @@ config X86_ACPI_CPUFREQ_CPB By enabling this option the acpi_cpufreq driver provides the old entry in addition to the new boost ones, for compatibility reasons. +config X86_SFI_CPUFREQ + tristate "SFI Performance-States driver" + depends on X86_INTEL_MID && SFI + help + This adds a CPUFreq driver for some Silvermont based Intel Atom + architectures like Z34xx and Z35xx which enumerate processor + performance states through SFI. + + If in doubt, say N. + config ELAN_CPUFREQ tristate "AMD Elan SC400 and SC410" depends on MELAN diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index b3ca7b0b2c33..8b4220ac888b 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_X86_P4_CLOCKMOD) += p4-clockmod.o obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o obj-$(CONFIG_X86_INTEL_PSTATE) += intel_pstate.o obj-$(CONFIG_X86_AMD_FREQ_SENSITIVITY) += amd_freq_sensitivity.o +obj-$(CONFIG_X86_SFI_CPUFREQ) += sfi-cpufreq.o ################################################################################## # ARM SoC drivers diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c index fde97d6e31d6..bab67db54b7e 100644 --- a/drivers/cpufreq/cpufreq-dt.c +++ b/drivers/cpufreq/cpufreq-dt.c @@ -320,8 +320,7 @@ static int cpufreq_exit(struct cpufreq_policy *policy) { struct private_data *priv = policy->driver_data; - if (priv->cdev) - cpufreq_cooling_unregister(priv->cdev); + cpufreq_cooling_unregister(priv->cdev); dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); of_free_opp_table(priv->cpu_dev); clk_put(policy->clk); diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 46bed4f81cde..28e59a48b35f 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -27,9 +27,21 @@ #include <linux/mutex.h> #include <linux/slab.h> #include <linux/suspend.h> +#include <linux/syscore_ops.h> #include <linux/tick.h> #include <trace/events/power.h> +/* Macros to iterate over lists */ +/* Iterate over online CPUs policies */ +static LIST_HEAD(cpufreq_policy_list); +#define for_each_policy(__policy) \ + list_for_each_entry(__policy, &cpufreq_policy_list, policy_list) + +/* Iterate over governors */ +static LIST_HEAD(cpufreq_governor_list); +#define for_each_governor(__governor) \ + list_for_each_entry(__governor, &cpufreq_governor_list, governor_list) + /** * The "cpufreq driver" - the arch- or hardware-dependent low * level driver of CPUFreq support, and its spinlock. This lock @@ -40,7 +52,6 @@ static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data); static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data_fallback); static DEFINE_RWLOCK(cpufreq_driver_lock); DEFINE_MUTEX(cpufreq_governor_lock); -static LIST_HEAD(cpufreq_policy_list); /* This one keeps track of the previously set governor of a removed CPU */ static DEFINE_PER_CPU(char[CPUFREQ_NAME_LEN], cpufreq_cpu_governor); @@ -62,7 +73,7 @@ static DECLARE_RWSEM(cpufreq_rwsem); /* internal prototypes */ static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event); -static unsigned int __cpufreq_get(unsigned int cpu); +static unsigned int __cpufreq_get(struct cpufreq_policy *policy); static void handle_update(struct work_struct *work); /** @@ -93,7 +104,6 @@ void disable_cpufreq(void) { off = 1; } -static LIST_HEAD(cpufreq_governor_list); static DEFINE_MUTEX(cpufreq_governor_mutex); bool have_governor_per_policy(void) @@ -202,7 +212,7 @@ struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) struct cpufreq_policy *policy = NULL; unsigned long flags; - if (cpufreq_disabled() || (cpu >= nr_cpu_ids)) + if (cpu >= nr_cpu_ids) return NULL; if (!down_read_trylock(&cpufreq_rwsem)) @@ -229,9 +239,6 @@ EXPORT_SYMBOL_GPL(cpufreq_cpu_get); void cpufreq_cpu_put(struct cpufreq_policy *policy) { - if (cpufreq_disabled()) - return; - kobject_put(&policy->kobj); up_read(&cpufreq_rwsem); } @@ -249,12 +256,12 @@ EXPORT_SYMBOL_GPL(cpufreq_cpu_put); * systems as each CPU might be scaled differently. So, use the arch * per-CPU loops_per_jiffy value wherever possible. */ -#ifndef CONFIG_SMP -static unsigned long l_p_j_ref; -static unsigned int l_p_j_ref_freq; - static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) { +#ifndef CONFIG_SMP + static unsigned long l_p_j_ref; + static unsigned int l_p_j_ref_freq; + if (ci->flags & CPUFREQ_CONST_LOOPS) return; @@ -270,13 +277,8 @@ static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) pr_debug("scaling loops_per_jiffy to %lu for frequency %u kHz\n", loops_per_jiffy, ci->new); } -} -#else -static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) -{ - return; -} #endif +} static void __cpufreq_notify_transition(struct cpufreq_policy *policy, struct cpufreq_freqs *freqs, unsigned int state) @@ -432,11 +434,11 @@ static ssize_t store_boost(struct kobject *kobj, struct attribute *attr, } define_one_global_rw(boost); -static struct cpufreq_governor *__find_governor(const char *str_governor) +static struct cpufreq_governor *find_governor(const char *str_governor) { struct cpufreq_governor *t; - list_for_each_entry(t, &cpufreq_governor_list, governor_list) + for_each_governor(t) if (!strncasecmp(str_governor, t->name, CPUFREQ_NAME_LEN)) return t; @@ -463,12 +465,12 @@ static int cpufreq_parse_governor(char *str_governor, unsigned int *policy, *policy = CPUFREQ_POLICY_POWERSAVE; err = 0; } - } else if (has_target()) { + } else { struct cpufreq_governor *t; mutex_lock(&cpufreq_governor_mutex); - t = __find_governor(str_governor); + t = find_governor(str_governor); if (t == NULL) { int ret; @@ -478,7 +480,7 @@ static int cpufreq_parse_governor(char *str_governor, unsigned int *policy, mutex_lock(&cpufreq_governor_mutex); if (ret == 0) - t = __find_governor(str_governor); + t = find_governor(str_governor); } if (t != NULL) { @@ -513,8 +515,7 @@ show_one(cpuinfo_transition_latency, cpuinfo.transition_latency); show_one(scaling_min_freq, min); show_one(scaling_max_freq, max); -static ssize_t show_scaling_cur_freq( - struct cpufreq_policy *policy, char *buf) +static ssize_t show_scaling_cur_freq(struct cpufreq_policy *policy, char *buf) { ssize_t ret; @@ -563,7 +564,7 @@ store_one(scaling_max_freq, max); static ssize_t show_cpuinfo_cur_freq(struct cpufreq_policy *policy, char *buf) { - unsigned int cur_freq = __cpufreq_get(policy->cpu); + unsigned int cur_freq = __cpufreq_get(policy); if (!cur_freq) return sprintf(buf, "<unknown>"); return sprintf(buf, "%u\n", cur_freq); @@ -639,7 +640,7 @@ static ssize_t show_scaling_available_governors(struct cpufreq_policy *policy, goto out; } - list_for_each_entry(t, &cpufreq_governor_list, governor_list) { + for_each_governor(t) { if (i >= (ssize_t) ((PAGE_SIZE / sizeof(char)) - (CPUFREQ_NAME_LEN + 2))) goto out; @@ -902,7 +903,7 @@ static int cpufreq_add_dev_interface(struct cpufreq_policy *policy, /* set up files for this cpu device */ drv_attr = cpufreq_driver->attr; - while ((drv_attr) && (*drv_attr)) { + while (drv_attr && *drv_attr) { ret = sysfs_create_file(&policy->kobj, &((*drv_attr)->attr)); if (ret) return ret; @@ -936,7 +937,7 @@ static void cpufreq_init_policy(struct cpufreq_policy *policy) memcpy(&new_policy, policy, sizeof(*policy)); /* Update governor of new_policy to the governor used before hotplug */ - gov = __find_governor(per_cpu(cpufreq_cpu_governor, policy->cpu)); + gov = find_governor(per_cpu(cpufreq_cpu_governor, policy->cpu)); if (gov) pr_debug("Restoring governor %s for cpu %d\n", policy->governor->name, policy->cpu); @@ -958,7 +959,6 @@ static void cpufreq_init_policy(struct cpufreq_policy *policy) } } -#ifdef CONFIG_HOTPLUG_CPU static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu, struct device *dev) { @@ -996,7 +996,6 @@ static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy, return sysfs_create_link(&dev->kobj, &policy->kobj, "cpufreq"); } -#endif static struct cpufreq_policy *cpufreq_policy_restore(unsigned int cpu) { @@ -1033,6 +1032,8 @@ static struct cpufreq_policy *cpufreq_policy_alloc(void) init_rwsem(&policy->rwsem); spin_lock_init(&policy->transition_lock); init_waitqueue_head(&policy->transition_wait); + init_completion(&policy->kobj_unregister); + INIT_WORK(&policy->update, handle_update); return policy; @@ -1091,15 +1092,9 @@ static int update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu, } down_write(&policy->rwsem); - - policy->last_cpu = policy->cpu; policy->cpu = cpu; - up_write(&policy->rwsem); - blocking_notifier_call_chain(&cpufreq_policy_notifier_list, - CPUFREQ_UPDATE_POLICY_CPU, policy); - return 0; } @@ -1110,41 +1105,32 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) struct cpufreq_policy *policy; unsigned long flags; bool recover_policy = cpufreq_suspended; -#ifdef CONFIG_HOTPLUG_CPU - struct cpufreq_policy *tpolicy; -#endif if (cpu_is_offline(cpu)) return 0; pr_debug("adding CPU %u\n", cpu); -#ifdef CONFIG_SMP /* check whether a different CPU already registered this * CPU because it is in the same boat. */ - policy = cpufreq_cpu_get(cpu); - if (unlikely(policy)) { - cpufreq_cpu_put(policy); + policy = cpufreq_cpu_get_raw(cpu); + if (unlikely(policy)) return 0; - } -#endif if (!down_read_trylock(&cpufreq_rwsem)) return 0; -#ifdef CONFIG_HOTPLUG_CPU /* Check if this cpu was hot-unplugged earlier and has siblings */ read_lock_irqsave(&cpufreq_driver_lock, flags); - list_for_each_entry(tpolicy, &cpufreq_policy_list, policy_list) { - if (cpumask_test_cpu(cpu, tpolicy->related_cpus)) { + for_each_policy(policy) { + if (cpumask_test_cpu(cpu, policy->related_cpus)) { read_unlock_irqrestore(&cpufreq_driver_lock, flags); - ret = cpufreq_add_policy_cpu(tpolicy, cpu, dev); + ret = cpufreq_add_policy_cpu(policy, cpu, dev); up_read(&cpufreq_rwsem); return ret; } } read_unlock_irqrestore(&cpufreq_driver_lock, flags); -#endif /* * Restore the saved policy when doing light-weight init and fall back @@ -1171,9 +1157,6 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif) cpumask_copy(policy->cpus, cpumask_of(cpu)); - init_completion(&policy->kobj_unregister); - INIT_WORK(&policy->update, handle_update); - /* call driver. From then on the cpufreq must be able * to accept all calls to ->verify and ->setpolicy for this CPU */ @@ -1371,11 +1354,10 @@ static int __cpufreq_remove_dev_prepare(struct device *dev, pr_err("%s: Failed to stop governor\n", __func__); return ret; } - } - if (!cpufreq_driver->setpolicy) strncpy(per_cpu(cpufreq_cpu_governor, cpu), policy->governor->name, CPUFREQ_NAME_LEN); + } down_read(&policy->rwsem); cpus = cpumask_weight(policy->cpus); @@ -1416,9 +1398,10 @@ static int __cpufreq_remove_dev_finish(struct device *dev, unsigned long flags; struct cpufreq_policy *policy; - read_lock_irqsave(&cpufreq_driver_lock, flags); + write_lock_irqsave(&cpufreq_driver_lock, flags); policy = per_cpu(cpufreq_cpu_data, cpu); - read_unlock_irqrestore(&cpufreq_driver_lock, flags); + per_cpu(cpufreq_cpu_data, cpu) = NULL; + write_unlock_irqrestore(&cpufreq_driver_lock, flags); if (!policy) { pr_debug("%s: No cpu_data found\n", __func__); @@ -1473,7 +1456,6 @@ static int __cpufreq_remove_dev_finish(struct device *dev, } } - per_cpu(cpufreq_cpu_data, cpu) = NULL; return 0; } @@ -1510,30 +1492,23 @@ static void handle_update(struct work_struct *work) /** * cpufreq_out_of_sync - If actual and saved CPU frequency differs, we're * in deep trouble. - * @cpu: cpu number - * @old_freq: CPU frequency the kernel thinks the CPU runs at + * @policy: policy managing CPUs * @new_freq: CPU frequency the CPU actually runs at * * We adjust to current frequency first, and need to clean up later. * So either call to cpufreq_update_policy() or schedule handle_update()). */ -static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq, +static void cpufreq_out_of_sync(struct cpufreq_policy *policy, unsigned int new_freq) { - struct cpufreq_policy *policy; struct cpufreq_freqs freqs; - unsigned long flags; pr_debug("Warning: CPU frequency out of sync: cpufreq and timing core thinks of %u, is %u kHz\n", - old_freq, new_freq); + policy->cur, new_freq); - freqs.old = old_freq; + freqs.old = policy->cur; freqs.new = new_freq; - read_lock_irqsave(&cpufreq_driver_lock, flags); - policy = per_cpu(cpufreq_cpu_data, cpu); - read_unlock_irqrestore(&cpufreq_driver_lock, flags); - cpufreq_freq_transition_begin(policy, &freqs); cpufreq_freq_transition_end(policy, &freqs, 0); } @@ -1583,22 +1558,21 @@ unsigned int cpufreq_quick_get_max(unsigned int cpu) } EXPORT_SYMBOL(cpufreq_quick_get_max); -static unsigned int __cpufreq_get(unsigned int cpu) +static unsigned int __cpufreq_get(struct cpufreq_policy *policy) { - struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu); unsigned int ret_freq = 0; if (!cpufreq_driver->get) return ret_freq; - ret_freq = cpufreq_driver->get(cpu); + ret_freq = cpufreq_driver->get(policy->cpu); if (ret_freq && policy->cur && !(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) { /* verify no discrepancy between actual and saved value exists */ if (unlikely(ret_freq != policy->cur)) { - cpufreq_out_of_sync(cpu, policy->cur, ret_freq); + cpufreq_out_of_sync(policy, ret_freq); schedule_work(&policy->update); } } @@ -1619,7 +1593,7 @@ unsigned int cpufreq_get(unsigned int cpu) if (policy) { down_read(&policy->rwsem); - ret_freq = __cpufreq_get(cpu); + ret_freq = __cpufreq_get(policy); up_read(&policy->rwsem); cpufreq_cpu_put(policy); @@ -1682,7 +1656,7 @@ void cpufreq_suspend(void) pr_debug("%s: Suspending Governors\n", __func__); - list_for_each_entry(policy, &cpufreq_policy_list, policy_list) { + for_each_policy(policy) { if (__cpufreq_governor(policy, CPUFREQ_GOV_STOP)) pr_err("%s: Failed to stop governor for policy: %p\n", __func__, policy); @@ -1716,7 +1690,7 @@ void cpufreq_resume(void) pr_debug("%s: Resuming Governors\n", __func__); - list_for_each_entry(policy, &cpufreq_policy_list, policy_list) { + for_each_policy(policy) { if (cpufreq_driver->resume && cpufreq_driver->resume(policy)) pr_err("%s: Failed to resume driver: %p\n", __func__, policy); @@ -2006,10 +1980,6 @@ int cpufreq_driver_target(struct cpufreq_policy *policy, } EXPORT_SYMBOL_GPL(cpufreq_driver_target); -/* - * when "event" is CPUFREQ_GOV_LIMITS - */ - static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event) { @@ -2107,7 +2077,7 @@ int cpufreq_register_governor(struct cpufreq_governor *governor) governor->initialized = 0; err = -EBUSY; - if (__find_governor(governor->name) == NULL) { + if (!find_governor(governor->name)) { err = 0; list_add(&governor->governor_list, &cpufreq_governor_list); } @@ -2307,8 +2277,7 @@ int cpufreq_update_policy(unsigned int cpu) policy->cur = new_policy.cur; } else { if (policy->cur != new_policy.cur && has_target()) - cpufreq_out_of_sync(cpu, policy->cur, - new_policy.cur); + cpufreq_out_of_sync(policy, new_policy.cur); } } @@ -2364,7 +2333,7 @@ static int cpufreq_boost_set_sw(int state) struct cpufreq_policy *policy; int ret = -EINVAL; - list_for_each_entry(policy, &cpufreq_policy_list, policy_list) { + for_each_policy(policy) { freq_table = cpufreq_frequency_get_table(policy->cpu); if (freq_table) { ret = cpufreq_frequency_table_cpuinfo(policy, @@ -2454,9 +2423,6 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) pr_debug("trying to register driver %s\n", driver_data->name); - if (driver_data->setpolicy) - driver_data->flags |= CPUFREQ_CONST_LOOPS; - write_lock_irqsave(&cpufreq_driver_lock, flags); if (cpufreq_driver) { write_unlock_irqrestore(&cpufreq_driver_lock, flags); @@ -2465,6 +2431,9 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) cpufreq_driver = driver_data; write_unlock_irqrestore(&cpufreq_driver_lock, flags); + if (driver_data->setpolicy) + driver_data->flags |= CPUFREQ_CONST_LOOPS; + if (cpufreq_boost_supported()) { /* * Check if driver provides function to enable boost - @@ -2485,23 +2454,12 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) if (ret) goto err_boost_unreg; - if (!(cpufreq_driver->flags & CPUFREQ_STICKY)) { - int i; - ret = -ENODEV; - - /* check for at least one working CPU */ - for (i = 0; i < nr_cpu_ids; i++) - if (cpu_possible(i) && per_cpu(cpufreq_cpu_data, i)) { - ret = 0; - break; - } - + if (!(cpufreq_driver->flags & CPUFREQ_STICKY) && + list_empty(&cpufreq_policy_list)) { /* if all ->init() calls failed, unregister */ - if (ret) { - pr_debug("no CPU initialized for driver %s\n", - driver_data->name); - goto err_if_unreg; - } + pr_debug("%s: No CPU initialized for driver %s\n", __func__, + driver_data->name); + goto err_if_unreg; } register_hotcpu_notifier(&cpufreq_cpu_notifier); @@ -2556,6 +2514,14 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver) } EXPORT_SYMBOL_GPL(cpufreq_unregister_driver); +/* + * Stop cpufreq at shutdown to make sure it isn't holding any locks + * or mutexes when secondary CPUs are halted. + */ +static struct syscore_ops cpufreq_syscore_ops = { + .shutdown = cpufreq_suspend, +}; + static int __init cpufreq_core_init(void) { if (cpufreq_disabled()) @@ -2564,6 +2530,8 @@ static int __init cpufreq_core_init(void) cpufreq_global_kobject = kobject_create(); BUG_ON(!cpufreq_global_kobject); + register_syscore_ops(&cpufreq_syscore_ops); + return 0; } core_initcall(cpufreq_core_init); diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 0cd9b4dcef99..5e370a30a964 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -18,7 +18,6 @@ static spinlock_t cpufreq_stats_lock; struct cpufreq_stats { - unsigned int cpu; unsigned int total_trans; unsigned long long last_time; unsigned int max_state; @@ -31,50 +30,33 @@ struct cpufreq_stats { #endif }; -static DEFINE_PER_CPU(struct cpufreq_stats *, cpufreq_stats_table); - -struct cpufreq_stats_attribute { - struct attribute attr; - ssize_t(*show) (struct cpufreq_stats *, char *); -}; - -static int cpufreq_stats_update(unsigned int cpu) +static int cpufreq_stats_update(struct cpufreq_stats *stats) { - struct cpufreq_stats *stat; - unsigned long long cur_time; + unsigned long long cur_time = get_jiffies_64(); - cur_time = get_jiffies_64(); spin_lock(&cpufreq_stats_lock); - stat = per_cpu(cpufreq_stats_table, cpu); - if (stat->time_in_state) - stat->time_in_state[stat->last_index] += - cur_time - stat->last_time; - stat->last_time = cur_time; + stats->time_in_state[stats->last_index] += cur_time - stats->last_time; + stats->last_time = cur_time; spin_unlock(&cpufreq_stats_lock); return 0; } static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf) { - struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu); - if (!stat) - return 0; - return sprintf(buf, "%d\n", - per_cpu(cpufreq_stats_table, stat->cpu)->total_trans); + return sprintf(buf, "%d\n", policy->stats->total_trans); } static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf) { + struct cpufreq_stats *stats = policy->stats; ssize_t len = 0; int i; - struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu); - if (!stat) - return 0; - cpufreq_stats_update(stat->cpu); - for (i = 0; i < stat->state_num; i++) { - len += sprintf(buf + len, "%u %llu\n", stat->freq_table[i], + + cpufreq_stats_update(stats); + for (i = 0; i < stats->state_num; i++) { + len += sprintf(buf + len, "%u %llu\n", stats->freq_table[i], (unsigned long long) - jiffies_64_to_clock_t(stat->time_in_state[i])); + jiffies_64_to_clock_t(stats->time_in_state[i])); } return len; } @@ -82,38 +64,35 @@ static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf) #ifdef CONFIG_CPU_FREQ_STAT_DETAILS static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf) { + struct cpufreq_stats *stats = policy->stats; ssize_t len = 0; int i, j; - struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu); - if (!stat) - return 0; - cpufreq_stats_update(stat->cpu); len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n"); len += snprintf(buf + len, PAGE_SIZE - len, " : "); - for (i = 0; i < stat->state_num; i++) { + for (i = 0; i < stats->state_num; i++) { if (len >= PAGE_SIZE) break; len += snprintf(buf + len, PAGE_SIZE - len, "%9u ", - stat->freq_table[i]); + stats->freq_table[i]); } if (len >= PAGE_SIZE) return PAGE_SIZE; len += snprintf(buf + len, PAGE_SIZE - len, "\n"); - for (i = 0; i < stat->state_num; i++) { + for (i = 0; i < stats->state_num; i++) { if (len >= PAGE_SIZE) break; len += snprintf(buf + len, PAGE_SIZE - len, "%9u: ", - stat->freq_table[i]); + stats->freq_table[i]); - for (j = 0; j < stat->state_num; j++) { + for (j = 0; j < stats->state_num; j++) { if (len >= PAGE_SIZE) break; len += snprintf(buf + len, PAGE_SIZE - len, "%9u ", - stat->trans_table[i*stat->max_state+j]); + stats->trans_table[i*stats->max_state+j]); } if (len >= PAGE_SIZE) break; @@ -142,28 +121,29 @@ static struct attribute_group stats_attr_group = { .name = "stats" }; -static int freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq) +static int freq_table_get_index(struct cpufreq_stats *stats, unsigned int freq) { int index; - for (index = 0; index < stat->max_state; index++) - if (stat->freq_table[index] == freq) + for (index = 0; index < stats->max_state; index++) + if (stats->freq_table[index] == freq) return index; return -1; } static void __cpufreq_stats_free_table(struct cpufreq_policy *policy) { - struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu); + struct cpufreq_stats *stats = policy->stats; - if (!stat) + /* Already freed */ + if (!stats) return; - pr_debug("%s: Free stat table\n", __func__); + pr_debug("%s: Free stats table\n", __func__); sysfs_remove_group(&policy->kobj, &stats_attr_group); - kfree(stat->time_in_state); - kfree(stat); - per_cpu(cpufreq_stats_table, policy->cpu) = NULL; + kfree(stats->time_in_state); + kfree(stats); + policy->stats = NULL; } static void cpufreq_stats_free_table(unsigned int cpu) @@ -174,37 +154,33 @@ static void cpufreq_stats_free_table(unsigned int cpu) if (!policy) return; - if (cpufreq_frequency_get_table(policy->cpu)) - __cpufreq_stats_free_table(policy); + __cpufreq_stats_free_table(policy); cpufreq_cpu_put(policy); } static int __cpufreq_stats_create_table(struct cpufreq_policy *policy) { - unsigned int i, count = 0, ret = 0; - struct cpufreq_stats *stat; + unsigned int i = 0, count = 0, ret = -ENOMEM; + struct cpufreq_stats *stats; unsigned int alloc_size; unsigned int cpu = policy->cpu; struct cpufreq_frequency_table *pos, *table; + /* We need cpufreq table for creating stats table */ table = cpufreq_frequency_get_table(cpu); if (unlikely(!table)) return 0; - if (per_cpu(cpufreq_stats_table, cpu)) - return -EBUSY; - stat = kzalloc(sizeof(*stat), GFP_KERNEL); - if ((stat) == NULL) - return -ENOMEM; - - ret = sysfs_create_group(&policy->kobj, &stats_attr_group); - if (ret) - goto error_out; + /* stats already initialized */ + if (policy->stats) + return -EEXIST; - stat->cpu = cpu; - per_cpu(cpufreq_stats_table, cpu) = stat; + stats = kzalloc(sizeof(*stats), GFP_KERNEL); + if (!stats) + return -ENOMEM; + /* Find total allocation size */ cpufreq_for_each_valid_entry(pos, table) count++; @@ -213,32 +189,40 @@ static int __cpufreq_stats_create_table(struct cpufreq_policy *policy) #ifdef CONFIG_CPU_FREQ_STAT_DETAILS alloc_size += count * count * sizeof(int); #endif - stat->max_state = count; - stat->time_in_state = kzalloc(alloc_size, GFP_KERNEL); - if (!stat->time_in_state) { - ret = -ENOMEM; - goto error_alloc; - } - stat->freq_table = (unsigned int *)(stat->time_in_state + count); + + /* Allocate memory for time_in_state/freq_table/trans_table in one go */ + stats->time_in_state = kzalloc(alloc_size, GFP_KERNEL); + if (!stats->time_in_state) + goto free_stat; + + stats->freq_table = (unsigned int *)(stats->time_in_state + count); #ifdef CONFIG_CPU_FREQ_STAT_DETAILS - stat->trans_table = stat->freq_table + count; + stats->trans_table = stats->freq_table + count; #endif - i = 0; + + stats->max_state = count; + + /* Find valid-unique entries */ cpufreq_for_each_valid_entry(pos, table) - if (freq_table_get_index(stat, pos->frequency) == -1) - stat->freq_table[i++] = pos->frequency; - stat->state_num = i; - spin_lock(&cpufreq_stats_lock); - stat->last_time = get_jiffies_64(); - stat->last_index = freq_table_get_index(stat, policy->cur); - spin_unlock(&cpufreq_stats_lock); - return 0; -error_alloc: - sysfs_remove_group(&policy->kobj, &stats_attr_group); -error_out: - kfree(stat); - per_cpu(cpufreq_stats_table, cpu) = NULL; + if (freq_table_get_index(stats, pos->frequency) == -1) + stats->freq_table[i++] = pos->frequency; + + stats->state_num = i; + stats->last_time = get_jiffies_64(); + stats->last_index = freq_table_get_index(stats, policy->cur); + + policy->stats = stats; + ret = sysfs_create_group(&policy->kobj, &stats_attr_group); + if (!ret) + return 0; + + /* We failed, release resources */ + policy->stats = NULL; + kfree(stats->time_in_state); +free_stat: + kfree(stats); + return ret; } @@ -259,30 +243,12 @@ static void cpufreq_stats_create_table(unsigned int cpu) cpufreq_cpu_put(policy); } -static void cpufreq_stats_update_policy_cpu(struct cpufreq_policy *policy) -{ - struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, - policy->last_cpu); - - pr_debug("Updating stats_table for new_cpu %u from last_cpu %u\n", - policy->cpu, policy->last_cpu); - per_cpu(cpufreq_stats_table, policy->cpu) = per_cpu(cpufreq_stats_table, - policy->last_cpu); - per_cpu(cpufreq_stats_table, policy->last_cpu) = NULL; - stat->cpu = policy->cpu; -} - static int cpufreq_stat_notifier_policy(struct notifier_block *nb, unsigned long val, void *data) { int ret = 0; struct cpufreq_policy *policy = data; - if (val == CPUFREQ_UPDATE_POLICY_CPU) { - cpufreq_stats_update_policy_cpu(policy); - return 0; - } - if (val == CPUFREQ_CREATE_POLICY) ret = __cpufreq_stats_create_table(policy); else if (val == CPUFREQ_REMOVE_POLICY) @@ -295,35 +261,45 @@ static int cpufreq_stat_notifier_trans(struct notifier_block *nb, unsigned long val, void *data) { struct cpufreq_freqs *freq = data; - struct cpufreq_stats *stat; + struct cpufreq_policy *policy = cpufreq_cpu_get(freq->cpu); + struct cpufreq_stats *stats; int old_index, new_index; - if (val != CPUFREQ_POSTCHANGE) + if (!policy) { + pr_err("%s: No policy found\n", __func__); return 0; + } - stat = per_cpu(cpufreq_stats_table, freq->cpu); - if (!stat) - return 0; + if (val != CPUFREQ_POSTCHANGE) + goto put_policy; - old_index = stat->last_index; - new_index = freq_table_get_index(stat, freq->new); + if (!policy->stats) { + pr_debug("%s: No stats found\n", __func__); + goto put_policy; + } - /* We can't do stat->time_in_state[-1]= .. */ - if (old_index == -1 || new_index == -1) - return 0; + stats = policy->stats; + + old_index = stats->last_index; + new_index = freq_table_get_index(stats, freq->new); - cpufreq_stats_update(freq->cpu); + /* We can't do stats->time_in_state[-1]= .. */ + if (old_index == -1 || new_index == -1) + goto put_policy; if (old_index == new_index) - return 0; + goto put_policy; - spin_lock(&cpufreq_stats_lock); - stat->last_index = new_index; + cpufreq_stats_update(stats); + + stats->last_index = new_index; #ifdef CONFIG_CPU_FREQ_STAT_DETAILS - stat->trans_table[old_index * stat->max_state + new_index]++; + stats->trans_table[old_index * stats->max_state + new_index]++; #endif - stat->total_trans++; - spin_unlock(&cpufreq_stats_lock); + stats->total_trans++; + +put_policy: + cpufreq_cpu_put(policy); return 0; } @@ -374,8 +350,7 @@ static void __exit cpufreq_stats_exit(void) } MODULE_AUTHOR("Zou Nan hai <nanhai.zou@intel.com>"); -MODULE_DESCRIPTION("'cpufreq_stats' - A driver to export cpufreq stats " - "through sysfs filesystem"); +MODULE_DESCRIPTION("Export cpufreq stats via sysfs"); MODULE_LICENSE("GPL"); module_init(cpufreq_stats_init); diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 742eefba12c2..872c5772c5d3 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -148,6 +148,8 @@ struct perf_limits { int32_t min_perf; int max_policy_pct; int max_sysfs_pct; + int min_policy_pct; + int min_sysfs_pct; }; static struct perf_limits limits = { @@ -159,6 +161,8 @@ static struct perf_limits limits = { .min_perf = 0, .max_policy_pct = 100, .max_sysfs_pct = 100, + .min_policy_pct = 0, + .min_sysfs_pct = 0, }; static inline void pid_reset(struct _pid *pid, int setpoint, int busy, @@ -338,6 +342,33 @@ static void __init intel_pstate_debug_expose_params(void) return sprintf(buf, "%u\n", limits.object); \ } +static ssize_t show_turbo_pct(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct cpudata *cpu; + int total, no_turbo, turbo_pct; + uint32_t turbo_fp; + + cpu = all_cpu_data[0]; + + total = cpu->pstate.turbo_pstate - cpu->pstate.min_pstate + 1; + no_turbo = cpu->pstate.max_pstate - cpu->pstate.min_pstate + 1; + turbo_fp = div_fp(int_tofp(no_turbo), int_tofp(total)); + turbo_pct = 100 - fp_toint(mul_fp(turbo_fp, int_tofp(100))); + return sprintf(buf, "%u\n", turbo_pct); +} + +static ssize_t show_num_pstates(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct cpudata *cpu; + int total; + + cpu = all_cpu_data[0]; + total = cpu->pstate.turbo_pstate - cpu->pstate.min_pstate + 1; + return sprintf(buf, "%u\n", total); +} + static ssize_t show_no_turbo(struct kobject *kobj, struct attribute *attr, char *buf) { @@ -404,7 +435,9 @@ static ssize_t store_min_perf_pct(struct kobject *a, struct attribute *b, ret = sscanf(buf, "%u", &input); if (ret != 1) return -EINVAL; - limits.min_perf_pct = clamp_t(int, input, 0 , 100); + + 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 = div_fp(int_tofp(limits.min_perf_pct), int_tofp(100)); if (hwp_active) @@ -418,11 +451,15 @@ show_one(min_perf_pct, min_perf_pct); define_one_global_rw(no_turbo); define_one_global_rw(max_perf_pct); define_one_global_rw(min_perf_pct); +define_one_global_ro(turbo_pct); +define_one_global_ro(num_pstates); static struct attribute *intel_pstate_attributes[] = { &no_turbo.attr, &max_perf_pct.attr, &min_perf_pct.attr, + &turbo_pct.attr, + &num_pstates.attr, NULL }; @@ -825,6 +862,7 @@ static const struct x86_cpu_id intel_pstate_cpu_ids[] = { ICPU(0x46, core_params), ICPU(0x47, core_params), ICPU(0x4c, byt_params), + ICPU(0x4e, core_params), ICPU(0x4f, core_params), ICPU(0x56, core_params), {} @@ -887,7 +925,9 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) if (!policy->cpuinfo.max_freq) return -ENODEV; - if (policy->policy == CPUFREQ_POLICY_PERFORMANCE) { + if (policy->policy == CPUFREQ_POLICY_PERFORMANCE && + policy->max >= policy->cpuinfo.max_freq) { + limits.min_policy_pct = 100; limits.min_perf_pct = 100; limits.min_perf = int_tofp(1); limits.max_policy_pct = 100; @@ -897,8 +937,9 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) return 0; } - limits.min_perf_pct = (policy->min * 100) / policy->cpuinfo.max_freq; - limits.min_perf_pct = clamp_t(int, limits.min_perf_pct, 0 , 100); + 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; @@ -978,6 +1019,7 @@ static struct cpufreq_driver intel_pstate_driver = { static int __initdata no_load; static int __initdata no_hwp; +static int __initdata hwp_only; static unsigned int force_load; static int intel_pstate_msrs_not_valid(void) @@ -1175,6 +1217,9 @@ static int __init intel_pstate_init(void) if (cpu_has(c,X86_FEATURE_HWP) && !no_hwp) intel_pstate_hwp_enable(); + if (!hwp_active && hwp_only) + goto out; + rc = cpufreq_register_driver(&intel_pstate_driver); if (rc) goto out; @@ -1209,6 +1254,8 @@ static int __init intel_pstate_setup(char *str) no_hwp = 1; if (!strcmp(str, "force")) force_load = 1; + if (!strcmp(str, "hwp_only")) + hwp_only = 1; return 0; } early_param("intel_pstate", intel_pstate_setup); diff --git a/drivers/cpufreq/ls1x-cpufreq.c b/drivers/cpufreq/ls1x-cpufreq.c index 25fbd6a1374f..f0913eee2f50 100644 --- a/drivers/cpufreq/ls1x-cpufreq.c +++ b/drivers/cpufreq/ls1x-cpufreq.c @@ -210,7 +210,6 @@ out: static struct platform_driver ls1x_cpufreq_platdrv = { .driver = { .name = "ls1x-cpufreq", - .owner = THIS_MODULE, }, .probe = ls1x_cpufreq_probe, .remove = ls1x_cpufreq_remove, diff --git a/drivers/cpufreq/sfi-cpufreq.c b/drivers/cpufreq/sfi-cpufreq.c new file mode 100644 index 000000000000..ffa3389e535b --- /dev/null +++ b/drivers/cpufreq/sfi-cpufreq.c @@ -0,0 +1,136 @@ +/* + * SFI Performance States Driver + * + * 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. + * + * 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. + * + * Author: Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com> + * Author: Srinidhi Kasagar <srinidhi.kasagar@intel.com> + */ + +#include <linux/cpufreq.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sfi.h> +#include <linux/slab.h> +#include <linux/smp.h> + +#include <asm/msr.h> + +struct cpufreq_frequency_table *freq_table; +static struct sfi_freq_table_entry *sfi_cpufreq_array; +static int num_freq_table_entries; + +static int sfi_parse_freq(struct sfi_table_header *table) +{ + struct sfi_table_simple *sb; + struct sfi_freq_table_entry *pentry; + int totallen; + + sb = (struct sfi_table_simple *)table; + num_freq_table_entries = SFI_GET_NUM_ENTRIES(sb, + struct sfi_freq_table_entry); + if (num_freq_table_entries <= 1) { + pr_err("No p-states discovered\n"); + return -ENODEV; + } + + pentry = (struct sfi_freq_table_entry *)sb->pentry; + totallen = num_freq_table_entries * sizeof(*pentry); + + sfi_cpufreq_array = kzalloc(totallen, GFP_KERNEL); + if (!sfi_cpufreq_array) + return -ENOMEM; + + memcpy(sfi_cpufreq_array, pentry, totallen); + + return 0; +} + +static int sfi_cpufreq_target(struct cpufreq_policy *policy, unsigned int index) +{ + unsigned int next_perf_state = 0; /* Index into perf table */ + u32 lo, hi; + + next_perf_state = policy->freq_table[index].driver_data; + + rdmsr_on_cpu(policy->cpu, MSR_IA32_PERF_CTL, &lo, &hi); + lo = (lo & ~INTEL_PERF_CTL_MASK) | + ((u32) sfi_cpufreq_array[next_perf_state].ctrl_val & + INTEL_PERF_CTL_MASK); + wrmsr_on_cpu(policy->cpu, MSR_IA32_PERF_CTL, lo, hi); + + return 0; +} + +static int sfi_cpufreq_cpu_init(struct cpufreq_policy *policy) +{ + policy->shared_type = CPUFREQ_SHARED_TYPE_HW; + policy->cpuinfo.transition_latency = 100000; /* 100us */ + + return cpufreq_table_validate_and_show(policy, freq_table); +} + +static struct cpufreq_driver sfi_cpufreq_driver = { + .flags = CPUFREQ_CONST_LOOPS, + .verify = cpufreq_generic_frequency_table_verify, + .target_index = sfi_cpufreq_target, + .init = sfi_cpufreq_cpu_init, + .name = "sfi-cpufreq", + .attr = cpufreq_generic_attr, +}; + +static int __init sfi_cpufreq_init(void) +{ + int ret, i; + + /* parse the freq table from SFI */ + ret = sfi_table_parse(SFI_SIG_FREQ, NULL, NULL, sfi_parse_freq); + if (ret) + return ret; + + freq_table = kzalloc(sizeof(*freq_table) * + (num_freq_table_entries + 1), GFP_KERNEL); + if (!freq_table) { + ret = -ENOMEM; + goto err_free_array; + } + + for (i = 0; i < num_freq_table_entries; i++) { + freq_table[i].driver_data = i; + freq_table[i].frequency = sfi_cpufreq_array[i].freq_mhz * 1000; + } + freq_table[i].frequency = CPUFREQ_TABLE_END; + + ret = cpufreq_register_driver(&sfi_cpufreq_driver); + if (ret) + goto err_free_tbl; + + return ret; + +err_free_tbl: + kfree(freq_table); +err_free_array: + kfree(sfi_cpufreq_array); + return ret; +} +late_initcall(sfi_cpufreq_init); + +static void __exit sfi_cpufreq_exit(void) +{ + cpufreq_unregister_driver(&sfi_cpufreq_driver); + kfree(freq_table); + kfree(sfi_cpufreq_array); +} +module_exit(sfi_cpufreq_exit); + +MODULE_AUTHOR("Vishwesh M Rudramuni <vishwesh.m.rudramuni@intel.com>"); +MODULE_DESCRIPTION("SFI Performance-States Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/cpuidle/cpuidle-big_little.c b/drivers/cpuidle/cpuidle-big_little.c index e3e225fe6b45..40c34faffe59 100644 --- a/drivers/cpuidle/cpuidle-big_little.c +++ b/drivers/cpuidle/cpuidle-big_little.c @@ -182,6 +182,10 @@ static int __init bl_idle_init(void) */ if (!of_match_node(compatible_machine_match, root)) return -ENODEV; + + if (!mcpm_is_available()) + return -EUNATCH; + /* * For now the differentiation between little and big cores * is based on the part number. A7 cores are considered little diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index faf4e70c42e0..64281bb2f650 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -1,5 +1,6 @@ menuconfig PM_DEVFREQ bool "Generic Dynamic Voltage and Frequency Scaling (DVFS) support" + select SRCU help A device may have a list of frequencies and voltages available. devfreq, a generic DVFS framework can be registered for a device @@ -87,4 +88,16 @@ config ARM_EXYNOS5_BUS_DEVFREQ It reads PPMU counters of memory controllers and adjusts the operating frequencies and voltages with OPP support. +config ARM_TEGRA_DEVFREQ + tristate "Tegra DEVFREQ Driver" + depends on ARCH_TEGRA_124_SOC + select DEVFREQ_GOV_SIMPLE_ONDEMAND + select PM_OPP + help + This adds the DEVFREQ driver for the Tegra family of SoCs. + It reads ACTMON counters of memory controllers and adjusts the + operating frequencies and voltages with OPP support. + +source "drivers/devfreq/event/Kconfig" + endif # PM_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 16138c9e0d58..5134f9ee983d 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_PM_DEVFREQ) += devfreq.o +obj-$(CONFIG_PM_DEVFREQ_EVENT) += devfreq-event.o obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) += governor_simpleondemand.o obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o @@ -7,3 +8,7 @@ obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o # DEVFREQ Drivers obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos/ obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos/ +obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra-devfreq.o + +# DEVFREQ Event Drivers +obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/ diff --git a/drivers/devfreq/devfreq-event.c b/drivers/devfreq/devfreq-event.c new file mode 100644 index 000000000000..f304a0289eda --- /dev/null +++ b/drivers/devfreq/devfreq-event.c @@ -0,0 +1,494 @@ +/* + * devfreq-event: a framework to provide raw data and events of devfreq devices + * + * Copyright (C) 2015 Samsung Electronics + * Author: Chanwoo Choi <cw00.choi@samsung.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. + * + * This driver is based on drivers/devfreq/devfreq.c. + */ + +#include <linux/devfreq-event.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/of.h> + +static struct class *devfreq_event_class; + +/* The list of all devfreq event list */ +static LIST_HEAD(devfreq_event_list); +static DEFINE_MUTEX(devfreq_event_list_lock); + +#define to_devfreq_event(DEV) container_of(DEV, struct devfreq_event_dev, dev) + +/** + * devfreq_event_enable_edev() - Enable the devfreq-event dev and increase + * the enable_count of devfreq-event dev. + * @edev : the devfreq-event device + * + * Note that this function increase the enable_count and enable the + * devfreq-event device. The devfreq-event device should be enabled before + * using it by devfreq device. + */ +int devfreq_event_enable_edev(struct devfreq_event_dev *edev) +{ + int ret = 0; + + if (!edev || !edev->desc) + return -EINVAL; + + mutex_lock(&edev->lock); + if (edev->desc->ops && edev->desc->ops->enable + && edev->enable_count == 0) { + ret = edev->desc->ops->enable(edev); + if (ret < 0) + goto err; + } + edev->enable_count++; +err: + mutex_unlock(&edev->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(devfreq_event_enable_edev); + +/** + * devfreq_event_disable_edev() - Disable the devfreq-event dev and decrease + * the enable_count of the devfreq-event dev. + * @edev : the devfreq-event device + * + * Note that this function decrease the enable_count and disable the + * devfreq-event device. After the devfreq-event device is disabled, + * devfreq device can't use the devfreq-event device for get/set/reset + * operations. + */ +int devfreq_event_disable_edev(struct devfreq_event_dev *edev) +{ + int ret = 0; + + if (!edev || !edev->desc) + return -EINVAL; + + mutex_lock(&edev->lock); + if (edev->enable_count <= 0) { + dev_warn(&edev->dev, "unbalanced enable_count\n"); + ret = -EIO; + goto err; + } + + if (edev->desc->ops && edev->desc->ops->disable + && edev->enable_count == 1) { + ret = edev->desc->ops->disable(edev); + if (ret < 0) + goto err; + } + edev->enable_count--; +err: + mutex_unlock(&edev->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(devfreq_event_disable_edev); + +/** + * devfreq_event_is_enabled() - Check whether devfreq-event dev is enabled or + * not. + * @edev : the devfreq-event device + * + * Note that this function check whether devfreq-event dev is enabled or not. + * If return true, the devfreq-event dev is enabeld. If return false, the + * devfreq-event dev is disabled. + */ +bool devfreq_event_is_enabled(struct devfreq_event_dev *edev) +{ + bool enabled = false; + + if (!edev || !edev->desc) + return enabled; + + mutex_lock(&edev->lock); + + if (edev->enable_count > 0) + enabled = true; + + mutex_unlock(&edev->lock); + + return enabled; +} +EXPORT_SYMBOL_GPL(devfreq_event_is_enabled); + +/** + * devfreq_event_set_event() - Set event to devfreq-event dev to start. + * @edev : the devfreq-event device + * + * Note that this function set the event to the devfreq-event device to start + * for getting the event data which could be various event type. + */ +int devfreq_event_set_event(struct devfreq_event_dev *edev) +{ + int ret; + + if (!edev || !edev->desc) + return -EINVAL; + + if (!edev->desc->ops || !edev->desc->ops->set_event) + return -EINVAL; + + if (!devfreq_event_is_enabled(edev)) + return -EPERM; + + mutex_lock(&edev->lock); + ret = edev->desc->ops->set_event(edev); + mutex_unlock(&edev->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(devfreq_event_set_event); + +/** + * devfreq_event_get_event() - Get {load|total}_count from devfreq-event dev. + * @edev : the devfreq-event device + * @edata : the calculated data of devfreq-event device + * + * Note that this function get the calculated event data from devfreq-event dev + * after stoping the progress of whole sequence of devfreq-event dev. + */ +int devfreq_event_get_event(struct devfreq_event_dev *edev, + struct devfreq_event_data *edata) +{ + int ret; + + if (!edev || !edev->desc) + return -EINVAL; + + if (!edev->desc->ops || !edev->desc->ops->get_event) + return -EINVAL; + + if (!devfreq_event_is_enabled(edev)) + return -EINVAL; + + edata->total_count = edata->load_count = 0; + + mutex_lock(&edev->lock); + ret = edev->desc->ops->get_event(edev, edata); + if (ret < 0) + edata->total_count = edata->load_count = 0; + mutex_unlock(&edev->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(devfreq_event_get_event); + +/** + * devfreq_event_reset_event() - Reset all opeations of devfreq-event dev. + * @edev : the devfreq-event device + * + * Note that this function stop all operations of devfreq-event dev and reset + * the current event data to make the devfreq-event device into initial state. + */ +int devfreq_event_reset_event(struct devfreq_event_dev *edev) +{ + int ret = 0; + + if (!edev || !edev->desc) + return -EINVAL; + + if (!devfreq_event_is_enabled(edev)) + return -EPERM; + + mutex_lock(&edev->lock); + if (edev->desc->ops && edev->desc->ops->reset) + ret = edev->desc->ops->reset(edev); + mutex_unlock(&edev->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(devfreq_event_reset_event); + +/** + * devfreq_event_get_edev_by_phandle() - Get the devfreq-event dev from + * devicetree. + * @dev : the pointer to the given device + * @index : the index into list of devfreq-event device + * + * Note that this function return the pointer of devfreq-event device. + */ +struct devfreq_event_dev *devfreq_event_get_edev_by_phandle(struct device *dev, + int index) +{ + struct device_node *node; + struct devfreq_event_dev *edev; + + if (!dev->of_node) { + dev_err(dev, "device does not have a device node entry\n"); + return ERR_PTR(-EINVAL); + } + + node = of_parse_phandle(dev->of_node, "devfreq-events", index); + if (!node) { + dev_err(dev, "failed to get phandle in %s node\n", + dev->of_node->full_name); + return ERR_PTR(-ENODEV); + } + + mutex_lock(&devfreq_event_list_lock); + list_for_each_entry(edev, &devfreq_event_list, node) { + if (!strcmp(edev->desc->name, node->name)) + goto out; + } + edev = NULL; +out: + mutex_unlock(&devfreq_event_list_lock); + + if (!edev) { + dev_err(dev, "unable to get devfreq-event device : %s\n", + node->name); + of_node_put(node); + return ERR_PTR(-ENODEV); + } + + of_node_put(node); + + return edev; +} +EXPORT_SYMBOL_GPL(devfreq_event_get_edev_by_phandle); + +/** + * devfreq_event_get_edev_count() - Get the count of devfreq-event dev + * @dev : the pointer to the given device + * + * Note that this function return the count of devfreq-event devices. + */ +int devfreq_event_get_edev_count(struct device *dev) +{ + int count; + + if (!dev->of_node) { + dev_err(dev, "device does not have a device node entry\n"); + return -EINVAL; + } + + count = of_property_count_elems_of_size(dev->of_node, "devfreq-events", + sizeof(u32)); + if (count < 0 ) { + dev_err(dev, + "failed to get the count of devfreq-event in %s node\n", + dev->of_node->full_name); + return count; + } + + return count; +} +EXPORT_SYMBOL_GPL(devfreq_event_get_edev_count); + +static void devfreq_event_release_edev(struct device *dev) +{ + struct devfreq_event_dev *edev = to_devfreq_event(dev); + + kfree(edev); +} + +/** + * devfreq_event_add_edev() - Add new devfreq-event device. + * @dev : the device owning the devfreq-event device being created + * @desc : the devfreq-event device's decriptor which include essential + * data for devfreq-event device. + * + * Note that this function add new devfreq-event device to devfreq-event class + * list and register the device of the devfreq-event device. + */ +struct devfreq_event_dev *devfreq_event_add_edev(struct device *dev, + struct devfreq_event_desc *desc) +{ + struct devfreq_event_dev *edev; + static atomic_t event_no = ATOMIC_INIT(0); + int ret; + + if (!dev || !desc) + return ERR_PTR(-EINVAL); + + if (!desc->name || !desc->ops) + return ERR_PTR(-EINVAL); + + if (!desc->ops->set_event || !desc->ops->get_event) + return ERR_PTR(-EINVAL); + + edev = kzalloc(sizeof(struct devfreq_event_dev), GFP_KERNEL); + if (!edev) + return ERR_PTR(-ENOMEM); + + mutex_init(&edev->lock); + edev->desc = desc; + edev->enable_count = 0; + edev->dev.parent = dev; + edev->dev.class = devfreq_event_class; + edev->dev.release = devfreq_event_release_edev; + + dev_set_name(&edev->dev, "event.%d", atomic_inc_return(&event_no) - 1); + ret = device_register(&edev->dev); + if (ret < 0) { + put_device(&edev->dev); + return ERR_PTR(ret); + } + dev_set_drvdata(&edev->dev, edev); + + INIT_LIST_HEAD(&edev->node); + + mutex_lock(&devfreq_event_list_lock); + list_add(&edev->node, &devfreq_event_list); + mutex_unlock(&devfreq_event_list_lock); + + return edev; +} +EXPORT_SYMBOL_GPL(devfreq_event_add_edev); + +/** + * devfreq_event_remove_edev() - Remove the devfreq-event device registered. + * @dev : the devfreq-event device + * + * Note that this function remove the registered devfreq-event device. + */ +int devfreq_event_remove_edev(struct devfreq_event_dev *edev) +{ + if (!edev) + return -EINVAL; + + WARN_ON(edev->enable_count); + + mutex_lock(&devfreq_event_list_lock); + list_del(&edev->node); + mutex_unlock(&devfreq_event_list_lock); + + device_unregister(&edev->dev); + + return 0; +} +EXPORT_SYMBOL_GPL(devfreq_event_remove_edev); + +static int devm_devfreq_event_match(struct device *dev, void *res, void *data) +{ + struct devfreq_event_dev **r = res; + + if (WARN_ON(!r || !*r)) + return 0; + + return *r == data; +} + +static void devm_devfreq_event_release(struct device *dev, void *res) +{ + devfreq_event_remove_edev(*(struct devfreq_event_dev **)res); +} + +/** + * devm_devfreq_event_add_edev() - Resource-managed devfreq_event_add_edev() + * @dev : the device owning the devfreq-event device being created + * @desc : the devfreq-event device's decriptor which include essential + * data for devfreq-event device. + * + * Note that this function manages automatically the memory of devfreq-event + * device using device resource management and simplify the free operation + * for memory of devfreq-event device. + */ +struct devfreq_event_dev *devm_devfreq_event_add_edev(struct device *dev, + struct devfreq_event_desc *desc) +{ + struct devfreq_event_dev **ptr, *edev; + + ptr = devres_alloc(devm_devfreq_event_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + edev = devfreq_event_add_edev(dev, desc); + if (IS_ERR(edev)) { + devres_free(ptr); + return ERR_PTR(-ENOMEM); + } + + *ptr = edev; + devres_add(dev, ptr); + + return edev; +} +EXPORT_SYMBOL_GPL(devm_devfreq_event_add_edev); + +/** + * devm_devfreq_event_remove_edev()- Resource-managed devfreq_event_remove_edev() + * @dev : the device owning the devfreq-event device being created + * @edev : the devfreq-event device + * + * Note that this function manages automatically the memory of devfreq-event + * device using device resource management. + */ +void devm_devfreq_event_remove_edev(struct device *dev, + struct devfreq_event_dev *edev) +{ + WARN_ON(devres_release(dev, devm_devfreq_event_release, + devm_devfreq_event_match, edev)); +} +EXPORT_SYMBOL_GPL(devm_devfreq_event_remove_edev); + +/* + * Device attributes for devfreq-event class. + */ +static ssize_t name_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct devfreq_event_dev *edev = to_devfreq_event(dev); + + if (!edev || !edev->desc) + return -EINVAL; + + return sprintf(buf, "%s\n", edev->desc->name); +} +static DEVICE_ATTR_RO(name); + +static ssize_t enable_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct devfreq_event_dev *edev = to_devfreq_event(dev); + + if (!edev || !edev->desc) + return -EINVAL; + + return sprintf(buf, "%d\n", edev->enable_count); +} +static DEVICE_ATTR_RO(enable_count); + +static struct attribute *devfreq_event_attrs[] = { + &dev_attr_name.attr, + &dev_attr_enable_count.attr, + NULL, +}; +ATTRIBUTE_GROUPS(devfreq_event); + +static int __init devfreq_event_init(void) +{ + devfreq_event_class = class_create(THIS_MODULE, "devfreq-event"); + if (IS_ERR(devfreq_event_class)) { + pr_err("%s: couldn't create class\n", __FILE__); + return PTR_ERR(devfreq_event_class); + } + + devfreq_event_class->dev_groups = devfreq_event_groups; + + return 0; +} +subsys_initcall(devfreq_event_init); + +static void __exit devfreq_event_exit(void) +{ + class_destroy(devfreq_event_class); +} +module_exit(devfreq_event_exit); + +MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); +MODULE_DESCRIPTION("DEVFREQ-Event class support"); +MODULE_LICENSE("GPL"); diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig new file mode 100644 index 000000000000..a11720affc31 --- /dev/null +++ b/drivers/devfreq/event/Kconfig @@ -0,0 +1,25 @@ +menuconfig PM_DEVFREQ_EVENT + bool "DEVFREQ-Event device Support" + help + The devfreq-event device provide the raw data and events which + indicate the current state of devfreq-event device. The provided + data from devfreq-event device is used to monitor the state of + device and determine the suitable size of resource to reduce the + wasted resource. + + The devfreq-event device can support the various type of events + (e.g., raw data, utilization, latency, bandwidth). The events + may be used by devfreq governor and other subsystem. + +if PM_DEVFREQ_EVENT + +config DEVFREQ_EVENT_EXYNOS_PPMU + bool "EXYNOS PPMU (Platform Performance Monitoring Unit) DEVFREQ event Driver" + depends on ARCH_EXYNOS + select PM_OPP + help + This add the devfreq-event driver for Exynos SoC. It provides PPMU + (Platform Performance Monitoring Unit) counters to estimate the + utilization of each module. + +endif # PM_DEVFREQ_EVENT diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile new file mode 100644 index 000000000000..be146ead79cf --- /dev/null +++ b/drivers/devfreq/event/Makefile @@ -0,0 +1,2 @@ +# Exynos DEVFREQ Event Drivers +obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o diff --git a/drivers/devfreq/event/exynos-ppmu.c b/drivers/devfreq/event/exynos-ppmu.c new file mode 100644 index 000000000000..135be0aada9d --- /dev/null +++ b/drivers/devfreq/event/exynos-ppmu.c @@ -0,0 +1,374 @@ +/* + * exynos_ppmu.c - EXYNOS PPMU (Platform Performance Monitoring Unit) support + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Author : Chanwoo Choi <cw00.choi@samsung.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. + * + * This driver is based on drivers/devfreq/exynos/exynos_ppmu.c + */ + +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/suspend.h> +#include <linux/devfreq-event.h> + +#include "exynos-ppmu.h" + +struct exynos_ppmu_data { + void __iomem *base; + struct clk *clk; +}; + +struct exynos_ppmu { + struct devfreq_event_dev **edev; + struct devfreq_event_desc *desc; + unsigned int num_events; + + struct device *dev; + struct mutex lock; + + struct exynos_ppmu_data ppmu; +}; + +#define PPMU_EVENT(name) \ + { "ppmu-event0-"#name, PPMU_PMNCNT0 }, \ + { "ppmu-event1-"#name, PPMU_PMNCNT1 }, \ + { "ppmu-event2-"#name, PPMU_PMNCNT2 }, \ + { "ppmu-event3-"#name, PPMU_PMNCNT3 } + +struct __exynos_ppmu_events { + char *name; + int id; +} ppmu_events[] = { + /* For Exynos3250, Exynos4 and Exynos5260 */ + PPMU_EVENT(g3d), + PPMU_EVENT(fsys), + + /* For Exynos4 SoCs and Exynos3250 */ + PPMU_EVENT(dmc0), + PPMU_EVENT(dmc1), + PPMU_EVENT(cpu), + PPMU_EVENT(rightbus), + PPMU_EVENT(leftbus), + PPMU_EVENT(lcd0), + PPMU_EVENT(camif), + + /* Only for Exynos3250 and Exynos5260 */ + PPMU_EVENT(mfc), + + /* Only for Exynos4 SoCs */ + PPMU_EVENT(mfc-left), + PPMU_EVENT(mfc-right), + + /* Only for Exynos5260 SoCs */ + PPMU_EVENT(drex0-s0), + PPMU_EVENT(drex0-s1), + PPMU_EVENT(drex1-s0), + PPMU_EVENT(drex1-s1), + PPMU_EVENT(eagle), + PPMU_EVENT(kfc), + PPMU_EVENT(isp), + PPMU_EVENT(fimc), + PPMU_EVENT(gscl), + PPMU_EVENT(mscl), + PPMU_EVENT(fimd0x), + PPMU_EVENT(fimd1x), + { /* sentinel */ }, +}; + +static int exynos_ppmu_find_ppmu_id(struct devfreq_event_dev *edev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) + if (!strcmp(edev->desc->name, ppmu_events[i].name)) + return ppmu_events[i].id; + + return -EINVAL; +} + +static int exynos_ppmu_disable(struct devfreq_event_dev *edev) +{ + struct exynos_ppmu *info = devfreq_event_get_drvdata(edev); + u32 pmnc; + + /* Disable all counters */ + __raw_writel(PPMU_CCNT_MASK | + PPMU_PMCNT0_MASK | + PPMU_PMCNT1_MASK | + PPMU_PMCNT2_MASK | + PPMU_PMCNT3_MASK, + info->ppmu.base + PPMU_CNTENC); + + /* Disable PPMU */ + pmnc = __raw_readl(info->ppmu.base + PPMU_PMNC); + pmnc &= ~PPMU_PMNC_ENABLE_MASK; + __raw_writel(pmnc, info->ppmu.base + PPMU_PMNC); + + return 0; +} + +static int exynos_ppmu_set_event(struct devfreq_event_dev *edev) +{ + struct exynos_ppmu *info = devfreq_event_get_drvdata(edev); + int id = exynos_ppmu_find_ppmu_id(edev); + u32 pmnc, cntens; + + if (id < 0) + return id; + + /* Enable specific counter */ + cntens = __raw_readl(info->ppmu.base + PPMU_CNTENS); + cntens |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id)); + __raw_writel(cntens, info->ppmu.base + PPMU_CNTENS); + + /* Set the event of Read/Write data count */ + __raw_writel(PPMU_RO_DATA_CNT | PPMU_WO_DATA_CNT, + info->ppmu.base + PPMU_BEVTxSEL(id)); + + /* Reset cycle counter/performance counter and enable PPMU */ + pmnc = __raw_readl(info->ppmu.base + PPMU_PMNC); + pmnc &= ~(PPMU_PMNC_ENABLE_MASK + | PPMU_PMNC_COUNTER_RESET_MASK + | PPMU_PMNC_CC_RESET_MASK); + pmnc |= (PPMU_ENABLE << PPMU_PMNC_ENABLE_SHIFT); + pmnc |= (PPMU_ENABLE << PPMU_PMNC_COUNTER_RESET_SHIFT); + pmnc |= (PPMU_ENABLE << PPMU_PMNC_CC_RESET_SHIFT); + __raw_writel(pmnc, info->ppmu.base + PPMU_PMNC); + + return 0; +} + +static int exynos_ppmu_get_event(struct devfreq_event_dev *edev, + struct devfreq_event_data *edata) +{ + struct exynos_ppmu *info = devfreq_event_get_drvdata(edev); + int id = exynos_ppmu_find_ppmu_id(edev); + u32 pmnc, cntenc; + + if (id < 0) + return -EINVAL; + + /* Disable PPMU */ + pmnc = __raw_readl(info->ppmu.base + PPMU_PMNC); + pmnc &= ~PPMU_PMNC_ENABLE_MASK; + __raw_writel(pmnc, info->ppmu.base + PPMU_PMNC); + + /* Read cycle count */ + edata->total_count = __raw_readl(info->ppmu.base + PPMU_CCNT); + + /* Read performance count */ + switch (id) { + case PPMU_PMNCNT0: + case PPMU_PMNCNT1: + case PPMU_PMNCNT2: + edata->load_count + = __raw_readl(info->ppmu.base + PPMU_PMNCT(id)); + break; + case PPMU_PMNCNT3: + edata->load_count = + ((__raw_readl(info->ppmu.base + PPMU_PMCNT3_HIGH) << 8) + | __raw_readl(info->ppmu.base + PPMU_PMCNT3_LOW)); + break; + default: + return -EINVAL; + } + + /* Disable specific counter */ + cntenc = __raw_readl(info->ppmu.base + PPMU_CNTENC); + cntenc |= (PPMU_CCNT_MASK | (PPMU_ENABLE << id)); + __raw_writel(cntenc, info->ppmu.base + PPMU_CNTENC); + + dev_dbg(&edev->dev, "%s (event: %ld/%ld)\n", edev->desc->name, + edata->load_count, edata->total_count); + + return 0; +} + +static struct devfreq_event_ops exynos_ppmu_ops = { + .disable = exynos_ppmu_disable, + .set_event = exynos_ppmu_set_event, + .get_event = exynos_ppmu_get_event, +}; + +static int of_get_devfreq_events(struct device_node *np, + struct exynos_ppmu *info) +{ + struct devfreq_event_desc *desc; + struct device *dev = info->dev; + struct device_node *events_np, *node; + int i, j, count; + + events_np = of_get_child_by_name(np, "events"); + if (!events_np) { + dev_err(dev, + "failed to get child node of devfreq-event devices\n"); + return -EINVAL; + } + + count = of_get_child_count(events_np); + desc = devm_kzalloc(dev, sizeof(*desc) * count, GFP_KERNEL); + if (!desc) + return -ENOMEM; + info->num_events = count; + + j = 0; + for_each_child_of_node(events_np, node) { + for (i = 0; i < ARRAY_SIZE(ppmu_events); i++) { + if (!ppmu_events[i].name) + continue; + + if (!of_node_cmp(node->name, ppmu_events[i].name)) + break; + } + + if (i == ARRAY_SIZE(ppmu_events)) { + dev_warn(dev, + "don't know how to configure events : %s\n", + node->name); + continue; + } + + desc[j].ops = &exynos_ppmu_ops; + desc[j].driver_data = info; + + of_property_read_string(node, "event-name", &desc[j].name); + + j++; + + of_node_put(node); + } + info->desc = desc; + + of_node_put(events_np); + + return 0; +} + +static int exynos_ppmu_parse_dt(struct exynos_ppmu *info) +{ + struct device *dev = info->dev; + struct device_node *np = dev->of_node; + int ret = 0; + + if (!np) { + dev_err(dev, "failed to find devicetree node\n"); + return -EINVAL; + } + + /* Maps the memory mapped IO to control PPMU register */ + info->ppmu.base = of_iomap(np, 0); + if (IS_ERR_OR_NULL(info->ppmu.base)) { + dev_err(dev, "failed to map memory region\n"); + return -ENOMEM; + } + + info->ppmu.clk = devm_clk_get(dev, "ppmu"); + if (IS_ERR(info->ppmu.clk)) { + info->ppmu.clk = NULL; + dev_warn(dev, "cannot get PPMU clock\n"); + } + + ret = of_get_devfreq_events(np, info); + if (ret < 0) { + dev_err(dev, "failed to parse exynos ppmu dt node\n"); + goto err; + } + + return 0; + +err: + iounmap(info->ppmu.base); + + return ret; +} + +static int exynos_ppmu_probe(struct platform_device *pdev) +{ + struct exynos_ppmu *info; + struct devfreq_event_dev **edev; + struct devfreq_event_desc *desc; + int i, ret = 0, size; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + mutex_init(&info->lock); + info->dev = &pdev->dev; + + /* Parse dt data to get resource */ + ret = exynos_ppmu_parse_dt(info); + if (ret < 0) { + dev_err(&pdev->dev, + "failed to parse devicetree for resource\n"); + return ret; + } + desc = info->desc; + + size = sizeof(struct devfreq_event_dev *) * info->num_events; + info->edev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!info->edev) { + dev_err(&pdev->dev, + "failed to allocate memory devfreq-event devices\n"); + return -ENOMEM; + } + edev = info->edev; + platform_set_drvdata(pdev, info); + + for (i = 0; i < info->num_events; i++) { + edev[i] = devm_devfreq_event_add_edev(&pdev->dev, &desc[i]); + if (IS_ERR(edev)) { + ret = PTR_ERR(edev); + dev_err(&pdev->dev, + "failed to add devfreq-event device\n"); + goto err; + } + } + + clk_prepare_enable(info->ppmu.clk); + + return 0; +err: + iounmap(info->ppmu.base); + + return ret; +} + +static int exynos_ppmu_remove(struct platform_device *pdev) +{ + struct exynos_ppmu *info = platform_get_drvdata(pdev); + + clk_disable_unprepare(info->ppmu.clk); + iounmap(info->ppmu.base); + + return 0; +} + +static struct of_device_id exynos_ppmu_id_match[] = { + { .compatible = "samsung,exynos-ppmu", }, + { /* sentinel */ }, +}; + +static struct platform_driver exynos_ppmu_driver = { + .probe = exynos_ppmu_probe, + .remove = exynos_ppmu_remove, + .driver = { + .name = "exynos-ppmu", + .of_match_table = exynos_ppmu_id_match, + }, +}; +module_platform_driver(exynos_ppmu_driver); + +MODULE_DESCRIPTION("Exynos PPMU(Platform Performance Monitoring Unit) driver"); +MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/devfreq/event/exynos-ppmu.h b/drivers/devfreq/event/exynos-ppmu.h new file mode 100644 index 000000000000..4e831d48c138 --- /dev/null +++ b/drivers/devfreq/event/exynos-ppmu.h @@ -0,0 +1,93 @@ +/* + * exynos_ppmu.h - EXYNOS PPMU header file + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * Author : Chanwoo Choi <cw00.choi@samsung.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. + */ + +#ifndef __EXYNOS_PPMU_H__ +#define __EXYNOS_PPMU_H__ + +enum ppmu_state { + PPMU_DISABLE = 0, + PPMU_ENABLE, +}; + +enum ppmu_counter { + PPMU_PMNCNT0 = 0, + PPMU_PMNCNT1, + PPMU_PMNCNT2, + PPMU_PMNCNT3, + + PPMU_PMNCNT_MAX, +}; + +enum ppmu_event_type { + PPMU_RO_BUSY_CYCLE_CNT = 0x0, + PPMU_WO_BUSY_CYCLE_CNT = 0x1, + PPMU_RW_BUSY_CYCLE_CNT = 0x2, + PPMU_RO_REQUEST_CNT = 0x3, + PPMU_WO_REQUEST_CNT = 0x4, + PPMU_RO_DATA_CNT = 0x5, + PPMU_WO_DATA_CNT = 0x6, + PPMU_RO_LATENCY = 0x12, + PPMU_WO_LATENCY = 0x16, +}; + +enum ppmu_reg { + /* PPC control register */ + PPMU_PMNC = 0x00, + PPMU_CNTENS = 0x10, + PPMU_CNTENC = 0x20, + PPMU_INTENS = 0x30, + PPMU_INTENC = 0x40, + PPMU_FLAG = 0x50, + + /* Cycle Counter and Performance Event Counter Register */ + PPMU_CCNT = 0x100, + PPMU_PMCNT0 = 0x110, + PPMU_PMCNT1 = 0x120, + PPMU_PMCNT2 = 0x130, + PPMU_PMCNT3_HIGH = 0x140, + PPMU_PMCNT3_LOW = 0x150, + + /* Bus Event Generator */ + PPMU_BEVT0SEL = 0x1000, + PPMU_BEVT1SEL = 0x1100, + PPMU_BEVT2SEL = 0x1200, + PPMU_BEVT3SEL = 0x1300, + PPMU_COUNTER_RESET = 0x1810, + PPMU_READ_OVERFLOW_CNT = 0x1810, + PPMU_READ_UNDERFLOW_CNT = 0x1814, + PPMU_WRITE_OVERFLOW_CNT = 0x1850, + PPMU_WRITE_UNDERFLOW_CNT = 0x1854, + PPMU_READ_PENDING_CNT = 0x1880, + PPMU_WRITE_PENDING_CNT = 0x1884 +}; + +/* PMNC register */ +#define PPMU_PMNC_CC_RESET_SHIFT 2 +#define PPMU_PMNC_COUNTER_RESET_SHIFT 1 +#define PPMU_PMNC_ENABLE_SHIFT 0 +#define PPMU_PMNC_START_MODE_MASK BIT(16) +#define PPMU_PMNC_CC_DIVIDER_MASK BIT(3) +#define PPMU_PMNC_CC_RESET_MASK BIT(2) +#define PPMU_PMNC_COUNTER_RESET_MASK BIT(1) +#define PPMU_PMNC_ENABLE_MASK BIT(0) + +/* CNTENS/CNTENC/INTENS/INTENC/FLAG register */ +#define PPMU_CCNT_MASK BIT(31) +#define PPMU_PMCNT3_MASK BIT(3) +#define PPMU_PMCNT2_MASK BIT(2) +#define PPMU_PMCNT1_MASK BIT(1) +#define PPMU_PMCNT0_MASK BIT(0) + +/* PPMU_PMNCTx/PPMU_BETxSEL registers */ +#define PPMU_PMNCT(x) (PPMU_PMCNT0 + (0x10 * x)) +#define PPMU_BEVTxSEL(x) (PPMU_BEVT0SEL + (0x100 * x)) + +#endif /* __EXYNOS_PPMU_H__ */ diff --git a/drivers/devfreq/tegra-devfreq.c b/drivers/devfreq/tegra-devfreq.c new file mode 100644 index 000000000000..34790961af5a --- /dev/null +++ b/drivers/devfreq/tegra-devfreq.c @@ -0,0 +1,718 @@ +/* + * A devfreq driver for NVIDIA Tegra SoCs + * + * Copyright (c) 2014 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2014 Google, Inc + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <linux/clk.h> +#include <linux/cpufreq.h> +#include <linux/devfreq.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_opp.h> +#include <linux/reset.h> + +#include "governor.h" + +#define ACTMON_GLB_STATUS 0x0 +#define ACTMON_GLB_PERIOD_CTRL 0x4 + +#define ACTMON_DEV_CTRL 0x0 +#define ACTMON_DEV_CTRL_K_VAL_SHIFT 10 +#define ACTMON_DEV_CTRL_ENB_PERIODIC BIT(18) +#define ACTMON_DEV_CTRL_AVG_BELOW_WMARK_EN BIT(20) +#define ACTMON_DEV_CTRL_AVG_ABOVE_WMARK_EN BIT(21) +#define ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_NUM_SHIFT 23 +#define ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_NUM_SHIFT 26 +#define ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN BIT(29) +#define ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN BIT(30) +#define ACTMON_DEV_CTRL_ENB BIT(31) + +#define ACTMON_DEV_UPPER_WMARK 0x4 +#define ACTMON_DEV_LOWER_WMARK 0x8 +#define ACTMON_DEV_INIT_AVG 0xc +#define ACTMON_DEV_AVG_UPPER_WMARK 0x10 +#define ACTMON_DEV_AVG_LOWER_WMARK 0x14 +#define ACTMON_DEV_COUNT_WEIGHT 0x18 +#define ACTMON_DEV_AVG_COUNT 0x20 +#define ACTMON_DEV_INTR_STATUS 0x24 + +#define ACTMON_INTR_STATUS_CLEAR 0xffffffff + +#define ACTMON_DEV_INTR_CONSECUTIVE_UPPER BIT(31) +#define ACTMON_DEV_INTR_CONSECUTIVE_LOWER BIT(30) + +#define ACTMON_ABOVE_WMARK_WINDOW 1 +#define ACTMON_BELOW_WMARK_WINDOW 3 +#define ACTMON_BOOST_FREQ_STEP 16000 + +/* activity counter is incremented every 256 memory transactions, and each + * transaction takes 4 EMC clocks for Tegra124; So the COUNT_WEIGHT is + * 4 * 256 = 1024. + */ +#define ACTMON_COUNT_WEIGHT 0x400 + +/* + * ACTMON_AVERAGE_WINDOW_LOG2: default value for @DEV_CTRL_K_VAL, which + * translates to 2 ^ (K_VAL + 1). ex: 2 ^ (6 + 1) = 128 + */ +#define ACTMON_AVERAGE_WINDOW_LOG2 6 +#define ACTMON_SAMPLING_PERIOD 12 /* ms */ +#define ACTMON_DEFAULT_AVG_BAND 6 /* 1/10 of % */ + +#define KHZ 1000 + +/* Assume that the bus is saturated if the utilization is 25% */ +#define BUS_SATURATION_RATIO 25 + +/** + * struct tegra_devfreq_device_config - configuration specific to an ACTMON + * device + * + * Coefficients and thresholds are in % + */ +struct tegra_devfreq_device_config { + u32 offset; + u32 irq_mask; + + unsigned int boost_up_coeff; + unsigned int boost_down_coeff; + unsigned int boost_up_threshold; + unsigned int boost_down_threshold; + u32 avg_dependency_threshold; +}; + +enum tegra_actmon_device { + MCALL = 0, + MCCPU, +}; + +static struct tegra_devfreq_device_config actmon_device_configs[] = { + { + /* MCALL */ + .offset = 0x1c0, + .irq_mask = 1 << 26, + .boost_up_coeff = 200, + .boost_down_coeff = 50, + .boost_up_threshold = 60, + .boost_down_threshold = 40, + }, + { + /* MCCPU */ + .offset = 0x200, + .irq_mask = 1 << 25, + .boost_up_coeff = 800, + .boost_down_coeff = 90, + .boost_up_threshold = 27, + .boost_down_threshold = 10, + .avg_dependency_threshold = 50000, + }, +}; + +/** + * struct tegra_devfreq_device - state specific to an ACTMON device + * + * Frequencies are in kHz. + */ +struct tegra_devfreq_device { + const struct tegra_devfreq_device_config *config; + + void __iomem *regs; + u32 avg_band_freq; + u32 avg_count; + + unsigned long target_freq; + unsigned long boost_freq; +}; + +struct tegra_devfreq { + struct devfreq *devfreq; + + struct platform_device *pdev; + struct reset_control *reset; + struct clk *clock; + void __iomem *regs; + + spinlock_t lock; + + struct clk *emc_clock; + unsigned long max_freq; + unsigned long cur_freq; + struct notifier_block rate_change_nb; + + struct tegra_devfreq_device devices[ARRAY_SIZE(actmon_device_configs)]; +}; + +struct tegra_actmon_emc_ratio { + unsigned long cpu_freq; + unsigned long emc_freq; +}; + +static struct tegra_actmon_emc_ratio actmon_emc_ratios[] = { + { 1400000, ULONG_MAX }, + { 1200000, 750000 }, + { 1100000, 600000 }, + { 1000000, 500000 }, + { 800000, 375000 }, + { 500000, 200000 }, + { 250000, 100000 }, +}; + +static unsigned long do_percent(unsigned long val, unsigned int pct) +{ + return val * pct / 100; +} + +static void tegra_devfreq_update_avg_wmark(struct tegra_devfreq_device *dev) +{ + u32 avg = dev->avg_count; + u32 band = dev->avg_band_freq * ACTMON_SAMPLING_PERIOD; + + writel(avg + band, dev->regs + ACTMON_DEV_AVG_UPPER_WMARK); + avg = max(avg, band); + writel(avg - band, dev->regs + ACTMON_DEV_AVG_LOWER_WMARK); +} + +static void tegra_devfreq_update_wmark(struct tegra_devfreq *tegra, + struct tegra_devfreq_device *dev) +{ + u32 val = tegra->cur_freq * ACTMON_SAMPLING_PERIOD; + + writel(do_percent(val, dev->config->boost_up_threshold), + dev->regs + ACTMON_DEV_UPPER_WMARK); + + writel(do_percent(val, dev->config->boost_down_threshold), + dev->regs + ACTMON_DEV_LOWER_WMARK); +} + +static void actmon_write_barrier(struct tegra_devfreq *tegra) +{ + /* ensure the update has reached the ACTMON */ + wmb(); + readl(tegra->regs + ACTMON_GLB_STATUS); +} + +static irqreturn_t actmon_isr(int irq, void *data) +{ + struct tegra_devfreq *tegra = data; + struct tegra_devfreq_device *dev = NULL; + unsigned long flags; + u32 val; + unsigned int i; + + val = readl(tegra->regs + ACTMON_GLB_STATUS); + + for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) { + if (val & tegra->devices[i].config->irq_mask) { + dev = tegra->devices + i; + break; + } + } + + if (!dev) + return IRQ_NONE; + + spin_lock_irqsave(&tegra->lock, flags); + + dev->avg_count = readl(dev->regs + ACTMON_DEV_AVG_COUNT); + tegra_devfreq_update_avg_wmark(dev); + + val = readl(dev->regs + ACTMON_DEV_INTR_STATUS); + if (val & ACTMON_DEV_INTR_CONSECUTIVE_UPPER) { + val = readl(dev->regs + ACTMON_DEV_CTRL) | + ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN | + ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; + + /* + * new_boost = min(old_boost * up_coef + step, max_freq) + */ + dev->boost_freq = do_percent(dev->boost_freq, + dev->config->boost_up_coeff); + dev->boost_freq += ACTMON_BOOST_FREQ_STEP; + if (dev->boost_freq >= tegra->max_freq) { + dev->boost_freq = tegra->max_freq; + val &= ~ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN; + } + writel(val, dev->regs + ACTMON_DEV_CTRL); + } else if (val & ACTMON_DEV_INTR_CONSECUTIVE_LOWER) { + val = readl(dev->regs + ACTMON_DEV_CTRL) | + ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN | + ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; + + /* + * new_boost = old_boost * down_coef + * or 0 if (old_boost * down_coef < step / 2) + */ + dev->boost_freq = do_percent(dev->boost_freq, + dev->config->boost_down_coeff); + if (dev->boost_freq < (ACTMON_BOOST_FREQ_STEP >> 1)) { + dev->boost_freq = 0; + val &= ~ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; + } + writel(val, dev->regs + ACTMON_DEV_CTRL); + } + + if (dev->config->avg_dependency_threshold) { + val = readl(dev->regs + ACTMON_DEV_CTRL); + if (dev->avg_count >= dev->config->avg_dependency_threshold) + val |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; + else if (dev->boost_freq == 0) + val &= ~ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN; + writel(val, dev->regs + ACTMON_DEV_CTRL); + } + + writel(ACTMON_INTR_STATUS_CLEAR, dev->regs + ACTMON_DEV_INTR_STATUS); + + actmon_write_barrier(tegra); + + spin_unlock_irqrestore(&tegra->lock, flags); + + return IRQ_WAKE_THREAD; +} + +static unsigned long actmon_cpu_to_emc_rate(struct tegra_devfreq *tegra, + unsigned long cpu_freq) +{ + unsigned int i; + struct tegra_actmon_emc_ratio *ratio = actmon_emc_ratios; + + for (i = 0; i < ARRAY_SIZE(actmon_emc_ratios); i++, ratio++) { + if (cpu_freq >= ratio->cpu_freq) { + if (ratio->emc_freq >= tegra->max_freq) + return tegra->max_freq; + else + return ratio->emc_freq; + } + } + + return 0; +} + +static void actmon_update_target(struct tegra_devfreq *tegra, + struct tegra_devfreq_device *dev) +{ + unsigned long cpu_freq = 0; + unsigned long static_cpu_emc_freq = 0; + unsigned int avg_sustain_coef; + unsigned long flags; + + if (dev->config->avg_dependency_threshold) { + cpu_freq = cpufreq_get(0); + static_cpu_emc_freq = actmon_cpu_to_emc_rate(tegra, cpu_freq); + } + + spin_lock_irqsave(&tegra->lock, flags); + + dev->target_freq = dev->avg_count / ACTMON_SAMPLING_PERIOD; + avg_sustain_coef = 100 * 100 / dev->config->boost_up_threshold; + dev->target_freq = do_percent(dev->target_freq, avg_sustain_coef); + dev->target_freq += dev->boost_freq; + + if (dev->avg_count >= dev->config->avg_dependency_threshold) + dev->target_freq = max(dev->target_freq, static_cpu_emc_freq); + + spin_unlock_irqrestore(&tegra->lock, flags); +} + +static irqreturn_t actmon_thread_isr(int irq, void *data) +{ + struct tegra_devfreq *tegra = data; + + mutex_lock(&tegra->devfreq->lock); + update_devfreq(tegra->devfreq); + mutex_unlock(&tegra->devfreq->lock); + + return IRQ_HANDLED; +} + +static int tegra_actmon_rate_notify_cb(struct notifier_block *nb, + unsigned long action, void *ptr) +{ + struct clk_notifier_data *data = ptr; + struct tegra_devfreq *tegra = container_of(nb, struct tegra_devfreq, + rate_change_nb); + unsigned int i; + unsigned long flags; + + spin_lock_irqsave(&tegra->lock, flags); + + switch (action) { + case POST_RATE_CHANGE: + tegra->cur_freq = data->new_rate / KHZ; + + for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) + tegra_devfreq_update_wmark(tegra, tegra->devices + i); + + actmon_write_barrier(tegra); + break; + case PRE_RATE_CHANGE: + /* fall through */ + case ABORT_RATE_CHANGE: + break; + }; + + spin_unlock_irqrestore(&tegra->lock, flags); + + return NOTIFY_OK; +} + +static void tegra_actmon_configure_device(struct tegra_devfreq *tegra, + struct tegra_devfreq_device *dev) +{ + u32 val; + + dev->avg_band_freq = tegra->max_freq * ACTMON_DEFAULT_AVG_BAND / KHZ; + dev->target_freq = tegra->cur_freq; + + dev->avg_count = tegra->cur_freq * ACTMON_SAMPLING_PERIOD; + writel(dev->avg_count, dev->regs + ACTMON_DEV_INIT_AVG); + + tegra_devfreq_update_avg_wmark(dev); + tegra_devfreq_update_wmark(tegra, dev); + + writel(ACTMON_COUNT_WEIGHT, dev->regs + ACTMON_DEV_COUNT_WEIGHT); + writel(ACTMON_INTR_STATUS_CLEAR, dev->regs + ACTMON_DEV_INTR_STATUS); + + val = 0; + val |= ACTMON_DEV_CTRL_ENB_PERIODIC | + ACTMON_DEV_CTRL_AVG_ABOVE_WMARK_EN | + ACTMON_DEV_CTRL_AVG_BELOW_WMARK_EN; + val |= (ACTMON_AVERAGE_WINDOW_LOG2 - 1) + << ACTMON_DEV_CTRL_K_VAL_SHIFT; + val |= (ACTMON_BELOW_WMARK_WINDOW - 1) + << ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_NUM_SHIFT; + val |= (ACTMON_ABOVE_WMARK_WINDOW - 1) + << ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_NUM_SHIFT; + val |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN | + ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN; + + writel(val, dev->regs + ACTMON_DEV_CTRL); + + actmon_write_barrier(tegra); + + val = readl(dev->regs + ACTMON_DEV_CTRL); + val |= ACTMON_DEV_CTRL_ENB; + writel(val, dev->regs + ACTMON_DEV_CTRL); + + actmon_write_barrier(tegra); +} + +static int tegra_devfreq_suspend(struct device *dev) +{ + struct platform_device *pdev; + struct tegra_devfreq *tegra; + struct tegra_devfreq_device *actmon_dev; + unsigned int i; + u32 val; + + pdev = container_of(dev, struct platform_device, dev); + tegra = platform_get_drvdata(pdev); + + for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) { + actmon_dev = &tegra->devices[i]; + + val = readl(actmon_dev->regs + ACTMON_DEV_CTRL); + val &= ~ACTMON_DEV_CTRL_ENB; + writel(val, actmon_dev->regs + ACTMON_DEV_CTRL); + + writel(ACTMON_INTR_STATUS_CLEAR, + actmon_dev->regs + ACTMON_DEV_INTR_STATUS); + + actmon_write_barrier(tegra); + } + + return 0; +} + +static int tegra_devfreq_resume(struct device *dev) +{ + struct platform_device *pdev; + struct tegra_devfreq *tegra; + struct tegra_devfreq_device *actmon_dev; + unsigned int i; + + pdev = container_of(dev, struct platform_device, dev); + tegra = platform_get_drvdata(pdev); + + for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) { + actmon_dev = &tegra->devices[i]; + + tegra_actmon_configure_device(tegra, actmon_dev); + } + + return 0; +} + +static int tegra_devfreq_target(struct device *dev, unsigned long *freq, + u32 flags) +{ + struct platform_device *pdev; + struct tegra_devfreq *tegra; + struct dev_pm_opp *opp; + unsigned long rate = *freq * KHZ; + + pdev = container_of(dev, struct platform_device, dev); + tegra = platform_get_drvdata(pdev); + + rcu_read_lock(); + opp = devfreq_recommended_opp(dev, &rate, flags); + if (IS_ERR(opp)) { + rcu_read_unlock(); + dev_err(dev, "Failed to find opp for %lu KHz\n", *freq); + return PTR_ERR(opp); + } + rate = dev_pm_opp_get_freq(opp); + rcu_read_unlock(); + + /* TODO: Once we have per-user clk constraints, set a floor */ + clk_set_rate(tegra->emc_clock, rate); + + /* TODO: Set voltage as well */ + + return 0; +} + +static int tegra_devfreq_get_dev_status(struct device *dev, + struct devfreq_dev_status *stat) +{ + struct platform_device *pdev; + struct tegra_devfreq *tegra; + struct tegra_devfreq_device *actmon_dev; + + pdev = container_of(dev, struct platform_device, dev); + tegra = platform_get_drvdata(pdev); + + stat->current_frequency = tegra->cur_freq; + + /* To be used by the tegra governor */ + stat->private_data = tegra; + + /* The below are to be used by the other governors */ + + actmon_dev = &tegra->devices[MCALL]; + + /* Number of cycles spent on memory access */ + stat->busy_time = actmon_dev->avg_count; + + /* The bus can be considered to be saturated way before 100% */ + stat->busy_time *= 100 / BUS_SATURATION_RATIO; + + /* Number of cycles in a sampling period */ + stat->total_time = ACTMON_SAMPLING_PERIOD * tegra->cur_freq; + + return 0; +} + +static int tegra_devfreq_get_target(struct devfreq *devfreq, + unsigned long *freq) +{ + 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); + if (err) + return err; + + tegra = stat.private_data; + + for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) { + dev = &tegra->devices[i]; + + actmon_update_target(tegra, dev); + + target_freq = max(target_freq, dev->target_freq); + } + + *freq = target_freq; + + return 0; +} + +static int tegra_devfreq_event_handler(struct devfreq *devfreq, + unsigned int event, void *data) +{ + return 0; +} + +static struct devfreq_governor tegra_devfreq_governor = { + .name = "tegra", + .get_target_freq = tegra_devfreq_get_target, + .event_handler = tegra_devfreq_event_handler, +}; + +static struct devfreq_dev_profile tegra_devfreq_profile = { + .polling_ms = 0, + .target = tegra_devfreq_target, + .get_dev_status = tegra_devfreq_get_dev_status, +}; + +static int tegra_devfreq_probe(struct platform_device *pdev) +{ + struct tegra_devfreq *tegra; + struct tegra_devfreq_device *dev; + struct resource *res; + unsigned long max_freq; + unsigned int i; + int irq; + int err; + + tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL); + if (!tegra) + return -ENOMEM; + + spin_lock_init(&tegra->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Failed to get regs resource\n"); + return -ENODEV; + } + + tegra->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(tegra->regs)) { + dev_err(&pdev->dev, "Failed to get IO memory\n"); + return PTR_ERR(tegra->regs); + } + + tegra->reset = devm_reset_control_get(&pdev->dev, "actmon"); + if (IS_ERR(tegra->reset)) { + dev_err(&pdev->dev, "Failed to get reset\n"); + return PTR_ERR(tegra->reset); + } + + tegra->clock = devm_clk_get(&pdev->dev, "actmon"); + if (IS_ERR(tegra->clock)) { + dev_err(&pdev->dev, "Failed to get actmon clock\n"); + return PTR_ERR(tegra->clock); + } + + tegra->emc_clock = devm_clk_get(&pdev->dev, "emc"); + if (IS_ERR(tegra->emc_clock)) { + dev_err(&pdev->dev, "Failed to get emc clock\n"); + return PTR_ERR(tegra->emc_clock); + } + + err = of_init_opp_table(&pdev->dev); + if (err) { + dev_err(&pdev->dev, "Failed to init operating point table\n"); + return err; + } + + tegra->rate_change_nb.notifier_call = tegra_actmon_rate_notify_cb; + err = clk_notifier_register(tegra->emc_clock, &tegra->rate_change_nb); + if (err) { + dev_err(&pdev->dev, + "Failed to register rate change notifier\n"); + return err; + } + + reset_control_assert(tegra->reset); + + err = clk_prepare_enable(tegra->clock); + if (err) { + reset_control_deassert(tegra->reset); + return err; + } + + reset_control_deassert(tegra->reset); + + max_freq = clk_round_rate(tegra->emc_clock, ULONG_MAX); + tegra->max_freq = max_freq / KHZ; + + clk_set_rate(tegra->emc_clock, max_freq); + + tegra->cur_freq = clk_get_rate(tegra->emc_clock) / KHZ; + + writel(ACTMON_SAMPLING_PERIOD - 1, + tegra->regs + ACTMON_GLB_PERIOD_CTRL); + + for (i = 0; i < ARRAY_SIZE(actmon_device_configs); i++) { + dev = tegra->devices + i; + dev->config = actmon_device_configs + i; + dev->regs = tegra->regs + dev->config->offset; + + tegra_actmon_configure_device(tegra, tegra->devices + i); + } + + err = devfreq_add_governor(&tegra_devfreq_governor); + if (err) { + dev_err(&pdev->dev, "Failed to add governor\n"); + return err; + } + + tegra_devfreq_profile.initial_freq = clk_get_rate(tegra->emc_clock); + tegra->devfreq = devm_devfreq_add_device(&pdev->dev, + &tegra_devfreq_profile, + "tegra", + NULL); + + irq = platform_get_irq(pdev, 0); + err = devm_request_threaded_irq(&pdev->dev, irq, actmon_isr, + actmon_thread_isr, IRQF_SHARED, + "tegra-devfreq", tegra); + if (err) { + dev_err(&pdev->dev, "Interrupt request failed\n"); + return err; + } + + platform_set_drvdata(pdev, tegra); + + return 0; +} + +static int tegra_devfreq_remove(struct platform_device *pdev) +{ + struct tegra_devfreq *tegra = platform_get_drvdata(pdev); + + clk_notifier_unregister(tegra->emc_clock, &tegra->rate_change_nb); + + clk_disable_unprepare(tegra->clock); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(tegra_devfreq_pm_ops, + tegra_devfreq_suspend, + tegra_devfreq_resume); + +static struct of_device_id tegra_devfreq_of_match[] = { + { .compatible = "nvidia,tegra124-actmon" }, + { }, +}; + +static struct platform_driver tegra_devfreq_driver = { + .probe = tegra_devfreq_probe, + .remove = tegra_devfreq_remove, + .driver = { + .name = "tegra-devfreq", + .owner = THIS_MODULE, + .of_match_table = tegra_devfreq_of_match, + .pm = &tegra_devfreq_pm_ops, + }, +}; +module_platform_driver(tegra_devfreq_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Tegra devfreq driver"); +MODULE_AUTHOR("Tomeu Vizoso <tomeu.vizoso@collabora.com>"); +MODULE_DEVICE_TABLE(of, tegra_devfreq_of_match); diff --git a/drivers/dma/acpi-dma.c b/drivers/dma/acpi-dma.c index de361a156b34..5a635646e05c 100644 --- a/drivers/dma/acpi-dma.c +++ b/drivers/dma/acpi-dma.c @@ -43,7 +43,7 @@ static int acpi_dma_parse_resource_group(const struct acpi_csrt_group *grp, { const struct acpi_csrt_shared_info *si; struct list_head resource_list; - struct resource_list_entry *rentry; + struct resource_entry *rentry; resource_size_t mem = 0, irq = 0; int ret; @@ -56,10 +56,10 @@ static int acpi_dma_parse_resource_group(const struct acpi_csrt_group *grp, return 0; list_for_each_entry(rentry, &resource_list, node) { - if (resource_type(&rentry->res) == IORESOURCE_MEM) - mem = rentry->res.start; - else if (resource_type(&rentry->res) == IORESOURCE_IRQ) - irq = rentry->res.start; + if (resource_type(rentry->res) == IORESOURCE_MEM) + mem = rentry->res->start; + else if (resource_type(rentry->res) == IORESOURCE_IRQ) + irq = rentry->res->start; } acpi_dev_free_resource_list(&resource_list); diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index 380478562b7d..5c062548957c 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -1505,7 +1505,6 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) dw->regs = chip->regs; chip->dw = dw; - pm_runtime_enable(chip->dev); pm_runtime_get_sync(chip->dev); dw_params = dma_read_byaddr(chip->regs, DW_PARAMS); @@ -1703,7 +1702,6 @@ int dw_dma_remove(struct dw_dma_chip *chip) } pm_runtime_put_sync_suspend(chip->dev); - pm_runtime_disable(chip->dev); return 0; } EXPORT_SYMBOL_GPL(dw_dma_remove); diff --git a/drivers/dma/dw/platform.c b/drivers/dma/dw/platform.c index a630161473a4..32ea1aca7a0e 100644 --- a/drivers/dma/dw/platform.c +++ b/drivers/dma/dw/platform.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/device.h> #include <linux/clk.h> +#include <linux/pm_runtime.h> #include <linux/platform_device.h> #include <linux/dmaengine.h> #include <linux/dma-mapping.h> @@ -185,6 +186,8 @@ static int dw_probe(struct platform_device *pdev) if (err) return err; + pm_runtime_enable(&pdev->dev); + err = dw_dma_probe(chip, pdata); if (err) goto err_dw_dma_probe; @@ -205,6 +208,7 @@ static int dw_probe(struct platform_device *pdev) return 0; err_dw_dma_probe: + pm_runtime_disable(&pdev->dev); clk_disable_unprepare(chip->clk); return err; } @@ -217,6 +221,7 @@ static int dw_remove(struct platform_device *pdev) of_dma_controller_free(pdev->dev.of_node); dw_dma_remove(chip); + pm_runtime_disable(&pdev->dev); clk_disable_unprepare(chip->clk); return 0; diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 49c265255a07..cb59619df23f 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -385,4 +385,11 @@ config EDAC_ALTERA_MC preloader must initialize the SDRAM before loading the kernel. +config EDAC_SYNOPSYS + tristate "Synopsys DDR Memory Controller" + depends on EDAC_MM_EDAC && ARCH_ZYNQ + help + Support for error detection and correction on the Synopsys DDR + memory controller. + endif # EDAC diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index d40c69a04df7..b255f362b1db 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -67,3 +67,4 @@ obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o obj-$(CONFIG_EDAC_ALTERA_MC) += altera_edac.o +obj-$(CONFIG_EDAC_SYNOPSYS) += synopsys_edac.o diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 670d2829c547..c84eecb191ef 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c @@ -157,7 +157,7 @@ struct dev_ch_attribute { }; #define DEVICE_CHANNEL(_name, _mode, _show, _store, _var) \ - struct dev_ch_attribute dev_attr_legacy_##_name = \ + static struct dev_ch_attribute dev_attr_legacy_##_name = \ { __ATTR(_name, _mode, _show, _store), (_var) } #define to_channel(k) (container_of(k, struct dev_ch_attribute, attr)->channel) @@ -850,20 +850,20 @@ static const struct file_operations debug_fake_inject_fops = { #endif /* default Control file */ -DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store); +static DEVICE_ATTR(reset_counters, S_IWUSR, NULL, mci_reset_counters_store); /* default Attribute files */ -DEVICE_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL); -DEVICE_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL); -DEVICE_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL); -DEVICE_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL); -DEVICE_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL); -DEVICE_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL); -DEVICE_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL); -DEVICE_ATTR(max_location, S_IRUGO, mci_max_location_show, NULL); +static DEVICE_ATTR(mc_name, S_IRUGO, mci_ctl_name_show, NULL); +static DEVICE_ATTR(size_mb, S_IRUGO, mci_size_mb_show, NULL); +static DEVICE_ATTR(seconds_since_reset, S_IRUGO, mci_seconds_show, NULL); +static DEVICE_ATTR(ue_noinfo_count, S_IRUGO, mci_ue_noinfo_show, NULL); +static DEVICE_ATTR(ce_noinfo_count, S_IRUGO, mci_ce_noinfo_show, NULL); +static DEVICE_ATTR(ue_count, S_IRUGO, mci_ue_count_show, NULL); +static DEVICE_ATTR(ce_count, S_IRUGO, mci_ce_count_show, NULL); +static DEVICE_ATTR(max_location, S_IRUGO, mci_max_location_show, NULL); /* memory scrubber attribute file */ -DEVICE_ATTR(sdram_scrub_rate, 0, NULL, NULL); +static DEVICE_ATTR(sdram_scrub_rate, 0, NULL, NULL); static struct attribute *mci_attrs[] = { &dev_attr_reset_counters.attr, @@ -989,7 +989,7 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) err = bus_register(mci->bus); if (err < 0) - return err; + goto fail_free_name; /* get the /sys/devices/system/edac subsys reference */ mci->dev.type = &mci_attr_type; @@ -1005,9 +1005,7 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) err = device_add(&mci->dev); if (err < 0) { edac_dbg(1, "failure: create device %s\n", dev_name(&mci->dev)); - bus_unregister(mci->bus); - kfree(mci->bus->name); - return err; + goto fail_unregister_bus; } if (mci->set_sdram_scrub_rate || mci->get_sdram_scrub_rate) { @@ -1015,15 +1013,16 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) dev_attr_sdram_scrub_rate.attr.mode |= S_IRUGO; dev_attr_sdram_scrub_rate.show = &mci_sdram_scrub_rate_show; } + if (mci->set_sdram_scrub_rate) { dev_attr_sdram_scrub_rate.attr.mode |= S_IWUSR; dev_attr_sdram_scrub_rate.store = &mci_sdram_scrub_rate_store; } - err = device_create_file(&mci->dev, - &dev_attr_sdram_scrub_rate); + + err = device_create_file(&mci->dev, &dev_attr_sdram_scrub_rate); if (err) { edac_dbg(1, "failure: create sdram_scrub_rate\n"); - goto fail2; + goto fail_unregister_dev; } } /* @@ -1032,8 +1031,9 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) for (i = 0; i < mci->tot_dimms; i++) { struct dimm_info *dimm = mci->dimms[i]; /* Only expose populated DIMMs */ - if (dimm->nr_pages == 0) + if (!dimm->nr_pages) continue; + #ifdef CONFIG_EDAC_DEBUG edac_dbg(1, "creating dimm%d, located at ", i); if (edac_debug_level >= 1) { @@ -1048,14 +1048,14 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) err = edac_create_dimm_object(mci, dimm, i); if (err) { edac_dbg(1, "failure: create dimm %d obj\n", i); - goto fail; + goto fail_unregister_dimm; } } #ifdef CONFIG_EDAC_LEGACY_SYSFS err = edac_create_csrow_objects(mci); if (err < 0) - goto fail; + goto fail_unregister_dimm; #endif #ifdef CONFIG_EDAC_DEBUG @@ -1063,16 +1063,19 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) #endif return 0; -fail: +fail_unregister_dimm: for (i--; i >= 0; i--) { struct dimm_info *dimm = mci->dimms[i]; - if (dimm->nr_pages == 0) + if (!dimm->nr_pages) continue; + device_unregister(&dimm->dev); } -fail2: +fail_unregister_dev: device_unregister(&mci->dev); +fail_unregister_bus: bus_unregister(mci->bus); +fail_free_name: kfree(mci->bus->name); return err; } diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c index 6247d186177e..e9f8a393915a 100644 --- a/drivers/edac/i5100_edac.c +++ b/drivers/edac/i5100_edac.c @@ -279,11 +279,6 @@ static inline u32 i5100_recmema_rank(u32 a) return i5100_nrecmema_rank(a); } -static inline u32 i5100_recmema_dm_buf_id(u32 a) -{ - return i5100_nrecmema_dm_buf_id(a); -} - static inline u32 i5100_recmemb_cas(u32 a) { return i5100_nrecmemb_cas(a); diff --git a/drivers/edac/mce_amd_inj.c b/drivers/edac/mce_amd_inj.c index 0bd91a802c67..f7681b553fd5 100644 --- a/drivers/edac/mce_amd_inj.c +++ b/drivers/edac/mce_amd_inj.c @@ -197,7 +197,7 @@ static int inj_bank_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(bank_fops, inj_bank_get, inj_bank_set, "%llu\n"); -struct dfs_node { +static struct dfs_node { char *name; struct dentry *d; const struct file_operations *fops; diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c index ffb1a9a15ccd..1fa76a588af3 100644 --- a/drivers/edac/mpc85xx_edac.c +++ b/drivers/edac/mpc85xx_edac.c @@ -1,5 +1,5 @@ /* - * Freescale MPC85xx Memory Controller kenel module + * Freescale MPC85xx Memory Controller kernel module * * Parts Copyrighted (c) 2013 by Freescale Semiconductor, Inc. * diff --git a/drivers/edac/mpc85xx_edac.h b/drivers/edac/mpc85xx_edac.h index 8c6256436227..4498baf9ce05 100644 --- a/drivers/edac/mpc85xx_edac.h +++ b/drivers/edac/mpc85xx_edac.h @@ -1,5 +1,5 @@ /* - * Freescale MPC85xx Memory Controller kenel module + * Freescale MPC85xx Memory Controller kernel module * Author: Dave Jiang <djiang@mvista.com> * * 2006-2007 (c) MontaVista Software, Inc. This file is licensed under diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c index 6366e880f978..0574e1bbe45c 100644 --- a/drivers/edac/mv64x60_edac.c +++ b/drivers/edac/mv64x60_edac.c @@ -789,7 +789,8 @@ static int mv64x60_mc_err_probe(struct platform_device *pdev) ctl = (ctl & 0xff00ffff) | 0x10000; out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CNTL, ctl); - if (edac_mc_add_mc(mci)) { + res = edac_mc_add_mc(mci); + if (res) { edac_dbg(3, "failed edac_mc_add_mc()\n"); goto err; } diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c new file mode 100644 index 000000000000..1c9691535e13 --- /dev/null +++ b/drivers/edac/synopsys_edac.c @@ -0,0 +1,535 @@ +/* + * Synopsys DDR ECC Driver + * This driver is based on ppc4xx_edac.c drivers + * + * Copyright (C) 2012 - 2014 Xilinx, Inc. + * + * 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. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details + */ + +#include <linux/edac.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include "edac_core.h" + +/* Number of cs_rows needed per memory controller */ +#define SYNPS_EDAC_NR_CSROWS 1 + +/* Number of channels per memory controller */ +#define SYNPS_EDAC_NR_CHANS 1 + +/* Granularity of reported error in bytes */ +#define SYNPS_EDAC_ERR_GRAIN 1 + +#define SYNPS_EDAC_MSG_SIZE 256 + +#define SYNPS_EDAC_MOD_STRING "synps_edac" +#define SYNPS_EDAC_MOD_VER "1" + +/* Synopsys DDR memory controller registers that are relevant to ECC */ +#define CTRL_OFST 0x0 +#define T_ZQ_OFST 0xA4 + +/* ECC control register */ +#define ECC_CTRL_OFST 0xC4 +/* ECC log register */ +#define CE_LOG_OFST 0xC8 +/* ECC address register */ +#define CE_ADDR_OFST 0xCC +/* ECC data[31:0] register */ +#define CE_DATA_31_0_OFST 0xD0 + +/* Uncorrectable error info registers */ +#define UE_LOG_OFST 0xDC +#define UE_ADDR_OFST 0xE0 +#define UE_DATA_31_0_OFST 0xE4 + +#define STAT_OFST 0xF0 +#define SCRUB_OFST 0xF4 + +/* Control register bit field definitions */ +#define CTRL_BW_MASK 0xC +#define CTRL_BW_SHIFT 2 + +#define DDRCTL_WDTH_16 1 +#define DDRCTL_WDTH_32 0 + +/* ZQ register bit field definitions */ +#define T_ZQ_DDRMODE_MASK 0x2 + +/* ECC control register bit field definitions */ +#define ECC_CTRL_CLR_CE_ERR 0x2 +#define ECC_CTRL_CLR_UE_ERR 0x1 + +/* ECC correctable/uncorrectable error log register definitions */ +#define LOG_VALID 0x1 +#define CE_LOG_BITPOS_MASK 0xFE +#define CE_LOG_BITPOS_SHIFT 1 + +/* ECC correctable/uncorrectable error address register definitions */ +#define ADDR_COL_MASK 0xFFF +#define ADDR_ROW_MASK 0xFFFF000 +#define ADDR_ROW_SHIFT 12 +#define ADDR_BANK_MASK 0x70000000 +#define ADDR_BANK_SHIFT 28 + +/* ECC statistic register definitions */ +#define STAT_UECNT_MASK 0xFF +#define STAT_CECNT_MASK 0xFF00 +#define STAT_CECNT_SHIFT 8 + +/* ECC scrub register definitions */ +#define SCRUB_MODE_MASK 0x7 +#define SCRUB_MODE_SECDED 0x4 + +/** + * struct ecc_error_info - ECC error log information + * @row: Row number + * @col: Column number + * @bank: Bank number + * @bitpos: Bit position + * @data: Data causing the error + */ +struct ecc_error_info { + u32 row; + u32 col; + u32 bank; + u32 bitpos; + u32 data; +}; + +/** + * struct synps_ecc_status - ECC status information to report + * @ce_cnt: Correctable error count + * @ue_cnt: Uncorrectable error count + * @ceinfo: Correctable error log information + * @ueinfo: Uncorrectable error log information + */ +struct synps_ecc_status { + u32 ce_cnt; + u32 ue_cnt; + struct ecc_error_info ceinfo; + struct ecc_error_info ueinfo; +}; + +/** + * struct synps_edac_priv - DDR memory controller private instance data + * @baseaddr: Base address of the DDR controller + * @message: Buffer for framing the event specific info + * @stat: ECC status information + * @ce_cnt: Correctable Error count + * @ue_cnt: Uncorrectable Error count + */ +struct synps_edac_priv { + void __iomem *baseaddr; + char message[SYNPS_EDAC_MSG_SIZE]; + struct synps_ecc_status stat; + u32 ce_cnt; + u32 ue_cnt; +}; + +/** + * synps_edac_geterror_info - Get the current ecc error info + * @base: Pointer to the base address of the ddr memory controller + * @p: Pointer to the synopsys ecc status structure + * + * Determines there is any ecc error or not + * + * Return: one if there is no error otherwise returns zero + */ +static int synps_edac_geterror_info(void __iomem *base, + struct synps_ecc_status *p) +{ + u32 regval, clearval = 0; + + regval = readl(base + STAT_OFST); + if (!regval) + return 1; + + p->ce_cnt = (regval & STAT_CECNT_MASK) >> STAT_CECNT_SHIFT; + p->ue_cnt = regval & STAT_UECNT_MASK; + + regval = readl(base + CE_LOG_OFST); + if (!(p->ce_cnt && (regval & LOG_VALID))) + goto ue_err; + + p->ceinfo.bitpos = (regval & CE_LOG_BITPOS_MASK) >> CE_LOG_BITPOS_SHIFT; + regval = readl(base + CE_ADDR_OFST); + p->ceinfo.row = (regval & ADDR_ROW_MASK) >> ADDR_ROW_SHIFT; + p->ceinfo.col = regval & ADDR_COL_MASK; + p->ceinfo.bank = (regval & ADDR_BANK_MASK) >> ADDR_BANK_SHIFT; + p->ceinfo.data = readl(base + CE_DATA_31_0_OFST); + edac_dbg(3, "ce bit position: %d data: %d\n", p->ceinfo.bitpos, + p->ceinfo.data); + clearval = ECC_CTRL_CLR_CE_ERR; + +ue_err: + regval = readl(base + UE_LOG_OFST); + if (!(p->ue_cnt && (regval & LOG_VALID))) + goto out; + + regval = readl(base + UE_ADDR_OFST); + p->ueinfo.row = (regval & ADDR_ROW_MASK) >> ADDR_ROW_SHIFT; + p->ueinfo.col = regval & ADDR_COL_MASK; + p->ueinfo.bank = (regval & ADDR_BANK_MASK) >> ADDR_BANK_SHIFT; + p->ueinfo.data = readl(base + UE_DATA_31_0_OFST); + clearval |= ECC_CTRL_CLR_UE_ERR; + +out: + writel(clearval, base + ECC_CTRL_OFST); + writel(0x0, base + ECC_CTRL_OFST); + + return 0; +} + +/** + * synps_edac_handle_error - Handle controller error types CE and UE + * @mci: Pointer to the edac memory controller instance + * @p: Pointer to the synopsys ecc status structure + * + * Handles the controller ECC correctable and un correctable error. + */ +static void synps_edac_handle_error(struct mem_ctl_info *mci, + struct synps_ecc_status *p) +{ + struct synps_edac_priv *priv = mci->pvt_info; + struct ecc_error_info *pinf; + + if (p->ce_cnt) { + pinf = &p->ceinfo; + snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, + "DDR ECC error type :%s Row %d Bank %d Col %d ", + "CE", pinf->row, pinf->bank, pinf->col); + edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, + p->ce_cnt, 0, 0, 0, 0, 0, -1, + priv->message, ""); + } + + if (p->ue_cnt) { + pinf = &p->ueinfo; + snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, + "DDR ECC error type :%s Row %d Bank %d Col %d ", + "UE", pinf->row, pinf->bank, pinf->col); + edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, + p->ue_cnt, 0, 0, 0, 0, 0, -1, + priv->message, ""); + } + + memset(p, 0, sizeof(*p)); +} + +/** + * synps_edac_check - Check controller for ECC errors + * @mci: Pointer to the edac memory controller instance + * + * Used to check and post ECC errors. Called by the polling thread + */ +static void synps_edac_check(struct mem_ctl_info *mci) +{ + struct synps_edac_priv *priv = mci->pvt_info; + int status; + + status = synps_edac_geterror_info(priv->baseaddr, &priv->stat); + if (status) + return; + + priv->ce_cnt += priv->stat.ce_cnt; + priv->ue_cnt += priv->stat.ue_cnt; + synps_edac_handle_error(mci, &priv->stat); + + edac_dbg(3, "Total error count ce %d ue %d\n", + priv->ce_cnt, priv->ue_cnt); +} + +/** + * synps_edac_get_dtype - Return the controller memory width + * @base: Pointer to the ddr memory controller base address + * + * Get the EDAC device type width appropriate for the current controller + * configuration. + * + * Return: a device type width enumeration. + */ +static enum dev_type synps_edac_get_dtype(const void __iomem *base) +{ + enum dev_type dt; + u32 width; + + width = readl(base + CTRL_OFST); + width = (width & CTRL_BW_MASK) >> CTRL_BW_SHIFT; + + switch (width) { + case DDRCTL_WDTH_16: + dt = DEV_X2; + break; + case DDRCTL_WDTH_32: + dt = DEV_X4; + break; + default: + dt = DEV_UNKNOWN; + } + + return dt; +} + +/** + * synps_edac_get_eccstate - Return the controller ecc enable/disable status + * @base: Pointer to the ddr memory controller base address + * + * Get the ECC enable/disable status for the controller + * + * Return: a ecc status boolean i.e true/false - enabled/disabled. + */ +static bool synps_edac_get_eccstate(void __iomem *base) +{ + enum dev_type dt; + u32 ecctype; + bool state = false; + + dt = synps_edac_get_dtype(base); + if (dt == DEV_UNKNOWN) + return state; + + ecctype = readl(base + SCRUB_OFST) & SCRUB_MODE_MASK; + if ((ecctype == SCRUB_MODE_SECDED) && (dt == DEV_X2)) + state = true; + + return state; +} + +/** + * synps_edac_get_memsize - reads the size of the attached memory device + * + * Return: the memory size in bytes + */ +static u32 synps_edac_get_memsize(void) +{ + struct sysinfo inf; + + si_meminfo(&inf); + + return inf.totalram * inf.mem_unit; +} + +/** + * synps_edac_get_mtype - Returns controller memory type + * @base: pointer to the synopsys ecc status structure + * + * Get the EDAC memory type appropriate for the current controller + * configuration. + * + * Return: a memory type enumeration. + */ +static enum mem_type synps_edac_get_mtype(const void __iomem *base) +{ + enum mem_type mt; + u32 memtype; + + memtype = readl(base + T_ZQ_OFST); + + if (memtype & T_ZQ_DDRMODE_MASK) + mt = MEM_DDR3; + else + mt = MEM_DDR2; + + return mt; +} + +/** + * synps_edac_init_csrows - Initialize the cs row data + * @mci: Pointer to the edac memory controller instance + * + * Initializes the chip select rows associated with the EDAC memory + * controller instance + * + * Return: Unconditionally 0. + */ +static int synps_edac_init_csrows(struct mem_ctl_info *mci) +{ + struct csrow_info *csi; + struct dimm_info *dimm; + struct synps_edac_priv *priv = mci->pvt_info; + u32 size; + int row, j; + + for (row = 0; row < mci->nr_csrows; row++) { + csi = mci->csrows[row]; + size = synps_edac_get_memsize(); + + for (j = 0; j < csi->nr_channels; j++) { + dimm = csi->channels[j]->dimm; + dimm->edac_mode = EDAC_FLAG_SECDED; + dimm->mtype = synps_edac_get_mtype(priv->baseaddr); + dimm->nr_pages = (size >> PAGE_SHIFT) / csi->nr_channels; + dimm->grain = SYNPS_EDAC_ERR_GRAIN; + dimm->dtype = synps_edac_get_dtype(priv->baseaddr); + } + } + + return 0; +} + +/** + * synps_edac_mc_init - Initialize driver instance + * @mci: Pointer to the edac memory controller instance + * @pdev: Pointer to the platform_device struct + * + * Performs initialization of the EDAC memory controller instance and + * related driver-private data associated with the memory controller the + * instance is bound to. + * + * Return: Always zero. + */ +static int synps_edac_mc_init(struct mem_ctl_info *mci, + struct platform_device *pdev) +{ + int status; + struct synps_edac_priv *priv; + + mci->pdev = &pdev->dev; + priv = mci->pvt_info; + platform_set_drvdata(pdev, mci); + + /* Initialize controller capabilities and configuration */ + mci->mtype_cap = MEM_FLAG_DDR3 | MEM_FLAG_DDR2; + mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; + mci->scrub_cap = SCRUB_HW_SRC; + mci->scrub_mode = SCRUB_NONE; + + mci->edac_cap = EDAC_FLAG_SECDED; + mci->ctl_name = "synps_ddr_controller"; + mci->dev_name = SYNPS_EDAC_MOD_STRING; + mci->mod_name = SYNPS_EDAC_MOD_VER; + mci->mod_ver = "1"; + + edac_op_state = EDAC_OPSTATE_POLL; + mci->edac_check = synps_edac_check; + mci->ctl_page_to_phys = NULL; + + status = synps_edac_init_csrows(mci); + + return status; +} + +/** + * synps_edac_mc_probe - Check controller and bind driver + * @pdev: Pointer to the platform_device struct + * + * Probes a specific controller instance for binding with the driver. + * + * Return: 0 if the controller instance was successfully bound to the + * driver; otherwise, < 0 on error. + */ +static int synps_edac_mc_probe(struct platform_device *pdev) +{ + struct mem_ctl_info *mci; + struct edac_mc_layer layers[2]; + struct synps_edac_priv *priv; + int rc; + struct resource *res; + void __iomem *baseaddr; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + baseaddr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(baseaddr)) + return PTR_ERR(baseaddr); + + if (!synps_edac_get_eccstate(baseaddr)) { + edac_printk(KERN_INFO, EDAC_MC, "ECC not enabled\n"); + return -ENXIO; + } + + layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; + layers[0].size = SYNPS_EDAC_NR_CSROWS; + layers[0].is_virt_csrow = true; + layers[1].type = EDAC_MC_LAYER_CHANNEL; + layers[1].size = SYNPS_EDAC_NR_CHANS; + layers[1].is_virt_csrow = false; + + mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, + sizeof(struct synps_edac_priv)); + if (!mci) { + edac_printk(KERN_ERR, EDAC_MC, + "Failed memory allocation for mc instance\n"); + return -ENOMEM; + } + + priv = mci->pvt_info; + priv->baseaddr = baseaddr; + rc = synps_edac_mc_init(mci, pdev); + if (rc) { + edac_printk(KERN_ERR, EDAC_MC, + "Failed to initialize instance\n"); + goto free_edac_mc; + } + + rc = edac_mc_add_mc(mci); + if (rc) { + edac_printk(KERN_ERR, EDAC_MC, + "Failed to register with EDAC core\n"); + goto free_edac_mc; + } + + /* + * Start capturing the correctable and uncorrectable errors. A write of + * 0 starts the counters. + */ + writel(0x0, baseaddr + ECC_CTRL_OFST); + return rc; + +free_edac_mc: + edac_mc_free(mci); + + return rc; +} + +/** + * synps_edac_mc_remove - Unbind driver from controller + * @pdev: Pointer to the platform_device struct + * + * Return: Unconditionally 0 + */ +static int synps_edac_mc_remove(struct platform_device *pdev) +{ + struct mem_ctl_info *mci = platform_get_drvdata(pdev); + + edac_mc_del_mc(&pdev->dev); + edac_mc_free(mci); + + return 0; +} + +static struct of_device_id synps_edac_match[] = { + { .compatible = "xlnx,zynq-ddrc-a05", }, + { /* end of table */ } +}; + +MODULE_DEVICE_TABLE(of, synps_edac_match); + +static struct platform_driver synps_edac_mc_driver = { + .driver = { + .name = "synopsys-edac", + .of_match_table = synps_edac_match, + }, + .probe = synps_edac_mc_probe, + .remove = synps_edac_mc_remove, +}; + +module_platform_driver(synps_edac_mc_driver); + +MODULE_AUTHOR("Xilinx Inc"); +MODULE_DESCRIPTION("Synopsys DDR ECC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index f712d47f30d8..8de4da5c9ab6 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -12,11 +12,11 @@ config EFI_VARS Note that using this driver in concert with efibootmgr requires at least test release version 0.5.0-test3 or later, which is - available from Matt Domsch's website located at: + available from: <http://linux.dell.com/efibootmgr/testing/efibootmgr-0.5.0-test3.tar.gz> Subsequent efibootmgr releases may be found at: - <http://linux.dell.com/efibootmgr> + <http://github.com/vathpela/efibootmgr> config EFI_VARS_PSTORE tristate "Register efivars backend for pstore" diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 9035c1b74d58..fccb464928c3 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -115,15 +115,24 @@ EFI_ATTR_SHOW(fw_vendor); EFI_ATTR_SHOW(runtime); EFI_ATTR_SHOW(config_table); +static ssize_t fw_platform_size_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", efi_enabled(EFI_64BIT) ? 64 : 32); +} + static struct kobj_attribute efi_attr_fw_vendor = __ATTR_RO(fw_vendor); static struct kobj_attribute efi_attr_runtime = __ATTR_RO(runtime); static struct kobj_attribute efi_attr_config_table = __ATTR_RO(config_table); +static struct kobj_attribute efi_attr_fw_platform_size = + __ATTR_RO(fw_platform_size); static struct attribute *efi_subsys_attrs[] = { &efi_attr_systab.attr, &efi_attr_fw_vendor.attr, &efi_attr_runtime.attr, &efi_attr_config_table.attr, + &efi_attr_fw_platform_size.attr, NULL, }; @@ -272,15 +281,10 @@ static __init int match_config_table(efi_guid_t *guid, unsigned long table, efi_config_table_type_t *table_types) { - u8 str[EFI_VARIABLE_GUID_LEN + 1]; int i; if (table_types) { - efi_guid_unparse(guid, str); - for (i = 0; efi_guidcmp(table_types[i].guid, NULL_GUID); i++) { - efi_guid_unparse(&table_types[i].guid, str); - if (!efi_guidcmp(*guid, table_types[i].guid)) { *(table_types[i].ptr) = table; pr_cont(" %s=0x%lx ", @@ -403,8 +407,7 @@ static int __init fdt_find_uefi_params(unsigned long node, const char *uname, u64 val; int i, len; - if (depth != 1 || - (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0)) + if (depth != 1 || strcmp(uname, "chosen") != 0) return 0; for (i = 0; i < ARRAY_SIZE(dt_params); i++) { diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c index f256ecd8a176..7b2e0496e0c0 100644 --- a/drivers/firmware/efi/efivars.c +++ b/drivers/firmware/efi/efivars.c @@ -39,7 +39,7 @@ * fix locking per Peter Chubb's findings * * 25 Mar 2002 - Matt Domsch <Matt_Domsch@dell.com> - * move uuid_unparse() to include/asm-ia64/efi.h:efi_guid_unparse() + * move uuid_unparse() to include/asm-ia64/efi.h:efi_guid_to_str() * * 12 Feb 2002 - Matt Domsch <Matt_Domsch@dell.com> * use list_for_each_safe when deleting vars. @@ -128,7 +128,7 @@ efivar_guid_read(struct efivar_entry *entry, char *buf) if (!entry || !buf) return 0; - efi_guid_unparse(&var->VendorGuid, str); + efi_guid_to_str(&var->VendorGuid, str); str += strlen(str); str += sprintf(str, "\n"); @@ -569,7 +569,7 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var) private variables from another's. */ *(short_name + strlen(short_name)) = '-'; - efi_guid_unparse(&new_var->var.VendorGuid, + efi_guid_to_str(&new_var->var.VendorGuid, short_name + strlen(short_name)); new_var->kobj.kset = efivars_kset; diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index b14bc2b9fb4d..8902f52e0998 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -24,3 +24,17 @@ lib-y := efi-stub-helper.o lib-$(CONFIG_EFI_ARMSTUB) += arm-stub.o fdt.o CFLAGS_fdt.o += -I$(srctree)/scripts/dtc/libfdt/ + +# +# arm64 puts the stub in the kernel proper, which will unnecessarily retain all +# code indefinitely unless it is annotated as __init/__initdata/__initconst etc. +# So let's apply the __init annotations at the section level, by prefixing +# the section names directly. This will ensure that even all the inline string +# literals are covered. +# +extra-$(CONFIG_ARM64) := $(lib-y) +lib-$(CONFIG_ARM64) := $(patsubst %.o,%.init.o,$(lib-y)) + +OBJCOPYFLAGS := --prefix-alloc-sections=.init +$(obj)/%.init.o: $(obj)/%.o FORCE + $(call if_changed,objcopy) diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index eb48a1a1a576..2b3814702dcf 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c @@ -17,10 +17,10 @@ #include "efistub.h" -static int __init efi_secureboot_enabled(efi_system_table_t *sys_table_arg) +static int efi_secureboot_enabled(efi_system_table_t *sys_table_arg) { - static efi_guid_t const var_guid __initconst = EFI_GLOBAL_VARIABLE_GUID; - static efi_char16_t const var_name[] __initconst = { + static efi_guid_t const var_guid = EFI_GLOBAL_VARIABLE_GUID; + static efi_char16_t const var_name[] = { 'S', 'e', 'c', 'u', 'r', 'e', 'B', 'o', 'o', 't', 0 }; efi_get_variable_t *f_getvar = sys_table_arg->runtime->get_variable; @@ -164,7 +164,7 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table, * for both archictectures, with the arch-specific code provided in the * handle_kernel_image() function. */ -unsigned long __init efi_entry(void *handle, efi_system_table_t *sys_table, +unsigned long efi_entry(void *handle, efi_system_table_t *sys_table, unsigned long *image_addr) { efi_loaded_image_t *image; diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index a920fec8fe88..d073e3946383 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -66,25 +66,29 @@ efi_status_t efi_get_memory_map(efi_system_table_t *sys_table_arg, unsigned long key; u32 desc_version; - *map_size = sizeof(*m) * 32; -again: + *map_size = 0; + *desc_size = 0; + key = 0; + status = efi_call_early(get_memory_map, map_size, NULL, + &key, desc_size, &desc_version); + if (status != EFI_BUFFER_TOO_SMALL) + return EFI_LOAD_ERROR; + /* * Add an additional efi_memory_desc_t because we're doing an * allocation which may be in a new descriptor region. */ - *map_size += sizeof(*m); + *map_size += *desc_size; status = efi_call_early(allocate_pool, EFI_LOADER_DATA, *map_size, (void **)&m); if (status != EFI_SUCCESS) goto fail; - *desc_size = 0; - key = 0; status = efi_call_early(get_memory_map, map_size, m, &key, desc_size, &desc_version); if (status == EFI_BUFFER_TOO_SMALL) { efi_call_early(free_pool, m); - goto again; + return EFI_LOAD_ERROR; } if (status != EFI_SUCCESS) @@ -101,7 +105,7 @@ fail: } -unsigned long __init get_dram_base(efi_system_table_t *sys_table_arg) +unsigned long get_dram_base(efi_system_table_t *sys_table_arg) { efi_status_t status; unsigned long map_size; diff --git a/drivers/firmware/efi/runtime-map.c b/drivers/firmware/efi/runtime-map.c index 018c29a26615..87b8e3b900d2 100644 --- a/drivers/firmware/efi/runtime-map.c +++ b/drivers/firmware/efi/runtime-map.c @@ -191,7 +191,7 @@ int __init efi_runtime_map_init(struct kobject *efi_kobj) return 0; out_add_entry: - for (j = i - 1; j > 0; j--) { + for (j = i - 1; j >= 0; j--) { entry = *(map_entries + j); kobject_put(&entry->kobj); } diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c index 55d4803d71b0..3d9e08f7e823 100644 --- a/drivers/gpio/gpio-crystalcove.c +++ b/drivers/gpio/gpio-crystalcove.c @@ -272,7 +272,7 @@ static irqreturn_t crystalcove_gpio_irq_handler(int irq, void *data) for (gpio = 0; gpio < CRYSTALCOVE_GPIO_NUM; gpio++) { if (pending & BIT(gpio)) { virq = irq_find_mapping(cg->chip.irqdomain, gpio); - generic_handle_irq(virq); + handle_nested_irq(virq); } } diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c index 978b51eae2ec..ce3c1558cb0a 100644 --- a/drivers/gpio/gpio-dln2.c +++ b/drivers/gpio/gpio-dln2.c @@ -47,13 +47,6 @@ #define DLN2_GPIO_MAX_PINS 32 -struct dln2_irq_work { - struct work_struct work; - struct dln2_gpio *dln2; - int pin; - int type; -}; - struct dln2_gpio { struct platform_device *pdev; struct gpio_chip gpio; @@ -64,10 +57,12 @@ struct dln2_gpio { */ DECLARE_BITMAP(output_enabled, DLN2_GPIO_MAX_PINS); - DECLARE_BITMAP(irqs_masked, DLN2_GPIO_MAX_PINS); - DECLARE_BITMAP(irqs_enabled, DLN2_GPIO_MAX_PINS); - DECLARE_BITMAP(irqs_pending, DLN2_GPIO_MAX_PINS); - struct dln2_irq_work *irq_work; + /* active IRQs - not synced to hardware */ + DECLARE_BITMAP(unmasked_irqs, DLN2_GPIO_MAX_PINS); + /* active IRQS - synced to hardware */ + DECLARE_BITMAP(enabled_irqs, DLN2_GPIO_MAX_PINS); + int irq_type[DLN2_GPIO_MAX_PINS]; + struct mutex irq_lock; }; struct dln2_gpio_pin { @@ -141,16 +136,16 @@ static int dln2_gpio_pin_get_out_val(struct dln2_gpio *dln2, unsigned int pin) return !!ret; } -static void dln2_gpio_pin_set_out_val(struct dln2_gpio *dln2, - unsigned int pin, int value) +static int dln2_gpio_pin_set_out_val(struct dln2_gpio *dln2, + unsigned int pin, int value) { struct dln2_gpio_pin_val req = { .pin = cpu_to_le16(pin), .value = value, }; - dln2_transfer_tx(dln2->pdev, DLN2_GPIO_PIN_SET_OUT_VAL, &req, - sizeof(req)); + return dln2_transfer_tx(dln2->pdev, DLN2_GPIO_PIN_SET_OUT_VAL, &req, + sizeof(req)); } #define DLN2_GPIO_DIRECTION_IN 0 @@ -267,6 +262,13 @@ static int dln2_gpio_direction_input(struct gpio_chip *chip, unsigned offset) static int dln2_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { + struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); + int ret; + + ret = dln2_gpio_pin_set_out_val(dln2, offset, value); + if (ret < 0) + return ret; + return dln2_gpio_set_direction(chip, offset, DLN2_GPIO_DIRECTION_OUT); } @@ -297,36 +299,13 @@ static int dln2_gpio_set_event_cfg(struct dln2_gpio *dln2, unsigned pin, &req, sizeof(req)); } -static void dln2_irq_work(struct work_struct *w) -{ - struct dln2_irq_work *iw = container_of(w, struct dln2_irq_work, work); - struct dln2_gpio *dln2 = iw->dln2; - u8 type = iw->type & DLN2_GPIO_EVENT_MASK; - - if (test_bit(iw->pin, dln2->irqs_enabled)) - dln2_gpio_set_event_cfg(dln2, iw->pin, type, 0); - else - dln2_gpio_set_event_cfg(dln2, iw->pin, DLN2_GPIO_EVENT_NONE, 0); -} - -static void dln2_irq_enable(struct irq_data *irqd) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); - struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); - int pin = irqd_to_hwirq(irqd); - - set_bit(pin, dln2->irqs_enabled); - schedule_work(&dln2->irq_work[pin].work); -} - -static void dln2_irq_disable(struct irq_data *irqd) +static void dln2_irq_unmask(struct irq_data *irqd) { struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); int pin = irqd_to_hwirq(irqd); - clear_bit(pin, dln2->irqs_enabled); - schedule_work(&dln2->irq_work[pin].work); + set_bit(pin, dln2->unmasked_irqs); } static void dln2_irq_mask(struct irq_data *irqd) @@ -335,27 +314,7 @@ static void dln2_irq_mask(struct irq_data *irqd) struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); int pin = irqd_to_hwirq(irqd); - set_bit(pin, dln2->irqs_masked); -} - -static void dln2_irq_unmask(struct irq_data *irqd) -{ - struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); - struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); - struct device *dev = dln2->gpio.dev; - int pin = irqd_to_hwirq(irqd); - - if (test_and_clear_bit(pin, dln2->irqs_pending)) { - int irq; - - irq = irq_find_mapping(dln2->gpio.irqdomain, pin); - if (!irq) { - dev_err(dev, "pin %d not mapped to IRQ\n", pin); - return; - } - - generic_handle_irq(irq); - } + clear_bit(pin, dln2->unmasked_irqs); } static int dln2_irq_set_type(struct irq_data *irqd, unsigned type) @@ -366,19 +325,19 @@ static int dln2_irq_set_type(struct irq_data *irqd, unsigned type) switch (type) { case IRQ_TYPE_LEVEL_HIGH: - dln2->irq_work[pin].type = DLN2_GPIO_EVENT_LVL_HIGH; + dln2->irq_type[pin] = DLN2_GPIO_EVENT_LVL_HIGH; break; case IRQ_TYPE_LEVEL_LOW: - dln2->irq_work[pin].type = DLN2_GPIO_EVENT_LVL_LOW; + dln2->irq_type[pin] = DLN2_GPIO_EVENT_LVL_LOW; break; case IRQ_TYPE_EDGE_BOTH: - dln2->irq_work[pin].type = DLN2_GPIO_EVENT_CHANGE; + dln2->irq_type[pin] = DLN2_GPIO_EVENT_CHANGE; break; case IRQ_TYPE_EDGE_RISING: - dln2->irq_work[pin].type = DLN2_GPIO_EVENT_CHANGE_RISING; + dln2->irq_type[pin] = DLN2_GPIO_EVENT_CHANGE_RISING; break; case IRQ_TYPE_EDGE_FALLING: - dln2->irq_work[pin].type = DLN2_GPIO_EVENT_CHANGE_FALLING; + dln2->irq_type[pin] = DLN2_GPIO_EVENT_CHANGE_FALLING; break; default: return -EINVAL; @@ -387,13 +346,50 @@ static int dln2_irq_set_type(struct irq_data *irqd, unsigned type) return 0; } +static void dln2_irq_bus_lock(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); + + mutex_lock(&dln2->irq_lock); +} + +static void dln2_irq_bus_unlock(struct irq_data *irqd) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); + struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); + int pin = irqd_to_hwirq(irqd); + int enabled, unmasked; + unsigned type; + int ret; + + enabled = test_bit(pin, dln2->enabled_irqs); + unmasked = test_bit(pin, dln2->unmasked_irqs); + + if (enabled != unmasked) { + if (unmasked) { + type = dln2->irq_type[pin] & DLN2_GPIO_EVENT_MASK; + set_bit(pin, dln2->enabled_irqs); + } else { + type = DLN2_GPIO_EVENT_NONE; + clear_bit(pin, dln2->enabled_irqs); + } + + ret = dln2_gpio_set_event_cfg(dln2, pin, type, 0); + if (ret) + dev_err(dln2->gpio.dev, "failed to set event\n"); + } + + mutex_unlock(&dln2->irq_lock); +} + static struct irq_chip dln2_gpio_irqchip = { .name = "dln2-irq", - .irq_enable = dln2_irq_enable, - .irq_disable = dln2_irq_disable, .irq_mask = dln2_irq_mask, .irq_unmask = dln2_irq_unmask, .irq_set_type = dln2_irq_set_type, + .irq_bus_lock = dln2_irq_bus_lock, + .irq_bus_sync_unlock = dln2_irq_bus_unlock, }; static void dln2_gpio_event(struct platform_device *pdev, u16 echo, @@ -425,14 +421,7 @@ static void dln2_gpio_event(struct platform_device *pdev, u16 echo, return; } - if (!test_bit(pin, dln2->irqs_enabled)) - return; - if (test_bit(pin, dln2->irqs_masked)) { - set_bit(pin, dln2->irqs_pending); - return; - } - - switch (dln2->irq_work[pin].type) { + switch (dln2->irq_type[pin]) { case DLN2_GPIO_EVENT_CHANGE_RISING: if (event->value) generic_handle_irq(irq); @@ -451,7 +440,7 @@ static int dln2_gpio_probe(struct platform_device *pdev) struct dln2_gpio *dln2; struct device *dev = &pdev->dev; int pins; - int i, ret; + int ret; pins = dln2_gpio_get_pin_count(pdev); if (pins < 0) { @@ -467,15 +456,7 @@ static int dln2_gpio_probe(struct platform_device *pdev) if (!dln2) return -ENOMEM; - dln2->irq_work = devm_kcalloc(&pdev->dev, pins, - sizeof(struct dln2_irq_work), GFP_KERNEL); - if (!dln2->irq_work) - return -ENOMEM; - for (i = 0; i < pins; i++) { - INIT_WORK(&dln2->irq_work[i].work, dln2_irq_work); - dln2->irq_work[i].pin = i; - dln2->irq_work[i].dln2 = dln2; - } + mutex_init(&dln2->irq_lock); dln2->pdev = pdev; @@ -529,11 +510,8 @@ out: static int dln2_gpio_remove(struct platform_device *pdev) { struct dln2_gpio *dln2 = platform_get_drvdata(pdev); - int i; dln2_unregister_event_cb(pdev, DLN2_GPIO_CONDITION_MET_EV); - for (i = 0; i < dln2->gpio.ngpio; i++) - flush_work(&dln2->irq_work[i].work); gpiochip_remove(&dln2->gpio); return 0; diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index 09daaf2aeb56..3a5a71050559 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c @@ -441,7 +441,8 @@ static int grgpio_probe(struct platform_device *ofdev) err = gpiochip_add(gc); if (err) { dev_err(&ofdev->dev, "Could not add gpiochip\n"); - irq_domain_remove(priv->domain); + if (priv->domain) + irq_domain_remove(priv->domain); return err; } diff --git a/drivers/gpio/gpio-mcp23s08.c b/drivers/gpio/gpio-mcp23s08.c index da9c316059bc..eea5d7e578c9 100644 --- a/drivers/gpio/gpio-mcp23s08.c +++ b/drivers/gpio/gpio-mcp23s08.c @@ -801,9 +801,11 @@ static int mcp230xx_probe(struct i2c_client *client, client->irq = irq_of_parse_and_map(client->dev.of_node, 0); } else { pdata = dev_get_platdata(&client->dev); - if (!pdata || !gpio_is_valid(pdata->base)) { - dev_dbg(&client->dev, "invalid platform data\n"); - return -EINVAL; + if (!pdata) { + pdata = devm_kzalloc(&client->dev, + sizeof(struct mcp23s08_platform_data), + GFP_KERNEL); + pdata->base = -1; } } @@ -924,10 +926,11 @@ static int mcp23s08_probe(struct spi_device *spi) } else { type = spi_get_device_id(spi)->driver_data; pdata = dev_get_platdata(&spi->dev); - if (!pdata || !gpio_is_valid(pdata->base)) { - dev_dbg(&spi->dev, - "invalid or missing platform data\n"); - return -EINVAL; + if (!pdata) { + pdata = devm_kzalloc(&spi->dev, + sizeof(struct mcp23s08_platform_data), + GFP_KERNEL); + pdata->base = -1; } for (addr = 0; addr < ARRAY_SIZE(pdata->chip); addr++) { diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 30646cfe0efa..f476ae2eb0b3 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -88,6 +88,8 @@ struct gpio_bank { #define BANK_USED(bank) (bank->mod_usage || bank->irq_usage) #define LINE_USED(line, offset) (line & (BIT(offset))) +static void omap_gpio_unmask_irq(struct irq_data *d); + static int omap_irq_to_gpio(struct gpio_bank *bank, unsigned int gpio_irq) { return bank->chip.base + gpio_irq; @@ -477,6 +479,16 @@ static int omap_gpio_is_input(struct gpio_bank *bank, int mask) return readl_relaxed(reg) & mask; } +static void omap_gpio_init_irq(struct gpio_bank *bank, unsigned gpio, + unsigned offset) +{ + if (!LINE_USED(bank->mod_usage, offset)) { + omap_enable_gpio_module(bank, offset); + omap_set_gpio_direction(bank, offset, 1); + } + bank->irq_usage |= BIT(GPIO_INDEX(bank, gpio)); +} + static int omap_gpio_irq_type(struct irq_data *d, unsigned type) { struct gpio_bank *bank = omap_irq_data_get_bank(d); @@ -506,15 +518,11 @@ static int omap_gpio_irq_type(struct irq_data *d, unsigned type) spin_lock_irqsave(&bank->lock, flags); offset = GPIO_INDEX(bank, gpio); retval = omap_set_gpio_triggering(bank, offset, type); - if (!LINE_USED(bank->mod_usage, offset)) { - omap_enable_gpio_module(bank, offset); - omap_set_gpio_direction(bank, offset, 1); - } else if (!omap_gpio_is_input(bank, BIT(offset))) { + omap_gpio_init_irq(bank, gpio, offset); + if (!omap_gpio_is_input(bank, BIT(offset))) { spin_unlock_irqrestore(&bank->lock, flags); return -EINVAL; } - - bank->irq_usage |= BIT(GPIO_INDEX(bank, gpio)); spin_unlock_irqrestore(&bank->lock, flags); if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) @@ -792,6 +800,24 @@ exit: pm_runtime_put(bank->dev); } +static unsigned int omap_gpio_irq_startup(struct irq_data *d) +{ + struct gpio_bank *bank = omap_irq_data_get_bank(d); + unsigned int gpio = omap_irq_to_gpio(bank, d->hwirq); + unsigned long flags; + unsigned offset = GPIO_INDEX(bank, gpio); + + if (!BANK_USED(bank)) + pm_runtime_get_sync(bank->dev); + + spin_lock_irqsave(&bank->lock, flags); + omap_gpio_init_irq(bank, gpio, offset); + spin_unlock_irqrestore(&bank->lock, flags); + omap_gpio_unmask_irq(d); + + return 0; +} + static void omap_gpio_irq_shutdown(struct irq_data *d) { struct gpio_bank *bank = omap_irq_data_get_bank(d); @@ -1181,6 +1207,7 @@ static int omap_gpio_probe(struct platform_device *pdev) if (!irqc) return -ENOMEM; + irqc->irq_startup = omap_gpio_irq_startup, irqc->irq_shutdown = omap_gpio_irq_shutdown, irqc->irq_ack = omap_gpio_ack_irq, irqc->irq_mask = omap_gpio_mask_irq, diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 604dbe60bdee..08261f2b3a82 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -45,8 +45,14 @@ static int of_gpiochip_find_and_xlate(struct gpio_chip *gc, void *data) return false; ret = gc->of_xlate(gc, &gg_data->gpiospec, gg_data->flags); - if (ret < 0) - return false; + if (ret < 0) { + /* We've found the gpio chip, but the translation failed. + * Return true to stop looking and return the translation + * error via out_gpio + */ + gg_data->out_gpio = ERR_PTR(ret); + return true; + } gg_data->out_gpio = gpiochip_get_desc(gc, ret); return true; diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 2ac1800b58bb..7722ed53bd65 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -128,7 +128,7 @@ static ssize_t gpio_value_store(struct device *dev, return status; } -static const DEVICE_ATTR(value, 0644, +static DEVICE_ATTR(value, 0644, gpio_value_show, gpio_value_store); static irqreturn_t gpio_sysfs_irq(int irq, void *priv) @@ -353,17 +353,46 @@ static ssize_t gpio_active_low_store(struct device *dev, return status ? : size; } -static const DEVICE_ATTR(active_low, 0644, +static DEVICE_ATTR(active_low, 0644, gpio_active_low_show, gpio_active_low_store); -static const struct attribute *gpio_attrs[] = { +static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, + int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct gpio_desc *desc = dev_get_drvdata(dev); + umode_t mode = attr->mode; + bool show_direction = test_bit(FLAG_SYSFS_DIR, &desc->flags); + + if (attr == &dev_attr_direction.attr) { + if (!show_direction) + mode = 0; + } else if (attr == &dev_attr_edge.attr) { + if (gpiod_to_irq(desc) < 0) + mode = 0; + if (!show_direction && test_bit(FLAG_IS_OUT, &desc->flags)) + mode = 0; + } + + return mode; +} + +static struct attribute *gpio_attrs[] = { + &dev_attr_direction.attr, + &dev_attr_edge.attr, &dev_attr_value.attr, &dev_attr_active_low.attr, NULL, }; -static const struct attribute_group gpio_attr_group = { - .attrs = (struct attribute **) gpio_attrs, +static const struct attribute_group gpio_group = { + .attrs = gpio_attrs, + .is_visible = gpio_is_visible, +}; + +static const struct attribute_group *gpio_groups[] = { + &gpio_group, + NULL }; /* @@ -400,16 +429,13 @@ static ssize_t chip_ngpio_show(struct device *dev, } static DEVICE_ATTR(ngpio, 0444, chip_ngpio_show, NULL); -static const struct attribute *gpiochip_attrs[] = { +static struct attribute *gpiochip_attrs[] = { &dev_attr_base.attr, &dev_attr_label.attr, &dev_attr_ngpio.attr, NULL, }; - -static const struct attribute_group gpiochip_attr_group = { - .attrs = (struct attribute **) gpiochip_attrs, -}; +ATTRIBUTE_GROUPS(gpiochip); /* * /sys/class/gpio/export ... write-only @@ -556,45 +582,30 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) goto fail_unlock; } - if (!desc->chip->direction_input || !desc->chip->direction_output) - direction_may_change = false; + if (desc->chip->direction_input && desc->chip->direction_output && + direction_may_change) { + set_bit(FLAG_SYSFS_DIR, &desc->flags); + } + spin_unlock_irqrestore(&gpio_lock, flags); offset = gpio_chip_hwgpio(desc); if (desc->chip->names && desc->chip->names[offset]) ioname = desc->chip->names[offset]; - dev = device_create(&gpio_class, desc->chip->dev, MKDEV(0, 0), - desc, ioname ? ioname : "gpio%u", - desc_to_gpio(desc)); + dev = device_create_with_groups(&gpio_class, desc->chip->dev, + MKDEV(0, 0), desc, gpio_groups, + ioname ? ioname : "gpio%u", + desc_to_gpio(desc)); if (IS_ERR(dev)) { status = PTR_ERR(dev); goto fail_unlock; } - status = sysfs_create_group(&dev->kobj, &gpio_attr_group); - if (status) - goto fail_unregister_device; - - if (direction_may_change) { - status = device_create_file(dev, &dev_attr_direction); - if (status) - goto fail_unregister_device; - } - - if (gpiod_to_irq(desc) >= 0 && (direction_may_change || - !test_bit(FLAG_IS_OUT, &desc->flags))) { - status = device_create_file(dev, &dev_attr_edge); - if (status) - goto fail_unregister_device; - } - set_bit(FLAG_EXPORT, &desc->flags); mutex_unlock(&sysfs_lock); return 0; -fail_unregister_device: - device_unregister(dev); fail_unlock: mutex_unlock(&sysfs_lock); gpiod_dbg(desc, "%s: status %d\n", __func__, status); @@ -637,6 +648,7 @@ int gpiod_export_link(struct device *dev, const char *name, if (tdev != NULL) { status = sysfs_create_link(&dev->kobj, &tdev->kobj, name); + put_device(tdev); } else { status = -ENODEV; } @@ -684,7 +696,7 @@ int gpiod_sysfs_set_active_low(struct gpio_desc *desc, int value) } status = sysfs_set_active_low(desc, dev, value); - + put_device(dev); unlock: mutex_unlock(&sysfs_lock); @@ -718,6 +730,7 @@ void gpiod_unexport(struct gpio_desc *desc) dev = class_find_device(&gpio_class, NULL, desc, match_export); if (dev) { gpio_setup_irq(desc, dev, 0); + clear_bit(FLAG_SYSFS_DIR, &desc->flags); clear_bit(FLAG_EXPORT, &desc->flags); } else status = -ENODEV; @@ -750,13 +763,13 @@ int gpiochip_export(struct gpio_chip *chip) /* use chip->base for the ID; it's already known to be unique */ mutex_lock(&sysfs_lock); - dev = device_create(&gpio_class, chip->dev, MKDEV(0, 0), chip, - "gpiochip%d", chip->base); - if (!IS_ERR(dev)) { - status = sysfs_create_group(&dev->kobj, - &gpiochip_attr_group); - } else + dev = device_create_with_groups(&gpio_class, chip->dev, MKDEV(0, 0), + chip, gpiochip_groups, + "gpiochip%d", chip->base); + if (IS_ERR(dev)) status = PTR_ERR(dev); + else + status = 0; chip->exported = (status == 0); mutex_unlock(&sysfs_lock); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 487afe6f22fc..568aa2b6bdb0 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -248,29 +248,30 @@ int gpiochip_add(struct gpio_chip *chip) base = gpiochip_find_base(chip->ngpio); if (base < 0) { status = base; - goto unlock; + spin_unlock_irqrestore(&gpio_lock, flags); + goto err_free_descs; } chip->base = base; } status = gpiochip_add_to_list(chip); + if (status) { + spin_unlock_irqrestore(&gpio_lock, flags); + goto err_free_descs; + } - if (status == 0) { - for (id = 0; id < chip->ngpio; id++) { - struct gpio_desc *desc = &descs[id]; - desc->chip = chip; - - /* REVISIT: most hardware initializes GPIOs as - * inputs (often with pullups enabled) so power - * usage is minimized. Linux code should set the - * gpio direction first thing; but until it does, - * and in case chip->get_direction is not set, - * we may expose the wrong direction in sysfs. - */ - desc->flags = !chip->direction_input - ? (1 << FLAG_IS_OUT) - : 0; - } + for (id = 0; id < chip->ngpio; id++) { + struct gpio_desc *desc = &descs[id]; + + desc->chip = chip; + + /* REVISIT: most hardware initializes GPIOs as inputs (often + * with pullups enabled) so power usage is minimized. Linux + * code should set the gpio direction first thing; but until + * it does, and in case chip->get_direction is not set, we may + * expose the wrong direction in sysfs. + */ + desc->flags = !chip->direction_input ? (1 << FLAG_IS_OUT) : 0; } chip->desc = descs; @@ -284,12 +285,9 @@ int gpiochip_add(struct gpio_chip *chip) of_gpiochip_add(chip); acpi_gpiochip_add(chip); - if (status) - goto fail; - status = gpiochip_export(chip); if (status) - goto fail; + goto err_remove_chip; pr_debug("%s: registered GPIOs %d to %d on device: %s\n", __func__, chip->base, chip->base + chip->ngpio - 1, @@ -297,11 +295,15 @@ int gpiochip_add(struct gpio_chip *chip) return 0; -unlock: +err_remove_chip: + acpi_gpiochip_remove(chip); + of_gpiochip_remove(chip); + spin_lock_irqsave(&gpio_lock, flags); + list_del(&chip->list); spin_unlock_irqrestore(&gpio_lock, flags); -fail: - kfree(descs); chip->desc = NULL; +err_free_descs: + kfree(descs); /* failures here can mean systems won't boot... */ pr_err("%s: GPIOs %d..%d (%s) failed to register\n", __func__, @@ -325,14 +327,15 @@ void gpiochip_remove(struct gpio_chip *chip) unsigned long flags; unsigned id; - acpi_gpiochip_remove(chip); - - spin_lock_irqsave(&gpio_lock, flags); + gpiochip_unexport(chip); gpiochip_irqchip_remove(chip); + + acpi_gpiochip_remove(chip); gpiochip_remove_pin_ranges(chip); of_gpiochip_remove(chip); + spin_lock_irqsave(&gpio_lock, flags); for (id = 0; id < chip->ngpio; id++) { if (test_bit(FLAG_REQUESTED, &chip->desc[id].flags)) dev_crit(chip->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n"); @@ -342,7 +345,6 @@ void gpiochip_remove(struct gpio_chip *chip) list_del(&chip->list); spin_unlock_irqrestore(&gpio_lock, flags); - gpiochip_unexport(chip); kfree(chip->desc); chip->desc = NULL; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index e3a52113a541..550a5eafbd38 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -77,6 +77,7 @@ struct gpio_desc { #define FLAG_OPEN_DRAIN 7 /* Gpio is open drain type */ #define FLAG_OPEN_SOURCE 8 /* Gpio is open source type */ #define FLAG_USED_AS_IRQ 9 /* GPIO is connected to an IRQ */ +#define FLAG_SYSFS_DIR 10 /* show sysfs direction attribute */ #define ID_SHIFT 16 /* add new flags before this one */ diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 66e40398b3d3..e620807418ea 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o obj-$(CONFIG_DRM_TTM) += ttm/ obj-$(CONFIG_DRM_TDFX) += tdfx/ obj-$(CONFIG_DRM_R128) += r128/ +obj-$(CONFIG_HSA_AMD) += amd/amdkfd/ obj-$(CONFIG_DRM_RADEON)+= radeon/ obj-$(CONFIG_DRM_MGA) += mga/ obj-$(CONFIG_DRM_I810) += i810/ @@ -67,4 +68,3 @@ obj-$(CONFIG_DRM_IMX) += imx/ obj-y += i2c/ obj-y += panel/ obj-y += bridge/ -obj-$(CONFIG_HSA_AMD) += amd/amdkfd/ diff --git a/drivers/gpu/drm/amd/amdkfd/Makefile b/drivers/gpu/drm/amd/amdkfd/Makefile index be6246de5091..307a309110e6 100644 --- a/drivers/gpu/drm/amd/amdkfd/Makefile +++ b/drivers/gpu/drm/amd/amdkfd/Makefile @@ -8,7 +8,6 @@ amdkfd-y := kfd_module.o kfd_device.o kfd_chardev.o kfd_topology.o \ kfd_pasid.o kfd_doorbell.o kfd_flat_memory.o \ kfd_process.o kfd_queue.o kfd_mqd_manager.o \ kfd_kernel_queue.o kfd_packet_manager.o \ - kfd_process_queue_manager.o kfd_device_queue_manager.o \ - kfd_interrupt.o + kfd_process_queue_manager.o kfd_device_queue_manager.o obj-$(CONFIG_HSA_AMD) += amdkfd.o diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 7d4974b83af7..fcfdf23e1913 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -31,7 +31,6 @@ #include <uapi/linux/kfd_ioctl.h> #include <linux/time.h> #include <linux/mm.h> -#include <linux/uaccess.h> #include <uapi/asm-generic/mman-common.h> #include <asm/processor.h> #include "kfd_priv.h" @@ -127,17 +126,14 @@ static int kfd_open(struct inode *inode, struct file *filep) return 0; } -static long kfd_ioctl_get_version(struct file *filep, struct kfd_process *p, - void __user *arg) +static int kfd_ioctl_get_version(struct file *filep, struct kfd_process *p, + void *data) { - struct kfd_ioctl_get_version_args args; + struct kfd_ioctl_get_version_args *args = data; int err = 0; - args.major_version = KFD_IOCTL_MAJOR_VERSION; - args.minor_version = KFD_IOCTL_MINOR_VERSION; - - if (copy_to_user(arg, &args, sizeof(args))) - err = -EFAULT; + args->major_version = KFD_IOCTL_MAJOR_VERSION; + args->minor_version = KFD_IOCTL_MINOR_VERSION; return err; } @@ -221,10 +217,10 @@ static int set_queue_properties_from_user(struct queue_properties *q_properties, return 0; } -static long kfd_ioctl_create_queue(struct file *filep, struct kfd_process *p, - void __user *arg) +static int kfd_ioctl_create_queue(struct file *filep, struct kfd_process *p, + void *data) { - struct kfd_ioctl_create_queue_args args; + struct kfd_ioctl_create_queue_args *args = data; struct kfd_dev *dev; int err = 0; unsigned int queue_id; @@ -233,16 +229,13 @@ static long kfd_ioctl_create_queue(struct file *filep, struct kfd_process *p, memset(&q_properties, 0, sizeof(struct queue_properties)); - if (copy_from_user(&args, arg, sizeof(args))) - return -EFAULT; - pr_debug("kfd: creating queue ioctl\n"); - err = set_queue_properties_from_user(&q_properties, &args); + err = set_queue_properties_from_user(&q_properties, args); if (err) return err; - dev = kfd_device_by_id(args.gpu_id); + dev = kfd_device_by_id(args->gpu_id); if (dev == NULL) return -EINVAL; @@ -250,7 +243,7 @@ static long kfd_ioctl_create_queue(struct file *filep, struct kfd_process *p, pdd = kfd_bind_process_to_device(dev, p); if (IS_ERR(pdd)) { - err = PTR_ERR(pdd); + err = -ESRCH; goto err_bind_process; } @@ -263,33 +256,26 @@ static long kfd_ioctl_create_queue(struct file *filep, struct kfd_process *p, if (err != 0) goto err_create_queue; - args.queue_id = queue_id; + args->queue_id = queue_id; /* Return gpu_id as doorbell offset for mmap usage */ - args.doorbell_offset = args.gpu_id << PAGE_SHIFT; - - if (copy_to_user(arg, &args, sizeof(args))) { - err = -EFAULT; - goto err_copy_args_out; - } + args->doorbell_offset = args->gpu_id << PAGE_SHIFT; mutex_unlock(&p->mutex); - pr_debug("kfd: queue id %d was created successfully\n", args.queue_id); + pr_debug("kfd: queue id %d was created successfully\n", args->queue_id); pr_debug("ring buffer address == 0x%016llX\n", - args.ring_base_address); + args->ring_base_address); pr_debug("read ptr address == 0x%016llX\n", - args.read_pointer_address); + args->read_pointer_address); pr_debug("write ptr address == 0x%016llX\n", - args.write_pointer_address); + args->write_pointer_address); return 0; -err_copy_args_out: - pqm_destroy_queue(&p->pqm, queue_id); err_create_queue: err_bind_process: mutex_unlock(&p->mutex); @@ -297,99 +283,90 @@ err_bind_process: } static int kfd_ioctl_destroy_queue(struct file *filp, struct kfd_process *p, - void __user *arg) + void *data) { int retval; - struct kfd_ioctl_destroy_queue_args args; - - if (copy_from_user(&args, arg, sizeof(args))) - return -EFAULT; + struct kfd_ioctl_destroy_queue_args *args = data; pr_debug("kfd: destroying queue id %d for PASID %d\n", - args.queue_id, + args->queue_id, p->pasid); mutex_lock(&p->mutex); - retval = pqm_destroy_queue(&p->pqm, args.queue_id); + retval = pqm_destroy_queue(&p->pqm, args->queue_id); mutex_unlock(&p->mutex); return retval; } static int kfd_ioctl_update_queue(struct file *filp, struct kfd_process *p, - void __user *arg) + void *data) { int retval; - struct kfd_ioctl_update_queue_args args; + struct kfd_ioctl_update_queue_args *args = data; struct queue_properties properties; - if (copy_from_user(&args, arg, sizeof(args))) - return -EFAULT; - - if (args.queue_percentage > KFD_MAX_QUEUE_PERCENTAGE) { + if (args->queue_percentage > KFD_MAX_QUEUE_PERCENTAGE) { pr_err("kfd: queue percentage must be between 0 to KFD_MAX_QUEUE_PERCENTAGE\n"); return -EINVAL; } - if (args.queue_priority > KFD_MAX_QUEUE_PRIORITY) { + if (args->queue_priority > KFD_MAX_QUEUE_PRIORITY) { pr_err("kfd: queue priority must be between 0 to KFD_MAX_QUEUE_PRIORITY\n"); return -EINVAL; } - if ((args.ring_base_address) && + if ((args->ring_base_address) && (!access_ok(VERIFY_WRITE, - (const void __user *) args.ring_base_address, + (const void __user *) args->ring_base_address, sizeof(uint64_t)))) { pr_err("kfd: can't access ring base address\n"); return -EFAULT; } - if (!is_power_of_2(args.ring_size) && (args.ring_size != 0)) { + if (!is_power_of_2(args->ring_size) && (args->ring_size != 0)) { pr_err("kfd: ring size must be a power of 2 or 0\n"); return -EINVAL; } - properties.queue_address = args.ring_base_address; - properties.queue_size = args.ring_size; - properties.queue_percent = args.queue_percentage; - properties.priority = args.queue_priority; + properties.queue_address = args->ring_base_address; + properties.queue_size = args->ring_size; + properties.queue_percent = args->queue_percentage; + properties.priority = args->queue_priority; pr_debug("kfd: updating queue id %d for PASID %d\n", - args.queue_id, p->pasid); + args->queue_id, p->pasid); mutex_lock(&p->mutex); - retval = pqm_update_queue(&p->pqm, args.queue_id, &properties); + retval = pqm_update_queue(&p->pqm, args->queue_id, &properties); mutex_unlock(&p->mutex); return retval; } -static long kfd_ioctl_set_memory_policy(struct file *filep, - struct kfd_process *p, void __user *arg) +static int kfd_ioctl_set_memory_policy(struct file *filep, + struct kfd_process *p, void *data) { - struct kfd_ioctl_set_memory_policy_args args; + struct kfd_ioctl_set_memory_policy_args *args = data; struct kfd_dev *dev; int err = 0; struct kfd_process_device *pdd; enum cache_policy default_policy, alternate_policy; - if (copy_from_user(&args, arg, sizeof(args))) - return -EFAULT; - - if (args.default_policy != KFD_IOC_CACHE_POLICY_COHERENT - && args.default_policy != KFD_IOC_CACHE_POLICY_NONCOHERENT) { + if (args->default_policy != KFD_IOC_CACHE_POLICY_COHERENT + && args->default_policy != KFD_IOC_CACHE_POLICY_NONCOHERENT) { return -EINVAL; } - if (args.alternate_policy != KFD_IOC_CACHE_POLICY_COHERENT - && args.alternate_policy != KFD_IOC_CACHE_POLICY_NONCOHERENT) { + if (args->alternate_policy != KFD_IOC_CACHE_POLICY_COHERENT + && args->alternate_policy != KFD_IOC_CACHE_POLICY_NONCOHERENT) { return -EINVAL; } - dev = kfd_device_by_id(args.gpu_id); + dev = kfd_device_by_id(args->gpu_id); if (dev == NULL) return -EINVAL; @@ -397,23 +374,23 @@ static long kfd_ioctl_set_memory_policy(struct file *filep, pdd = kfd_bind_process_to_device(dev, p); if (IS_ERR(pdd)) { - err = PTR_ERR(pdd); + err = -ESRCH; goto out; } - default_policy = (args.default_policy == KFD_IOC_CACHE_POLICY_COHERENT) + default_policy = (args->default_policy == KFD_IOC_CACHE_POLICY_COHERENT) ? cache_policy_coherent : cache_policy_noncoherent; alternate_policy = - (args.alternate_policy == KFD_IOC_CACHE_POLICY_COHERENT) + (args->alternate_policy == KFD_IOC_CACHE_POLICY_COHERENT) ? cache_policy_coherent : cache_policy_noncoherent; if (!dev->dqm->set_cache_memory_policy(dev->dqm, &pdd->qpd, default_policy, alternate_policy, - (void __user *)args.alternate_aperture_base, - args.alternate_aperture_size)) + (void __user *)args->alternate_aperture_base, + args->alternate_aperture_size)) err = -EINVAL; out: @@ -422,53 +399,44 @@ out: return err; } -static long kfd_ioctl_get_clock_counters(struct file *filep, - struct kfd_process *p, void __user *arg) +static int kfd_ioctl_get_clock_counters(struct file *filep, + struct kfd_process *p, void *data) { - struct kfd_ioctl_get_clock_counters_args args; + struct kfd_ioctl_get_clock_counters_args *args = data; struct kfd_dev *dev; struct timespec time; - if (copy_from_user(&args, arg, sizeof(args))) - return -EFAULT; - - dev = kfd_device_by_id(args.gpu_id); + dev = kfd_device_by_id(args->gpu_id); if (dev == NULL) return -EINVAL; /* Reading GPU clock counter from KGD */ - args.gpu_clock_counter = kfd2kgd->get_gpu_clock_counter(dev->kgd); + args->gpu_clock_counter = kfd2kgd->get_gpu_clock_counter(dev->kgd); /* No access to rdtsc. Using raw monotonic time */ getrawmonotonic(&time); - args.cpu_clock_counter = (uint64_t)timespec_to_ns(&time); + args->cpu_clock_counter = (uint64_t)timespec_to_ns(&time); get_monotonic_boottime(&time); - args.system_clock_counter = (uint64_t)timespec_to_ns(&time); + args->system_clock_counter = (uint64_t)timespec_to_ns(&time); /* Since the counter is in nano-seconds we use 1GHz frequency */ - args.system_clock_freq = 1000000000; - - if (copy_to_user(arg, &args, sizeof(args))) - return -EFAULT; + args->system_clock_freq = 1000000000; return 0; } static int kfd_ioctl_get_process_apertures(struct file *filp, - struct kfd_process *p, void __user *arg) + struct kfd_process *p, void *data) { - struct kfd_ioctl_get_process_apertures_args args; + struct kfd_ioctl_get_process_apertures_args *args = data; struct kfd_process_device_apertures *pAperture; struct kfd_process_device *pdd; dev_dbg(kfd_device, "get apertures for PASID %d", p->pasid); - if (copy_from_user(&args, arg, sizeof(args))) - return -EFAULT; - - args.num_of_nodes = 0; + args->num_of_nodes = 0; mutex_lock(&p->mutex); @@ -477,7 +445,8 @@ static int kfd_ioctl_get_process_apertures(struct file *filp, /* Run over all pdd of the process */ pdd = kfd_get_first_process_device_data(p); do { - pAperture = &args.process_apertures[args.num_of_nodes]; + pAperture = + &args->process_apertures[args->num_of_nodes]; pAperture->gpu_id = pdd->dev->id; pAperture->lds_base = pdd->lds_base; pAperture->lds_limit = pdd->lds_limit; @@ -487,7 +456,7 @@ static int kfd_ioctl_get_process_apertures(struct file *filp, pAperture->scratch_limit = pdd->scratch_limit; dev_dbg(kfd_device, - "node id %u\n", args.num_of_nodes); + "node id %u\n", args->num_of_nodes); dev_dbg(kfd_device, "gpu id %u\n", pdd->dev->id); dev_dbg(kfd_device, @@ -503,80 +472,131 @@ static int kfd_ioctl_get_process_apertures(struct file *filp, dev_dbg(kfd_device, "scratch_limit %llX\n", pdd->scratch_limit); - args.num_of_nodes++; + args->num_of_nodes++; } while ((pdd = kfd_get_next_process_device_data(p, pdd)) != NULL && - (args.num_of_nodes < NUM_OF_SUPPORTED_GPUS)); + (args->num_of_nodes < NUM_OF_SUPPORTED_GPUS)); } mutex_unlock(&p->mutex); - if (copy_to_user(arg, &args, sizeof(args))) - return -EFAULT; - return 0; } +#define AMDKFD_IOCTL_DEF(ioctl, _func, _flags) \ + [_IOC_NR(ioctl)] = {.cmd = ioctl, .func = _func, .flags = _flags, .cmd_drv = 0, .name = #ioctl} + +/** Ioctl table */ +static const struct amdkfd_ioctl_desc amdkfd_ioctls[] = { + AMDKFD_IOCTL_DEF(AMDKFD_IOC_GET_VERSION, + kfd_ioctl_get_version, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_CREATE_QUEUE, + kfd_ioctl_create_queue, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_DESTROY_QUEUE, + kfd_ioctl_destroy_queue, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_SET_MEMORY_POLICY, + kfd_ioctl_set_memory_policy, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_GET_CLOCK_COUNTERS, + kfd_ioctl_get_clock_counters, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_GET_PROCESS_APERTURES, + kfd_ioctl_get_process_apertures, 0), + + AMDKFD_IOCTL_DEF(AMDKFD_IOC_UPDATE_QUEUE, + kfd_ioctl_update_queue, 0), +}; + +#define AMDKFD_CORE_IOCTL_COUNT ARRAY_SIZE(amdkfd_ioctls) + static long kfd_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { struct kfd_process *process; - long err = -EINVAL; + amdkfd_ioctl_t *func; + const struct amdkfd_ioctl_desc *ioctl = NULL; + unsigned int nr = _IOC_NR(cmd); + char stack_kdata[128]; + char *kdata = NULL; + unsigned int usize, asize; + int retcode = -EINVAL; - dev_dbg(kfd_device, - "ioctl cmd 0x%x (#%d), arg 0x%lx\n", - cmd, _IOC_NR(cmd), arg); + if (nr >= AMDKFD_CORE_IOCTL_COUNT) + goto err_i1; + + if ((nr >= AMDKFD_COMMAND_START) && (nr < AMDKFD_COMMAND_END)) { + u32 amdkfd_size; + + ioctl = &amdkfd_ioctls[nr]; + + amdkfd_size = _IOC_SIZE(ioctl->cmd); + usize = asize = _IOC_SIZE(cmd); + if (amdkfd_size > asize) + asize = amdkfd_size; + + cmd = ioctl->cmd; + } else + goto err_i1; + + dev_dbg(kfd_device, "ioctl cmd 0x%x (#%d), arg 0x%lx\n", cmd, nr, arg); process = kfd_get_process(current); - if (IS_ERR(process)) - return PTR_ERR(process); + if (IS_ERR(process)) { + dev_dbg(kfd_device, "no process\n"); + goto err_i1; + } - switch (cmd) { - case KFD_IOC_GET_VERSION: - err = kfd_ioctl_get_version(filep, process, (void __user *)arg); - break; - case KFD_IOC_CREATE_QUEUE: - err = kfd_ioctl_create_queue(filep, process, - (void __user *)arg); - break; - - case KFD_IOC_DESTROY_QUEUE: - err = kfd_ioctl_destroy_queue(filep, process, - (void __user *)arg); - break; - - case KFD_IOC_SET_MEMORY_POLICY: - err = kfd_ioctl_set_memory_policy(filep, process, - (void __user *)arg); - break; - - case KFD_IOC_GET_CLOCK_COUNTERS: - err = kfd_ioctl_get_clock_counters(filep, process, - (void __user *)arg); - break; - - case KFD_IOC_GET_PROCESS_APERTURES: - err = kfd_ioctl_get_process_apertures(filep, process, - (void __user *)arg); - break; - - case KFD_IOC_UPDATE_QUEUE: - err = kfd_ioctl_update_queue(filep, process, - (void __user *)arg); - break; - - default: - dev_err(kfd_device, - "unknown ioctl cmd 0x%x, arg 0x%lx)\n", - cmd, arg); - err = -EINVAL; - break; + /* Do not trust userspace, use our own definition */ + func = ioctl->func; + + if (unlikely(!func)) { + dev_dbg(kfd_device, "no function\n"); + retcode = -EINVAL; + goto err_i1; } - if (err < 0) - dev_err(kfd_device, - "ioctl error %ld for ioctl cmd 0x%x (#%d)\n", - err, cmd, _IOC_NR(cmd)); + if (cmd & (IOC_IN | IOC_OUT)) { + if (asize <= sizeof(stack_kdata)) { + kdata = stack_kdata; + } else { + kdata = kmalloc(asize, GFP_KERNEL); + if (!kdata) { + retcode = -ENOMEM; + goto err_i1; + } + } + if (asize > usize) + memset(kdata + usize, 0, asize - usize); + } - return err; + if (cmd & IOC_IN) { + if (copy_from_user(kdata, (void __user *)arg, usize) != 0) { + retcode = -EFAULT; + goto err_i1; + } + } else if (cmd & IOC_OUT) { + memset(kdata, 0, usize); + } + + retcode = func(filep, process, kdata); + + if (cmd & IOC_OUT) + if (copy_to_user((void __user *)arg, kdata, usize) != 0) + retcode = -EFAULT; + +err_i1: + if (!ioctl) + dev_dbg(kfd_device, "invalid ioctl: pid=%d, cmd=0x%02x, nr=0x%02x\n", + task_pid_nr(current), cmd, nr); + + if (kdata != stack_kdata) + kfree(kdata); + + if (retcode) + dev_dbg(kfd_device, "ret = %d\n", retcode); + + return retcode; } static int kfd_mmap(struct file *filp, struct vm_area_struct *vma) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device.c b/drivers/gpu/drm/amd/amdkfd/kfd_device.c index 43884ebd4303..25bc47f3c1cf 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device.c @@ -26,6 +26,7 @@ #include <linux/slab.h> #include "kfd_priv.h" #include "kfd_device_queue_manager.h" +#include "kfd_pm4_headers.h" #define MQD_SIZE_ALIGNED 768 @@ -169,9 +170,8 @@ bool kgd2kfd_device_init(struct kfd_dev *kfd, kfd->shared_resources = *gpu_resources; /* calculate max size of mqds needed for queues */ - size = max_num_of_processes * - max_num_of_queues_per_process * - kfd->device_info->mqd_size_aligned; + size = max_num_of_queues_per_device * + kfd->device_info->mqd_size_aligned; /* add another 512KB for all other allocations on gart */ size += 512 * 1024; @@ -192,13 +192,6 @@ bool kgd2kfd_device_init(struct kfd_dev *kfd, goto kfd_topology_add_device_error; } - if (kfd_interrupt_init(kfd)) { - dev_err(kfd_device, - "Error initializing interrupts for device (%x:%x)\n", - kfd->pdev->vendor, kfd->pdev->device); - goto kfd_interrupt_error; - } - if (!device_iommu_pasid_init(kfd)) { dev_err(kfd_device, "Error initializing iommuv2 for device (%x:%x)\n", @@ -237,8 +230,6 @@ dqm_start_error: device_queue_manager_error: amd_iommu_free_device(kfd->pdev); device_iommu_pasid_error: - kfd_interrupt_exit(kfd); -kfd_interrupt_error: kfd_topology_remove_device(kfd); kfd_topology_add_device_error: kfd2kgd->fini_sa_manager(kfd->kgd); @@ -254,7 +245,6 @@ void kgd2kfd_device_exit(struct kfd_dev *kfd) if (kfd->init_complete) { device_queue_manager_uninit(kfd->dqm); amd_iommu_free_device(kfd->pdev); - kfd_interrupt_exit(kfd); kfd_topology_remove_device(kfd); } @@ -296,13 +286,5 @@ int kgd2kfd_resume(struct kfd_dev *kfd) /* This is called directly from KGD at ISR. */ void kgd2kfd_interrupt(struct kfd_dev *kfd, const void *ih_ring_entry) { - if (kfd->init_complete) { - spin_lock(&kfd->interrupt_lock); - - if (kfd->interrupts_active - && enqueue_ih_ring_entry(kfd, ih_ring_entry)) - schedule_work(&kfd->interrupt_work); - - spin_unlock(&kfd->interrupt_lock); - } + /* Process interrupts / schedule work as necessary */ } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index 924e90c072e5..0fd592799d58 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -161,6 +161,9 @@ static void deallocate_vmid(struct device_queue_manager *dqm, { int bit = qpd->vmid - KFD_VMID_START_OFFSET; + /* Release the vmid mapping */ + set_pasid_vmid_mapping(dqm, 0, qpd->vmid); + set_bit(bit, (unsigned long *)&dqm->vmid_bitmap); qpd->vmid = 0; q->properties.vmid = 0; @@ -180,6 +183,13 @@ static int create_queue_nocpsch(struct device_queue_manager *dqm, mutex_lock(&dqm->lock); + if (dqm->total_queue_count >= max_num_of_queues_per_device) { + pr_warn("amdkfd: Can't create new usermode queue because %d queues were already created\n", + dqm->total_queue_count); + mutex_unlock(&dqm->lock); + return -EPERM; + } + if (list_empty(&qpd->queues_list)) { retval = allocate_vmid(dqm, qpd, q); if (retval != 0) { @@ -204,6 +214,14 @@ static int create_queue_nocpsch(struct device_queue_manager *dqm, list_add(&q->list, &qpd->queues_list); dqm->queue_count++; + /* + * Unconditionally increment this counter, regardless of the queue's + * type or whether the queue is active. + */ + dqm->total_queue_count++; + pr_debug("Total of %d queues are accountable so far\n", + dqm->total_queue_count); + mutex_unlock(&dqm->lock); return 0; } @@ -272,6 +290,18 @@ static int create_compute_queue_nocpsch(struct device_queue_manager *dqm, return retval; } + pr_debug("kfd: loading mqd to hqd on pipe (%d) queue (%d)\n", + q->pipe, + q->queue); + + retval = mqd->load_mqd(mqd, q->mqd, q->pipe, + q->queue, (uint32_t __user *) q->properties.write_ptr); + if (retval != 0) { + deallocate_hqd(dqm, q); + mqd->uninit_mqd(mqd, q->mqd, q->mqd_mem_obj); + return retval; + } + return 0; } @@ -311,6 +341,15 @@ static int destroy_queue_nocpsch(struct device_queue_manager *dqm, if (list_empty(&qpd->queues_list)) deallocate_vmid(dqm, qpd, q); dqm->queue_count--; + + /* + * Unconditionally decrement this counter, regardless of the queue's + * type + */ + dqm->total_queue_count--; + pr_debug("Total of %d queues are accountable so far\n", + dqm->total_queue_count); + out: mutex_unlock(&dqm->lock); return retval; @@ -320,6 +359,7 @@ static int update_queue(struct device_queue_manager *dqm, struct queue *q) { int retval; struct mqd_manager *mqd; + bool prev_active = false; BUG_ON(!dqm || !q || !q->mqd); @@ -330,10 +370,18 @@ static int update_queue(struct device_queue_manager *dqm, struct queue *q) return -ENOMEM; } - retval = mqd->update_mqd(mqd, q->mqd, &q->properties); if (q->properties.is_active == true) + prev_active = true; + + /* + * + * check active state vs. the previous state + * and modify counter accordingly + */ + retval = mqd->update_mqd(mqd, q->mqd, &q->properties); + if ((q->properties.is_active == true) && (prev_active == false)) dqm->queue_count++; - else + else if ((q->properties.is_active == false) && (prev_active == true)) dqm->queue_count--; if (sched_policy != KFD_SCHED_POLICY_NO_HWS) @@ -517,10 +565,14 @@ static int init_pipelines(struct device_queue_manager *dqm, for (i = 0; i < pipes_num; i++) { inx = i + first_pipe; + /* + * HPD buffer on GTT is allocated by amdkfd, no need to waste + * space in GTT for pipelines we don't initialize + */ pipe_hpd_addr = dqm->pipelines_addr + i * CIK_HPD_EOP_BYTES; pr_debug("kfd: pipeline address %llX\n", pipe_hpd_addr); /* = log2(bytes/4)-1 */ - kfd2kgd->init_pipeline(dqm->dev->kgd, i, + kfd2kgd->init_pipeline(dqm->dev->kgd, inx, CIK_HPD_EOP_BYTES_LOG2 - 3, pipe_hpd_addr); } @@ -536,7 +588,7 @@ static int init_scheduler(struct device_queue_manager *dqm) pr_debug("kfd: In %s\n", __func__); - retval = init_pipelines(dqm, get_pipes_num(dqm), KFD_DQM_FIRST_PIPE); + retval = init_pipelines(dqm, get_pipes_num(dqm), get_first_pipe(dqm)); if (retval != 0) return retval; @@ -728,6 +780,21 @@ static int create_kernel_queue_cpsch(struct device_queue_manager *dqm, pr_debug("kfd: In func %s\n", __func__); mutex_lock(&dqm->lock); + if (dqm->total_queue_count >= max_num_of_queues_per_device) { + pr_warn("amdkfd: Can't create new kernel queue because %d queues were already created\n", + dqm->total_queue_count); + mutex_unlock(&dqm->lock); + return -EPERM; + } + + /* + * Unconditionally increment this counter, regardless of the queue's + * type or whether the queue is active. + */ + dqm->total_queue_count++; + pr_debug("Total of %d queues are accountable so far\n", + dqm->total_queue_count); + list_add(&kq->list, &qpd->priv_queue_list); dqm->queue_count++; qpd->is_debug = true; @@ -751,6 +818,13 @@ static void destroy_kernel_queue_cpsch(struct device_queue_manager *dqm, dqm->queue_count--; qpd->is_debug = false; execute_queues_cpsch(dqm, false); + /* + * Unconditionally decrement this counter, regardless of the queue's + * type. + */ + dqm->total_queue_count--; + pr_debug("Total of %d queues are accountable so far\n", + dqm->total_queue_count); mutex_unlock(&dqm->lock); } @@ -769,6 +843,13 @@ static int create_queue_cpsch(struct device_queue_manager *dqm, struct queue *q, mutex_lock(&dqm->lock); + if (dqm->total_queue_count >= max_num_of_queues_per_device) { + pr_warn("amdkfd: Can't create new usermode queue because %d queues were already created\n", + dqm->total_queue_count); + retval = -EPERM; + goto out; + } + mqd = dqm->get_mqd_manager(dqm, KFD_MQD_TYPE_CIK_CP); if (mqd == NULL) { mutex_unlock(&dqm->lock); @@ -786,6 +867,15 @@ static int create_queue_cpsch(struct device_queue_manager *dqm, struct queue *q, retval = execute_queues_cpsch(dqm, false); } + /* + * Unconditionally increment this counter, regardless of the queue's + * type or whether the queue is active. + */ + dqm->total_queue_count++; + + pr_debug("Total of %d queues are accountable so far\n", + dqm->total_queue_count); + out: mutex_unlock(&dqm->lock); return retval; @@ -906,6 +996,14 @@ static int destroy_queue_cpsch(struct device_queue_manager *dqm, mqd->uninit_mqd(mqd, q->mqd, q->mqd_mem_obj); + /* + * Unconditionally decrement this counter, regardless of the queue's + * type + */ + dqm->total_queue_count--; + pr_debug("Total of %d queues are accountable so far\n", + dqm->total_queue_count); + mutex_unlock(&dqm->lock); return 0; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h index c3f189e8ae35..52035bf0c1cb 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h @@ -130,6 +130,7 @@ struct device_queue_manager { struct list_head queues; unsigned int processes_count; unsigned int queue_count; + unsigned int total_queue_count; unsigned int next_pipe_to_allocate; unsigned int *allocated_queues; unsigned int vmid_bitmap; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_interrupt.c b/drivers/gpu/drm/amd/amdkfd/kfd_interrupt.c deleted file mode 100644 index 5b999095a1f7..000000000000 --- a/drivers/gpu/drm/amd/amdkfd/kfd_interrupt.c +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2014 Advanced Micro Devices, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * KFD Interrupts. - * - * AMD GPUs deliver interrupts by pushing an interrupt description onto the - * interrupt ring and then sending an interrupt. KGD receives the interrupt - * in ISR and sends us a pointer to each new entry on the interrupt ring. - * - * We generally can't process interrupt-signaled events from ISR, so we call - * out to each interrupt client module (currently only the scheduler) to ask if - * each interrupt is interesting. If they return true, then it requires further - * processing so we copy it to an internal interrupt ring and call each - * interrupt client again from a work-queue. - * - * There's no acknowledgment for the interrupts we use. The hardware simply - * queues a new interrupt each time without waiting. - * - * The fixed-size internal queue means that it's possible for us to lose - * interrupts because we have no back-pressure to the hardware. - */ - -#include <linux/slab.h> -#include <linux/device.h> -#include "kfd_priv.h" - -#define KFD_INTERRUPT_RING_SIZE 256 - -static void interrupt_wq(struct work_struct *); - -int kfd_interrupt_init(struct kfd_dev *kfd) -{ - void *interrupt_ring = kmalloc_array(KFD_INTERRUPT_RING_SIZE, - kfd->device_info->ih_ring_entry_size, - GFP_KERNEL); - if (!interrupt_ring) - return -ENOMEM; - - kfd->interrupt_ring = interrupt_ring; - kfd->interrupt_ring_size = - KFD_INTERRUPT_RING_SIZE * kfd->device_info->ih_ring_entry_size; - atomic_set(&kfd->interrupt_ring_wptr, 0); - atomic_set(&kfd->interrupt_ring_rptr, 0); - - spin_lock_init(&kfd->interrupt_lock); - - INIT_WORK(&kfd->interrupt_work, interrupt_wq); - - kfd->interrupts_active = true; - - /* - * After this function returns, the interrupt will be enabled. This - * barrier ensures that the interrupt running on a different processor - * sees all the above writes. - */ - smp_wmb(); - - return 0; -} - -void kfd_interrupt_exit(struct kfd_dev *kfd) -{ - /* - * Stop the interrupt handler from writing to the ring and scheduling - * workqueue items. The spinlock ensures that any interrupt running - * after we have unlocked sees interrupts_active = false. - */ - unsigned long flags; - - spin_lock_irqsave(&kfd->interrupt_lock, flags); - kfd->interrupts_active = false; - spin_unlock_irqrestore(&kfd->interrupt_lock, flags); - - /* - * Flush_scheduled_work ensures that there are no outstanding - * work-queue items that will access interrupt_ring. New work items - * can't be created because we stopped interrupt handling above. - */ - flush_scheduled_work(); - - kfree(kfd->interrupt_ring); -} - -/* - * This assumes that it can't be called concurrently with itself - * but only with dequeue_ih_ring_entry. - */ -bool enqueue_ih_ring_entry(struct kfd_dev *kfd, const void *ih_ring_entry) -{ - unsigned int rptr = atomic_read(&kfd->interrupt_ring_rptr); - unsigned int wptr = atomic_read(&kfd->interrupt_ring_wptr); - - if ((rptr - wptr) % kfd->interrupt_ring_size == - kfd->device_info->ih_ring_entry_size) { - /* This is very bad, the system is likely to hang. */ - dev_err_ratelimited(kfd_chardev(), - "Interrupt ring overflow, dropping interrupt.\n"); - return false; - } - - memcpy(kfd->interrupt_ring + wptr, ih_ring_entry, - kfd->device_info->ih_ring_entry_size); - - wptr = (wptr + kfd->device_info->ih_ring_entry_size) % - kfd->interrupt_ring_size; - smp_wmb(); /* Ensure memcpy'd data is visible before wptr update. */ - atomic_set(&kfd->interrupt_ring_wptr, wptr); - - return true; -} - -/* - * This assumes that it can't be called concurrently with itself - * but only with enqueue_ih_ring_entry. - */ -static bool dequeue_ih_ring_entry(struct kfd_dev *kfd, void *ih_ring_entry) -{ - /* - * Assume that wait queues have an implicit barrier, i.e. anything that - * happened in the ISR before it queued work is visible. - */ - - unsigned int wptr = atomic_read(&kfd->interrupt_ring_wptr); - unsigned int rptr = atomic_read(&kfd->interrupt_ring_rptr); - - if (rptr == wptr) - return false; - - memcpy(ih_ring_entry, kfd->interrupt_ring + rptr, - kfd->device_info->ih_ring_entry_size); - - rptr = (rptr + kfd->device_info->ih_ring_entry_size) % - kfd->interrupt_ring_size; - - /* - * Ensure the rptr write update is not visible until - * memcpy has finished reading. - */ - smp_mb(); - atomic_set(&kfd->interrupt_ring_rptr, rptr); - - return true; -} - -static void interrupt_wq(struct work_struct *work) -{ - struct kfd_dev *dev = container_of(work, struct kfd_dev, - interrupt_work); - - uint32_t ih_ring_entry[DIV_ROUND_UP( - dev->device_info->ih_ring_entry_size, - sizeof(uint32_t))]; - - while (dequeue_ih_ring_entry(dev, ih_ring_entry)) - ; -} diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_module.c b/drivers/gpu/drm/amd/amdkfd/kfd_module.c index 95d5af138e6e..1c385c23dd0b 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_module.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_module.c @@ -50,15 +50,10 @@ module_param(sched_policy, int, 0444); MODULE_PARM_DESC(sched_policy, "Kernel cmdline parameter that defines the amdkfd scheduling policy"); -int max_num_of_processes = KFD_MAX_NUM_OF_PROCESSES_DEFAULT; -module_param(max_num_of_processes, int, 0444); -MODULE_PARM_DESC(max_num_of_processes, - "Kernel cmdline parameter that defines the amdkfd maximum number of supported processes"); - -int max_num_of_queues_per_process = KFD_MAX_NUM_OF_QUEUES_PER_PROCESS_DEFAULT; -module_param(max_num_of_queues_per_process, int, 0444); -MODULE_PARM_DESC(max_num_of_queues_per_process, - "Kernel cmdline parameter that defines the amdkfd maximum number of supported queues per process"); +int max_num_of_queues_per_device = KFD_MAX_NUM_OF_QUEUES_PER_DEVICE_DEFAULT; +module_param(max_num_of_queues_per_device, int, 0444); +MODULE_PARM_DESC(max_num_of_queues_per_device, + "Maximum number of supported queues per device (1 = Minimum, 4096 = default)"); bool kgd2kfd_init(unsigned interface_version, const struct kfd2kgd_calls *f2g, @@ -100,16 +95,10 @@ static int __init kfd_module_init(void) } /* Verify module parameters */ - if ((max_num_of_processes < 0) || - (max_num_of_processes > KFD_MAX_NUM_OF_PROCESSES)) { - pr_err("kfd: max_num_of_processes must be between 0 to KFD_MAX_NUM_OF_PROCESSES\n"); - return -1; - } - - if ((max_num_of_queues_per_process < 0) || - (max_num_of_queues_per_process > - KFD_MAX_NUM_OF_QUEUES_PER_PROCESS)) { - pr_err("kfd: max_num_of_queues_per_process must be between 0 to KFD_MAX_NUM_OF_QUEUES_PER_PROCESS\n"); + if ((max_num_of_queues_per_device < 1) || + (max_num_of_queues_per_device > + KFD_MAX_NUM_OF_QUEUES_PER_DEVICE)) { + pr_err("kfd: max_num_of_queues_per_device must be between 1 to KFD_MAX_NUM_OF_QUEUES_PER_DEVICE\n"); return -1; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c index adc31474e786..4c3828cf45bf 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c @@ -184,7 +184,7 @@ static bool is_occupied(struct mqd_manager *mm, void *mqd, uint32_t queue_id) { - return kfd2kgd->hqd_is_occupies(mm->dev->kgd, queue_address, + return kfd2kgd->hqd_is_occupied(mm->dev->kgd, queue_address, pipe_id, queue_id); } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_pasid.c b/drivers/gpu/drm/amd/amdkfd/kfd_pasid.c index 71699ad97d74..6cfe7f1f18cf 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_pasid.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_pasid.c @@ -30,9 +30,9 @@ static DEFINE_MUTEX(pasid_mutex); int kfd_pasid_init(void) { - pasid_limit = max_num_of_processes; + pasid_limit = KFD_MAX_NUM_OF_PROCESSES; - pasid_bitmap = kzalloc(BITS_TO_LONGS(pasid_limit), GFP_KERNEL); + pasid_bitmap = kcalloc(BITS_TO_LONGS(pasid_limit), sizeof(long), GFP_KERNEL); if (!pasid_bitmap) return -ENOMEM; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index f9fb81e3bb09..96dc10e8904a 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -52,20 +52,19 @@ #define kfd_alloc_struct(ptr_to_struct) \ ((typeof(ptr_to_struct)) kzalloc(sizeof(*ptr_to_struct), GFP_KERNEL)) -/* Kernel module parameter to specify maximum number of supported processes */ -extern int max_num_of_processes; - -#define KFD_MAX_NUM_OF_PROCESSES_DEFAULT 32 #define KFD_MAX_NUM_OF_PROCESSES 512 +#define KFD_MAX_NUM_OF_QUEUES_PER_PROCESS 1024 /* - * Kernel module parameter to specify maximum number of supported queues - * per process + * Kernel module parameter to specify maximum number of supported queues per + * device */ -extern int max_num_of_queues_per_process; +extern int max_num_of_queues_per_device; -#define KFD_MAX_NUM_OF_QUEUES_PER_PROCESS_DEFAULT 128 -#define KFD_MAX_NUM_OF_QUEUES_PER_PROCESS 1024 +#define KFD_MAX_NUM_OF_QUEUES_PER_DEVICE_DEFAULT 4096 +#define KFD_MAX_NUM_OF_QUEUES_PER_DEVICE \ + (KFD_MAX_NUM_OF_PROCESSES * \ + KFD_MAX_NUM_OF_QUEUES_PER_PROCESS) #define KFD_KERNEL_QUEUE_SIZE 2048 @@ -135,22 +134,10 @@ struct kfd_dev { struct kgd2kfd_shared_resources shared_resources; - void *interrupt_ring; - size_t interrupt_ring_size; - atomic_t interrupt_ring_rptr; - atomic_t interrupt_ring_wptr; - struct work_struct interrupt_work; - spinlock_t interrupt_lock; - /* QCM Device instance */ struct device_queue_manager *dqm; bool init_complete; - /* - * Interrupts of interest to KFD are copied - * from the HW ring into a SW ring. - */ - bool interrupts_active; }; /* KGD2KFD callbacks */ @@ -463,6 +450,24 @@ struct kfd_process { bool is_32bit_user_mode; }; +/** + * Ioctl function type. + * + * \param filep pointer to file structure. + * \param p amdkfd process pointer. + * \param data pointer to arg that was copied from user. + */ +typedef int amdkfd_ioctl_t(struct file *filep, struct kfd_process *p, + void *data); + +struct amdkfd_ioctl_desc { + unsigned int cmd; + int flags; + amdkfd_ioctl_t *func; + unsigned int cmd_drv; + const char *name; +}; + void kfd_process_create_wq(void); void kfd_process_destroy_wq(void); struct kfd_process *kfd_create_process(const struct task_struct *); @@ -513,10 +518,7 @@ struct kfd_dev *kfd_device_by_pci_dev(const struct pci_dev *pdev); struct kfd_dev *kfd_topology_enum_kfd_devices(uint8_t idx); /* Interrupts */ -int kfd_interrupt_init(struct kfd_dev *dev); -void kfd_interrupt_exit(struct kfd_dev *dev); void kgd2kfd_interrupt(struct kfd_dev *kfd, const void *ih_ring_entry); -bool enqueue_ih_ring_entry(struct kfd_dev *kfd, const void *ih_ring_entry); /* Power Management */ void kgd2kfd_suspend(struct kfd_dev *kfd); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c index 47526780d736..2fda1927bff7 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c @@ -54,11 +54,11 @@ static int find_available_queue_slot(struct process_queue_manager *pqm, pr_debug("kfd: in %s\n", __func__); found = find_first_zero_bit(pqm->queue_slot_bitmap, - max_num_of_queues_per_process); + KFD_MAX_NUM_OF_QUEUES_PER_PROCESS); pr_debug("kfd: the new slot id %lu\n", found); - if (found >= max_num_of_queues_per_process) { + if (found >= KFD_MAX_NUM_OF_QUEUES_PER_PROCESS) { pr_info("amdkfd: Can not open more queues for process with pasid %d\n", pqm->process->pasid); return -ENOMEM; @@ -76,7 +76,7 @@ int pqm_init(struct process_queue_manager *pqm, struct kfd_process *p) INIT_LIST_HEAD(&pqm->queues); pqm->queue_slot_bitmap = - kzalloc(DIV_ROUND_UP(max_num_of_queues_per_process, + kzalloc(DIV_ROUND_UP(KFD_MAX_NUM_OF_QUEUES_PER_PROCESS, BITS_PER_BYTE), GFP_KERNEL); if (pqm->queue_slot_bitmap == NULL) return -ENOMEM; @@ -203,6 +203,7 @@ int pqm_create_queue(struct process_queue_manager *pqm, pqn->kq = NULL; retval = dev->dqm->create_queue(dev->dqm, q, &pdd->qpd, &q->properties.vmid); + pr_debug("DQM returned %d for create_queue\n", retval); print_queue(q); break; case KFD_QUEUE_TYPE_DIQ: @@ -222,7 +223,7 @@ int pqm_create_queue(struct process_queue_manager *pqm, } if (retval != 0) { - pr_err("kfd: error dqm create queue\n"); + pr_debug("Error dqm create queue\n"); goto err_create_queue; } @@ -241,7 +242,10 @@ int pqm_create_queue(struct process_queue_manager *pqm, err_create_queue: kfree(pqn); err_allocate_pqn: + /* check if queues list is empty unregister process from device */ clear_bit(*qid, pqm->queue_slot_bitmap); + if (list_empty(&pqm->queues)) + dev->dqm->unregister_process(dev->dqm, &pdd->qpd); return retval; } @@ -311,7 +315,11 @@ int pqm_update_queue(struct process_queue_manager *pqm, unsigned int qid, BUG_ON(!pqm); pqn = get_queue_by_qid(pqm, qid); - BUG_ON(!pqn); + if (!pqn) { + pr_debug("amdkfd: No queue %d exists for update operation\n", + qid); + return -EFAULT; + } pqn->q->properties.queue_address = p->queue_address; pqn->q->properties.queue_size = p->queue_size; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c index b11792d7e70e..cca1708fd811 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c @@ -921,7 +921,7 @@ static int kfd_build_sysfs_node_tree(void) uint32_t i = 0; list_for_each_entry(dev, &topology_device_list, list) { - ret = kfd_build_sysfs_node_entry(dev, 0); + ret = kfd_build_sysfs_node_entry(dev, i); if (ret < 0) return ret; i++; diff --git a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h index 47b551970a14..96a512208fad 100644 --- a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h +++ b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h @@ -183,7 +183,7 @@ struct kfd2kgd_calls { int (*hqd_load)(struct kgd_dev *kgd, void *mqd, uint32_t pipe_id, uint32_t queue_id, uint32_t __user *wptr); - bool (*hqd_is_occupies)(struct kgd_dev *kgd, uint64_t queue_address, + bool (*hqd_is_occupied)(struct kgd_dev *kgd, uint64_t queue_address, uint32_t pipe_id, uint32_t queue_id); int (*hqd_destroy)(struct kgd_dev *kgd, uint32_t reset_type, diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.c b/drivers/gpu/drm/cirrus/cirrus_drv.c index c2a1cba1e984..b9140032962d 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.c +++ b/drivers/gpu/drm/cirrus/cirrus_drv.c @@ -16,9 +16,12 @@ #include "cirrus_drv.h" int cirrus_modeset = -1; +int cirrus_bpp = 24; MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); module_param_named(modeset, cirrus_modeset, int, 0400); +MODULE_PARM_DESC(bpp, "Max bits-per-pixel (default:24)"); +module_param_named(bpp, cirrus_bpp, int, 0400); /* * This is the generic driver code. This binds the driver to the drm core, diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h index 693a4565c4ff..705061537a27 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.h +++ b/drivers/gpu/drm/cirrus/cirrus_drv.h @@ -262,4 +262,7 @@ static inline void cirrus_bo_unreserve(struct cirrus_bo *bo) int cirrus_bo_push_sysram(struct cirrus_bo *bo); int cirrus_bo_pin(struct cirrus_bo *bo, u32 pl_flag, u64 *gpu_addr); + +extern int cirrus_bpp; + #endif /* __CIRRUS_DRV_H__ */ diff --git a/drivers/gpu/drm/cirrus/cirrus_main.c b/drivers/gpu/drm/cirrus/cirrus_main.c index 4c2d68e9102d..e4b976658087 100644 --- a/drivers/gpu/drm/cirrus/cirrus_main.c +++ b/drivers/gpu/drm/cirrus/cirrus_main.c @@ -320,6 +320,8 @@ bool cirrus_check_framebuffer(struct cirrus_device *cdev, int width, int height, const int max_pitch = 0x1FF << 3; /* (4096 - 1) & ~111b bytes */ const int max_size = cdev->mc.vram_size; + if (bpp > cirrus_bpp) + return false; if (bpp > 32) return false; diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index 99d4a74ffeaf..61385f2298bf 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -501,8 +501,13 @@ static int cirrus_vga_get_modes(struct drm_connector *connector) int count; /* Just add a static list of modes */ - count = drm_add_modes_noedid(connector, 1280, 1024); - drm_set_preferred_mode(connector, 1024, 768); + if (cirrus_bpp <= 24) { + count = drm_add_modes_noedid(connector, 1280, 1024); + drm_set_preferred_mode(connector, 1024, 768); + } else { + count = drm_add_modes_noedid(connector, 800, 600); + drm_set_preferred_mode(connector, 800, 600); + } return count; } diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 52ce26d6b4fb..dc386ebe5193 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -145,6 +145,31 @@ int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_ } EXPORT_SYMBOL(drm_fb_helper_add_one_connector); +static void remove_from_modeset(struct drm_mode_set *set, + struct drm_connector *connector) +{ + int i, j; + + for (i = 0; i < set->num_connectors; i++) { + if (set->connectors[i] == connector) + break; + } + + if (i == set->num_connectors) + return; + + for (j = i + 1; j < set->num_connectors; j++) { + set->connectors[j - 1] = set->connectors[j]; + } + set->num_connectors--; + + /* because i915 is pissy about this.. + * TODO maybe need to makes sure we set it back to !=NULL somewhere? + */ + if (set->num_connectors == 0) + set->fb = NULL; +} + int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector) { @@ -167,6 +192,11 @@ int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, } fb_helper->connector_count--; kfree(fb_helper_connector); + + /* also cleanup dangling references to the connector: */ + for (i = 0; i < fb_helper->crtc_count; i++) + remove_from_modeset(&fb_helper->crtc_info[i].mode_set, connector); + return 0; } EXPORT_SYMBOL(drm_fb_helper_remove_one_connector); @@ -741,7 +771,9 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) int i, j, rc = 0; int start; - drm_modeset_lock_all(dev); + if (__drm_modeset_lock_all(dev, !!oops_in_progress)) { + return -EBUSY; + } if (!drm_fb_helper_is_bound(fb_helper)) { drm_modeset_unlock_all(dev); return -EBUSY; @@ -915,7 +947,9 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, int ret = 0; int i; - drm_modeset_lock_all(dev); + if (__drm_modeset_lock_all(dev, !!oops_in_progress)) { + return -EBUSY; + } if (!drm_fb_helper_is_bound(fb_helper)) { drm_modeset_unlock_all(dev); return -EBUSY; diff --git a/drivers/gpu/drm/drm_vma_manager.c b/drivers/gpu/drm/drm_vma_manager.c index 63b471205072..68c1f32fb086 100644 --- a/drivers/gpu/drm/drm_vma_manager.c +++ b/drivers/gpu/drm/drm_vma_manager.c @@ -50,8 +50,7 @@ * * You must not use multiple offset managers on a single address_space. * Otherwise, mm-core will be unable to tear down memory mappings as the VM will - * no longer be linear. Please use VM_NONLINEAR in that case and implement your - * own offset managers. + * no longer be linear. * * This offset manager works on page-based addresses. That is, every argument * and return code (with the exception of drm_vma_node_offset_addr()) is given diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 121470a83d1a..1bcbe07cecfc 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -645,18 +645,6 @@ static int exynos_drm_init(void) if (!is_exynos) return -ENODEV; - /* - * Register device object only in case of Exynos SoC. - * - * Below codes resolves temporarily infinite loop issue incurred - * by Exynos drm driver when using multi-platform kernel. - * So these codes will be replaced with more generic way later. - */ - if (!of_machine_is_compatible("samsung,exynos3") && - !of_machine_is_compatible("samsung,exynos4") && - !of_machine_is_compatible("samsung,exynos5")) - return -ENODEV; - exynos_drm_pdev = platform_device_register_simple("exynos-drm", -1, NULL, 0); if (IS_ERR(exynos_drm_pdev)) diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 5765a161abdd..98051e8e855a 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1669,7 +1669,6 @@ static void hdmi_mode_apply(struct hdmi_context *hdata) static void hdmiphy_conf_reset(struct hdmi_context *hdata) { - u8 buffer[2]; u32 reg; clk_disable_unprepare(hdata->res.sclk_hdmi); @@ -1677,11 +1676,8 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata) clk_prepare_enable(hdata->res.sclk_hdmi); /* operation mode */ - buffer[0] = 0x1f; - buffer[1] = 0x00; - - if (hdata->hdmiphy_port) - i2c_master_send(hdata->hdmiphy_port, buffer, 2); + hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE, + HDMI_PHY_ENABLE_MODE_SET); if (hdata->type == HDMI_TYPE13) reg = HDMI_V13_PHY_RSTOUT; diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index 820b76234ef4..064ed6597def 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -1026,6 +1026,7 @@ static void mixer_win_disable(struct exynos_drm_manager *mgr, int zpos) static void mixer_wait_for_vblank(struct exynos_drm_manager *mgr) { struct mixer_context *mixer_ctx = mgr_to_mixer(mgr); + int err; mutex_lock(&mixer_ctx->mixer_mutex); if (!mixer_ctx->powered) { @@ -1034,7 +1035,11 @@ static void mixer_wait_for_vblank(struct exynos_drm_manager *mgr) } mutex_unlock(&mixer_ctx->mixer_mutex); - drm_vblank_get(mgr->crtc->dev, mixer_ctx->pipe); + err = drm_vblank_get(mgr->crtc->dev, mixer_ctx->pipe); + if (err < 0) { + DRM_DEBUG_KMS("failed to acquire vblank counter\n"); + return; + } atomic_set(&mixer_ctx->wait_vsync_event, 1); @@ -1262,8 +1267,6 @@ static int mixer_bind(struct device *dev, struct device *manager, void *data) return ret; } - pm_runtime_enable(dev); - return 0; } @@ -1272,8 +1275,6 @@ static void mixer_unbind(struct device *dev, struct device *master, void *data) struct mixer_context *ctx = dev_get_drvdata(dev); mixer_mgr_remove(&ctx->manager); - - pm_runtime_disable(dev); } static const struct component_ops mixer_component_ops = { diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c index d4762799351d..a9041d1a8ff0 100644 --- a/drivers/gpu/drm/i2c/tda998x_drv.c +++ b/drivers/gpu/drm/i2c/tda998x_drv.c @@ -32,6 +32,8 @@ struct tda998x_priv { struct i2c_client *cec; struct i2c_client *hdmi; + struct mutex mutex; + struct delayed_work dwork; uint16_t rev; uint8_t current_page; int dpms; @@ -402,9 +404,10 @@ reg_read_range(struct tda998x_priv *priv, uint16_t reg, char *buf, int cnt) uint8_t addr = REG2ADDR(reg); int ret; + mutex_lock(&priv->mutex); ret = set_page(priv, reg); if (ret < 0) - return ret; + goto out; ret = i2c_master_send(client, &addr, sizeof(addr)); if (ret < 0) @@ -414,10 +417,12 @@ reg_read_range(struct tda998x_priv *priv, uint16_t reg, char *buf, int cnt) if (ret < 0) goto fail; - return ret; + goto out; fail: dev_err(&client->dev, "Error %d reading from 0x%x\n", ret, reg); +out: + mutex_unlock(&priv->mutex); return ret; } @@ -431,13 +436,16 @@ reg_write_range(struct tda998x_priv *priv, uint16_t reg, uint8_t *p, int cnt) buf[0] = REG2ADDR(reg); memcpy(&buf[1], p, cnt); + mutex_lock(&priv->mutex); ret = set_page(priv, reg); if (ret < 0) - return; + goto out; ret = i2c_master_send(client, buf, cnt + 1); if (ret < 0) dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg); +out: + mutex_unlock(&priv->mutex); } static int @@ -459,13 +467,16 @@ reg_write(struct tda998x_priv *priv, uint16_t reg, uint8_t val) uint8_t buf[] = {REG2ADDR(reg), val}; int ret; + mutex_lock(&priv->mutex); ret = set_page(priv, reg); if (ret < 0) - return; + goto out; ret = i2c_master_send(client, buf, sizeof(buf)); if (ret < 0) dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg); +out: + mutex_unlock(&priv->mutex); } static void @@ -475,13 +486,16 @@ reg_write16(struct tda998x_priv *priv, uint16_t reg, uint16_t val) uint8_t buf[] = {REG2ADDR(reg), val >> 8, val}; int ret; + mutex_lock(&priv->mutex); ret = set_page(priv, reg); if (ret < 0) - return; + goto out; ret = i2c_master_send(client, buf, sizeof(buf)); if (ret < 0) dev_err(&client->dev, "Error %d writing to 0x%x\n", ret, reg); +out: + mutex_unlock(&priv->mutex); } static void @@ -536,6 +550,17 @@ tda998x_reset(struct tda998x_priv *priv) reg_write(priv, REG_MUX_VP_VIP_OUT, 0x24); } +/* handle HDMI connect/disconnect */ +static void tda998x_hpd(struct work_struct *work) +{ + struct delayed_work *dwork = to_delayed_work(work); + struct tda998x_priv *priv = + container_of(dwork, struct tda998x_priv, dwork); + + if (priv->encoder && priv->encoder->dev) + drm_kms_helper_hotplug_event(priv->encoder->dev); +} + /* * only 2 interrupts may occur: screen plug/unplug and EDID read */ @@ -559,8 +584,7 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data) priv->wq_edid_wait = 0; wake_up(&priv->wq_edid); } else if (cec != 0) { /* HPD change */ - if (priv->encoder && priv->encoder->dev) - drm_helper_hpd_irq_event(priv->encoder->dev); + schedule_delayed_work(&priv->dwork, HZ/10); } return IRQ_HANDLED; } @@ -1170,8 +1194,10 @@ static void tda998x_destroy(struct tda998x_priv *priv) /* disable all IRQs and free the IRQ handler */ cec_write(priv, REG_CEC_RXSHPDINTENA, 0); reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); - if (priv->hdmi->irq) + if (priv->hdmi->irq) { free_irq(priv->hdmi->irq, priv); + cancel_delayed_work_sync(&priv->dwork); + } i2c_unregister_device(priv->cec); } @@ -1255,6 +1281,7 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) struct device_node *np = client->dev.of_node; u32 video; int rev_lo, rev_hi, ret; + unsigned short cec_addr; priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(2) | VIP_CNTRL_0_SWAP_B(3); priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(0) | VIP_CNTRL_1_SWAP_D(1); @@ -1262,12 +1289,16 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) priv->current_page = 0xff; priv->hdmi = client; - priv->cec = i2c_new_dummy(client->adapter, 0x34); + /* CEC I2C address bound to TDA998x I2C addr by configuration pins */ + cec_addr = 0x34 + (client->addr & 0x03); + priv->cec = i2c_new_dummy(client->adapter, cec_addr); if (!priv->cec) return -ENODEV; priv->dpms = DRM_MODE_DPMS_OFF; + mutex_init(&priv->mutex); /* protect the page access */ + /* wake up the device: */ cec_write(priv, REG_CEC_ENAMODS, CEC_ENAMODS_EN_RXSENS | CEC_ENAMODS_EN_HDMI); @@ -1323,8 +1354,9 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) if (client->irq) { int irqf_trigger; - /* init read EDID waitqueue */ + /* init read EDID waitqueue and HDP work */ init_waitqueue_head(&priv->wq_edid); + INIT_DELAYED_WORK(&priv->dwork, tda998x_hpd); /* clear pending interrupts */ reg_read(priv, REG_INT_FLAGS_0); diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index ecee3bcc8772..26b3199e0af2 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -830,6 +830,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) intel_runtime_pm_enable(dev_priv); + i915_audio_component_init(dev_priv); + return 0; out_power_well: @@ -870,6 +872,8 @@ int i915_driver_unload(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; int ret; + i915_audio_component_cleanup(dev_priv); + ret = i915_gem_suspend(dev); if (ret) { DRM_ERROR("failed to idle hardware: %d\n", ret); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 574057cd1d09..489b220af02b 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -462,19 +462,13 @@ void intel_detect_pch(struct drm_device *dev) } else if (id == INTEL_PCH_LPT_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_LPT; DRM_DEBUG_KMS("Found LynxPoint PCH\n"); - WARN_ON(!IS_HASWELL(dev)); - WARN_ON(IS_HSW_ULT(dev)); - } else if (IS_BROADWELL(dev)) { - dev_priv->pch_type = PCH_LPT; - dev_priv->pch_id = - INTEL_PCH_LPT_LP_DEVICE_ID_TYPE; - DRM_DEBUG_KMS("This is Broadwell, assuming " - "LynxPoint LP PCH\n"); + WARN_ON(!IS_HASWELL(dev) && !IS_BROADWELL(dev)); + WARN_ON(IS_HSW_ULT(dev) || IS_BDW_ULT(dev)); } else if (id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_LPT; DRM_DEBUG_KMS("Found LynxPoint LP PCH\n"); - WARN_ON(!IS_HASWELL(dev)); - WARN_ON(!IS_HSW_ULT(dev)); + WARN_ON(!IS_HASWELL(dev) && !IS_BROADWELL(dev)); + WARN_ON(!IS_HSW_ULT(dev) && !IS_BDW_ULT(dev)); } else if (id == INTEL_PCH_SPT_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_SPT; DRM_DEBUG_KMS("Found SunrisePoint PCH\n"); @@ -940,8 +934,7 @@ static int i915_pm_suspend(struct device *dev) static int i915_pm_suspend_late(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct drm_device *drm_dev = dev_to_i915(dev)->dev; /* * We have a suspedn ordering issue with the snd-hda driver also @@ -960,8 +953,7 @@ static int i915_pm_suspend_late(struct device *dev) static int i915_pm_resume_early(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct drm_device *drm_dev = dev_to_i915(dev)->dev; if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; @@ -971,8 +963,7 @@ static int i915_pm_resume_early(struct device *dev) static int i915_pm_resume(struct device *dev) { - struct pci_dev *pdev = to_pci_dev(dev); - struct drm_device *drm_dev = pci_get_drvdata(pdev); + struct drm_device *drm_dev = dev_to_i915(dev)->dev; if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 70d0f0f06f1a..24cc36a9f3ff 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1698,6 +1698,9 @@ struct drm_i915_private { struct drm_property *broadcast_rgb_property; struct drm_property *force_audio_property; + /* hda/i915 audio component */ + bool audio_component_registered; + uint32_t hw_context_size; struct list_head context_list; @@ -1756,8 +1759,6 @@ struct drm_i915_private { */ struct workqueue_struct *dp_wq; - uint32_t bios_vgacntr; - /* Abstract the submission mechanism (legacy ringbuffer or execlists) away */ struct { int (*do_execbuf)(struct drm_device *dev, struct drm_file *file, @@ -1783,6 +1784,11 @@ static inline struct drm_i915_private *to_i915(const struct drm_device *dev) return dev->dev_private; } +static inline struct drm_i915_private *dev_to_i915(struct device *dev) +{ + return to_i915(dev_get_drvdata(dev)); +} + /* Iterate over initialised rings */ #define for_each_ring(ring__, dev_priv__, i__) \ for ((i__) = 0; (i__) < I915_NUM_RINGS; (i__)++) \ @@ -2161,8 +2167,7 @@ struct drm_i915_cmd_table { #define IS_HSW_EARLY_SDV(dev) (IS_HASWELL(dev) && \ (INTEL_DEVID(dev) & 0xFF00) == 0x0C00) #define IS_BDW_ULT(dev) (IS_BROADWELL(dev) && \ - ((INTEL_DEVID(dev) & 0xf) == 0x2 || \ - (INTEL_DEVID(dev) & 0xf) == 0x6 || \ + ((INTEL_DEVID(dev) & 0xf) == 0x6 || \ (INTEL_DEVID(dev) & 0xf) == 0xe)) #define IS_BDW_GT3(dev) (IS_BROADWELL(dev) && \ (INTEL_DEVID(dev) & 0x00F0) == 0x0020) diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 52adcb680be3..5f614828d365 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1048,6 +1048,7 @@ int i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, struct drm_file *file) { + struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_pwrite *args = data; struct drm_i915_gem_object *obj; int ret; @@ -1067,9 +1068,11 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, return -EFAULT; } + intel_runtime_pm_get(dev_priv); + ret = i915_mutex_lock_interruptible(dev); if (ret) - return ret; + goto put_rpm; obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle)); if (&obj->base == NULL) { @@ -1121,6 +1124,9 @@ out: drm_gem_object_unreference(&obj->base); unlock: mutex_unlock(&dev->struct_mutex); +put_rpm: + intel_runtime_pm_put(dev_priv); + return ret; } @@ -3142,6 +3148,13 @@ static void i965_write_fence_reg(struct drm_device *dev, int reg, u32 size = i915_gem_obj_ggtt_size(obj); uint64_t val; + /* Adjust fence size to match tiled area */ + if (obj->tiling_mode != I915_TILING_NONE) { + uint32_t row_size = obj->stride * + (obj->tiling_mode == I915_TILING_Y ? 32 : 8); + size = (size / row_size) * row_size; + } + val = (uint64_t)((i915_gem_obj_ggtt_offset(obj) + size - 4096) & 0xfffff000) << 32; val |= i915_gem_obj_ggtt_offset(obj) & 0xfffff000; @@ -4878,25 +4891,18 @@ i915_gem_init_hw(struct drm_device *dev) for (i = 0; i < NUM_L3_SLICES(dev); i++) i915_gem_l3_remap(&dev_priv->ring[RCS], i); - /* - * XXX: Contexts should only be initialized once. Doing a switch to the - * default context switch however is something we'd like to do after - * reset or thaw (the latter may not actually be necessary for HW, but - * goes with our code better). Context switching requires rings (for - * the do_switch), but before enabling PPGTT. So don't move this. - */ - ret = i915_gem_context_enable(dev_priv); + ret = i915_ppgtt_init_hw(dev); if (ret && ret != -EIO) { - DRM_ERROR("Context enable failed %d\n", ret); + DRM_ERROR("PPGTT enable failed %d\n", ret); i915_gem_cleanup_ringbuffer(dev); - - return ret; } - ret = i915_ppgtt_init_hw(dev); + ret = i915_gem_context_enable(dev_priv); if (ret && ret != -EIO) { - DRM_ERROR("PPGTT enable failed %d\n", ret); + DRM_ERROR("Context enable failed %d\n", ret); i915_gem_cleanup_ringbuffer(dev); + + return ret; } return ret; @@ -5149,7 +5155,7 @@ static bool mutex_is_locked_by(struct mutex *mutex, struct task_struct *task) if (!mutex_is_locked(mutex)) return false; -#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_MUTEXES) +#if defined(CONFIG_SMP) && !defined(CONFIG_DEBUG_MUTEXES) return mutex->owner == task; #else /* Since UP may be pre-empted, we cannot assume that we own the lock */ diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 996c2931c499..b051a238baf9 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -292,6 +292,23 @@ void gen6_enable_rps_interrupts(struct drm_device *dev) spin_unlock_irq(&dev_priv->irq_lock); } +u32 gen6_sanitize_rps_pm_mask(struct drm_i915_private *dev_priv, u32 mask) +{ + /* + * SNB,IVB can while VLV,CHV may hard hang on looping batchbuffer + * if GEN6_PM_UP_EI_EXPIRED is masked. + * + * TODO: verify if this can be reproduced on VLV,CHV. + */ + if (INTEL_INFO(dev_priv)->gen <= 7 && !IS_HASWELL(dev_priv)) + mask &= ~GEN6_PM_RP_UP_EI_EXPIRED; + + if (INTEL_INFO(dev_priv)->gen >= 8) + mask &= ~GEN8_PMINTR_REDIRECT_TO_NON_DISP; + + return mask; +} + void gen6_disable_rps_interrupts(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -304,8 +321,7 @@ void gen6_disable_rps_interrupts(struct drm_device *dev) spin_lock_irq(&dev_priv->irq_lock); - I915_WRITE(GEN6_PMINTRMSK, INTEL_INFO(dev_priv)->gen >= 8 ? - ~GEN8_PMINTR_REDIRECT_TO_NON_DISP : ~0); + I915_WRITE(GEN6_PMINTRMSK, gen6_sanitize_rps_pm_mask(dev_priv, ~0)); __gen6_disable_pm_irq(dev_priv, dev_priv->pm_rps_events); I915_WRITE(gen6_pm_ier(dev_priv), I915_READ(gen6_pm_ier(dev_priv)) & @@ -3725,8 +3741,6 @@ static bool i8xx_handle_vblank(struct drm_device *dev, if ((iir & flip_pending) == 0) goto check_page_flip; - intel_prepare_page_flip(dev, plane); - /* We detect FlipDone by looking for the change in PendingFlip from '1' * to '0' on the following vblank, i.e. IIR has the Pendingflip * asserted following the MI_DISPLAY_FLIP, but ISR is deasserted, hence @@ -3736,6 +3750,7 @@ static bool i8xx_handle_vblank(struct drm_device *dev, if (I915_READ16(ISR) & flip_pending) goto check_page_flip; + intel_prepare_page_flip(dev, plane); intel_finish_page_flip(dev, pipe); return true; @@ -3907,8 +3922,6 @@ static bool i915_handle_vblank(struct drm_device *dev, if ((iir & flip_pending) == 0) goto check_page_flip; - intel_prepare_page_flip(dev, plane); - /* We detect FlipDone by looking for the change in PendingFlip from '1' * to '0' on the following vblank, i.e. IIR has the Pendingflip * asserted following the MI_DISPLAY_FLIP, but ISR is deasserted, hence @@ -3918,6 +3931,7 @@ static bool i915_handle_vblank(struct drm_device *dev, if (I915_READ(ISR) & flip_pending) goto check_page_flip; + intel_prepare_page_flip(dev, plane); intel_finish_page_flip(dev, pipe); return true; diff --git a/drivers/gpu/drm/i915/intel_audio.c b/drivers/gpu/drm/i915/intel_audio.c index 2c7ed5cb29c0..ee41b882e71a 100644 --- a/drivers/gpu/drm/i915/intel_audio.c +++ b/drivers/gpu/drm/i915/intel_audio.c @@ -22,6 +22,9 @@ */ #include <linux/kernel.h> +#include <linux/component.h> +#include <drm/i915_component.h> +#include "intel_drv.h" #include <drm/drmP.h> #include <drm/drm_edid.h> @@ -461,3 +464,110 @@ void intel_init_audio(struct drm_device *dev) dev_priv->display.audio_codec_disable = ilk_audio_codec_disable; } } + +static void i915_audio_component_get_power(struct device *dev) +{ + intel_display_power_get(dev_to_i915(dev), POWER_DOMAIN_AUDIO); +} + +static void i915_audio_component_put_power(struct device *dev) +{ + intel_display_power_put(dev_to_i915(dev), POWER_DOMAIN_AUDIO); +} + +/* Get CDCLK in kHz */ +static int i915_audio_component_get_cdclk_freq(struct device *dev) +{ + struct drm_i915_private *dev_priv = dev_to_i915(dev); + int ret; + + if (WARN_ON_ONCE(!HAS_DDI(dev_priv))) + return -ENODEV; + + intel_display_power_get(dev_priv, POWER_DOMAIN_AUDIO); + ret = intel_ddi_get_cdclk_freq(dev_priv); + intel_display_power_put(dev_priv, POWER_DOMAIN_AUDIO); + + return ret; +} + +static const struct i915_audio_component_ops i915_audio_component_ops = { + .owner = THIS_MODULE, + .get_power = i915_audio_component_get_power, + .put_power = i915_audio_component_put_power, + .get_cdclk_freq = i915_audio_component_get_cdclk_freq, +}; + +static int i915_audio_component_bind(struct device *i915_dev, + struct device *hda_dev, void *data) +{ + struct i915_audio_component *acomp = data; + + if (WARN_ON(acomp->ops || acomp->dev)) + return -EEXIST; + + acomp->ops = &i915_audio_component_ops; + acomp->dev = i915_dev; + + return 0; +} + +static void i915_audio_component_unbind(struct device *i915_dev, + struct device *hda_dev, void *data) +{ + struct i915_audio_component *acomp = data; + + acomp->ops = NULL; + acomp->dev = NULL; +} + +static const struct component_ops i915_audio_component_bind_ops = { + .bind = i915_audio_component_bind, + .unbind = i915_audio_component_unbind, +}; + +/** + * i915_audio_component_init - initialize and register the audio component + * @dev_priv: i915 device instance + * + * This will register with the component framework a child component which + * will bind dynamically to the snd_hda_intel driver's corresponding master + * component when the latter is registered. During binding the child + * initializes an instance of struct i915_audio_component which it receives + * from the master. The master can then start to use the interface defined by + * this struct. Each side can break the binding at any point by deregistering + * its own component after which each side's component unbind callback is + * called. + * + * We ignore any error during registration and continue with reduced + * functionality (i.e. without HDMI audio). + */ +void i915_audio_component_init(struct drm_i915_private *dev_priv) +{ + int ret; + + ret = component_add(dev_priv->dev->dev, &i915_audio_component_bind_ops); + if (ret < 0) { + DRM_ERROR("failed to add audio component (%d)\n", ret); + /* continue with reduced functionality */ + return; + } + + dev_priv->audio_component_registered = true; +} + +/** + * i915_audio_component_cleanup - deregister the audio component + * @dev_priv: i915 device instance + * + * Deregisters the audio component, breaking any existing binding to the + * corresponding snd_hda_intel driver's master component. + */ +void i915_audio_component_cleanup(struct drm_i915_private *dev_priv) +{ + if (!dev_priv->audio_component_registered) + return; + + component_del(dev_priv->dev->dev, &i915_audio_component_bind_ops); + dev_priv->audio_component_registered = false; +} diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index fb3e3d429191..e7a16f119a29 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -9815,7 +9815,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, if (obj->tiling_mode != work->old_fb_obj->tiling_mode) /* vlv: DISPLAY_FLIP fails to change tiling */ ring = NULL; - } else if (IS_IVYBRIDGE(dev)) { + } else if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) { ring = &dev_priv->ring[BCS]; } else if (INTEL_INFO(dev)->gen >= 7) { ring = obj->ring; @@ -13057,11 +13057,7 @@ static void i915_disable_vga(struct drm_device *dev) vga_put(dev->pdev, VGA_RSRC_LEGACY_IO); udelay(300); - /* - * Fujitsu-Siemens Lifebook S6010 (830) has problems resuming - * from S3 without preserving (some of?) the other bits. - */ - I915_WRITE(vga_reg, dev_priv->bios_vgacntr | VGA_DISP_DISABLE); + I915_WRITE(vga_reg, VGA_DISP_DISABLE); POSTING_READ(vga_reg); } @@ -13146,8 +13142,6 @@ void intel_modeset_init(struct drm_device *dev) intel_shared_dpll_init(dev); - /* save the BIOS value before clobbering it */ - dev_priv->bios_vgacntr = I915_READ(i915_vgacntrl_reg(dev)); /* Just disable it once at startup */ i915_disable_vga(dev); intel_setup_outputs(dev); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 25fdbb16d4e0..29ba962d15e3 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -794,6 +794,7 @@ void gen6_disable_pm_irq(struct drm_i915_private *dev_priv, uint32_t mask); void gen6_reset_rps_interrupts(struct drm_device *dev); void gen6_enable_rps_interrupts(struct drm_device *dev); void gen6_disable_rps_interrupts(struct drm_device *dev); +u32 gen6_sanitize_rps_pm_mask(struct drm_i915_private *dev_priv, u32 mask); void intel_runtime_pm_disable_interrupts(struct drm_i915_private *dev_priv); void intel_runtime_pm_enable_interrupts(struct drm_i915_private *dev_priv); static inline bool intel_irqs_enabled(struct drm_i915_private *dev_priv) @@ -872,6 +873,8 @@ void intel_fb_obj_flush(struct drm_i915_gem_object *obj, bool retire); void intel_init_audio(struct drm_device *dev); void intel_audio_codec_enable(struct intel_encoder *encoder); void intel_audio_codec_disable(struct intel_encoder *encoder); +void i915_audio_component_init(struct drm_i915_private *dev_priv); +void i915_audio_component_cleanup(struct drm_i915_private *dev_priv); /* intel_display.c */ const char *intel_output_name(int output); diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 4d63839bd9b4..dfb783a8f2c3 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -962,7 +962,7 @@ void intel_panel_enable_backlight(struct intel_connector *connector) WARN_ON(panel->backlight.max == 0); - if (panel->backlight.level == 0) { + if (panel->backlight.level <= panel->backlight.min) { panel->backlight.level = panel->backlight.max; if (panel->backlight.device) panel->backlight.device->props.brightness = diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 964b28e3c630..bf814a64582a 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4363,16 +4363,7 @@ static u32 gen6_rps_pm_mask(struct drm_i915_private *dev_priv, u8 val) mask |= dev_priv->pm_rps_events & (GEN6_PM_RP_DOWN_EI_EXPIRED | GEN6_PM_RP_UP_EI_EXPIRED); mask &= dev_priv->pm_rps_events; - /* IVB and SNB hard hangs on looping batchbuffer - * if GEN6_PM_UP_EI_EXPIRED is masked. - */ - if (INTEL_INFO(dev_priv->dev)->gen <= 7 && !IS_HASWELL(dev_priv->dev)) - mask |= GEN6_PM_RP_UP_EI_EXPIRED; - - if (IS_GEN8(dev_priv->dev)) - mask |= GEN8_PMINTR_REDIRECT_TO_NON_DISP; - - return ~mask; + return gen6_sanitize_rps_pm_mask(dev_priv, ~mask); } /* gen6_set_rps is called to update the frequency request, but should also be @@ -4441,7 +4432,8 @@ static void vlv_set_rps_idle(struct drm_i915_private *dev_priv) return; /* Mask turbo interrupt so that they will not come in between */ - I915_WRITE(GEN6_PMINTRMSK, 0xffffffff); + I915_WRITE(GEN6_PMINTRMSK, + gen6_sanitize_rps_pm_mask(dev_priv, ~0)); vlv_force_gfx_clock(dev_priv, true); diff --git a/drivers/gpu/drm/i915/intel_runtime_pm.c b/drivers/gpu/drm/i915/intel_runtime_pm.c index f5a78d53e297..39ddf40171bf 100644 --- a/drivers/gpu/drm/i915/intel_runtime_pm.c +++ b/drivers/gpu/drm/i915/intel_runtime_pm.c @@ -31,7 +31,6 @@ #include "i915_drv.h" #include "intel_drv.h" -#include <drm/i915_powerwell.h> /** * DOC: runtime pm @@ -50,8 +49,6 @@ * present for a given platform. */ -static struct i915_power_domains *hsw_pwr; - #define for_each_power_well(i, power_well, domain_mask, power_domains) \ for (i = 0; \ i < (power_domains)->power_well_count && \ @@ -615,29 +612,6 @@ static void chv_pipe_power_well_disable(struct drm_i915_private *dev_priv, vlv_power_sequencer_reset(dev_priv); } -static void check_power_well_state(struct drm_i915_private *dev_priv, - struct i915_power_well *power_well) -{ - bool enabled = power_well->ops->is_enabled(dev_priv, power_well); - - if (power_well->always_on || !i915.disable_power_well) { - if (!enabled) - goto mismatch; - - return; - } - - if (enabled != (power_well->count > 0)) - goto mismatch; - - return; - -mismatch: - WARN(1, "state mismatch for '%s' (always_on %d hw state %d use-count %d disable_power_well %d\n", - power_well->name, power_well->always_on, enabled, - power_well->count, i915.disable_power_well); -} - /** * intel_display_power_get - grab a power domain reference * @dev_priv: i915 device instance @@ -669,8 +643,6 @@ void intel_display_power_get(struct drm_i915_private *dev_priv, power_well->ops->enable(dev_priv, power_well); power_well->hw_enabled = true; } - - check_power_well_state(dev_priv, power_well); } power_domains->domain_use_count[domain]++; @@ -709,8 +681,6 @@ void intel_display_power_put(struct drm_i915_private *dev_priv, power_well->hw_enabled = false; power_well->ops->disable(dev_priv, power_well); } - - check_power_well_state(dev_priv, power_well); } mutex_unlock(&power_domains->lock); @@ -1098,10 +1068,8 @@ int intel_power_domains_init(struct drm_i915_private *dev_priv) */ if (IS_HASWELL(dev_priv->dev)) { set_power_wells(power_domains, hsw_power_wells); - hsw_pwr = power_domains; } else if (IS_BROADWELL(dev_priv->dev)) { set_power_wells(power_domains, bdw_power_wells); - hsw_pwr = power_domains; } else if (IS_CHERRYVIEW(dev_priv->dev)) { set_power_wells(power_domains, chv_power_wells); } else if (IS_VALLEYVIEW(dev_priv->dev)) { @@ -1145,8 +1113,6 @@ void intel_power_domains_fini(struct drm_i915_private *dev_priv) * the power well is not enabled, so just enable it in case * we're going to unload/reload. */ intel_display_set_init_power(dev_priv, true); - - hsw_pwr = NULL; } static void intel_power_domains_resume(struct drm_i915_private *dev_priv) @@ -1355,52 +1321,3 @@ void intel_runtime_pm_enable(struct drm_i915_private *dev_priv) pm_runtime_put_autosuspend(device); } -/* Display audio driver power well request */ -int i915_request_power_well(void) -{ - struct drm_i915_private *dev_priv; - - if (!hsw_pwr) - return -ENODEV; - - dev_priv = container_of(hsw_pwr, struct drm_i915_private, - power_domains); - intel_display_power_get(dev_priv, POWER_DOMAIN_AUDIO); - return 0; -} -EXPORT_SYMBOL_GPL(i915_request_power_well); - -/* Display audio driver power well release */ -int i915_release_power_well(void) -{ - struct drm_i915_private *dev_priv; - - if (!hsw_pwr) - return -ENODEV; - - dev_priv = container_of(hsw_pwr, struct drm_i915_private, - power_domains); - intel_display_power_put(dev_priv, POWER_DOMAIN_AUDIO); - return 0; -} -EXPORT_SYMBOL_GPL(i915_release_power_well); - -/* - * Private interface for the audio driver to get CDCLK in kHz. - * - * Caller must request power well using i915_request_power_well() prior to - * making the call. - */ -int i915_get_cdclk_freq(void) -{ - struct drm_i915_private *dev_priv; - - if (!hsw_pwr) - return -ENODEV; - - dev_priv = container_of(hsw_pwr, struct drm_i915_private, - power_domains); - - return intel_ddi_get_cdclk_freq(dev_priv); -} -EXPORT_SYMBOL_GPL(i915_get_cdclk_freq); diff --git a/drivers/gpu/drm/nouveau/core/core/event.c b/drivers/gpu/drm/nouveau/core/core/event.c index ff2b434b3db4..760947e380c9 100644 --- a/drivers/gpu/drm/nouveau/core/core/event.c +++ b/drivers/gpu/drm/nouveau/core/core/event.c @@ -26,7 +26,7 @@ void nvkm_event_put(struct nvkm_event *event, u32 types, int index) { - BUG_ON(!spin_is_locked(&event->refs_lock)); + assert_spin_locked(&event->refs_lock); while (types) { int type = __ffs(types); types &= ~(1 << type); if (--event->refs[index * event->types_nr + type] == 0) { @@ -39,7 +39,7 @@ nvkm_event_put(struct nvkm_event *event, u32 types, int index) void nvkm_event_get(struct nvkm_event *event, u32 types, int index) { - BUG_ON(!spin_is_locked(&event->refs_lock)); + assert_spin_locked(&event->refs_lock); while (types) { int type = __ffs(types); types &= ~(1 << type); if (++event->refs[index * event->types_nr + type] == 1) { diff --git a/drivers/gpu/drm/nouveau/core/core/notify.c b/drivers/gpu/drm/nouveau/core/core/notify.c index d1bcde55e9d7..839a32577680 100644 --- a/drivers/gpu/drm/nouveau/core/core/notify.c +++ b/drivers/gpu/drm/nouveau/core/core/notify.c @@ -98,7 +98,7 @@ nvkm_notify_send(struct nvkm_notify *notify, void *data, u32 size) struct nvkm_event *event = notify->event; unsigned long flags; - BUG_ON(!spin_is_locked(&event->list_lock)); + assert_spin_locked(&event->list_lock); BUG_ON(size != notify->size); spin_lock_irqsave(&event->refs_lock, flags); diff --git a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c index 674da1f095b2..732922690653 100644 --- a/drivers/gpu/drm/nouveau/core/engine/device/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/device/nve0.c @@ -249,6 +249,39 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; device->oclass[NVDEV_ENGINE_PERFMON] = &nvf0_perfmon_oclass; break; + case 0x106: + device->cname = "GK208B"; + device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; + device->oclass[NVDEV_SUBDEV_GPIO ] = nve0_gpio_oclass; + device->oclass[NVDEV_SUBDEV_I2C ] = nve0_i2c_oclass; + device->oclass[NVDEV_SUBDEV_FUSE ] = &gf100_fuse_oclass; + device->oclass[NVDEV_SUBDEV_CLOCK ] = &nve0_clock_oclass; + device->oclass[NVDEV_SUBDEV_THERM ] = &nvd0_therm_oclass; + device->oclass[NVDEV_SUBDEV_MXM ] = &nv50_mxm_oclass; + device->oclass[NVDEV_SUBDEV_DEVINIT] = nvc0_devinit_oclass; + device->oclass[NVDEV_SUBDEV_MC ] = gk20a_mc_oclass; + device->oclass[NVDEV_SUBDEV_BUS ] = nvc0_bus_oclass; + device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = nve0_fb_oclass; + device->oclass[NVDEV_SUBDEV_LTC ] = gk104_ltc_oclass; + device->oclass[NVDEV_SUBDEV_IBUS ] = &nve0_ibus_oclass; + device->oclass[NVDEV_SUBDEV_INSTMEM] = nv50_instmem_oclass; + device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass; + device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass; + device->oclass[NVDEV_SUBDEV_PWR ] = nv108_pwr_oclass; + device->oclass[NVDEV_SUBDEV_VOLT ] = &nv40_volt_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = nvd0_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_FIFO ] = nv108_fifo_oclass; + device->oclass[NVDEV_ENGINE_SW ] = nvc0_software_oclass; + device->oclass[NVDEV_ENGINE_GR ] = nv108_graph_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = nvf0_disp_oclass; + device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass; + device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass; + device->oclass[NVDEV_ENGINE_COPY2 ] = &nve0_copy2_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &nve0_bsp_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &nve0_vp_oclass; + device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; + break; case 0x108: device->cname = "GK208"; device->oclass[NVDEV_SUBDEV_VBIOS ] = &nouveau_bios_oclass; diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/shadowramin.c b/drivers/gpu/drm/nouveau/core/subdev/bios/shadowramin.c index 5e58bba0dd5c..a7a890fad1e5 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/shadowramin.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/shadowramin.c @@ -44,8 +44,10 @@ static void pramin_fini(void *data) { struct priv *priv = data; - nv_wr32(priv->bios, 0x001700, priv->bar0); - kfree(priv); + if (priv) { + nv_wr32(priv->bios, 0x001700, priv->bar0); + kfree(priv); + } } static void * diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvaa.c b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvaa.c index 00f2ca7e44a5..033a8e999497 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvaa.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/ramnvaa.c @@ -24,34 +24,71 @@ #include "nv50.h" +struct nvaa_ram_priv { + struct nouveau_ram base; + u64 poller_base; +}; + static int nvaa_ram_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 datasize, struct nouveau_object **pobject) { - const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */ - const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */ + u32 rsvd_head = ( 256 * 1024); /* vga memory */ + u32 rsvd_tail = (1024 * 1024); /* vbios etc */ struct nouveau_fb *pfb = nouveau_fb(parent); - struct nouveau_ram *ram; + struct nvaa_ram_priv *priv; int ret; - ret = nouveau_ram_create(parent, engine, oclass, &ram); - *pobject = nv_object(ram); + ret = nouveau_ram_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); if (ret) return ret; - ram->size = nv_rd32(pfb, 0x10020c); - ram->size = (ram->size & 0xffffff00) | ((ram->size & 0x000000ff) << 32); + priv->base.type = NV_MEM_TYPE_STOLEN; + priv->base.stolen = (u64)nv_rd32(pfb, 0x100e10) << 12; + priv->base.size = (u64)nv_rd32(pfb, 0x100e14) << 12; - ret = nouveau_mm_init(&pfb->vram, rsvd_head, (ram->size >> 12) - - (rsvd_head + rsvd_tail), 1); + rsvd_tail += 0x1000; + priv->poller_base = priv->base.size - rsvd_tail; + + ret = nouveau_mm_init(&pfb->vram, rsvd_head >> 12, + (priv->base.size - (rsvd_head + rsvd_tail)) >> 12, + 1); if (ret) return ret; - ram->type = NV_MEM_TYPE_STOLEN; - ram->stolen = (u64)nv_rd32(pfb, 0x100e10) << 12; - ram->get = nv50_ram_get; - ram->put = nv50_ram_put; + priv->base.get = nv50_ram_get; + priv->base.put = nv50_ram_put; + return 0; +} + +static int +nvaa_ram_init(struct nouveau_object *object) +{ + struct nouveau_fb *pfb = nouveau_fb(object); + struct nvaa_ram_priv *priv = (void *)object; + int ret; + u64 dniso, hostnb, flush; + + ret = nouveau_ram_init(&priv->base); + if (ret) + return ret; + + dniso = ((priv->base.size - (priv->poller_base + 0x00)) >> 5) - 1; + hostnb = ((priv->base.size - (priv->poller_base + 0x20)) >> 5) - 1; + flush = ((priv->base.size - (priv->poller_base + 0x40)) >> 5) - 1; + + /* Enable NISO poller for various clients and set their associated + * read address, only for MCP77/78 and MCP79/7A. (fd#25701) + */ + nv_wr32(pfb, 0x100c18, dniso); + nv_mask(pfb, 0x100c14, 0x00000000, 0x00000001); + nv_wr32(pfb, 0x100c1c, hostnb); + nv_mask(pfb, 0x100c14, 0x00000000, 0x00000002); + nv_wr32(pfb, 0x100c24, flush); + nv_mask(pfb, 0x100c14, 0x00000000, 0x00010000); + return 0; } @@ -60,7 +97,7 @@ nvaa_ram_oclass = { .ofuncs = &(struct nouveau_ofuncs) { .ctor = nvaa_ram_ctor, .dtor = _nouveau_ram_dtor, - .init = _nouveau_ram_init, + .init = nvaa_ram_init, .fini = _nouveau_ram_fini, }, }; diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv4c.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv4c.c index a75c35ccf25c..165401c4045c 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv4c.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv4c.c @@ -24,13 +24,6 @@ #include "nv04.h" -static void -nv4c_mc_msi_rearm(struct nouveau_mc *pmc) -{ - struct nv04_mc_priv *priv = (void *)pmc; - nv_wr08(priv, 0x088050, 0xff); -} - struct nouveau_oclass * nv4c_mc_oclass = &(struct nouveau_mc_oclass) { .base.handle = NV_SUBDEV(MC, 0x4c), @@ -41,5 +34,4 @@ nv4c_mc_oclass = &(struct nouveau_mc_oclass) { .fini = _nouveau_mc_fini, }, .intr = nv04_mc_intr, - .msi_rearm = nv4c_mc_msi_rearm, }.base; diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 21ec561edc99..bba2960d3dfb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -1572,8 +1572,10 @@ nouveau_ttm_tt_unpopulate(struct ttm_tt *ttm) * so use the DMA API for them. */ if (!nv_device_is_cpu_coherent(device) && - ttm->caching_state == tt_uncached) + ttm->caching_state == tt_uncached) { ttm_dma_unpopulate(ttm_dma, dev->dev); + return; + } #if __OS_HAS_AGP if (drm->agp.stat == ENABLED) { diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 42c34babc2e5..bf0f9e21d714 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -36,7 +36,14 @@ void nouveau_gem_object_del(struct drm_gem_object *gem) { struct nouveau_bo *nvbo = nouveau_gem_object(gem); + struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev); struct ttm_buffer_object *bo = &nvbo->bo; + struct device *dev = drm->dev->dev; + int ret; + + ret = pm_runtime_get_sync(dev); + if (WARN_ON(ret < 0 && ret != -EACCES)) + return; if (gem->import_attach) drm_prime_gem_destroy(gem, nvbo->bo.sg); @@ -46,6 +53,9 @@ nouveau_gem_object_del(struct drm_gem_object *gem) /* reset filp so nouveau_bo_del_ttm() can test for it */ gem->filp = NULL; ttm_bo_unref(&bo); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); } int @@ -53,7 +63,9 @@ nouveau_gem_object_open(struct drm_gem_object *gem, struct drm_file *file_priv) { struct nouveau_cli *cli = nouveau_cli(file_priv); struct nouveau_bo *nvbo = nouveau_gem_object(gem); + struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev); struct nouveau_vma *vma; + struct device *dev = drm->dev->dev; int ret; if (!cli->vm) @@ -71,11 +83,16 @@ nouveau_gem_object_open(struct drm_gem_object *gem, struct drm_file *file_priv) goto out; } + ret = pm_runtime_get_sync(dev); + if (ret < 0 && ret != -EACCES) + goto out; + ret = nouveau_bo_vma_add(nvbo, cli->vm, vma); - if (ret) { + if (ret) kfree(vma); - goto out; - } + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); } else { vma->refcount++; } @@ -129,6 +146,8 @@ nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv) { struct nouveau_cli *cli = nouveau_cli(file_priv); struct nouveau_bo *nvbo = nouveau_gem_object(gem); + struct nouveau_drm *drm = nouveau_bdev(nvbo->bo.bdev); + struct device *dev = drm->dev->dev; struct nouveau_vma *vma; int ret; @@ -141,8 +160,14 @@ nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv) vma = nouveau_bo_vma_find(nvbo, cli->vm); if (vma) { - if (--vma->refcount == 0) - nouveau_gem_object_unmap(nvbo, vma); + if (--vma->refcount == 0) { + ret = pm_runtime_get_sync(dev); + if (!WARN_ON(ret < 0 && ret != -EACCES)) { + nouveau_gem_object_unmap(nvbo, vma); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + } + } } ttm_bo_unreserve(&nvbo->bo); } diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index d59ec491dbb9..ed644a4f6f57 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -1851,10 +1851,9 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc) return pll; } /* otherwise, pick one of the plls */ - if ((rdev->family == CHIP_KAVERI) || - (rdev->family == CHIP_KABINI) || + if ((rdev->family == CHIP_KABINI) || (rdev->family == CHIP_MULLINS)) { - /* KB/KV/ML has PPLL1 and PPLL2 */ + /* KB/ML has PPLL1 and PPLL2 */ pll_in_use = radeon_get_pll_use_mask(crtc); if (!(pll_in_use & (1 << ATOM_PPLL2))) return ATOM_PPLL2; @@ -1863,7 +1862,7 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc) DRM_ERROR("unable to allocate a PPLL\n"); return ATOM_PPLL_INVALID; } else { - /* CI has PPLL0, PPLL1, and PPLL2 */ + /* CI/KV has PPLL0, PPLL1, and PPLL2 */ pll_in_use = radeon_get_pll_use_mask(crtc); if (!(pll_in_use & (1 << ATOM_PPLL2))) return ATOM_PPLL2; @@ -2155,6 +2154,7 @@ static void atombios_crtc_disable(struct drm_crtc *crtc) case ATOM_PPLL0: /* disable the ppll */ if ((rdev->family == CHIP_ARUBA) || + (rdev->family == CHIP_KAVERI) || (rdev->family == CHIP_BONAIRE) || (rdev->family == CHIP_HAWAII)) atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id, diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 11ba9d21b89b..db42a670f995 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -492,6 +492,10 @@ int radeon_dp_mode_valid_helper(struct drm_connector *connector, struct radeon_connector_atom_dig *dig_connector; int dp_clock; + if ((mode->clock > 340000) && + (!radeon_connector_is_dp12_capable(connector))) + return MODE_CLOCK_HIGH; + if (!radeon_connector->con_priv) return MODE_CLOCK_HIGH; dig_connector = radeon_connector->con_priv; diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 6dcde3798b45..64fdae558d36 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -6033,6 +6033,17 @@ void cik_vm_flush(struct radeon_device *rdev, struct radeon_ring *ring, radeon_ring_write(ring, 0); radeon_ring_write(ring, 1 << vm_id); + /* wait for the invalidate to complete */ + radeon_ring_write(ring, PACKET3(PACKET3_WAIT_REG_MEM, 5)); + radeon_ring_write(ring, (WAIT_REG_MEM_OPERATION(0) | /* wait */ + WAIT_REG_MEM_FUNCTION(0) | /* always */ + WAIT_REG_MEM_ENGINE(0))); /* me */ + radeon_ring_write(ring, VM_INVALIDATE_REQUEST >> 2); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); /* ref */ + radeon_ring_write(ring, 0); /* mask */ + radeon_ring_write(ring, 0x20); /* poll interval */ + /* compute doesn't have PFP */ if (usepfp) { /* sync PFP to ME, otherwise we might get invalid PFP reads */ diff --git a/drivers/gpu/drm/radeon/cik_sdma.c b/drivers/gpu/drm/radeon/cik_sdma.c index dde5c7e29eb2..42cd0cffe210 100644 --- a/drivers/gpu/drm/radeon/cik_sdma.c +++ b/drivers/gpu/drm/radeon/cik_sdma.c @@ -816,7 +816,6 @@ void cik_sdma_vm_write_pages(struct radeon_device *rdev, for (; ndw > 0; ndw -= 2, --count, pe += 8) { if (flags & R600_PTE_SYSTEM) { value = radeon_vm_map_gart(rdev, addr); - value &= 0xFFFFFFFFFFFFF000ULL; } else if (flags & R600_PTE_VALID) { value = addr; } else { @@ -903,6 +902,9 @@ void cik_sdma_vm_pad_ib(struct radeon_ib *ib) void cik_dma_vm_flush(struct radeon_device *rdev, struct radeon_ring *ring, unsigned vm_id, uint64_t pd_addr) { + u32 extra_bits = (SDMA_POLL_REG_MEM_EXTRA_OP(0) | + SDMA_POLL_REG_MEM_EXTRA_FUNC(0)); /* always */ + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000)); if (vm_id < 8) { radeon_ring_write(ring, (VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (vm_id << 2)) >> 2); @@ -943,5 +945,12 @@ void cik_dma_vm_flush(struct radeon_device *rdev, struct radeon_ring *ring, radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000)); radeon_ring_write(ring, VM_INVALIDATE_REQUEST >> 2); radeon_ring_write(ring, 1 << vm_id); + + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_POLL_REG_MEM, 0, extra_bits)); + radeon_ring_write(ring, VM_INVALIDATE_REQUEST >> 2); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); /* reference */ + radeon_ring_write(ring, 0); /* mask */ + radeon_ring_write(ring, (0xfff << 16) | 10); /* retry count, poll interval */ } diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index ba85986febea..03003f8a6de6 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -2156,4 +2156,6 @@ #define ATC_VM_APERTURE1_HIGH_ADDR 0x330Cu #define ATC_VM_APERTURE1_LOW_ADDR 0x3304u +#define IH_VMID_0_LUT 0x3D40u + #endif diff --git a/drivers/gpu/drm/radeon/dce3_1_afmt.c b/drivers/gpu/drm/radeon/dce3_1_afmt.c index 2fe8cfc966d9..bafdf92a5732 100644 --- a/drivers/gpu/drm/radeon/dce3_1_afmt.c +++ b/drivers/gpu/drm/radeon/dce3_1_afmt.c @@ -103,7 +103,7 @@ static void dce3_2_afmt_write_sad_regs(struct drm_encoder *encoder) } sad_count = drm_edid_to_sad(radeon_connector->edid, &sads); - if (sad_count < 0) { + if (sad_count <= 0) { DRM_ERROR("Couldn't read SADs: %d\n", sad_count); return; } diff --git a/drivers/gpu/drm/radeon/kv_dpm.c b/drivers/gpu/drm/radeon/kv_dpm.c index 9b42001295ba..e3e9c10cfba9 100644 --- a/drivers/gpu/drm/radeon/kv_dpm.c +++ b/drivers/gpu/drm/radeon/kv_dpm.c @@ -2745,13 +2745,11 @@ int kv_dpm_init(struct radeon_device *rdev) pi->enable_auto_thermal_throttling = true; pi->disable_nb_ps3_in_battery = false; if (radeon_bapm == -1) { - /* There are stability issues reported on with - * bapm enabled on an asrock system. - */ - if (rdev->pdev->subsystem_vendor == 0x1849) - pi->bapm_enable = false; - else + /* only enable bapm on KB, ML by default */ + if (rdev->family == CHIP_KABINI || rdev->family == CHIP_MULLINS) pi->bapm_enable = true; + else + pi->bapm_enable = false; } else if (radeon_bapm == 0) { pi->bapm_enable = false; } else { diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index 360de9f1f491..aea48c89b241 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -2516,6 +2516,16 @@ void cayman_vm_flush(struct radeon_device *rdev, struct radeon_ring *ring, radeon_ring_write(ring, PACKET0(VM_INVALIDATE_REQUEST, 0)); radeon_ring_write(ring, 1 << vm_id); + /* wait for the invalidate to complete */ + radeon_ring_write(ring, PACKET3(PACKET3_WAIT_REG_MEM, 5)); + radeon_ring_write(ring, (WAIT_REG_MEM_FUNCTION(0) | /* always */ + WAIT_REG_MEM_ENGINE(0))); /* me */ + radeon_ring_write(ring, VM_INVALIDATE_REQUEST >> 2); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); /* ref */ + radeon_ring_write(ring, 0); /* mask */ + radeon_ring_write(ring, 0x20); /* poll interval */ + /* sync PFP to ME, otherwise we might get invalid PFP reads */ radeon_ring_write(ring, PACKET3(PACKET3_PFP_SYNC_ME, 0)); radeon_ring_write(ring, 0x0); diff --git a/drivers/gpu/drm/radeon/ni_dma.c b/drivers/gpu/drm/radeon/ni_dma.c index 50f88611ff60..ce787a9f12c0 100644 --- a/drivers/gpu/drm/radeon/ni_dma.c +++ b/drivers/gpu/drm/radeon/ni_dma.c @@ -372,7 +372,6 @@ void cayman_dma_vm_write_pages(struct radeon_device *rdev, for (; ndw > 0; ndw -= 2, --count, pe += 8) { if (flags & R600_PTE_SYSTEM) { value = radeon_vm_map_gart(rdev, addr); - value &= 0xFFFFFFFFFFFFF000ULL; } else if (flags & R600_PTE_VALID) { value = addr; } else { @@ -463,5 +462,11 @@ void cayman_dma_vm_flush(struct radeon_device *rdev, struct radeon_ring *ring, radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_SRBM_WRITE, 0, 0, 0)); radeon_ring_write(ring, (0xf << 16) | (VM_INVALIDATE_REQUEST >> 2)); radeon_ring_write(ring, 1 << vm_id); + + /* wait for invalidate to complete */ + radeon_ring_write(ring, DMA_SRBM_READ_PACKET); + radeon_ring_write(ring, (0xff << 20) | (VM_INVALIDATE_REQUEST >> 2)); + radeon_ring_write(ring, 0); /* mask */ + radeon_ring_write(ring, 0); /* value */ } diff --git a/drivers/gpu/drm/radeon/nid.h b/drivers/gpu/drm/radeon/nid.h index 2e12e4d69253..ad7125486894 100644 --- a/drivers/gpu/drm/radeon/nid.h +++ b/drivers/gpu/drm/radeon/nid.h @@ -1133,6 +1133,23 @@ #define PACKET3_MEM_SEMAPHORE 0x39 #define PACKET3_MPEG_INDEX 0x3A #define PACKET3_WAIT_REG_MEM 0x3C +#define WAIT_REG_MEM_FUNCTION(x) ((x) << 0) + /* 0 - always + * 1 - < + * 2 - <= + * 3 - == + * 4 - != + * 5 - >= + * 6 - > + */ +#define WAIT_REG_MEM_MEM_SPACE(x) ((x) << 4) + /* 0 - reg + * 1 - mem + */ +#define WAIT_REG_MEM_ENGINE(x) ((x) << 8) + /* 0 - me + * 1 - pfp + */ #define PACKET3_MEM_WRITE 0x3D #define PACKET3_PFP_SYNC_ME 0x42 #define PACKET3_SURFACE_SYNC 0x43 @@ -1272,6 +1289,13 @@ (1 << 21) | \ (((n) & 0xFFFFF) << 0)) +#define DMA_SRBM_POLL_PACKET ((9 << 28) | \ + (1 << 27) | \ + (1 << 26)) + +#define DMA_SRBM_READ_PACKET ((9 << 28) | \ + (1 << 27)) + /* async DMA Packet types */ #define DMA_PACKET_WRITE 0x2 #define DMA_PACKET_COPY 0x3 diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index 74f06d540591..279801ca5110 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -644,6 +644,7 @@ int r100_pci_gart_init(struct radeon_device *rdev) return r; rdev->gart.table_size = rdev->gart.num_gpu_pages * 4; rdev->asic->gart.tlb_flush = &r100_pci_gart_tlb_flush; + rdev->asic->gart.get_page_entry = &r100_pci_gart_get_page_entry; rdev->asic->gart.set_page = &r100_pci_gart_set_page; return radeon_gart_table_ram_alloc(rdev); } @@ -681,11 +682,16 @@ void r100_pci_gart_disable(struct radeon_device *rdev) WREG32(RADEON_AIC_HI_ADDR, 0); } +uint64_t r100_pci_gart_get_page_entry(uint64_t addr, uint32_t flags) +{ + return addr; +} + void r100_pci_gart_set_page(struct radeon_device *rdev, unsigned i, - uint64_t addr, uint32_t flags) + uint64_t entry) { u32 *gtt = rdev->gart.ptr; - gtt[i] = cpu_to_le32(lower_32_bits(addr)); + gtt[i] = cpu_to_le32(lower_32_bits(entry)); } void r100_pci_gart_fini(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c index 064ad5569cca..08d68f3e13e9 100644 --- a/drivers/gpu/drm/radeon/r300.c +++ b/drivers/gpu/drm/radeon/r300.c @@ -73,11 +73,8 @@ void rv370_pcie_gart_tlb_flush(struct radeon_device *rdev) #define R300_PTE_WRITEABLE (1 << 2) #define R300_PTE_READABLE (1 << 3) -void rv370_pcie_gart_set_page(struct radeon_device *rdev, unsigned i, - uint64_t addr, uint32_t flags) +uint64_t rv370_pcie_gart_get_page_entry(uint64_t addr, uint32_t flags) { - void __iomem *ptr = rdev->gart.ptr; - addr = (lower_32_bits(addr) >> 8) | ((upper_32_bits(addr) & 0xff) << 24); if (flags & RADEON_GART_PAGE_READ) @@ -86,10 +83,18 @@ void rv370_pcie_gart_set_page(struct radeon_device *rdev, unsigned i, addr |= R300_PTE_WRITEABLE; if (!(flags & RADEON_GART_PAGE_SNOOP)) addr |= R300_PTE_UNSNOOPED; + return addr; +} + +void rv370_pcie_gart_set_page(struct radeon_device *rdev, unsigned i, + uint64_t entry) +{ + void __iomem *ptr = rdev->gart.ptr; + /* on x86 we want this to be CPU endian, on powerpc * on powerpc without HW swappers, it'll get swapped on way * into VRAM - so no need for cpu_to_le32 on VRAM tables */ - writel(addr, ((void __iomem *)ptr) + (i * 4)); + writel(entry, ((void __iomem *)ptr) + (i * 4)); } int rv370_pcie_gart_init(struct radeon_device *rdev) @@ -109,6 +114,7 @@ int rv370_pcie_gart_init(struct radeon_device *rdev) DRM_ERROR("Failed to register debugfs file for PCIE gart !\n"); rdev->gart.table_size = rdev->gart.num_gpu_pages * 4; rdev->asic->gart.tlb_flush = &rv370_pcie_gart_tlb_flush; + rdev->asic->gart.get_page_entry = &rv370_pcie_gart_get_page_entry; rdev->asic->gart.set_page = &rv370_pcie_gart_set_page; return radeon_gart_table_vram_alloc(rdev); } diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 54529b837afa..3f2a8d3febca 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -242,6 +242,7 @@ bool radeon_get_bios(struct radeon_device *rdev); * Dummy page */ struct radeon_dummy_page { + uint64_t entry; struct page *page; dma_addr_t addr; }; @@ -645,7 +646,7 @@ struct radeon_gart { unsigned num_cpu_pages; unsigned table_size; struct page **pages; - dma_addr_t *pages_addr; + uint64_t *pages_entry; bool ready; }; @@ -1847,8 +1848,9 @@ struct radeon_asic { /* gart */ struct { void (*tlb_flush)(struct radeon_device *rdev); + uint64_t (*get_page_entry)(uint64_t addr, uint32_t flags); void (*set_page)(struct radeon_device *rdev, unsigned i, - uint64_t addr, uint32_t flags); + uint64_t entry); } gart; struct { int (*init)(struct radeon_device *rdev); @@ -2852,7 +2854,8 @@ static inline void radeon_ring_write(struct radeon_ring *ring, uint32_t v) #define radeon_vga_set_state(rdev, state) (rdev)->asic->vga_set_state((rdev), (state)) #define radeon_asic_reset(rdev) (rdev)->asic->asic_reset((rdev)) #define radeon_gart_tlb_flush(rdev) (rdev)->asic->gart.tlb_flush((rdev)) -#define radeon_gart_set_page(rdev, i, p, f) (rdev)->asic->gart.set_page((rdev), (i), (p), (f)) +#define radeon_gart_get_page_entry(a, f) (rdev)->asic->gart.get_page_entry((a), (f)) +#define radeon_gart_set_page(rdev, i, e) (rdev)->asic->gart.set_page((rdev), (i), (e)) #define radeon_asic_vm_init(rdev) (rdev)->asic->vm.init((rdev)) #define radeon_asic_vm_fini(rdev) (rdev)->asic->vm.fini((rdev)) #define radeon_asic_vm_copy_pages(rdev, ib, pe, src, count) ((rdev)->asic->vm.copy_pages((rdev), (ib), (pe), (src), (count))) diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 850de57069be..ed0e10eee2dc 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -159,11 +159,13 @@ void radeon_agp_disable(struct radeon_device *rdev) DRM_INFO("Forcing AGP to PCIE mode\n"); rdev->flags |= RADEON_IS_PCIE; rdev->asic->gart.tlb_flush = &rv370_pcie_gart_tlb_flush; + rdev->asic->gart.get_page_entry = &rv370_pcie_gart_get_page_entry; rdev->asic->gart.set_page = &rv370_pcie_gart_set_page; } else { DRM_INFO("Forcing AGP to PCI mode\n"); rdev->flags |= RADEON_IS_PCI; rdev->asic->gart.tlb_flush = &r100_pci_gart_tlb_flush; + rdev->asic->gart.get_page_entry = &r100_pci_gart_get_page_entry; rdev->asic->gart.set_page = &r100_pci_gart_set_page; } rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024; @@ -199,6 +201,7 @@ static struct radeon_asic r100_asic = { .mc_wait_for_idle = &r100_mc_wait_for_idle, .gart = { .tlb_flush = &r100_pci_gart_tlb_flush, + .get_page_entry = &r100_pci_gart_get_page_entry, .set_page = &r100_pci_gart_set_page, }, .ring = { @@ -265,6 +268,7 @@ static struct radeon_asic r200_asic = { .mc_wait_for_idle = &r100_mc_wait_for_idle, .gart = { .tlb_flush = &r100_pci_gart_tlb_flush, + .get_page_entry = &r100_pci_gart_get_page_entry, .set_page = &r100_pci_gart_set_page, }, .ring = { @@ -333,6 +337,20 @@ static struct radeon_asic_ring r300_gfx_ring = { .set_wptr = &r100_gfx_set_wptr, }; +static struct radeon_asic_ring rv515_gfx_ring = { + .ib_execute = &r100_ring_ib_execute, + .emit_fence = &r300_fence_ring_emit, + .emit_semaphore = &r100_semaphore_ring_emit, + .cs_parse = &r300_cs_parse, + .ring_start = &rv515_ring_start, + .ring_test = &r100_ring_test, + .ib_test = &r100_ib_test, + .is_lockup = &r100_gpu_is_lockup, + .get_rptr = &r100_gfx_get_rptr, + .get_wptr = &r100_gfx_get_wptr, + .set_wptr = &r100_gfx_set_wptr, +}; + static struct radeon_asic r300_asic = { .init = &r300_init, .fini = &r300_fini, @@ -345,6 +363,7 @@ static struct radeon_asic r300_asic = { .mc_wait_for_idle = &r300_mc_wait_for_idle, .gart = { .tlb_flush = &r100_pci_gart_tlb_flush, + .get_page_entry = &r100_pci_gart_get_page_entry, .set_page = &r100_pci_gart_set_page, }, .ring = { @@ -411,6 +430,7 @@ static struct radeon_asic r300_asic_pcie = { .mc_wait_for_idle = &r300_mc_wait_for_idle, .gart = { .tlb_flush = &rv370_pcie_gart_tlb_flush, + .get_page_entry = &rv370_pcie_gart_get_page_entry, .set_page = &rv370_pcie_gart_set_page, }, .ring = { @@ -477,6 +497,7 @@ static struct radeon_asic r420_asic = { .mc_wait_for_idle = &r300_mc_wait_for_idle, .gart = { .tlb_flush = &rv370_pcie_gart_tlb_flush, + .get_page_entry = &rv370_pcie_gart_get_page_entry, .set_page = &rv370_pcie_gart_set_page, }, .ring = { @@ -543,6 +564,7 @@ static struct radeon_asic rs400_asic = { .mc_wait_for_idle = &rs400_mc_wait_for_idle, .gart = { .tlb_flush = &rs400_gart_tlb_flush, + .get_page_entry = &rs400_gart_get_page_entry, .set_page = &rs400_gart_set_page, }, .ring = { @@ -609,6 +631,7 @@ static struct radeon_asic rs600_asic = { .mc_wait_for_idle = &rs600_mc_wait_for_idle, .gart = { .tlb_flush = &rs600_gart_tlb_flush, + .get_page_entry = &rs600_gart_get_page_entry, .set_page = &rs600_gart_set_page, }, .ring = { @@ -677,6 +700,7 @@ static struct radeon_asic rs690_asic = { .mc_wait_for_idle = &rs690_mc_wait_for_idle, .gart = { .tlb_flush = &rs400_gart_tlb_flush, + .get_page_entry = &rs400_gart_get_page_entry, .set_page = &rs400_gart_set_page, }, .ring = { @@ -745,10 +769,11 @@ static struct radeon_asic rv515_asic = { .mc_wait_for_idle = &rv515_mc_wait_for_idle, .gart = { .tlb_flush = &rv370_pcie_gart_tlb_flush, + .get_page_entry = &rv370_pcie_gart_get_page_entry, .set_page = &rv370_pcie_gart_set_page, }, .ring = { - [RADEON_RING_TYPE_GFX_INDEX] = &r300_gfx_ring + [RADEON_RING_TYPE_GFX_INDEX] = &rv515_gfx_ring }, .irq = { .set = &rs600_irq_set, @@ -811,10 +836,11 @@ static struct radeon_asic r520_asic = { .mc_wait_for_idle = &r520_mc_wait_for_idle, .gart = { .tlb_flush = &rv370_pcie_gart_tlb_flush, + .get_page_entry = &rv370_pcie_gart_get_page_entry, .set_page = &rv370_pcie_gart_set_page, }, .ring = { - [RADEON_RING_TYPE_GFX_INDEX] = &r300_gfx_ring + [RADEON_RING_TYPE_GFX_INDEX] = &rv515_gfx_ring }, .irq = { .set = &rs600_irq_set, @@ -905,6 +931,7 @@ static struct radeon_asic r600_asic = { .get_gpu_clock_counter = &r600_get_gpu_clock_counter, .gart = { .tlb_flush = &r600_pcie_gart_tlb_flush, + .get_page_entry = &rs600_gart_get_page_entry, .set_page = &rs600_gart_set_page, }, .ring = { @@ -990,6 +1017,7 @@ static struct radeon_asic rv6xx_asic = { .get_gpu_clock_counter = &r600_get_gpu_clock_counter, .gart = { .tlb_flush = &r600_pcie_gart_tlb_flush, + .get_page_entry = &rs600_gart_get_page_entry, .set_page = &rs600_gart_set_page, }, .ring = { @@ -1081,6 +1109,7 @@ static struct radeon_asic rs780_asic = { .get_gpu_clock_counter = &r600_get_gpu_clock_counter, .gart = { .tlb_flush = &r600_pcie_gart_tlb_flush, + .get_page_entry = &rs600_gart_get_page_entry, .set_page = &rs600_gart_set_page, }, .ring = { @@ -1185,6 +1214,7 @@ static struct radeon_asic rv770_asic = { .get_gpu_clock_counter = &r600_get_gpu_clock_counter, .gart = { .tlb_flush = &r600_pcie_gart_tlb_flush, + .get_page_entry = &rs600_gart_get_page_entry, .set_page = &rs600_gart_set_page, }, .ring = { @@ -1303,6 +1333,7 @@ static struct radeon_asic evergreen_asic = { .get_gpu_clock_counter = &r600_get_gpu_clock_counter, .gart = { .tlb_flush = &evergreen_pcie_gart_tlb_flush, + .get_page_entry = &rs600_gart_get_page_entry, .set_page = &rs600_gart_set_page, }, .ring = { @@ -1395,6 +1426,7 @@ static struct radeon_asic sumo_asic = { .get_gpu_clock_counter = &r600_get_gpu_clock_counter, .gart = { .tlb_flush = &evergreen_pcie_gart_tlb_flush, + .get_page_entry = &rs600_gart_get_page_entry, .set_page = &rs600_gart_set_page, }, .ring = { @@ -1486,6 +1518,7 @@ static struct radeon_asic btc_asic = { .get_gpu_clock_counter = &r600_get_gpu_clock_counter, .gart = { .tlb_flush = &evergreen_pcie_gart_tlb_flush, + .get_page_entry = &rs600_gart_get_page_entry, .set_page = &rs600_gart_set_page, }, .ring = { @@ -1621,6 +1654,7 @@ static struct radeon_asic cayman_asic = { .get_gpu_clock_counter = &r600_get_gpu_clock_counter, .gart = { .tlb_flush = &cayman_pcie_gart_tlb_flush, + .get_page_entry = &rs600_gart_get_page_entry, .set_page = &rs600_gart_set_page, }, .vm = { @@ -1724,6 +1758,7 @@ static struct radeon_asic trinity_asic = { .get_gpu_clock_counter = &r600_get_gpu_clock_counter, .gart = { .tlb_flush = &cayman_pcie_gart_tlb_flush, + .get_page_entry = &rs600_gart_get_page_entry, .set_page = &rs600_gart_set_page, }, .vm = { @@ -1857,6 +1892,7 @@ static struct radeon_asic si_asic = { .get_gpu_clock_counter = &si_get_gpu_clock_counter, .gart = { .tlb_flush = &si_pcie_gart_tlb_flush, + .get_page_entry = &rs600_gart_get_page_entry, .set_page = &rs600_gart_set_page, }, .vm = { @@ -2018,6 +2054,7 @@ static struct radeon_asic ci_asic = { .get_gpu_clock_counter = &cik_get_gpu_clock_counter, .gart = { .tlb_flush = &cik_pcie_gart_tlb_flush, + .get_page_entry = &rs600_gart_get_page_entry, .set_page = &rs600_gart_set_page, }, .vm = { @@ -2125,6 +2162,7 @@ static struct radeon_asic kv_asic = { .get_gpu_clock_counter = &cik_get_gpu_clock_counter, .gart = { .tlb_flush = &cik_pcie_gart_tlb_flush, + .get_page_entry = &rs600_gart_get_page_entry, .set_page = &rs600_gart_set_page, }, .vm = { diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 2a45d548d5ec..8d787d115653 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -67,8 +67,9 @@ bool r100_gpu_is_lockup(struct radeon_device *rdev, struct radeon_ring *cp); int r100_asic_reset(struct radeon_device *rdev); u32 r100_get_vblank_counter(struct radeon_device *rdev, int crtc); void r100_pci_gart_tlb_flush(struct radeon_device *rdev); +uint64_t r100_pci_gart_get_page_entry(uint64_t addr, uint32_t flags); void r100_pci_gart_set_page(struct radeon_device *rdev, unsigned i, - uint64_t addr, uint32_t flags); + uint64_t entry); void r100_ring_start(struct radeon_device *rdev, struct radeon_ring *ring); int r100_irq_set(struct radeon_device *rdev); int r100_irq_process(struct radeon_device *rdev); @@ -172,8 +173,9 @@ extern void r300_fence_ring_emit(struct radeon_device *rdev, struct radeon_fence *fence); extern int r300_cs_parse(struct radeon_cs_parser *p); extern void rv370_pcie_gart_tlb_flush(struct radeon_device *rdev); +extern uint64_t rv370_pcie_gart_get_page_entry(uint64_t addr, uint32_t flags); extern void rv370_pcie_gart_set_page(struct radeon_device *rdev, unsigned i, - uint64_t addr, uint32_t flags); + uint64_t entry); extern void rv370_set_pcie_lanes(struct radeon_device *rdev, int lanes); extern int rv370_get_pcie_lanes(struct radeon_device *rdev); extern void r300_set_reg_safe(struct radeon_device *rdev); @@ -208,8 +210,9 @@ extern void rs400_fini(struct radeon_device *rdev); extern int rs400_suspend(struct radeon_device *rdev); extern int rs400_resume(struct radeon_device *rdev); void rs400_gart_tlb_flush(struct radeon_device *rdev); +uint64_t rs400_gart_get_page_entry(uint64_t addr, uint32_t flags); void rs400_gart_set_page(struct radeon_device *rdev, unsigned i, - uint64_t addr, uint32_t flags); + uint64_t entry); uint32_t rs400_mc_rreg(struct radeon_device *rdev, uint32_t reg); void rs400_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); int rs400_gart_init(struct radeon_device *rdev); @@ -232,8 +235,9 @@ int rs600_irq_process(struct radeon_device *rdev); void rs600_irq_disable(struct radeon_device *rdev); u32 rs600_get_vblank_counter(struct radeon_device *rdev, int crtc); void rs600_gart_tlb_flush(struct radeon_device *rdev); +uint64_t rs600_gart_get_page_entry(uint64_t addr, uint32_t flags); void rs600_gart_set_page(struct radeon_device *rdev, unsigned i, - uint64_t addr, uint32_t flags); + uint64_t entry); uint32_t rs600_mc_rreg(struct radeon_device *rdev, uint32_t reg); void rs600_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); void rs600_bandwidth_update(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/radeon_benchmark.c b/drivers/gpu/drm/radeon/radeon_benchmark.c index 9e7f23dd14bd..87d5fb21cb61 100644 --- a/drivers/gpu/drm/radeon/radeon_benchmark.c +++ b/drivers/gpu/drm/radeon/radeon_benchmark.c @@ -34,7 +34,8 @@ static int radeon_benchmark_do_move(struct radeon_device *rdev, unsigned size, uint64_t saddr, uint64_t daddr, - int flag, int n) + int flag, int n, + struct reservation_object *resv) { unsigned long start_jiffies; unsigned long end_jiffies; @@ -47,12 +48,12 @@ static int radeon_benchmark_do_move(struct radeon_device *rdev, unsigned size, case RADEON_BENCHMARK_COPY_DMA: fence = radeon_copy_dma(rdev, saddr, daddr, size / RADEON_GPU_PAGE_SIZE, - NULL); + resv); break; case RADEON_BENCHMARK_COPY_BLIT: fence = radeon_copy_blit(rdev, saddr, daddr, size / RADEON_GPU_PAGE_SIZE, - NULL); + resv); break; default: DRM_ERROR("Unknown copy method\n"); @@ -120,7 +121,8 @@ static void radeon_benchmark_move(struct radeon_device *rdev, unsigned size, if (rdev->asic->copy.dma) { time = radeon_benchmark_do_move(rdev, size, saddr, daddr, - RADEON_BENCHMARK_COPY_DMA, n); + RADEON_BENCHMARK_COPY_DMA, n, + dobj->tbo.resv); if (time < 0) goto out_cleanup; if (time > 0) @@ -130,7 +132,8 @@ static void radeon_benchmark_move(struct radeon_device *rdev, unsigned size, if (rdev->asic->copy.blit) { time = radeon_benchmark_do_move(rdev, size, saddr, daddr, - RADEON_BENCHMARK_COPY_BLIT, n); + RADEON_BENCHMARK_COPY_BLIT, n, + dobj->tbo.resv); if (time < 0) goto out_cleanup; if (time > 0) diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 0ec65168f331..bd7519fdd3f4 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -774,6 +774,8 @@ int radeon_dummy_page_init(struct radeon_device *rdev) rdev->dummy_page.page = NULL; return -ENOMEM; } + rdev->dummy_page.entry = radeon_gart_get_page_entry(rdev->dummy_page.addr, + RADEON_GART_PAGE_DUMMY); return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 102116902a07..913fafa597ad 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -960,6 +960,9 @@ void radeon_compute_pll_avivo(struct radeon_pll *pll, if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV && pll->flags & RADEON_PLL_USE_REF_DIV) ref_div_max = pll->reference_div; + else if (pll->flags & RADEON_PLL_PREFER_MINM_OVER_MAXP) + /* fix for problems on RS880 */ + ref_div_max = min(pll->max_ref_div, 7u); else ref_div_max = pll->max_ref_div; diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c index 84146d5901aa..5450fa95a47e 100644 --- a/drivers/gpu/drm/radeon/radeon_gart.c +++ b/drivers/gpu/drm/radeon/radeon_gart.c @@ -165,6 +165,19 @@ int radeon_gart_table_vram_pin(struct radeon_device *rdev) radeon_bo_unpin(rdev->gart.robj); radeon_bo_unreserve(rdev->gart.robj); rdev->gart.table_addr = gpu_addr; + + if (!r) { + int i; + + /* We might have dropped some GART table updates while it wasn't + * mapped, restore all entries + */ + for (i = 0; i < rdev->gart.num_gpu_pages; i++) + radeon_gart_set_page(rdev, i, rdev->gart.pages_entry[i]); + mb(); + radeon_gart_tlb_flush(rdev); + } + return r; } @@ -228,7 +241,6 @@ void radeon_gart_unbind(struct radeon_device *rdev, unsigned offset, unsigned t; unsigned p; int i, j; - u64 page_base; if (!rdev->gart.ready) { WARN(1, "trying to unbind memory from uninitialized GART !\n"); @@ -239,14 +251,12 @@ void radeon_gart_unbind(struct radeon_device *rdev, unsigned offset, for (i = 0; i < pages; i++, p++) { if (rdev->gart.pages[p]) { rdev->gart.pages[p] = NULL; - rdev->gart.pages_addr[p] = rdev->dummy_page.addr; - page_base = rdev->gart.pages_addr[p]; for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) { + rdev->gart.pages_entry[t] = rdev->dummy_page.entry; if (rdev->gart.ptr) { - radeon_gart_set_page(rdev, t, page_base, - RADEON_GART_PAGE_DUMMY); + radeon_gart_set_page(rdev, t, + rdev->dummy_page.entry); } - page_base += RADEON_GPU_PAGE_SIZE; } } } @@ -274,7 +284,7 @@ int radeon_gart_bind(struct radeon_device *rdev, unsigned offset, { unsigned t; unsigned p; - uint64_t page_base; + uint64_t page_base, page_entry; int i, j; if (!rdev->gart.ready) { @@ -285,14 +295,15 @@ int radeon_gart_bind(struct radeon_device *rdev, unsigned offset, p = t / (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); for (i = 0; i < pages; i++, p++) { - rdev->gart.pages_addr[p] = dma_addr[i]; rdev->gart.pages[p] = pagelist[i]; - if (rdev->gart.ptr) { - page_base = rdev->gart.pages_addr[p]; - for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) { - radeon_gart_set_page(rdev, t, page_base, flags); - page_base += RADEON_GPU_PAGE_SIZE; + page_base = dma_addr[i]; + for (j = 0; j < (PAGE_SIZE / RADEON_GPU_PAGE_SIZE); j++, t++) { + page_entry = radeon_gart_get_page_entry(page_base, flags); + rdev->gart.pages_entry[t] = page_entry; + if (rdev->gart.ptr) { + radeon_gart_set_page(rdev, t, page_entry); } + page_base += RADEON_GPU_PAGE_SIZE; } } mb(); @@ -334,16 +345,15 @@ int radeon_gart_init(struct radeon_device *rdev) radeon_gart_fini(rdev); return -ENOMEM; } - rdev->gart.pages_addr = vzalloc(sizeof(dma_addr_t) * - rdev->gart.num_cpu_pages); - if (rdev->gart.pages_addr == NULL) { + rdev->gart.pages_entry = vmalloc(sizeof(uint64_t) * + rdev->gart.num_gpu_pages); + if (rdev->gart.pages_entry == NULL) { radeon_gart_fini(rdev); return -ENOMEM; } /* set GART entry to point to the dummy page by default */ - for (i = 0; i < rdev->gart.num_cpu_pages; i++) { - rdev->gart.pages_addr[i] = rdev->dummy_page.addr; - } + for (i = 0; i < rdev->gart.num_gpu_pages; i++) + rdev->gart.pages_entry[i] = rdev->dummy_page.entry; return 0; } @@ -356,15 +366,15 @@ int radeon_gart_init(struct radeon_device *rdev) */ void radeon_gart_fini(struct radeon_device *rdev) { - if (rdev->gart.pages && rdev->gart.pages_addr && rdev->gart.ready) { + if (rdev->gart.ready) { /* unbind pages */ radeon_gart_unbind(rdev, 0, rdev->gart.num_cpu_pages); } rdev->gart.ready = false; vfree(rdev->gart.pages); - vfree(rdev->gart.pages_addr); + vfree(rdev->gart.pages_entry); rdev->gart.pages = NULL; - rdev->gart.pages_addr = NULL; + rdev->gart.pages_entry = NULL; radeon_dummy_page_fini(rdev); } diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c index a46f73737994..ac3c1310b953 100644 --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -146,7 +146,8 @@ int radeon_gem_object_open(struct drm_gem_object *obj, struct drm_file *file_pri struct radeon_bo_va *bo_va; int r; - if (rdev->family < CHIP_CAYMAN) { + if ((rdev->family < CHIP_CAYMAN) || + (!rdev->accel_working)) { return 0; } @@ -176,7 +177,8 @@ void radeon_gem_object_close(struct drm_gem_object *obj, struct radeon_bo_va *bo_va; int r; - if (rdev->family < CHIP_CAYMAN) { + if ((rdev->family < CHIP_CAYMAN) || + (!rdev->accel_working)) { return; } @@ -576,7 +578,7 @@ error_unreserve: error_free: drm_free_large(vm_bos); - if (r) + if (r && r != -ERESTARTSYS) DRM_ERROR("Couldn't update BO_VA (%d)\n", r); } diff --git a/drivers/gpu/drm/radeon/radeon_kfd.c b/drivers/gpu/drm/radeon/radeon_kfd.c index 242fd8b1b221..bef9a0953284 100644 --- a/drivers/gpu/drm/radeon/radeon_kfd.c +++ b/drivers/gpu/drm/radeon/radeon_kfd.c @@ -72,7 +72,7 @@ static int kgd_init_pipeline(struct kgd_dev *kgd, uint32_t pipe_id, static int kgd_hqd_load(struct kgd_dev *kgd, void *mqd, uint32_t pipe_id, uint32_t queue_id, uint32_t __user *wptr); -static bool kgd_hqd_is_occupies(struct kgd_dev *kgd, uint64_t queue_address, +static bool kgd_hqd_is_occupied(struct kgd_dev *kgd, uint64_t queue_address, uint32_t pipe_id, uint32_t queue_id); static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type, @@ -92,7 +92,7 @@ static const struct kfd2kgd_calls kfd2kgd = { .init_memory = kgd_init_memory, .init_pipeline = kgd_init_pipeline, .hqd_load = kgd_hqd_load, - .hqd_is_occupies = kgd_hqd_is_occupies, + .hqd_is_occupied = kgd_hqd_is_occupied, .hqd_destroy = kgd_hqd_destroy, .get_fw_version = get_fw_version }; @@ -101,6 +101,7 @@ static const struct kgd2kfd_calls *kgd2kfd; bool radeon_kfd_init(void) { +#if defined(CONFIG_HSA_AMD_MODULE) bool (*kgd2kfd_init_p)(unsigned, const struct kfd2kgd_calls*, const struct kgd2kfd_calls**); @@ -117,6 +118,17 @@ bool radeon_kfd_init(void) } return true; +#elif defined(CONFIG_HSA_AMD) + if (!kgd2kfd_init(KFD_INTERFACE_VERSION, &kfd2kgd, &kgd2kfd)) { + kgd2kfd = NULL; + + return false; + } + + return true; +#else + return false; +#endif } void radeon_kfd_fini(void) @@ -378,6 +390,10 @@ static int kgd_set_pasid_vmid_mapping(struct kgd_dev *kgd, unsigned int pasid, cpu_relax(); write_register(kgd, ATC_VMID_PASID_MAPPING_UPDATE_STATUS, 1U << vmid); + /* Mapping vmid to pasid also for IH block */ + write_register(kgd, IH_VMID_0_LUT + vmid * sizeof(uint32_t), + pasid_mapping); + return 0; } @@ -420,7 +436,7 @@ static int kgd_init_memory(struct kgd_dev *kgd) static int kgd_init_pipeline(struct kgd_dev *kgd, uint32_t pipe_id, uint32_t hpd_size, uint64_t hpd_gpu_addr) { - uint32_t mec = (++pipe_id / CIK_PIPE_PER_MEC) + 1; + uint32_t mec = (pipe_id / CIK_PIPE_PER_MEC) + 1; uint32_t pipe = (pipe_id % CIK_PIPE_PER_MEC); lock_srbm(kgd, mec, pipe, 0, 0); @@ -517,7 +533,7 @@ static int kgd_hqd_load(struct kgd_dev *kgd, void *mqd, uint32_t pipe_id, return 0; } -static bool kgd_hqd_is_occupies(struct kgd_dev *kgd, uint64_t queue_address, +static bool kgd_hqd_is_occupied(struct kgd_dev *kgd, uint64_t queue_address, uint32_t pipe_id, uint32_t queue_id) { uint32_t act; @@ -556,6 +572,7 @@ static int kgd_hqd_destroy(struct kgd_dev *kgd, uint32_t reset_type, if (timeout == 0) { pr_err("kfd: cp queue preemption time out (%dms)\n", temp); + release_queue(kgd); return -ETIME; } msleep(20); diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 3cf9c1fa6475..686411e4e4f6 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -605,14 +605,14 @@ int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) return -ENOMEM; } - vm = &fpriv->vm; - r = radeon_vm_init(rdev, vm); - if (r) { - kfree(fpriv); - return r; - } - if (rdev->accel_working) { + vm = &fpriv->vm; + r = radeon_vm_init(rdev, vm); + if (r) { + kfree(fpriv); + return r; + } + r = radeon_bo_reserve(rdev->ring_tmp_bo.bo, false); if (r) { radeon_vm_fini(rdev, vm); @@ -668,9 +668,9 @@ void radeon_driver_postclose_kms(struct drm_device *dev, radeon_vm_bo_rmv(rdev, vm->ib_bo_va); radeon_bo_unreserve(rdev->ring_tmp_bo.bo); } + radeon_vm_fini(rdev, vm); } - radeon_vm_fini(rdev, vm); kfree(fpriv); file_priv->driver_priv = NULL; } diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 32522cc940a1..f7da8fe96a66 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1287,8 +1287,39 @@ dpm_failed: return ret; } +struct radeon_dpm_quirk { + u32 chip_vendor; + u32 chip_device; + u32 subsys_vendor; + u32 subsys_device; +}; + +/* cards with dpm stability problems */ +static struct radeon_dpm_quirk radeon_dpm_quirk_list[] = { + /* TURKS - https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1386534 */ + { PCI_VENDOR_ID_ATI, 0x6759, 0x1682, 0x3195 }, + /* TURKS - https://bugzilla.kernel.org/show_bug.cgi?id=83731 */ + { PCI_VENDOR_ID_ATI, 0x6840, 0x1179, 0xfb81 }, + { 0, 0, 0, 0 }, +}; + int radeon_pm_init(struct radeon_device *rdev) { + struct radeon_dpm_quirk *p = radeon_dpm_quirk_list; + bool disable_dpm = false; + + /* Apply dpm quirks */ + while (p && p->chip_device != 0) { + if (rdev->pdev->vendor == p->chip_vendor && + rdev->pdev->device == p->chip_device && + rdev->pdev->subsystem_vendor == p->subsys_vendor && + rdev->pdev->subsystem_device == p->subsys_device) { + disable_dpm = true; + break; + } + ++p; + } + /* enable dpm on rv6xx+ */ switch (rdev->family) { case CHIP_RV610: @@ -1344,6 +1375,8 @@ int radeon_pm_init(struct radeon_device *rdev) (!(rdev->flags & RADEON_IS_IGP)) && (!rdev->smc_fw)) rdev->pm.pm_method = PM_METHOD_PROFILE; + else if (disable_dpm && (radeon_dpm == -1)) + rdev->pm.pm_method = PM_METHOD_PROFILE; else if (radeon_dpm == 0) rdev->pm.pm_method = PM_METHOD_PROFILE; else diff --git a/drivers/gpu/drm/radeon/radeon_state.c b/drivers/gpu/drm/radeon/radeon_state.c index 535403e0c8a2..15aee723db77 100644 --- a/drivers/gpu/drm/radeon/radeon_state.c +++ b/drivers/gpu/drm/radeon/radeon_state.c @@ -1703,7 +1703,7 @@ static int radeon_cp_dispatch_texture(struct drm_device * dev, u32 format; u32 *buffer; const u8 __user *data; - int size, dwords, tex_width, blit_width, spitch; + unsigned int size, dwords, tex_width, blit_width, spitch; u32 height; int i; u32 texpitch, microtile; diff --git a/drivers/gpu/drm/radeon/radeon_test.c b/drivers/gpu/drm/radeon/radeon_test.c index 07b506b41008..791818165c76 100644 --- a/drivers/gpu/drm/radeon/radeon_test.c +++ b/drivers/gpu/drm/radeon/radeon_test.c @@ -119,11 +119,11 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag) if (ring == R600_RING_TYPE_DMA_INDEX) fence = radeon_copy_dma(rdev, gtt_addr, vram_addr, size / RADEON_GPU_PAGE_SIZE, - NULL); + vram_obj->tbo.resv); else fence = radeon_copy_blit(rdev, gtt_addr, vram_addr, size / RADEON_GPU_PAGE_SIZE, - NULL); + vram_obj->tbo.resv); if (IS_ERR(fence)) { DRM_ERROR("Failed GTT->VRAM copy %d\n", i); r = PTR_ERR(fence); @@ -170,11 +170,11 @@ static void radeon_do_test_moves(struct radeon_device *rdev, int flag) if (ring == R600_RING_TYPE_DMA_INDEX) fence = radeon_copy_dma(rdev, vram_addr, gtt_addr, size / RADEON_GPU_PAGE_SIZE, - NULL); + vram_obj->tbo.resv); else fence = radeon_copy_blit(rdev, vram_addr, gtt_addr, size / RADEON_GPU_PAGE_SIZE, - NULL); + vram_obj->tbo.resv); if (IS_ERR(fence)) { DRM_ERROR("Failed VRAM->GTT copy %d\n", i); r = PTR_ERR(fence); diff --git a/drivers/gpu/drm/radeon/radeon_vm.c b/drivers/gpu/drm/radeon/radeon_vm.c index cde48c42b30a..2a5a4a9e772d 100644 --- a/drivers/gpu/drm/radeon/radeon_vm.c +++ b/drivers/gpu/drm/radeon/radeon_vm.c @@ -587,10 +587,8 @@ uint64_t radeon_vm_map_gart(struct radeon_device *rdev, uint64_t addr) uint64_t result; /* page table offset */ - result = rdev->gart.pages_addr[addr >> PAGE_SHIFT]; - - /* in case cpu page size != gpu page size*/ - result |= addr & (~PAGE_MASK); + result = rdev->gart.pages_entry[addr >> RADEON_GPU_PAGE_SHIFT]; + result &= ~RADEON_GPU_PAGE_MASK; return result; } @@ -745,9 +743,11 @@ static void radeon_vm_frag_ptes(struct radeon_device *rdev, */ /* NI is optimized for 256KB fragments, SI and newer for 64KB */ - uint64_t frag_flags = rdev->family == CHIP_CAYMAN ? + uint64_t frag_flags = ((rdev->family == CHIP_CAYMAN) || + (rdev->family == CHIP_ARUBA)) ? R600_PTE_FRAG_256KB : R600_PTE_FRAG_64KB; - uint64_t frag_align = rdev->family == CHIP_CAYMAN ? 0x200 : 0x80; + uint64_t frag_align = ((rdev->family == CHIP_CAYMAN) || + (rdev->family == CHIP_ARUBA)) ? 0x200 : 0x80; uint64_t frag_start = ALIGN(pe_start, frag_align); uint64_t frag_end = pe_end & ~(frag_align - 1); diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c index c5799f16aa4b..34e3235f41d2 100644 --- a/drivers/gpu/drm/radeon/rs400.c +++ b/drivers/gpu/drm/radeon/rs400.c @@ -212,11 +212,9 @@ void rs400_gart_fini(struct radeon_device *rdev) #define RS400_PTE_WRITEABLE (1 << 2) #define RS400_PTE_READABLE (1 << 3) -void rs400_gart_set_page(struct radeon_device *rdev, unsigned i, - uint64_t addr, uint32_t flags) +uint64_t rs400_gart_get_page_entry(uint64_t addr, uint32_t flags) { uint32_t entry; - u32 *gtt = rdev->gart.ptr; entry = (lower_32_bits(addr) & PAGE_MASK) | ((upper_32_bits(addr) & 0xff) << 4); @@ -226,8 +224,14 @@ void rs400_gart_set_page(struct radeon_device *rdev, unsigned i, entry |= RS400_PTE_WRITEABLE; if (!(flags & RADEON_GART_PAGE_SNOOP)) entry |= RS400_PTE_UNSNOOPED; - entry = cpu_to_le32(entry); - gtt[i] = entry; + return entry; +} + +void rs400_gart_set_page(struct radeon_device *rdev, unsigned i, + uint64_t entry) +{ + u32 *gtt = rdev->gart.ptr; + gtt[i] = cpu_to_le32(lower_32_bits(entry)); } int rs400_mc_wait_for_idle(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index 9acb1c3c005b..74bce91aecc1 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -625,11 +625,8 @@ static void rs600_gart_fini(struct radeon_device *rdev) radeon_gart_table_vram_free(rdev); } -void rs600_gart_set_page(struct radeon_device *rdev, unsigned i, - uint64_t addr, uint32_t flags) +uint64_t rs600_gart_get_page_entry(uint64_t addr, uint32_t flags) { - void __iomem *ptr = (void *)rdev->gart.ptr; - addr = addr & 0xFFFFFFFFFFFFF000ULL; addr |= R600_PTE_SYSTEM; if (flags & RADEON_GART_PAGE_VALID) @@ -640,7 +637,14 @@ void rs600_gart_set_page(struct radeon_device *rdev, unsigned i, addr |= R600_PTE_WRITEABLE; if (flags & RADEON_GART_PAGE_SNOOP) addr |= R600_PTE_SNOOPED; - writeq(addr, ptr + (i * 8)); + return addr; +} + +void rs600_gart_set_page(struct radeon_device *rdev, unsigned i, + uint64_t entry) +{ + void __iomem *ptr = (void *)rdev->gart.ptr; + writeq(entry, ptr + (i * 8)); } int rs600_irq_set(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index 60df444bd075..5d89b874a1a2 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -5057,6 +5057,16 @@ void si_vm_flush(struct radeon_device *rdev, struct radeon_ring *ring, radeon_ring_write(ring, 0); radeon_ring_write(ring, 1 << vm_id); + /* wait for the invalidate to complete */ + radeon_ring_write(ring, PACKET3(PACKET3_WAIT_REG_MEM, 5)); + radeon_ring_write(ring, (WAIT_REG_MEM_FUNCTION(0) | /* always */ + WAIT_REG_MEM_ENGINE(0))); /* me */ + radeon_ring_write(ring, VM_INVALIDATE_REQUEST >> 2); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); /* ref */ + radeon_ring_write(ring, 0); /* mask */ + radeon_ring_write(ring, 0x20); /* poll interval */ + /* sync PFP to ME, otherwise we might get invalid PFP reads */ radeon_ring_write(ring, PACKET3(PACKET3_PFP_SYNC_ME, 0)); radeon_ring_write(ring, 0x0); diff --git a/drivers/gpu/drm/radeon/si_dma.c b/drivers/gpu/drm/radeon/si_dma.c index f5cc777e1c5f..83207929fc62 100644 --- a/drivers/gpu/drm/radeon/si_dma.c +++ b/drivers/gpu/drm/radeon/si_dma.c @@ -123,7 +123,6 @@ void si_dma_vm_write_pages(struct radeon_device *rdev, for (; ndw > 0; ndw -= 2, --count, pe += 8) { if (flags & R600_PTE_SYSTEM) { value = radeon_vm_map_gart(rdev, addr); - value &= 0xFFFFFFFFFFFFF000ULL; } else if (flags & R600_PTE_VALID) { value = addr; } else { @@ -206,6 +205,14 @@ void si_dma_vm_flush(struct radeon_device *rdev, struct radeon_ring *ring, radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_SRBM_WRITE, 0, 0, 0, 0)); radeon_ring_write(ring, (0xf << 16) | (VM_INVALIDATE_REQUEST >> 2)); radeon_ring_write(ring, 1 << vm_id); + + /* wait for invalidate to complete */ + radeon_ring_write(ring, DMA_PACKET(DMA_PACKET_POLL_REG_MEM, 0, 0, 0, 0)); + radeon_ring_write(ring, VM_INVALIDATE_REQUEST); + radeon_ring_write(ring, 0xff << 16); /* retry */ + radeon_ring_write(ring, 1 << vm_id); /* mask */ + radeon_ring_write(ring, 0); /* value */ + radeon_ring_write(ring, (0 << 28) | 0x20); /* func(always) | poll interval */ } /** diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index 32e354b8b0ab..eff8a6444956 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -2908,6 +2908,22 @@ static int si_init_smc_spll_table(struct radeon_device *rdev) return ret; } +struct si_dpm_quirk { + u32 chip_vendor; + u32 chip_device; + u32 subsys_vendor; + u32 subsys_device; + u32 max_sclk; + u32 max_mclk; +}; + +/* cards with dpm stability problems */ +static struct si_dpm_quirk si_dpm_quirk_list[] = { + /* PITCAIRN - https://bugs.freedesktop.org/show_bug.cgi?id=76490 */ + { PCI_VENDOR_ID_ATI, 0x6810, 0x1462, 0x3036, 0, 120000 }, + { 0, 0, 0, 0 }, +}; + static void si_apply_state_adjust_rules(struct radeon_device *rdev, struct radeon_ps *rps) { @@ -2918,7 +2934,22 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev, u32 mclk, sclk; u16 vddc, vddci; u32 max_sclk_vddc, max_mclk_vddci, max_mclk_vddc; + u32 max_sclk = 0, max_mclk = 0; int i; + struct si_dpm_quirk *p = si_dpm_quirk_list; + + /* Apply dpm quirks */ + while (p && p->chip_device != 0) { + if (rdev->pdev->vendor == p->chip_vendor && + rdev->pdev->device == p->chip_device && + rdev->pdev->subsystem_vendor == p->subsys_vendor && + rdev->pdev->subsystem_device == p->subsys_device) { + max_sclk = p->max_sclk; + max_mclk = p->max_mclk; + break; + } + ++p; + } if ((rdev->pm.dpm.new_active_crtc_count > 1) || ni_dpm_vblank_too_short(rdev)) @@ -2972,6 +3003,14 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev, if (ps->performance_levels[i].mclk > max_mclk_vddc) ps->performance_levels[i].mclk = max_mclk_vddc; } + if (max_mclk) { + if (ps->performance_levels[i].mclk > max_mclk) + ps->performance_levels[i].mclk = max_mclk; + } + if (max_sclk) { + if (ps->performance_levels[i].sclk > max_sclk) + ps->performance_levels[i].sclk = max_sclk; + } } /* XXX validate the min clocks required for display */ diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h index 4069be89e585..84999242c747 100644 --- a/drivers/gpu/drm/radeon/sid.h +++ b/drivers/gpu/drm/radeon/sid.h @@ -1632,6 +1632,23 @@ #define PACKET3_MPEG_INDEX 0x3A #define PACKET3_COPY_DW 0x3B #define PACKET3_WAIT_REG_MEM 0x3C +#define WAIT_REG_MEM_FUNCTION(x) ((x) << 0) + /* 0 - always + * 1 - < + * 2 - <= + * 3 - == + * 4 - != + * 5 - >= + * 6 - > + */ +#define WAIT_REG_MEM_MEM_SPACE(x) ((x) << 4) + /* 0 - reg + * 1 - mem + */ +#define WAIT_REG_MEM_ENGINE(x) ((x) << 8) + /* 0 - me + * 1 - pfp + */ #define PACKET3_MEM_WRITE 0x3D #define PACKET3_COPY_DATA 0x40 #define PACKET3_CP_DMA 0x41 @@ -1835,6 +1852,7 @@ #define DMA_PACKET_TRAP 0x7 #define DMA_PACKET_SRBM_WRITE 0x9 #define DMA_PACKET_CONSTANT_FILL 0xd +#define DMA_PACKET_POLL_REG_MEM 0xe #define DMA_PACKET_NOP 0xf #define VCE_STATUS 0x20004 diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 7b5d22110f25..6c6b655defcf 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -406,11 +406,9 @@ int vmw_3d_resource_inc(struct vmw_private *dev_priv, if (unlikely(ret != 0)) --dev_priv->num_3d_resources; } else if (unhide_svga) { - mutex_lock(&dev_priv->hw_mutex); vmw_write(dev_priv, SVGA_REG_ENABLE, vmw_read(dev_priv, SVGA_REG_ENABLE) & ~SVGA_REG_ENABLE_HIDE); - mutex_unlock(&dev_priv->hw_mutex); } mutex_unlock(&dev_priv->release_mutex); @@ -433,13 +431,10 @@ void vmw_3d_resource_dec(struct vmw_private *dev_priv, mutex_lock(&dev_priv->release_mutex); if (unlikely(--dev_priv->num_3d_resources == 0)) vmw_release_device(dev_priv); - else if (hide_svga) { - mutex_lock(&dev_priv->hw_mutex); + else if (hide_svga) vmw_write(dev_priv, SVGA_REG_ENABLE, vmw_read(dev_priv, SVGA_REG_ENABLE) | SVGA_REG_ENABLE_HIDE); - mutex_unlock(&dev_priv->hw_mutex); - } n3d = (int32_t) dev_priv->num_3d_resources; mutex_unlock(&dev_priv->release_mutex); @@ -600,12 +595,14 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) dev_priv->dev = dev; dev_priv->vmw_chipset = chipset; dev_priv->last_read_seqno = (uint32_t) -100; - mutex_init(&dev_priv->hw_mutex); mutex_init(&dev_priv->cmdbuf_mutex); mutex_init(&dev_priv->release_mutex); mutex_init(&dev_priv->binding_mutex); rwlock_init(&dev_priv->resource_lock); ttm_lock_init(&dev_priv->reservation_sem); + spin_lock_init(&dev_priv->hw_lock); + spin_lock_init(&dev_priv->waiter_lock); + spin_lock_init(&dev_priv->cap_lock); for (i = vmw_res_context; i < vmw_res_max; ++i) { idr_init(&dev_priv->res_idr[i]); @@ -626,14 +623,11 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) dev_priv->enable_fb = enable_fbdev; - mutex_lock(&dev_priv->hw_mutex); - vmw_write(dev_priv, SVGA_REG_ID, SVGA_ID_2); svga_id = vmw_read(dev_priv, SVGA_REG_ID); if (svga_id != SVGA_ID_2) { ret = -ENOSYS; DRM_ERROR("Unsupported SVGA ID 0x%x\n", svga_id); - mutex_unlock(&dev_priv->hw_mutex); goto out_err0; } @@ -683,10 +677,8 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) dev_priv->prim_bb_mem = dev_priv->vram_size; ret = vmw_dma_masks(dev_priv); - if (unlikely(ret != 0)) { - mutex_unlock(&dev_priv->hw_mutex); + if (unlikely(ret != 0)) goto out_err0; - } /* * Limit back buffer size to VRAM size. Remove this once @@ -695,8 +687,6 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) if (dev_priv->prim_bb_mem > dev_priv->vram_size) dev_priv->prim_bb_mem = dev_priv->vram_size; - mutex_unlock(&dev_priv->hw_mutex); - vmw_print_capabilities(dev_priv->capabilities); if (dev_priv->capabilities & SVGA_CAP_GMR2) { @@ -1160,9 +1150,7 @@ static int vmw_master_set(struct drm_device *dev, if (unlikely(ret != 0)) return ret; vmw_kms_save_vga(dev_priv); - mutex_lock(&dev_priv->hw_mutex); vmw_write(dev_priv, SVGA_REG_TRACES, 0); - mutex_unlock(&dev_priv->hw_mutex); } if (active) { @@ -1196,9 +1184,7 @@ out_no_active_lock: if (!dev_priv->enable_fb) { vmw_kms_restore_vga(dev_priv); vmw_3d_resource_dec(dev_priv, true); - mutex_lock(&dev_priv->hw_mutex); vmw_write(dev_priv, SVGA_REG_TRACES, 1); - mutex_unlock(&dev_priv->hw_mutex); } return ret; } @@ -1233,9 +1219,7 @@ static void vmw_master_drop(struct drm_device *dev, DRM_ERROR("Unable to clean VRAM on master drop.\n"); vmw_kms_restore_vga(dev_priv); vmw_3d_resource_dec(dev_priv, true); - mutex_lock(&dev_priv->hw_mutex); vmw_write(dev_priv, SVGA_REG_TRACES, 1); - mutex_unlock(&dev_priv->hw_mutex); } dev_priv->active_master = &dev_priv->fbdev_master; @@ -1367,10 +1351,8 @@ static void vmw_pm_complete(struct device *kdev) struct drm_device *dev = pci_get_drvdata(pdev); struct vmw_private *dev_priv = vmw_priv(dev); - mutex_lock(&dev_priv->hw_mutex); vmw_write(dev_priv, SVGA_REG_ID, SVGA_ID_2); (void) vmw_read(dev_priv, SVGA_REG_ID); - mutex_unlock(&dev_priv->hw_mutex); /** * Reclaim 3d reference held by fbdev and potentially diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 4ee799b43d5d..d26a6daa9719 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -399,7 +399,8 @@ struct vmw_private { uint32_t memory_size; bool has_gmr; bool has_mob; - struct mutex hw_mutex; + spinlock_t hw_lock; + spinlock_t cap_lock; /* * VGA registers. @@ -449,8 +450,9 @@ struct vmw_private { atomic_t marker_seq; wait_queue_head_t fence_queue; wait_queue_head_t fifo_queue; - int fence_queue_waiters; /* Protected by hw_mutex */ - int goal_queue_waiters; /* Protected by hw_mutex */ + spinlock_t waiter_lock; + int fence_queue_waiters; /* Protected by waiter_lock */ + int goal_queue_waiters; /* Protected by waiter_lock */ atomic_t fifo_queue_waiters; uint32_t last_read_seqno; spinlock_t irq_lock; @@ -553,20 +555,35 @@ static inline struct vmw_master *vmw_master(struct drm_master *master) return (struct vmw_master *) master->driver_priv; } +/* + * The locking here is fine-grained, so that it is performed once + * for every read- and write operation. This is of course costly, but we + * don't perform much register access in the timing critical paths anyway. + * Instead we have the extra benefit of being sure that we don't forget + * the hw lock around register accesses. + */ static inline void vmw_write(struct vmw_private *dev_priv, unsigned int offset, uint32_t value) { + unsigned long irq_flags; + + spin_lock_irqsave(&dev_priv->hw_lock, irq_flags); outl(offset, dev_priv->io_start + VMWGFX_INDEX_PORT); outl(value, dev_priv->io_start + VMWGFX_VALUE_PORT); + spin_unlock_irqrestore(&dev_priv->hw_lock, irq_flags); } static inline uint32_t vmw_read(struct vmw_private *dev_priv, unsigned int offset) { - uint32_t val; + unsigned long irq_flags; + u32 val; + spin_lock_irqsave(&dev_priv->hw_lock, irq_flags); outl(offset, dev_priv->io_start + VMWGFX_INDEX_PORT); val = inl(dev_priv->io_start + VMWGFX_VALUE_PORT); + spin_unlock_irqrestore(&dev_priv->hw_lock, irq_flags); + return val; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c index b7594cb758af..945f1e0dad92 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c @@ -35,7 +35,7 @@ struct vmw_fence_manager { struct vmw_private *dev_priv; spinlock_t lock; struct list_head fence_list; - struct work_struct work, ping_work; + struct work_struct work; u32 user_fence_size; u32 fence_size; u32 event_fence_action_size; @@ -134,14 +134,6 @@ static const char *vmw_fence_get_timeline_name(struct fence *f) return "svga"; } -static void vmw_fence_ping_func(struct work_struct *work) -{ - struct vmw_fence_manager *fman = - container_of(work, struct vmw_fence_manager, ping_work); - - vmw_fifo_ping_host(fman->dev_priv, SVGA_SYNC_GENERIC); -} - static bool vmw_fence_enable_signaling(struct fence *f) { struct vmw_fence_obj *fence = @@ -155,11 +147,7 @@ static bool vmw_fence_enable_signaling(struct fence *f) if (seqno - fence->base.seqno < VMW_FENCE_WRAP) return false; - if (mutex_trylock(&dev_priv->hw_mutex)) { - vmw_fifo_ping_host_locked(dev_priv, SVGA_SYNC_GENERIC); - mutex_unlock(&dev_priv->hw_mutex); - } else - schedule_work(&fman->ping_work); + vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC); return true; } @@ -305,7 +293,6 @@ struct vmw_fence_manager *vmw_fence_manager_init(struct vmw_private *dev_priv) INIT_LIST_HEAD(&fman->fence_list); INIT_LIST_HEAD(&fman->cleanup_list); INIT_WORK(&fman->work, &vmw_fence_work_func); - INIT_WORK(&fman->ping_work, &vmw_fence_ping_func); fman->fifo_down = true; fman->user_fence_size = ttm_round_pot(sizeof(struct vmw_user_fence)); fman->fence_size = ttm_round_pot(sizeof(struct vmw_fence_obj)); @@ -323,7 +310,6 @@ void vmw_fence_manager_takedown(struct vmw_fence_manager *fman) bool lists_empty; (void) cancel_work_sync(&fman->work); - (void) cancel_work_sync(&fman->ping_work); spin_lock_irqsave(&fman->lock, irq_flags); lists_empty = list_empty(&fman->fence_list) && diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c index 09e10aefcd8e..39f2b03888e7 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c @@ -44,10 +44,10 @@ bool vmw_fifo_have_3d(struct vmw_private *dev_priv) if (!dev_priv->has_mob) return false; - mutex_lock(&dev_priv->hw_mutex); + spin_lock(&dev_priv->cap_lock); vmw_write(dev_priv, SVGA_REG_DEV_CAP, SVGA3D_DEVCAP_3D); result = vmw_read(dev_priv, SVGA_REG_DEV_CAP); - mutex_unlock(&dev_priv->hw_mutex); + spin_unlock(&dev_priv->cap_lock); return (result != 0); } @@ -120,7 +120,6 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) DRM_INFO("height %d\n", vmw_read(dev_priv, SVGA_REG_HEIGHT)); DRM_INFO("bpp %d\n", vmw_read(dev_priv, SVGA_REG_BITS_PER_PIXEL)); - mutex_lock(&dev_priv->hw_mutex); dev_priv->enable_state = vmw_read(dev_priv, SVGA_REG_ENABLE); dev_priv->config_done_state = vmw_read(dev_priv, SVGA_REG_CONFIG_DONE); dev_priv->traces_state = vmw_read(dev_priv, SVGA_REG_TRACES); @@ -143,7 +142,6 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) mb(); vmw_write(dev_priv, SVGA_REG_CONFIG_DONE, 1); - mutex_unlock(&dev_priv->hw_mutex); max = ioread32(fifo_mem + SVGA_FIFO_MAX); min = ioread32(fifo_mem + SVGA_FIFO_MIN); @@ -160,31 +158,28 @@ int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) return vmw_fifo_send_fence(dev_priv, &dummy); } -void vmw_fifo_ping_host_locked(struct vmw_private *dev_priv, uint32_t reason) +void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason) { __le32 __iomem *fifo_mem = dev_priv->mmio_virt; + static DEFINE_SPINLOCK(ping_lock); + unsigned long irq_flags; + /* + * The ping_lock is needed because we don't have an atomic + * test-and-set of the SVGA_FIFO_BUSY register. + */ + spin_lock_irqsave(&ping_lock, irq_flags); if (unlikely(ioread32(fifo_mem + SVGA_FIFO_BUSY) == 0)) { iowrite32(1, fifo_mem + SVGA_FIFO_BUSY); vmw_write(dev_priv, SVGA_REG_SYNC, reason); } -} - -void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason) -{ - mutex_lock(&dev_priv->hw_mutex); - - vmw_fifo_ping_host_locked(dev_priv, reason); - - mutex_unlock(&dev_priv->hw_mutex); + spin_unlock_irqrestore(&ping_lock, irq_flags); } void vmw_fifo_release(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) { __le32 __iomem *fifo_mem = dev_priv->mmio_virt; - mutex_lock(&dev_priv->hw_mutex); - vmw_write(dev_priv, SVGA_REG_SYNC, SVGA_SYNC_GENERIC); while (vmw_read(dev_priv, SVGA_REG_BUSY) != 0) ; @@ -198,7 +193,6 @@ void vmw_fifo_release(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo) vmw_write(dev_priv, SVGA_REG_TRACES, dev_priv->traces_state); - mutex_unlock(&dev_priv->hw_mutex); vmw_marker_queue_takedown(&fifo->marker_queue); if (likely(fifo->static_buffer != NULL)) { @@ -271,7 +265,7 @@ static int vmw_fifo_wait(struct vmw_private *dev_priv, return vmw_fifo_wait_noirq(dev_priv, bytes, interruptible, timeout); - mutex_lock(&dev_priv->hw_mutex); + spin_lock(&dev_priv->waiter_lock); if (atomic_add_return(1, &dev_priv->fifo_queue_waiters) > 0) { spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); outl(SVGA_IRQFLAG_FIFO_PROGRESS, @@ -280,7 +274,7 @@ static int vmw_fifo_wait(struct vmw_private *dev_priv, vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); } - mutex_unlock(&dev_priv->hw_mutex); + spin_unlock(&dev_priv->waiter_lock); if (interruptible) ret = wait_event_interruptible_timeout @@ -296,14 +290,14 @@ static int vmw_fifo_wait(struct vmw_private *dev_priv, else if (likely(ret > 0)) ret = 0; - mutex_lock(&dev_priv->hw_mutex); + spin_lock(&dev_priv->waiter_lock); if (atomic_dec_and_test(&dev_priv->fifo_queue_waiters)) { spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); dev_priv->irq_mask &= ~SVGA_IRQFLAG_FIFO_PROGRESS; vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); } - mutex_unlock(&dev_priv->hw_mutex); + spin_unlock(&dev_priv->waiter_lock); return ret; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index 37881ecf5d7a..69c8ce23123c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -135,13 +135,13 @@ static int vmw_fill_compat_cap(struct vmw_private *dev_priv, void *bounce, (pair_offset + max_size * sizeof(SVGA3dCapPair)) / sizeof(u32); compat_cap->header.type = SVGA3DCAPS_RECORD_DEVCAPS; - mutex_lock(&dev_priv->hw_mutex); + spin_lock(&dev_priv->cap_lock); for (i = 0; i < max_size; ++i) { vmw_write(dev_priv, SVGA_REG_DEV_CAP, i); compat_cap->pairs[i][0] = i; compat_cap->pairs[i][1] = vmw_read(dev_priv, SVGA_REG_DEV_CAP); } - mutex_unlock(&dev_priv->hw_mutex); + spin_unlock(&dev_priv->cap_lock); return 0; } @@ -191,12 +191,12 @@ int vmw_get_cap_3d_ioctl(struct drm_device *dev, void *data, if (num > SVGA3D_DEVCAP_MAX) num = SVGA3D_DEVCAP_MAX; - mutex_lock(&dev_priv->hw_mutex); + spin_lock(&dev_priv->cap_lock); for (i = 0; i < num; ++i) { vmw_write(dev_priv, SVGA_REG_DEV_CAP, i); *bounce32++ = vmw_read(dev_priv, SVGA_REG_DEV_CAP); } - mutex_unlock(&dev_priv->hw_mutex); + spin_unlock(&dev_priv->cap_lock); } else if (gb_objects) { ret = vmw_fill_compat_cap(dev_priv, bounce, size); if (unlikely(ret != 0)) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c index 0c423766c441..9fe9827ee499 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c @@ -62,13 +62,8 @@ irqreturn_t vmw_irq_handler(int irq, void *arg) static bool vmw_fifo_idle(struct vmw_private *dev_priv, uint32_t seqno) { - uint32_t busy; - mutex_lock(&dev_priv->hw_mutex); - busy = vmw_read(dev_priv, SVGA_REG_BUSY); - mutex_unlock(&dev_priv->hw_mutex); - - return (busy == 0); + return (vmw_read(dev_priv, SVGA_REG_BUSY) == 0); } void vmw_update_seqno(struct vmw_private *dev_priv, @@ -184,7 +179,7 @@ int vmw_fallback_wait(struct vmw_private *dev_priv, void vmw_seqno_waiter_add(struct vmw_private *dev_priv) { - mutex_lock(&dev_priv->hw_mutex); + spin_lock(&dev_priv->waiter_lock); if (dev_priv->fence_queue_waiters++ == 0) { unsigned long irq_flags; @@ -195,12 +190,12 @@ void vmw_seqno_waiter_add(struct vmw_private *dev_priv) vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); } - mutex_unlock(&dev_priv->hw_mutex); + spin_unlock(&dev_priv->waiter_lock); } void vmw_seqno_waiter_remove(struct vmw_private *dev_priv) { - mutex_lock(&dev_priv->hw_mutex); + spin_lock(&dev_priv->waiter_lock); if (--dev_priv->fence_queue_waiters == 0) { unsigned long irq_flags; @@ -209,13 +204,13 @@ void vmw_seqno_waiter_remove(struct vmw_private *dev_priv) vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); } - mutex_unlock(&dev_priv->hw_mutex); + spin_unlock(&dev_priv->waiter_lock); } void vmw_goal_waiter_add(struct vmw_private *dev_priv) { - mutex_lock(&dev_priv->hw_mutex); + spin_lock(&dev_priv->waiter_lock); if (dev_priv->goal_queue_waiters++ == 0) { unsigned long irq_flags; @@ -226,12 +221,12 @@ void vmw_goal_waiter_add(struct vmw_private *dev_priv) vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); } - mutex_unlock(&dev_priv->hw_mutex); + spin_unlock(&dev_priv->waiter_lock); } void vmw_goal_waiter_remove(struct vmw_private *dev_priv) { - mutex_lock(&dev_priv->hw_mutex); + spin_lock(&dev_priv->waiter_lock); if (--dev_priv->goal_queue_waiters == 0) { unsigned long irq_flags; @@ -240,7 +235,7 @@ void vmw_goal_waiter_remove(struct vmw_private *dev_priv) vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); } - mutex_unlock(&dev_priv->hw_mutex); + spin_unlock(&dev_priv->waiter_lock); } int vmw_wait_seqno(struct vmw_private *dev_priv, @@ -315,9 +310,7 @@ void vmw_irq_uninstall(struct drm_device *dev) if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK)) return; - mutex_lock(&dev_priv->hw_mutex); vmw_write(dev_priv, SVGA_REG_IRQMASK, 0); - mutex_unlock(&dev_priv->hw_mutex); status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 3725b521d931..8725b79e7847 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1828,9 +1828,7 @@ vmw_du_connector_detect(struct drm_connector *connector, bool force) struct vmw_private *dev_priv = vmw_priv(dev); struct vmw_display_unit *du = vmw_connector_to_du(connector); - mutex_lock(&dev_priv->hw_mutex); num_displays = vmw_read(dev_priv, SVGA_REG_NUM_DISPLAYS); - mutex_unlock(&dev_priv->hw_mutex); return ((vmw_connector_to_du(connector)->unit < num_displays && du->pref_active) ? diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 230b6f887cd8..152b006833cd 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -27,7 +27,8 @@ if HID config HID_BATTERY_STRENGTH bool "Battery level reporting for HID devices" - depends on HID && POWER_SUPPLY && HID = POWER_SUPPLY + depends on HID + select POWER_SUPPLY default n ---help--- This option adds support of reporting battery strength (for HID devices @@ -146,6 +147,16 @@ config HID_BELKIN ---help--- Support for Belkin Flip KVM and Wireless keyboard. +config HID_BETOP_FF + tristate "Betop Production Inc. force feedback support" + depends on USB_HID + select INPUT_FF_MEMLESS + ---help--- + Say Y here if you want to enable force feedback support for devices by + BETOP Production Ltd. + Currently the following devices are known to be supported: + - BETOP 2185 PC & BFM MODE + config HID_CHERRY tristate "Cherry Cymotion keyboard" if EXPERT depends on HID @@ -388,7 +399,7 @@ config HID_LOGITECH_HIDPP Say Y if you want support for Logitech devices relying on the HID++ specification. Such devices are the various Logitech Touchpads (T650, T651, TK820), some mice (Zone Touch mouse), or even keyboards (Solar - Keayboard). + Keyboard). config LOGITECH_FF bool "Logitech force feedback support" diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index debd15b44b59..6f19958dfc38 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -2,10 +2,7 @@ # Makefile for the HID driver # hid-y := hid-core.o hid-input.o - -ifdef CONFIG_DEBUG_FS - hid-objs += hid-debug.o -endif +hid-$(CONFIG_DEBUG_FS) += hid-debug.o obj-$(CONFIG_HID) += hid.o obj-$(CONFIG_UHID) += uhid.o @@ -15,23 +12,13 @@ obj-$(CONFIG_HID_GENERIC) += hid-generic.o hid-$(CONFIG_HIDRAW) += hidraw.o hid-logitech-y := hid-lg.o -ifdef CONFIG_LOGITECH_FF - hid-logitech-y += hid-lgff.o -endif -ifdef CONFIG_LOGIRUMBLEPAD2_FF - hid-logitech-y += hid-lg2ff.o -endif -ifdef CONFIG_LOGIG940_FF - hid-logitech-y += hid-lg3ff.o -endif -ifdef CONFIG_LOGIWHEELS_FF - hid-logitech-y += hid-lg4ff.o -endif +hid-logitech-$(CONFIG_LOGITECH_FF) += hid-lgff.o +hid-logitech-$(CONFIG_LOGIRUMBLEPAD2_FF) += hid-lg2ff.o +hid-logitech-$(CONFIG_LOGIG940_FF) += hid-lg3ff.o +hid-logitech-$(CONFIG_LOGIWHEELS_FF) += hid-lg4ff.o hid-wiimote-y := hid-wiimote-core.o hid-wiimote-modules.o -ifdef CONFIG_DEBUG_FS - hid-wiimote-y += hid-wiimote-debug.o -endif +hid-wiimote-$(CONFIG_DEBUG_FS) += hid-wiimote-debug.o obj-$(CONFIG_HID_A4TECH) += hid-a4tech.o obj-$(CONFIG_HID_ACRUX) += hid-axff.o @@ -39,6 +26,7 @@ obj-$(CONFIG_HID_APPLE) += hid-apple.o obj-$(CONFIG_HID_APPLEIR) += hid-appleir.o obj-$(CONFIG_HID_AUREAL) += hid-aureal.o obj-$(CONFIG_HID_BELKIN) += hid-belkin.o +obj-$(CONFIG_HID_BETOP_FF) += hid-betopff.o obj-$(CONFIG_HID_CHERRY) += hid-cherry.o obj-$(CONFIG_HID_CHICONY) += hid-chicony.o obj-$(CONFIG_HID_CP2112) += hid-cp2112.o @@ -76,24 +64,12 @@ obj-$(CONFIG_HID_PENMOUNT) += hid-penmount.o obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o hid-picolcd-y += hid-picolcd_core.o -ifdef CONFIG_HID_PICOLCD_FB -hid-picolcd-y += hid-picolcd_fb.o -endif -ifdef CONFIG_HID_PICOLCD_BACKLIGHT -hid-picolcd-y += hid-picolcd_backlight.o -endif -ifdef CONFIG_HID_PICOLCD_LCD -hid-picolcd-y += hid-picolcd_lcd.o -endif -ifdef CONFIG_HID_PICOLCD_LEDS -hid-picolcd-y += hid-picolcd_leds.o -endif -ifdef CONFIG_HID_PICOLCD_CIR -hid-picolcd-y += hid-picolcd_cir.o -endif -ifdef CONFIG_DEBUG_FS -hid-picolcd-y += hid-picolcd_debugfs.o -endif +hid-picolcd-$(CONFIG_HID_PICOLCD_FB) += hid-picolcd_fb.o +hid-picolcd-$(CONFIG_HID_PICOLCD_BACKLIGHT) += hid-picolcd_backlight.o +hid-picolcd-$(CONFIG_HID_PICOLCD_LCD) += hid-picolcd_lcd.o +hid-picolcd-$(CONFIG_HID_PICOLCD_LEDS) += hid-picolcd_leds.o +hid-picolcd-$(CONFIG_HID_PICOLCD_CIR) += hid-picolcd_cir.o +hid-picolcd-$(CONFIG_DEBUG_FS) += hid-picolcd_debugfs.o obj-$(CONFIG_HID_PLANTRONICS) += hid-plantronics.o obj-$(CONFIG_HID_PRIMAX) += hid-primax.o diff --git a/drivers/hid/hid-betopff.c b/drivers/hid/hid-betopff.c new file mode 100644 index 000000000000..69cfc8dc6af1 --- /dev/null +++ b/drivers/hid/hid-betopff.c @@ -0,0 +1,160 @@ +/* + * Force feedback support for Betop based devices + * + * The devices are distributed under various names and the same USB device ID + * can be used in both adapters and actual game controllers. + * + * 0x11c2:0x2208 "BTP2185 BFM mode Joystick" + * - tested with BTP2185 BFM Mode. + * + * 0x11C0:0x5506 "BTP2185 PC mode Joystick" + * - tested with BTP2185 PC Mode. + * + * 0x8380:0x1850 "BTP2185 V2 PC mode USB Gamepad" + * - tested with BTP2185 PC Mode with another version. + * + * 0x20bc:0x5500 "BTP2185 V2 BFM mode Joystick" + * - tested with BTP2171s. + * Copyright (c) 2014 Huang Bo <huangbobupt@163.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/input.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/hid.h> + +#include "hid-ids.h" + +struct betopff_device { + struct hid_report *report; +}; + +static int hid_betopff_play(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct betopff_device *betopff = data; + __u16 left, right; + + left = effect->u.rumble.strong_magnitude; + right = effect->u.rumble.weak_magnitude; + + betopff->report->field[2]->value[0] = left / 256; + betopff->report->field[3]->value[0] = right / 256; + + hid_hw_request(hid, betopff->report, HID_REQ_SET_REPORT); + + return 0; +} + +static int betopff_init(struct hid_device *hid) +{ + struct betopff_device *betopff; + struct hid_report *report; + struct hid_input *hidinput = + list_first_entry(&hid->inputs, struct hid_input, list); + struct list_head *report_list = + &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct input_dev *dev = hidinput->input; + int field_count = 0; + int error; + int i, j; + + if (list_empty(report_list)) { + hid_err(hid, "no output reports found\n"); + return -ENODEV; + } + + report = list_first_entry(report_list, struct hid_report, list); + /* + * Actually there are 4 fields for 4 Bytes as below: + * ----------------------------------------- + * Byte0 Byte1 Byte2 Byte3 + * 0x00 0x00 left_motor right_motor + * ----------------------------------------- + * Do init them with default value. + */ + for (i = 0; i < report->maxfield; i++) { + for (j = 0; j < report->field[i]->report_count; j++) { + report->field[i]->value[j] = 0x00; + field_count++; + } + } + + if (field_count < 4) { + hid_err(hid, "not enough fields in the report: %d\n", + field_count); + return -ENODEV; + } + + betopff = kzalloc(sizeof(*betopff), GFP_KERNEL); + if (!betopff) + return -ENOMEM; + + set_bit(FF_RUMBLE, dev->ffbit); + + error = input_ff_create_memless(dev, betopff, hid_betopff_play); + if (error) { + kfree(betopff); + return error; + } + + betopff->report = report; + hid_hw_request(hid, betopff->report, HID_REQ_SET_REPORT); + + hid_info(hid, "Force feedback for betop devices by huangbo <huangbobupt@163.com>\n"); + + return 0; +} + +static int betop_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + + if (id->driver_data) + hdev->quirks |= HID_QUIRK_MULTI_INPUT; + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "parse failed\n"); + goto err; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); + if (ret) { + hid_err(hdev, "hw start failed\n"); + goto err; + } + + betopff_init(hdev); + + return 0; +err: + return ret; +} + +static const struct hid_device_id betop_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185BFM, 0x2208) }, + { HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185PC, 0x5506) }, + { HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185V2PC, 0x1850) }, + { HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185V2BFM, 0x5500) }, + { } +}; +MODULE_DEVICE_TABLE(hid, betop_devices); + +static struct hid_driver betop_driver = { + .name = "betop", + .id_table = betop_devices, + .probe = betop_probe, +}; +module_hid_driver(betop_driver); + +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index c3d0ac1a0988..db4fb6e1cc5b 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -698,15 +698,25 @@ static void hid_scan_feature_usage(struct hid_parser *parser, u32 usage) static void hid_scan_collection(struct hid_parser *parser, unsigned type) { struct hid_device *hid = parser->device; + int i; if (((parser->global.usage_page << 16) == HID_UP_SENSOR) && type == HID_COLLECTION_PHYSICAL) hid->group = HID_GROUP_SENSOR_HUB; if (hid->vendor == USB_VENDOR_ID_MICROSOFT && - hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 && + (hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3 || + hid->product == USB_DEVICE_ID_MS_TYPE_COVER_3_JP) && hid->group == HID_GROUP_MULTITOUCH) hid->group = HID_GROUP_GENERIC; + + if ((parser->global.usage_page << 16) == HID_UP_GENDESK) + for (i = 0; i < parser->local.usage_index; i++) + if (parser->local.usage[i] == HID_GD_POINTER) + parser->scan_flags |= HID_SCAN_FLAG_GD_POINTER; + + if ((parser->global.usage_page << 16) >= HID_UP_MSVENDOR) + parser->scan_flags |= HID_SCAN_FLAG_VENDOR_SPECIFIC; } static int hid_scan_main(struct hid_parser *parser, struct hid_item *item) @@ -792,11 +802,14 @@ static int hid_scan_report(struct hid_device *hid) hid->group = HID_GROUP_WACOM; break; case USB_VENDOR_ID_SYNAPTICS: - if ((hid->group == HID_GROUP_GENERIC) && - (hid->bus != BUS_USB || hid->type == HID_TYPE_USBMOUSE)) - /* hid-rmi should only bind to the mouse interface of - * composite USB devices */ - hid->group = HID_GROUP_RMI; + if (hid->group == HID_GROUP_GENERIC) + if ((parser->scan_flags & HID_SCAN_FLAG_VENDOR_SPECIFIC) + && (parser->scan_flags & HID_SCAN_FLAG_GD_POINTER)) + /* + * hid-rmi should take care of them, + * not hid-generic + */ + hid->group = HID_GROUP_RMI; break; } @@ -1757,6 +1770,10 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY) }, { HID_USB_DEVICE(USB_VENDOR_ID_AUREAL, USB_DEVICE_ID_AUREAL_W01RN) }, { HID_USB_DEVICE(USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM) }, + { HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185BFM, 0x2208) }, + { HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185PC, 0x5506) }, + { HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185V2PC, 0x1850) }, + { HID_USB_DEVICE(USB_VENDOR_ID_BETOP_2185V2BFM, 0x5500) }, { HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_BTC, USB_DEVICE_ID_BTC_EMPREX_REMOTE_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION) }, @@ -1805,6 +1822,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_I405X) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) }, @@ -1860,6 +1878,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3_JP) }, { HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) }, { HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) }, @@ -1970,6 +1989,7 @@ static const struct hid_device_id hid_have_special_driver[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_WIIMOTE2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_RAZER, USB_DEVICE_ID_RAZER_BLADE_14) }, { } }; @@ -2327,6 +2347,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICK16F1454) }, { HID_USB_DEVICE(USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR, USB_DEVICE_ID_N_S_HARMONY) }, { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100) }, { HID_USB_DEVICE(USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20) }, diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c index 31fad641b744..6039f071fab1 100644 --- a/drivers/hid/hid-hyperv.c +++ b/drivers/hid/hid-hyperv.c @@ -381,7 +381,7 @@ static void mousevsc_on_channel_callback(void *context) static int mousevsc_connect_to_vsp(struct hv_device *device) { int ret = 0; - int t; + unsigned long t; struct mousevsc_dev *input_dev = hv_get_drvdata(device); struct mousevsc_prt_msg *request; struct mousevsc_prt_msg *response; diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 7460f3402298..46edb4d3ed28 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -189,6 +189,11 @@ #define USB_VENDOR_ID_BERKSHIRE 0x0c98 #define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140 +#define USB_VENDOR_ID_BETOP_2185BFM 0x11c2 +#define USB_VENDOR_ID_BETOP_2185PC 0x11c0 +#define USB_VENDOR_ID_BETOP_2185V2PC 0x8380 +#define USB_VENDOR_ID_BETOP_2185V2BFM 0x20bc + #define USB_VENDOR_ID_BTC 0x046e #define USB_DEVICE_ID_BTC_EMPREX_REMOTE 0x5578 #define USB_DEVICE_ID_BTC_EMPREX_REMOTE_2 0x5577 @@ -526,6 +531,7 @@ #define USB_DEVICE_ID_KYE_GPEN_560 0x5003 #define USB_DEVICE_ID_KYE_EASYPEN_I405X 0x5010 #define USB_DEVICE_ID_KYE_MOUSEPEN_I608X 0x5011 +#define USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2 0x501a #define USB_DEVICE_ID_KYE_EASYPEN_M610X 0x5013 #define USB_VENDOR_ID_LABTEC 0x1020 @@ -637,6 +643,7 @@ #define USB_DEVICE_ID_PICKIT2 0x0033 #define USB_DEVICE_ID_PICOLCD 0xc002 #define USB_DEVICE_ID_PICOLCD_BOOTLOADER 0xf002 +#define USB_DEVICE_ID_PICK16F1454 0x0042 #define USB_VENDOR_ID_MICROSOFT 0x045e #define USB_DEVICE_ID_SIDEWINDER_GV 0x003b @@ -653,6 +660,7 @@ #define USB_DEVICE_ID_MS_TOUCH_COVER_2 0x07a7 #define USB_DEVICE_ID_MS_TYPE_COVER_2 0x07a9 #define USB_DEVICE_ID_MS_TYPE_COVER_3 0x07dc +#define USB_DEVICE_ID_MS_TYPE_COVER_3_JP 0x07dd #define USB_VENDOR_ID_MOJO 0x8282 #define USB_DEVICE_ID_RETRO_ADAPTER 0x3201 @@ -767,6 +775,9 @@ #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001 0x3001 #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008 0x3008 +#define USB_VENDOR_ID_RAZER 0x1532 +#define USB_DEVICE_ID_RAZER_BLADE_14 0x011D + #define USB_VENDOR_ID_REALTEK 0x0bda #define USB_DEVICE_ID_REALTEK_READER 0x0152 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index e0a0f06ac5ef..052869d0ab78 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -306,10 +306,16 @@ static enum power_supply_property hidinput_battery_props[] = { static const struct hid_device_id hid_battery_quirks[] = { { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, - USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO), - HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, + USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ISO), + HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_ANSI), + HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, + USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI), + HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, - USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ANSI), + USB_DEVICE_ID_APPLE_ALU_WIRELESS_2011_ISO), HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI), @@ -1101,6 +1107,23 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct return; } + /* + * Ignore reports for absolute data if the data didn't change. This is + * not only an optimization but also fixes 'dead' key reports. Some + * RollOver implementations for localized keys (like BACKSLASH/PIPE; HID + * 0x31 and 0x32) report multiple keys, even though a localized keyboard + * can only have one of them physically available. The 'dead' keys + * report constant 0. As all map to the same keycode, they'd confuse + * the input layer. If we filter the 'dead' keys on the HID level, we + * skip the keycode translation and only forward real events. + */ + if (!(field->flags & (HID_MAIN_ITEM_RELATIVE | + HID_MAIN_ITEM_BUFFERED_BYTE)) && + (field->flags & HID_MAIN_ITEM_VARIABLE) && + usage->usage_index < field->maxusage && + value == field->value[usage->usage_index]) + return; + /* report the usage code as scancode if the key status has changed */ if (usage->type == EV_KEY && !!test_bit(usage->code, input->key) != value) input_event(input, EV_MSC, MSC_SCAN, usage->hid); diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c index b92bf01a1ae8..158fcf577fae 100644 --- a/drivers/hid/hid-kye.c +++ b/drivers/hid/hid-kye.c @@ -323,6 +323,7 @@ static __u8 *kye_report_fixup(struct hid_device *hdev, __u8 *rdesc, } break; case USB_DEVICE_ID_KYE_MOUSEPEN_I608X: + case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2: if (*rsize == MOUSEPEN_I608X_RDESC_ORIG_SIZE) { rdesc = mousepen_i608x_rdesc_fixed; *rsize = sizeof(mousepen_i608x_rdesc_fixed); @@ -415,6 +416,7 @@ static int kye_probe(struct hid_device *hdev, const struct hid_device_id *id) switch (id->product) { case USB_DEVICE_ID_KYE_EASYPEN_I405X: case USB_DEVICE_ID_KYE_MOUSEPEN_I608X: + case USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2: case USB_DEVICE_ID_KYE_EASYPEN_M610X: ret = kye_tablet_enable(hdev); if (ret) { @@ -446,6 +448,8 @@ static const struct hid_device_id kye_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, + USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) }, { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_GENIUS_GILA_GAMING_MOUSE) }, diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c index 4c55f4d95798..c4c3f0952521 100644 --- a/drivers/hid/hid-lenovo.c +++ b/drivers/hid/hid-lenovo.c @@ -38,6 +38,7 @@ struct lenovo_drvdata_tpkbd { struct lenovo_drvdata_cptkbd { bool fn_lock; + int sensitivity; }; #define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c)) @@ -91,6 +92,38 @@ static int lenovo_input_mapping_cptkbd(struct hid_device *hdev, case 0x00fa: /* Fn-Esc: Fn-lock toggle */ map_key_clear(KEY_FN_ESC); return 1; + case 0x00fb: /* Middle mouse button (in native mode) */ + map_key_clear(BTN_MIDDLE); + return 1; + } + } + + /* Compatibility middle/wheel mappings should be ignored */ + if (usage->hid == HID_GD_WHEEL) + return -1; + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON && + (usage->hid & HID_USAGE) == 0x003) + return -1; + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_CONSUMER && + (usage->hid & HID_USAGE) == 0x238) + return -1; + + /* Map wheel emulation reports: 0xffa1 = USB, 0xff10 = BT */ + if ((usage->hid & HID_USAGE_PAGE) == 0xff100000 || + (usage->hid & HID_USAGE_PAGE) == 0xffa10000) { + field->flags |= HID_MAIN_ITEM_RELATIVE | HID_MAIN_ITEM_VARIABLE; + field->logical_minimum = -127; + field->logical_maximum = 127; + + switch (usage->hid & HID_USAGE) { + case 0x0000: + hid_map_usage(hi, usage, bit, max, EV_REL, 0x06); + return 1; + case 0x0001: + hid_map_usage(hi, usage, bit, max, EV_REL, 0x08); + return 1; + default: + return -1; } } @@ -145,6 +178,7 @@ static void lenovo_features_set_cptkbd(struct hid_device *hdev) struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); ret = lenovo_send_cmd_cptkbd(hdev, 0x05, cptkbd_data->fn_lock); + ret = lenovo_send_cmd_cptkbd(hdev, 0x02, cptkbd_data->sensitivity); if (ret) hid_err(hdev, "Fn-lock setting failed: %d\n", ret); } @@ -179,13 +213,50 @@ static ssize_t attr_fn_lock_store_cptkbd(struct device *dev, return count; } +static ssize_t attr_sensitivity_show_cptkbd(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); + + return snprintf(buf, PAGE_SIZE, "%u\n", + cptkbd_data->sensitivity); +} + +static ssize_t attr_sensitivity_store_cptkbd(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct hid_device *hdev = container_of(dev, struct hid_device, dev); + struct lenovo_drvdata_cptkbd *cptkbd_data = hid_get_drvdata(hdev); + int value; + + if (kstrtoint(buf, 10, &value) || value < 1 || value > 255) + return -EINVAL; + + cptkbd_data->sensitivity = value; + lenovo_features_set_cptkbd(hdev); + + return count; +} + + static struct device_attribute dev_attr_fn_lock_cptkbd = __ATTR(fn_lock, S_IWUSR | S_IRUGO, attr_fn_lock_show_cptkbd, attr_fn_lock_store_cptkbd); +static struct device_attribute dev_attr_sensitivity_cptkbd = + __ATTR(sensitivity, S_IWUSR | S_IRUGO, + attr_sensitivity_show_cptkbd, + attr_sensitivity_store_cptkbd); + + static struct attribute *lenovo_attributes_cptkbd[] = { &dev_attr_fn_lock_cptkbd.attr, + &dev_attr_sensitivity_cptkbd.attr, NULL }; @@ -594,8 +665,14 @@ static int lenovo_probe_cptkbd(struct hid_device *hdev) if (ret) hid_warn(hdev, "Failed to switch F7/9/11 mode: %d\n", ret); - /* Turn Fn-Lock on by default */ + /* Switch middle button to native mode */ + ret = lenovo_send_cmd_cptkbd(hdev, 0x09, 0x01); + if (ret) + hid_warn(hdev, "Failed to switch middle button: %d\n", ret); + + /* Set keyboard settings to known state */ cptkbd_data->fn_lock = true; + cptkbd_data->sensitivity = 0x05; lenovo_features_set_cptkbd(hdev); ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_cptkbd); diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c index 7835717bc020..db0dd9b17e53 100644 --- a/drivers/hid/hid-lg4ff.c +++ b/drivers/hid/hid-lg4ff.c @@ -49,10 +49,6 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range); static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range); -static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf); -static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); - -static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IROTH, lg4ff_range_show, lg4ff_range_store); struct lg4ff_device_entry { __u32 product_id; @@ -416,7 +412,8 @@ static void hid_lg4ff_switch_native(struct hid_device *hid, const struct lg4ff_n } /* Read current range and display it in terminal */ -static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *attr, char *buf) +static ssize_t range_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct hid_device *hid = to_hid_device(dev); struct lg4ff_device_entry *entry; @@ -441,7 +438,8 @@ static ssize_t lg4ff_range_show(struct device *dev, struct device_attribute *att /* Set range to user specified value, call appropriate function * according to the type of the wheel */ -static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static ssize_t range_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct hid_device *hid = to_hid_device(dev); struct lg4ff_device_entry *entry; @@ -472,6 +470,7 @@ static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *at return count; } +static DEVICE_ATTR_RW(range); #ifdef CONFIG_LEDS_CLASS static void lg4ff_set_leds(struct hid_device *hid, __u8 leds) diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c index c917ab61aafa..5bc6d80d5be7 100644 --- a/drivers/hid/hid-logitech-dj.c +++ b/drivers/hid/hid-logitech-dj.c @@ -962,10 +962,24 @@ static int logi_dj_raw_event(struct hid_device *hdev, switch (data[0]) { case REPORT_ID_DJ_SHORT: + if (size != DJREPORT_SHORT_LENGTH) { + dev_err(&hdev->dev, "DJ report of bad size (%d)", size); + return false; + } return logi_dj_dj_event(hdev, report, data, size); case REPORT_ID_HIDPP_SHORT: - /* intentional fallthrough */ + if (size != HIDPP_REPORT_SHORT_LENGTH) { + dev_err(&hdev->dev, + "Short HID++ report of bad size (%d)", size); + return false; + } + return logi_dj_hidpp_event(hdev, report, data, size); case REPORT_ID_HIDPP_LONG: + if (size != HIDPP_REPORT_LONG_LENGTH) { + dev_err(&hdev->dev, + "Long HID++ report of bad size (%d)", size); + return false; + } return logi_dj_hidpp_event(hdev, report, data, size); } diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 2f420c0b6609..e77658cd037c 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -89,6 +89,7 @@ struct hidpp_device { struct hid_device *hid_dev; struct mutex send_mutex; void *send_receive_buf; + char *name; /* will never be NULL and should not be freed */ wait_queue_head_t wait; bool answer_available; u8 protocol_major; @@ -105,6 +106,7 @@ struct hidpp_device { }; +/* HID++ 1.0 error codes */ #define HIDPP_ERROR 0x8f #define HIDPP_ERROR_SUCCESS 0x00 #define HIDPP_ERROR_INVALID_SUBID 0x01 @@ -119,6 +121,8 @@ struct hidpp_device { #define HIDPP_ERROR_REQUEST_UNAVAILABLE 0x0a #define HIDPP_ERROR_INVALID_PARAM_VALUE 0x0b #define HIDPP_ERROR_WRONG_PIN_CODE 0x0c +/* HID++ 2.0 error codes */ +#define HIDPP20_ERROR 0xff static void hidpp_connect_event(struct hidpp_device *hidpp_dev); @@ -192,9 +196,16 @@ static int hidpp_send_message_sync(struct hidpp_device *hidpp, } if (response->report_id == REPORT_ID_HIDPP_SHORT && - response->fap.feature_index == HIDPP_ERROR) { + response->rap.sub_id == HIDPP_ERROR) { + ret = response->rap.params[1]; + dbg_hid("%s:got hidpp error %02X\n", __func__, ret); + goto exit; + } + + if (response->report_id == REPORT_ID_HIDPP_LONG && + response->fap.feature_index == HIDPP20_ERROR) { ret = response->fap.params[1]; - dbg_hid("__hidpp_send_report got hidpp error %02X\n", ret); + dbg_hid("%s:got hidpp 2.0 error %02X\n", __func__, ret); goto exit; } @@ -271,7 +282,8 @@ static inline bool hidpp_match_answer(struct hidpp_report *question, static inline bool hidpp_match_error(struct hidpp_report *question, struct hidpp_report *answer) { - return (answer->fap.feature_index == HIDPP_ERROR) && + return ((answer->rap.sub_id == HIDPP_ERROR) || + (answer->fap.feature_index == HIDPP20_ERROR)) && (answer->fap.funcindex_clientid == question->fap.feature_index) && (answer->fap.params[0] == question->fap.funcindex_clientid); } @@ -282,6 +294,33 @@ static inline bool hidpp_report_is_connect_event(struct hidpp_report *report) (report->rap.sub_id == 0x41); } +/** + * hidpp_prefix_name() prefixes the current given name with "Logitech ". + */ +static void hidpp_prefix_name(char **name, int name_length) +{ +#define PREFIX_LENGTH 9 /* "Logitech " */ + + int new_length; + char *new_name; + + if (name_length > PREFIX_LENGTH && + strncmp(*name, "Logitech ", PREFIX_LENGTH) == 0) + /* The prefix has is already in the name */ + return; + + new_length = PREFIX_LENGTH + name_length; + new_name = kzalloc(new_length, GFP_KERNEL); + if (!new_name) + return; + + snprintf(new_name, new_length, "Logitech %s", *name); + + kfree(*name); + + *name = new_name; +} + /* -------------------------------------------------------------------------- */ /* HIDP++ 1.0 commands */ /* -------------------------------------------------------------------------- */ @@ -321,6 +360,10 @@ static char *hidpp_get_unifying_name(struct hidpp_device *hidpp_dev) return NULL; memcpy(name, &response.rap.params[2], len); + + /* include the terminating '\0' */ + hidpp_prefix_name(&name, len + 1); + return name; } @@ -498,6 +541,9 @@ static char *hidpp_get_device_name(struct hidpp_device *hidpp) index += ret; } + /* include the terminating '\0' */ + hidpp_prefix_name(&name, __name_length + 1); + return name; } @@ -794,18 +840,25 @@ static int wtp_raw_event(struct hid_device *hdev, u8 *data, int size) switch (data[0]) { case 0x02: + if (size < 2) { + hid_err(hdev, "Received HID report of bad size (%d)", + size); + return 1; + } if (hidpp->quirks & HIDPP_QUIRK_WTP_PHYSICAL_BUTTONS) { input_event(wd->input, EV_KEY, BTN_LEFT, !!(data[1] & 0x01)); input_event(wd->input, EV_KEY, BTN_RIGHT, !!(data[1] & 0x02)); input_sync(wd->input); + return 0; } else { if (size < 21) return 1; return wtp_mouse_raw_xy_event(hidpp, &data[7]); } case REPORT_ID_HIDPP_LONG: + /* size is already checked in hidpp_raw_event. */ if ((report->fap.feature_index != wd->mt_feature_index) || (report->fap.funcindex_clientid != EVENT_TOUCHPAD_RAW_XY)) return 1; @@ -862,24 +915,24 @@ static int wtp_allocate(struct hid_device *hdev, const struct hid_device_id *id) return 0; }; -static void wtp_connect(struct hid_device *hdev, bool connected) +static int wtp_connect(struct hid_device *hdev, bool connected) { struct hidpp_device *hidpp = hid_get_drvdata(hdev); struct wtp_data *wd = hidpp->private_data; int ret; if (!connected) - return; + return 0; if (!wd->x_size) { ret = wtp_get_config(hidpp); if (ret) { hid_err(hdev, "Can not get wtp config: %d\n", ret); - return; + return ret; } } - hidpp_touchpad_set_raw_report_state(hidpp, wd->mt_feature_index, + return hidpp_touchpad_set_raw_report_state(hidpp, wd->mt_feature_index, true, true); } @@ -924,7 +977,7 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, /* * If the mutex is locked then we have a pending answer from a - * previoulsly sent command + * previously sent command. */ if (unlikely(mutex_is_locked(&hidpp->send_mutex))) { /* @@ -955,9 +1008,6 @@ static int hidpp_raw_hidpp_event(struct hidpp_device *hidpp, u8 *data, return 1; } - if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) - return wtp_raw_event(hidpp->hid_dev, data, size); - return 0; } @@ -965,7 +1015,9 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { struct hidpp_device *hidpp = hid_get_drvdata(hdev); + int ret = 0; + /* Generic HID++ processing. */ switch (data[0]) { case REPORT_ID_HIDPP_LONG: if (size != HIDPP_REPORT_LONG_LENGTH) { @@ -973,16 +1025,23 @@ static int hidpp_raw_event(struct hid_device *hdev, struct hid_report *report, size); return 1; } - return hidpp_raw_hidpp_event(hidpp, data, size); + ret = hidpp_raw_hidpp_event(hidpp, data, size); + break; case REPORT_ID_HIDPP_SHORT: if (size != HIDPP_REPORT_SHORT_LENGTH) { hid_err(hdev, "received hid++ report of bad size (%d)", size); return 1; } - return hidpp_raw_hidpp_event(hidpp, data, size); + ret = hidpp_raw_hidpp_event(hidpp, data, size); + break; } + /* If no report is available for further processing, skip calling + * raw_event of subclasses. */ + if (ret != 0) + return ret; + if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) return wtp_raw_event(hdev, data, size); @@ -1029,6 +1088,7 @@ static void hidpp_input_close(struct input_dev *dev) static struct input_dev *hidpp_allocate_input(struct hid_device *hdev) { struct input_dev *input_dev = devm_input_allocate_device(&hdev->dev); + struct hidpp_device *hidpp = hid_get_drvdata(hdev); if (!input_dev) return NULL; @@ -1037,7 +1097,7 @@ static struct input_dev *hidpp_allocate_input(struct hid_device *hdev) input_dev->open = hidpp_input_open; input_dev->close = hidpp_input_close; - input_dev->name = hdev->name; + input_dev->name = hidpp->name; input_dev->phys = hdev->phys; input_dev->uniq = hdev->uniq; input_dev->id.bustype = hdev->bus; @@ -1057,8 +1117,11 @@ static void hidpp_connect_event(struct hidpp_device *hidpp) struct input_dev *input; char *name, *devm_name; - if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) - wtp_connect(hdev, connected); + if (hidpp->quirks & HIDPP_QUIRK_CLASS_WTP) { + ret = wtp_connect(hdev, connected); + if (ret) + return; + } if (!connected || hidpp->delayed_input) return; @@ -1076,22 +1139,28 @@ static void hidpp_connect_event(struct hidpp_device *hidpp) hid_info(hdev, "HID++ %u.%u device connected.\n", hidpp->protocol_major, hidpp->protocol_minor); + if (!hidpp->name || hidpp->name == hdev->name) { + name = hidpp_get_device_name(hidpp); + if (!name) { + hid_err(hdev, + "unable to retrieve the name of the device"); + return; + } + + devm_name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s", name); + kfree(name); + if (!devm_name) + return; + + hidpp->name = devm_name; + } + input = hidpp_allocate_input(hdev); if (!input) { hid_err(hdev, "cannot allocate new input device: %d\n", ret); return; } - name = hidpp_get_device_name(hidpp); - if (!name) { - hid_err(hdev, "unable to retrieve the name of the device"); - } else { - devm_name = devm_kasprintf(&hdev->dev, GFP_KERNEL, "%s", name); - if (devm_name) - input->name = devm_name; - kfree(name); - } - hidpp_populate_input(hidpp, input, false); ret = input_register_device(input); @@ -1114,6 +1183,7 @@ static int hidpp_probe(struct hid_device *hdev, const struct hid_device_id *id) return -ENOMEM; hidpp->hid_dev = hdev; + hidpp->name = hdev->name; hid_set_drvdata(hdev, hidpp); hidpp->quirks = id->driver_data; diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index cacda43f6a6f..fbaea6eb882e 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -276,6 +276,8 @@ static const struct hid_device_id ms_devices[] = { .driver_data = MS_DUPLICATE_USAGES }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3), .driver_data = MS_HIDINPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3_JP), + .driver_data = MS_HIDINPUT }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT), .driver_data = MS_PRESENTER }, diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c index b51200fe2f33..49d4fe4f5987 100644 --- a/drivers/hid/hid-rmi.c +++ b/drivers/hid/hid-rmi.c @@ -33,6 +33,10 @@ #define RMI_READ_DATA_PENDING BIT(1) #define RMI_STARTED BIT(2) +/* device flags */ +#define RMI_DEVICE BIT(0) +#define RMI_DEVICE_HAS_PHYS_BUTTONS BIT(1) + enum rmi_mode_type { RMI_MODE_OFF = 0, RMI_MODE_ATTN_REPORTS = 1, @@ -118,6 +122,8 @@ struct rmi_data { struct work_struct reset_work; struct hid_device *hdev; + + unsigned long device_flags; }; #define RMI_PAGE(addr) (((addr) >> 8) & 0xff) @@ -452,9 +458,32 @@ static int rmi_raw_event(struct hid_device *hdev, return rmi_read_data_event(hdev, data, size); case RMI_ATTN_REPORT_ID: return rmi_input_event(hdev, data, size); - case RMI_MOUSE_REPORT_ID: + default: + return 1; + } + + return 0; +} + +static int rmi_event(struct hid_device *hdev, struct hid_field *field, + struct hid_usage *usage, __s32 value) +{ + struct rmi_data *data = hid_get_drvdata(hdev); + + if ((data->device_flags & RMI_DEVICE) && + (field->application == HID_GD_POINTER || + field->application == HID_GD_MOUSE)) { + if (data->device_flags & RMI_DEVICE_HAS_PHYS_BUTTONS) { + if ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON) + return 0; + + if ((usage->hid == HID_GD_X || usage->hid == HID_GD_Y) + && !value) + return 1; + } + rmi_schedule_reset(hdev); - break; + return 1; } return 0; @@ -856,6 +885,9 @@ static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi) if (ret) return; + if (!(data->device_flags & RMI_DEVICE)) + return; + /* Allow incoming hid reports */ hid_device_io_start(hdev); @@ -914,8 +946,38 @@ static int rmi_input_mapping(struct hid_device *hdev, struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, unsigned long **bit, int *max) { - /* we want to make HID ignore the advertised HID collection */ - return -1; + struct rmi_data *data = hid_get_drvdata(hdev); + + /* + * we want to make HID ignore the advertised HID collection + * for RMI deivces + */ + if (data->device_flags & RMI_DEVICE) { + if ((data->device_flags & RMI_DEVICE_HAS_PHYS_BUTTONS) && + ((usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON)) + return 0; + + return -1; + } + + return 0; +} + +static int rmi_check_valid_report_id(struct hid_device *hdev, unsigned type, + unsigned id, struct hid_report **report) +{ + int i; + + *report = hdev->report_enum[type].report_id_hash[id]; + if (*report) { + for (i = 0; i < (*report)->maxfield; i++) { + unsigned app = (*report)->field[i]->application; + if ((app & HID_USAGE_PAGE) >= HID_UP_MSVENDOR) + return 1; + } + } + + return 0; } static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) @@ -925,6 +987,7 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) size_t alloc_size; struct hid_report *input_report; struct hid_report *output_report; + struct hid_report *feature_report; data = devm_kzalloc(&hdev->dev, sizeof(struct rmi_data), GFP_KERNEL); if (!data) @@ -943,27 +1006,37 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) return ret; } - input_report = hdev->report_enum[HID_INPUT_REPORT] - .report_id_hash[RMI_ATTN_REPORT_ID]; - if (!input_report) { - hid_err(hdev, "device does not have expected input report\n"); - ret = -ENODEV; - return ret; + if (id->driver_data) + data->device_flags = id->driver_data; + + /* + * Check for the RMI specific report ids. If they are misisng + * simply return and let the events be processed by hid-input + */ + if (!rmi_check_valid_report_id(hdev, HID_FEATURE_REPORT, + RMI_SET_RMI_MODE_REPORT_ID, &feature_report)) { + hid_dbg(hdev, "device does not have set mode feature report\n"); + goto start; } - data->input_report_size = (input_report->size >> 3) + 1 /* report id */; + if (!rmi_check_valid_report_id(hdev, HID_INPUT_REPORT, + RMI_ATTN_REPORT_ID, &input_report)) { + hid_dbg(hdev, "device does not have attention input report\n"); + goto start; + } - output_report = hdev->report_enum[HID_OUTPUT_REPORT] - .report_id_hash[RMI_WRITE_REPORT_ID]; - if (!output_report) { - hid_err(hdev, "device does not have expected output report\n"); - ret = -ENODEV; - return ret; + data->input_report_size = hid_report_len(input_report); + + if (!rmi_check_valid_report_id(hdev, HID_OUTPUT_REPORT, + RMI_WRITE_REPORT_ID, &output_report)) { + hid_dbg(hdev, + "device does not have rmi write output report\n"); + goto start; } - data->output_report_size = (output_report->size >> 3) - + 1 /* report id */; + data->output_report_size = hid_report_len(output_report); + data->device_flags |= RMI_DEVICE; alloc_size = data->output_report_size + data->input_report_size; data->writeReport = devm_kzalloc(&hdev->dev, alloc_size, GFP_KERNEL); @@ -978,13 +1051,15 @@ static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id) mutex_init(&data->page_mutex); +start: ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); if (ret) { hid_err(hdev, "hw start failed\n"); return ret; } - if (!test_bit(RMI_STARTED, &data->flags)) + if ((data->device_flags & RMI_DEVICE) && + !test_bit(RMI_STARTED, &data->flags)) /* * The device maybe in the bootloader if rmi_input_configured * failed to find F11 in the PDT. Print an error, but don't @@ -1007,6 +1082,8 @@ static void rmi_remove(struct hid_device *hdev) } static const struct hid_device_id rmi_id[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_RAZER, USB_DEVICE_ID_RAZER_BLADE_14), + .driver_data = RMI_DEVICE_HAS_PHYS_BUTTONS }, { HID_DEVICE(HID_BUS_ANY, HID_GROUP_RMI, HID_ANY_ID, HID_ANY_ID) }, { } }; @@ -1017,6 +1094,7 @@ static struct hid_driver rmi_driver = { .id_table = rmi_id, .probe = rmi_probe, .remove = rmi_remove, + .event = rmi_event, .raw_event = rmi_raw_event, .input_mapping = rmi_input_mapping, .input_configured = rmi_input_configured, diff --git a/drivers/hid/hid-roccat-pyra.c b/drivers/hid/hid-roccat-pyra.c index 1a07e07d99a0..47d7e74231e5 100644 --- a/drivers/hid/hid-roccat-pyra.c +++ b/drivers/hid/hid-roccat-pyra.c @@ -35,6 +35,8 @@ static struct class *pyra_class; static void profile_activated(struct pyra_device *pyra, unsigned int new_profile) { + if (new_profile >= ARRAY_SIZE(pyra->profile_settings)) + return; pyra->actual_profile = new_profile; pyra->actual_cpi = pyra->profile_settings[pyra->actual_profile].y_cpi; } @@ -257,9 +259,11 @@ static ssize_t pyra_sysfs_write_settings(struct file *fp, if (off != 0 || count != PYRA_SIZE_SETTINGS) return -EINVAL; - mutex_lock(&pyra->pyra_lock); - settings = (struct pyra_settings const *)buf; + if (settings->startup_profile >= ARRAY_SIZE(pyra->profile_settings)) + return -EINVAL; + + mutex_lock(&pyra->pyra_lock); retval = pyra_set_settings(usb_dev, settings); if (retval) { diff --git a/drivers/hid/i2c-hid/i2c-hid.c b/drivers/hid/i2c-hid/i2c-hid.c index d32037cbf9db..d43e967e7533 100644 --- a/drivers/hid/i2c-hid/i2c-hid.c +++ b/drivers/hid/i2c-hid/i2c-hid.c @@ -706,12 +706,7 @@ static int i2c_hid_start(struct hid_device *hid) static void i2c_hid_stop(struct hid_device *hid) { - struct i2c_client *client = hid->driver_data; - struct i2c_hid *ihid = i2c_get_clientdata(client); - hid->claimed = 0; - - i2c_hid_free_buffers(ihid); } static int i2c_hid_open(struct hid_device *hid) diff --git a/drivers/hid/usbhid/Makefile b/drivers/hid/usbhid/Makefile index db3cf31c6fa1..890f2914a8ff 100644 --- a/drivers/hid/usbhid/Makefile +++ b/drivers/hid/usbhid/Makefile @@ -2,17 +2,9 @@ # Makefile for the USB input drivers # -# Multipart objects. usbhid-y := hid-core.o hid-quirks.o - -# Optional parts of multipart objects. - -ifeq ($(CONFIG_USB_HIDDEV),y) - usbhid-y += hiddev.o -endif -ifeq ($(CONFIG_HID_PID),y) - usbhid-y += hid-pidff.o -endif +usbhid-$(CONFIG_USB_HIDDEV) += hiddev.o +usbhid-$(CONFIG_HID_PID) += hid-pidff.o obj-$(CONFIG_USB_HID) += usbhid.o obj-$(CONFIG_USB_KBD) += usbkbd.o diff --git a/drivers/hid/usbhid/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index 10b616702780..0b531c6a76a5 100644 --- a/drivers/hid/usbhid/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c @@ -1252,6 +1252,8 @@ int hid_pidff_init(struct hid_device *hid) pidff->hid = hid; + hid_device_io_start(hid); + pidff_find_reports(hid, HID_OUTPUT_REPORT, pidff); pidff_find_reports(hid, HID_FEATURE_REPORT, pidff); @@ -1315,9 +1317,13 @@ int hid_pidff_init(struct hid_device *hid) hid_info(dev, "Force feedback for USB HID PID devices by Anssi Hannula <anssi.hannula@gmail.com>\n"); + hid_device_io_stop(hid); + return 0; fail: + hid_device_io_stop(hid); + kfree(pidff); return error; } diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c index dc89be90b35e..9be99a67bfe2 100644 --- a/drivers/hid/usbhid/hid-quirks.c +++ b/drivers/hid/usbhid/hid-quirks.c @@ -80,6 +80,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET }, { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET }, { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3, HID_QUIRK_NO_INIT_REPORTS }, + { USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_TYPE_COVER_3_JP, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS }, @@ -124,6 +125,7 @@ static const struct hid_blacklist { { USB_VENDOR_ID_CHICONY, USB_DEVICE_ID_CHICONY_WIRELESS, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_SIGMA_MICRO, USB_DEVICE_ID_SIGMA_MICRO_KEYBOARD, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X, HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_MOUSEPEN_I608X_2, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X, HID_QUIRK_MULTI_INPUT }, { USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_DUOSENSE, HID_QUIRK_NO_INIT_REPORTS }, { USB_VENDOR_ID_SEMICO, USB_DEVICE_ID_SEMICO_USB_KEYKOARD, HID_QUIRK_NO_INIT_REPORTS }, diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 654202941d30..f0568a7e6de9 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -173,10 +173,8 @@ static void wacom_usage_mapping(struct hid_device *hdev, { struct wacom *wacom = hid_get_drvdata(hdev); struct wacom_features *features = &wacom->wacom_wac.features; - bool finger = (field->logical == HID_DG_FINGER) || - (field->physical == HID_DG_FINGER); - bool pen = (field->logical == HID_DG_STYLUS) || - (field->physical == HID_DG_STYLUS); + bool finger = WACOM_FINGER_FIELD(field); + bool pen = WACOM_PEN_FIELD(field); /* * Requiring Stylus Usage will ignore boot mouse @@ -405,6 +403,9 @@ static int wacom_query_tablet_data(struct hid_device *hdev, else if (features->type == WACOM_24HDT || features->type == CINTIQ_HYBRID) { return wacom_set_device_mode(hdev, 18, 3, 2); } + else if (features->type == WACOM_27QHDT) { + return wacom_set_device_mode(hdev, 131, 3, 2); + } } else if (features->device_type == BTN_TOOL_PEN) { if (features->type <= BAMBOO_PT && features->type != WIRELESS) { return wacom_set_device_mode(hdev, 2, 2, 2); diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index ac7447c7b82e..1a6507999a65 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -15,7 +15,6 @@ #include "wacom_wac.h" #include "wacom.h" #include <linux/input/mt.h> -#include <linux/hid.h> /* resolution for penabled devices */ #define WACOM_PL_RES 20 @@ -444,9 +443,6 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) /* Enter report */ if ((data[1] & 0xfc) == 0xc0) { - if (features->quirks & WACOM_QUIRK_MULTI_INPUT) - wacom->shared->stylus_in_proximity = true; - /* serial number of the tool */ wacom->serial[idx] = ((data[3] & 0x0f) << 28) + (data[4] << 20) + (data[5] << 12) + @@ -535,24 +531,46 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) return 1; } + /* + * don't report events for invalid data + */ /* older I4 styli don't work with new Cintiqs */ - if (!((wacom->id[idx] >> 20) & 0x01) && - (features->type == WACOM_21UX2)) + if ((!((wacom->id[idx] >> 20) & 0x01) && + (features->type == WACOM_21UX2)) || + /* Only large Intuos support Lense Cursor */ + (wacom->tool[idx] == BTN_TOOL_LENS && + (features->type == INTUOS3 || + features->type == INTUOS3S || + features->type == INTUOS4 || + features->type == INTUOS4S || + features->type == INTUOS5 || + features->type == INTUOS5S || + features->type == INTUOSPM || + features->type == INTUOSPS)) || + /* Cintiq doesn't send data when RDY bit isn't set */ + (features->type == CINTIQ && !(data[1] & 0x40))) return 1; - /* Range Report */ - if ((data[1] & 0xfe) == 0x20) { + if (features->quirks & WACOM_QUIRK_MULTI_INPUT) + wacom->shared->stylus_in_proximity = true; + + /* in Range while exiting */ + if (((data[1] & 0xfe) == 0x20) && wacom->reporting_data) { input_report_key(input, BTN_TOUCH, 0); input_report_abs(input, ABS_PRESSURE, 0); input_report_abs(input, ABS_DISTANCE, wacom->features.distance_max); - if (features->quirks & WACOM_QUIRK_MULTI_INPUT) - wacom->shared->stylus_in_proximity = true; + return 2; } /* Exit report */ if ((data[1] & 0xfe) == 0x80) { if (features->quirks & WACOM_QUIRK_MULTI_INPUT) wacom->shared->stylus_in_proximity = false; + wacom->reporting_data = false; + + /* don't report exit if we don't know the ID */ + if (!wacom->id[idx]) + return 1; /* * Reset all states otherwise we lose the initial states @@ -586,6 +604,11 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) wacom->id[idx] = 0; return 2; } + + /* don't report other events if we don't know the ID */ + if (!wacom->id[idx]) + return 1; + return 0; } @@ -633,6 +656,8 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) data[0] != WACOM_REPORT_INTUOSREAD && data[0] != WACOM_REPORT_INTUOSWRITE && data[0] != WACOM_REPORT_INTUOSPAD && + data[0] != WACOM_REPORT_CINTIQ && + data[0] != WACOM_REPORT_CINTIQPAD && data[0] != WACOM_REPORT_INTUOS5PAD) { dev_dbg(input->dev.parent, "%s: received unknown report #%d\n", __func__, data[0]); @@ -644,7 +669,8 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) idx = data[1] & 0x01; /* pad packets. Works as a second tool and is always in prox */ - if (data[0] == WACOM_REPORT_INTUOSPAD || data[0] == WACOM_REPORT_INTUOS5PAD) { + if (data[0] == WACOM_REPORT_INTUOSPAD || data[0] == WACOM_REPORT_INTUOS5PAD || + data[0] == WACOM_REPORT_CINTIQPAD) { input = wacom->pad_input; if (features->type >= INTUOS4S && features->type <= INTUOS4L) { input_report_key(input, BTN_0, (data[2] & 0x01)); @@ -744,6 +770,14 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) } else { input_report_abs(input, ABS_MISC, 0); } + } else if (features->type == WACOM_27QHD) { + input_report_key(input, KEY_PROG1, data[2] & 0x01); + input_report_key(input, KEY_PROG2, data[2] & 0x02); + input_report_key(input, KEY_PROG3, data[2] & 0x04); + + input_report_abs(input, ABS_X, be16_to_cpup((__be16 *)&data[4])); + input_report_abs(input, ABS_Y, be16_to_cpup((__be16 *)&data[6])); + input_report_abs(input, ABS_Z, be16_to_cpup((__be16 *)&data[8])); } else if (features->type == CINTIQ_HYBRID) { /* * Do not send hardware buttons under Android. They @@ -760,6 +794,12 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) input_report_key(input, BTN_7, (data[4] & 0x40)); /* Left */ input_report_key(input, BTN_8, (data[4] & 0x80)); /* Down */ input_report_key(input, BTN_0, (data[3] & 0x01)); /* Center */ + + if (data[4] | (data[3] & 0x01)) { + input_report_abs(input, ABS_MISC, PAD_DEVICE_ID); + } else { + input_report_abs(input, ABS_MISC, 0); + } } else if (features->type >= INTUOS5S && features->type <= INTUOSPL) { int i; @@ -843,28 +883,6 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) if (result) return result - 1; - /* don't proceed if we don't know the ID */ - if (!wacom->id[idx]) - return 0; - - /* Only large Intuos support Lense Cursor */ - if (wacom->tool[idx] == BTN_TOOL_LENS && - (features->type == INTUOS3 || - features->type == INTUOS3S || - features->type == INTUOS4 || - features->type == INTUOS4S || - features->type == INTUOS5 || - features->type == INTUOS5S || - features->type == INTUOSPM || - features->type == INTUOSPS)) { - - return 0; - } - - /* Cintiq doesn't send data when RDY bit isn't set */ - if (features->type == CINTIQ && !(data[1] & 0x40)) - return 0; - if (features->type >= INTUOS3S) { input_report_abs(input, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1)); input_report_abs(input, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1)); @@ -951,6 +969,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) input_report_abs(input, ABS_MISC, wacom->id[idx]); /* report tool id */ input_report_key(input, wacom->tool[idx], 1); input_event(input, EV_MSC, MSC_SERIAL, wacom->serial[idx]); + wacom->reporting_data = true; return 1; } @@ -1019,8 +1038,20 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom) struct input_dev *input = wacom->input; unsigned char *data = wacom->data; int i; - int current_num_contacts = data[61]; + int current_num_contacts = 0; int contacts_to_send = 0; + int num_contacts_left = 4; /* maximum contacts per packet */ + int byte_per_packet = WACOM_BYTES_PER_24HDT_PACKET; + int y_offset = 2; + + if (wacom->features.type == WACOM_27QHDT) { + current_num_contacts = data[63]; + num_contacts_left = 10; + byte_per_packet = WACOM_BYTES_PER_QHDTHID_PACKET; + y_offset = 0; + } else { + current_num_contacts = data[61]; + } /* * First packet resets the counter since only the first @@ -1029,12 +1060,11 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom) if (current_num_contacts) wacom->num_contacts_left = current_num_contacts; - /* There are at most 4 contacts per packet */ - contacts_to_send = min(4, wacom->num_contacts_left); + contacts_to_send = min(num_contacts_left, wacom->num_contacts_left); for (i = 0; i < contacts_to_send; i++) { - int offset = (WACOM_BYTES_PER_24HDT_PACKET * i) + 1; - bool touch = data[offset] & 0x1 && !wacom->shared->stylus_in_proximity; + int offset = (byte_per_packet * i) + 1; + bool touch = (data[offset] & 0x1) && !wacom->shared->stylus_in_proximity; int slot = input_mt_get_slot_by_key(input, data[offset + 1]); if (slot < 0) @@ -1044,18 +1074,23 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom) if (touch) { int t_x = get_unaligned_le16(&data[offset + 2]); - int c_x = get_unaligned_le16(&data[offset + 4]); - int t_y = get_unaligned_le16(&data[offset + 6]); - int c_y = get_unaligned_le16(&data[offset + 8]); - int w = get_unaligned_le16(&data[offset + 10]); - int h = get_unaligned_le16(&data[offset + 12]); + int t_y = get_unaligned_le16(&data[offset + 4 + y_offset]); input_report_abs(input, ABS_MT_POSITION_X, t_x); input_report_abs(input, ABS_MT_POSITION_Y, t_y); - input_report_abs(input, ABS_MT_TOUCH_MAJOR, min(w,h)); - input_report_abs(input, ABS_MT_WIDTH_MAJOR, min(w, h) + int_dist(t_x, t_y, c_x, c_y)); - input_report_abs(input, ABS_MT_WIDTH_MINOR, min(w, h)); - input_report_abs(input, ABS_MT_ORIENTATION, w > h); + + if (wacom->features.type != WACOM_27QHDT) { + int c_x = get_unaligned_le16(&data[offset + 4]); + int c_y = get_unaligned_le16(&data[offset + 8]); + int w = get_unaligned_le16(&data[offset + 10]); + int h = get_unaligned_le16(&data[offset + 12]); + + input_report_abs(input, ABS_MT_TOUCH_MAJOR, min(w,h)); + input_report_abs(input, ABS_MT_WIDTH_MAJOR, + min(w, h) + int_dist(t_x, t_y, c_x, c_y)); + input_report_abs(input, ABS_MT_WIDTH_MINOR, min(w, h)); + input_report_abs(input, ABS_MT_ORIENTATION, w > h); + } } } input_mt_report_pointer_emulation(input, true); @@ -1064,6 +1099,7 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom) if (wacom->num_contacts_left <= 0) wacom->num_contacts_left = 0; + wacom->shared->touch_down = (wacom->num_contacts_left > 0); return 1; } @@ -1092,7 +1128,7 @@ static int wacom_mt_touch(struct wacom_wac *wacom) for (i = 0; i < contacts_to_send; i++) { int offset = (WACOM_BYTES_PER_MT_PACKET + x_offset) * i + 3; - bool touch = data[offset] & 0x1; + bool touch = (data[offset] & 0x1) && !wacom->shared->stylus_in_proximity; int id = get_unaligned_le16(&data[offset + 1]); int slot = input_mt_get_slot_by_key(input, id); @@ -1114,6 +1150,7 @@ static int wacom_mt_touch(struct wacom_wac *wacom) if (wacom->num_contacts_left < 0) wacom->num_contacts_left = 0; + wacom->shared->touch_down = (wacom->num_contacts_left > 0); return 1; } @@ -1514,13 +1551,6 @@ static void wacom_wac_finger_report(struct hid_device *hdev, wacom_wac->shared->touch_down = wacom_wac_finger_count_touches(hdev); } -#define WACOM_PEN_FIELD(f) (((f)->logical == HID_DG_STYLUS) || \ - ((f)->physical == HID_DG_STYLUS) || \ - ((f)->application == HID_DG_PEN)) -#define WACOM_FINGER_FIELD(f) (((f)->logical == HID_DG_FINGER) || \ - ((f)->physical == HID_DG_FINGER) || \ - ((f)->application == HID_DG_TOUCHSCREEN)) - void wacom_wac_usage_mapping(struct hid_device *hdev, struct hid_field *field, struct hid_usage *usage) { @@ -1891,6 +1921,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) case WACOM_21UX2: case WACOM_22HD: case WACOM_24HD: + case WACOM_27QHD: case DTK: case CINTIQ_HYBRID: sync = wacom_intuos_irq(wacom_wac); @@ -1901,6 +1932,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) break; case WACOM_24HDT: + case WACOM_27QHDT: sync = wacom_24hdt_irq(wacom_wac); break; @@ -2086,32 +2118,17 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev, wacom_abs_set_axis(input_dev, wacom_wac); switch (features->type) { - case WACOM_MO: - case WACOM_G4: - /* fall through */ - - case GRAPHIRE: - input_set_capability(input_dev, EV_REL, REL_WHEEL); - - __set_bit(BTN_LEFT, input_dev->keybit); - __set_bit(BTN_RIGHT, input_dev->keybit); - __set_bit(BTN_MIDDLE, input_dev->keybit); - - __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); - __set_bit(BTN_TOOL_PEN, input_dev->keybit); - __set_bit(BTN_TOOL_MOUSE, input_dev->keybit); - __set_bit(BTN_STYLUS, input_dev->keybit); - __set_bit(BTN_STYLUS2, input_dev->keybit); - - __set_bit(INPUT_PROP_POINTER, input_dev->propbit); - break; - case GRAPHIRE_BT: __clear_bit(ABS_MISC, input_dev->absbit); + + case WACOM_MO: + case WACOM_G4: input_set_abs_params(input_dev, ABS_DISTANCE, 0, features->distance_max, 0, 0); + /* fall through */ + case GRAPHIRE: input_set_capability(input_dev, EV_REL, REL_WHEEL); __set_bit(BTN_LEFT, input_dev->keybit); @@ -2127,31 +2144,15 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev, __set_bit(INPUT_PROP_POINTER, input_dev->propbit); break; + case WACOM_27QHD: case WACOM_24HD: - input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); - input_abs_set_res(input_dev, ABS_Z, 287); - input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0); - /* fall through */ - case DTK: - __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); - - wacom_setup_cintiq(wacom_wac); - break; - case WACOM_22HD: case WACOM_21UX2: case WACOM_BEE: case CINTIQ: - input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); - input_abs_set_res(input_dev, ABS_Z, 287); - - __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); - - wacom_setup_cintiq(wacom_wac); - break; - case WACOM_13HD: + case CINTIQ_HYBRID: input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); input_abs_set_res(input_dev, ABS_Z, 287); __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); @@ -2161,6 +2162,10 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev, case INTUOS3: case INTUOS3L: case INTUOS3S: + case INTUOS4: + case INTUOS4WL: + case INTUOS4L: + case INTUOS4S: input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); input_abs_set_res(input_dev, ABS_Z, 287); /* fall through */ @@ -2199,17 +2204,6 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev, } break; - case INTUOS4: - case INTUOS4WL: - case INTUOS4L: - case INTUOS4S: - input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); - input_abs_set_res(input_dev, ABS_Z, 287); - wacom_setup_intuos(wacom_wac); - - __set_bit(INPUT_PROP_POINTER, input_dev->propbit); - break; - case WACOM_24HDT: if (features->device_type == BTN_TOOL_FINGER) { input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, features->x_max, 0, 0); @@ -2219,6 +2213,7 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev, } /* fall through */ + case WACOM_27QHDT: case MTSCREEN: case MTTPC: case MTTPC_B: @@ -2305,14 +2300,6 @@ int wacom_setup_pentouch_input_capabilities(struct input_dev *input_dev, 0, 0); } break; - - case CINTIQ_HYBRID: - input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); - input_abs_set_res(input_dev, ABS_Z, 287); - __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); - - wacom_setup_cintiq(wacom_wac); - break; } return 0; } @@ -2374,6 +2361,19 @@ int wacom_setup_pad_input_capabilities(struct input_dev *input_dev, input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0); break; + case WACOM_27QHD: + __set_bit(KEY_PROG1, input_dev->keybit); + __set_bit(KEY_PROG2, input_dev->keybit); + __set_bit(KEY_PROG3, input_dev->keybit); + input_set_abs_params(input_dev, ABS_X, -2048, 2048, 0, 0); + input_abs_set_res(input_dev, ABS_X, 1024); /* points/g */ + input_set_abs_params(input_dev, ABS_Y, -2048, 2048, 0, 0); + input_abs_set_res(input_dev, ABS_Y, 1024); + input_set_abs_params(input_dev, ABS_Z, -2048, 2048, 0, 0); + input_abs_set_res(input_dev, ABS_Z, 1024); + __set_bit(INPUT_PROP_ACCELEROMETER, input_dev->propbit); + break; + case DTK: for (i = 0; i < 6; i++) __set_bit(BTN_0 + i, input_dev->keybit); @@ -2724,6 +2724,18 @@ static const struct wacom_features wacom_features_0xF6 = { "Wacom Cintiq 24HD touch", .type = WACOM_24HDT, /* Touch */ .oVid = USB_VENDOR_ID_WACOM, .oPid = 0xf8, .touch_max = 10, .check_for_hid_type = true, .hid_type = HID_TYPE_USBNONE }; +static const struct wacom_features wacom_features_0x32A = + { "Wacom Cintiq 27QHD", 119740, 67520, 2047, + 63, WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, + WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; +static const struct wacom_features wacom_features_0x32B = + { "Wacom Cintiq 27QHD touch", 119740, 67520, 2047, 63, + WACOM_27QHD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, + WACOM_CINTIQ_OFFSET, WACOM_CINTIQ_OFFSET, + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x32C }; +static const struct wacom_features wacom_features_0x32C = + { "Wacom Cintiq 27QHD touch", .type = WACOM_27QHDT, + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x32B, .touch_max = 10 }; static const struct wacom_features wacom_features_0x3F = { "Wacom Cintiq 21UX", 87200, 65600, 1023, 63, CINTIQ, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; @@ -3090,6 +3102,9 @@ const struct hid_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0x315) }, { USB_DEVICE_WACOM(0x317) }, { USB_DEVICE_WACOM(0x323) }, + { USB_DEVICE_WACOM(0x32A) }, + { USB_DEVICE_WACOM(0x32B) }, + { USB_DEVICE_WACOM(0x32C) }, { USB_DEVICE_WACOM(0x32F) }, { USB_DEVICE_WACOM(0x4001) }, { USB_DEVICE_WACOM(0x4004) }, diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h index bfad815cda8a..021ee1c1980a 100644 --- a/drivers/hid/wacom_wac.h +++ b/drivers/hid/wacom_wac.h @@ -10,9 +10,10 @@ #define WACOM_WAC_H #include <linux/types.h> +#include <linux/hid.h> /* maximum packet length for USB devices */ -#define WACOM_PKGLEN_MAX 68 +#define WACOM_PKGLEN_MAX 192 #define WACOM_NAME_MAX 64 @@ -36,6 +37,7 @@ /* wacom data size per MT contact */ #define WACOM_BYTES_PER_MT_PACKET 11 #define WACOM_BYTES_PER_24HDT_PACKET 14 +#define WACOM_BYTES_PER_QHDTHID_PACKET 6 /* device IDs */ #define STYLUS_DEVICE_ID 0x02 @@ -57,6 +59,8 @@ #define WACOM_REPORT_TPCMT 13 #define WACOM_REPORT_TPCMT2 3 #define WACOM_REPORT_TPCHID 15 +#define WACOM_REPORT_CINTIQ 16 +#define WACOM_REPORT_CINTIQPAD 17 #define WACOM_REPORT_TPCST 16 #define WACOM_REPORT_DTUS 17 #define WACOM_REPORT_TPC1FGE 18 @@ -71,6 +75,14 @@ #define WACOM_QUIRK_MONITOR 0x0008 #define WACOM_QUIRK_BATTERY 0x0010 +#define WACOM_PEN_FIELD(f) (((f)->logical == HID_DG_STYLUS) || \ + ((f)->physical == HID_DG_STYLUS) || \ + ((f)->physical == HID_DG_PEN) || \ + ((f)->application == HID_DG_PEN)) +#define WACOM_FINGER_FIELD(f) (((f)->logical == HID_DG_FINGER) || \ + ((f)->physical == HID_DG_FINGER) || \ + ((f)->application == HID_DG_TOUCHSCREEN)) + enum { PENPARTNER = 0, GRAPHIRE, @@ -100,6 +112,7 @@ enum { WACOM_22HD, DTK, WACOM_24HD, + WACOM_27QHD, CINTIQ_HYBRID, CINTIQ, WACOM_BEE, @@ -108,6 +121,7 @@ enum { WIRELESS, BAMBOO_PT, WACOM_24HDT, + WACOM_27QHDT, TABLETPC, /* add new TPC below */ TABLETPCE, TABLETPC2FG, @@ -180,6 +194,7 @@ struct wacom_wac { int tool[2]; int id[2]; __u32 serial[2]; + bool reporting_data; struct wacom_features features; struct wacom_shared *shared; struct input_dev *input; diff --git a/drivers/hsi/clients/nokia-modem.c b/drivers/hsi/clients/nokia-modem.c index f0c21458962c..eb4dc63dbc93 100644 --- a/drivers/hsi/clients/nokia-modem.c +++ b/drivers/hsi/clients/nokia-modem.c @@ -162,6 +162,7 @@ static int nokia_modem_probe(struct device *dev) return -ENOMEM; } dev_set_drvdata(dev, modem); + modem->device = dev; irq = irq_of_parse_and_map(np, 0); if (!irq) { diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 4d6b26979fbd..bb3725b672cf 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -861,8 +861,8 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx) break; case ACPI_RESOURCE_TYPE_ADDRESS64: - hyperv_mmio.start = res->data.address64.minimum; - hyperv_mmio.end = res->data.address64.maximum; + hyperv_mmio.start = res->data.address64.address.minimum; + hyperv_mmio.end = res->data.address64.address.maximum; break; } diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 6529c09c46f0..d931cbbed240 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -574,6 +574,16 @@ config SENSORS_IIO_HWMON for those channels specified in the map. This map can be provided either via platform data or the device tree bindings. +config SENSORS_I5500 + tristate "Intel 5500/5520/X58 temperature sensor" + depends on X86 && PCI + help + If you say yes here you get support for the temperature + sensor inside the Intel 5500, 5520 and X58 chipsets. + + This driver can also be built as a module. If so, the module + will be called i5500_temp. + config SENSORS_CORETEMP tristate "Intel Core/Core2/Atom temperature sensor" depends on X86 @@ -1379,6 +1389,7 @@ config SENSORS_ADS1015 config SENSORS_ADS7828 tristate "Texas Instruments ADS7828 and compatibles" depends on I2C + select REGMAP_I2C help If you say yes here you get support for Texas Instruments ADS7828 and ADS7830 8-channel A/D converters. ADS7828 resolution is 12-bit, while @@ -1420,8 +1431,8 @@ config SENSORS_INA2XX tristate "Texas Instruments INA219 and compatibles" depends on I2C help - If you say yes here you get support for INA219, INA220, INA226, and - INA230 power monitor chips. + If you say yes here you get support for INA219, INA220, INA226, + INA230, and INA231 power monitor chips. The INA2xx driver is configured for the default configuration of the part as described in the datasheet. diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 67280643bcf0..6c941472e707 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o obj-$(CONFIG_SENSORS_HIH6130) += hih6130.o obj-$(CONFIG_SENSORS_HTU21) += htu21.o obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o +obj-$(CONFIG_SENSORS_I5500) += i5500_temp.o obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o diff --git a/drivers/hwmon/abx500.c b/drivers/hwmon/abx500.c index 13875968c844..6cb89c0ebab6 100644 --- a/drivers/hwmon/abx500.c +++ b/drivers/hwmon/abx500.c @@ -221,7 +221,7 @@ static ssize_t show_min(struct device *dev, struct abx500_temp *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - return sprintf(buf, "%ld\n", data->min[attr->index]); + return sprintf(buf, "%lu\n", data->min[attr->index]); } static ssize_t show_max(struct device *dev, @@ -230,7 +230,7 @@ static ssize_t show_max(struct device *dev, struct abx500_temp *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - return sprintf(buf, "%ld\n", data->max[attr->index]); + return sprintf(buf, "%lu\n", data->max[attr->index]); } static ssize_t show_max_hyst(struct device *dev, @@ -239,7 +239,7 @@ static ssize_t show_max_hyst(struct device *dev, struct abx500_temp *data = dev_get_drvdata(dev); struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - return sprintf(buf, "%ld\n", data->max_hyst[attr->index]); + return sprintf(buf, "%lu\n", data->max_hyst[attr->index]); } static ssize_t show_min_alarm(struct device *dev, diff --git a/drivers/hwmon/ad7314.c b/drivers/hwmon/ad7314.c index f4f9b219bf16..11955467fc0f 100644 --- a/drivers/hwmon/ad7314.c +++ b/drivers/hwmon/ad7314.c @@ -16,6 +16,7 @@ #include <linux/err.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> +#include <linux/bitops.h> /* * AD7314 temperature masks @@ -67,7 +68,7 @@ static ssize_t ad7314_show_temperature(struct device *dev, switch (spi_get_device_id(chip->spi_dev)->driver_data) { case ad7314: data = (ret & AD7314_TEMP_MASK) >> AD7314_TEMP_SHIFT; - data = (data << 6) >> 6; + data = sign_extend32(data, 9); return sprintf(buf, "%d\n", 250 * data); case adt7301: @@ -78,7 +79,7 @@ static ssize_t ad7314_show_temperature(struct device *dev, * register. 1lsb - 31.25 milli degrees centigrade */ data = ret & ADT7301_TEMP_MASK; - data = (data << 2) >> 2; + data = sign_extend32(data, 13); return sprintf(buf, "%d\n", DIV_ROUND_CLOSEST(data * 3125, 100)); diff --git a/drivers/hwmon/adc128d818.c b/drivers/hwmon/adc128d818.c index 0625e50d7a6e..ad2b47e40345 100644 --- a/drivers/hwmon/adc128d818.c +++ b/drivers/hwmon/adc128d818.c @@ -27,6 +27,7 @@ #include <linux/err.h> #include <linux/regulator/consumer.h> #include <linux/mutex.h> +#include <linux/bitops.h> /* Addresses to scan * The chip also supports addresses 0x35..0x37. Don't scan those addresses @@ -189,7 +190,7 @@ static ssize_t adc128_show_temp(struct device *dev, if (IS_ERR(data)) return PTR_ERR(data); - temp = (data->temp[index] << 7) >> 7; /* sign extend */ + temp = sign_extend32(data->temp[index], 8); return sprintf(buf, "%d\n", temp * 500);/* 0.5 degrees C resolution */ } diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c index a622d40eec17..bce4e9ff21bf 100644 --- a/drivers/hwmon/ads7828.c +++ b/drivers/hwmon/ads7828.c @@ -30,14 +30,12 @@ #include <linux/hwmon-sysfs.h> #include <linux/i2c.h> #include <linux/init.h> -#include <linux/jiffies.h> #include <linux/module.h> -#include <linux/mutex.h> #include <linux/platform_data/ads7828.h> +#include <linux/regmap.h> #include <linux/slab.h> /* The ADS7828 registers */ -#define ADS7828_NCH 8 /* 8 channels supported */ #define ADS7828_CMD_SD_SE 0x80 /* Single ended inputs */ #define ADS7828_CMD_PD1 0x04 /* Internal vref OFF && A/D ON */ #define ADS7828_CMD_PD3 0x0C /* Internal vref ON && A/D ON */ @@ -50,17 +48,9 @@ enum ads7828_chips { ads7828, ads7830 }; /* Client specific data */ struct ads7828_data { - struct i2c_client *client; - struct mutex update_lock; /* Mutex protecting updates */ - unsigned long last_updated; /* Last updated time (in jiffies) */ - u16 adc_input[ADS7828_NCH]; /* ADS7828_NCH samples */ - bool valid; /* Validity flag */ - bool diff_input; /* Differential input */ - bool ext_vref; /* External voltage reference */ - unsigned int vref_mv; /* voltage reference value */ + struct regmap *regmap; u8 cmd_byte; /* Command byte without channel bits */ unsigned int lsb_resol; /* Resolution of the ADC sample LSB */ - s32 (*read_channel)(const struct i2c_client *client, u8 command); }; /* Command byte C2,C1,C0 - see datasheet */ @@ -69,42 +59,22 @@ static inline u8 ads7828_cmd_byte(u8 cmd, int ch) return cmd | (((ch >> 1) | (ch & 0x01) << 2) << 4); } -/* Update data for the device (all 8 channels) */ -static struct ads7828_data *ads7828_update_device(struct device *dev) -{ - struct ads7828_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - unsigned int ch; - dev_dbg(&client->dev, "Starting ads7828 update\n"); - - for (ch = 0; ch < ADS7828_NCH; ch++) { - u8 cmd = ads7828_cmd_byte(data->cmd_byte, ch); - data->adc_input[ch] = data->read_channel(client, cmd); - } - data->last_updated = jiffies; - data->valid = true; - } - - mutex_unlock(&data->update_lock); - - return data; -} - /* sysfs callback function */ static ssize_t ads7828_show_in(struct device *dev, struct device_attribute *da, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct ads7828_data *data = ads7828_update_device(dev); - unsigned int value = DIV_ROUND_CLOSEST(data->adc_input[attr->index] * - data->lsb_resol, 1000); + struct ads7828_data *data = dev_get_drvdata(dev); + u8 cmd = ads7828_cmd_byte(data->cmd_byte, attr->index); + unsigned int regval; + int err; - return sprintf(buf, "%d\n", value); + err = regmap_read(data->regmap, cmd, ®val); + if (err < 0) + return err; + + return sprintf(buf, "%d\n", + DIV_ROUND_CLOSEST(regval * data->lsb_resol, 1000)); } static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ads7828_show_in, NULL, 0); @@ -130,6 +100,16 @@ static struct attribute *ads7828_attrs[] = { ATTRIBUTE_GROUPS(ads7828); +static const struct regmap_config ads2828_regmap_config = { + .reg_bits = 8, + .val_bits = 16, +}; + +static const struct regmap_config ads2830_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + static int ads7828_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -137,42 +117,40 @@ static int ads7828_probe(struct i2c_client *client, struct ads7828_platform_data *pdata = dev_get_platdata(dev); struct ads7828_data *data; struct device *hwmon_dev; + unsigned int vref_mv = ADS7828_INT_VREF_MV; + bool diff_input = false; + bool ext_vref = false; data = devm_kzalloc(dev, sizeof(struct ads7828_data), GFP_KERNEL); if (!data) return -ENOMEM; if (pdata) { - data->diff_input = pdata->diff_input; - data->ext_vref = pdata->ext_vref; - if (data->ext_vref) - data->vref_mv = pdata->vref_mv; + diff_input = pdata->diff_input; + ext_vref = pdata->ext_vref; + if (ext_vref && pdata->vref_mv) + vref_mv = pdata->vref_mv; } - /* Bound Vref with min/max values if it was provided */ - if (data->vref_mv) - data->vref_mv = clamp_val(data->vref_mv, - ADS7828_EXT_VREF_MV_MIN, - ADS7828_EXT_VREF_MV_MAX); - else - data->vref_mv = ADS7828_INT_VREF_MV; + /* Bound Vref with min/max values */ + vref_mv = clamp_val(vref_mv, ADS7828_EXT_VREF_MV_MIN, + ADS7828_EXT_VREF_MV_MAX); /* ADS7828 uses 12-bit samples, while ADS7830 is 8-bit */ if (id->driver_data == ads7828) { - data->lsb_resol = DIV_ROUND_CLOSEST(data->vref_mv * 1000, 4096); - data->read_channel = i2c_smbus_read_word_swapped; + data->lsb_resol = DIV_ROUND_CLOSEST(vref_mv * 1000, 4096); + data->regmap = devm_regmap_init_i2c(client, + &ads2828_regmap_config); } else { - data->lsb_resol = DIV_ROUND_CLOSEST(data->vref_mv * 1000, 256); - data->read_channel = i2c_smbus_read_byte_data; + data->lsb_resol = DIV_ROUND_CLOSEST(vref_mv * 1000, 256); + data->regmap = devm_regmap_init_i2c(client, + &ads2830_regmap_config); } - data->cmd_byte = data->ext_vref ? ADS7828_CMD_PD1 : ADS7828_CMD_PD3; - if (!data->diff_input) + data->cmd_byte = ext_vref ? ADS7828_CMD_PD1 : ADS7828_CMD_PD3; + if (!diff_input) data->cmd_byte |= ADS7828_CMD_SD_SE; - data->client = client; - mutex_init(&data->update_lock); - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, data, ads7828_groups); diff --git a/drivers/hwmon/i5500_temp.c b/drivers/hwmon/i5500_temp.c new file mode 100644 index 000000000000..3e3ccbf18b4e --- /dev/null +++ b/drivers/hwmon/i5500_temp.c @@ -0,0 +1,149 @@ +/* + * i5500_temp - Driver for Intel 5500/5520/X58 chipset thermal sensor + * + * Copyright (C) 2012, 2014 Jean Delvare <jdelvare@suse.de> + * + * 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. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/device.h> +#include <linux/pci.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> + +/* Register definitions from datasheet */ +#define REG_TSTHRCATA 0xE2 +#define REG_TSCTRL 0xE8 +#define REG_TSTHRRPEX 0xEB +#define REG_TSTHRLO 0xEC +#define REG_TSTHRHI 0xEE +#define REG_CTHINT 0xF0 +#define REG_TSFSC 0xF3 +#define REG_CTSTS 0xF4 +#define REG_TSTHRRQPI 0xF5 +#define REG_CTCTRL 0xF7 +#define REG_TSTIMER 0xF8 + +/* + * Sysfs stuff + */ + +/* Sensor resolution : 0.5 degree C */ +static ssize_t show_temp(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev->parent); + long temp; + u16 tsthrhi; + s8 tsfsc; + + pci_read_config_word(pdev, REG_TSTHRHI, &tsthrhi); + pci_read_config_byte(pdev, REG_TSFSC, &tsfsc); + temp = ((long)tsthrhi - tsfsc) * 500; + + return sprintf(buf, "%ld\n", temp); +} + +static ssize_t show_thresh(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev->parent); + int reg = to_sensor_dev_attr(devattr)->index; + long temp; + u16 tsthr; + + pci_read_config_word(pdev, reg, &tsthr); + temp = tsthr * 500; + + return sprintf(buf, "%ld\n", temp); +} + +static ssize_t show_alarm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev->parent); + int nr = to_sensor_dev_attr(devattr)->index; + u8 ctsts; + + pci_read_config_byte(pdev, REG_CTSTS, &ctsts); + return sprintf(buf, "%u\n", (unsigned int)ctsts & (1 << nr)); +} + +static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_thresh, NULL, 0xE2); +static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_thresh, NULL, 0xEC); +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_thresh, NULL, 0xEE); +static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 1); + +static struct attribute *i5500_temp_attrs[] = { + &dev_attr_temp1_input.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + NULL +}; + +ATTRIBUTE_GROUPS(i5500_temp); + +static const struct pci_device_id i5500_temp_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x3438) }, + { 0 }, +}; + +MODULE_DEVICE_TABLE(pci, i5500_temp_ids); + +static int i5500_temp_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int err; + struct device *hwmon_dev; + u32 tstimer; + s8 tsfsc; + + err = pci_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "Failed to enable device\n"); + return err; + } + + pci_read_config_byte(pdev, REG_TSFSC, &tsfsc); + pci_read_config_dword(pdev, REG_TSTIMER, &tstimer); + if (tsfsc == 0x7F && tstimer == 0x07D30D40) { + dev_notice(&pdev->dev, "Sensor seems to be disabled\n"); + return -ENODEV; + } + + hwmon_dev = devm_hwmon_device_register_with_groups(&pdev->dev, + "intel5500", NULL, + i5500_temp_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static struct pci_driver i5500_temp_driver = { + .name = "i5500_temp", + .id_table = i5500_temp_ids, + .probe = i5500_temp_probe, +}; + +module_pci_driver(i5500_temp_driver); + +MODULE_AUTHOR("Jean Delvare <jdelvare@suse.de>"); +MODULE_DESCRIPTION("Intel 5500/5520/X58 chipset thermal sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index e01feba909c3..d1542b7d4bc3 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -35,6 +35,7 @@ #include <linux/hwmon-sysfs.h> #include <linux/jiffies.h> #include <linux/of.h> +#include <linux/delay.h> #include <linux/platform_data/ina2xx.h> @@ -51,7 +52,6 @@ #define INA226_ALERT_LIMIT 0x07 #define INA226_DIE_ID 0xFF - /* register count */ #define INA219_REGISTERS 6 #define INA226_REGISTERS 8 @@ -64,6 +64,24 @@ /* worst case is 68.10 ms (~14.6Hz, ina219) */ #define INA2XX_CONVERSION_RATE 15 +#define INA2XX_MAX_DELAY 69 /* worst case delay in ms */ + +#define INA2XX_RSHUNT_DEFAULT 10000 + +/* bit mask for reading the averaging setting in the configuration register */ +#define INA226_AVG_RD_MASK 0x0E00 + +#define INA226_READ_AVG(reg) (((reg) & INA226_AVG_RD_MASK) >> 9) +#define INA226_SHIFT_AVG(val) ((val) << 9) + +/* common attrs, ina226 attrs and NULL */ +#define INA2XX_MAX_ATTRIBUTE_GROUPS 3 + +/* + * Both bus voltage and shunt voltage conversion times for ina226 are set + * to 0b0100 on POR, which translates to 2200 microseconds in total. + */ +#define INA226_TOTAL_CONV_TIME_DEFAULT 2200 enum ina2xx_ids { ina219, ina226 }; @@ -81,11 +99,16 @@ struct ina2xx_data { struct i2c_client *client; const struct ina2xx_config *config; + long rshunt; + u16 curr_config; + struct mutex update_lock; bool valid; unsigned long last_updated; + int update_interval; /* in jiffies */ int kind; + const struct attribute_group *groups[INA2XX_MAX_ATTRIBUTE_GROUPS]; u16 regs[INA2XX_MAX_REGISTERS]; }; @@ -110,34 +133,156 @@ static const struct ina2xx_config ina2xx_config[] = { }, }; -static struct ina2xx_data *ina2xx_update_device(struct device *dev) +/* + * Available averaging rates for ina226. The indices correspond with + * the bit values expected by the chip (according to the ina226 datasheet, + * table 3 AVG bit settings, found at + * http://www.ti.com/lit/ds/symlink/ina226.pdf. + */ +static const int ina226_avg_tab[] = { 1, 4, 16, 64, 128, 256, 512, 1024 }; + +static int ina226_avg_bits(int avg) +{ + int i; + + /* Get the closest average from the tab. */ + for (i = 0; i < ARRAY_SIZE(ina226_avg_tab) - 1; i++) { + if (avg <= (ina226_avg_tab[i] + ina226_avg_tab[i + 1]) / 2) + break; + } + + return i; /* Return 0b0111 for values greater than 1024. */ +} + +static int ina226_reg_to_interval(u16 config) +{ + int avg = ina226_avg_tab[INA226_READ_AVG(config)]; + + /* + * Multiply the total conversion time by the number of averages. + * Return the result in milliseconds. + */ + return DIV_ROUND_CLOSEST(avg * INA226_TOTAL_CONV_TIME_DEFAULT, 1000); +} + +static u16 ina226_interval_to_reg(int interval, u16 config) +{ + int avg, avg_bits; + + avg = DIV_ROUND_CLOSEST(interval * 1000, + INA226_TOTAL_CONV_TIME_DEFAULT); + avg_bits = ina226_avg_bits(avg); + + return (config & ~INA226_AVG_RD_MASK) | INA226_SHIFT_AVG(avg_bits); +} + +static void ina226_set_update_interval(struct ina2xx_data *data) +{ + int ms; + + ms = ina226_reg_to_interval(data->curr_config); + data->update_interval = msecs_to_jiffies(ms); +} + +static int ina2xx_calibrate(struct ina2xx_data *data) +{ + u16 val = DIV_ROUND_CLOSEST(data->config->calibration_factor, + data->rshunt); + + return i2c_smbus_write_word_swapped(data->client, + INA2XX_CALIBRATION, val); +} + +/* + * Initialize the configuration and calibration registers. + */ +static int ina2xx_init(struct ina2xx_data *data) { - struct ina2xx_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; - struct ina2xx_data *ret = data; + int ret; - mutex_lock(&data->update_lock); + /* device configuration */ + ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG, + data->curr_config); + if (ret < 0) + return ret; - if (time_after(jiffies, data->last_updated + - HZ / INA2XX_CONVERSION_RATE) || !data->valid) { + /* + * Set current LSB to 1mA, shunt is in uOhms + * (equation 13 in datasheet). + */ + return ina2xx_calibrate(data); +} - int i; +static int ina2xx_do_update(struct device *dev) +{ + struct ina2xx_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int i, rv, retry; - dev_dbg(&client->dev, "Starting ina2xx update\n"); + dev_dbg(&client->dev, "Starting ina2xx update\n"); + for (retry = 5; retry; retry--) { /* Read all registers */ for (i = 0; i < data->config->registers; i++) { - int rv = i2c_smbus_read_word_swapped(client, i); - if (rv < 0) { - ret = ERR_PTR(rv); - goto abort; - } + rv = i2c_smbus_read_word_swapped(client, i); + if (rv < 0) + return rv; data->regs[i] = rv; } + + /* + * If the current value in the calibration register is 0, the + * power and current registers will also remain at 0. In case + * the chip has been reset let's check the calibration + * register and reinitialize if needed. + */ + if (data->regs[INA2XX_CALIBRATION] == 0) { + dev_warn(dev, "chip not calibrated, reinitializing\n"); + + rv = ina2xx_init(data); + if (rv < 0) + return rv; + + /* + * Let's make sure the power and current registers + * have been updated before trying again. + */ + msleep(INA2XX_MAX_DELAY); + continue; + } + data->last_updated = jiffies; data->valid = 1; + + return 0; } -abort: + + /* + * If we're here then although all write operations succeeded, the + * chip still returns 0 in the calibration register. Nothing more we + * can do here. + */ + dev_err(dev, "unable to reinitialize the chip\n"); + return -ENODEV; +} + +static struct ina2xx_data *ina2xx_update_device(struct device *dev) +{ + struct ina2xx_data *data = dev_get_drvdata(dev); + struct ina2xx_data *ret = data; + unsigned long after; + int rv; + + mutex_lock(&data->update_lock); + + after = data->last_updated + data->update_interval; + if (time_after(jiffies, after) || !data->valid) { + rv = ina2xx_do_update(dev); + if (rv < 0) + ret = ERR_PTR(rv); + } + mutex_unlock(&data->update_lock); return ret; } @@ -164,6 +309,10 @@ static int ina2xx_get_value(struct ina2xx_data *data, u8 reg) /* signed register, LSB=1mA (selected), in mA */ val = (s16)data->regs[reg]; break; + case INA2XX_CALIBRATION: + val = DIV_ROUND_CLOSEST(data->config->calibration_factor, + data->regs[reg]); + break; default: /* programmer goofed */ WARN_ON_ONCE(1); @@ -187,6 +336,85 @@ static ssize_t ina2xx_show_value(struct device *dev, ina2xx_get_value(data, attr->index)); } +static ssize_t ina2xx_set_shunt(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct ina2xx_data *data = ina2xx_update_device(dev); + unsigned long val; + int status; + + if (IS_ERR(data)) + return PTR_ERR(data); + + status = kstrtoul(buf, 10, &val); + if (status < 0) + return status; + + if (val == 0 || + /* Values greater than the calibration factor make no sense. */ + val > data->config->calibration_factor) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->rshunt = val; + status = ina2xx_calibrate(data); + mutex_unlock(&data->update_lock); + if (status < 0) + return status; + + return count; +} + +static ssize_t ina226_set_interval(struct device *dev, + struct device_attribute *da, + const char *buf, size_t count) +{ + struct ina2xx_data *data = dev_get_drvdata(dev); + unsigned long val; + int status; + + status = kstrtoul(buf, 10, &val); + if (status < 0) + return status; + + if (val > INT_MAX || val == 0) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->curr_config = ina226_interval_to_reg(val, + data->regs[INA2XX_CONFIG]); + status = i2c_smbus_write_word_swapped(data->client, + INA2XX_CONFIG, + data->curr_config); + + ina226_set_update_interval(data); + /* Make sure the next access re-reads all registers. */ + data->valid = 0; + mutex_unlock(&data->update_lock); + if (status < 0) + return status; + + return count; +} + +static ssize_t ina226_show_interval(struct device *dev, + struct device_attribute *da, char *buf) +{ + struct ina2xx_data *data = ina2xx_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + /* + * We don't use data->update_interval here as we want to display + * the actual interval used by the chip and jiffies_to_msecs() + * doesn't seem to be accurate enough. + */ + return snprintf(buf, PAGE_SIZE, "%d\n", + ina226_reg_to_interval(data->regs[INA2XX_CONFIG])); +} + /* shunt voltage */ static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, ina2xx_show_value, NULL, INA2XX_SHUNT_VOLTAGE); @@ -203,15 +431,37 @@ static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ina2xx_show_value, NULL, static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ina2xx_show_value, NULL, INA2XX_POWER); +/* shunt resistance */ +static SENSOR_DEVICE_ATTR(shunt_resistor, S_IRUGO | S_IWUSR, + ina2xx_show_value, ina2xx_set_shunt, + INA2XX_CALIBRATION); + +/* update interval (ina226 only) */ +static SENSOR_DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, + ina226_show_interval, ina226_set_interval, 0); + /* pointers to created device attributes */ static struct attribute *ina2xx_attrs[] = { &sensor_dev_attr_in0_input.dev_attr.attr, &sensor_dev_attr_in1_input.dev_attr.attr, &sensor_dev_attr_curr1_input.dev_attr.attr, &sensor_dev_attr_power1_input.dev_attr.attr, + &sensor_dev_attr_shunt_resistor.dev_attr.attr, NULL, }; -ATTRIBUTE_GROUPS(ina2xx); + +static const struct attribute_group ina2xx_group = { + .attrs = ina2xx_attrs, +}; + +static struct attribute *ina226_attrs[] = { + &sensor_dev_attr_update_interval.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ina226_group = { + .attrs = ina226_attrs, +}; static int ina2xx_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -221,9 +471,8 @@ static int ina2xx_probe(struct i2c_client *client, struct device *dev = &client->dev; struct ina2xx_data *data; struct device *hwmon_dev; - long shunt = 10000; /* default shunt value 10mOhms */ u32 val; - int ret; + int ret, group = 0; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; @@ -234,50 +483,52 @@ static int ina2xx_probe(struct i2c_client *client, if (dev_get_platdata(dev)) { pdata = dev_get_platdata(dev); - shunt = pdata->shunt_uohms; + data->rshunt = pdata->shunt_uohms; } else if (!of_property_read_u32(dev->of_node, "shunt-resistor", &val)) { - shunt = val; + data->rshunt = val; + } else { + data->rshunt = INA2XX_RSHUNT_DEFAULT; } - if (shunt <= 0) - return -ENODEV; - /* set the device type */ data->kind = id->driver_data; data->config = &ina2xx_config[data->kind]; - - /* device configuration */ - ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG, - data->config->config_default); - if (ret < 0) { - dev_err(dev, - "error writing to the config register: %d", ret); - return -ENODEV; - } + data->curr_config = data->config->config_default; + data->client = client; /* - * Set current LSB to 1mA, shunt is in uOhms - * (equation 13 in datasheet). + * Ina226 has a variable update_interval. For ina219 we + * use a constant value. */ - ret = i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION, - data->config->calibration_factor / shunt); + if (data->kind == ina226) + ina226_set_update_interval(data); + else + data->update_interval = HZ / INA2XX_CONVERSION_RATE; + + if (data->rshunt <= 0 || + data->rshunt > data->config->calibration_factor) + return -ENODEV; + + ret = ina2xx_init(data); if (ret < 0) { - dev_err(dev, - "error writing to the calibration register: %d", ret); + dev_err(dev, "error configuring the device: %d\n", ret); return -ENODEV; } - data->client = client; mutex_init(&data->update_lock); + data->groups[group++] = &ina2xx_group; + if (data->kind == ina226) + data->groups[group++] = &ina226_group; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - data, ina2xx_groups); + data, data->groups); if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); dev_info(dev, "power monitor %s (Rshunt = %li uOhm)\n", - id->name, shunt); + id->name, data->rshunt); return 0; } @@ -287,6 +538,7 @@ static const struct i2c_device_id ina2xx_id[] = { { "ina220", ina219 }, { "ina226", ina226 }, { "ina230", ina226 }, + { "ina231", ina226 }, { } }; MODULE_DEVICE_TABLE(i2c, ina2xx_id); diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 388f8bcd898e..996bdfd5cf25 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -201,7 +201,7 @@ struct jc42_data { #define JC42_TEMP_MIN 0 #define JC42_TEMP_MAX 125000 -static u16 jc42_temp_to_reg(int temp, bool extended) +static u16 jc42_temp_to_reg(long temp, bool extended) { int ntemp = clamp_val(temp, extended ? JC42_TEMP_MIN_EXTENDED : @@ -213,11 +213,7 @@ static u16 jc42_temp_to_reg(int temp, bool extended) static int jc42_temp_from_reg(s16 reg) { - reg &= 0x1fff; - - /* sign extend register */ - if (reg & 0x1000) - reg |= 0xf000; + reg = sign_extend32(reg, 12); /* convert from 0.0625 to 0.001 resolution */ return reg * 125 / 2; @@ -308,15 +304,18 @@ static ssize_t set_temp_crit_hyst(struct device *dev, const char *buf, size_t count) { struct jc42_data *data = dev_get_drvdata(dev); - unsigned long val; + long val; int diff, hyst; int err; int ret = count; - if (kstrtoul(buf, 10, &val) < 0) + if (kstrtol(buf, 10, &val) < 0) return -EINVAL; + val = clamp_val(val, (data->extended ? JC42_TEMP_MIN_EXTENDED : + JC42_TEMP_MIN) - 6000, JC42_TEMP_MAX); diff = jc42_temp_from_reg(data->temp[t_crit]) - val; + hyst = 0; if (diff > 0) { if (diff < 2250) diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c index ec5678289e4a..55765790907b 100644 --- a/drivers/hwmon/nct7802.c +++ b/drivers/hwmon/nct7802.c @@ -779,7 +779,7 @@ static bool nct7802_regmap_is_volatile(struct device *dev, unsigned int reg) return reg != REG_BANK && reg <= 0x20; } -static struct regmap_config nct7802_regmap_config = { +static const struct regmap_config nct7802_regmap_config = { .reg_bits = 8, .val_bits = 8, .cache_type = REGCACHE_RBTREE, diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c index ba9f478f64ee..9da2735f1424 100644 --- a/drivers/hwmon/tmp102.c +++ b/drivers/hwmon/tmp102.c @@ -253,7 +253,7 @@ static int tmp102_remove(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int tmp102_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -279,17 +279,10 @@ static int tmp102_resume(struct device *dev) config &= ~TMP102_CONF_SD; return i2c_smbus_write_word_swapped(client, TMP102_CONF_REG, config); } - -static const struct dev_pm_ops tmp102_dev_pm_ops = { - .suspend = tmp102_suspend, - .resume = tmp102_resume, -}; - -#define TMP102_DEV_PM_OPS (&tmp102_dev_pm_ops) -#else -#define TMP102_DEV_PM_OPS NULL #endif /* CONFIG_PM */ +static SIMPLE_DEV_PM_OPS(tmp102_dev_pm_ops, tmp102_suspend, tmp102_resume); + static const struct i2c_device_id tmp102_id[] = { { "tmp102", 0 }, { } @@ -298,7 +291,7 @@ MODULE_DEVICE_TABLE(i2c, tmp102_id); static struct i2c_driver tmp102_driver = { .driver.name = DRIVER_NAME, - .driver.pm = TMP102_DEV_PM_OPS, + .driver.pm = &tmp102_dev_pm_ops, .probe = tmp102_probe, .remove = tmp102_remove, .id_table = tmp102_id, diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 31e8308ba899..ab838d9e28b6 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -881,6 +881,7 @@ config I2C_XLR config I2C_RCAR tristate "Renesas R-Car I2C Controller" depends on ARCH_SHMOBILE || COMPILE_TEST + select I2C_SLAVE help If you say yes to this option, support will be included for the R-Car I2C controller. diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index bff20a589621..958c8db4ec30 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -785,14 +785,16 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap, int ret; pm_runtime_get_sync(&adap->dev); - clk_prepare_enable(i2c->clk); + ret = clk_enable(i2c->clk); + if (ret) + return ret; for (retry = 0; retry < adap->retries; retry++) { ret = s3c24xx_i2c_doxfer(i2c, msgs, num); if (ret != -EAGAIN) { - clk_disable_unprepare(i2c->clk); + clk_disable(i2c->clk); pm_runtime_put(&adap->dev); return ret; } @@ -802,7 +804,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap, udelay(100); } - clk_disable_unprepare(i2c->clk); + clk_disable(i2c->clk); pm_runtime_put(&adap->dev); return -EREMOTEIO; } @@ -1197,7 +1199,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) clk_prepare_enable(i2c->clk); ret = s3c24xx_i2c_init(i2c); - clk_disable_unprepare(i2c->clk); + clk_disable(i2c->clk); if (ret != 0) { dev_err(&pdev->dev, "I2C controller init failed\n"); return ret; @@ -1210,6 +1212,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) i2c->irq = ret = platform_get_irq(pdev, 0); if (ret <= 0) { dev_err(&pdev->dev, "cannot find IRQ\n"); + clk_unprepare(i2c->clk); return ret; } @@ -1218,6 +1221,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) if (ret != 0) { dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); + clk_unprepare(i2c->clk); return ret; } } @@ -1225,6 +1229,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) ret = s3c24xx_i2c_register_cpufreq(i2c); if (ret < 0) { dev_err(&pdev->dev, "failed to register cpufreq notifier\n"); + clk_unprepare(i2c->clk); return ret; } @@ -1241,6 +1246,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "failed to add bus to i2c core\n"); s3c24xx_i2c_deregister_cpufreq(i2c); + clk_unprepare(i2c->clk); return ret; } @@ -1262,6 +1268,8 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev) { struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); + clk_unprepare(i2c->clk); + pm_runtime_disable(&i2c->adap.dev); pm_runtime_disable(&pdev->dev); @@ -1293,13 +1301,16 @@ static int s3c24xx_i2c_resume_noirq(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); + int ret; if (!IS_ERR(i2c->sysreg)) regmap_write(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, i2c->sys_i2c_cfg); - clk_prepare_enable(i2c->clk); + ret = clk_enable(i2c->clk); + if (ret) + return ret; s3c24xx_i2c_init(i2c); - clk_disable_unprepare(i2c->clk); + clk_disable(i2c->clk); i2c->suspended = 0; return 0; diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 440d5dbc8b5f..007818b3e174 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -139,6 +139,7 @@ struct sh_mobile_i2c_data { int pos; int sr; bool send_stop; + bool stop_after_dma; struct resource *res; struct dma_chan *dma_tx; @@ -407,7 +408,7 @@ static int sh_mobile_i2c_isr_tx(struct sh_mobile_i2c_data *pd) if (pd->pos == pd->msg->len) { /* Send stop if we haven't yet (DMA case) */ - if (pd->send_stop && (iic_rd(pd, ICCR) & ICCR_BBSY)) + if (pd->send_stop && pd->stop_after_dma) i2c_op(pd, OP_TX_STOP, 0); return 1; } @@ -449,6 +450,13 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) real_pos = pd->pos - 2; if (pd->pos == pd->msg->len) { + if (pd->stop_after_dma) { + /* Simulate PIO end condition after DMA transfer */ + i2c_op(pd, OP_RX_STOP, 0); + pd->pos++; + break; + } + if (real_pos < 0) { i2c_op(pd, OP_RX_STOP, 0); break; @@ -536,6 +544,7 @@ static void sh_mobile_i2c_dma_callback(void *data) sh_mobile_i2c_dma_unmap(pd); pd->pos = pd->msg->len; + pd->stop_after_dma = true; iic_set_clr(pd, ICIC, 0, ICIC_TDMAE | ICIC_RDMAE); } @@ -726,6 +735,7 @@ static int sh_mobile_i2c_xfer(struct i2c_adapter *adapter, bool do_start = pd->send_stop || !i; msg = &msgs[i]; pd->send_stop = i == num - 1 || msg->flags & I2C_M_STOP; + pd->stop_after_dma = false; err = start_ch(pd, msg, do_start); if (err) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 39d25a8cb1ad..e9eae57a2b50 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -2972,6 +2972,7 @@ trace: } EXPORT_SYMBOL(i2c_smbus_xfer); +#if IS_ENABLED(CONFIG_I2C_SLAVE) int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb) { int ret; @@ -3019,6 +3020,7 @@ int i2c_slave_unregister(struct i2c_client *client) return ret; } EXPORT_SYMBOL_GPL(i2c_slave_unregister); +#endif MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>"); MODULE_DESCRIPTION("I2C-Bus main module"); diff --git a/drivers/i2c/i2c-slave-eeprom.c b/drivers/i2c/i2c-slave-eeprom.c index 6631400b5f02..cf9b09db092f 100644 --- a/drivers/i2c/i2c-slave-eeprom.c +++ b/drivers/i2c/i2c-slave-eeprom.c @@ -74,7 +74,7 @@ static ssize_t i2c_slave_eeprom_bin_read(struct file *filp, struct kobject *kobj struct eeprom_data *eeprom; unsigned long flags; - if (off + count >= attr->size) + if (off + count > attr->size) return -EFBIG; eeprom = dev_get_drvdata(container_of(kobj, struct device, kobj)); @@ -92,7 +92,7 @@ static ssize_t i2c_slave_eeprom_bin_write(struct file *filp, struct kobject *kob struct eeprom_data *eeprom; unsigned long flags; - if (off + count >= attr->size) + if (off + count > attr->size) return -EFBIG; eeprom = dev_get_drvdata(container_of(kobj, struct device, kobj)); diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index e37412da15f5..b99de00e57b8 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -143,9 +143,15 @@ static int ad799x_write_config(struct ad799x_state *st, u16 val) case ad7998: return i2c_smbus_write_word_swapped(st->client, AD7998_CONF_REG, val); - default: + case ad7992: + case ad7993: + case ad7994: return i2c_smbus_write_byte_data(st->client, AD7998_CONF_REG, val); + default: + /* Will be written when doing a conversion */ + st->config = val; + return 0; } } @@ -155,8 +161,13 @@ static int ad799x_read_config(struct ad799x_state *st) case ad7997: case ad7998: return i2c_smbus_read_word_swapped(st->client, AD7998_CONF_REG); - default: + case ad7992: + case ad7993: + case ad7994: return i2c_smbus_read_byte_data(st->client, AD7998_CONF_REG); + default: + /* No readback support */ + return st->config; } } diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index b730864731e8..adba23246474 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -86,19 +86,18 @@ static void tiadc_step_config(struct iio_dev *indio_dev) { struct tiadc_device *adc_dev = iio_priv(indio_dev); unsigned int stepconfig; - int i, steps; + int i, steps = 0; /* * There are 16 configurable steps and 8 analog input * lines available which are shared between Touchscreen and ADC. * - * Steps backwards i.e. from 16 towards 0 are used by ADC + * Steps forwards i.e. from 0 towards 16 are used by ADC * depending on number of input lines needed. * Channel would represent which analog input * needs to be given to ADC to digitalize data. */ - steps = TOTAL_STEPS - adc_dev->channels; if (iio_buffer_enabled(indio_dev)) stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1 | STEPCONFIG_MODE_SWCNT; diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 866fe904cba2..90c8cb727cc7 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -449,6 +449,9 @@ static int iio_channel_read(struct iio_channel *chan, int *val, int *val2, if (val2 == NULL) val2 = &unused; + if(!iio_channel_has_info(chan->channel, info)) + return -EINVAL; + if (chan->indio_dev->info->read_raw_multi) { ret = chan->indio_dev->info->read_raw_multi(chan->indio_dev, chan->channel, INDIO_MAX_RAW_ELEMENTS, diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h index b716b0815644..643c08a025a5 100644 --- a/drivers/infiniband/core/uverbs.h +++ b/drivers/infiniband/core/uverbs.h @@ -258,6 +258,5 @@ IB_UVERBS_DECLARE_CMD(close_xrcd); IB_UVERBS_DECLARE_EX_CMD(create_flow); IB_UVERBS_DECLARE_EX_CMD(destroy_flow); -IB_UVERBS_DECLARE_EX_CMD(query_device); #endif /* UVERBS_H */ diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index 532d8eba8b02..b7943ff16ed3 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -400,52 +400,6 @@ err: return ret; } -static void copy_query_dev_fields(struct ib_uverbs_file *file, - struct ib_uverbs_query_device_resp *resp, - struct ib_device_attr *attr) -{ - resp->fw_ver = attr->fw_ver; - resp->node_guid = file->device->ib_dev->node_guid; - resp->sys_image_guid = attr->sys_image_guid; - resp->max_mr_size = attr->max_mr_size; - resp->page_size_cap = attr->page_size_cap; - resp->vendor_id = attr->vendor_id; - resp->vendor_part_id = attr->vendor_part_id; - resp->hw_ver = attr->hw_ver; - resp->max_qp = attr->max_qp; - resp->max_qp_wr = attr->max_qp_wr; - resp->device_cap_flags = attr->device_cap_flags; - resp->max_sge = attr->max_sge; - resp->max_sge_rd = attr->max_sge_rd; - resp->max_cq = attr->max_cq; - resp->max_cqe = attr->max_cqe; - resp->max_mr = attr->max_mr; - resp->max_pd = attr->max_pd; - resp->max_qp_rd_atom = attr->max_qp_rd_atom; - resp->max_ee_rd_atom = attr->max_ee_rd_atom; - resp->max_res_rd_atom = attr->max_res_rd_atom; - resp->max_qp_init_rd_atom = attr->max_qp_init_rd_atom; - resp->max_ee_init_rd_atom = attr->max_ee_init_rd_atom; - resp->atomic_cap = attr->atomic_cap; - resp->max_ee = attr->max_ee; - resp->max_rdd = attr->max_rdd; - resp->max_mw = attr->max_mw; - resp->max_raw_ipv6_qp = attr->max_raw_ipv6_qp; - resp->max_raw_ethy_qp = attr->max_raw_ethy_qp; - resp->max_mcast_grp = attr->max_mcast_grp; - resp->max_mcast_qp_attach = attr->max_mcast_qp_attach; - resp->max_total_mcast_qp_attach = attr->max_total_mcast_qp_attach; - resp->max_ah = attr->max_ah; - resp->max_fmr = attr->max_fmr; - resp->max_map_per_fmr = attr->max_map_per_fmr; - resp->max_srq = attr->max_srq; - resp->max_srq_wr = attr->max_srq_wr; - resp->max_srq_sge = attr->max_srq_sge; - resp->max_pkeys = attr->max_pkeys; - resp->local_ca_ack_delay = attr->local_ca_ack_delay; - resp->phys_port_cnt = file->device->ib_dev->phys_port_cnt; -} - ssize_t ib_uverbs_query_device(struct ib_uverbs_file *file, const char __user *buf, int in_len, int out_len) @@ -466,7 +420,47 @@ ssize_t ib_uverbs_query_device(struct ib_uverbs_file *file, return ret; memset(&resp, 0, sizeof resp); - copy_query_dev_fields(file, &resp, &attr); + + resp.fw_ver = attr.fw_ver; + resp.node_guid = file->device->ib_dev->node_guid; + resp.sys_image_guid = attr.sys_image_guid; + resp.max_mr_size = attr.max_mr_size; + resp.page_size_cap = attr.page_size_cap; + resp.vendor_id = attr.vendor_id; + resp.vendor_part_id = attr.vendor_part_id; + resp.hw_ver = attr.hw_ver; + resp.max_qp = attr.max_qp; + resp.max_qp_wr = attr.max_qp_wr; + resp.device_cap_flags = attr.device_cap_flags; + resp.max_sge = attr.max_sge; + resp.max_sge_rd = attr.max_sge_rd; + resp.max_cq = attr.max_cq; + resp.max_cqe = attr.max_cqe; + resp.max_mr = attr.max_mr; + resp.max_pd = attr.max_pd; + resp.max_qp_rd_atom = attr.max_qp_rd_atom; + resp.max_ee_rd_atom = attr.max_ee_rd_atom; + resp.max_res_rd_atom = attr.max_res_rd_atom; + resp.max_qp_init_rd_atom = attr.max_qp_init_rd_atom; + resp.max_ee_init_rd_atom = attr.max_ee_init_rd_atom; + resp.atomic_cap = attr.atomic_cap; + resp.max_ee = attr.max_ee; + resp.max_rdd = attr.max_rdd; + resp.max_mw = attr.max_mw; + resp.max_raw_ipv6_qp = attr.max_raw_ipv6_qp; + resp.max_raw_ethy_qp = attr.max_raw_ethy_qp; + resp.max_mcast_grp = attr.max_mcast_grp; + resp.max_mcast_qp_attach = attr.max_mcast_qp_attach; + resp.max_total_mcast_qp_attach = attr.max_total_mcast_qp_attach; + resp.max_ah = attr.max_ah; + resp.max_fmr = attr.max_fmr; + resp.max_map_per_fmr = attr.max_map_per_fmr; + resp.max_srq = attr.max_srq; + resp.max_srq_wr = attr.max_srq_wr; + resp.max_srq_sge = attr.max_srq_sge; + resp.max_pkeys = attr.max_pkeys; + resp.local_ca_ack_delay = attr.local_ca_ack_delay; + resp.phys_port_cnt = file->device->ib_dev->phys_port_cnt; if (copy_to_user((void __user *) (unsigned long) cmd.response, &resp, sizeof resp)) @@ -3293,52 +3287,3 @@ ssize_t ib_uverbs_destroy_srq(struct ib_uverbs_file *file, return ret ? ret : in_len; } - -int ib_uverbs_ex_query_device(struct ib_uverbs_file *file, - struct ib_udata *ucore, - struct ib_udata *uhw) -{ - struct ib_uverbs_ex_query_device_resp resp; - struct ib_uverbs_ex_query_device cmd; - struct ib_device_attr attr; - struct ib_device *device; - int err; - - device = file->device->ib_dev; - if (ucore->inlen < sizeof(cmd)) - return -EINVAL; - - err = ib_copy_from_udata(&cmd, ucore, sizeof(cmd)); - if (err) - return err; - - if (cmd.reserved) - return -EINVAL; - - err = device->query_device(device, &attr); - if (err) - return err; - - memset(&resp, 0, sizeof(resp)); - copy_query_dev_fields(file, &resp.base, &attr); - resp.comp_mask = 0; - -#ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING - if (cmd.comp_mask & IB_USER_VERBS_EX_QUERY_DEVICE_ODP) { - resp.odp_caps.general_caps = attr.odp_caps.general_caps; - resp.odp_caps.per_transport_caps.rc_odp_caps = - attr.odp_caps.per_transport_caps.rc_odp_caps; - resp.odp_caps.per_transport_caps.uc_odp_caps = - attr.odp_caps.per_transport_caps.uc_odp_caps; - resp.odp_caps.per_transport_caps.ud_odp_caps = - attr.odp_caps.per_transport_caps.ud_odp_caps; - resp.comp_mask |= IB_USER_VERBS_EX_QUERY_DEVICE_ODP; - } -#endif - - err = ib_copy_to_udata(ucore, &resp, sizeof(resp)); - if (err) - return err; - - return 0; -} diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c index e6c23b9eab33..5db1a8cc388d 100644 --- a/drivers/infiniband/core/uverbs_main.c +++ b/drivers/infiniband/core/uverbs_main.c @@ -123,7 +123,6 @@ static int (*uverbs_ex_cmd_table[])(struct ib_uverbs_file *file, struct ib_udata *uhw) = { [IB_USER_VERBS_EX_CMD_CREATE_FLOW] = ib_uverbs_ex_create_flow, [IB_USER_VERBS_EX_CMD_DESTROY_FLOW] = ib_uverbs_ex_destroy_flow, - [IB_USER_VERBS_EX_CMD_QUERY_DEVICE] = ib_uverbs_ex_query_device }; static void ib_uverbs_add_one(struct ib_device *device); diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 9edc200b311d..57176ddd4c50 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -235,19 +235,19 @@ static void release_tid(struct c4iw_rdev *rdev, u32 hwtid, struct sk_buff *skb) static void set_emss(struct c4iw_ep *ep, u16 opt) { - ep->emss = ep->com.dev->rdev.lldi.mtus[GET_TCPOPT_MSS(opt)] - + ep->emss = ep->com.dev->rdev.lldi.mtus[TCPOPT_MSS_G(opt)] - ((AF_INET == ep->com.remote_addr.ss_family) ? sizeof(struct iphdr) : sizeof(struct ipv6hdr)) - sizeof(struct tcphdr); ep->mss = ep->emss; - if (GET_TCPOPT_TSTAMP(opt)) + if (TCPOPT_TSTAMP_G(opt)) ep->emss -= round_up(TCPOLEN_TIMESTAMP, 4); if (ep->emss < 128) ep->emss = 128; if (ep->emss & 7) PDBG("Warning: misaligned mtu idx %u mss %u emss=%u\n", - GET_TCPOPT_MSS(opt), ep->mss, ep->emss); - PDBG("%s mss_idx %u mss %u emss=%u\n", __func__, GET_TCPOPT_MSS(opt), + TCPOPT_MSS_G(opt), ep->mss, ep->emss); + PDBG("%s mss_idx %u mss %u emss=%u\n", __func__, TCPOPT_MSS_G(opt), ep->mss, ep->emss); } @@ -652,29 +652,29 @@ static int send_connect(struct c4iw_ep *ep) if (win > RCV_BUFSIZ_M) win = RCV_BUFSIZ_M; - opt0 = (nocong ? NO_CONG(1) : 0) | + opt0 = (nocong ? NO_CONG_F : 0) | KEEP_ALIVE_F | - DELACK(1) | + DELACK_F | WND_SCALE_V(wscale) | MSS_IDX_V(mtu_idx) | L2T_IDX_V(ep->l2t->idx) | TX_CHAN_V(ep->tx_chan) | SMAC_SEL_V(ep->smac_idx) | - DSCP(ep->tos) | + DSCP_V(ep->tos) | ULP_MODE_V(ULP_MODE_TCPDDP) | RCV_BUFSIZ_V(win); opt2 = RX_CHANNEL_V(0) | - CCTRL_ECN(enable_ecn) | + CCTRL_ECN_V(enable_ecn) | RSS_QUEUE_VALID_F | RSS_QUEUE_V(ep->rss_qid); if (enable_tcp_timestamps) - opt2 |= TSTAMPS_EN(1); + opt2 |= TSTAMPS_EN_F; if (enable_tcp_sack) - opt2 |= SACK_EN(1); + opt2 |= SACK_EN_F; if (wscale && enable_tcp_window_scaling) opt2 |= WND_SCALE_EN_F; if (is_t5(ep->com.dev->rdev.lldi.adapter_type)) { opt2 |= T5_OPT_2_VALID_F; - opt2 |= V_CONG_CNTRL(CONG_ALG_TAHOE); + opt2 |= CONG_CNTRL_V(CONG_ALG_TAHOE); opt2 |= CONG_CNTRL_VALID; /* OPT_2_ISS for T5 */ } t4_set_arp_err_handler(skb, ep, act_open_req_arp_failure); @@ -1042,7 +1042,7 @@ static int act_establish(struct c4iw_dev *dev, struct sk_buff *skb) struct c4iw_ep *ep; struct cpl_act_establish *req = cplhdr(skb); unsigned int tid = GET_TID(req); - unsigned int atid = GET_TID_TID(ntohl(req->tos_atid)); + unsigned int atid = TID_TID_G(ntohl(req->tos_atid)); struct tid_info *t = dev->rdev.lldi.tids; ep = lookup_atid(t, atid); @@ -1258,8 +1258,8 @@ static int update_rx_credits(struct c4iw_ep *ep, u32 credits) OPCODE_TID(req) = cpu_to_be32(MK_OPCODE_TID(CPL_RX_DATA_ACK, ep->hwtid)); req->credit_dack = cpu_to_be32(credits | RX_FORCE_ACK_F | - F_RX_DACK_CHANGE | - V_RX_DACK_MODE(dack_mode)); + RX_DACK_CHANGE_F | + RX_DACK_MODE_V(dack_mode)); set_wr_txq(skb, CPL_PRIORITY_ACK, ep->ctrlq_idx); c4iw_ofld_send(&ep->com.dev->rdev, skb); return credits; @@ -1751,7 +1751,7 @@ static void send_fw_act_open_req(struct c4iw_ep *ep, unsigned int atid) skb = get_skb(NULL, sizeof(*req), GFP_KERNEL); req = (struct fw_ofld_connection_wr *)__skb_put(skb, sizeof(*req)); memset(req, 0, sizeof(*req)); - req->op_compl = htonl(V_WR_OP(FW_OFLD_CONNECTION_WR)); + req->op_compl = htonl(WR_OP_V(FW_OFLD_CONNECTION_WR)); req->len16_pkd = htonl(FW_WR_LEN16_V(DIV_ROUND_UP(sizeof(*req), 16))); req->le.filter = cpu_to_be32(cxgb4_select_ntuple( ep->com.dev->rdev.lldi.ports[0], @@ -1782,27 +1782,27 @@ static void send_fw_act_open_req(struct c4iw_ep *ep, unsigned int atid) if (win > RCV_BUFSIZ_M) win = RCV_BUFSIZ_M; - req->tcb.opt0 = (__force __be64) (TCAM_BYPASS(1) | - (nocong ? NO_CONG(1) : 0) | + req->tcb.opt0 = (__force __be64) (TCAM_BYPASS_F | + (nocong ? NO_CONG_F : 0) | KEEP_ALIVE_F | - DELACK(1) | + DELACK_F | WND_SCALE_V(wscale) | MSS_IDX_V(mtu_idx) | L2T_IDX_V(ep->l2t->idx) | TX_CHAN_V(ep->tx_chan) | SMAC_SEL_V(ep->smac_idx) | - DSCP(ep->tos) | + DSCP_V(ep->tos) | ULP_MODE_V(ULP_MODE_TCPDDP) | RCV_BUFSIZ_V(win)); - req->tcb.opt2 = (__force __be32) (PACE(1) | - TX_QUEUE(ep->com.dev->rdev.lldi.tx_modq[ep->tx_chan]) | + req->tcb.opt2 = (__force __be32) (PACE_V(1) | + TX_QUEUE_V(ep->com.dev->rdev.lldi.tx_modq[ep->tx_chan]) | RX_CHANNEL_V(0) | - CCTRL_ECN(enable_ecn) | + CCTRL_ECN_V(enable_ecn) | RSS_QUEUE_VALID_F | RSS_QUEUE_V(ep->rss_qid)); if (enable_tcp_timestamps) - req->tcb.opt2 |= (__force __be32)TSTAMPS_EN(1); + req->tcb.opt2 |= (__force __be32)TSTAMPS_EN_F; if (enable_tcp_sack) - req->tcb.opt2 |= (__force __be32)SACK_EN(1); + req->tcb.opt2 |= (__force __be32)SACK_EN_F; if (wscale && enable_tcp_window_scaling) req->tcb.opt2 |= (__force __be32)WND_SCALE_EN_F; req->tcb.opt0 = cpu_to_be64((__force u64)req->tcb.opt0); @@ -2023,10 +2023,10 @@ static int act_open_rpl(struct c4iw_dev *dev, struct sk_buff *skb) { struct c4iw_ep *ep; struct cpl_act_open_rpl *rpl = cplhdr(skb); - unsigned int atid = GET_TID_TID(GET_AOPEN_ATID( - ntohl(rpl->atid_status))); + unsigned int atid = TID_TID_G(AOPEN_ATID_G( + ntohl(rpl->atid_status))); struct tid_info *t = dev->rdev.lldi.tids; - int status = GET_AOPEN_STATUS(ntohl(rpl->atid_status)); + int status = AOPEN_STATUS_G(ntohl(rpl->atid_status)); struct sockaddr_in *la; struct sockaddr_in *ra; struct sockaddr_in6 *la6; @@ -2064,7 +2064,7 @@ static int act_open_rpl(struct c4iw_dev *dev, struct sk_buff *skb) if (ep->com.local_addr.ss_family == AF_INET && dev->rdev.lldi.enable_fw_ofld_conn) { send_fw_act_open_req(ep, - GET_TID_TID(GET_AOPEN_ATID( + TID_TID_G(AOPEN_ATID_G( ntohl(rpl->atid_status)))); return 0; } @@ -2181,39 +2181,39 @@ static void accept_cr(struct c4iw_ep *ep, struct sk_buff *skb, win = ep->rcv_win >> 10; if (win > RCV_BUFSIZ_M) win = RCV_BUFSIZ_M; - opt0 = (nocong ? NO_CONG(1) : 0) | + opt0 = (nocong ? NO_CONG_F : 0) | KEEP_ALIVE_F | - DELACK(1) | + DELACK_F | WND_SCALE_V(wscale) | MSS_IDX_V(mtu_idx) | L2T_IDX_V(ep->l2t->idx) | TX_CHAN_V(ep->tx_chan) | SMAC_SEL_V(ep->smac_idx) | - DSCP(ep->tos >> 2) | + DSCP_V(ep->tos >> 2) | ULP_MODE_V(ULP_MODE_TCPDDP) | RCV_BUFSIZ_V(win); opt2 = RX_CHANNEL_V(0) | RSS_QUEUE_VALID_F | RSS_QUEUE_V(ep->rss_qid); if (enable_tcp_timestamps && req->tcpopt.tstamp) - opt2 |= TSTAMPS_EN(1); + opt2 |= TSTAMPS_EN_F; if (enable_tcp_sack && req->tcpopt.sack) - opt2 |= SACK_EN(1); + opt2 |= SACK_EN_F; if (wscale && enable_tcp_window_scaling) opt2 |= WND_SCALE_EN_F; if (enable_ecn) { const struct tcphdr *tcph; u32 hlen = ntohl(req->hdr_len); - tcph = (const void *)(req + 1) + G_ETH_HDR_LEN(hlen) + - G_IP_HDR_LEN(hlen); + tcph = (const void *)(req + 1) + ETH_HDR_LEN_G(hlen) + + IP_HDR_LEN_G(hlen); if (tcph->ece && tcph->cwr) - opt2 |= CCTRL_ECN(1); + opt2 |= CCTRL_ECN_V(1); } if (is_t5(ep->com.dev->rdev.lldi.adapter_type)) { u32 isn = (prandom_u32() & ~7UL) - 1; opt2 |= T5_OPT_2_VALID_F; - opt2 |= V_CONG_CNTRL(CONG_ALG_TAHOE); + opt2 |= CONG_CNTRL_V(CONG_ALG_TAHOE); opt2 |= CONG_CNTRL_VALID; /* OPT_2_ISS for T5 */ rpl5 = (void *)rpl; memset(&rpl5->iss, 0, roundup(sizeof(*rpl5)-sizeof(*rpl), 16)); @@ -2245,8 +2245,8 @@ static void get_4tuple(struct cpl_pass_accept_req *req, int *iptype, __u8 *local_ip, __u8 *peer_ip, __be16 *local_port, __be16 *peer_port) { - int eth_len = G_ETH_HDR_LEN(be32_to_cpu(req->hdr_len)); - int ip_len = G_IP_HDR_LEN(be32_to_cpu(req->hdr_len)); + int eth_len = ETH_HDR_LEN_G(be32_to_cpu(req->hdr_len)); + int ip_len = IP_HDR_LEN_G(be32_to_cpu(req->hdr_len)); struct iphdr *ip = (struct iphdr *)((u8 *)(req + 1) + eth_len); struct ipv6hdr *ip6 = (struct ipv6hdr *)((u8 *)(req + 1) + eth_len); struct tcphdr *tcp = (struct tcphdr *) @@ -2277,7 +2277,7 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb) { struct c4iw_ep *child_ep = NULL, *parent_ep; struct cpl_pass_accept_req *req = cplhdr(skb); - unsigned int stid = GET_POPEN_TID(ntohl(req->tos_stid)); + unsigned int stid = PASS_OPEN_TID_G(ntohl(req->tos_stid)); struct tid_info *t = dev->rdev.lldi.tids; unsigned int hwtid = GET_TID(req); struct dst_entry *dst; @@ -2310,14 +2310,14 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb) ntohs(peer_port), peer_mss); dst = find_route(dev, *(__be32 *)local_ip, *(__be32 *)peer_ip, local_port, peer_port, - GET_POPEN_TOS(ntohl(req->tos_stid))); + PASS_OPEN_TOS_G(ntohl(req->tos_stid))); } else { PDBG("%s parent ep %p hwtid %u laddr %pI6 raddr %pI6 lport %d rport %d peer_mss %d\n" , __func__, parent_ep, hwtid, local_ip, peer_ip, ntohs(local_port), ntohs(peer_port), peer_mss); dst = find_route6(dev, local_ip, peer_ip, local_port, peer_port, - PASS_OPEN_TOS(ntohl(req->tos_stid)), + PASS_OPEN_TOS_G(ntohl(req->tos_stid)), ((struct sockaddr_in6 *) &parent_ep->com.local_addr)->sin6_scope_id); } @@ -2375,7 +2375,7 @@ static int pass_accept_req(struct c4iw_dev *dev, struct sk_buff *skb) } c4iw_get_ep(&parent_ep->com); child_ep->parent_ep = parent_ep; - child_ep->tos = GET_POPEN_TOS(ntohl(req->tos_stid)); + child_ep->tos = PASS_OPEN_TOS_G(ntohl(req->tos_stid)); child_ep->dst = dst; child_ep->hwtid = hwtid; @@ -3500,24 +3500,24 @@ static void build_cpl_pass_accept_req(struct sk_buff *skb, int stid , u8 tos) req = (struct cpl_pass_accept_req *)__skb_push(skb, sizeof(*req)); memset(req, 0, sizeof(*req)); - req->l2info = cpu_to_be16(V_SYN_INTF(intf) | - V_SYN_MAC_IDX(G_RX_MACIDX( + req->l2info = cpu_to_be16(SYN_INTF_V(intf) | + SYN_MAC_IDX_V(RX_MACIDX_G( (__force int) htonl(l2info))) | - F_SYN_XACT_MATCH); + SYN_XACT_MATCH_F); eth_hdr_len = is_t4(dev->rdev.lldi.adapter_type) ? - G_RX_ETHHDR_LEN((__force int) htonl(l2info)) : - G_RX_T5_ETHHDR_LEN((__force int) htonl(l2info)); - req->hdr_len = cpu_to_be32(V_SYN_RX_CHAN(G_RX_CHAN( + RX_ETHHDR_LEN_G((__force int)htonl(l2info)) : + RX_T5_ETHHDR_LEN_G((__force int)htonl(l2info)); + req->hdr_len = cpu_to_be32(SYN_RX_CHAN_V(RX_CHAN_G( (__force int) htonl(l2info))) | - V_TCP_HDR_LEN(G_RX_TCPHDR_LEN( + TCP_HDR_LEN_V(RX_TCPHDR_LEN_G( (__force int) htons(hdr_len))) | - V_IP_HDR_LEN(G_RX_IPHDR_LEN( + IP_HDR_LEN_V(RX_IPHDR_LEN_G( (__force int) htons(hdr_len))) | - V_ETH_HDR_LEN(G_RX_ETHHDR_LEN(eth_hdr_len))); + ETH_HDR_LEN_V(RX_ETHHDR_LEN_G(eth_hdr_len))); req->vlan = (__force __be16) vlantag; req->len = (__force __be16) len; - req->tos_stid = cpu_to_be32(PASS_OPEN_TID(stid) | - PASS_OPEN_TOS(tos)); + req->tos_stid = cpu_to_be32(PASS_OPEN_TID_V(stid) | + PASS_OPEN_TOS_V(tos)); req->tcpopt.mss = htons(tmp_opt.mss_clamp); if (tmp_opt.wscale_ok) req->tcpopt.wsf = tmp_opt.snd_wscale; @@ -3542,7 +3542,7 @@ static void send_fw_pass_open_req(struct c4iw_dev *dev, struct sk_buff *skb, req_skb = alloc_skb(sizeof(struct fw_ofld_connection_wr), GFP_KERNEL); req = (struct fw_ofld_connection_wr *)__skb_put(req_skb, sizeof(*req)); memset(req, 0, sizeof(*req)); - req->op_compl = htonl(V_WR_OP(FW_OFLD_CONNECTION_WR) | FW_WR_COMPL_F); + req->op_compl = htonl(WR_OP_V(FW_OFLD_CONNECTION_WR) | FW_WR_COMPL_F); req->len16_pkd = htonl(FW_WR_LEN16_V(DIV_ROUND_UP(sizeof(*req), 16))); req->le.version_cpl = htonl(FW_OFLD_CONNECTION_WR_CPL_F); req->le.filter = (__force __be32) filter; @@ -3556,7 +3556,7 @@ static void send_fw_pass_open_req(struct c4iw_dev *dev, struct sk_buff *skb, htonl(FW_OFLD_CONNECTION_WR_T_STATE_V(TCP_SYN_RECV) | FW_OFLD_CONNECTION_WR_RCV_SCALE_V(cpl->tcpopt.wsf) | FW_OFLD_CONNECTION_WR_ASTID_V( - GET_PASS_OPEN_TID(ntohl(cpl->tos_stid)))); + PASS_OPEN_TID_G(ntohl(cpl->tos_stid)))); /* * We store the qid in opt2 which will be used by the firmware @@ -3613,7 +3613,7 @@ static int rx_pkt(struct c4iw_dev *dev, struct sk_buff *skb) struct neighbour *neigh; /* Drop all non-SYN packets */ - if (!(cpl->l2info & cpu_to_be32(F_RXF_SYN))) + if (!(cpl->l2info & cpu_to_be32(RXF_SYN_F))) goto reject; /* @@ -3635,8 +3635,8 @@ static int rx_pkt(struct c4iw_dev *dev, struct sk_buff *skb) } eth_hdr_len = is_t4(dev->rdev.lldi.adapter_type) ? - G_RX_ETHHDR_LEN(htonl(cpl->l2info)) : - G_RX_T5_ETHHDR_LEN(htonl(cpl->l2info)); + RX_ETHHDR_LEN_G(htonl(cpl->l2info)) : + RX_T5_ETHHDR_LEN_G(htonl(cpl->l2info)); if (eth_hdr_len == ETH_HLEN) { eh = (struct ethhdr *)(req + 1); iph = (struct iphdr *)(eh + 1); diff --git a/drivers/infiniband/hw/cxgb4/cq.c b/drivers/infiniband/hw/cxgb4/cq.c index e9fd3a029296..ab7692ac2044 100644 --- a/drivers/infiniband/hw/cxgb4/cq.c +++ b/drivers/infiniband/hw/cxgb4/cq.c @@ -52,7 +52,7 @@ static int destroy_cq(struct c4iw_rdev *rdev, struct t4_cq *cq, memset(res_wr, 0, wr_len); res_wr->op_nres = cpu_to_be32( FW_WR_OP_V(FW_RI_RES_WR) | - V_FW_RI_RES_WR_NRES(1) | + FW_RI_RES_WR_NRES_V(1) | FW_WR_COMPL_F); res_wr->len16_pkd = cpu_to_be32(DIV_ROUND_UP(wr_len, 16)); res_wr->cookie = (unsigned long) &wr_wait; @@ -122,7 +122,7 @@ static int create_cq(struct c4iw_rdev *rdev, struct t4_cq *cq, memset(res_wr, 0, wr_len); res_wr->op_nres = cpu_to_be32( FW_WR_OP_V(FW_RI_RES_WR) | - V_FW_RI_RES_WR_NRES(1) | + FW_RI_RES_WR_NRES_V(1) | FW_WR_COMPL_F); res_wr->len16_pkd = cpu_to_be32(DIV_ROUND_UP(wr_len, 16)); res_wr->cookie = (unsigned long) &wr_wait; @@ -131,17 +131,17 @@ static int create_cq(struct c4iw_rdev *rdev, struct t4_cq *cq, res->u.cq.op = FW_RI_RES_OP_WRITE; res->u.cq.iqid = cpu_to_be32(cq->cqid); res->u.cq.iqandst_to_iqandstindex = cpu_to_be32( - V_FW_RI_RES_WR_IQANUS(0) | - V_FW_RI_RES_WR_IQANUD(1) | - F_FW_RI_RES_WR_IQANDST | - V_FW_RI_RES_WR_IQANDSTINDEX( + FW_RI_RES_WR_IQANUS_V(0) | + FW_RI_RES_WR_IQANUD_V(1) | + FW_RI_RES_WR_IQANDST_F | + FW_RI_RES_WR_IQANDSTINDEX_V( rdev->lldi.ciq_ids[cq->vector])); res->u.cq.iqdroprss_to_iqesize = cpu_to_be16( - F_FW_RI_RES_WR_IQDROPRSS | - V_FW_RI_RES_WR_IQPCIECH(2) | - V_FW_RI_RES_WR_IQINTCNTTHRESH(0) | - F_FW_RI_RES_WR_IQO | - V_FW_RI_RES_WR_IQESIZE(1)); + FW_RI_RES_WR_IQDROPRSS_F | + FW_RI_RES_WR_IQPCIECH_V(2) | + FW_RI_RES_WR_IQINTCNTTHRESH_V(0) | + FW_RI_RES_WR_IQO_F | + FW_RI_RES_WR_IQESIZE_V(1)); res->u.cq.iqsize = cpu_to_be16(cq->size); res->u.cq.iqaddr = cpu_to_be64(cq->dma_addr); @@ -182,12 +182,12 @@ static void insert_recv_cqe(struct t4_wq *wq, struct t4_cq *cq) PDBG("%s wq %p cq %p sw_cidx %u sw_pidx %u\n", __func__, wq, cq, cq->sw_cidx, cq->sw_pidx); memset(&cqe, 0, sizeof(cqe)); - cqe.header = cpu_to_be32(V_CQE_STATUS(T4_ERR_SWFLUSH) | - V_CQE_OPCODE(FW_RI_SEND) | - V_CQE_TYPE(0) | - V_CQE_SWCQE(1) | - V_CQE_QPID(wq->sq.qid)); - cqe.bits_type_ts = cpu_to_be64(V_CQE_GENBIT((u64)cq->gen)); + cqe.header = cpu_to_be32(CQE_STATUS_V(T4_ERR_SWFLUSH) | + CQE_OPCODE_V(FW_RI_SEND) | + CQE_TYPE_V(0) | + CQE_SWCQE_V(1) | + CQE_QPID_V(wq->sq.qid)); + cqe.bits_type_ts = cpu_to_be64(CQE_GENBIT_V((u64)cq->gen)); cq->sw_queue[cq->sw_pidx] = cqe; t4_swcq_produce(cq); } @@ -215,13 +215,13 @@ static void insert_sq_cqe(struct t4_wq *wq, struct t4_cq *cq, PDBG("%s wq %p cq %p sw_cidx %u sw_pidx %u\n", __func__, wq, cq, cq->sw_cidx, cq->sw_pidx); memset(&cqe, 0, sizeof(cqe)); - cqe.header = cpu_to_be32(V_CQE_STATUS(T4_ERR_SWFLUSH) | - V_CQE_OPCODE(swcqe->opcode) | - V_CQE_TYPE(1) | - V_CQE_SWCQE(1) | - V_CQE_QPID(wq->sq.qid)); + cqe.header = cpu_to_be32(CQE_STATUS_V(T4_ERR_SWFLUSH) | + CQE_OPCODE_V(swcqe->opcode) | + CQE_TYPE_V(1) | + CQE_SWCQE_V(1) | + CQE_QPID_V(wq->sq.qid)); CQE_WRID_SQ_IDX(&cqe) = swcqe->idx; - cqe.bits_type_ts = cpu_to_be64(V_CQE_GENBIT((u64)cq->gen)); + cqe.bits_type_ts = cpu_to_be64(CQE_GENBIT_V((u64)cq->gen)); cq->sw_queue[cq->sw_pidx] = cqe; t4_swcq_produce(cq); } @@ -284,7 +284,7 @@ static void flush_completed_wrs(struct t4_wq *wq, struct t4_cq *cq) */ PDBG("%s moving cqe into swcq sq idx %u cq idx %u\n", __func__, cidx, cq->sw_pidx); - swsqe->cqe.header |= htonl(V_CQE_SWCQE(1)); + swsqe->cqe.header |= htonl(CQE_SWCQE_V(1)); cq->sw_queue[cq->sw_pidx] = swsqe->cqe; t4_swcq_produce(cq); swsqe->flushed = 1; @@ -301,10 +301,10 @@ static void create_read_req_cqe(struct t4_wq *wq, struct t4_cqe *hw_cqe, { read_cqe->u.scqe.cidx = wq->sq.oldest_read->idx; read_cqe->len = htonl(wq->sq.oldest_read->read_len); - read_cqe->header = htonl(V_CQE_QPID(CQE_QPID(hw_cqe)) | - V_CQE_SWCQE(SW_CQE(hw_cqe)) | - V_CQE_OPCODE(FW_RI_READ_REQ) | - V_CQE_TYPE(1)); + read_cqe->header = htonl(CQE_QPID_V(CQE_QPID(hw_cqe)) | + CQE_SWCQE_V(SW_CQE(hw_cqe)) | + CQE_OPCODE_V(FW_RI_READ_REQ) | + CQE_TYPE_V(1)); read_cqe->bits_type_ts = hw_cqe->bits_type_ts; } @@ -400,7 +400,7 @@ void c4iw_flush_hw_cq(struct c4iw_cq *chp) } else { swcqe = &chp->cq.sw_queue[chp->cq.sw_pidx]; *swcqe = *hw_cqe; - swcqe->header |= cpu_to_be32(V_CQE_SWCQE(1)); + swcqe->header |= cpu_to_be32(CQE_SWCQE_V(1)); t4_swcq_produce(&chp->cq); } next_cqe: @@ -576,7 +576,7 @@ static int poll_cq(struct t4_wq *wq, struct t4_cq *cq, struct t4_cqe *cqe, } if (unlikely((CQE_WRID_MSN(hw_cqe) != (wq->rq.msn)))) { t4_set_wq_in_error(wq); - hw_cqe->header |= htonl(V_CQE_STATUS(T4_ERR_MSN)); + hw_cqe->header |= htonl(CQE_STATUS_V(T4_ERR_MSN)); goto proc_cqe; } goto proc_cqe; diff --git a/drivers/infiniband/hw/cxgb4/device.c b/drivers/infiniband/hw/cxgb4/device.c index eb5df4e62703..aafdbcd84fc4 100644 --- a/drivers/infiniband/hw/cxgb4/device.c +++ b/drivers/infiniband/hw/cxgb4/device.c @@ -380,12 +380,12 @@ static int dump_stag(int id, void *p, void *data) "stag: idx 0x%x valid %d key 0x%x state %d pdid %d " "perm 0x%x ps %d len 0x%llx va 0x%llx\n", (u32)id<<8, - G_FW_RI_TPTE_VALID(ntohl(tpte.valid_to_pdid)), - G_FW_RI_TPTE_STAGKEY(ntohl(tpte.valid_to_pdid)), - G_FW_RI_TPTE_STAGSTATE(ntohl(tpte.valid_to_pdid)), - G_FW_RI_TPTE_PDID(ntohl(tpte.valid_to_pdid)), - G_FW_RI_TPTE_PERM(ntohl(tpte.locread_to_qpid)), - G_FW_RI_TPTE_PS(ntohl(tpte.locread_to_qpid)), + FW_RI_TPTE_VALID_G(ntohl(tpte.valid_to_pdid)), + FW_RI_TPTE_STAGKEY_G(ntohl(tpte.valid_to_pdid)), + FW_RI_TPTE_STAGSTATE_G(ntohl(tpte.valid_to_pdid)), + FW_RI_TPTE_PDID_G(ntohl(tpte.valid_to_pdid)), + FW_RI_TPTE_PERM_G(ntohl(tpte.locread_to_qpid)), + FW_RI_TPTE_PS_G(ntohl(tpte.locread_to_qpid)), ((u64)ntohl(tpte.len_hi) << 32) | ntohl(tpte.len_lo), ((u64)ntohl(tpte.va_hi) << 32) | ntohl(tpte.va_lo_fbo)); if (cc < space) diff --git a/drivers/infiniband/hw/cxgb4/ev.c b/drivers/infiniband/hw/cxgb4/ev.c index c9df0549f51d..794555dc86a5 100644 --- a/drivers/infiniband/hw/cxgb4/ev.c +++ b/drivers/infiniband/hw/cxgb4/ev.c @@ -50,12 +50,12 @@ static void print_tpte(struct c4iw_dev *dev, u32 stag) PDBG("stag idx 0x%x valid %d key 0x%x state %d pdid %d " "perm 0x%x ps %d len 0x%llx va 0x%llx\n", stag & 0xffffff00, - G_FW_RI_TPTE_VALID(ntohl(tpte.valid_to_pdid)), - G_FW_RI_TPTE_STAGKEY(ntohl(tpte.valid_to_pdid)), - G_FW_RI_TPTE_STAGSTATE(ntohl(tpte.valid_to_pdid)), - G_FW_RI_TPTE_PDID(ntohl(tpte.valid_to_pdid)), - G_FW_RI_TPTE_PERM(ntohl(tpte.locread_to_qpid)), - G_FW_RI_TPTE_PS(ntohl(tpte.locread_to_qpid)), + FW_RI_TPTE_VALID_G(ntohl(tpte.valid_to_pdid)), + FW_RI_TPTE_STAGKEY_G(ntohl(tpte.valid_to_pdid)), + FW_RI_TPTE_STAGSTATE_G(ntohl(tpte.valid_to_pdid)), + FW_RI_TPTE_PDID_G(ntohl(tpte.valid_to_pdid)), + FW_RI_TPTE_PERM_G(ntohl(tpte.locread_to_qpid)), + FW_RI_TPTE_PS_G(ntohl(tpte.locread_to_qpid)), ((u64)ntohl(tpte.len_hi) << 32) | ntohl(tpte.len_lo), ((u64)ntohl(tpte.va_hi) << 32) | ntohl(tpte.va_lo_fbo)); } diff --git a/drivers/infiniband/hw/cxgb4/mem.c b/drivers/infiniband/hw/cxgb4/mem.c index cb43c2299ac0..6791fd16272c 100644 --- a/drivers/infiniband/hw/cxgb4/mem.c +++ b/drivers/infiniband/hw/cxgb4/mem.c @@ -86,14 +86,14 @@ static int _c4iw_write_mem_dma_aligned(struct c4iw_rdev *rdev, u32 addr, req->wr.wr_lo = wait ? (__force __be64)(unsigned long) &wr_wait : 0L; req->wr.wr_mid = cpu_to_be32(FW_WR_LEN16_V(DIV_ROUND_UP(wr_len, 16))); req->cmd = cpu_to_be32(ULPTX_CMD_V(ULP_TX_MEM_WRITE)); - req->cmd |= cpu_to_be32(V_T5_ULP_MEMIO_ORDER(1)); + req->cmd |= cpu_to_be32(T5_ULP_MEMIO_ORDER_V(1)); req->dlen = cpu_to_be32(ULP_MEMIO_DATA_LEN_V(len>>5)); req->len16 = cpu_to_be32(DIV_ROUND_UP(wr_len-sizeof(req->wr), 16)); req->lock_addr = cpu_to_be32(ULP_MEMIO_ADDR_V(addr)); sgl = (struct ulptx_sgl *)(req + 1); sgl->cmd_nsge = cpu_to_be32(ULPTX_CMD_V(ULP_TX_SC_DSGL) | - ULPTX_NSGE(1)); + ULPTX_NSGE_V(1)); sgl->len0 = cpu_to_be32(len); sgl->addr0 = cpu_to_be64(data); @@ -286,17 +286,17 @@ static int write_tpt_entry(struct c4iw_rdev *rdev, u32 reset_tpt_entry, if (reset_tpt_entry) memset(&tpt, 0, sizeof(tpt)); else { - tpt.valid_to_pdid = cpu_to_be32(F_FW_RI_TPTE_VALID | - V_FW_RI_TPTE_STAGKEY((*stag & M_FW_RI_TPTE_STAGKEY)) | - V_FW_RI_TPTE_STAGSTATE(stag_state) | - V_FW_RI_TPTE_STAGTYPE(type) | V_FW_RI_TPTE_PDID(pdid)); - tpt.locread_to_qpid = cpu_to_be32(V_FW_RI_TPTE_PERM(perm) | - (bind_enabled ? F_FW_RI_TPTE_MWBINDEN : 0) | - V_FW_RI_TPTE_ADDRTYPE((zbva ? FW_RI_ZERO_BASED_TO : + tpt.valid_to_pdid = cpu_to_be32(FW_RI_TPTE_VALID_F | + FW_RI_TPTE_STAGKEY_V((*stag & FW_RI_TPTE_STAGKEY_M)) | + FW_RI_TPTE_STAGSTATE_V(stag_state) | + FW_RI_TPTE_STAGTYPE_V(type) | FW_RI_TPTE_PDID_V(pdid)); + tpt.locread_to_qpid = cpu_to_be32(FW_RI_TPTE_PERM_V(perm) | + (bind_enabled ? FW_RI_TPTE_MWBINDEN_F : 0) | + FW_RI_TPTE_ADDRTYPE_V((zbva ? FW_RI_ZERO_BASED_TO : FW_RI_VA_BASED_TO))| - V_FW_RI_TPTE_PS(page_size)); + FW_RI_TPTE_PS_V(page_size)); tpt.nosnoop_pbladdr = !pbl_size ? 0 : cpu_to_be32( - V_FW_RI_TPTE_PBLADDR(PBL_OFF(rdev, pbl_addr)>>3)); + FW_RI_TPTE_PBLADDR_V(PBL_OFF(rdev, pbl_addr)>>3)); tpt.len_lo = cpu_to_be32((u32)(len & 0xffffffffUL)); tpt.va_hi = cpu_to_be32((u32)(to >> 32)); tpt.va_lo_fbo = cpu_to_be32((u32)(to & 0xffffffffUL)); diff --git a/drivers/infiniband/hw/cxgb4/qp.c b/drivers/infiniband/hw/cxgb4/qp.c index bb85d479e66e..15cae5a31018 100644 --- a/drivers/infiniband/hw/cxgb4/qp.c +++ b/drivers/infiniband/hw/cxgb4/qp.c @@ -272,7 +272,7 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq, memset(res_wr, 0, wr_len); res_wr->op_nres = cpu_to_be32( FW_WR_OP_V(FW_RI_RES_WR) | - V_FW_RI_RES_WR_NRES(2) | + FW_RI_RES_WR_NRES_V(2) | FW_WR_COMPL_F); res_wr->len16_pkd = cpu_to_be32(DIV_ROUND_UP(wr_len, 16)); res_wr->cookie = (unsigned long) &wr_wait; @@ -287,19 +287,19 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq, rdev->hw_queue.t4_eq_status_entries; res->u.sqrq.fetchszm_to_iqid = cpu_to_be32( - V_FW_RI_RES_WR_HOSTFCMODE(0) | /* no host cidx updates */ - V_FW_RI_RES_WR_CPRIO(0) | /* don't keep in chip cache */ - V_FW_RI_RES_WR_PCIECHN(0) | /* set by uP at ri_init time */ - (t4_sq_onchip(&wq->sq) ? F_FW_RI_RES_WR_ONCHIP : 0) | - V_FW_RI_RES_WR_IQID(scq->cqid)); + FW_RI_RES_WR_HOSTFCMODE_V(0) | /* no host cidx updates */ + FW_RI_RES_WR_CPRIO_V(0) | /* don't keep in chip cache */ + FW_RI_RES_WR_PCIECHN_V(0) | /* set by uP at ri_init time */ + (t4_sq_onchip(&wq->sq) ? FW_RI_RES_WR_ONCHIP_F : 0) | + FW_RI_RES_WR_IQID_V(scq->cqid)); res->u.sqrq.dcaen_to_eqsize = cpu_to_be32( - V_FW_RI_RES_WR_DCAEN(0) | - V_FW_RI_RES_WR_DCACPU(0) | - V_FW_RI_RES_WR_FBMIN(2) | - V_FW_RI_RES_WR_FBMAX(2) | - V_FW_RI_RES_WR_CIDXFTHRESHO(0) | - V_FW_RI_RES_WR_CIDXFTHRESH(0) | - V_FW_RI_RES_WR_EQSIZE(eqsize)); + FW_RI_RES_WR_DCAEN_V(0) | + FW_RI_RES_WR_DCACPU_V(0) | + FW_RI_RES_WR_FBMIN_V(2) | + FW_RI_RES_WR_FBMAX_V(2) | + FW_RI_RES_WR_CIDXFTHRESHO_V(0) | + FW_RI_RES_WR_CIDXFTHRESH_V(0) | + FW_RI_RES_WR_EQSIZE_V(eqsize)); res->u.sqrq.eqid = cpu_to_be32(wq->sq.qid); res->u.sqrq.eqaddr = cpu_to_be64(wq->sq.dma_addr); res++; @@ -312,18 +312,18 @@ static int create_qp(struct c4iw_rdev *rdev, struct t4_wq *wq, eqsize = wq->rq.size * T4_RQ_NUM_SLOTS + rdev->hw_queue.t4_eq_status_entries; res->u.sqrq.fetchszm_to_iqid = cpu_to_be32( - V_FW_RI_RES_WR_HOSTFCMODE(0) | /* no host cidx updates */ - V_FW_RI_RES_WR_CPRIO(0) | /* don't keep in chip cache */ - V_FW_RI_RES_WR_PCIECHN(0) | /* set by uP at ri_init time */ - V_FW_RI_RES_WR_IQID(rcq->cqid)); + FW_RI_RES_WR_HOSTFCMODE_V(0) | /* no host cidx updates */ + FW_RI_RES_WR_CPRIO_V(0) | /* don't keep in chip cache */ + FW_RI_RES_WR_PCIECHN_V(0) | /* set by uP at ri_init time */ + FW_RI_RES_WR_IQID_V(rcq->cqid)); res->u.sqrq.dcaen_to_eqsize = cpu_to_be32( - V_FW_RI_RES_WR_DCAEN(0) | - V_FW_RI_RES_WR_DCACPU(0) | - V_FW_RI_RES_WR_FBMIN(2) | - V_FW_RI_RES_WR_FBMAX(2) | - V_FW_RI_RES_WR_CIDXFTHRESHO(0) | - V_FW_RI_RES_WR_CIDXFTHRESH(0) | - V_FW_RI_RES_WR_EQSIZE(eqsize)); + FW_RI_RES_WR_DCAEN_V(0) | + FW_RI_RES_WR_DCACPU_V(0) | + FW_RI_RES_WR_FBMIN_V(2) | + FW_RI_RES_WR_FBMAX_V(2) | + FW_RI_RES_WR_CIDXFTHRESHO_V(0) | + FW_RI_RES_WR_CIDXFTHRESH_V(0) | + FW_RI_RES_WR_EQSIZE_V(eqsize)); res->u.sqrq.eqid = cpu_to_be32(wq->rq.qid); res->u.sqrq.eqaddr = cpu_to_be64(wq->rq.dma_addr); @@ -444,19 +444,19 @@ static int build_rdma_send(struct t4_sq *sq, union t4_wr *wqe, case IB_WR_SEND: if (wr->send_flags & IB_SEND_SOLICITED) wqe->send.sendop_pkd = cpu_to_be32( - V_FW_RI_SEND_WR_SENDOP(FW_RI_SEND_WITH_SE)); + FW_RI_SEND_WR_SENDOP_V(FW_RI_SEND_WITH_SE)); else wqe->send.sendop_pkd = cpu_to_be32( - V_FW_RI_SEND_WR_SENDOP(FW_RI_SEND)); + FW_RI_SEND_WR_SENDOP_V(FW_RI_SEND)); wqe->send.stag_inv = 0; break; case IB_WR_SEND_WITH_INV: if (wr->send_flags & IB_SEND_SOLICITED) wqe->send.sendop_pkd = cpu_to_be32( - V_FW_RI_SEND_WR_SENDOP(FW_RI_SEND_WITH_SE_INV)); + FW_RI_SEND_WR_SENDOP_V(FW_RI_SEND_WITH_SE_INV)); else wqe->send.sendop_pkd = cpu_to_be32( - V_FW_RI_SEND_WR_SENDOP(FW_RI_SEND_WITH_INV)); + FW_RI_SEND_WR_SENDOP_V(FW_RI_SEND_WITH_INV)); wqe->send.stag_inv = cpu_to_be32(wr->ex.invalidate_rkey); break; @@ -1283,8 +1283,8 @@ static int rdma_init(struct c4iw_dev *rhp, struct c4iw_qp *qhp) wqe->u.init.type = FW_RI_TYPE_INIT; wqe->u.init.mpareqbit_p2ptype = - V_FW_RI_WR_MPAREQBIT(qhp->attr.mpa_attr.initiator) | - V_FW_RI_WR_P2PTYPE(qhp->attr.mpa_attr.p2p_type); + FW_RI_WR_MPAREQBIT_V(qhp->attr.mpa_attr.initiator) | + FW_RI_WR_P2PTYPE_V(qhp->attr.mpa_attr.p2p_type); wqe->u.init.mpa_attrs = FW_RI_MPA_IETF_ENABLE; if (qhp->attr.mpa_attr.recv_marker_enabled) wqe->u.init.mpa_attrs |= FW_RI_MPA_RX_MARKER_ENABLE; @@ -1776,7 +1776,7 @@ struct ib_qp *c4iw_create_qp(struct ib_pd *pd, struct ib_qp_init_attr *attrs, if (mm5) { mm5->key = uresp.ma_sync_key; mm5->addr = (pci_resource_start(rhp->rdev.lldi.pdev, 0) - + A_PCIE_MA_SYNC) & PAGE_MASK; + + PCIE_MA_SYNC_A) & PAGE_MASK; mm5->len = PAGE_SIZE; insert_mmap(ucontext, mm5); } diff --git a/drivers/infiniband/hw/cxgb4/t4.h b/drivers/infiniband/hw/cxgb4/t4.h index c04e5134b30c..871cdcac7be2 100644 --- a/drivers/infiniband/hw/cxgb4/t4.h +++ b/drivers/infiniband/hw/cxgb4/t4.h @@ -41,7 +41,7 @@ #define T4_PAGESIZE_MASK 0xffff000 /* 4KB-128MB */ #define T4_STAG_UNSET 0xffffffff #define T4_FW_MAJ 0 -#define A_PCIE_MA_SYNC 0x30b4 +#define PCIE_MA_SYNC_A 0x30b4 struct t4_status_page { __be32 rsvd1; /* flit 0 - hw owns */ @@ -184,44 +184,44 @@ struct t4_cqe { /* macros for flit 0 of the cqe */ -#define S_CQE_QPID 12 -#define M_CQE_QPID 0xFFFFF -#define G_CQE_QPID(x) ((((x) >> S_CQE_QPID)) & M_CQE_QPID) -#define V_CQE_QPID(x) ((x)<<S_CQE_QPID) - -#define S_CQE_SWCQE 11 -#define M_CQE_SWCQE 0x1 -#define G_CQE_SWCQE(x) ((((x) >> S_CQE_SWCQE)) & M_CQE_SWCQE) -#define V_CQE_SWCQE(x) ((x)<<S_CQE_SWCQE) - -#define S_CQE_STATUS 5 -#define M_CQE_STATUS 0x1F -#define G_CQE_STATUS(x) ((((x) >> S_CQE_STATUS)) & M_CQE_STATUS) -#define V_CQE_STATUS(x) ((x)<<S_CQE_STATUS) - -#define S_CQE_TYPE 4 -#define M_CQE_TYPE 0x1 -#define G_CQE_TYPE(x) ((((x) >> S_CQE_TYPE)) & M_CQE_TYPE) -#define V_CQE_TYPE(x) ((x)<<S_CQE_TYPE) - -#define S_CQE_OPCODE 0 -#define M_CQE_OPCODE 0xF -#define G_CQE_OPCODE(x) ((((x) >> S_CQE_OPCODE)) & M_CQE_OPCODE) -#define V_CQE_OPCODE(x) ((x)<<S_CQE_OPCODE) - -#define SW_CQE(x) (G_CQE_SWCQE(be32_to_cpu((x)->header))) -#define CQE_QPID(x) (G_CQE_QPID(be32_to_cpu((x)->header))) -#define CQE_TYPE(x) (G_CQE_TYPE(be32_to_cpu((x)->header))) +#define CQE_QPID_S 12 +#define CQE_QPID_M 0xFFFFF +#define CQE_QPID_G(x) ((((x) >> CQE_QPID_S)) & CQE_QPID_M) +#define CQE_QPID_V(x) ((x)<<CQE_QPID_S) + +#define CQE_SWCQE_S 11 +#define CQE_SWCQE_M 0x1 +#define CQE_SWCQE_G(x) ((((x) >> CQE_SWCQE_S)) & CQE_SWCQE_M) +#define CQE_SWCQE_V(x) ((x)<<CQE_SWCQE_S) + +#define CQE_STATUS_S 5 +#define CQE_STATUS_M 0x1F +#define CQE_STATUS_G(x) ((((x) >> CQE_STATUS_S)) & CQE_STATUS_M) +#define CQE_STATUS_V(x) ((x)<<CQE_STATUS_S) + +#define CQE_TYPE_S 4 +#define CQE_TYPE_M 0x1 +#define CQE_TYPE_G(x) ((((x) >> CQE_TYPE_S)) & CQE_TYPE_M) +#define CQE_TYPE_V(x) ((x)<<CQE_TYPE_S) + +#define CQE_OPCODE_S 0 +#define CQE_OPCODE_M 0xF +#define CQE_OPCODE_G(x) ((((x) >> CQE_OPCODE_S)) & CQE_OPCODE_M) +#define CQE_OPCODE_V(x) ((x)<<CQE_OPCODE_S) + +#define SW_CQE(x) (CQE_SWCQE_G(be32_to_cpu((x)->header))) +#define CQE_QPID(x) (CQE_QPID_G(be32_to_cpu((x)->header))) +#define CQE_TYPE(x) (CQE_TYPE_G(be32_to_cpu((x)->header))) #define SQ_TYPE(x) (CQE_TYPE((x))) #define RQ_TYPE(x) (!CQE_TYPE((x))) -#define CQE_STATUS(x) (G_CQE_STATUS(be32_to_cpu((x)->header))) -#define CQE_OPCODE(x) (G_CQE_OPCODE(be32_to_cpu((x)->header))) +#define CQE_STATUS(x) (CQE_STATUS_G(be32_to_cpu((x)->header))) +#define CQE_OPCODE(x) (CQE_OPCODE_G(be32_to_cpu((x)->header))) #define CQE_SEND_OPCODE(x)( \ - (G_CQE_OPCODE(be32_to_cpu((x)->header)) == FW_RI_SEND) || \ - (G_CQE_OPCODE(be32_to_cpu((x)->header)) == FW_RI_SEND_WITH_SE) || \ - (G_CQE_OPCODE(be32_to_cpu((x)->header)) == FW_RI_SEND_WITH_INV) || \ - (G_CQE_OPCODE(be32_to_cpu((x)->header)) == FW_RI_SEND_WITH_SE_INV)) + (CQE_OPCODE_G(be32_to_cpu((x)->header)) == FW_RI_SEND) || \ + (CQE_OPCODE_G(be32_to_cpu((x)->header)) == FW_RI_SEND_WITH_SE) || \ + (CQE_OPCODE_G(be32_to_cpu((x)->header)) == FW_RI_SEND_WITH_INV) || \ + (CQE_OPCODE_G(be32_to_cpu((x)->header)) == FW_RI_SEND_WITH_SE_INV)) #define CQE_LEN(x) (be32_to_cpu((x)->len)) @@ -237,25 +237,25 @@ struct t4_cqe { #define CQE_WRID_LOW(x) (be32_to_cpu((x)->u.gen.wrid_low)) /* macros for flit 3 of the cqe */ -#define S_CQE_GENBIT 63 -#define M_CQE_GENBIT 0x1 -#define G_CQE_GENBIT(x) (((x) >> S_CQE_GENBIT) & M_CQE_GENBIT) -#define V_CQE_GENBIT(x) ((x)<<S_CQE_GENBIT) +#define CQE_GENBIT_S 63 +#define CQE_GENBIT_M 0x1 +#define CQE_GENBIT_G(x) (((x) >> CQE_GENBIT_S) & CQE_GENBIT_M) +#define CQE_GENBIT_V(x) ((x)<<CQE_GENBIT_S) -#define S_CQE_OVFBIT 62 -#define M_CQE_OVFBIT 0x1 -#define G_CQE_OVFBIT(x) ((((x) >> S_CQE_OVFBIT)) & M_CQE_OVFBIT) +#define CQE_OVFBIT_S 62 +#define CQE_OVFBIT_M 0x1 +#define CQE_OVFBIT_G(x) ((((x) >> CQE_OVFBIT_S)) & CQE_OVFBIT_M) -#define S_CQE_IQTYPE 60 -#define M_CQE_IQTYPE 0x3 -#define G_CQE_IQTYPE(x) ((((x) >> S_CQE_IQTYPE)) & M_CQE_IQTYPE) +#define CQE_IQTYPE_S 60 +#define CQE_IQTYPE_M 0x3 +#define CQE_IQTYPE_G(x) ((((x) >> CQE_IQTYPE_S)) & CQE_IQTYPE_M) -#define M_CQE_TS 0x0fffffffffffffffULL -#define G_CQE_TS(x) ((x) & M_CQE_TS) +#define CQE_TS_M 0x0fffffffffffffffULL +#define CQE_TS_G(x) ((x) & CQE_TS_M) -#define CQE_OVFBIT(x) ((unsigned)G_CQE_OVFBIT(be64_to_cpu((x)->bits_type_ts))) -#define CQE_GENBIT(x) ((unsigned)G_CQE_GENBIT(be64_to_cpu((x)->bits_type_ts))) -#define CQE_TS(x) (G_CQE_TS(be64_to_cpu((x)->bits_type_ts))) +#define CQE_OVFBIT(x) ((unsigned)CQE_OVFBIT_G(be64_to_cpu((x)->bits_type_ts))) +#define CQE_GENBIT(x) ((unsigned)CQE_GENBIT_G(be64_to_cpu((x)->bits_type_ts))) +#define CQE_TS(x) (CQE_TS_G(be64_to_cpu((x)->bits_type_ts))) struct t4_swsqe { u64 wr_id; @@ -465,14 +465,14 @@ static inline void t4_ring_sq_db(struct t4_wq *wq, u16 inc, u8 t5, } else { PDBG("%s: DB wq->sq.pidx = %d\n", __func__, wq->sq.pidx); - writel(PIDX_T5(inc), wq->sq.udb); + writel(PIDX_T5_V(inc), wq->sq.udb); } /* Flush user doorbell area writes. */ wmb(); return; } - writel(QID(wq->sq.qid) | PIDX(inc), wq->db); + writel(QID_V(wq->sq.qid) | PIDX_V(inc), wq->db); } static inline void t4_ring_rq_db(struct t4_wq *wq, u16 inc, u8 t5, @@ -489,14 +489,14 @@ static inline void t4_ring_rq_db(struct t4_wq *wq, u16 inc, u8 t5, } else { PDBG("%s: DB wq->rq.pidx = %d\n", __func__, wq->rq.pidx); - writel(PIDX_T5(inc), wq->rq.udb); + writel(PIDX_T5_V(inc), wq->rq.udb); } /* Flush user doorbell area writes. */ wmb(); return; } - writel(QID(wq->rq.qid) | PIDX(inc), wq->db); + writel(QID_V(wq->rq.qid) | PIDX_V(inc), wq->db); } static inline int t4_wq_in_error(struct t4_wq *wq) @@ -561,14 +561,14 @@ static inline int t4_arm_cq(struct t4_cq *cq, int se) u32 val; set_bit(CQ_ARMED, &cq->flags); - while (cq->cidx_inc > CIDXINC_MASK) { - val = SEINTARM(0) | CIDXINC(CIDXINC_MASK) | TIMERREG(7) | - INGRESSQID(cq->cqid); + while (cq->cidx_inc > CIDXINC_M) { + val = SEINTARM_V(0) | CIDXINC_V(CIDXINC_M) | TIMERREG_V(7) | + INGRESSQID_V(cq->cqid); writel(val, cq->gts); - cq->cidx_inc -= CIDXINC_MASK; + cq->cidx_inc -= CIDXINC_M; } - val = SEINTARM(se) | CIDXINC(cq->cidx_inc) | TIMERREG(6) | - INGRESSQID(cq->cqid); + val = SEINTARM_V(se) | CIDXINC_V(cq->cidx_inc) | TIMERREG_V(6) | + INGRESSQID_V(cq->cqid); writel(val, cq->gts); cq->cidx_inc = 0; return 0; @@ -597,11 +597,11 @@ static inline void t4_swcq_consume(struct t4_cq *cq) static inline void t4_hwcq_consume(struct t4_cq *cq) { cq->bits_type_ts = cq->queue[cq->cidx].bits_type_ts; - if (++cq->cidx_inc == (cq->size >> 4) || cq->cidx_inc == CIDXINC_MASK) { + if (++cq->cidx_inc == (cq->size >> 4) || cq->cidx_inc == CIDXINC_M) { u32 val; - val = SEINTARM(0) | CIDXINC(cq->cidx_inc) | TIMERREG(7) | - INGRESSQID(cq->cqid); + val = SEINTARM_V(0) | CIDXINC_V(cq->cidx_inc) | TIMERREG_V(7) | + INGRESSQID_V(cq->cqid); writel(val, cq->gts); cq->cidx_inc = 0; } diff --git a/drivers/infiniband/hw/cxgb4/t4fw_ri_api.h b/drivers/infiniband/hw/cxgb4/t4fw_ri_api.h index 5709e77faf7c..5e53327fc647 100644 --- a/drivers/infiniband/hw/cxgb4/t4fw_ri_api.h +++ b/drivers/infiniband/hw/cxgb4/t4fw_ri_api.h @@ -162,102 +162,102 @@ struct fw_ri_tpte { __be32 len_hi; }; -#define S_FW_RI_TPTE_VALID 31 -#define M_FW_RI_TPTE_VALID 0x1 -#define V_FW_RI_TPTE_VALID(x) ((x) << S_FW_RI_TPTE_VALID) -#define G_FW_RI_TPTE_VALID(x) \ - (((x) >> S_FW_RI_TPTE_VALID) & M_FW_RI_TPTE_VALID) -#define F_FW_RI_TPTE_VALID V_FW_RI_TPTE_VALID(1U) - -#define S_FW_RI_TPTE_STAGKEY 23 -#define M_FW_RI_TPTE_STAGKEY 0xff -#define V_FW_RI_TPTE_STAGKEY(x) ((x) << S_FW_RI_TPTE_STAGKEY) -#define G_FW_RI_TPTE_STAGKEY(x) \ - (((x) >> S_FW_RI_TPTE_STAGKEY) & M_FW_RI_TPTE_STAGKEY) - -#define S_FW_RI_TPTE_STAGSTATE 22 -#define M_FW_RI_TPTE_STAGSTATE 0x1 -#define V_FW_RI_TPTE_STAGSTATE(x) ((x) << S_FW_RI_TPTE_STAGSTATE) -#define G_FW_RI_TPTE_STAGSTATE(x) \ - (((x) >> S_FW_RI_TPTE_STAGSTATE) & M_FW_RI_TPTE_STAGSTATE) -#define F_FW_RI_TPTE_STAGSTATE V_FW_RI_TPTE_STAGSTATE(1U) - -#define S_FW_RI_TPTE_STAGTYPE 20 -#define M_FW_RI_TPTE_STAGTYPE 0x3 -#define V_FW_RI_TPTE_STAGTYPE(x) ((x) << S_FW_RI_TPTE_STAGTYPE) -#define G_FW_RI_TPTE_STAGTYPE(x) \ - (((x) >> S_FW_RI_TPTE_STAGTYPE) & M_FW_RI_TPTE_STAGTYPE) - -#define S_FW_RI_TPTE_PDID 0 -#define M_FW_RI_TPTE_PDID 0xfffff -#define V_FW_RI_TPTE_PDID(x) ((x) << S_FW_RI_TPTE_PDID) -#define G_FW_RI_TPTE_PDID(x) \ - (((x) >> S_FW_RI_TPTE_PDID) & M_FW_RI_TPTE_PDID) - -#define S_FW_RI_TPTE_PERM 28 -#define M_FW_RI_TPTE_PERM 0xf -#define V_FW_RI_TPTE_PERM(x) ((x) << S_FW_RI_TPTE_PERM) -#define G_FW_RI_TPTE_PERM(x) \ - (((x) >> S_FW_RI_TPTE_PERM) & M_FW_RI_TPTE_PERM) - -#define S_FW_RI_TPTE_REMINVDIS 27 -#define M_FW_RI_TPTE_REMINVDIS 0x1 -#define V_FW_RI_TPTE_REMINVDIS(x) ((x) << S_FW_RI_TPTE_REMINVDIS) -#define G_FW_RI_TPTE_REMINVDIS(x) \ - (((x) >> S_FW_RI_TPTE_REMINVDIS) & M_FW_RI_TPTE_REMINVDIS) -#define F_FW_RI_TPTE_REMINVDIS V_FW_RI_TPTE_REMINVDIS(1U) - -#define S_FW_RI_TPTE_ADDRTYPE 26 -#define M_FW_RI_TPTE_ADDRTYPE 1 -#define V_FW_RI_TPTE_ADDRTYPE(x) ((x) << S_FW_RI_TPTE_ADDRTYPE) -#define G_FW_RI_TPTE_ADDRTYPE(x) \ - (((x) >> S_FW_RI_TPTE_ADDRTYPE) & M_FW_RI_TPTE_ADDRTYPE) -#define F_FW_RI_TPTE_ADDRTYPE V_FW_RI_TPTE_ADDRTYPE(1U) - -#define S_FW_RI_TPTE_MWBINDEN 25 -#define M_FW_RI_TPTE_MWBINDEN 0x1 -#define V_FW_RI_TPTE_MWBINDEN(x) ((x) << S_FW_RI_TPTE_MWBINDEN) -#define G_FW_RI_TPTE_MWBINDEN(x) \ - (((x) >> S_FW_RI_TPTE_MWBINDEN) & M_FW_RI_TPTE_MWBINDEN) -#define F_FW_RI_TPTE_MWBINDEN V_FW_RI_TPTE_MWBINDEN(1U) - -#define S_FW_RI_TPTE_PS 20 -#define M_FW_RI_TPTE_PS 0x1f -#define V_FW_RI_TPTE_PS(x) ((x) << S_FW_RI_TPTE_PS) -#define G_FW_RI_TPTE_PS(x) \ - (((x) >> S_FW_RI_TPTE_PS) & M_FW_RI_TPTE_PS) - -#define S_FW_RI_TPTE_QPID 0 -#define M_FW_RI_TPTE_QPID 0xfffff -#define V_FW_RI_TPTE_QPID(x) ((x) << S_FW_RI_TPTE_QPID) -#define G_FW_RI_TPTE_QPID(x) \ - (((x) >> S_FW_RI_TPTE_QPID) & M_FW_RI_TPTE_QPID) - -#define S_FW_RI_TPTE_NOSNOOP 30 -#define M_FW_RI_TPTE_NOSNOOP 0x1 -#define V_FW_RI_TPTE_NOSNOOP(x) ((x) << S_FW_RI_TPTE_NOSNOOP) -#define G_FW_RI_TPTE_NOSNOOP(x) \ - (((x) >> S_FW_RI_TPTE_NOSNOOP) & M_FW_RI_TPTE_NOSNOOP) -#define F_FW_RI_TPTE_NOSNOOP V_FW_RI_TPTE_NOSNOOP(1U) - -#define S_FW_RI_TPTE_PBLADDR 0 -#define M_FW_RI_TPTE_PBLADDR 0x1fffffff -#define V_FW_RI_TPTE_PBLADDR(x) ((x) << S_FW_RI_TPTE_PBLADDR) -#define G_FW_RI_TPTE_PBLADDR(x) \ - (((x) >> S_FW_RI_TPTE_PBLADDR) & M_FW_RI_TPTE_PBLADDR) - -#define S_FW_RI_TPTE_DCA 24 -#define M_FW_RI_TPTE_DCA 0x1f -#define V_FW_RI_TPTE_DCA(x) ((x) << S_FW_RI_TPTE_DCA) -#define G_FW_RI_TPTE_DCA(x) \ - (((x) >> S_FW_RI_TPTE_DCA) & M_FW_RI_TPTE_DCA) - -#define S_FW_RI_TPTE_MWBCNT_PSTAG 0 -#define M_FW_RI_TPTE_MWBCNT_PSTAG 0xffffff -#define V_FW_RI_TPTE_MWBCNT_PSTAT(x) \ - ((x) << S_FW_RI_TPTE_MWBCNT_PSTAG) -#define G_FW_RI_TPTE_MWBCNT_PSTAG(x) \ - (((x) >> S_FW_RI_TPTE_MWBCNT_PSTAG) & M_FW_RI_TPTE_MWBCNT_PSTAG) +#define FW_RI_TPTE_VALID_S 31 +#define FW_RI_TPTE_VALID_M 0x1 +#define FW_RI_TPTE_VALID_V(x) ((x) << FW_RI_TPTE_VALID_S) +#define FW_RI_TPTE_VALID_G(x) \ + (((x) >> FW_RI_TPTE_VALID_S) & FW_RI_TPTE_VALID_M) +#define FW_RI_TPTE_VALID_F FW_RI_TPTE_VALID_V(1U) + +#define FW_RI_TPTE_STAGKEY_S 23 +#define FW_RI_TPTE_STAGKEY_M 0xff +#define FW_RI_TPTE_STAGKEY_V(x) ((x) << FW_RI_TPTE_STAGKEY_S) +#define FW_RI_TPTE_STAGKEY_G(x) \ + (((x) >> FW_RI_TPTE_STAGKEY_S) & FW_RI_TPTE_STAGKEY_M) + +#define FW_RI_TPTE_STAGSTATE_S 22 +#define FW_RI_TPTE_STAGSTATE_M 0x1 +#define FW_RI_TPTE_STAGSTATE_V(x) ((x) << FW_RI_TPTE_STAGSTATE_S) +#define FW_RI_TPTE_STAGSTATE_G(x) \ + (((x) >> FW_RI_TPTE_STAGSTATE_S) & FW_RI_TPTE_STAGSTATE_M) +#define FW_RI_TPTE_STAGSTATE_F FW_RI_TPTE_STAGSTATE_V(1U) + +#define FW_RI_TPTE_STAGTYPE_S 20 +#define FW_RI_TPTE_STAGTYPE_M 0x3 +#define FW_RI_TPTE_STAGTYPE_V(x) ((x) << FW_RI_TPTE_STAGTYPE_S) +#define FW_RI_TPTE_STAGTYPE_G(x) \ + (((x) >> FW_RI_TPTE_STAGTYPE_S) & FW_RI_TPTE_STAGTYPE_M) + +#define FW_RI_TPTE_PDID_S 0 +#define FW_RI_TPTE_PDID_M 0xfffff +#define FW_RI_TPTE_PDID_V(x) ((x) << FW_RI_TPTE_PDID_S) +#define FW_RI_TPTE_PDID_G(x) \ + (((x) >> FW_RI_TPTE_PDID_S) & FW_RI_TPTE_PDID_M) + +#define FW_RI_TPTE_PERM_S 28 +#define FW_RI_TPTE_PERM_M 0xf +#define FW_RI_TPTE_PERM_V(x) ((x) << FW_RI_TPTE_PERM_S) +#define FW_RI_TPTE_PERM_G(x) \ + (((x) >> FW_RI_TPTE_PERM_S) & FW_RI_TPTE_PERM_M) + +#define FW_RI_TPTE_REMINVDIS_S 27 +#define FW_RI_TPTE_REMINVDIS_M 0x1 +#define FW_RI_TPTE_REMINVDIS_V(x) ((x) << FW_RI_TPTE_REMINVDIS_S) +#define FW_RI_TPTE_REMINVDIS_G(x) \ + (((x) >> FW_RI_TPTE_REMINVDIS_S) & FW_RI_TPTE_REMINVDIS_M) +#define FW_RI_TPTE_REMINVDIS_F FW_RI_TPTE_REMINVDIS_V(1U) + +#define FW_RI_TPTE_ADDRTYPE_S 26 +#define FW_RI_TPTE_ADDRTYPE_M 1 +#define FW_RI_TPTE_ADDRTYPE_V(x) ((x) << FW_RI_TPTE_ADDRTYPE_S) +#define FW_RI_TPTE_ADDRTYPE_G(x) \ + (((x) >> FW_RI_TPTE_ADDRTYPE_S) & FW_RI_TPTE_ADDRTYPE_M) +#define FW_RI_TPTE_ADDRTYPE_F FW_RI_TPTE_ADDRTYPE_V(1U) + +#define FW_RI_TPTE_MWBINDEN_S 25 +#define FW_RI_TPTE_MWBINDEN_M 0x1 +#define FW_RI_TPTE_MWBINDEN_V(x) ((x) << FW_RI_TPTE_MWBINDEN_S) +#define FW_RI_TPTE_MWBINDEN_G(x) \ + (((x) >> FW_RI_TPTE_MWBINDEN_S) & FW_RI_TPTE_MWBINDEN_M) +#define FW_RI_TPTE_MWBINDEN_F FW_RI_TPTE_MWBINDEN_V(1U) + +#define FW_RI_TPTE_PS_S 20 +#define FW_RI_TPTE_PS_M 0x1f +#define FW_RI_TPTE_PS_V(x) ((x) << FW_RI_TPTE_PS_S) +#define FW_RI_TPTE_PS_G(x) \ + (((x) >> FW_RI_TPTE_PS_S) & FW_RI_TPTE_PS_M) + +#define FW_RI_TPTE_QPID_S 0 +#define FW_RI_TPTE_QPID_M 0xfffff +#define FW_RI_TPTE_QPID_V(x) ((x) << FW_RI_TPTE_QPID_S) +#define FW_RI_TPTE_QPID_G(x) \ + (((x) >> FW_RI_TPTE_QPID_S) & FW_RI_TPTE_QPID_M) + +#define FW_RI_TPTE_NOSNOOP_S 30 +#define FW_RI_TPTE_NOSNOOP_M 0x1 +#define FW_RI_TPTE_NOSNOOP_V(x) ((x) << FW_RI_TPTE_NOSNOOP_S) +#define FW_RI_TPTE_NOSNOOP_G(x) \ + (((x) >> FW_RI_TPTE_NOSNOOP_S) & FW_RI_TPTE_NOSNOOP_M) +#define FW_RI_TPTE_NOSNOOP_F FW_RI_TPTE_NOSNOOP_V(1U) + +#define FW_RI_TPTE_PBLADDR_S 0 +#define FW_RI_TPTE_PBLADDR_M 0x1fffffff +#define FW_RI_TPTE_PBLADDR_V(x) ((x) << FW_RI_TPTE_PBLADDR_S) +#define FW_RI_TPTE_PBLADDR_G(x) \ + (((x) >> FW_RI_TPTE_PBLADDR_S) & FW_RI_TPTE_PBLADDR_M) + +#define FW_RI_TPTE_DCA_S 24 +#define FW_RI_TPTE_DCA_M 0x1f +#define FW_RI_TPTE_DCA_V(x) ((x) << FW_RI_TPTE_DCA_S) +#define FW_RI_TPTE_DCA_G(x) \ + (((x) >> FW_RI_TPTE_DCA_S) & FW_RI_TPTE_DCA_M) + +#define FW_RI_TPTE_MWBCNT_PSTAG_S 0 +#define FW_RI_TPTE_MWBCNT_PSTAG_M 0xffffff +#define FW_RI_TPTE_MWBCNT_PSTAT_V(x) \ + ((x) << FW_RI_TPTE_MWBCNT_PSTAG_S) +#define FW_RI_TPTE_MWBCNT_PSTAG_G(x) \ + (((x) >> FW_RI_TPTE_MWBCNT_PSTAG_S) & FW_RI_TPTE_MWBCNT_PSTAG_M) enum fw_ri_res_type { FW_RI_RES_TYPE_SQ, @@ -308,222 +308,222 @@ struct fw_ri_res_wr { #endif }; -#define S_FW_RI_RES_WR_NRES 0 -#define M_FW_RI_RES_WR_NRES 0xff -#define V_FW_RI_RES_WR_NRES(x) ((x) << S_FW_RI_RES_WR_NRES) -#define G_FW_RI_RES_WR_NRES(x) \ - (((x) >> S_FW_RI_RES_WR_NRES) & M_FW_RI_RES_WR_NRES) - -#define S_FW_RI_RES_WR_FETCHSZM 26 -#define M_FW_RI_RES_WR_FETCHSZM 0x1 -#define V_FW_RI_RES_WR_FETCHSZM(x) ((x) << S_FW_RI_RES_WR_FETCHSZM) -#define G_FW_RI_RES_WR_FETCHSZM(x) \ - (((x) >> S_FW_RI_RES_WR_FETCHSZM) & M_FW_RI_RES_WR_FETCHSZM) -#define F_FW_RI_RES_WR_FETCHSZM V_FW_RI_RES_WR_FETCHSZM(1U) - -#define S_FW_RI_RES_WR_STATUSPGNS 25 -#define M_FW_RI_RES_WR_STATUSPGNS 0x1 -#define V_FW_RI_RES_WR_STATUSPGNS(x) ((x) << S_FW_RI_RES_WR_STATUSPGNS) -#define G_FW_RI_RES_WR_STATUSPGNS(x) \ - (((x) >> S_FW_RI_RES_WR_STATUSPGNS) & M_FW_RI_RES_WR_STATUSPGNS) -#define F_FW_RI_RES_WR_STATUSPGNS V_FW_RI_RES_WR_STATUSPGNS(1U) - -#define S_FW_RI_RES_WR_STATUSPGRO 24 -#define M_FW_RI_RES_WR_STATUSPGRO 0x1 -#define V_FW_RI_RES_WR_STATUSPGRO(x) ((x) << S_FW_RI_RES_WR_STATUSPGRO) -#define G_FW_RI_RES_WR_STATUSPGRO(x) \ - (((x) >> S_FW_RI_RES_WR_STATUSPGRO) & M_FW_RI_RES_WR_STATUSPGRO) -#define F_FW_RI_RES_WR_STATUSPGRO V_FW_RI_RES_WR_STATUSPGRO(1U) - -#define S_FW_RI_RES_WR_FETCHNS 23 -#define M_FW_RI_RES_WR_FETCHNS 0x1 -#define V_FW_RI_RES_WR_FETCHNS(x) ((x) << S_FW_RI_RES_WR_FETCHNS) -#define G_FW_RI_RES_WR_FETCHNS(x) \ - (((x) >> S_FW_RI_RES_WR_FETCHNS) & M_FW_RI_RES_WR_FETCHNS) -#define F_FW_RI_RES_WR_FETCHNS V_FW_RI_RES_WR_FETCHNS(1U) - -#define S_FW_RI_RES_WR_FETCHRO 22 -#define M_FW_RI_RES_WR_FETCHRO 0x1 -#define V_FW_RI_RES_WR_FETCHRO(x) ((x) << S_FW_RI_RES_WR_FETCHRO) -#define G_FW_RI_RES_WR_FETCHRO(x) \ - (((x) >> S_FW_RI_RES_WR_FETCHRO) & M_FW_RI_RES_WR_FETCHRO) -#define F_FW_RI_RES_WR_FETCHRO V_FW_RI_RES_WR_FETCHRO(1U) - -#define S_FW_RI_RES_WR_HOSTFCMODE 20 -#define M_FW_RI_RES_WR_HOSTFCMODE 0x3 -#define V_FW_RI_RES_WR_HOSTFCMODE(x) ((x) << S_FW_RI_RES_WR_HOSTFCMODE) -#define G_FW_RI_RES_WR_HOSTFCMODE(x) \ - (((x) >> S_FW_RI_RES_WR_HOSTFCMODE) & M_FW_RI_RES_WR_HOSTFCMODE) - -#define S_FW_RI_RES_WR_CPRIO 19 -#define M_FW_RI_RES_WR_CPRIO 0x1 -#define V_FW_RI_RES_WR_CPRIO(x) ((x) << S_FW_RI_RES_WR_CPRIO) -#define G_FW_RI_RES_WR_CPRIO(x) \ - (((x) >> S_FW_RI_RES_WR_CPRIO) & M_FW_RI_RES_WR_CPRIO) -#define F_FW_RI_RES_WR_CPRIO V_FW_RI_RES_WR_CPRIO(1U) - -#define S_FW_RI_RES_WR_ONCHIP 18 -#define M_FW_RI_RES_WR_ONCHIP 0x1 -#define V_FW_RI_RES_WR_ONCHIP(x) ((x) << S_FW_RI_RES_WR_ONCHIP) -#define G_FW_RI_RES_WR_ONCHIP(x) \ - (((x) >> S_FW_RI_RES_WR_ONCHIP) & M_FW_RI_RES_WR_ONCHIP) -#define F_FW_RI_RES_WR_ONCHIP V_FW_RI_RES_WR_ONCHIP(1U) - -#define S_FW_RI_RES_WR_PCIECHN 16 -#define M_FW_RI_RES_WR_PCIECHN 0x3 -#define V_FW_RI_RES_WR_PCIECHN(x) ((x) << S_FW_RI_RES_WR_PCIECHN) -#define G_FW_RI_RES_WR_PCIECHN(x) \ - (((x) >> S_FW_RI_RES_WR_PCIECHN) & M_FW_RI_RES_WR_PCIECHN) - -#define S_FW_RI_RES_WR_IQID 0 -#define M_FW_RI_RES_WR_IQID 0xffff -#define V_FW_RI_RES_WR_IQID(x) ((x) << S_FW_RI_RES_WR_IQID) -#define G_FW_RI_RES_WR_IQID(x) \ - (((x) >> S_FW_RI_RES_WR_IQID) & M_FW_RI_RES_WR_IQID) - -#define S_FW_RI_RES_WR_DCAEN 31 -#define M_FW_RI_RES_WR_DCAEN 0x1 -#define V_FW_RI_RES_WR_DCAEN(x) ((x) << S_FW_RI_RES_WR_DCAEN) -#define G_FW_RI_RES_WR_DCAEN(x) \ - (((x) >> S_FW_RI_RES_WR_DCAEN) & M_FW_RI_RES_WR_DCAEN) -#define F_FW_RI_RES_WR_DCAEN V_FW_RI_RES_WR_DCAEN(1U) - -#define S_FW_RI_RES_WR_DCACPU 26 -#define M_FW_RI_RES_WR_DCACPU 0x1f -#define V_FW_RI_RES_WR_DCACPU(x) ((x) << S_FW_RI_RES_WR_DCACPU) -#define G_FW_RI_RES_WR_DCACPU(x) \ - (((x) >> S_FW_RI_RES_WR_DCACPU) & M_FW_RI_RES_WR_DCACPU) - -#define S_FW_RI_RES_WR_FBMIN 23 -#define M_FW_RI_RES_WR_FBMIN 0x7 -#define V_FW_RI_RES_WR_FBMIN(x) ((x) << S_FW_RI_RES_WR_FBMIN) -#define G_FW_RI_RES_WR_FBMIN(x) \ - (((x) >> S_FW_RI_RES_WR_FBMIN) & M_FW_RI_RES_WR_FBMIN) - -#define S_FW_RI_RES_WR_FBMAX 20 -#define M_FW_RI_RES_WR_FBMAX 0x7 -#define V_FW_RI_RES_WR_FBMAX(x) ((x) << S_FW_RI_RES_WR_FBMAX) -#define G_FW_RI_RES_WR_FBMAX(x) \ - (((x) >> S_FW_RI_RES_WR_FBMAX) & M_FW_RI_RES_WR_FBMAX) - -#define S_FW_RI_RES_WR_CIDXFTHRESHO 19 -#define M_FW_RI_RES_WR_CIDXFTHRESHO 0x1 -#define V_FW_RI_RES_WR_CIDXFTHRESHO(x) ((x) << S_FW_RI_RES_WR_CIDXFTHRESHO) -#define G_FW_RI_RES_WR_CIDXFTHRESHO(x) \ - (((x) >> S_FW_RI_RES_WR_CIDXFTHRESHO) & M_FW_RI_RES_WR_CIDXFTHRESHO) -#define F_FW_RI_RES_WR_CIDXFTHRESHO V_FW_RI_RES_WR_CIDXFTHRESHO(1U) - -#define S_FW_RI_RES_WR_CIDXFTHRESH 16 -#define M_FW_RI_RES_WR_CIDXFTHRESH 0x7 -#define V_FW_RI_RES_WR_CIDXFTHRESH(x) ((x) << S_FW_RI_RES_WR_CIDXFTHRESH) -#define G_FW_RI_RES_WR_CIDXFTHRESH(x) \ - (((x) >> S_FW_RI_RES_WR_CIDXFTHRESH) & M_FW_RI_RES_WR_CIDXFTHRESH) - -#define S_FW_RI_RES_WR_EQSIZE 0 -#define M_FW_RI_RES_WR_EQSIZE 0xffff -#define V_FW_RI_RES_WR_EQSIZE(x) ((x) << S_FW_RI_RES_WR_EQSIZE) -#define G_FW_RI_RES_WR_EQSIZE(x) \ - (((x) >> S_FW_RI_RES_WR_EQSIZE) & M_FW_RI_RES_WR_EQSIZE) - -#define S_FW_RI_RES_WR_IQANDST 15 -#define M_FW_RI_RES_WR_IQANDST 0x1 -#define V_FW_RI_RES_WR_IQANDST(x) ((x) << S_FW_RI_RES_WR_IQANDST) -#define G_FW_RI_RES_WR_IQANDST(x) \ - (((x) >> S_FW_RI_RES_WR_IQANDST) & M_FW_RI_RES_WR_IQANDST) -#define F_FW_RI_RES_WR_IQANDST V_FW_RI_RES_WR_IQANDST(1U) - -#define S_FW_RI_RES_WR_IQANUS 14 -#define M_FW_RI_RES_WR_IQANUS 0x1 -#define V_FW_RI_RES_WR_IQANUS(x) ((x) << S_FW_RI_RES_WR_IQANUS) -#define G_FW_RI_RES_WR_IQANUS(x) \ - (((x) >> S_FW_RI_RES_WR_IQANUS) & M_FW_RI_RES_WR_IQANUS) -#define F_FW_RI_RES_WR_IQANUS V_FW_RI_RES_WR_IQANUS(1U) - -#define S_FW_RI_RES_WR_IQANUD 12 -#define M_FW_RI_RES_WR_IQANUD 0x3 -#define V_FW_RI_RES_WR_IQANUD(x) ((x) << S_FW_RI_RES_WR_IQANUD) -#define G_FW_RI_RES_WR_IQANUD(x) \ - (((x) >> S_FW_RI_RES_WR_IQANUD) & M_FW_RI_RES_WR_IQANUD) - -#define S_FW_RI_RES_WR_IQANDSTINDEX 0 -#define M_FW_RI_RES_WR_IQANDSTINDEX 0xfff -#define V_FW_RI_RES_WR_IQANDSTINDEX(x) ((x) << S_FW_RI_RES_WR_IQANDSTINDEX) -#define G_FW_RI_RES_WR_IQANDSTINDEX(x) \ - (((x) >> S_FW_RI_RES_WR_IQANDSTINDEX) & M_FW_RI_RES_WR_IQANDSTINDEX) - -#define S_FW_RI_RES_WR_IQDROPRSS 15 -#define M_FW_RI_RES_WR_IQDROPRSS 0x1 -#define V_FW_RI_RES_WR_IQDROPRSS(x) ((x) << S_FW_RI_RES_WR_IQDROPRSS) -#define G_FW_RI_RES_WR_IQDROPRSS(x) \ - (((x) >> S_FW_RI_RES_WR_IQDROPRSS) & M_FW_RI_RES_WR_IQDROPRSS) -#define F_FW_RI_RES_WR_IQDROPRSS V_FW_RI_RES_WR_IQDROPRSS(1U) - -#define S_FW_RI_RES_WR_IQGTSMODE 14 -#define M_FW_RI_RES_WR_IQGTSMODE 0x1 -#define V_FW_RI_RES_WR_IQGTSMODE(x) ((x) << S_FW_RI_RES_WR_IQGTSMODE) -#define G_FW_RI_RES_WR_IQGTSMODE(x) \ - (((x) >> S_FW_RI_RES_WR_IQGTSMODE) & M_FW_RI_RES_WR_IQGTSMODE) -#define F_FW_RI_RES_WR_IQGTSMODE V_FW_RI_RES_WR_IQGTSMODE(1U) - -#define S_FW_RI_RES_WR_IQPCIECH 12 -#define M_FW_RI_RES_WR_IQPCIECH 0x3 -#define V_FW_RI_RES_WR_IQPCIECH(x) ((x) << S_FW_RI_RES_WR_IQPCIECH) -#define G_FW_RI_RES_WR_IQPCIECH(x) \ - (((x) >> S_FW_RI_RES_WR_IQPCIECH) & M_FW_RI_RES_WR_IQPCIECH) - -#define S_FW_RI_RES_WR_IQDCAEN 11 -#define M_FW_RI_RES_WR_IQDCAEN 0x1 -#define V_FW_RI_RES_WR_IQDCAEN(x) ((x) << S_FW_RI_RES_WR_IQDCAEN) -#define G_FW_RI_RES_WR_IQDCAEN(x) \ - (((x) >> S_FW_RI_RES_WR_IQDCAEN) & M_FW_RI_RES_WR_IQDCAEN) -#define F_FW_RI_RES_WR_IQDCAEN V_FW_RI_RES_WR_IQDCAEN(1U) - -#define S_FW_RI_RES_WR_IQDCACPU 6 -#define M_FW_RI_RES_WR_IQDCACPU 0x1f -#define V_FW_RI_RES_WR_IQDCACPU(x) ((x) << S_FW_RI_RES_WR_IQDCACPU) -#define G_FW_RI_RES_WR_IQDCACPU(x) \ - (((x) >> S_FW_RI_RES_WR_IQDCACPU) & M_FW_RI_RES_WR_IQDCACPU) - -#define S_FW_RI_RES_WR_IQINTCNTTHRESH 4 -#define M_FW_RI_RES_WR_IQINTCNTTHRESH 0x3 -#define V_FW_RI_RES_WR_IQINTCNTTHRESH(x) \ - ((x) << S_FW_RI_RES_WR_IQINTCNTTHRESH) -#define G_FW_RI_RES_WR_IQINTCNTTHRESH(x) \ - (((x) >> S_FW_RI_RES_WR_IQINTCNTTHRESH) & M_FW_RI_RES_WR_IQINTCNTTHRESH) - -#define S_FW_RI_RES_WR_IQO 3 -#define M_FW_RI_RES_WR_IQO 0x1 -#define V_FW_RI_RES_WR_IQO(x) ((x) << S_FW_RI_RES_WR_IQO) -#define G_FW_RI_RES_WR_IQO(x) \ - (((x) >> S_FW_RI_RES_WR_IQO) & M_FW_RI_RES_WR_IQO) -#define F_FW_RI_RES_WR_IQO V_FW_RI_RES_WR_IQO(1U) - -#define S_FW_RI_RES_WR_IQCPRIO 2 -#define M_FW_RI_RES_WR_IQCPRIO 0x1 -#define V_FW_RI_RES_WR_IQCPRIO(x) ((x) << S_FW_RI_RES_WR_IQCPRIO) -#define G_FW_RI_RES_WR_IQCPRIO(x) \ - (((x) >> S_FW_RI_RES_WR_IQCPRIO) & M_FW_RI_RES_WR_IQCPRIO) -#define F_FW_RI_RES_WR_IQCPRIO V_FW_RI_RES_WR_IQCPRIO(1U) - -#define S_FW_RI_RES_WR_IQESIZE 0 -#define M_FW_RI_RES_WR_IQESIZE 0x3 -#define V_FW_RI_RES_WR_IQESIZE(x) ((x) << S_FW_RI_RES_WR_IQESIZE) -#define G_FW_RI_RES_WR_IQESIZE(x) \ - (((x) >> S_FW_RI_RES_WR_IQESIZE) & M_FW_RI_RES_WR_IQESIZE) - -#define S_FW_RI_RES_WR_IQNS 31 -#define M_FW_RI_RES_WR_IQNS 0x1 -#define V_FW_RI_RES_WR_IQNS(x) ((x) << S_FW_RI_RES_WR_IQNS) -#define G_FW_RI_RES_WR_IQNS(x) \ - (((x) >> S_FW_RI_RES_WR_IQNS) & M_FW_RI_RES_WR_IQNS) -#define F_FW_RI_RES_WR_IQNS V_FW_RI_RES_WR_IQNS(1U) - -#define S_FW_RI_RES_WR_IQRO 30 -#define M_FW_RI_RES_WR_IQRO 0x1 -#define V_FW_RI_RES_WR_IQRO(x) ((x) << S_FW_RI_RES_WR_IQRO) -#define G_FW_RI_RES_WR_IQRO(x) \ - (((x) >> S_FW_RI_RES_WR_IQRO) & M_FW_RI_RES_WR_IQRO) -#define F_FW_RI_RES_WR_IQRO V_FW_RI_RES_WR_IQRO(1U) +#define FW_RI_RES_WR_NRES_S 0 +#define FW_RI_RES_WR_NRES_M 0xff +#define FW_RI_RES_WR_NRES_V(x) ((x) << FW_RI_RES_WR_NRES_S) +#define FW_RI_RES_WR_NRES_G(x) \ + (((x) >> FW_RI_RES_WR_NRES_S) & FW_RI_RES_WR_NRES_M) + +#define FW_RI_RES_WR_FETCHSZM_S 26 +#define FW_RI_RES_WR_FETCHSZM_M 0x1 +#define FW_RI_RES_WR_FETCHSZM_V(x) ((x) << FW_RI_RES_WR_FETCHSZM_S) +#define FW_RI_RES_WR_FETCHSZM_G(x) \ + (((x) >> FW_RI_RES_WR_FETCHSZM_S) & FW_RI_RES_WR_FETCHSZM_M) +#define FW_RI_RES_WR_FETCHSZM_F FW_RI_RES_WR_FETCHSZM_V(1U) + +#define FW_RI_RES_WR_STATUSPGNS_S 25 +#define FW_RI_RES_WR_STATUSPGNS_M 0x1 +#define FW_RI_RES_WR_STATUSPGNS_V(x) ((x) << FW_RI_RES_WR_STATUSPGNS_S) +#define FW_RI_RES_WR_STATUSPGNS_G(x) \ + (((x) >> FW_RI_RES_WR_STATUSPGNS_S) & FW_RI_RES_WR_STATUSPGNS_M) +#define FW_RI_RES_WR_STATUSPGNS_F FW_RI_RES_WR_STATUSPGNS_V(1U) + +#define FW_RI_RES_WR_STATUSPGRO_S 24 +#define FW_RI_RES_WR_STATUSPGRO_M 0x1 +#define FW_RI_RES_WR_STATUSPGRO_V(x) ((x) << FW_RI_RES_WR_STATUSPGRO_S) +#define FW_RI_RES_WR_STATUSPGRO_G(x) \ + (((x) >> FW_RI_RES_WR_STATUSPGRO_S) & FW_RI_RES_WR_STATUSPGRO_M) +#define FW_RI_RES_WR_STATUSPGRO_F FW_RI_RES_WR_STATUSPGRO_V(1U) + +#define FW_RI_RES_WR_FETCHNS_S 23 +#define FW_RI_RES_WR_FETCHNS_M 0x1 +#define FW_RI_RES_WR_FETCHNS_V(x) ((x) << FW_RI_RES_WR_FETCHNS_S) +#define FW_RI_RES_WR_FETCHNS_G(x) \ + (((x) >> FW_RI_RES_WR_FETCHNS_S) & FW_RI_RES_WR_FETCHNS_M) +#define FW_RI_RES_WR_FETCHNS_F FW_RI_RES_WR_FETCHNS_V(1U) + +#define FW_RI_RES_WR_FETCHRO_S 22 +#define FW_RI_RES_WR_FETCHRO_M 0x1 +#define FW_RI_RES_WR_FETCHRO_V(x) ((x) << FW_RI_RES_WR_FETCHRO_S) +#define FW_RI_RES_WR_FETCHRO_G(x) \ + (((x) >> FW_RI_RES_WR_FETCHRO_S) & FW_RI_RES_WR_FETCHRO_M) +#define FW_RI_RES_WR_FETCHRO_F FW_RI_RES_WR_FETCHRO_V(1U) + +#define FW_RI_RES_WR_HOSTFCMODE_S 20 +#define FW_RI_RES_WR_HOSTFCMODE_M 0x3 +#define FW_RI_RES_WR_HOSTFCMODE_V(x) ((x) << FW_RI_RES_WR_HOSTFCMODE_S) +#define FW_RI_RES_WR_HOSTFCMODE_G(x) \ + (((x) >> FW_RI_RES_WR_HOSTFCMODE_S) & FW_RI_RES_WR_HOSTFCMODE_M) + +#define FW_RI_RES_WR_CPRIO_S 19 +#define FW_RI_RES_WR_CPRIO_M 0x1 +#define FW_RI_RES_WR_CPRIO_V(x) ((x) << FW_RI_RES_WR_CPRIO_S) +#define FW_RI_RES_WR_CPRIO_G(x) \ + (((x) >> FW_RI_RES_WR_CPRIO_S) & FW_RI_RES_WR_CPRIO_M) +#define FW_RI_RES_WR_CPRIO_F FW_RI_RES_WR_CPRIO_V(1U) + +#define FW_RI_RES_WR_ONCHIP_S 18 +#define FW_RI_RES_WR_ONCHIP_M 0x1 +#define FW_RI_RES_WR_ONCHIP_V(x) ((x) << FW_RI_RES_WR_ONCHIP_S) +#define FW_RI_RES_WR_ONCHIP_G(x) \ + (((x) >> FW_RI_RES_WR_ONCHIP_S) & FW_RI_RES_WR_ONCHIP_M) +#define FW_RI_RES_WR_ONCHIP_F FW_RI_RES_WR_ONCHIP_V(1U) + +#define FW_RI_RES_WR_PCIECHN_S 16 +#define FW_RI_RES_WR_PCIECHN_M 0x3 +#define FW_RI_RES_WR_PCIECHN_V(x) ((x) << FW_RI_RES_WR_PCIECHN_S) +#define FW_RI_RES_WR_PCIECHN_G(x) \ + (((x) >> FW_RI_RES_WR_PCIECHN_S) & FW_RI_RES_WR_PCIECHN_M) + +#define FW_RI_RES_WR_IQID_S 0 +#define FW_RI_RES_WR_IQID_M 0xffff +#define FW_RI_RES_WR_IQID_V(x) ((x) << FW_RI_RES_WR_IQID_S) +#define FW_RI_RES_WR_IQID_G(x) \ + (((x) >> FW_RI_RES_WR_IQID_S) & FW_RI_RES_WR_IQID_M) + +#define FW_RI_RES_WR_DCAEN_S 31 +#define FW_RI_RES_WR_DCAEN_M 0x1 +#define FW_RI_RES_WR_DCAEN_V(x) ((x) << FW_RI_RES_WR_DCAEN_S) +#define FW_RI_RES_WR_DCAEN_G(x) \ + (((x) >> FW_RI_RES_WR_DCAEN_S) & FW_RI_RES_WR_DCAEN_M) +#define FW_RI_RES_WR_DCAEN_F FW_RI_RES_WR_DCAEN_V(1U) + +#define FW_RI_RES_WR_DCACPU_S 26 +#define FW_RI_RES_WR_DCACPU_M 0x1f +#define FW_RI_RES_WR_DCACPU_V(x) ((x) << FW_RI_RES_WR_DCACPU_S) +#define FW_RI_RES_WR_DCACPU_G(x) \ + (((x) >> FW_RI_RES_WR_DCACPU_S) & FW_RI_RES_WR_DCACPU_M) + +#define FW_RI_RES_WR_FBMIN_S 23 +#define FW_RI_RES_WR_FBMIN_M 0x7 +#define FW_RI_RES_WR_FBMIN_V(x) ((x) << FW_RI_RES_WR_FBMIN_S) +#define FW_RI_RES_WR_FBMIN_G(x) \ + (((x) >> FW_RI_RES_WR_FBMIN_S) & FW_RI_RES_WR_FBMIN_M) + +#define FW_RI_RES_WR_FBMAX_S 20 +#define FW_RI_RES_WR_FBMAX_M 0x7 +#define FW_RI_RES_WR_FBMAX_V(x) ((x) << FW_RI_RES_WR_FBMAX_S) +#define FW_RI_RES_WR_FBMAX_G(x) \ + (((x) >> FW_RI_RES_WR_FBMAX_S) & FW_RI_RES_WR_FBMAX_M) + +#define FW_RI_RES_WR_CIDXFTHRESHO_S 19 +#define FW_RI_RES_WR_CIDXFTHRESHO_M 0x1 +#define FW_RI_RES_WR_CIDXFTHRESHO_V(x) ((x) << FW_RI_RES_WR_CIDXFTHRESHO_S) +#define FW_RI_RES_WR_CIDXFTHRESHO_G(x) \ + (((x) >> FW_RI_RES_WR_CIDXFTHRESHO_S) & FW_RI_RES_WR_CIDXFTHRESHO_M) +#define FW_RI_RES_WR_CIDXFTHRESHO_F FW_RI_RES_WR_CIDXFTHRESHO_V(1U) + +#define FW_RI_RES_WR_CIDXFTHRESH_S 16 +#define FW_RI_RES_WR_CIDXFTHRESH_M 0x7 +#define FW_RI_RES_WR_CIDXFTHRESH_V(x) ((x) << FW_RI_RES_WR_CIDXFTHRESH_S) +#define FW_RI_RES_WR_CIDXFTHRESH_G(x) \ + (((x) >> FW_RI_RES_WR_CIDXFTHRESH_S) & FW_RI_RES_WR_CIDXFTHRESH_M) + +#define FW_RI_RES_WR_EQSIZE_S 0 +#define FW_RI_RES_WR_EQSIZE_M 0xffff +#define FW_RI_RES_WR_EQSIZE_V(x) ((x) << FW_RI_RES_WR_EQSIZE_S) +#define FW_RI_RES_WR_EQSIZE_G(x) \ + (((x) >> FW_RI_RES_WR_EQSIZE_S) & FW_RI_RES_WR_EQSIZE_M) + +#define FW_RI_RES_WR_IQANDST_S 15 +#define FW_RI_RES_WR_IQANDST_M 0x1 +#define FW_RI_RES_WR_IQANDST_V(x) ((x) << FW_RI_RES_WR_IQANDST_S) +#define FW_RI_RES_WR_IQANDST_G(x) \ + (((x) >> FW_RI_RES_WR_IQANDST_S) & FW_RI_RES_WR_IQANDST_M) +#define FW_RI_RES_WR_IQANDST_F FW_RI_RES_WR_IQANDST_V(1U) + +#define FW_RI_RES_WR_IQANUS_S 14 +#define FW_RI_RES_WR_IQANUS_M 0x1 +#define FW_RI_RES_WR_IQANUS_V(x) ((x) << FW_RI_RES_WR_IQANUS_S) +#define FW_RI_RES_WR_IQANUS_G(x) \ + (((x) >> FW_RI_RES_WR_IQANUS_S) & FW_RI_RES_WR_IQANUS_M) +#define FW_RI_RES_WR_IQANUS_F FW_RI_RES_WR_IQANUS_V(1U) + +#define FW_RI_RES_WR_IQANUD_S 12 +#define FW_RI_RES_WR_IQANUD_M 0x3 +#define FW_RI_RES_WR_IQANUD_V(x) ((x) << FW_RI_RES_WR_IQANUD_S) +#define FW_RI_RES_WR_IQANUD_G(x) \ + (((x) >> FW_RI_RES_WR_IQANUD_S) & FW_RI_RES_WR_IQANUD_M) + +#define FW_RI_RES_WR_IQANDSTINDEX_S 0 +#define FW_RI_RES_WR_IQANDSTINDEX_M 0xfff +#define FW_RI_RES_WR_IQANDSTINDEX_V(x) ((x) << FW_RI_RES_WR_IQANDSTINDEX_S) +#define FW_RI_RES_WR_IQANDSTINDEX_G(x) \ + (((x) >> FW_RI_RES_WR_IQANDSTINDEX_S) & FW_RI_RES_WR_IQANDSTINDEX_M) + +#define FW_RI_RES_WR_IQDROPRSS_S 15 +#define FW_RI_RES_WR_IQDROPRSS_M 0x1 +#define FW_RI_RES_WR_IQDROPRSS_V(x) ((x) << FW_RI_RES_WR_IQDROPRSS_S) +#define FW_RI_RES_WR_IQDROPRSS_G(x) \ + (((x) >> FW_RI_RES_WR_IQDROPRSS_S) & FW_RI_RES_WR_IQDROPRSS_M) +#define FW_RI_RES_WR_IQDROPRSS_F FW_RI_RES_WR_IQDROPRSS_V(1U) + +#define FW_RI_RES_WR_IQGTSMODE_S 14 +#define FW_RI_RES_WR_IQGTSMODE_M 0x1 +#define FW_RI_RES_WR_IQGTSMODE_V(x) ((x) << FW_RI_RES_WR_IQGTSMODE_S) +#define FW_RI_RES_WR_IQGTSMODE_G(x) \ + (((x) >> FW_RI_RES_WR_IQGTSMODE_S) & FW_RI_RES_WR_IQGTSMODE_M) +#define FW_RI_RES_WR_IQGTSMODE_F FW_RI_RES_WR_IQGTSMODE_V(1U) + +#define FW_RI_RES_WR_IQPCIECH_S 12 +#define FW_RI_RES_WR_IQPCIECH_M 0x3 +#define FW_RI_RES_WR_IQPCIECH_V(x) ((x) << FW_RI_RES_WR_IQPCIECH_S) +#define FW_RI_RES_WR_IQPCIECH_G(x) \ + (((x) >> FW_RI_RES_WR_IQPCIECH_S) & FW_RI_RES_WR_IQPCIECH_M) + +#define FW_RI_RES_WR_IQDCAEN_S 11 +#define FW_RI_RES_WR_IQDCAEN_M 0x1 +#define FW_RI_RES_WR_IQDCAEN_V(x) ((x) << FW_RI_RES_WR_IQDCAEN_S) +#define FW_RI_RES_WR_IQDCAEN_G(x) \ + (((x) >> FW_RI_RES_WR_IQDCAEN_S) & FW_RI_RES_WR_IQDCAEN_M) +#define FW_RI_RES_WR_IQDCAEN_F FW_RI_RES_WR_IQDCAEN_V(1U) + +#define FW_RI_RES_WR_IQDCACPU_S 6 +#define FW_RI_RES_WR_IQDCACPU_M 0x1f +#define FW_RI_RES_WR_IQDCACPU_V(x) ((x) << FW_RI_RES_WR_IQDCACPU_S) +#define FW_RI_RES_WR_IQDCACPU_G(x) \ + (((x) >> FW_RI_RES_WR_IQDCACPU_S) & FW_RI_RES_WR_IQDCACPU_M) + +#define FW_RI_RES_WR_IQINTCNTTHRESH_S 4 +#define FW_RI_RES_WR_IQINTCNTTHRESH_M 0x3 +#define FW_RI_RES_WR_IQINTCNTTHRESH_V(x) \ + ((x) << FW_RI_RES_WR_IQINTCNTTHRESH_S) +#define FW_RI_RES_WR_IQINTCNTTHRESH_G(x) \ + (((x) >> FW_RI_RES_WR_IQINTCNTTHRESH_S) & FW_RI_RES_WR_IQINTCNTTHRESH_M) + +#define FW_RI_RES_WR_IQO_S 3 +#define FW_RI_RES_WR_IQO_M 0x1 +#define FW_RI_RES_WR_IQO_V(x) ((x) << FW_RI_RES_WR_IQO_S) +#define FW_RI_RES_WR_IQO_G(x) \ + (((x) >> FW_RI_RES_WR_IQO_S) & FW_RI_RES_WR_IQO_M) +#define FW_RI_RES_WR_IQO_F FW_RI_RES_WR_IQO_V(1U) + +#define FW_RI_RES_WR_IQCPRIO_S 2 +#define FW_RI_RES_WR_IQCPRIO_M 0x1 +#define FW_RI_RES_WR_IQCPRIO_V(x) ((x) << FW_RI_RES_WR_IQCPRIO_S) +#define FW_RI_RES_WR_IQCPRIO_G(x) \ + (((x) >> FW_RI_RES_WR_IQCPRIO_S) & FW_RI_RES_WR_IQCPRIO_M) +#define FW_RI_RES_WR_IQCPRIO_F FW_RI_RES_WR_IQCPRIO_V(1U) + +#define FW_RI_RES_WR_IQESIZE_S 0 +#define FW_RI_RES_WR_IQESIZE_M 0x3 +#define FW_RI_RES_WR_IQESIZE_V(x) ((x) << FW_RI_RES_WR_IQESIZE_S) +#define FW_RI_RES_WR_IQESIZE_G(x) \ + (((x) >> FW_RI_RES_WR_IQESIZE_S) & FW_RI_RES_WR_IQESIZE_M) + +#define FW_RI_RES_WR_IQNS_S 31 +#define FW_RI_RES_WR_IQNS_M 0x1 +#define FW_RI_RES_WR_IQNS_V(x) ((x) << FW_RI_RES_WR_IQNS_S) +#define FW_RI_RES_WR_IQNS_G(x) \ + (((x) >> FW_RI_RES_WR_IQNS_S) & FW_RI_RES_WR_IQNS_M) +#define FW_RI_RES_WR_IQNS_F FW_RI_RES_WR_IQNS_V(1U) + +#define FW_RI_RES_WR_IQRO_S 30 +#define FW_RI_RES_WR_IQRO_M 0x1 +#define FW_RI_RES_WR_IQRO_V(x) ((x) << FW_RI_RES_WR_IQRO_S) +#define FW_RI_RES_WR_IQRO_G(x) \ + (((x) >> FW_RI_RES_WR_IQRO_S) & FW_RI_RES_WR_IQRO_M) +#define FW_RI_RES_WR_IQRO_F FW_RI_RES_WR_IQRO_V(1U) struct fw_ri_rdma_write_wr { __u8 opcode; @@ -562,11 +562,11 @@ struct fw_ri_send_wr { #endif }; -#define S_FW_RI_SEND_WR_SENDOP 0 -#define M_FW_RI_SEND_WR_SENDOP 0xf -#define V_FW_RI_SEND_WR_SENDOP(x) ((x) << S_FW_RI_SEND_WR_SENDOP) -#define G_FW_RI_SEND_WR_SENDOP(x) \ - (((x) >> S_FW_RI_SEND_WR_SENDOP) & M_FW_RI_SEND_WR_SENDOP) +#define FW_RI_SEND_WR_SENDOP_S 0 +#define FW_RI_SEND_WR_SENDOP_M 0xf +#define FW_RI_SEND_WR_SENDOP_V(x) ((x) << FW_RI_SEND_WR_SENDOP_S) +#define FW_RI_SEND_WR_SENDOP_G(x) \ + (((x) >> FW_RI_SEND_WR_SENDOP_S) & FW_RI_SEND_WR_SENDOP_M) struct fw_ri_rdma_read_wr { __u8 opcode; @@ -612,25 +612,25 @@ struct fw_ri_bind_mw_wr { __be64 r4; }; -#define S_FW_RI_BIND_MW_WR_QPBINDE 6 -#define M_FW_RI_BIND_MW_WR_QPBINDE 0x1 -#define V_FW_RI_BIND_MW_WR_QPBINDE(x) ((x) << S_FW_RI_BIND_MW_WR_QPBINDE) -#define G_FW_RI_BIND_MW_WR_QPBINDE(x) \ - (((x) >> S_FW_RI_BIND_MW_WR_QPBINDE) & M_FW_RI_BIND_MW_WR_QPBINDE) -#define F_FW_RI_BIND_MW_WR_QPBINDE V_FW_RI_BIND_MW_WR_QPBINDE(1U) +#define FW_RI_BIND_MW_WR_QPBINDE_S 6 +#define FW_RI_BIND_MW_WR_QPBINDE_M 0x1 +#define FW_RI_BIND_MW_WR_QPBINDE_V(x) ((x) << FW_RI_BIND_MW_WR_QPBINDE_S) +#define FW_RI_BIND_MW_WR_QPBINDE_G(x) \ + (((x) >> FW_RI_BIND_MW_WR_QPBINDE_S) & FW_RI_BIND_MW_WR_QPBINDE_M) +#define FW_RI_BIND_MW_WR_QPBINDE_F FW_RI_BIND_MW_WR_QPBINDE_V(1U) -#define S_FW_RI_BIND_MW_WR_NS 5 -#define M_FW_RI_BIND_MW_WR_NS 0x1 -#define V_FW_RI_BIND_MW_WR_NS(x) ((x) << S_FW_RI_BIND_MW_WR_NS) -#define G_FW_RI_BIND_MW_WR_NS(x) \ - (((x) >> S_FW_RI_BIND_MW_WR_NS) & M_FW_RI_BIND_MW_WR_NS) -#define F_FW_RI_BIND_MW_WR_NS V_FW_RI_BIND_MW_WR_NS(1U) +#define FW_RI_BIND_MW_WR_NS_S 5 +#define FW_RI_BIND_MW_WR_NS_M 0x1 +#define FW_RI_BIND_MW_WR_NS_V(x) ((x) << FW_RI_BIND_MW_WR_NS_S) +#define FW_RI_BIND_MW_WR_NS_G(x) \ + (((x) >> FW_RI_BIND_MW_WR_NS_S) & FW_RI_BIND_MW_WR_NS_M) +#define FW_RI_BIND_MW_WR_NS_F FW_RI_BIND_MW_WR_NS_V(1U) -#define S_FW_RI_BIND_MW_WR_DCACPU 0 -#define M_FW_RI_BIND_MW_WR_DCACPU 0x1f -#define V_FW_RI_BIND_MW_WR_DCACPU(x) ((x) << S_FW_RI_BIND_MW_WR_DCACPU) -#define G_FW_RI_BIND_MW_WR_DCACPU(x) \ - (((x) >> S_FW_RI_BIND_MW_WR_DCACPU) & M_FW_RI_BIND_MW_WR_DCACPU) +#define FW_RI_BIND_MW_WR_DCACPU_S 0 +#define FW_RI_BIND_MW_WR_DCACPU_M 0x1f +#define FW_RI_BIND_MW_WR_DCACPU_V(x) ((x) << FW_RI_BIND_MW_WR_DCACPU_S) +#define FW_RI_BIND_MW_WR_DCACPU_G(x) \ + (((x) >> FW_RI_BIND_MW_WR_DCACPU_S) & FW_RI_BIND_MW_WR_DCACPU_M) struct fw_ri_fr_nsmr_wr { __u8 opcode; @@ -649,25 +649,25 @@ struct fw_ri_fr_nsmr_wr { __be32 va_lo_fbo; }; -#define S_FW_RI_FR_NSMR_WR_QPBINDE 6 -#define M_FW_RI_FR_NSMR_WR_QPBINDE 0x1 -#define V_FW_RI_FR_NSMR_WR_QPBINDE(x) ((x) << S_FW_RI_FR_NSMR_WR_QPBINDE) -#define G_FW_RI_FR_NSMR_WR_QPBINDE(x) \ - (((x) >> S_FW_RI_FR_NSMR_WR_QPBINDE) & M_FW_RI_FR_NSMR_WR_QPBINDE) -#define F_FW_RI_FR_NSMR_WR_QPBINDE V_FW_RI_FR_NSMR_WR_QPBINDE(1U) +#define FW_RI_FR_NSMR_WR_QPBINDE_S 6 +#define FW_RI_FR_NSMR_WR_QPBINDE_M 0x1 +#define FW_RI_FR_NSMR_WR_QPBINDE_V(x) ((x) << FW_RI_FR_NSMR_WR_QPBINDE_S) +#define FW_RI_FR_NSMR_WR_QPBINDE_G(x) \ + (((x) >> FW_RI_FR_NSMR_WR_QPBINDE_S) & FW_RI_FR_NSMR_WR_QPBINDE_M) +#define FW_RI_FR_NSMR_WR_QPBINDE_F FW_RI_FR_NSMR_WR_QPBINDE_V(1U) -#define S_FW_RI_FR_NSMR_WR_NS 5 -#define M_FW_RI_FR_NSMR_WR_NS 0x1 -#define V_FW_RI_FR_NSMR_WR_NS(x) ((x) << S_FW_RI_FR_NSMR_WR_NS) -#define G_FW_RI_FR_NSMR_WR_NS(x) \ - (((x) >> S_FW_RI_FR_NSMR_WR_NS) & M_FW_RI_FR_NSMR_WR_NS) -#define F_FW_RI_FR_NSMR_WR_NS V_FW_RI_FR_NSMR_WR_NS(1U) +#define FW_RI_FR_NSMR_WR_NS_S 5 +#define FW_RI_FR_NSMR_WR_NS_M 0x1 +#define FW_RI_FR_NSMR_WR_NS_V(x) ((x) << FW_RI_FR_NSMR_WR_NS_S) +#define FW_RI_FR_NSMR_WR_NS_G(x) \ + (((x) >> FW_RI_FR_NSMR_WR_NS_S) & FW_RI_FR_NSMR_WR_NS_M) +#define FW_RI_FR_NSMR_WR_NS_F FW_RI_FR_NSMR_WR_NS_V(1U) -#define S_FW_RI_FR_NSMR_WR_DCACPU 0 -#define M_FW_RI_FR_NSMR_WR_DCACPU 0x1f -#define V_FW_RI_FR_NSMR_WR_DCACPU(x) ((x) << S_FW_RI_FR_NSMR_WR_DCACPU) -#define G_FW_RI_FR_NSMR_WR_DCACPU(x) \ - (((x) >> S_FW_RI_FR_NSMR_WR_DCACPU) & M_FW_RI_FR_NSMR_WR_DCACPU) +#define FW_RI_FR_NSMR_WR_DCACPU_S 0 +#define FW_RI_FR_NSMR_WR_DCACPU_M 0x1f +#define FW_RI_FR_NSMR_WR_DCACPU_V(x) ((x) << FW_RI_FR_NSMR_WR_DCACPU_S) +#define FW_RI_FR_NSMR_WR_DCACPU_G(x) \ + (((x) >> FW_RI_FR_NSMR_WR_DCACPU_S) & FW_RI_FR_NSMR_WR_DCACPU_M) struct fw_ri_inv_lstag_wr { __u8 opcode; @@ -740,18 +740,18 @@ struct fw_ri_wr { } u; }; -#define S_FW_RI_WR_MPAREQBIT 7 -#define M_FW_RI_WR_MPAREQBIT 0x1 -#define V_FW_RI_WR_MPAREQBIT(x) ((x) << S_FW_RI_WR_MPAREQBIT) -#define G_FW_RI_WR_MPAREQBIT(x) \ - (((x) >> S_FW_RI_WR_MPAREQBIT) & M_FW_RI_WR_MPAREQBIT) -#define F_FW_RI_WR_MPAREQBIT V_FW_RI_WR_MPAREQBIT(1U) +#define FW_RI_WR_MPAREQBIT_S 7 +#define FW_RI_WR_MPAREQBIT_M 0x1 +#define FW_RI_WR_MPAREQBIT_V(x) ((x) << FW_RI_WR_MPAREQBIT_S) +#define FW_RI_WR_MPAREQBIT_G(x) \ + (((x) >> FW_RI_WR_MPAREQBIT_S) & FW_RI_WR_MPAREQBIT_M) +#define FW_RI_WR_MPAREQBIT_F FW_RI_WR_MPAREQBIT_V(1U) -#define S_FW_RI_WR_P2PTYPE 0 -#define M_FW_RI_WR_P2PTYPE 0xf -#define V_FW_RI_WR_P2PTYPE(x) ((x) << S_FW_RI_WR_P2PTYPE) -#define G_FW_RI_WR_P2PTYPE(x) \ - (((x) >> S_FW_RI_WR_P2PTYPE) & M_FW_RI_WR_P2PTYPE) +#define FW_RI_WR_P2PTYPE_S 0 +#define FW_RI_WR_P2PTYPE_M 0xf +#define FW_RI_WR_P2PTYPE_V(x) ((x) << FW_RI_WR_P2PTYPE_S) +#define FW_RI_WR_P2PTYPE_G(x) \ + (((x) >> FW_RI_WR_P2PTYPE_S) & FW_RI_WR_P2PTYPE_M) struct tcp_options { __be16 mss; @@ -783,58 +783,58 @@ struct cpl_pass_accept_req { }; /* cpl_pass_accept_req.hdr_len fields */ -#define S_SYN_RX_CHAN 0 -#define M_SYN_RX_CHAN 0xF -#define V_SYN_RX_CHAN(x) ((x) << S_SYN_RX_CHAN) -#define G_SYN_RX_CHAN(x) (((x) >> S_SYN_RX_CHAN) & M_SYN_RX_CHAN) - -#define S_TCP_HDR_LEN 10 -#define M_TCP_HDR_LEN 0x3F -#define V_TCP_HDR_LEN(x) ((x) << S_TCP_HDR_LEN) -#define G_TCP_HDR_LEN(x) (((x) >> S_TCP_HDR_LEN) & M_TCP_HDR_LEN) - -#define S_IP_HDR_LEN 16 -#define M_IP_HDR_LEN 0x3FF -#define V_IP_HDR_LEN(x) ((x) << S_IP_HDR_LEN) -#define G_IP_HDR_LEN(x) (((x) >> S_IP_HDR_LEN) & M_IP_HDR_LEN) - -#define S_ETH_HDR_LEN 26 -#define M_ETH_HDR_LEN 0x1F -#define V_ETH_HDR_LEN(x) ((x) << S_ETH_HDR_LEN) -#define G_ETH_HDR_LEN(x) (((x) >> S_ETH_HDR_LEN) & M_ETH_HDR_LEN) +#define SYN_RX_CHAN_S 0 +#define SYN_RX_CHAN_M 0xF +#define SYN_RX_CHAN_V(x) ((x) << SYN_RX_CHAN_S) +#define SYN_RX_CHAN_G(x) (((x) >> SYN_RX_CHAN_S) & SYN_RX_CHAN_M) + +#define TCP_HDR_LEN_S 10 +#define TCP_HDR_LEN_M 0x3F +#define TCP_HDR_LEN_V(x) ((x) << TCP_HDR_LEN_S) +#define TCP_HDR_LEN_G(x) (((x) >> TCP_HDR_LEN_S) & TCP_HDR_LEN_M) + +#define IP_HDR_LEN_S 16 +#define IP_HDR_LEN_M 0x3FF +#define IP_HDR_LEN_V(x) ((x) << IP_HDR_LEN_S) +#define IP_HDR_LEN_G(x) (((x) >> IP_HDR_LEN_S) & IP_HDR_LEN_M) + +#define ETH_HDR_LEN_S 26 +#define ETH_HDR_LEN_M 0x1F +#define ETH_HDR_LEN_V(x) ((x) << ETH_HDR_LEN_S) +#define ETH_HDR_LEN_G(x) (((x) >> ETH_HDR_LEN_S) & ETH_HDR_LEN_M) /* cpl_pass_accept_req.l2info fields */ -#define S_SYN_MAC_IDX 0 -#define M_SYN_MAC_IDX 0x1FF -#define V_SYN_MAC_IDX(x) ((x) << S_SYN_MAC_IDX) -#define G_SYN_MAC_IDX(x) (((x) >> S_SYN_MAC_IDX) & M_SYN_MAC_IDX) +#define SYN_MAC_IDX_S 0 +#define SYN_MAC_IDX_M 0x1FF +#define SYN_MAC_IDX_V(x) ((x) << SYN_MAC_IDX_S) +#define SYN_MAC_IDX_G(x) (((x) >> SYN_MAC_IDX_S) & SYN_MAC_IDX_M) -#define S_SYN_XACT_MATCH 9 -#define V_SYN_XACT_MATCH(x) ((x) << S_SYN_XACT_MATCH) -#define F_SYN_XACT_MATCH V_SYN_XACT_MATCH(1U) +#define SYN_XACT_MATCH_S 9 +#define SYN_XACT_MATCH_V(x) ((x) << SYN_XACT_MATCH_S) +#define SYN_XACT_MATCH_F SYN_XACT_MATCH_V(1U) -#define S_SYN_INTF 12 -#define M_SYN_INTF 0xF -#define V_SYN_INTF(x) ((x) << S_SYN_INTF) -#define G_SYN_INTF(x) (((x) >> S_SYN_INTF) & M_SYN_INTF) +#define SYN_INTF_S 12 +#define SYN_INTF_M 0xF +#define SYN_INTF_V(x) ((x) << SYN_INTF_S) +#define SYN_INTF_G(x) (((x) >> SYN_INTF_S) & SYN_INTF_M) struct ulptx_idata { __be32 cmd_more; __be32 len; }; -#define S_ULPTX_NSGE 0 -#define M_ULPTX_NSGE 0xFFFF -#define V_ULPTX_NSGE(x) ((x) << S_ULPTX_NSGE) +#define ULPTX_NSGE_S 0 +#define ULPTX_NSGE_M 0xFFFF +#define ULPTX_NSGE_V(x) ((x) << ULPTX_NSGE_S) -#define S_RX_DACK_MODE 29 -#define M_RX_DACK_MODE 0x3 -#define V_RX_DACK_MODE(x) ((x) << S_RX_DACK_MODE) -#define G_RX_DACK_MODE(x) (((x) >> S_RX_DACK_MODE) & M_RX_DACK_MODE) +#define RX_DACK_MODE_S 29 +#define RX_DACK_MODE_M 0x3 +#define RX_DACK_MODE_V(x) ((x) << RX_DACK_MODE_S) +#define RX_DACK_MODE_G(x) (((x) >> RX_DACK_MODE_S) & RX_DACK_MODE_M) -#define S_RX_DACK_CHANGE 31 -#define V_RX_DACK_CHANGE(x) ((x) << S_RX_DACK_CHANGE) -#define F_RX_DACK_CHANGE V_RX_DACK_CHANGE(1U) +#define RX_DACK_CHANGE_S 31 +#define RX_DACK_CHANGE_V(x) ((x) << RX_DACK_CHANGE_S) +#define RX_DACK_CHANGE_F RX_DACK_CHANGE_V(1U) enum { /* TCP congestion control algorithms */ CONG_ALG_RENO, @@ -843,10 +843,10 @@ enum { /* TCP congestion control algorithms */ CONG_ALG_HIGHSPEED }; -#define S_CONG_CNTRL 14 -#define M_CONG_CNTRL 0x3 -#define V_CONG_CNTRL(x) ((x) << S_CONG_CNTRL) -#define G_CONG_CNTRL(x) (((x) >> S_CONG_CNTRL) & M_CONG_CNTRL) +#define CONG_CNTRL_S 14 +#define CONG_CNTRL_M 0x3 +#define CONG_CNTRL_V(x) ((x) << CONG_CNTRL_S) +#define CONG_CNTRL_G(x) (((x) >> CONG_CNTRL_S) & CONG_CNTRL_M) #define CONG_CNTRL_VALID (1 << 18) diff --git a/drivers/infiniband/hw/mlx4/ah.c b/drivers/infiniband/hw/mlx4/ah.c index 2d8c3397774f..f50a546224ad 100644 --- a/drivers/infiniband/hw/mlx4/ah.c +++ b/drivers/infiniband/hw/mlx4/ah.c @@ -36,6 +36,7 @@ #include <linux/slab.h> #include <linux/inet.h> #include <linux/string.h> +#include <linux/mlx4/driver.h> #include "mlx4_ib.h" diff --git a/drivers/infiniband/hw/mlx4/alias_GUID.c b/drivers/infiniband/hw/mlx4/alias_GUID.c index 0eb141c41416..a31e031afd87 100644 --- a/drivers/infiniband/hw/mlx4/alias_GUID.c +++ b/drivers/infiniband/hw/mlx4/alias_GUID.c @@ -154,7 +154,7 @@ void mlx4_ib_notify_slaves_on_guid_change(struct mlx4_ib_dev *dev, continue; slave_id = (block_num * NUM_ALIAS_GUID_IN_REC) + i ; - if (slave_id >= dev->dev->num_vfs + 1) + if (slave_id >= dev->dev->persist->num_vfs + 1) return; tmp_cur_ag = *(__be64 *)&p_data[i * GUID_REC_SIZE]; form_cache_ag = get_cached_alias_guid(dev, port_num, diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c index a3b70f6c4035..543ecdd8667b 100644 --- a/drivers/infiniband/hw/mlx4/cq.c +++ b/drivers/infiniband/hw/mlx4/cq.c @@ -188,6 +188,8 @@ struct ib_cq *mlx4_ib_create_cq(struct ib_device *ibdev, int entries, int vector spin_lock_init(&cq->lock); cq->resize_buf = NULL; cq->resize_umem = NULL; + INIT_LIST_HEAD(&cq->send_qp_list); + INIT_LIST_HEAD(&cq->recv_qp_list); if (context) { struct mlx4_ib_create_cq ucmd; @@ -594,6 +596,55 @@ static int use_tunnel_data(struct mlx4_ib_qp *qp, struct mlx4_ib_cq *cq, struct return 0; } +static void mlx4_ib_qp_sw_comp(struct mlx4_ib_qp *qp, int num_entries, + struct ib_wc *wc, int *npolled, int is_send) +{ + struct mlx4_ib_wq *wq; + unsigned cur; + int i; + + wq = is_send ? &qp->sq : &qp->rq; + cur = wq->head - wq->tail; + + if (cur == 0) + return; + + for (i = 0; i < cur && *npolled < num_entries; i++) { + wc->wr_id = wq->wrid[wq->tail & (wq->wqe_cnt - 1)]; + wc->status = IB_WC_WR_FLUSH_ERR; + wc->vendor_err = MLX4_CQE_SYNDROME_WR_FLUSH_ERR; + wq->tail++; + (*npolled)++; + wc->qp = &qp->ibqp; + wc++; + } +} + +static void mlx4_ib_poll_sw_comp(struct mlx4_ib_cq *cq, int num_entries, + struct ib_wc *wc, int *npolled) +{ + struct mlx4_ib_qp *qp; + + *npolled = 0; + /* Find uncompleted WQEs belonging to that cq and retrun + * simulated FLUSH_ERR completions + */ + list_for_each_entry(qp, &cq->send_qp_list, cq_send_list) { + mlx4_ib_qp_sw_comp(qp, num_entries, wc, npolled, 1); + if (*npolled >= num_entries) + goto out; + } + + list_for_each_entry(qp, &cq->recv_qp_list, cq_recv_list) { + mlx4_ib_qp_sw_comp(qp, num_entries, wc + *npolled, npolled, 0); + if (*npolled >= num_entries) + goto out; + } + +out: + return; +} + static int mlx4_ib_poll_one(struct mlx4_ib_cq *cq, struct mlx4_ib_qp **cur_qp, struct ib_wc *wc) @@ -836,8 +887,13 @@ int mlx4_ib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc) unsigned long flags; int npolled; int err = 0; + struct mlx4_ib_dev *mdev = to_mdev(cq->ibcq.device); spin_lock_irqsave(&cq->lock, flags); + if (mdev->dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR) { + mlx4_ib_poll_sw_comp(cq, num_entries, wc, &npolled); + goto out; + } for (npolled = 0; npolled < num_entries; ++npolled) { err = mlx4_ib_poll_one(cq, &cur_qp, wc + npolled); @@ -847,6 +903,7 @@ int mlx4_ib_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc) mlx4_cq_set_ci(&cq->mcq); +out: spin_unlock_irqrestore(&cq->lock, flags); if (err == 0 || err == -EAGAIN) diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c index 82a7dd87089b..c7619716c31d 100644 --- a/drivers/infiniband/hw/mlx4/mad.c +++ b/drivers/infiniband/hw/mlx4/mad.c @@ -1951,7 +1951,8 @@ static int mlx4_ib_alloc_demux_ctx(struct mlx4_ib_dev *dev, ctx->ib_dev = &dev->ib_dev; for (i = 0; - i < min(dev->dev->caps.sqp_demux, (u16)(dev->dev->num_vfs + 1)); + i < min(dev->dev->caps.sqp_demux, + (u16)(dev->dev->persist->num_vfs + 1)); i++) { struct mlx4_active_ports actv_ports = mlx4_get_active_ports(dev->dev, i); diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c index 57ecc5b204f3..eb8e215f1613 100644 --- a/drivers/infiniband/hw/mlx4/main.c +++ b/drivers/infiniband/hw/mlx4/main.c @@ -198,7 +198,7 @@ static int mlx4_ib_query_device(struct ib_device *ibdev, props->vendor_id = be32_to_cpup((__be32 *) (out_mad->data + 36)) & 0xffffff; - props->vendor_part_id = dev->dev->pdev->device; + props->vendor_part_id = dev->dev->persist->pdev->device; props->hw_ver = be32_to_cpup((__be32 *) (out_mad->data + 32)); memcpy(&props->sys_image_guid, out_mad->data + 4, 8); @@ -351,6 +351,7 @@ static int eth_link_query_port(struct ib_device *ibdev, u8 port, enum ib_mtu tmp; struct mlx4_cmd_mailbox *mailbox; int err = 0; + int is_bonded = mlx4_is_bonded(mdev->dev); mailbox = mlx4_alloc_cmd_mailbox(mdev->dev); if (IS_ERR(mailbox)) @@ -374,8 +375,12 @@ static int eth_link_query_port(struct ib_device *ibdev, u8 port, props->state = IB_PORT_DOWN; props->phys_state = state_to_phys_state(props->state); props->active_mtu = IB_MTU_256; + if (is_bonded) + rtnl_lock(); /* required to get upper dev */ spin_lock_bh(&iboe->lock); ndev = iboe->netdevs[port - 1]; + if (ndev && is_bonded) + ndev = netdev_master_upper_dev_get(ndev); if (!ndev) goto out_unlock; @@ -387,6 +392,8 @@ static int eth_link_query_port(struct ib_device *ibdev, u8 port, props->phys_state = state_to_phys_state(props->state); out_unlock: spin_unlock_bh(&iboe->lock); + if (is_bonded) + rtnl_unlock(); out: mlx4_free_cmd_mailbox(mdev->dev, mailbox); return err; @@ -844,7 +851,7 @@ int mlx4_ib_add_mc(struct mlx4_ib_dev *mdev, struct mlx4_ib_qp *mqp, struct mlx4_ib_steering { struct list_head list; - u64 reg_id; + struct mlx4_flow_reg_id reg_id; union ib_gid gid; }; @@ -1114,7 +1121,8 @@ static int mlx4_ib_tunnel_steer_add(struct ib_qp *qp, struct ib_flow_attr *flow_ struct mlx4_dev *dev = to_mdev(qp->device)->dev; int err = 0; - if (dev->caps.tunnel_offload_mode != MLX4_TUNNEL_OFFLOAD_MODE_VXLAN) + if (dev->caps.tunnel_offload_mode != MLX4_TUNNEL_OFFLOAD_MODE_VXLAN || + dev->caps.dmfs_high_steer_mode == MLX4_STEERING_DMFS_A0_STATIC) return 0; /* do nothing */ ib_flow = flow_attr + 1; @@ -1134,9 +1142,11 @@ static struct ib_flow *mlx4_ib_create_flow(struct ib_qp *qp, struct ib_flow_attr *flow_attr, int domain) { - int err = 0, i = 0; + int err = 0, i = 0, j = 0; struct mlx4_ib_flow *mflow; enum mlx4_net_trans_promisc_mode type[2]; + struct mlx4_dev *dev = (to_mdev(qp->device))->dev; + int is_bonded = mlx4_is_bonded(dev); memset(type, 0, sizeof(type)); @@ -1171,26 +1181,58 @@ static struct ib_flow *mlx4_ib_create_flow(struct ib_qp *qp, while (i < ARRAY_SIZE(type) && type[i]) { err = __mlx4_ib_create_flow(qp, flow_attr, domain, type[i], - &mflow->reg_id[i]); + &mflow->reg_id[i].id); if (err) goto err_create_flow; i++; + if (is_bonded) { + /* Application always sees one port so the mirror rule + * must be on port #2 + */ + flow_attr->port = 2; + err = __mlx4_ib_create_flow(qp, flow_attr, + domain, type[j], + &mflow->reg_id[j].mirror); + flow_attr->port = 1; + if (err) + goto err_create_flow; + j++; + } + } if (i < ARRAY_SIZE(type) && flow_attr->type == IB_FLOW_ATTR_NORMAL) { - err = mlx4_ib_tunnel_steer_add(qp, flow_attr, &mflow->reg_id[i]); + err = mlx4_ib_tunnel_steer_add(qp, flow_attr, + &mflow->reg_id[i].id); if (err) goto err_create_flow; i++; + if (is_bonded) { + flow_attr->port = 2; + err = mlx4_ib_tunnel_steer_add(qp, flow_attr, + &mflow->reg_id[j].mirror); + flow_attr->port = 1; + if (err) + goto err_create_flow; + j++; + } + /* function to create mirror rule */ } return &mflow->ibflow; err_create_flow: while (i) { - (void)__mlx4_ib_destroy_flow(to_mdev(qp->device)->dev, mflow->reg_id[i]); + (void)__mlx4_ib_destroy_flow(to_mdev(qp->device)->dev, + mflow->reg_id[i].id); i--; } + + while (j) { + (void)__mlx4_ib_destroy_flow(to_mdev(qp->device)->dev, + mflow->reg_id[j].mirror); + j--; + } err_free: kfree(mflow); return ERR_PTR(err); @@ -1203,10 +1245,16 @@ static int mlx4_ib_destroy_flow(struct ib_flow *flow_id) struct mlx4_ib_dev *mdev = to_mdev(flow_id->qp->device); struct mlx4_ib_flow *mflow = to_mflow(flow_id); - while (i < ARRAY_SIZE(mflow->reg_id) && mflow->reg_id[i]) { - err = __mlx4_ib_destroy_flow(mdev->dev, mflow->reg_id[i]); + while (i < ARRAY_SIZE(mflow->reg_id) && mflow->reg_id[i].id) { + err = __mlx4_ib_destroy_flow(mdev->dev, mflow->reg_id[i].id); if (err) ret = err; + if (mflow->reg_id[i].mirror) { + err = __mlx4_ib_destroy_flow(mdev->dev, + mflow->reg_id[i].mirror); + if (err) + ret = err; + } i++; } @@ -1218,11 +1266,12 @@ static int mlx4_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) { int err; struct mlx4_ib_dev *mdev = to_mdev(ibqp->device); + struct mlx4_dev *dev = mdev->dev; struct mlx4_ib_qp *mqp = to_mqp(ibqp); - u64 reg_id; struct mlx4_ib_steering *ib_steering = NULL; enum mlx4_protocol prot = (gid->raw[1] == 0x0e) ? MLX4_PROT_IB_IPV4 : MLX4_PROT_IB_IPV6; + struct mlx4_flow_reg_id reg_id; if (mdev->dev->caps.steering_mode == MLX4_STEERING_MODE_DEVICE_MANAGED) { @@ -1234,10 +1283,21 @@ static int mlx4_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) err = mlx4_multicast_attach(mdev->dev, &mqp->mqp, gid->raw, mqp->port, !!(mqp->flags & MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK), - prot, ®_id); + prot, ®_id.id); if (err) goto err_malloc; + reg_id.mirror = 0; + if (mlx4_is_bonded(dev)) { + err = mlx4_multicast_attach(mdev->dev, &mqp->mqp, gid->raw, + (mqp->port == 1) ? 2 : 1, + !!(mqp->flags & + MLX4_IB_QP_BLOCK_MULTICAST_LOOPBACK), + prot, ®_id.mirror); + if (err) + goto err_add; + } + err = add_gid_entry(ibqp, gid); if (err) goto err_add; @@ -1253,7 +1313,10 @@ static int mlx4_ib_mcg_attach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) err_add: mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw, - prot, reg_id); + prot, reg_id.id); + if (reg_id.mirror) + mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw, + prot, reg_id.mirror); err_malloc: kfree(ib_steering); @@ -1280,10 +1343,12 @@ static int mlx4_ib_mcg_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) { int err; struct mlx4_ib_dev *mdev = to_mdev(ibqp->device); + struct mlx4_dev *dev = mdev->dev; struct mlx4_ib_qp *mqp = to_mqp(ibqp); struct net_device *ndev; struct mlx4_ib_gid_entry *ge; - u64 reg_id = 0; + struct mlx4_flow_reg_id reg_id = {0, 0}; + enum mlx4_protocol prot = (gid->raw[1] == 0x0e) ? MLX4_PROT_IB_IPV4 : MLX4_PROT_IB_IPV6; @@ -1308,10 +1373,17 @@ static int mlx4_ib_mcg_detach(struct ib_qp *ibqp, union ib_gid *gid, u16 lid) } err = mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw, - prot, reg_id); + prot, reg_id.id); if (err) return err; + if (mlx4_is_bonded(dev)) { + err = mlx4_multicast_detach(mdev->dev, &mqp->mqp, gid->raw, + prot, reg_id.mirror); + if (err) + return err; + } + mutex_lock(&mqp->mutex); ge = find_gid_entry(mqp, gid->raw); if (ge) { @@ -1375,7 +1447,7 @@ static ssize_t show_hca(struct device *device, struct device_attribute *attr, { struct mlx4_ib_dev *dev = container_of(device, struct mlx4_ib_dev, ib_dev.dev); - return sprintf(buf, "MT%d\n", dev->dev->pdev->device); + return sprintf(buf, "MT%d\n", dev->dev->persist->pdev->device); } static ssize_t show_fw_ver(struct device *device, struct device_attribute *attr, @@ -1439,6 +1511,7 @@ static void update_gids_task(struct work_struct *work) union ib_gid *gids; int err; struct mlx4_dev *dev = gw->dev->dev; + int is_bonded = mlx4_is_bonded(dev); if (!gw->dev->ib_active) return; @@ -1458,7 +1531,10 @@ static void update_gids_task(struct work_struct *work) if (err) pr_warn("set port command failed\n"); else - mlx4_ib_dispatch_event(gw->dev, gw->port, IB_EVENT_GID_CHANGE); + if ((gw->port == 1) || !is_bonded) + mlx4_ib_dispatch_event(gw->dev, + is_bonded ? 1 : gw->port, + IB_EVENT_GID_CHANGE); mlx4_free_cmd_mailbox(dev, mailbox); kfree(gw); @@ -1874,7 +1950,8 @@ static void mlx4_ib_scan_netdevs(struct mlx4_ib_dev *ibdev, * don't want the bond IP based gids in the table since * flows that select port by gid may get the down port. */ - if (port_state == IB_PORT_DOWN) { + if (port_state == IB_PORT_DOWN && + !mlx4_is_bonded(ibdev->dev)) { reset_gid_table(ibdev, port); mlx4_ib_set_default_gid(ibdev, curr_netdev, @@ -1937,7 +2014,8 @@ static void init_pkeys(struct mlx4_ib_dev *ibdev) int i; if (mlx4_is_master(ibdev->dev)) { - for (slave = 0; slave <= ibdev->dev->num_vfs; ++slave) { + for (slave = 0; slave <= ibdev->dev->persist->num_vfs; + ++slave) { for (port = 1; port <= ibdev->dev->caps.num_ports; ++port) { for (i = 0; i < ibdev->dev->phys_caps.pkey_phys_table_len[port]; @@ -1994,7 +2072,7 @@ static void mlx4_ib_alloc_eqs(struct mlx4_dev *dev, struct mlx4_ib_dev *ibdev) mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_IB) { for (j = 0; j < eq_per_port; j++) { snprintf(name, sizeof(name), "mlx4-ib-%d-%d@%s", - i, j, dev->pdev->bus->name); + i, j, dev->persist->pdev->bus->name); /* Set IRQ for specific name (per ring) */ if (mlx4_assign_eq(dev, name, NULL, &ibdev->eq_table[eq])) { @@ -2045,6 +2123,7 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) int err; struct mlx4_ib_iboe *iboe; int ib_num_ports = 0; + int num_req_counters; pr_info_once("%s", mlx4_ib_version); @@ -2058,7 +2137,8 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) ibdev = (struct mlx4_ib_dev *) ib_alloc_device(sizeof *ibdev); if (!ibdev) { - dev_err(&dev->pdev->dev, "Device struct alloc failed\n"); + dev_err(&dev->persist->pdev->dev, + "Device struct alloc failed\n"); return NULL; } @@ -2077,15 +2157,17 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) MLX4_INIT_DOORBELL_LOCK(&ibdev->uar_lock); ibdev->dev = dev; + ibdev->bond_next_port = 0; strlcpy(ibdev->ib_dev.name, "mlx4_%d", IB_DEVICE_NAME_MAX); ibdev->ib_dev.owner = THIS_MODULE; ibdev->ib_dev.node_type = RDMA_NODE_IB_CA; ibdev->ib_dev.local_dma_lkey = dev->caps.reserved_lkey; ibdev->num_ports = num_ports; - ibdev->ib_dev.phys_port_cnt = ibdev->num_ports; + ibdev->ib_dev.phys_port_cnt = mlx4_is_bonded(dev) ? + 1 : ibdev->num_ports; ibdev->ib_dev.num_comp_vectors = dev->caps.num_comp_vectors; - ibdev->ib_dev.dma_device = &dev->pdev->dev; + ibdev->ib_dev.dma_device = &dev->persist->pdev->dev; if (dev->caps.userspace_caps) ibdev->ib_dev.uverbs_abi_ver = MLX4_IB_UVERBS_ABI_VERSION; @@ -2204,7 +2286,8 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) if (init_node_data(ibdev)) goto err_map; - for (i = 0; i < ibdev->num_ports; ++i) { + num_req_counters = mlx4_is_bonded(dev) ? 1 : ibdev->num_ports; + for (i = 0; i < num_req_counters; ++i) { mutex_init(&ibdev->qp1_proxy_lock[i]); if (mlx4_ib_port_link_layer(&ibdev->ib_dev, i + 1) == IB_LINK_LAYER_ETHERNET) { @@ -2215,12 +2298,18 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) ibdev->counters[i] = -1; } } + if (mlx4_is_bonded(dev)) + for (i = 1; i < ibdev->num_ports ; ++i) + ibdev->counters[i] = ibdev->counters[0]; + mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_IB) ib_num_ports++; spin_lock_init(&ibdev->sm_lock); mutex_init(&ibdev->cap_mask_mutex); + INIT_LIST_HEAD(&ibdev->qp_list); + spin_lock_init(&ibdev->reset_flow_resource_lock); if (ibdev->steering_support == MLX4_STEERING_MODE_DEVICE_MANAGED && ib_num_ports) { @@ -2236,7 +2325,8 @@ static void *mlx4_ib_add(struct mlx4_dev *dev) sizeof(long), GFP_KERNEL); if (!ibdev->ib_uc_qpns_bitmap) { - dev_err(&dev->pdev->dev, "bit map alloc failed\n"); + dev_err(&dev->persist->pdev->dev, + "bit map alloc failed\n"); goto err_steer_qp_release; } @@ -2534,6 +2624,99 @@ out: return; } +static void mlx4_ib_handle_catas_error(struct mlx4_ib_dev *ibdev) +{ + struct mlx4_ib_qp *mqp; + unsigned long flags_qp; + unsigned long flags_cq; + struct mlx4_ib_cq *send_mcq, *recv_mcq; + struct list_head cq_notify_list; + struct mlx4_cq *mcq; + unsigned long flags; + + pr_warn("mlx4_ib_handle_catas_error was started\n"); + INIT_LIST_HEAD(&cq_notify_list); + + /* Go over qp list reside on that ibdev, sync with create/destroy qp.*/ + spin_lock_irqsave(&ibdev->reset_flow_resource_lock, flags); + + list_for_each_entry(mqp, &ibdev->qp_list, qps_list) { + spin_lock_irqsave(&mqp->sq.lock, flags_qp); + if (mqp->sq.tail != mqp->sq.head) { + send_mcq = to_mcq(mqp->ibqp.send_cq); + spin_lock_irqsave(&send_mcq->lock, flags_cq); + if (send_mcq->mcq.comp && + mqp->ibqp.send_cq->comp_handler) { + if (!send_mcq->mcq.reset_notify_added) { + send_mcq->mcq.reset_notify_added = 1; + list_add_tail(&send_mcq->mcq.reset_notify, + &cq_notify_list); + } + } + spin_unlock_irqrestore(&send_mcq->lock, flags_cq); + } + spin_unlock_irqrestore(&mqp->sq.lock, flags_qp); + /* Now, handle the QP's receive queue */ + spin_lock_irqsave(&mqp->rq.lock, flags_qp); + /* no handling is needed for SRQ */ + if (!mqp->ibqp.srq) { + if (mqp->rq.tail != mqp->rq.head) { + recv_mcq = to_mcq(mqp->ibqp.recv_cq); + spin_lock_irqsave(&recv_mcq->lock, flags_cq); + if (recv_mcq->mcq.comp && + mqp->ibqp.recv_cq->comp_handler) { + if (!recv_mcq->mcq.reset_notify_added) { + recv_mcq->mcq.reset_notify_added = 1; + list_add_tail(&recv_mcq->mcq.reset_notify, + &cq_notify_list); + } + } + spin_unlock_irqrestore(&recv_mcq->lock, + flags_cq); + } + } + spin_unlock_irqrestore(&mqp->rq.lock, flags_qp); + } + + list_for_each_entry(mcq, &cq_notify_list, reset_notify) { + mcq->comp(mcq); + } + spin_unlock_irqrestore(&ibdev->reset_flow_resource_lock, flags); + pr_warn("mlx4_ib_handle_catas_error ended\n"); +} + +static void handle_bonded_port_state_event(struct work_struct *work) +{ + struct ib_event_work *ew = + container_of(work, struct ib_event_work, work); + struct mlx4_ib_dev *ibdev = ew->ib_dev; + enum ib_port_state bonded_port_state = IB_PORT_NOP; + int i; + struct ib_event ibev; + + kfree(ew); + spin_lock_bh(&ibdev->iboe.lock); + for (i = 0; i < MLX4_MAX_PORTS; ++i) { + struct net_device *curr_netdev = ibdev->iboe.netdevs[i]; + + enum ib_port_state curr_port_state = + (netif_running(curr_netdev) && + netif_carrier_ok(curr_netdev)) ? + IB_PORT_ACTIVE : IB_PORT_DOWN; + + bonded_port_state = (bonded_port_state != IB_PORT_ACTIVE) ? + curr_port_state : IB_PORT_ACTIVE; + } + spin_unlock_bh(&ibdev->iboe.lock); + + ibev.device = &ibdev->ib_dev; + ibev.element.port_num = 1; + ibev.event = (bonded_port_state == IB_PORT_ACTIVE) ? + IB_EVENT_PORT_ACTIVE : IB_EVENT_PORT_ERR; + + ib_dispatch_event(&ibev); +} + static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr, enum mlx4_dev_event event, unsigned long param) { @@ -2543,6 +2726,18 @@ static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr, struct ib_event_work *ew; int p = 0; + if (mlx4_is_bonded(dev) && + ((event == MLX4_DEV_EVENT_PORT_UP) || + (event == MLX4_DEV_EVENT_PORT_DOWN))) { + ew = kmalloc(sizeof(*ew), GFP_ATOMIC); + if (!ew) + return; + INIT_WORK(&ew->work, handle_bonded_port_state_event); + ew->ib_dev = ibdev; + queue_work(wq, &ew->work); + return; + } + if (event == MLX4_DEV_EVENT_PORT_MGMT_CHANGE) eqe = (struct mlx4_eqe *)param; else @@ -2569,6 +2764,7 @@ static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr, case MLX4_DEV_EVENT_CATASTROPHIC_ERROR: ibdev->ib_active = false; ibev.event = IB_EVENT_DEVICE_FATAL; + mlx4_ib_handle_catas_error(ibdev); break; case MLX4_DEV_EVENT_PORT_MGMT_CHANGE: @@ -2603,7 +2799,7 @@ static void mlx4_ib_event(struct mlx4_dev *dev, void *ibdev_ptr, } ibev.device = ibdev_ptr; - ibev.element.port_num = (u8) p; + ibev.element.port_num = mlx4_is_bonded(ibdev->dev) ? 1 : (u8)p; ib_dispatch_event(&ibev); } @@ -2612,7 +2808,8 @@ static struct mlx4_interface mlx4_ib_interface = { .add = mlx4_ib_add, .remove = mlx4_ib_remove, .event = mlx4_ib_event, - .protocol = MLX4_PROT_IB_IPV6 + .protocol = MLX4_PROT_IB_IPV6, + .flags = MLX4_INTFF_BONDING }; static int __init mlx4_ib_init(void) diff --git a/drivers/infiniband/hw/mlx4/mlx4_ib.h b/drivers/infiniband/hw/mlx4/mlx4_ib.h index 6eb743f65f6f..f829fd935b79 100644 --- a/drivers/infiniband/hw/mlx4/mlx4_ib.h +++ b/drivers/infiniband/hw/mlx4/mlx4_ib.h @@ -110,6 +110,9 @@ struct mlx4_ib_cq { struct mutex resize_mutex; struct ib_umem *umem; struct ib_umem *resize_umem; + /* List of qps that it serves.*/ + struct list_head send_qp_list; + struct list_head recv_qp_list; }; struct mlx4_ib_mr { @@ -134,10 +137,17 @@ struct mlx4_ib_fmr { struct mlx4_fmr mfmr; }; +#define MAX_REGS_PER_FLOW 2 + +struct mlx4_flow_reg_id { + u64 id; + u64 mirror; +}; + struct mlx4_ib_flow { struct ib_flow ibflow; /* translating DMFS verbs sniffer rule to FW API requires two reg IDs */ - u64 reg_id[2]; + struct mlx4_flow_reg_id reg_id[MAX_REGS_PER_FLOW]; }; struct mlx4_ib_wq { @@ -293,6 +303,9 @@ struct mlx4_ib_qp { struct mlx4_roce_smac_vlan_info pri; struct mlx4_roce_smac_vlan_info alt; u64 reg_id; + struct list_head qps_list; + struct list_head cq_recv_list; + struct list_head cq_send_list; }; struct mlx4_ib_srq { @@ -527,6 +540,10 @@ struct mlx4_ib_dev { struct mlx4_ib_qp *qp1_proxy[MLX4_MAX_PORTS]; /* lock when destroying qp1_proxy and getting netdev events */ struct mutex qp1_proxy_lock[MLX4_MAX_PORTS]; + u8 bond_next_port; + /* protect resources needed as part of reset flow */ + spinlock_t reset_flow_resource_lock; + struct list_head qp_list; }; struct ib_event_work { @@ -622,6 +639,13 @@ static inline struct mlx4_ib_ah *to_mah(struct ib_ah *ibah) return container_of(ibah, struct mlx4_ib_ah, ibah); } +static inline u8 mlx4_ib_bond_next_port(struct mlx4_ib_dev *dev) +{ + dev->bond_next_port = (dev->bond_next_port + 1) % dev->num_ports; + + return dev->bond_next_port + 1; +} + int mlx4_ib_init_sriov(struct mlx4_ib_dev *dev); void mlx4_ib_close_sriov(struct mlx4_ib_dev *dev); diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c index c36ccbd9a644..e0d271782d0a 100644 --- a/drivers/infiniband/hw/mlx4/mr.c +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -401,7 +401,8 @@ struct ib_fast_reg_page_list *mlx4_ib_alloc_fast_reg_page_list(struct ib_device if (!mfrpl->ibfrpl.page_list) goto err_free; - mfrpl->mapped_page_list = dma_alloc_coherent(&dev->dev->pdev->dev, + mfrpl->mapped_page_list = dma_alloc_coherent(&dev->dev->persist-> + pdev->dev, size, &mfrpl->map, GFP_KERNEL); if (!mfrpl->mapped_page_list) @@ -423,7 +424,8 @@ void mlx4_ib_free_fast_reg_page_list(struct ib_fast_reg_page_list *page_list) struct mlx4_ib_fast_reg_page_list *mfrpl = to_mfrpl(page_list); int size = page_list->max_page_list_len * sizeof (u64); - dma_free_coherent(&dev->dev->pdev->dev, size, mfrpl->mapped_page_list, + dma_free_coherent(&dev->dev->persist->pdev->dev, size, + mfrpl->mapped_page_list, mfrpl->map); kfree(mfrpl->ibfrpl.page_list); kfree(mfrpl); diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index cf000b7ad64f..dfc6ca128a7e 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -40,11 +40,17 @@ #include <rdma/ib_addr.h> #include <rdma/ib_mad.h> +#include <linux/mlx4/driver.h> #include <linux/mlx4/qp.h> #include "mlx4_ib.h" #include "user.h" +static void mlx4_ib_lock_cqs(struct mlx4_ib_cq *send_cq, + struct mlx4_ib_cq *recv_cq); +static void mlx4_ib_unlock_cqs(struct mlx4_ib_cq *send_cq, + struct mlx4_ib_cq *recv_cq); + enum { MLX4_IB_ACK_REQ_FREQ = 8, }; @@ -93,17 +99,6 @@ enum { #ifndef ETH_ALEN #define ETH_ALEN 6 #endif -static inline u64 mlx4_mac_to_u64(u8 *addr) -{ - u64 mac = 0; - int i; - - for (i = 0; i < ETH_ALEN; i++) { - mac <<= 8; - mac |= addr[i]; - } - return mac; -} static const __be32 mlx4_ib_opcode[] = { [IB_WR_SEND] = cpu_to_be32(MLX4_OPCODE_SEND), @@ -628,6 +623,8 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd, struct mlx4_ib_sqp *sqp; struct mlx4_ib_qp *qp; enum mlx4_ib_qp_type qp_type = (enum mlx4_ib_qp_type) init_attr->qp_type; + struct mlx4_ib_cq *mcq; + unsigned long flags; /* When tunneling special qps, we use a plain UD qp */ if (sqpn) { @@ -838,6 +835,24 @@ static int create_qp_common(struct mlx4_ib_dev *dev, struct ib_pd *pd, qp->mqp.event = mlx4_ib_qp_event; if (!*caller_qp) *caller_qp = qp; + + spin_lock_irqsave(&dev->reset_flow_resource_lock, flags); + mlx4_ib_lock_cqs(to_mcq(init_attr->send_cq), + to_mcq(init_attr->recv_cq)); + /* Maintain device to QPs access, needed for further handling + * via reset flow + */ + list_add_tail(&qp->qps_list, &dev->qp_list); + /* Maintain CQ to QPs access, needed for further handling + * via reset flow + */ + mcq = to_mcq(init_attr->send_cq); + list_add_tail(&qp->cq_send_list, &mcq->send_qp_list); + mcq = to_mcq(init_attr->recv_cq); + list_add_tail(&qp->cq_recv_list, &mcq->recv_qp_list); + mlx4_ib_unlock_cqs(to_mcq(init_attr->send_cq), + to_mcq(init_attr->recv_cq)); + spin_unlock_irqrestore(&dev->reset_flow_resource_lock, flags); return 0; err_qpn: @@ -896,13 +911,13 @@ static void mlx4_ib_lock_cqs(struct mlx4_ib_cq *send_cq, struct mlx4_ib_cq *recv __acquires(&send_cq->lock) __acquires(&recv_cq->lock) { if (send_cq == recv_cq) { - spin_lock_irq(&send_cq->lock); + spin_lock(&send_cq->lock); __acquire(&recv_cq->lock); } else if (send_cq->mcq.cqn < recv_cq->mcq.cqn) { - spin_lock_irq(&send_cq->lock); + spin_lock(&send_cq->lock); spin_lock_nested(&recv_cq->lock, SINGLE_DEPTH_NESTING); } else { - spin_lock_irq(&recv_cq->lock); + spin_lock(&recv_cq->lock); spin_lock_nested(&send_cq->lock, SINGLE_DEPTH_NESTING); } } @@ -912,13 +927,13 @@ static void mlx4_ib_unlock_cqs(struct mlx4_ib_cq *send_cq, struct mlx4_ib_cq *re { if (send_cq == recv_cq) { __release(&recv_cq->lock); - spin_unlock_irq(&send_cq->lock); + spin_unlock(&send_cq->lock); } else if (send_cq->mcq.cqn < recv_cq->mcq.cqn) { spin_unlock(&recv_cq->lock); - spin_unlock_irq(&send_cq->lock); + spin_unlock(&send_cq->lock); } else { spin_unlock(&send_cq->lock); - spin_unlock_irq(&recv_cq->lock); + spin_unlock(&recv_cq->lock); } } @@ -963,6 +978,7 @@ static void destroy_qp_common(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp, int is_user) { struct mlx4_ib_cq *send_cq, *recv_cq; + unsigned long flags; if (qp->state != IB_QPS_RESET) { if (mlx4_qp_modify(dev->dev, NULL, to_mlx4_state(qp->state), @@ -994,8 +1010,13 @@ static void destroy_qp_common(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp, get_cqs(qp, &send_cq, &recv_cq); + spin_lock_irqsave(&dev->reset_flow_resource_lock, flags); mlx4_ib_lock_cqs(send_cq, recv_cq); + /* del from lists under both locks above to protect reset flow paths */ + list_del(&qp->qps_list); + list_del(&qp->cq_send_list); + list_del(&qp->cq_recv_list); if (!is_user) { __mlx4_ib_cq_clean(recv_cq, qp->mqp.qpn, qp->ibqp.srq ? to_msrq(qp->ibqp.srq): NULL); @@ -1006,6 +1027,7 @@ static void destroy_qp_common(struct mlx4_ib_dev *dev, struct mlx4_ib_qp *qp, mlx4_qp_remove(dev->dev, &qp->mqp); mlx4_ib_unlock_cqs(send_cq, recv_cq); + spin_unlock_irqrestore(&dev->reset_flow_resource_lock, flags); mlx4_qp_free(dev->dev, &qp->mqp); @@ -1915,6 +1937,22 @@ int mlx4_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, goto out; } + if (mlx4_is_bonded(dev->dev) && (attr_mask & IB_QP_PORT)) { + if ((cur_state == IB_QPS_RESET) && (new_state == IB_QPS_INIT)) { + if ((ibqp->qp_type == IB_QPT_RC) || + (ibqp->qp_type == IB_QPT_UD) || + (ibqp->qp_type == IB_QPT_UC) || + (ibqp->qp_type == IB_QPT_RAW_PACKET) || + (ibqp->qp_type == IB_QPT_XRC_INI)) { + attr->port_num = mlx4_ib_bond_next_port(dev); + } + } else { + /* no sense in changing port_num + * when ports are bonded */ + attr_mask &= ~IB_QP_PORT; + } + } + if ((attr_mask & IB_QP_PORT) && (attr->port_num == 0 || attr->port_num > dev->num_ports)) { pr_debug("qpn 0x%x: invalid port number (%d) specified " @@ -1965,6 +2003,9 @@ int mlx4_ib_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, err = __mlx4_ib_modify_qp(ibqp, attr, attr_mask, cur_state, new_state); + if (mlx4_is_bonded(dev->dev) && (attr_mask & IB_QP_PORT)) + attr->port_num = 1; + out: mutex_unlock(&qp->mutex); return err; @@ -2609,8 +2650,15 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, __be32 uninitialized_var(lso_hdr_sz); __be32 blh; int i; + struct mlx4_ib_dev *mdev = to_mdev(ibqp->device); spin_lock_irqsave(&qp->sq.lock, flags); + if (mdev->dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR) { + err = -EIO; + *bad_wr = wr; + nreq = 0; + goto out; + } ind = qp->sq_next_wqe; @@ -2908,10 +2956,18 @@ int mlx4_ib_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *wr, int ind; int max_gs; int i; + struct mlx4_ib_dev *mdev = to_mdev(ibqp->device); max_gs = qp->rq.max_gs; spin_lock_irqsave(&qp->rq.lock, flags); + if (mdev->dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR) { + err = -EIO; + *bad_wr = wr; + nreq = 0; + goto out; + } + ind = qp->rq.head & (qp->rq.wqe_cnt - 1); for (nreq = 0; wr; ++nreq, wr = wr->next) { diff --git a/drivers/infiniband/hw/mlx4/srq.c b/drivers/infiniband/hw/mlx4/srq.c index 62d9285300af..dce5dfe3a70e 100644 --- a/drivers/infiniband/hw/mlx4/srq.c +++ b/drivers/infiniband/hw/mlx4/srq.c @@ -316,8 +316,15 @@ int mlx4_ib_post_srq_recv(struct ib_srq *ibsrq, struct ib_recv_wr *wr, int err = 0; int nreq; int i; + struct mlx4_ib_dev *mdev = to_mdev(ibsrq->device); spin_lock_irqsave(&srq->lock, flags); + if (mdev->dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR) { + err = -EIO; + *bad_wr = wr; + nreq = 0; + goto out; + } for (nreq = 0; wr; ++nreq, wr = wr->next) { if (unlikely(wr->num_sge > srq->msrq.max_gs)) { @@ -362,6 +369,7 @@ int mlx4_ib_post_srq_recv(struct ib_srq *ibsrq, struct ib_recv_wr *wr, *srq->db.db = cpu_to_be32(srq->wqe_ctr); } +out: spin_unlock_irqrestore(&srq->lock, flags); diff --git a/drivers/infiniband/hw/mlx4/sysfs.c b/drivers/infiniband/hw/mlx4/sysfs.c index cb4c66e723b5..d10c2b8a5dad 100644 --- a/drivers/infiniband/hw/mlx4/sysfs.c +++ b/drivers/infiniband/hw/mlx4/sysfs.c @@ -375,7 +375,7 @@ static void get_name(struct mlx4_ib_dev *dev, char *name, int i, int max) char base_name[9]; /* pci_name format is: bus:dev:func -> xxxx:yy:zz.n */ - strlcpy(name, pci_name(dev->dev->pdev), max); + strlcpy(name, pci_name(dev->dev->persist->pdev), max); strncpy(base_name, name, 8); /*till xxxx:yy:*/ base_name[8] = '\0'; /* with no ARI only 3 last bits are used so when the fn is higher than 8 @@ -792,7 +792,7 @@ static int register_pkey_tree(struct mlx4_ib_dev *device) if (!mlx4_is_master(device->dev)) return 0; - for (i = 0; i <= device->dev->num_vfs; ++i) + for (i = 0; i <= device->dev->persist->num_vfs; ++i) register_one_pkey_tree(device, i); return 0; @@ -807,7 +807,7 @@ static void unregister_pkey_tree(struct mlx4_ib_dev *device) if (!mlx4_is_master(device->dev)) return; - for (slave = device->dev->num_vfs; slave >= 0; --slave) { + for (slave = device->dev->persist->num_vfs; slave >= 0; --slave) { list_for_each_entry_safe(p, t, &device->pkeys.pkey_port_list[slave], entry) { diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 8a87404e9c76..03bf81211a54 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -1331,8 +1331,6 @@ static void *mlx5_ib_add(struct mlx5_core_dev *mdev) (1ull << IB_USER_VERBS_CMD_DESTROY_SRQ) | (1ull << IB_USER_VERBS_CMD_CREATE_XSRQ) | (1ull << IB_USER_VERBS_CMD_OPEN_QP); - dev->ib_dev.uverbs_ex_cmd_mask = - (1ull << IB_USER_VERBS_EX_CMD_QUERY_DEVICE); dev->ib_dev.query_device = mlx5_ib_query_device; dev->ib_dev.query_port = mlx5_ib_query_port; diff --git a/drivers/infiniband/hw/mlx5/mem.c b/drivers/infiniband/hw/mlx5/mem.c index b56e4c5593ee..611a9fdf2f38 100644 --- a/drivers/infiniband/hw/mlx5/mem.c +++ b/drivers/infiniband/hw/mlx5/mem.c @@ -81,7 +81,7 @@ void mlx5_ib_cont_pages(struct ib_umem *umem, u64 addr, int *count, int *shift, for (k = 0; k < len; k++) { if (!(i & mask)) { tmp = (unsigned long)pfn; - m = min(m, find_first_bit(&tmp, sizeof(tmp))); + m = min_t(unsigned long, m, find_first_bit(&tmp, sizeof(tmp))); skip = 1 << m; mask = skip - 1; base = pfn; diff --git a/drivers/infiniband/hw/nes/nes_nic.c b/drivers/infiniband/hw/nes/nes_nic.c index 49eb5111d2cd..70acda91eb2a 100644 --- a/drivers/infiniband/hw/nes/nes_nic.c +++ b/drivers/infiniband/hw/nes/nes_nic.c @@ -373,11 +373,11 @@ static int nes_nic_send(struct sk_buff *skb, struct net_device *netdev) wqe_fragment_length = (__le16 *)&nic_sqe->wqe_words[NES_NIC_SQ_WQE_LENGTH_0_TAG_IDX]; /* setup the VLAN tag if present */ - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { nes_debug(NES_DBG_NIC_TX, "%s: VLAN packet to send... VLAN = %08X\n", - netdev->name, vlan_tx_tag_get(skb)); + netdev->name, skb_vlan_tag_get(skb)); wqe_misc = NES_NIC_SQ_WQE_TAGVALUE_ENABLE; - wqe_fragment_length[0] = (__force __le16) vlan_tx_tag_get(skb); + wqe_fragment_length[0] = (__force __le16) skb_vlan_tag_get(skb); } else wqe_misc = 0; @@ -576,11 +576,12 @@ tso_sq_no_longer_full: wqe_fragment_length = (__le16 *)&nic_sqe->wqe_words[NES_NIC_SQ_WQE_LENGTH_0_TAG_IDX]; /* setup the VLAN tag if present */ - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { nes_debug(NES_DBG_NIC_TX, "%s: VLAN packet to send... VLAN = %08X\n", - netdev->name, vlan_tx_tag_get(skb) ); + netdev->name, + skb_vlan_tag_get(skb)); wqe_misc = NES_NIC_SQ_WQE_TAGVALUE_ENABLE; - wqe_fragment_length[0] = (__force __le16) vlan_tx_tag_get(skb); + wqe_fragment_length[0] = (__force __le16) skb_vlan_tag_get(skb); } else wqe_misc = 0; diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index 8ba80a6d3a46..d7562beb5423 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -98,15 +98,9 @@ enum { IPOIB_MCAST_FLAG_FOUND = 0, /* used in set_multicast_list */ IPOIB_MCAST_FLAG_SENDONLY = 1, - /* - * For IPOIB_MCAST_FLAG_BUSY - * When set, in flight join and mcast->mc is unreliable - * When clear and mcast->mc IS_ERR_OR_NULL, need to restart or - * haven't started yet - * When clear and mcast->mc is valid pointer, join was successful - */ - IPOIB_MCAST_FLAG_BUSY = 2, + IPOIB_MCAST_FLAG_BUSY = 2, /* joining or already joined */ IPOIB_MCAST_FLAG_ATTACHED = 3, + IPOIB_MCAST_JOIN_STARTED = 4, MAX_SEND_CQE = 16, IPOIB_CM_COPYBREAK = 256, @@ -323,7 +317,6 @@ struct ipoib_dev_priv { struct list_head multicast_list; struct rb_root multicast_tree; - struct workqueue_struct *wq; struct delayed_work mcast_task; struct work_struct carrier_on_task; struct work_struct flush_light; @@ -484,10 +477,10 @@ void ipoib_ib_dev_flush_heavy(struct work_struct *work); void ipoib_pkey_event(struct work_struct *work); void ipoib_ib_dev_cleanup(struct net_device *dev); -int ipoib_ib_dev_open(struct net_device *dev); +int ipoib_ib_dev_open(struct net_device *dev, int flush); int ipoib_ib_dev_up(struct net_device *dev); -int ipoib_ib_dev_down(struct net_device *dev); -int ipoib_ib_dev_stop(struct net_device *dev); +int ipoib_ib_dev_down(struct net_device *dev, int flush); +int ipoib_ib_dev_stop(struct net_device *dev, int flush); void ipoib_pkey_dev_check_presence(struct net_device *dev); int ipoib_dev_init(struct net_device *dev, struct ib_device *ca, int port); @@ -499,7 +492,7 @@ void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb); void ipoib_mcast_restart_task(struct work_struct *work); int ipoib_mcast_start_thread(struct net_device *dev); -int ipoib_mcast_stop_thread(struct net_device *dev); +int ipoib_mcast_stop_thread(struct net_device *dev, int flush); void ipoib_mcast_dev_down(struct net_device *dev); void ipoib_mcast_dev_flush(struct net_device *dev); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c index 56959adb6c7d..933efcea0d03 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c @@ -474,7 +474,7 @@ static int ipoib_cm_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *even } spin_lock_irq(&priv->lock); - queue_delayed_work(priv->wq, + queue_delayed_work(ipoib_workqueue, &priv->cm.stale_task, IPOIB_CM_RX_DELAY); /* Add this entry to passive ids list head, but do not re-add it * if IB_EVENT_QP_LAST_WQE_REACHED has moved it to flush list. */ @@ -576,7 +576,7 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) spin_lock_irqsave(&priv->lock, flags); list_splice_init(&priv->cm.rx_drain_list, &priv->cm.rx_reap_list); ipoib_cm_start_rx_drain(priv); - queue_work(priv->wq, &priv->cm.rx_reap_task); + queue_work(ipoib_workqueue, &priv->cm.rx_reap_task); spin_unlock_irqrestore(&priv->lock, flags); } else ipoib_warn(priv, "cm recv completion event with wrid %d (> %d)\n", @@ -603,7 +603,7 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) spin_lock_irqsave(&priv->lock, flags); list_move(&p->list, &priv->cm.rx_reap_list); spin_unlock_irqrestore(&priv->lock, flags); - queue_work(priv->wq, &priv->cm.rx_reap_task); + queue_work(ipoib_workqueue, &priv->cm.rx_reap_task); } return; } @@ -827,7 +827,7 @@ void ipoib_cm_handle_tx_wc(struct net_device *dev, struct ib_wc *wc) if (test_and_clear_bit(IPOIB_FLAG_INITIALIZED, &tx->flags)) { list_move(&tx->list, &priv->cm.reap_list); - queue_work(priv->wq, &priv->cm.reap_task); + queue_work(ipoib_workqueue, &priv->cm.reap_task); } clear_bit(IPOIB_FLAG_OPER_UP, &tx->flags); @@ -1255,7 +1255,7 @@ static int ipoib_cm_tx_handler(struct ib_cm_id *cm_id, if (test_and_clear_bit(IPOIB_FLAG_INITIALIZED, &tx->flags)) { list_move(&tx->list, &priv->cm.reap_list); - queue_work(priv->wq, &priv->cm.reap_task); + queue_work(ipoib_workqueue, &priv->cm.reap_task); } spin_unlock_irqrestore(&priv->lock, flags); @@ -1284,7 +1284,7 @@ struct ipoib_cm_tx *ipoib_cm_create_tx(struct net_device *dev, struct ipoib_path tx->dev = dev; list_add(&tx->list, &priv->cm.start_list); set_bit(IPOIB_FLAG_INITIALIZED, &tx->flags); - queue_work(priv->wq, &priv->cm.start_task); + queue_work(ipoib_workqueue, &priv->cm.start_task); return tx; } @@ -1295,7 +1295,7 @@ void ipoib_cm_destroy_tx(struct ipoib_cm_tx *tx) if (test_and_clear_bit(IPOIB_FLAG_INITIALIZED, &tx->flags)) { spin_lock_irqsave(&priv->lock, flags); list_move(&tx->list, &priv->cm.reap_list); - queue_work(priv->wq, &priv->cm.reap_task); + queue_work(ipoib_workqueue, &priv->cm.reap_task); ipoib_dbg(priv, "Reap connection for gid %pI6\n", tx->neigh->daddr + 4); tx->neigh = NULL; @@ -1417,7 +1417,7 @@ void ipoib_cm_skb_too_long(struct net_device *dev, struct sk_buff *skb, skb_queue_tail(&priv->cm.skb_queue, skb); if (e) - queue_work(priv->wq, &priv->cm.skb_task); + queue_work(ipoib_workqueue, &priv->cm.skb_task); } static void ipoib_cm_rx_reap(struct work_struct *work) @@ -1450,7 +1450,7 @@ static void ipoib_cm_stale_task(struct work_struct *work) } if (!list_empty(&priv->cm.passive_ids)) - queue_delayed_work(priv->wq, + queue_delayed_work(ipoib_workqueue, &priv->cm.stale_task, IPOIB_CM_RX_DELAY); spin_unlock_irq(&priv->lock); } diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c index fe65abb5150c..72626c348174 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c @@ -655,7 +655,7 @@ void ipoib_reap_ah(struct work_struct *work) __ipoib_reap_ah(dev); if (!test_bit(IPOIB_STOP_REAPER, &priv->flags)) - queue_delayed_work(priv->wq, &priv->ah_reap_task, + queue_delayed_work(ipoib_workqueue, &priv->ah_reap_task, round_jiffies_relative(HZ)); } @@ -664,7 +664,7 @@ static void ipoib_ib_tx_timer_func(unsigned long ctx) drain_tx_cq((struct net_device *)ctx); } -int ipoib_ib_dev_open(struct net_device *dev) +int ipoib_ib_dev_open(struct net_device *dev, int flush) { struct ipoib_dev_priv *priv = netdev_priv(dev); int ret; @@ -696,7 +696,7 @@ int ipoib_ib_dev_open(struct net_device *dev) } clear_bit(IPOIB_STOP_REAPER, &priv->flags); - queue_delayed_work(priv->wq, &priv->ah_reap_task, + queue_delayed_work(ipoib_workqueue, &priv->ah_reap_task, round_jiffies_relative(HZ)); if (!test_and_set_bit(IPOIB_FLAG_INITIALIZED, &priv->flags)) @@ -706,7 +706,7 @@ int ipoib_ib_dev_open(struct net_device *dev) dev_stop: if (!test_and_set_bit(IPOIB_FLAG_INITIALIZED, &priv->flags)) napi_enable(&priv->napi); - ipoib_ib_dev_stop(dev); + ipoib_ib_dev_stop(dev, flush); return -1; } @@ -738,7 +738,7 @@ int ipoib_ib_dev_up(struct net_device *dev) return ipoib_mcast_start_thread(dev); } -int ipoib_ib_dev_down(struct net_device *dev) +int ipoib_ib_dev_down(struct net_device *dev, int flush) { struct ipoib_dev_priv *priv = netdev_priv(dev); @@ -747,7 +747,7 @@ int ipoib_ib_dev_down(struct net_device *dev) clear_bit(IPOIB_FLAG_OPER_UP, &priv->flags); netif_carrier_off(dev); - ipoib_mcast_stop_thread(dev); + ipoib_mcast_stop_thread(dev, flush); ipoib_mcast_dev_flush(dev); ipoib_flush_paths(dev); @@ -807,7 +807,7 @@ void ipoib_drain_cq(struct net_device *dev) local_bh_enable(); } -int ipoib_ib_dev_stop(struct net_device *dev) +int ipoib_ib_dev_stop(struct net_device *dev, int flush) { struct ipoib_dev_priv *priv = netdev_priv(dev); struct ib_qp_attr qp_attr; @@ -880,7 +880,8 @@ timeout: /* Wait for all AHs to be reaped */ set_bit(IPOIB_STOP_REAPER, &priv->flags); cancel_delayed_work(&priv->ah_reap_task); - flush_workqueue(priv->wq); + if (flush) + flush_workqueue(ipoib_workqueue); begin = jiffies; @@ -917,7 +918,7 @@ int ipoib_ib_dev_init(struct net_device *dev, struct ib_device *ca, int port) (unsigned long) dev); if (dev->flags & IFF_UP) { - if (ipoib_ib_dev_open(dev)) { + if (ipoib_ib_dev_open(dev, 1)) { ipoib_transport_dev_cleanup(dev); return -ENODEV; } @@ -1039,12 +1040,12 @@ static void __ipoib_ib_dev_flush(struct ipoib_dev_priv *priv, } if (level >= IPOIB_FLUSH_NORMAL) - ipoib_ib_dev_down(dev); + ipoib_ib_dev_down(dev, 0); if (level == IPOIB_FLUSH_HEAVY) { if (test_bit(IPOIB_FLAG_INITIALIZED, &priv->flags)) - ipoib_ib_dev_stop(dev); - if (ipoib_ib_dev_open(dev) != 0) + ipoib_ib_dev_stop(dev, 0); + if (ipoib_ib_dev_open(dev, 0) != 0) return; if (netif_queue_stopped(dev)) netif_start_queue(dev); @@ -1096,7 +1097,7 @@ void ipoib_ib_dev_cleanup(struct net_device *dev) */ ipoib_flush_paths(dev); - ipoib_mcast_stop_thread(dev); + ipoib_mcast_stop_thread(dev, 1); ipoib_mcast_dev_flush(dev); ipoib_transport_dev_cleanup(dev); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 6bad17d4d588..58b5aa3b6f2d 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -108,7 +108,7 @@ int ipoib_open(struct net_device *dev) set_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags); - if (ipoib_ib_dev_open(dev)) { + if (ipoib_ib_dev_open(dev, 1)) { if (!test_bit(IPOIB_PKEY_ASSIGNED, &priv->flags)) return 0; goto err_disable; @@ -139,7 +139,7 @@ int ipoib_open(struct net_device *dev) return 0; err_stop: - ipoib_ib_dev_stop(dev); + ipoib_ib_dev_stop(dev, 1); err_disable: clear_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags); @@ -157,8 +157,8 @@ static int ipoib_stop(struct net_device *dev) netif_stop_queue(dev); - ipoib_ib_dev_down(dev); - ipoib_ib_dev_stop(dev); + ipoib_ib_dev_down(dev, 1); + ipoib_ib_dev_stop(dev, 0); if (!test_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags)) { struct ipoib_dev_priv *cpriv; @@ -839,7 +839,7 @@ static void ipoib_set_mcast_list(struct net_device *dev) return; } - queue_work(priv->wq, &priv->restart_task); + queue_work(ipoib_workqueue, &priv->restart_task); } static u32 ipoib_addr_hash(struct ipoib_neigh_hash *htbl, u8 *daddr) @@ -954,7 +954,7 @@ static void ipoib_reap_neigh(struct work_struct *work) __ipoib_reap_neigh(priv); if (!test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags)) - queue_delayed_work(priv->wq, &priv->neigh_reap_task, + queue_delayed_work(ipoib_workqueue, &priv->neigh_reap_task, arp_tbl.gc_interval); } @@ -1133,7 +1133,7 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv) /* start garbage collection */ clear_bit(IPOIB_STOP_NEIGH_GC, &priv->flags); - queue_delayed_work(priv->wq, &priv->neigh_reap_task, + queue_delayed_work(ipoib_workqueue, &priv->neigh_reap_task, arp_tbl.gc_interval); return 0; @@ -1262,13 +1262,15 @@ int ipoib_dev_init(struct net_device *dev, struct ib_device *ca, int port) { struct ipoib_dev_priv *priv = netdev_priv(dev); + if (ipoib_neigh_hash_init(priv) < 0) + goto out; /* Allocate RX/TX "rings" to hold queued skbs */ priv->rx_ring = kzalloc(ipoib_recvq_size * sizeof *priv->rx_ring, GFP_KERNEL); if (!priv->rx_ring) { printk(KERN_WARNING "%s: failed to allocate RX ring (%d entries)\n", ca->name, ipoib_recvq_size); - goto out; + goto out_neigh_hash_cleanup; } priv->tx_ring = vzalloc(ipoib_sendq_size * sizeof *priv->tx_ring); @@ -1283,24 +1285,16 @@ int ipoib_dev_init(struct net_device *dev, struct ib_device *ca, int port) if (ipoib_ib_dev_init(dev, ca, port)) goto out_tx_ring_cleanup; - /* - * Must be after ipoib_ib_dev_init so we can allocate a per - * device wq there and use it here - */ - if (ipoib_neigh_hash_init(priv) < 0) - goto out_dev_uninit; - return 0; -out_dev_uninit: - ipoib_ib_dev_cleanup(dev); - out_tx_ring_cleanup: vfree(priv->tx_ring); out_rx_ring_cleanup: kfree(priv->rx_ring); +out_neigh_hash_cleanup: + ipoib_neigh_hash_uninit(dev); out: return -ENOMEM; } @@ -1323,12 +1317,6 @@ void ipoib_dev_cleanup(struct net_device *dev) } unregister_netdevice_many(&head); - /* - * Must be before ipoib_ib_dev_cleanup or we delete an in use - * work queue - */ - ipoib_neigh_hash_uninit(dev); - ipoib_ib_dev_cleanup(dev); kfree(priv->rx_ring); @@ -1336,6 +1324,8 @@ void ipoib_dev_cleanup(struct net_device *dev) priv->rx_ring = NULL; priv->tx_ring = NULL; + + ipoib_neigh_hash_uninit(dev); } static const struct header_ops ipoib_header_ops = { @@ -1646,7 +1636,7 @@ register_failed: /* Stop GC if started before flush */ set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags); cancel_delayed_work(&priv->neigh_reap_task); - flush_workqueue(priv->wq); + flush_workqueue(ipoib_workqueue); event_failed: ipoib_dev_cleanup(priv->dev); @@ -1717,7 +1707,7 @@ static void ipoib_remove_one(struct ib_device *device) /* Stop GC */ set_bit(IPOIB_STOP_NEIGH_GC, &priv->flags); cancel_delayed_work(&priv->neigh_reap_task); - flush_workqueue(priv->wq); + flush_workqueue(ipoib_workqueue); unregister_netdev(priv->dev); free_netdev(priv->dev); @@ -1758,13 +1748,8 @@ static int __init ipoib_init_module(void) * unregister_netdev() and linkwatch_event take the rtnl lock, * so flush_scheduled_work() can deadlock during device * removal. - * - * In addition, bringing one device up and another down at the - * same time can deadlock a single workqueue, so we have this - * global fallback workqueue, but we also attempt to open a - * per device workqueue each time we bring an interface up */ - ipoib_workqueue = create_singlethread_workqueue("ipoib_flush"); + ipoib_workqueue = create_singlethread_workqueue("ipoib"); if (!ipoib_workqueue) { ret = -ENOMEM; goto err_fs; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index bc50dd0d0e4d..ffb83b5f7e80 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -190,6 +190,12 @@ static int ipoib_mcast_join_finish(struct ipoib_mcast *mcast, spin_unlock_irq(&priv->lock); priv->tx_wr.wr.ud.remote_qkey = priv->qkey; set_qkey = 1; + + if (!ipoib_cm_admin_enabled(dev)) { + rtnl_lock(); + dev_set_mtu(dev, min(priv->mcast_mtu, priv->admin_mtu)); + rtnl_unlock(); + } } if (!test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)) { @@ -271,27 +277,16 @@ ipoib_mcast_sendonly_join_complete(int status, struct ipoib_mcast *mcast = multicast->context; struct net_device *dev = mcast->dev; - /* - * We have to take the mutex to force mcast_sendonly_join to - * return from ib_sa_multicast_join and set mcast->mc to a - * valid value. Otherwise we were racing with ourselves in - * that we might fail here, but get a valid return from - * ib_sa_multicast_join after we had cleared mcast->mc here, - * resulting in mis-matched joins and leaves and a deadlock - */ - mutex_lock(&mcast_mutex); - /* We trap for port events ourselves. */ if (status == -ENETRESET) - goto out; + return 0; if (!status) status = ipoib_mcast_join_finish(mcast, &multicast->rec); if (status) { if (mcast->logcount++ < 20) - ipoib_dbg_mcast(netdev_priv(dev), "sendonly multicast " - "join failed for %pI6, status %d\n", + ipoib_dbg_mcast(netdev_priv(dev), "multicast join failed for %pI6, status %d\n", mcast->mcmember.mgid.raw, status); /* Flush out any queued packets */ @@ -301,15 +296,11 @@ ipoib_mcast_sendonly_join_complete(int status, dev_kfree_skb_any(skb_dequeue(&mcast->pkt_queue)); } netif_tx_unlock_bh(dev); + + /* Clear the busy flag so we try again */ + status = test_and_clear_bit(IPOIB_MCAST_FLAG_BUSY, + &mcast->flags); } -out: - clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags); - if (status) - mcast->mc = NULL; - complete(&mcast->done); - if (status == -ENETRESET) - status = 0; - mutex_unlock(&mcast_mutex); return status; } @@ -327,14 +318,12 @@ static int ipoib_mcast_sendonly_join(struct ipoib_mcast *mcast) int ret = 0; if (!test_bit(IPOIB_FLAG_OPER_UP, &priv->flags)) { - ipoib_dbg_mcast(priv, "device shutting down, no sendonly " - "multicast joins\n"); + ipoib_dbg_mcast(priv, "device shutting down, no multicast joins\n"); return -ENODEV; } - if (test_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags)) { - ipoib_dbg_mcast(priv, "multicast entry busy, skipping " - "sendonly join\n"); + if (test_and_set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags)) { + ipoib_dbg_mcast(priv, "multicast entry busy, skipping\n"); return -EBUSY; } @@ -342,9 +331,6 @@ static int ipoib_mcast_sendonly_join(struct ipoib_mcast *mcast) rec.port_gid = priv->local_gid; rec.pkey = cpu_to_be16(priv->pkey); - mutex_lock(&mcast_mutex); - init_completion(&mcast->done); - set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags); mcast->mc = ib_sa_join_multicast(&ipoib_sa_client, priv->ca, priv->port, &rec, IB_SA_MCMEMBER_REC_MGID | @@ -357,14 +343,12 @@ static int ipoib_mcast_sendonly_join(struct ipoib_mcast *mcast) if (IS_ERR(mcast->mc)) { ret = PTR_ERR(mcast->mc); clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags); - complete(&mcast->done); - ipoib_warn(priv, "ib_sa_join_multicast for sendonly join " - "failed (ret = %d)\n", ret); + ipoib_warn(priv, "ib_sa_join_multicast failed (ret = %d)\n", + ret); } else { - ipoib_dbg_mcast(priv, "no multicast record for %pI6, starting " - "sendonly join\n", mcast->mcmember.mgid.raw); + ipoib_dbg_mcast(priv, "no multicast record for %pI6, starting join\n", + mcast->mcmember.mgid.raw); } - mutex_unlock(&mcast_mutex); return ret; } @@ -375,29 +359,18 @@ void ipoib_mcast_carrier_on_task(struct work_struct *work) carrier_on_task); struct ib_port_attr attr; + /* + * Take rtnl_lock to avoid racing with ipoib_stop() and + * turning the carrier back on while a device is being + * removed. + */ if (ib_query_port(priv->ca, priv->port, &attr) || attr.state != IB_PORT_ACTIVE) { ipoib_dbg(priv, "Keeping carrier off until IB port is active\n"); return; } - /* - * Take rtnl_lock to avoid racing with ipoib_stop() and - * turning the carrier back on while a device is being - * removed. However, ipoib_stop() will attempt to flush - * the workqueue while holding the rtnl lock, so loop - * on trylock until either we get the lock or we see - * FLAG_ADMIN_UP go away as that signals that we are bailing - * and can safely ignore the carrier on work. - */ - while (!rtnl_trylock()) { - if (!test_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags)) - return; - else - msleep(20); - } - if (!ipoib_cm_admin_enabled(priv->dev)) - dev_set_mtu(priv->dev, min(priv->mcast_mtu, priv->admin_mtu)); + rtnl_lock(); netif_carrier_on(priv->dev); rtnl_unlock(); } @@ -412,63 +385,60 @@ static int ipoib_mcast_join_complete(int status, ipoib_dbg_mcast(priv, "join completion for %pI6 (status %d)\n", mcast->mcmember.mgid.raw, status); - /* - * We have to take the mutex to force mcast_join to - * return from ib_sa_multicast_join and set mcast->mc to a - * valid value. Otherwise we were racing with ourselves in - * that we might fail here, but get a valid return from - * ib_sa_multicast_join after we had cleared mcast->mc here, - * resulting in mis-matched joins and leaves and a deadlock - */ - mutex_lock(&mcast_mutex); - /* We trap for port events ourselves. */ - if (status == -ENETRESET) + if (status == -ENETRESET) { + status = 0; goto out; + } if (!status) status = ipoib_mcast_join_finish(mcast, &multicast->rec); if (!status) { mcast->backoff = 1; + mutex_lock(&mcast_mutex); if (test_bit(IPOIB_MCAST_RUN, &priv->flags)) - queue_delayed_work(priv->wq, &priv->mcast_task, 0); + queue_delayed_work(ipoib_workqueue, + &priv->mcast_task, 0); + mutex_unlock(&mcast_mutex); /* - * Defer carrier on work to priv->wq to avoid a + * Defer carrier on work to ipoib_workqueue to avoid a * deadlock on rtnl_lock here. */ if (mcast == priv->broadcast) - queue_work(priv->wq, &priv->carrier_on_task); - } else { - if (mcast->logcount++ < 20) { - if (status == -ETIMEDOUT || status == -EAGAIN) { - ipoib_dbg_mcast(priv, "multicast join failed for %pI6, status %d\n", - mcast->mcmember.mgid.raw, status); - } else { - ipoib_warn(priv, "multicast join failed for %pI6, status %d\n", - mcast->mcmember.mgid.raw, status); - } - } + queue_work(ipoib_workqueue, &priv->carrier_on_task); - mcast->backoff *= 2; - if (mcast->backoff > IPOIB_MAX_BACKOFF_SECONDS) - mcast->backoff = IPOIB_MAX_BACKOFF_SECONDS; + status = 0; + goto out; } -out: + + if (mcast->logcount++ < 20) { + if (status == -ETIMEDOUT || status == -EAGAIN) { + ipoib_dbg_mcast(priv, "multicast join failed for %pI6, status %d\n", + mcast->mcmember.mgid.raw, status); + } else { + ipoib_warn(priv, "multicast join failed for %pI6, status %d\n", + mcast->mcmember.mgid.raw, status); + } + } + + mcast->backoff *= 2; + if (mcast->backoff > IPOIB_MAX_BACKOFF_SECONDS) + mcast->backoff = IPOIB_MAX_BACKOFF_SECONDS; + + /* Clear the busy flag so we try again */ + status = test_and_clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags); + + mutex_lock(&mcast_mutex); spin_lock_irq(&priv->lock); - clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags); - if (status) - mcast->mc = NULL; - complete(&mcast->done); - if (status == -ENETRESET) - status = 0; - if (status && test_bit(IPOIB_MCAST_RUN, &priv->flags)) - queue_delayed_work(priv->wq, &priv->mcast_task, + if (test_bit(IPOIB_MCAST_RUN, &priv->flags)) + queue_delayed_work(ipoib_workqueue, &priv->mcast_task, mcast->backoff * HZ); spin_unlock_irq(&priv->lock); mutex_unlock(&mcast_mutex); - +out: + complete(&mcast->done); return status; } @@ -517,9 +487,10 @@ static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast, rec.hop_limit = priv->broadcast->mcmember.hop_limit; } - mutex_lock(&mcast_mutex); - init_completion(&mcast->done); set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags); + init_completion(&mcast->done); + set_bit(IPOIB_MCAST_JOIN_STARTED, &mcast->flags); + mcast->mc = ib_sa_join_multicast(&ipoib_sa_client, priv->ca, priv->port, &rec, comp_mask, GFP_KERNEL, ipoib_mcast_join_complete, mcast); @@ -533,11 +504,13 @@ static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast, if (mcast->backoff > IPOIB_MAX_BACKOFF_SECONDS) mcast->backoff = IPOIB_MAX_BACKOFF_SECONDS; + mutex_lock(&mcast_mutex); if (test_bit(IPOIB_MCAST_RUN, &priv->flags)) - queue_delayed_work(priv->wq, &priv->mcast_task, + queue_delayed_work(ipoib_workqueue, + &priv->mcast_task, mcast->backoff * HZ); + mutex_unlock(&mcast_mutex); } - mutex_unlock(&mcast_mutex); } void ipoib_mcast_join_task(struct work_struct *work) @@ -574,8 +547,8 @@ void ipoib_mcast_join_task(struct work_struct *work) ipoib_warn(priv, "failed to allocate broadcast group\n"); mutex_lock(&mcast_mutex); if (test_bit(IPOIB_MCAST_RUN, &priv->flags)) - queue_delayed_work(priv->wq, &priv->mcast_task, - HZ); + queue_delayed_work(ipoib_workqueue, + &priv->mcast_task, HZ); mutex_unlock(&mcast_mutex); return; } @@ -590,8 +563,7 @@ void ipoib_mcast_join_task(struct work_struct *work) } if (!test_bit(IPOIB_MCAST_FLAG_ATTACHED, &priv->broadcast->flags)) { - if (IS_ERR_OR_NULL(priv->broadcast->mc) && - !test_bit(IPOIB_MCAST_FLAG_BUSY, &priv->broadcast->flags)) + if (!test_bit(IPOIB_MCAST_FLAG_BUSY, &priv->broadcast->flags)) ipoib_mcast_join(dev, priv->broadcast, 0); return; } @@ -599,33 +571,23 @@ void ipoib_mcast_join_task(struct work_struct *work) while (1) { struct ipoib_mcast *mcast = NULL; - /* - * Need the mutex so our flags are consistent, need the - * priv->lock so we don't race with list removals in either - * mcast_dev_flush or mcast_restart_task - */ - mutex_lock(&mcast_mutex); spin_lock_irq(&priv->lock); list_for_each_entry(mcast, &priv->multicast_list, list) { - if (IS_ERR_OR_NULL(mcast->mc) && - !test_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags) && - !test_bit(IPOIB_MCAST_FLAG_ATTACHED, &mcast->flags)) { + if (!test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags) + && !test_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags) + && !test_bit(IPOIB_MCAST_FLAG_ATTACHED, &mcast->flags)) { /* Found the next unjoined group */ break; } } spin_unlock_irq(&priv->lock); - mutex_unlock(&mcast_mutex); if (&mcast->list == &priv->multicast_list) { /* All done */ break; } - if (test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)) - ipoib_mcast_sendonly_join(mcast); - else - ipoib_mcast_join(dev, mcast, 1); + ipoib_mcast_join(dev, mcast, 1); return; } @@ -642,13 +604,13 @@ int ipoib_mcast_start_thread(struct net_device *dev) mutex_lock(&mcast_mutex); if (!test_and_set_bit(IPOIB_MCAST_RUN, &priv->flags)) - queue_delayed_work(priv->wq, &priv->mcast_task, 0); + queue_delayed_work(ipoib_workqueue, &priv->mcast_task, 0); mutex_unlock(&mcast_mutex); return 0; } -int ipoib_mcast_stop_thread(struct net_device *dev) +int ipoib_mcast_stop_thread(struct net_device *dev, int flush) { struct ipoib_dev_priv *priv = netdev_priv(dev); @@ -659,7 +621,8 @@ int ipoib_mcast_stop_thread(struct net_device *dev) cancel_delayed_work(&priv->mcast_task); mutex_unlock(&mcast_mutex); - flush_workqueue(priv->wq); + if (flush) + flush_workqueue(ipoib_workqueue); return 0; } @@ -670,9 +633,6 @@ static int ipoib_mcast_leave(struct net_device *dev, struct ipoib_mcast *mcast) int ret = 0; if (test_and_clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags)) - ipoib_warn(priv, "ipoib_mcast_leave on an in-flight join\n"); - - if (!IS_ERR_OR_NULL(mcast->mc)) ib_sa_free_multicast(mcast->mc); if (test_and_clear_bit(IPOIB_MCAST_FLAG_ATTACHED, &mcast->flags)) { @@ -725,8 +685,6 @@ void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb) memcpy(mcast->mcmember.mgid.raw, mgid, sizeof (union ib_gid)); __ipoib_mcast_add(dev, mcast); list_add_tail(&mcast->list, &priv->multicast_list); - if (!test_and_set_bit(IPOIB_MCAST_RUN, &priv->flags)) - queue_delayed_work(priv->wq, &priv->mcast_task, 0); } if (!mcast->ah) { @@ -740,6 +698,8 @@ void ipoib_mcast_send(struct net_device *dev, u8 *daddr, struct sk_buff *skb) if (test_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags)) ipoib_dbg_mcast(priv, "no address vector, " "but multicast join already started\n"); + else if (test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)) + ipoib_mcast_sendonly_join(mcast); /* * If lookup completes between here and out:, don't @@ -799,12 +759,9 @@ void ipoib_mcast_dev_flush(struct net_device *dev) spin_unlock_irqrestore(&priv->lock, flags); - /* - * make sure the in-flight joins have finished before we attempt - * to leave - */ + /* seperate between the wait to the leave*/ list_for_each_entry_safe(mcast, tmcast, &remove_list, list) - if (test_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags)) + if (test_bit(IPOIB_MCAST_JOIN_STARTED, &mcast->flags)) wait_for_completion(&mcast->done); list_for_each_entry_safe(mcast, tmcast, &remove_list, list) { @@ -837,6 +794,8 @@ void ipoib_mcast_restart_task(struct work_struct *work) ipoib_dbg_mcast(priv, "restarting multicast task\n"); + ipoib_mcast_stop_thread(dev, 0); + local_irq_save(flags); netif_addr_lock(dev); spin_lock(&priv->lock); @@ -921,38 +880,14 @@ void ipoib_mcast_restart_task(struct work_struct *work) netif_addr_unlock(dev); local_irq_restore(flags); - /* - * make sure the in-flight joins have finished before we attempt - * to leave - */ - list_for_each_entry_safe(mcast, tmcast, &remove_list, list) - if (test_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags)) - wait_for_completion(&mcast->done); - - /* - * We have to cancel outside of the spinlock, but we have to - * take the rtnl lock or else we race with the removal of - * entries from the remove list in mcast_dev_flush as part - * of ipoib_stop(). We detect the drop of the ADMIN_UP flag - * to signal that we have hit this particular race, and we - * return since we know we don't need to do anything else - * anyway. - */ - while (!rtnl_trylock()) { - if (!test_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags)) - return; - else - msleep(20); - } + /* We have to cancel outside of the spinlock */ list_for_each_entry_safe(mcast, tmcast, &remove_list, list) { ipoib_mcast_leave(mcast->dev, mcast); ipoib_mcast_free(mcast); } - /* - * Restart our join task if needed - */ - ipoib_mcast_start_thread(dev); - rtnl_unlock(); + + if (test_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags)) + ipoib_mcast_start_thread(dev); } #ifdef CONFIG_INFINIBAND_IPOIB_DEBUG diff --git a/drivers/infiniband/ulp/ipoib/ipoib_verbs.c b/drivers/infiniband/ulp/ipoib/ipoib_verbs.c index b72a753eb41d..c56d5d44c53b 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_verbs.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_verbs.c @@ -145,20 +145,10 @@ int ipoib_transport_dev_init(struct net_device *dev, struct ib_device *ca) int ret, size; int i; - /* - * the various IPoIB tasks assume they will never race against - * themselves, so always use a single thread workqueue - */ - priv->wq = create_singlethread_workqueue("ipoib_wq"); - if (!priv->wq) { - printk(KERN_WARNING "ipoib: failed to allocate device WQ\n"); - return -ENODEV; - } - priv->pd = ib_alloc_pd(priv->ca); if (IS_ERR(priv->pd)) { printk(KERN_WARNING "%s: failed to allocate PD\n", ca->name); - goto out_free_wq; + return -ENODEV; } priv->mr = ib_get_dma_mr(priv->pd, IB_ACCESS_LOCAL_WRITE); @@ -252,10 +242,6 @@ out_free_mr: out_free_pd: ib_dealloc_pd(priv->pd); - -out_free_wq: - destroy_workqueue(priv->wq); - priv->wq = NULL; return -ENODEV; } @@ -284,12 +270,6 @@ void ipoib_transport_dev_cleanup(struct net_device *dev) if (ib_dealloc_pd(priv->pd)) ipoib_warn(priv, "ib_dealloc_pd failed\n"); - - if (priv->wq) { - flush_workqueue(priv->wq); - destroy_workqueue(priv->wq); - priv->wq = NULL; - } } void ipoib_event(struct ib_event_handler *handler, diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 18d4b2c8fe55..a18f41b89b6a 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -62,26 +62,6 @@ struct evdev_client { struct input_event buffer[]; }; -static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid) -{ - switch (clkid) { - - case CLOCK_REALTIME: - client->clk_type = EV_CLK_REAL; - break; - case CLOCK_MONOTONIC: - client->clk_type = EV_CLK_MONO; - break; - case CLOCK_BOOTTIME: - client->clk_type = EV_CLK_BOOT; - break; - default: - return -EINVAL; - } - - return 0; -} - /* flush queued events of type @type, caller must hold client->buffer_lock */ static void __evdev_flush_queue(struct evdev_client *client, unsigned int type) { @@ -128,10 +108,8 @@ static void __evdev_flush_queue(struct evdev_client *client, unsigned int type) client->head = head; } -/* queue SYN_DROPPED event */ -static void evdev_queue_syn_dropped(struct evdev_client *client) +static void __evdev_queue_syn_dropped(struct evdev_client *client) { - unsigned long flags; struct input_event ev; ktime_t time; @@ -146,8 +124,6 @@ static void evdev_queue_syn_dropped(struct evdev_client *client) ev.code = SYN_DROPPED; ev.value = 0; - spin_lock_irqsave(&client->buffer_lock, flags); - client->buffer[client->head++] = ev; client->head &= client->bufsize - 1; @@ -156,8 +132,53 @@ static void evdev_queue_syn_dropped(struct evdev_client *client) client->tail = (client->head - 1) & (client->bufsize - 1); client->packet_head = client->tail; } +} + +static void evdev_queue_syn_dropped(struct evdev_client *client) +{ + unsigned long flags; + + spin_lock_irqsave(&client->buffer_lock, flags); + __evdev_queue_syn_dropped(client); + spin_unlock_irqrestore(&client->buffer_lock, flags); +} + +static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid) +{ + unsigned long flags; + + if (client->clk_type == clkid) + return 0; + + switch (clkid) { + + case CLOCK_REALTIME: + client->clk_type = EV_CLK_REAL; + break; + case CLOCK_MONOTONIC: + client->clk_type = EV_CLK_MONO; + break; + case CLOCK_BOOTTIME: + client->clk_type = EV_CLK_BOOT; + break; + default: + return -EINVAL; + } + + /* + * Flush pending events and queue SYN_DROPPED event, + * but only if the queue is not empty. + */ + spin_lock_irqsave(&client->buffer_lock, flags); + + if (client->head != client->tail) { + client->packet_head = client->head = client->tail; + __evdev_queue_syn_dropped(client); + } spin_unlock_irqrestore(&client->buffer_lock, flags); + + return 0; } static void __pass_event(struct evdev_client *client, diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index fbe29fcb15c5..cb150a1dbaff 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -293,7 +293,7 @@ void input_mt_sync_frame(struct input_dev *dev) } EXPORT_SYMBOL(input_mt_sync_frame); -static int adjust_dual(int *begin, int step, int *end, int eq) +static int adjust_dual(int *begin, int step, int *end, int eq, int mu) { int f, *p, s, c; @@ -311,9 +311,10 @@ static int adjust_dual(int *begin, int step, int *end, int eq) s = *p; c = (f + s + 1) / 2; - if (c == 0 || (c > 0 && !eq)) + if (c == 0 || (c > mu && (!eq || mu > 0))) return 0; - if (s < 0) + /* Improve convergence for positive matrices by penalizing overcovers */ + if (s < 0 && mu <= 0) c *= 2; for (p = begin; p != end; p += step) @@ -322,23 +323,24 @@ static int adjust_dual(int *begin, int step, int *end, int eq) return (c < s && s <= 0) || (f >= 0 && f < c); } -static void find_reduced_matrix(int *w, int nr, int nc, int nrc) +static void find_reduced_matrix(int *w, int nr, int nc, int nrc, int mu) { int i, k, sum; for (k = 0; k < nrc; k++) { for (i = 0; i < nr; i++) - adjust_dual(w + i, nr, w + i + nrc, nr <= nc); + adjust_dual(w + i, nr, w + i + nrc, nr <= nc, mu); sum = 0; for (i = 0; i < nrc; i += nr) - sum += adjust_dual(w + i, 1, w + i + nr, nc <= nr); + sum += adjust_dual(w + i, 1, w + i + nr, nc <= nr, mu); if (!sum) break; } } static int input_mt_set_matrix(struct input_mt *mt, - const struct input_mt_pos *pos, int num_pos) + const struct input_mt_pos *pos, int num_pos, + int mu) { const struct input_mt_pos *p; struct input_mt_slot *s; @@ -352,7 +354,7 @@ static int input_mt_set_matrix(struct input_mt *mt, y = input_mt_get_value(s, ABS_MT_POSITION_Y); for (p = pos; p != pos + num_pos; p++) { int dx = x - p->x, dy = y - p->y; - *w++ = dx * dx + dy * dy; + *w++ = dx * dx + dy * dy - mu; } } @@ -393,17 +395,24 @@ static void input_mt_set_slots(struct input_mt *mt, * @slots: the slot assignment to be filled * @pos: the position array to match * @num_pos: number of positions + * @dmax: maximum ABS_MT_POSITION displacement (zero for infinite) * * Performs a best match against the current contacts and returns * the slot assignment list. New contacts are assigned to unused * slots. * + * The assignments are balanced so that all coordinate displacements are + * below the euclidian distance dmax. If no such assignment can be found, + * some contacts are assigned to unused slots. + * * Returns zero on success, or negative error in case of failure. */ int input_mt_assign_slots(struct input_dev *dev, int *slots, - const struct input_mt_pos *pos, int num_pos) + const struct input_mt_pos *pos, int num_pos, + int dmax) { struct input_mt *mt = dev->mt; + int mu = 2 * dmax * dmax; int nrc; if (!mt || !mt->red) @@ -413,8 +422,8 @@ int input_mt_assign_slots(struct input_dev *dev, int *slots, if (num_pos < 1) return 0; - nrc = input_mt_set_matrix(mt, pos, num_pos); - find_reduced_matrix(mt->red, num_pos, nrc / num_pos, nrc); + nrc = input_mt_set_matrix(mt, pos, num_pos, mu); + find_reduced_matrix(mt->red, num_pos, nrc / num_pos, nrc, mu); input_mt_set_slots(mt, slots, num_pos); return 0; diff --git a/drivers/input/input.c b/drivers/input/input.c index 213e3a1903ee..cc357f1516a7 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -100,23 +100,24 @@ static unsigned int input_to_handler(struct input_handle *handle, struct input_value *end = vals; struct input_value *v; - for (v = vals; v != vals + count; v++) { - if (handler->filter && - handler->filter(handle, v->type, v->code, v->value)) - continue; - if (end != v) - *end = *v; - end++; + if (handler->filter) { + for (v = vals; v != vals + count; v++) { + if (handler->filter(handle, v->type, v->code, v->value)) + continue; + if (end != v) + *end = *v; + end++; + } + count = end - vals; } - count = end - vals; if (!count) return 0; if (handler->events) handler->events(handle, vals, count); else if (handler->event) - for (v = vals; v != end; v++) + for (v = vals; v != vals + count; v++) handler->event(handle, v->type, v->code, v->value); return count; @@ -143,8 +144,11 @@ static void input_pass_values(struct input_dev *dev, count = input_to_handler(handle, vals, count); } else { list_for_each_entry_rcu(handle, &dev->h_list, d_node) - if (handle->open) + if (handle->open) { count = input_to_handler(handle, vals, count); + if (!count) + break; + } } rcu_read_unlock(); @@ -152,12 +156,14 @@ static void input_pass_values(struct input_dev *dev, add_input_randomness(vals->type, vals->code, vals->value); /* trigger auto repeat for key events */ - for (v = vals; v != vals + count; v++) { - if (v->type == EV_KEY && v->value != 2) { - if (v->value) - input_start_autorepeat(dev, v->code); - else - input_stop_autorepeat(dev); + if (test_bit(EV_REP, dev->evbit) && test_bit(EV_KEY, dev->evbit)) { + for (v = vals; v != vals + count; v++) { + if (v->type == EV_KEY && v->value != 2) { + if (v->value) + input_start_autorepeat(dev, v->code); + else + input_stop_autorepeat(dev); + } } } } diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index a5d9b3f3c871..a89ba7cb96f1 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -568,6 +568,16 @@ config KEYBOARD_STMPE To compile this driver as a module, choose M here: the module will be called stmpe-keypad. +config KEYBOARD_SUN4I_LRADC + tristate "Allwinner sun4i low res adc attached tablet keys support" + depends on ARCH_SUNXI + help + This selects support for the Allwinner low res adc attached tablet + keys found on Allwinner sunxi SoCs. + + To compile this driver as a module, choose M here: the + module will be called sun4i-lradc-keys. + config KEYBOARD_DAVINCI tristate "TI DaVinci Key Scan" depends on ARCH_DAVINCI_DM365 diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index febafa527eb6..470767884bd8 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o obj-$(CONFIG_KEYBOARD_ST_KEYSCAN) += st-keyscan.o +obj-$(CONFIG_KEYBOARD_SUN4I_LRADC) += sun4i-lradc-keys.o obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o diff --git a/drivers/input/keyboard/atakbd.c b/drivers/input/keyboard/atakbd.c index 10bcd4ae5402..f1235831283d 100644 --- a/drivers/input/keyboard/atakbd.c +++ b/drivers/input/keyboard/atakbd.c @@ -170,7 +170,7 @@ static unsigned char atakbd_keycode[0x72] = { /* American layout */ [93] = KEY_KPASTERISK, [94] = KEY_KPPLUS, [95] = KEY_HELP, - [96] = KEY_BACKSLASH, /* FIXME: '<' */ + [96] = KEY_102ND, [97] = KEY_KPASTERISK, /* FIXME */ [98] = KEY_KPSLASH, [99] = KEY_KPLEFTPAREN, diff --git a/drivers/input/keyboard/cap11xx.c b/drivers/input/keyboard/cap11xx.c index 4f59f0bab28f..f07461a64d85 100644 --- a/drivers/input/keyboard/cap11xx.c +++ b/drivers/input/keyboard/cap11xx.c @@ -370,7 +370,6 @@ static struct i2c_driver cap11xx_i2c_driver = { module_i2c_driver(cap11xx_i2c_driver); -MODULE_ALIAS("platform:cap11xx"); MODULE_DESCRIPTION("Microchip CAP11XX driver"); MODULE_AUTHOR("Daniel Mack <linux@zonque.org>"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index e53f232eda0e..2e855e6f3565 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c @@ -448,8 +448,7 @@ static int imx_keypad_probe(struct platform_device *pdev) return -ENOMEM; } - keypad = devm_kzalloc(&pdev->dev, sizeof(struct imx_keypad), - GFP_KERNEL); + keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL); if (!keypad) { dev_err(&pdev->dev, "not enough memory for driver data\n"); return -ENOMEM; diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c index a90d6bdc499e..a89488aa1aa4 100644 --- a/drivers/input/keyboard/pxa27x_keypad.c +++ b/drivers/input/keyboard/pxa27x_keypad.c @@ -20,6 +20,7 @@ #include <linux/module.h> #include <linux/interrupt.h> #include <linux/input.h> +#include <linux/io.h> #include <linux/device.h> #include <linux/platform_device.h> #include <linux/clk.h> @@ -28,10 +29,6 @@ #include <linux/slab.h> #include <linux/of.h> -#include <asm/mach/arch.h> -#include <asm/mach/map.h> - -#include <mach/hardware.h> #include <linux/platform_data/keypad-pxa27x.h> /* * Keypad Controller registers diff --git a/drivers/input/keyboard/sun4i-lradc-keys.c b/drivers/input/keyboard/sun4i-lradc-keys.c new file mode 100644 index 000000000000..cc8f7ddcee53 --- /dev/null +++ b/drivers/input/keyboard/sun4i-lradc-keys.c @@ -0,0 +1,286 @@ +/* + * Allwinner sun4i low res adc attached tablet keys driver + * + * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.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. + * + * 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. + */ + +/* + * Allwinnner sunxi SoCs have a lradc which is specifically designed to have + * various (tablet) keys (ie home, back, search, etc). attached to it using + * a resistor network. This driver is for the keys on such boards. + * + * There are 2 channels, currently this driver only supports channel 0 since + * there are no boards known to use channel 1. + */ + +#include <linux/err.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> + +#define LRADC_CTRL 0x00 +#define LRADC_INTC 0x04 +#define LRADC_INTS 0x08 +#define LRADC_DATA0 0x0c +#define LRADC_DATA1 0x10 + +/* LRADC_CTRL bits */ +#define FIRST_CONVERT_DLY(x) ((x) << 24) /* 8 bits */ +#define CHAN_SELECT(x) ((x) << 22) /* 2 bits */ +#define CONTINUE_TIME_SEL(x) ((x) << 16) /* 4 bits */ +#define KEY_MODE_SEL(x) ((x) << 12) /* 2 bits */ +#define LEVELA_B_CNT(x) ((x) << 8) /* 4 bits */ +#define HOLD_EN(x) ((x) << 6) +#define LEVELB_VOL(x) ((x) << 4) /* 2 bits */ +#define SAMPLE_RATE(x) ((x) << 2) /* 2 bits */ +#define ENABLE(x) ((x) << 0) + +/* LRADC_INTC and LRADC_INTS bits */ +#define CHAN1_KEYUP_IRQ BIT(12) +#define CHAN1_ALRDY_HOLD_IRQ BIT(11) +#define CHAN1_HOLD_IRQ BIT(10) +#define CHAN1_KEYDOWN_IRQ BIT(9) +#define CHAN1_DATA_IRQ BIT(8) +#define CHAN0_KEYUP_IRQ BIT(4) +#define CHAN0_ALRDY_HOLD_IRQ BIT(3) +#define CHAN0_HOLD_IRQ BIT(2) +#define CHAN0_KEYDOWN_IRQ BIT(1) +#define CHAN0_DATA_IRQ BIT(0) + +struct sun4i_lradc_keymap { + u32 voltage; + u32 keycode; +}; + +struct sun4i_lradc_data { + struct device *dev; + struct input_dev *input; + void __iomem *base; + struct regulator *vref_supply; + struct sun4i_lradc_keymap *chan0_map; + u32 chan0_map_count; + u32 chan0_keycode; + u32 vref; +}; + +static irqreturn_t sun4i_lradc_irq(int irq, void *dev_id) +{ + struct sun4i_lradc_data *lradc = dev_id; + u32 i, ints, val, voltage, diff, keycode = 0, closest = 0xffffffff; + + ints = readl(lradc->base + LRADC_INTS); + + /* + * lradc supports only one keypress at a time, release does not give + * any info as to which key was released, so we cache the keycode. + */ + + if (ints & CHAN0_KEYUP_IRQ) { + input_report_key(lradc->input, lradc->chan0_keycode, 0); + lradc->chan0_keycode = 0; + } + + if ((ints & CHAN0_KEYDOWN_IRQ) && lradc->chan0_keycode == 0) { + val = readl(lradc->base + LRADC_DATA0) & 0x3f; + voltage = val * lradc->vref / 63; + + for (i = 0; i < lradc->chan0_map_count; i++) { + diff = abs(lradc->chan0_map[i].voltage - voltage); + if (diff < closest) { + closest = diff; + keycode = lradc->chan0_map[i].keycode; + } + } + + lradc->chan0_keycode = keycode; + input_report_key(lradc->input, lradc->chan0_keycode, 1); + } + + input_sync(lradc->input); + + writel(ints, lradc->base + LRADC_INTS); + + return IRQ_HANDLED; +} + +static int sun4i_lradc_open(struct input_dev *dev) +{ + struct sun4i_lradc_data *lradc = input_get_drvdata(dev); + int error; + + error = regulator_enable(lradc->vref_supply); + if (error) + return error; + + /* lradc Vref internally is divided by 2/3 */ + lradc->vref = regulator_get_voltage(lradc->vref_supply) * 2 / 3; + + /* + * Set sample time to 4 ms / 250 Hz. Wait 2 * 4 ms for key to + * stabilize on press, wait (1 + 1) * 4 ms for key release + */ + writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) | + SAMPLE_RATE(0) | ENABLE(1), lradc->base + LRADC_CTRL); + + writel(CHAN0_KEYUP_IRQ | CHAN0_KEYDOWN_IRQ, lradc->base + LRADC_INTC); + + return 0; +} + +static void sun4i_lradc_close(struct input_dev *dev) +{ + struct sun4i_lradc_data *lradc = input_get_drvdata(dev); + + /* Disable lradc, leave other settings unchanged */ + writel(FIRST_CONVERT_DLY(2) | LEVELA_B_CNT(1) | HOLD_EN(1) | + SAMPLE_RATE(2), lradc->base + LRADC_CTRL); + writel(0, lradc->base + LRADC_INTC); + + regulator_disable(lradc->vref_supply); +} + +static int sun4i_lradc_load_dt_keymap(struct device *dev, + struct sun4i_lradc_data *lradc) +{ + struct device_node *np, *pp; + int i; + int error; + + np = dev->of_node; + if (!np) + return -EINVAL; + + lradc->chan0_map_count = of_get_child_count(np); + if (lradc->chan0_map_count == 0) { + dev_err(dev, "keymap is missing in device tree\n"); + return -EINVAL; + } + + lradc->chan0_map = devm_kmalloc_array(dev, lradc->chan0_map_count, + sizeof(struct sun4i_lradc_keymap), + GFP_KERNEL); + if (!lradc->chan0_map) + return -ENOMEM; + + i = 0; + for_each_child_of_node(np, pp) { + struct sun4i_lradc_keymap *map = &lradc->chan0_map[i]; + u32 channel; + + error = of_property_read_u32(pp, "channel", &channel); + if (error || channel != 0) { + dev_err(dev, "%s: Inval channel prop\n", pp->name); + return -EINVAL; + } + + error = of_property_read_u32(pp, "voltage", &map->voltage); + if (error) { + dev_err(dev, "%s: Inval voltage prop\n", pp->name); + return -EINVAL; + } + + error = of_property_read_u32(pp, "linux,code", &map->keycode); + if (error) { + dev_err(dev, "%s: Inval linux,code prop\n", pp->name); + return -EINVAL; + } + + i++; + } + + return 0; +} + +static int sun4i_lradc_probe(struct platform_device *pdev) +{ + struct sun4i_lradc_data *lradc; + struct device *dev = &pdev->dev; + int i; + int error; + + lradc = devm_kzalloc(dev, sizeof(struct sun4i_lradc_data), GFP_KERNEL); + if (!lradc) + return -ENOMEM; + + error = sun4i_lradc_load_dt_keymap(dev, lradc); + if (error) + return error; + + lradc->vref_supply = devm_regulator_get(dev, "vref"); + if (IS_ERR(lradc->vref_supply)) + return PTR_ERR(lradc->vref_supply); + + lradc->dev = dev; + lradc->input = devm_input_allocate_device(dev); + if (!lradc->input) + return -ENOMEM; + + lradc->input->name = pdev->name; + lradc->input->phys = "sun4i_lradc/input0"; + lradc->input->open = sun4i_lradc_open; + lradc->input->close = sun4i_lradc_close; + lradc->input->id.bustype = BUS_HOST; + lradc->input->id.vendor = 0x0001; + lradc->input->id.product = 0x0001; + lradc->input->id.version = 0x0100; + + __set_bit(EV_KEY, lradc->input->evbit); + for (i = 0; i < lradc->chan0_map_count; i++) + __set_bit(lradc->chan0_map[i].keycode, lradc->input->keybit); + + input_set_drvdata(lradc->input, lradc); + + lradc->base = devm_ioremap_resource(dev, + platform_get_resource(pdev, IORESOURCE_MEM, 0)); + if (IS_ERR(lradc->base)) + return PTR_ERR(lradc->base); + + error = devm_request_irq(dev, platform_get_irq(pdev, 0), + sun4i_lradc_irq, 0, + "sun4i-a10-lradc-keys", lradc); + if (error) + return error; + + error = input_register_device(lradc->input); + if (error) + return error; + + platform_set_drvdata(pdev, lradc); + return 0; +} + +static const struct of_device_id sun4i_lradc_of_match[] = { + { .compatible = "allwinner,sun4i-a10-lradc-keys", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, sun4i_lradc_of_match); + +static struct platform_driver sun4i_lradc_driver = { + .driver = { + .name = "sun4i-a10-lradc-keys", + .of_match_table = of_match_ptr(sun4i_lradc_of_match), + }, + .probe = sun4i_lradc_probe, +}; + +module_platform_driver(sun4i_lradc_driver); + +MODULE_DESCRIPTION("Allwinner sun4i low res adc attached tablet keys driver"); +MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 23297ab6163f..6deb8dae3205 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -93,6 +93,16 @@ config INPUT_BMA150 To compile this driver as a module, choose M here: the module will be called bma150. +config INPUT_E3X0_BUTTON + tristate "NI Ettus Research USRP E3x0 Button support." + default n + help + Say Y here to enable support for the NI Ettus Research + USRP E3x0 Button. + + To compile this driver as a module, choose M here: the + module will be called e3x0_button. + config INPUT_PCSPKR tristate "PC Speaker support" depends on PCSPKR_PLATFORM @@ -394,6 +404,18 @@ config INPUT_CM109 To compile this driver as a module, choose M here: the module will be called cm109. +config INPUT_REGULATOR_HAPTIC + tristate "Regulator haptics support" + depends on REGULATOR + select INPUT_FF_MEMLESS + help + This option enables device driver support for the haptic controlled + by a regulator. This driver supports ff-memless interface + from input framework. + + To compile this driver as a module, choose M here: the + module will be called regulator-haptic. + config INPUT_RETU_PWRBUTTON tristate "Retu Power button Driver" depends on MFD_RETU @@ -404,6 +426,27 @@ config INPUT_RETU_PWRBUTTON To compile this driver as a module, choose M here. The module will be called retu-pwrbutton. +config INPUT_TPS65218_PWRBUTTON + tristate "TPS65218 Power button driver" + depends on MFD_TPS65218 + help + Say Y here if you want to enable power buttong reporting for + the TPS65218 Power Management IC device. + + To compile this driver as a module, choose M here. The module will + be called tps65218-pwrbutton. + +config INPUT_AXP20X_PEK + tristate "X-Powers AXP20X power button driver" + depends on MFD_AXP20X + help + Say Y here if you want to enable power key reporting via the + AXP20X PMIC. + + To compile this driver as a module, choose M here. The module will + be called axp20x-pek. + + config INPUT_TWL4030_PWRBUTTON tristate "TWL4030 Power button Driver" depends on TWL4030_CORE diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 19c760361f80..403a1a54a76c 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o +obj-$(CONFIG_INPUT_E3X0_BUTTON) += e3x0-button.o obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o @@ -53,12 +54,15 @@ obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o obj-$(CONFIG_INPUT_POWERMATE) += powermate.o obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o +obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o +obj-$(CONFIG_INPUT_AXP20X_PEK) += axp20x-pek.o obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY) += soc_button_array.o obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o +obj-$(CONFIG_INPUT_TPS65218_PWRBUTTON) += tps65218-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o obj-$(CONFIG_INPUT_TWL6040_VIBRA) += twl6040-vibra.o diff --git a/drivers/input/misc/axp20x-pek.c b/drivers/input/misc/axp20x-pek.c new file mode 100644 index 000000000000..f1c844739cd7 --- /dev/null +++ b/drivers/input/misc/axp20x-pek.c @@ -0,0 +1,290 @@ +/* + * axp20x power button driver. + * + * Copyright (C) 2013 Carlo Caione <carlo@caione.org> + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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/errno.h> +#include <linux/irq.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/axp20x.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#define AXP20X_PEK_STARTUP_MASK (0xc0) +#define AXP20X_PEK_SHUTDOWN_MASK (0x03) + +struct axp20x_pek { + struct axp20x_dev *axp20x; + struct input_dev *input; + int irq_dbr; + int irq_dbf; +}; + +struct axp20x_time { + unsigned int time; + unsigned int idx; +}; + +static const struct axp20x_time startup_time[] = { + { .time = 128, .idx = 0 }, + { .time = 1000, .idx = 2 }, + { .time = 3000, .idx = 1 }, + { .time = 2000, .idx = 3 }, +}; + +static const struct axp20x_time shutdown_time[] = { + { .time = 4000, .idx = 0 }, + { .time = 6000, .idx = 1 }, + { .time = 8000, .idx = 2 }, + { .time = 10000, .idx = 3 }, +}; + +struct axp20x_pek_ext_attr { + const struct axp20x_time *p_time; + unsigned int mask; +}; + +static struct axp20x_pek_ext_attr axp20x_pek_startup_ext_attr = { + .p_time = startup_time, + .mask = AXP20X_PEK_STARTUP_MASK, +}; + +static struct axp20x_pek_ext_attr axp20x_pek_shutdown_ext_attr = { + .p_time = shutdown_time, + .mask = AXP20X_PEK_SHUTDOWN_MASK, +}; + +static struct axp20x_pek_ext_attr *get_axp_ext_attr(struct device_attribute *attr) +{ + return container_of(attr, struct dev_ext_attribute, attr)->var; +} + +static ssize_t axp20x_show_ext_attr(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); + struct axp20x_pek_ext_attr *axp20x_ea = get_axp_ext_attr(attr); + unsigned int val; + int ret, i; + + ret = regmap_read(axp20x_pek->axp20x->regmap, AXP20X_PEK_KEY, &val); + if (ret != 0) + return ret; + + val &= axp20x_ea->mask; + val >>= ffs(axp20x_ea->mask) - 1; + + for (i = 0; i < 4; i++) + if (val == axp20x_ea->p_time[i].idx) + val = axp20x_ea->p_time[i].time; + + return sprintf(buf, "%u\n", val); +} + +static ssize_t axp20x_store_ext_attr(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev); + struct axp20x_pek_ext_attr *axp20x_ea = get_axp_ext_attr(attr); + char val_str[20]; + size_t len; + int ret, i; + unsigned int val, idx = 0; + unsigned int best_err = UINT_MAX; + + val_str[sizeof(val_str) - 1] = '\0'; + strncpy(val_str, buf, sizeof(val_str) - 1); + len = strlen(val_str); + + if (len && val_str[len - 1] == '\n') + val_str[len - 1] = '\0'; + + ret = kstrtouint(val_str, 10, &val); + if (ret) + return ret; + + for (i = 3; i >= 0; i--) { + unsigned int err; + + err = abs(axp20x_ea->p_time[i].time - val); + if (err < best_err) { + best_err = err; + idx = axp20x_ea->p_time[i].idx; + } + + if (!err) + break; + } + + idx <<= ffs(axp20x_ea->mask) - 1; + ret = regmap_update_bits(axp20x_pek->axp20x->regmap, + AXP20X_PEK_KEY, + axp20x_ea->mask, idx); + if (ret != 0) + return -EINVAL; + + return count; +} + +static struct dev_ext_attribute axp20x_dev_attr_startup = { + .attr = __ATTR(startup, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr), + .var = &axp20x_pek_startup_ext_attr, +}; + +static struct dev_ext_attribute axp20x_dev_attr_shutdown = { + .attr = __ATTR(shutdown, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr), + .var = &axp20x_pek_shutdown_ext_attr, +}; + +static struct attribute *axp20x_attributes[] = { + &axp20x_dev_attr_startup.attr.attr, + &axp20x_dev_attr_shutdown.attr.attr, + NULL, +}; + +static const struct attribute_group axp20x_attribute_group = { + .attrs = axp20x_attributes, +}; + +static irqreturn_t axp20x_pek_irq(int irq, void *pwr) +{ + struct input_dev *idev = pwr; + struct axp20x_pek *axp20x_pek = input_get_drvdata(idev); + + if (irq == axp20x_pek->irq_dbr) + input_report_key(idev, KEY_POWER, true); + else if (irq == axp20x_pek->irq_dbf) + input_report_key(idev, KEY_POWER, false); + + input_sync(idev); + + return IRQ_HANDLED; +} + +static void axp20x_remove_sysfs_group(void *_data) +{ + struct device *dev = _data; + + sysfs_remove_group(&dev->kobj, &axp20x_attribute_group); +} + +static int axp20x_pek_probe(struct platform_device *pdev) +{ + struct axp20x_pek *axp20x_pek; + struct axp20x_dev *axp20x; + struct input_dev *idev; + int error; + + axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek), + GFP_KERNEL); + if (!axp20x_pek) + return -ENOMEM; + + axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent); + axp20x = axp20x_pek->axp20x; + + axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR"); + if (axp20x_pek->irq_dbr < 0) { + dev_err(&pdev->dev, "No IRQ for PEK_DBR, error=%d\n", + axp20x_pek->irq_dbr); + return axp20x_pek->irq_dbr; + } + axp20x_pek->irq_dbr = regmap_irq_get_virq(axp20x->regmap_irqc, + axp20x_pek->irq_dbr); + + axp20x_pek->irq_dbf = platform_get_irq_byname(pdev, "PEK_DBF"); + if (axp20x_pek->irq_dbf < 0) { + dev_err(&pdev->dev, "No IRQ for PEK_DBF, error=%d\n", + axp20x_pek->irq_dbf); + return axp20x_pek->irq_dbf; + } + axp20x_pek->irq_dbf = regmap_irq_get_virq(axp20x->regmap_irqc, + axp20x_pek->irq_dbf); + + axp20x_pek->input = devm_input_allocate_device(&pdev->dev); + if (!axp20x_pek->input) + return -ENOMEM; + + idev = axp20x_pek->input; + + idev->name = "axp20x-pek"; + idev->phys = "m1kbd/input2"; + idev->dev.parent = &pdev->dev; + + input_set_capability(idev, EV_KEY, KEY_POWER); + + input_set_drvdata(idev, axp20x_pek); + + error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbr, + axp20x_pek_irq, 0, + "axp20x-pek-dbr", idev); + if (error < 0) { + dev_err(axp20x->dev, "Failed to request dbr IRQ#%d: %d\n", + axp20x_pek->irq_dbr, error); + return error; + } + + error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbf, + axp20x_pek_irq, 0, + "axp20x-pek-dbf", idev); + if (error < 0) { + dev_err(axp20x->dev, "Failed to request dbf IRQ#%d: %d\n", + axp20x_pek->irq_dbf, error); + return error; + } + + error = sysfs_create_group(&pdev->dev.kobj, &axp20x_attribute_group); + if (error) { + dev_err(axp20x->dev, "Failed to create sysfs attributes: %d\n", + error); + return error; + } + + error = devm_add_action(&pdev->dev, + axp20x_remove_sysfs_group, &pdev->dev); + if (error) { + axp20x_remove_sysfs_group(&pdev->dev); + dev_err(&pdev->dev, "Failed to add sysfs cleanup action: %d\n", + error); + return error; + } + + error = input_register_device(idev); + if (error) { + dev_err(axp20x->dev, "Can't register input device: %d\n", + error); + return error; + } + + platform_set_drvdata(pdev, axp20x_pek); + + return 0; +} + +static struct platform_driver axp20x_pek_driver = { + .probe = axp20x_pek_probe, + .driver = { + .name = "axp20x-pek", + }, +}; +module_platform_driver(axp20x_pek_driver); + +MODULE_DESCRIPTION("axp20x Power Button"); +MODULE_AUTHOR("Carlo Caione <carlo@caione.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c index a364e109ca7c..599578042ea0 100644 --- a/drivers/input/misc/drv260x.c +++ b/drivers/input/misc/drv260x.c @@ -733,7 +733,6 @@ static struct i2c_driver drv260x_driver = { }; module_i2c_driver(drv260x_driver); -MODULE_ALIAS("platform:drv260x-haptics"); MODULE_DESCRIPTION("TI DRV260x haptics driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); diff --git a/drivers/input/misc/drv2667.c b/drivers/input/misc/drv2667.c index a021744e608c..fc0fddf0896a 100644 --- a/drivers/input/misc/drv2667.c +++ b/drivers/input/misc/drv2667.c @@ -492,7 +492,6 @@ static struct i2c_driver drv2667_driver = { }; module_i2c_driver(drv2667_driver); -MODULE_ALIAS("platform:drv2667-haptics"); MODULE_DESCRIPTION("TI DRV2667 haptics driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); diff --git a/drivers/input/misc/e3x0-button.c b/drivers/input/misc/e3x0-button.c new file mode 100644 index 000000000000..13bfca8a7b16 --- /dev/null +++ b/drivers/input/misc/e3x0-button.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2014, National Instruments Corp. All rights reserved. + * + * Driver for NI Ettus Research USRP E3x0 Button Driver + * + * 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 of the License. + * + * 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/device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/of.h> +#include <linux/slab.h> + +static irqreturn_t e3x0_button_release_handler(int irq, void *data) +{ + struct input_dev *idev = data; + + input_report_key(idev, KEY_POWER, 0); + input_sync(idev); + + return IRQ_HANDLED; +} + +static irqreturn_t e3x0_button_press_handler(int irq, void *data) +{ + struct input_dev *idev = data; + + input_report_key(idev, KEY_POWER, 1); + pm_wakeup_event(idev->dev.parent, 0); + input_sync(idev); + + return IRQ_HANDLED; +} + +static int __maybe_unused e3x0_button_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(platform_get_irq_byname(pdev, "press")); + + return 0; +} + +static int __maybe_unused e3x0_button_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(platform_get_irq_byname(pdev, "press")); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(e3x0_button_pm_ops, + e3x0_button_suspend, e3x0_button_resume); + +static int e3x0_button_probe(struct platform_device *pdev) +{ + struct input_dev *input; + int irq_press, irq_release; + int error; + + irq_press = platform_get_irq_byname(pdev, "press"); + if (irq_press < 0) { + dev_err(&pdev->dev, "No IRQ for 'press', error=%d\n", + irq_press); + return irq_press; + } + + irq_release = platform_get_irq_byname(pdev, "release"); + if (irq_release < 0) { + dev_err(&pdev->dev, "No IRQ for 'release', error=%d\n", + irq_release); + return irq_release; + } + + input = devm_input_allocate_device(&pdev->dev); + if (!input) + return -ENOMEM; + + input->name = "NI Ettus Research USRP E3x0 Button Driver"; + input->phys = "e3x0_button/input0"; + input->dev.parent = &pdev->dev; + + input_set_capability(input, EV_KEY, KEY_POWER); + + error = devm_request_irq(&pdev->dev, irq_press, + e3x0_button_press_handler, 0, + "e3x0-button", input); + if (error) { + dev_err(&pdev->dev, "Failed to request 'press' IRQ#%d: %d\n", + irq_press, error); + return error; + } + + error = devm_request_irq(&pdev->dev, irq_release, + e3x0_button_release_handler, 0, + "e3x0-button", input); + if (error) { + dev_err(&pdev->dev, "Failed to request 'release' IRQ#%d: %d\n", + irq_release, error); + return error; + } + + error = input_register_device(input); + if (error) { + dev_err(&pdev->dev, "Can't register input device: %d\n", error); + return error; + } + + platform_set_drvdata(pdev, input); + device_init_wakeup(&pdev->dev, 1); + return 0; +} + +static int e3x0_button_remove(struct platform_device *pdev) +{ + device_init_wakeup(&pdev->dev, 0); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id e3x0_button_match[] = { + { .compatible = "ettus,e3x0-button", }, + { } +}; +MODULE_DEVICE_TABLE(of, e3x0_button_match); +#endif + +static struct platform_driver e3x0_button_driver = { + .driver = { + .name = "e3x0-button", + .of_match_table = of_match_ptr(e3x0_button_match), + .pm = &e3x0_button_pm_ops, + }, + .probe = e3x0_button_probe, + .remove = e3x0_button_remove, +}; + +module_platform_driver(e3x0_button_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Moritz Fischer <moritz.fischer@ettus.com>"); +MODULE_DESCRIPTION("NI Ettus Research USRP E3x0 Button driver"); +MODULE_ALIAS("platform:e3x0-button"); diff --git a/drivers/input/misc/regulator-haptic.c b/drivers/input/misc/regulator-haptic.c new file mode 100644 index 000000000000..132eb914ea3e --- /dev/null +++ b/drivers/input/misc/regulator-haptic.c @@ -0,0 +1,266 @@ +/* + * Regulator haptic driver + * + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Author: Jaewon Kim <jaewon02.kim@samsung.com> + * Author: Hyunhee Kim <hyunhee.kim@samsung.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/input.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_data/regulator-haptic.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> + +#define MAX_MAGNITUDE_SHIFT 16 + +struct regulator_haptic { + struct device *dev; + struct input_dev *input_dev; + struct regulator *regulator; + + struct work_struct work; + struct mutex mutex; + + bool active; + bool suspended; + + unsigned int max_volt; + unsigned int min_volt; + unsigned int magnitude; +}; + +static int regulator_haptic_toggle(struct regulator_haptic *haptic, bool on) +{ + int error; + + if (haptic->active != on) { + + error = on ? regulator_enable(haptic->regulator) : + regulator_disable(haptic->regulator); + if (error) { + dev_err(haptic->dev, + "failed to switch regulator %s: %d\n", + on ? "on" : "off", error); + return error; + } + + haptic->active = on; + } + + return 0; +} + +static int regulator_haptic_set_voltage(struct regulator_haptic *haptic, + unsigned int magnitude) +{ + u64 volt_mag_multi; + unsigned int intensity; + int error; + + volt_mag_multi = (u64)(haptic->max_volt - haptic->min_volt) * magnitude; + intensity = (unsigned int)(volt_mag_multi >> MAX_MAGNITUDE_SHIFT); + + error = regulator_set_voltage(haptic->regulator, + intensity + haptic->min_volt, + haptic->max_volt); + if (error) { + dev_err(haptic->dev, "cannot set regulator voltage to %d: %d\n", + intensity + haptic->min_volt, error); + return error; + } + + regulator_haptic_toggle(haptic, !!magnitude); + + return 0; +} + +static void regulator_haptic_work(struct work_struct *work) +{ + struct regulator_haptic *haptic = container_of(work, + struct regulator_haptic, work); + + mutex_lock(&haptic->mutex); + + if (!haptic->suspended) + regulator_haptic_set_voltage(haptic, haptic->magnitude); + + mutex_unlock(&haptic->mutex); +} + +static int regulator_haptic_play_effect(struct input_dev *input, void *data, + struct ff_effect *effect) +{ + struct regulator_haptic *haptic = input_get_drvdata(input); + + haptic->magnitude = effect->u.rumble.strong_magnitude; + if (!haptic->magnitude) + haptic->magnitude = effect->u.rumble.weak_magnitude; + + schedule_work(&haptic->work); + + return 0; +} + +static void regulator_haptic_close(struct input_dev *input) +{ + struct regulator_haptic *haptic = input_get_drvdata(input); + + cancel_work_sync(&haptic->work); + regulator_haptic_set_voltage(haptic, 0); +} + +static int __maybe_unused +regulator_haptic_parse_dt(struct device *dev, struct regulator_haptic *haptic) +{ + struct device_node *node; + int error; + + node = dev->of_node; + if(!node) { + dev_err(dev, "Missing dveice tree data\n"); + return -EINVAL; + } + + error = of_property_read_u32(node, "max-microvolt", &haptic->max_volt); + if (error) { + dev_err(dev, "cannot parse max-microvolt\n"); + return error; + } + + error = of_property_read_u32(node, "min-microvolt", &haptic->min_volt); + if (error) { + dev_err(dev, "cannot parse min-microvolt\n"); + return error; + } + + return 0; +} + +static int regulator_haptic_probe(struct platform_device *pdev) +{ + const struct regulator_haptic_data *pdata = dev_get_platdata(&pdev->dev); + struct regulator_haptic *haptic; + struct input_dev *input_dev; + int error; + + haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL); + if (!haptic) + return -ENOMEM; + + platform_set_drvdata(pdev, haptic); + haptic->dev = &pdev->dev; + mutex_init(&haptic->mutex); + INIT_WORK(&haptic->work, regulator_haptic_work); + + if (pdata) { + haptic->max_volt = pdata->max_volt; + haptic->min_volt = pdata->min_volt; + } else if (IS_ENABLED(CONFIG_OF)) { + error = regulator_haptic_parse_dt(&pdev->dev, haptic); + if (error) + return error; + } else { + dev_err(&pdev->dev, "Missing platform data\n"); + return -EINVAL; + } + + haptic->regulator = devm_regulator_get_exclusive(&pdev->dev, "haptic"); + if (IS_ERR(haptic->regulator)) { + dev_err(&pdev->dev, "failed to get regulator\n"); + return PTR_ERR(haptic->regulator); + } + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) + return -ENOMEM; + + haptic->input_dev = input_dev; + haptic->input_dev->name = "regulator-haptic"; + haptic->input_dev->dev.parent = &pdev->dev; + haptic->input_dev->close = regulator_haptic_close; + input_set_drvdata(haptic->input_dev, haptic); + input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE); + + error = input_ff_create_memless(input_dev, NULL, + regulator_haptic_play_effect); + if (error) { + dev_err(&pdev->dev, "failed to create force-feedback\n"); + return error; + } + + error = input_register_device(haptic->input_dev); + if (error) { + dev_err(&pdev->dev, "failed to register input device\n"); + return error; + } + + return 0; +} + +static int __maybe_unused regulator_haptic_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct regulator_haptic *haptic = platform_get_drvdata(pdev); + int error; + + error = mutex_lock_interruptible(&haptic->mutex); + if (error) + return error; + + regulator_haptic_set_voltage(haptic, 0); + + haptic->suspended = true; + + mutex_unlock(&haptic->mutex); + + return 0; +} + +static int __maybe_unused regulator_haptic_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct regulator_haptic *haptic = platform_get_drvdata(pdev); + unsigned int magnitude; + + mutex_lock(&haptic->mutex); + + haptic->suspended = false; + + magnitude = ACCESS_ONCE(haptic->magnitude); + if (magnitude) + regulator_haptic_set_voltage(haptic, magnitude); + + mutex_unlock(&haptic->mutex); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(regulator_haptic_pm_ops, + regulator_haptic_suspend, regulator_haptic_resume); + +static struct of_device_id regulator_haptic_dt_match[] = { + { .compatible = "regulator-haptic" }, + { /* sentinel */ }, +}; + +static struct platform_driver regulator_haptic_driver = { + .probe = regulator_haptic_probe, + .driver = { + .name = "regulator-haptic", + .of_match_table = regulator_haptic_dt_match, + .pm = ®ulator_haptic_pm_ops, + }, +}; +module_platform_driver(regulator_haptic_driver); + +MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>"); +MODULE_AUTHOR("Hyunhee Kim <hyunhee.kim@samsung.com>"); +MODULE_DESCRIPTION("Regulator haptic driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/tps65218-pwrbutton.c b/drivers/input/misc/tps65218-pwrbutton.c new file mode 100644 index 000000000000..54508dec4eb3 --- /dev/null +++ b/drivers/input/misc/tps65218-pwrbutton.c @@ -0,0 +1,126 @@ +/* + * Texas Instruments' TPS65218 Power Button Input Driver + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ + * Author: Felipe Balbi <balbi@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. + * + * 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/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/mfd/tps65218.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +struct tps65218_pwrbutton { + struct device *dev; + struct tps65218 *tps; + struct input_dev *idev; +}; + +static irqreturn_t tps65218_pwr_irq(int irq, void *_pwr) +{ + struct tps65218_pwrbutton *pwr = _pwr; + unsigned int reg; + int error; + + error = tps65218_reg_read(pwr->tps, TPS65218_REG_STATUS, ®); + if (error) { + dev_err(pwr->dev, "can't read register: %d\n", error); + goto out; + } + + if (reg & TPS65218_STATUS_PB_STATE) { + input_report_key(pwr->idev, KEY_POWER, 1); + pm_wakeup_event(pwr->dev, 0); + } else { + input_report_key(pwr->idev, KEY_POWER, 0); + } + + input_sync(pwr->idev); + +out: + return IRQ_HANDLED; +} + +static int tps65218_pwron_probe(struct platform_device *pdev) +{ + struct tps65218 *tps = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct tps65218_pwrbutton *pwr; + struct input_dev *idev; + int error; + int irq; + + pwr = devm_kzalloc(dev, sizeof(*pwr), GFP_KERNEL); + if (!pwr) + return -ENOMEM; + + idev = devm_input_allocate_device(dev); + if (!idev) + return -ENOMEM; + + idev->name = "tps65218_pwrbutton"; + idev->phys = "tps65218_pwrbutton/input0"; + idev->dev.parent = dev; + idev->id.bustype = BUS_I2C; + + input_set_capability(idev, EV_KEY, KEY_POWER); + + pwr->tps = tps; + pwr->dev = dev; + pwr->idev = idev; + platform_set_drvdata(pdev, pwr); + device_init_wakeup(dev, true); + + irq = platform_get_irq(pdev, 0); + error = devm_request_threaded_irq(dev, irq, NULL, tps65218_pwr_irq, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + "tps65218-pwrbutton", pwr); + if (error) { + dev_err(dev, "failed to request IRQ #%d: %d\n", + irq, error); + return error; + } + + error= input_register_device(idev); + if (error) { + dev_err(dev, "Can't register power button: %d\n", error); + return error; + } + + return 0; +} + +static struct of_device_id of_tps65218_pwr_match[] = { + { .compatible = "ti,tps65218-pwrbutton" }, + { }, +}; +MODULE_DEVICE_TABLE(of, of_tps65218_pwr_match); + +static struct platform_driver tps65218_pwron_driver = { + .probe = tps65218_pwron_probe, + .driver = { + .name = "tps65218_pwrbutton", + .of_match_table = of_tps65218_pwr_match, + }, +}; +module_platform_driver(tps65218_pwron_driver); + +MODULE_DESCRIPTION("TPS65218 Power Button"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>"); diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index d8b46b0f2dbe..4658b5d41dd7 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -105,19 +105,12 @@ config MOUSE_PS2_ELANTECH Say Y here if you have an Elantech PS/2 touchpad connected to your system. - Note that if you enable this driver you will need an updated - X.org Synaptics driver that does not require ABS_PRESSURE - reports from the touchpad (i.e. post 1.5.0 version). You can - grab a patch for the driver here: - - http://userweb.kernel.org/~dtor/synaptics-no-abspressure.patch - - If unsure, say N. - This driver exposes some configuration registers via sysfs entries. For further information, see <file:Documentation/input/elantech.txt>. + If unsure, say N. + config MOUSE_PS2_SENTELIC bool "Sentelic Finger Sensing Pad PS/2 protocol extension" depends on MOUSE_PS2 @@ -146,6 +139,16 @@ config MOUSE_PS2_OLPC If unsure, say N. +config MOUSE_PS2_FOCALTECH + bool "FocalTech PS/2 mouse protocol extension" if EXPERT + default y + depends on MOUSE_PS2 + help + Say Y here if you have a FocalTech PS/2 TouchPad connected to + your system. + + If unsure, say Y. + config MOUSE_SERIAL tristate "Serial mouse" select SERIO @@ -206,6 +209,7 @@ config MOUSE_BCM5974 config MOUSE_CYAPA tristate "Cypress APA I2C Trackpad support" depends on I2C + select CRC_ITU_T help This driver adds support for Cypress All Points Addressable (APA) I2C Trackpads, including the ones used in 2012 Samsung Chromebooks. diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 560003dcac37..8a9c98e76d9c 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -8,7 +8,7 @@ obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o -obj-$(CONFIG_MOUSE_CYAPA) += cyapa.o +obj-$(CONFIG_MOUSE_CYAPA) += cyapatp.o obj-$(CONFIG_MOUSE_ELAN_I2C) += elan_i2c.o obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o obj-$(CONFIG_MOUSE_INPORT) += inport.o @@ -24,6 +24,7 @@ obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o obj-$(CONFIG_MOUSE_SYNAPTICS_USB) += synaptics_usb.o obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o +cyapatp-objs := cyapa.o cyapa_gen3.o cyapa_gen5.o psmouse-objs := psmouse-base.o synaptics.o focaltech.o psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index d88d73d83552..f205b8be2ce4 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -435,7 +435,7 @@ static void alps_report_mt_data(struct psmouse *psmouse, int n) struct alps_fields *f = &priv->f; int i, slot[MAX_TOUCHES]; - input_mt_assign_slots(dev, slot, f->mt, n); + input_mt_assign_slots(dev, slot, f->mt, n, 0); for (i = 0; i < n; i++) alps_set_slot(dev, slot[i], f->mt[i].x, f->mt[i].y); @@ -475,6 +475,13 @@ static void alps_process_trackstick_packet_v3(struct psmouse *psmouse) struct input_dev *dev = priv->dev2; int x, y, z, left, right, middle; + /* It should be a DualPoint when received trackstick packet */ + if (!(priv->flags & ALPS_DUALPOINT)) { + psmouse_warn(psmouse, + "Rejected trackstick packet from non DualPoint device"); + return; + } + /* Sanity check packet */ if (!(packet[0] & 0x40)) { psmouse_dbg(psmouse, "Bad trackstick packet, discarding\n"); @@ -699,7 +706,8 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse) alps_report_semi_mt_data(psmouse, fingers); - if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) { + if ((priv->flags & ALPS_DUALPOINT) && + !(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) { input_report_key(dev2, BTN_LEFT, f->ts_left); input_report_key(dev2, BTN_RIGHT, f->ts_right); input_report_key(dev2, BTN_MIDDLE, f->ts_middle); @@ -743,8 +751,11 @@ static void alps_process_packet_v6(struct psmouse *psmouse) */ if (packet[5] == 0x7F) { /* It should be a DualPoint when received Trackpoint packet */ - if (!(priv->flags & ALPS_DUALPOINT)) + if (!(priv->flags & ALPS_DUALPOINT)) { + psmouse_warn(psmouse, + "Rejected trackstick packet from non DualPoint device"); return; + } /* Trackpoint packet */ x = packet[1] | ((packet[3] & 0x20) << 2); @@ -1026,6 +1037,13 @@ static void alps_process_trackstick_packet_v7(struct psmouse *psmouse) struct input_dev *dev2 = priv->dev2; int x, y, z, left, right, middle; + /* It should be a DualPoint when received trackstick packet */ + if (!(priv->flags & ALPS_DUALPOINT)) { + psmouse_warn(psmouse, + "Rejected trackstick packet from non DualPoint device"); + return; + } + /* * b7 b6 b5 b4 b3 b2 b1 b0 * Byte0 0 1 0 0 1 0 0 0 @@ -2443,14 +2461,24 @@ int alps_init(struct psmouse *psmouse) dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE); } + if (priv->flags & ALPS_DUALPOINT) { + /* + * format of input device name is: "protocol vendor name" + * see function psmouse_switch_protocol() in psmouse-base.c + */ + dev2->name = "AlpsPS/2 ALPS DualPoint Stick"; + dev2->id.product = PSMOUSE_ALPS; + dev2->id.version = priv->proto_version; + } else { + dev2->name = "PS/2 ALPS Mouse"; + dev2->id.product = PSMOUSE_PS2; + dev2->id.version = 0x0000; + } + snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys); dev2->phys = priv->phys; - dev2->name = (priv->flags & ALPS_DUALPOINT) ? - "DualPoint Stick" : "ALPS PS/2 Device"; dev2->id.bustype = BUS_I8042; dev2->id.vendor = 0x0002; - dev2->id.product = PSMOUSE_ALPS; - dev2->id.version = 0x0000; dev2->dev.parent = &psmouse->ps2dev.serio->dev; dev2->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL); diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index c329cdb0b91a..b10709f04615 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -564,7 +564,7 @@ static int report_tp_state(struct bcm5974 *dev, int size) dev->index[n++] = &f[i]; } - input_mt_assign_slots(input, dev->slots, dev->pos, n); + input_mt_assign_slots(input, dev->slots, dev->pos, n, 0); for (i = 0; i < n; i++) report_finger_data(input, dev->slots[i], diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c index 1bece8cad46f..58f4f6fa4857 100644 --- a/drivers/input/mouse/cyapa.c +++ b/drivers/input/mouse/cyapa.c @@ -20,408 +20,131 @@ #include <linux/input/mt.h> #include <linux/interrupt.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/pm_runtime.h> +#include <linux/acpi.h> +#include "cyapa.h" -/* APA trackpad firmware generation */ -#define CYAPA_GEN3 0x03 /* support MT-protocol B with tracking ID. */ - -#define CYAPA_NAME "Cypress APA Trackpad (cyapa)" - -/* commands for read/write registers of Cypress trackpad */ -#define CYAPA_CMD_SOFT_RESET 0x00 -#define CYAPA_CMD_POWER_MODE 0x01 -#define CYAPA_CMD_DEV_STATUS 0x02 -#define CYAPA_CMD_GROUP_DATA 0x03 -#define CYAPA_CMD_GROUP_CMD 0x04 -#define CYAPA_CMD_GROUP_QUERY 0x05 -#define CYAPA_CMD_BL_STATUS 0x06 -#define CYAPA_CMD_BL_HEAD 0x07 -#define CYAPA_CMD_BL_CMD 0x08 -#define CYAPA_CMD_BL_DATA 0x09 -#define CYAPA_CMD_BL_ALL 0x0a -#define CYAPA_CMD_BLK_PRODUCT_ID 0x0b -#define CYAPA_CMD_BLK_HEAD 0x0c - -/* report data start reg offset address. */ -#define DATA_REG_START_OFFSET 0x0000 - -#define BL_HEAD_OFFSET 0x00 -#define BL_DATA_OFFSET 0x10 - -/* - * Operational Device Status Register - * - * bit 7: Valid interrupt source - * bit 6 - 4: Reserved - * bit 3 - 2: Power status - * bit 1 - 0: Device status - */ -#define REG_OP_STATUS 0x00 -#define OP_STATUS_SRC 0x80 -#define OP_STATUS_POWER 0x0c -#define OP_STATUS_DEV 0x03 -#define OP_STATUS_MASK (OP_STATUS_SRC | OP_STATUS_POWER | OP_STATUS_DEV) - -/* - * Operational Finger Count/Button Flags Register - * - * bit 7 - 4: Number of touched finger - * bit 3: Valid data - * bit 2: Middle Physical Button - * bit 1: Right Physical Button - * bit 0: Left physical Button - */ -#define REG_OP_DATA1 0x01 -#define OP_DATA_VALID 0x08 -#define OP_DATA_MIDDLE_BTN 0x04 -#define OP_DATA_RIGHT_BTN 0x02 -#define OP_DATA_LEFT_BTN 0x01 -#define OP_DATA_BTN_MASK (OP_DATA_MIDDLE_BTN | OP_DATA_RIGHT_BTN | \ - OP_DATA_LEFT_BTN) - -/* - * Bootloader Status Register - * - * bit 7: Busy - * bit 6 - 5: Reserved - * bit 4: Bootloader running - * bit 3 - 1: Reserved - * bit 0: Checksum valid - */ -#define REG_BL_STATUS 0x01 -#define BL_STATUS_BUSY 0x80 -#define BL_STATUS_RUNNING 0x10 -#define BL_STATUS_DATA_VALID 0x08 -#define BL_STATUS_CSUM_VALID 0x01 - -/* - * Bootloader Error Register - * - * bit 7: Invalid - * bit 6: Invalid security key - * bit 5: Bootloading - * bit 4: Command checksum - * bit 3: Flash protection error - * bit 2: Flash checksum error - * bit 1 - 0: Reserved - */ -#define REG_BL_ERROR 0x02 -#define BL_ERROR_INVALID 0x80 -#define BL_ERROR_INVALID_KEY 0x40 -#define BL_ERROR_BOOTLOADING 0x20 -#define BL_ERROR_CMD_CSUM 0x10 -#define BL_ERROR_FLASH_PROT 0x08 -#define BL_ERROR_FLASH_CSUM 0x04 - -#define BL_STATUS_SIZE 3 /* length of bootloader status registers */ -#define BLK_HEAD_BYTES 32 - -#define PRODUCT_ID_SIZE 16 -#define QUERY_DATA_SIZE 27 -#define REG_PROTOCOL_GEN_QUERY_OFFSET 20 - -#define REG_OFFSET_DATA_BASE 0x0000 -#define REG_OFFSET_COMMAND_BASE 0x0028 -#define REG_OFFSET_QUERY_BASE 0x002a - -#define CAPABILITY_LEFT_BTN_MASK (0x01 << 3) -#define CAPABILITY_RIGHT_BTN_MASK (0x01 << 4) -#define CAPABILITY_MIDDLE_BTN_MASK (0x01 << 5) -#define CAPABILITY_BTN_MASK (CAPABILITY_LEFT_BTN_MASK | \ - CAPABILITY_RIGHT_BTN_MASK | \ - CAPABILITY_MIDDLE_BTN_MASK) - -#define CYAPA_OFFSET_SOFT_RESET REG_OFFSET_COMMAND_BASE - -#define REG_OFFSET_POWER_MODE (REG_OFFSET_COMMAND_BASE + 1) - -#define PWR_MODE_MASK 0xfc -#define PWR_MODE_FULL_ACTIVE (0x3f << 2) -#define PWR_MODE_IDLE (0x05 << 2) /* default sleep time is 50 ms. */ -#define PWR_MODE_OFF (0x00 << 2) - -#define PWR_STATUS_MASK 0x0c -#define PWR_STATUS_ACTIVE (0x03 << 2) -#define PWR_STATUS_IDLE (0x02 << 2) -#define PWR_STATUS_OFF (0x00 << 2) - -/* - * CYAPA trackpad device states. - * Used in register 0x00, bit1-0, DeviceStatus field. - * Other values indicate device is in an abnormal state and must be reset. - */ -#define CYAPA_DEV_NORMAL 0x03 -#define CYAPA_DEV_BUSY 0x01 - -enum cyapa_state { - CYAPA_STATE_OP, - CYAPA_STATE_BL_IDLE, - CYAPA_STATE_BL_ACTIVE, - CYAPA_STATE_BL_BUSY, - CYAPA_STATE_NO_DEVICE, -}; - - -struct cyapa_touch { - /* - * high bits or x/y position value - * bit 7 - 4: high 4 bits of x position value - * bit 3 - 0: high 4 bits of y position value - */ - u8 xy_hi; - u8 x_lo; /* low 8 bits of x position value. */ - u8 y_lo; /* low 8 bits of y position value. */ - u8 pressure; - /* id range is 1 - 15. It is incremented with every new touch. */ - u8 id; -} __packed; - -/* The touch.id is used as the MT slot id, thus max MT slot is 15 */ -#define CYAPA_MAX_MT_SLOTS 15 - -struct cyapa_reg_data { - /* - * bit 0 - 1: device status - * bit 3 - 2: power mode - * bit 6 - 4: reserved - * bit 7: interrupt valid bit - */ - u8 device_status; - /* - * bit 7 - 4: number of fingers currently touching pad - * bit 3: valid data check bit - * bit 2: middle mechanism button state if exists - * bit 1: right mechanism button state if exists - * bit 0: left mechanism button state if exists - */ - u8 finger_btn; - /* CYAPA reports up to 5 touches per packet. */ - struct cyapa_touch touches[5]; -} __packed; - -/* The main device structure */ -struct cyapa { - enum cyapa_state state; - - struct i2c_client *client; - struct input_dev *input; - char phys[32]; /* device physical location */ - bool irq_wake; /* irq wake is enabled */ - bool smbus; - - /* read from query data region. */ - char product_id[16]; - u8 btn_capability; - u8 gen; - int max_abs_x; - int max_abs_y; - int physical_size_x; - int physical_size_y; -}; - -static const u8 bl_deactivate[] = { 0x00, 0xff, 0x3b, 0x00, 0x01, 0x02, 0x03, - 0x04, 0x05, 0x06, 0x07 }; -static const u8 bl_exit[] = { 0x00, 0xff, 0xa5, 0x00, 0x01, 0x02, 0x03, 0x04, - 0x05, 0x06, 0x07 }; - -struct cyapa_cmd_len { - u8 cmd; - u8 len; -}; #define CYAPA_ADAPTER_FUNC_NONE 0 #define CYAPA_ADAPTER_FUNC_I2C 1 #define CYAPA_ADAPTER_FUNC_SMBUS 2 #define CYAPA_ADAPTER_FUNC_BOTH 3 -/* - * macros for SMBus communication - */ -#define SMBUS_READ 0x01 -#define SMBUS_WRITE 0x00 -#define SMBUS_ENCODE_IDX(cmd, idx) ((cmd) | (((idx) & 0x03) << 1)) -#define SMBUS_ENCODE_RW(cmd, rw) ((cmd) | ((rw) & 0x01)) -#define SMBUS_BYTE_BLOCK_CMD_MASK 0x80 -#define SMBUS_GROUP_BLOCK_CMD_MASK 0x40 - - /* for byte read/write command */ -#define CMD_RESET 0 -#define CMD_POWER_MODE 1 -#define CMD_DEV_STATUS 2 -#define SMBUS_BYTE_CMD(cmd) (((cmd) & 0x3f) << 1) -#define CYAPA_SMBUS_RESET SMBUS_BYTE_CMD(CMD_RESET) -#define CYAPA_SMBUS_POWER_MODE SMBUS_BYTE_CMD(CMD_POWER_MODE) -#define CYAPA_SMBUS_DEV_STATUS SMBUS_BYTE_CMD(CMD_DEV_STATUS) - - /* for group registers read/write command */ -#define REG_GROUP_DATA 0 -#define REG_GROUP_CMD 2 -#define REG_GROUP_QUERY 3 -#define SMBUS_GROUP_CMD(grp) (0x80 | (((grp) & 0x07) << 3)) -#define CYAPA_SMBUS_GROUP_DATA SMBUS_GROUP_CMD(REG_GROUP_DATA) -#define CYAPA_SMBUS_GROUP_CMD SMBUS_GROUP_CMD(REG_GROUP_CMD) -#define CYAPA_SMBUS_GROUP_QUERY SMBUS_GROUP_CMD(REG_GROUP_QUERY) - - /* for register block read/write command */ -#define CMD_BL_STATUS 0 -#define CMD_BL_HEAD 1 -#define CMD_BL_CMD 2 -#define CMD_BL_DATA 3 -#define CMD_BL_ALL 4 -#define CMD_BLK_PRODUCT_ID 5 -#define CMD_BLK_HEAD 6 -#define SMBUS_BLOCK_CMD(cmd) (0xc0 | (((cmd) & 0x1f) << 1)) - -/* register block read/write command in bootloader mode */ -#define CYAPA_SMBUS_BL_STATUS SMBUS_BLOCK_CMD(CMD_BL_STATUS) -#define CYAPA_SMBUS_BL_HEAD SMBUS_BLOCK_CMD(CMD_BL_HEAD) -#define CYAPA_SMBUS_BL_CMD SMBUS_BLOCK_CMD(CMD_BL_CMD) -#define CYAPA_SMBUS_BL_DATA SMBUS_BLOCK_CMD(CMD_BL_DATA) -#define CYAPA_SMBUS_BL_ALL SMBUS_BLOCK_CMD(CMD_BL_ALL) - -/* register block read/write command in operational mode */ -#define CYAPA_SMBUS_BLK_PRODUCT_ID SMBUS_BLOCK_CMD(CMD_BLK_PRODUCT_ID) -#define CYAPA_SMBUS_BLK_HEAD SMBUS_BLOCK_CMD(CMD_BLK_HEAD) - -static const struct cyapa_cmd_len cyapa_i2c_cmds[] = { - { CYAPA_OFFSET_SOFT_RESET, 1 }, - { REG_OFFSET_COMMAND_BASE + 1, 1 }, - { REG_OFFSET_DATA_BASE, 1 }, - { REG_OFFSET_DATA_BASE, sizeof(struct cyapa_reg_data) }, - { REG_OFFSET_COMMAND_BASE, 0 }, - { REG_OFFSET_QUERY_BASE, QUERY_DATA_SIZE }, - { BL_HEAD_OFFSET, 3 }, - { BL_HEAD_OFFSET, 16 }, - { BL_HEAD_OFFSET, 16 }, - { BL_DATA_OFFSET, 16 }, - { BL_HEAD_OFFSET, 32 }, - { REG_OFFSET_QUERY_BASE, PRODUCT_ID_SIZE }, - { REG_OFFSET_DATA_BASE, 32 } -}; +#define CYAPA_FW_NAME "cyapa.bin" -static const struct cyapa_cmd_len cyapa_smbus_cmds[] = { - { CYAPA_SMBUS_RESET, 1 }, - { CYAPA_SMBUS_POWER_MODE, 1 }, - { CYAPA_SMBUS_DEV_STATUS, 1 }, - { CYAPA_SMBUS_GROUP_DATA, sizeof(struct cyapa_reg_data) }, - { CYAPA_SMBUS_GROUP_CMD, 2 }, - { CYAPA_SMBUS_GROUP_QUERY, QUERY_DATA_SIZE }, - { CYAPA_SMBUS_BL_STATUS, 3 }, - { CYAPA_SMBUS_BL_HEAD, 16 }, - { CYAPA_SMBUS_BL_CMD, 16 }, - { CYAPA_SMBUS_BL_DATA, 16 }, - { CYAPA_SMBUS_BL_ALL, 32 }, - { CYAPA_SMBUS_BLK_PRODUCT_ID, PRODUCT_ID_SIZE }, - { CYAPA_SMBUS_BLK_HEAD, 16 }, -}; +const char product_id[] = "CYTRA"; -static ssize_t cyapa_i2c_reg_read_block(struct cyapa *cyapa, u8 reg, size_t len, - u8 *values) +static int cyapa_reinitialize(struct cyapa *cyapa); + +static inline bool cyapa_is_bootloader_mode(struct cyapa *cyapa) { - return i2c_smbus_read_i2c_block_data(cyapa->client, reg, len, values); + if (cyapa->gen == CYAPA_GEN5 && cyapa->state == CYAPA_STATE_GEN5_BL) + return true; + + if (cyapa->gen == CYAPA_GEN3 && + cyapa->state >= CYAPA_STATE_BL_BUSY && + cyapa->state <= CYAPA_STATE_BL_ACTIVE) + return true; + + return false; } -static ssize_t cyapa_i2c_reg_write_block(struct cyapa *cyapa, u8 reg, - size_t len, const u8 *values) +static inline bool cyapa_is_operational_mode(struct cyapa *cyapa) { - return i2c_smbus_write_i2c_block_data(cyapa->client, reg, len, values); + if (cyapa->gen == CYAPA_GEN5 && cyapa->state == CYAPA_STATE_GEN5_APP) + return true; + + if (cyapa->gen == CYAPA_GEN3 && cyapa->state == CYAPA_STATE_OP) + return true; + + return false; } -/* - * cyapa_smbus_read_block - perform smbus block read command - * @cyapa - private data structure of the driver - * @cmd - the properly encoded smbus command - * @len - expected length of smbus command result - * @values - buffer to store smbus command result - * - * Returns negative errno, else the number of bytes written. - * - * Note: - * In trackpad device, the memory block allocated for I2C register map - * is 256 bytes, so the max read block for I2C bus is 256 bytes. - */ -static ssize_t cyapa_smbus_read_block(struct cyapa *cyapa, u8 cmd, size_t len, - u8 *values) +/* Returns 0 on success, else negative errno on failure. */ +static ssize_t cyapa_i2c_read(struct cyapa *cyapa, u8 reg, size_t len, + u8 *values) { - ssize_t ret; - u8 index; - u8 smbus_cmd; - u8 *buf; struct i2c_client *client = cyapa->client; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = ®, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = values, + }, + }; + int ret; - if (!(SMBUS_BYTE_BLOCK_CMD_MASK & cmd)) - return -EINVAL; - - if (SMBUS_GROUP_BLOCK_CMD_MASK & cmd) { - /* read specific block registers command. */ - smbus_cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ); - ret = i2c_smbus_read_block_data(client, smbus_cmd, values); - goto out; - } + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); - ret = 0; - for (index = 0; index * I2C_SMBUS_BLOCK_MAX < len; index++) { - smbus_cmd = SMBUS_ENCODE_IDX(cmd, index); - smbus_cmd = SMBUS_ENCODE_RW(smbus_cmd, SMBUS_READ); - buf = values + I2C_SMBUS_BLOCK_MAX * index; - ret = i2c_smbus_read_block_data(client, smbus_cmd, buf); - if (ret < 0) - goto out; - } + if (ret != ARRAY_SIZE(msgs)) + return ret < 0 ? ret : -EIO; -out: - return ret > 0 ? len : ret; + return 0; } -static s32 cyapa_read_byte(struct cyapa *cyapa, u8 cmd_idx) +/** + * cyapa_i2c_write - Execute i2c block data write operation + * @cyapa: Handle to this driver + * @ret: Offset of the data to written in the register map + * @len: number of bytes to write + * @values: Data to be written + * + * Return negative errno code on error; return zero when success. + */ +static int cyapa_i2c_write(struct cyapa *cyapa, u8 reg, + size_t len, const void *values) { - u8 cmd; + struct i2c_client *client = cyapa->client; + char buf[32]; + int ret; - if (cyapa->smbus) { - cmd = cyapa_smbus_cmds[cmd_idx].cmd; - cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ); - } else { - cmd = cyapa_i2c_cmds[cmd_idx].cmd; - } - return i2c_smbus_read_byte_data(cyapa->client, cmd); -} + if (len > sizeof(buf) - 1) + return -ENOMEM; -static s32 cyapa_write_byte(struct cyapa *cyapa, u8 cmd_idx, u8 value) -{ - u8 cmd; + buf[0] = reg; + memcpy(&buf[1], values, len); - if (cyapa->smbus) { - cmd = cyapa_smbus_cmds[cmd_idx].cmd; - cmd = SMBUS_ENCODE_RW(cmd, SMBUS_WRITE); - } else { - cmd = cyapa_i2c_cmds[cmd_idx].cmd; - } - return i2c_smbus_write_byte_data(cyapa->client, cmd, value); + ret = i2c_master_send(client, buf, len + 1); + if (ret != len + 1) + return ret < 0 ? ret : -EIO; + + return 0; } -static ssize_t cyapa_read_block(struct cyapa *cyapa, u8 cmd_idx, u8 *values) +static u8 cyapa_check_adapter_functionality(struct i2c_client *client) { - u8 cmd; - size_t len; + u8 ret = CYAPA_ADAPTER_FUNC_NONE; - if (cyapa->smbus) { - cmd = cyapa_smbus_cmds[cmd_idx].cmd; - len = cyapa_smbus_cmds[cmd_idx].len; - return cyapa_smbus_read_block(cyapa, cmd, len, values); - } else { - cmd = cyapa_i2c_cmds[cmd_idx].cmd; - len = cyapa_i2c_cmds[cmd_idx].len; - return cyapa_i2c_reg_read_block(cyapa, cmd, len, values); - } + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + ret |= CYAPA_ADAPTER_FUNC_I2C; + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) + ret |= CYAPA_ADAPTER_FUNC_SMBUS; + return ret; } /* * Query device for its current operating state. - * */ static int cyapa_get_state(struct cyapa *cyapa) { u8 status[BL_STATUS_SIZE]; + u8 cmd[32]; + /* The i2c address of gen4 and gen5 trackpad device must be even. */ + bool even_addr = ((cyapa->client->addr & 0x0001) == 0); + bool smbus = false; + int retries = 2; int error; cyapa->state = CYAPA_STATE_NO_DEVICE; @@ -433,39 +156,74 @@ static int cyapa_get_state(struct cyapa *cyapa) * */ error = cyapa_i2c_reg_read_block(cyapa, BL_HEAD_OFFSET, BL_STATUS_SIZE, - status); + status); /* * On smbus systems in OP mode, the i2c_reg_read will fail with * -ETIMEDOUT. In this case, try again using the smbus equivalent * command. This should return a BL_HEAD indicating CYAPA_STATE_OP. */ - if (cyapa->smbus && (error == -ETIMEDOUT || error == -ENXIO)) - error = cyapa_read_block(cyapa, CYAPA_CMD_BL_STATUS, status); + if (cyapa->smbus && (error == -ETIMEDOUT || error == -ENXIO)) { + if (!even_addr) + error = cyapa_read_block(cyapa, + CYAPA_CMD_BL_STATUS, status); + smbus = true; + } if (error != BL_STATUS_SIZE) goto error; - if ((status[REG_OP_STATUS] & OP_STATUS_SRC) == OP_STATUS_SRC) { - switch (status[REG_OP_STATUS] & OP_STATUS_DEV) { - case CYAPA_DEV_NORMAL: - case CYAPA_DEV_BUSY: - cyapa->state = CYAPA_STATE_OP; - break; - default: - error = -EAGAIN; - goto error; + /* + * Detect trackpad protocol based on characteristic registers and bits. + */ + do { + cyapa->status[REG_OP_STATUS] = status[REG_OP_STATUS]; + cyapa->status[REG_BL_STATUS] = status[REG_BL_STATUS]; + cyapa->status[REG_BL_ERROR] = status[REG_BL_ERROR]; + + if (cyapa->gen == CYAPA_GEN_UNKNOWN || + cyapa->gen == CYAPA_GEN3) { + error = cyapa_gen3_ops.state_parse(cyapa, + status, BL_STATUS_SIZE); + if (!error) + goto out_detected; } - } else { - if (status[REG_BL_STATUS] & BL_STATUS_BUSY) - cyapa->state = CYAPA_STATE_BL_BUSY; - else if (status[REG_BL_ERROR] & BL_ERROR_BOOTLOADING) - cyapa->state = CYAPA_STATE_BL_ACTIVE; - else - cyapa->state = CYAPA_STATE_BL_IDLE; - } + if ((cyapa->gen == CYAPA_GEN_UNKNOWN || + cyapa->gen == CYAPA_GEN5) && + !smbus && even_addr) { + error = cyapa_gen5_ops.state_parse(cyapa, + status, BL_STATUS_SIZE); + if (!error) + goto out_detected; + } + + /* + * Write 0x00 0x00 to trackpad device to force update its + * status, then redo the detection again. + */ + if (!smbus) { + cmd[0] = 0x00; + cmd[1] = 0x00; + error = cyapa_i2c_write(cyapa, 0, 2, cmd); + if (error) + goto error; + + msleep(50); + + error = cyapa_i2c_read(cyapa, BL_HEAD_OFFSET, + BL_STATUS_SIZE, status); + if (error) + goto error; + } + } while (--retries > 0 && !smbus); + + goto error; +out_detected: + if (cyapa->state <= CYAPA_STATE_BL_BUSY) + return -EAGAIN; return 0; + error: return (error < 0) ? error : -EAGAIN; } @@ -482,364 +240,939 @@ error: * Returns: * 0 when the device eventually responds with a valid non-busy state. * -ETIMEDOUT if device never responds (too many -EAGAIN) - * < 0 other errors + * -EAGAIN if bootload is busy, or unknown state. + * < 0 other errors */ -static int cyapa_poll_state(struct cyapa *cyapa, unsigned int timeout) +int cyapa_poll_state(struct cyapa *cyapa, unsigned int timeout) { int error; int tries = timeout / 100; - error = cyapa_get_state(cyapa); - while ((error || cyapa->state >= CYAPA_STATE_BL_BUSY) && tries--) { - msleep(100); + do { error = cyapa_get_state(cyapa); - } - return (error == -EAGAIN || error == -ETIMEDOUT) ? -ETIMEDOUT : error; -} + if (!error && cyapa->state > CYAPA_STATE_BL_BUSY) + return 0; -static int cyapa_bl_deactivate(struct cyapa *cyapa) -{ - int error; - - error = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_deactivate), - bl_deactivate); - if (error) - return error; + msleep(100); + } while (tries--); - /* wait for bootloader to switch to idle state; should take < 100ms */ - msleep(100); - error = cyapa_poll_state(cyapa, 500); - if (error) - return error; - if (cyapa->state != CYAPA_STATE_BL_IDLE) - return -EAGAIN; - return 0; + return (error == -EAGAIN || error == -ETIMEDOUT) ? -ETIMEDOUT : error; } /* - * Exit bootloader + * Check if device is operational. * - * Send bl_exit command, then wait 50 - 100 ms to let device transition to - * operational mode. If this is the first time the device's firmware is - * running, it can take up to 2 seconds to calibrate its sensors. So, poll - * the device's new state for up to 2 seconds. + * An operational device is responding, has exited bootloader, and has + * firmware supported by this driver. * * Returns: + * -ENODEV no device + * -EBUSY no device or in bootloader * -EIO failure while reading from device - * -EAGAIN device is stuck in bootloader, b/c it has invalid firmware - * 0 device is supported and in operational mode + * -ETIMEDOUT timeout failure for bus idle or bus no response + * -EAGAIN device is still in bootloader + * if ->state = CYAPA_STATE_BL_IDLE, device has invalid firmware + * -EINVAL device is in operational mode, but not supported by this driver + * 0 device is supported */ -static int cyapa_bl_exit(struct cyapa *cyapa) +static int cyapa_check_is_operational(struct cyapa *cyapa) { int error; - error = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_exit), bl_exit); + error = cyapa_poll_state(cyapa, 4000); if (error) return error; - /* - * Wait for bootloader to exit, and operation mode to start. - * Normally, this takes at least 50 ms. - */ - usleep_range(50000, 100000); - /* - * In addition, when a device boots for the first time after being - * updated to new firmware, it must first calibrate its sensors, which - * can take up to an additional 2 seconds. - */ - error = cyapa_poll_state(cyapa, 2000); - if (error < 0) - return error; - if (cyapa->state != CYAPA_STATE_OP) - return -EAGAIN; + switch (cyapa->gen) { + case CYAPA_GEN5: + cyapa->ops = &cyapa_gen5_ops; + break; + case CYAPA_GEN3: + cyapa->ops = &cyapa_gen3_ops; + break; + default: + return -ENODEV; + } - return 0; + error = cyapa->ops->operational_check(cyapa); + if (!error && cyapa_is_operational_mode(cyapa)) + cyapa->operational = true; + else + cyapa->operational = false; + + return error; } + /* - * Set device power mode - * + * Returns 0 on device detected, negative errno on no device detected. + * And when the device is detected and opertaional, it will be reset to + * full power active mode automatically. */ -static int cyapa_set_power_mode(struct cyapa *cyapa, u8 power_mode) +static int cyapa_detect(struct cyapa *cyapa) { struct device *dev = &cyapa->client->dev; - int ret; - u8 power; - - if (cyapa->state != CYAPA_STATE_OP) - return 0; + int error; - ret = cyapa_read_byte(cyapa, CYAPA_CMD_POWER_MODE); - if (ret < 0) - return ret; + error = cyapa_check_is_operational(cyapa); + if (error) { + if (error != -ETIMEDOUT && error != -ENODEV && + cyapa_is_bootloader_mode(cyapa)) { + dev_warn(dev, "device detected but not operational\n"); + return 0; + } - power = ret & ~PWR_MODE_MASK; - power |= power_mode & PWR_MODE_MASK; - ret = cyapa_write_byte(cyapa, CYAPA_CMD_POWER_MODE, power); - if (ret < 0) { - dev_err(dev, "failed to set power_mode 0x%02x err = %d\n", - power_mode, ret); - return ret; + dev_err(dev, "no device detected: %d\n", error); + return error; } return 0; } -static int cyapa_get_query_data(struct cyapa *cyapa) +static int cyapa_open(struct input_dev *input) { - u8 query_data[QUERY_DATA_SIZE]; - int ret; + struct cyapa *cyapa = input_get_drvdata(input); + struct i2c_client *client = cyapa->client; + int error; - if (cyapa->state != CYAPA_STATE_OP) - return -EBUSY; + error = mutex_lock_interruptible(&cyapa->state_sync_lock); + if (error) + return error; - ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_QUERY, query_data); - if (ret < 0) - return ret; - if (ret != QUERY_DATA_SIZE) - return -EIO; + if (cyapa->operational) { + /* + * though failed to set active power mode, + * but still may be able to work in lower scan rate + * when in operational mode. + */ + error = cyapa->ops->set_power_mode(cyapa, + PWR_MODE_FULL_ACTIVE, 0); + if (error) { + dev_warn(&client->dev, + "set active power failed: %d\n", error); + goto out; + } + } else { + error = cyapa_reinitialize(cyapa); + if (error || !cyapa->operational) { + error = error ? error : -EAGAIN; + goto out; + } + } + + enable_irq(client->irq); + if (!pm_runtime_enabled(&client->dev)) { + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + } +out: + mutex_unlock(&cyapa->state_sync_lock); + return error; +} + +static void cyapa_close(struct input_dev *input) +{ + struct cyapa *cyapa = input_get_drvdata(input); + struct i2c_client *client = cyapa->client; + + mutex_lock(&cyapa->state_sync_lock); + + disable_irq(client->irq); + if (pm_runtime_enabled(&client->dev)) + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + + if (cyapa->operational) + cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0); - memcpy(&cyapa->product_id[0], &query_data[0], 5); - cyapa->product_id[5] = '-'; - memcpy(&cyapa->product_id[6], &query_data[5], 6); - cyapa->product_id[12] = '-'; - memcpy(&cyapa->product_id[13], &query_data[11], 2); - cyapa->product_id[15] = '\0'; + mutex_unlock(&cyapa->state_sync_lock); +} - cyapa->btn_capability = query_data[19] & CAPABILITY_BTN_MASK; +static int cyapa_create_input_dev(struct cyapa *cyapa) +{ + struct device *dev = &cyapa->client->dev; + struct input_dev *input; + int error; - cyapa->gen = query_data[20] & 0x0f; + if (!cyapa->physical_size_x || !cyapa->physical_size_y) + return -EINVAL; - cyapa->max_abs_x = ((query_data[21] & 0xf0) << 4) | query_data[22]; - cyapa->max_abs_y = ((query_data[21] & 0x0f) << 8) | query_data[23]; + input = devm_input_allocate_device(dev); + if (!input) { + dev_err(dev, "failed to allocate memory for input device.\n"); + return -ENOMEM; + } + + input->name = CYAPA_NAME; + input->phys = cyapa->phys; + input->id.bustype = BUS_I2C; + input->id.version = 1; + input->id.product = 0; /* Means any product in eventcomm. */ + input->dev.parent = &cyapa->client->dev; + + input->open = cyapa_open; + input->close = cyapa_close; + + input_set_drvdata(input, cyapa); + + __set_bit(EV_ABS, input->evbit); + + /* Finger position */ + input_set_abs_params(input, ABS_MT_POSITION_X, 0, cyapa->max_abs_x, 0, + 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cyapa->max_abs_y, 0, + 0); + input_set_abs_params(input, ABS_MT_PRESSURE, 0, cyapa->max_z, 0, 0); + if (cyapa->gen > CYAPA_GEN3) { + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0); + /* + * Orientation is the angle between the vertical axis and + * the major axis of the contact ellipse. + * The range is -127 to 127. + * the positive direction is clockwise form the vertical axis. + * If the ellipse of contact degenerates into a circle, + * orientation is reported as 0. + * + * Also, for Gen5 trackpad the accurate of this orientation + * value is value + (-30 ~ 30). + */ + input_set_abs_params(input, ABS_MT_ORIENTATION, + -127, 127, 0, 0); + } + if (cyapa->gen >= CYAPA_GEN5) { + input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(input, ABS_MT_WIDTH_MINOR, 0, 255, 0, 0); + } + + input_abs_set_res(input, ABS_MT_POSITION_X, + cyapa->max_abs_x / cyapa->physical_size_x); + input_abs_set_res(input, ABS_MT_POSITION_Y, + cyapa->max_abs_y / cyapa->physical_size_y); + + if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK) + __set_bit(BTN_LEFT, input->keybit); + if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK) + __set_bit(BTN_MIDDLE, input->keybit); + if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK) + __set_bit(BTN_RIGHT, input->keybit); + + if (cyapa->btn_capability == CAPABILITY_LEFT_BTN_MASK) + __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); + + /* Handle pointer emulation and unused slots in core */ + error = input_mt_init_slots(input, CYAPA_MAX_MT_SLOTS, + INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED); + if (error) { + dev_err(dev, "failed to initialize MT slots: %d\n", error); + return error; + } - cyapa->physical_size_x = - ((query_data[24] & 0xf0) << 4) | query_data[25]; - cyapa->physical_size_y = - ((query_data[24] & 0x0f) << 8) | query_data[26]; + /* Register the device in input subsystem */ + error = input_register_device(input); + if (error) { + dev_err(dev, "failed to register input device: %d\n", error); + return error; + } + cyapa->input = input; return 0; } +static void cyapa_enable_irq_for_cmd(struct cyapa *cyapa) +{ + struct input_dev *input = cyapa->input; + + if (!input || !input->users) { + /* + * When input is NULL, TP must be in deep sleep mode. + * In this mode, later non-power I2C command will always failed + * if not bring it out of deep sleep mode firstly, + * so must command TP to active mode here. + */ + if (!input || cyapa->operational) + cyapa->ops->set_power_mode(cyapa, + PWR_MODE_FULL_ACTIVE, 0); + /* Gen3 always using polling mode for command. */ + if (cyapa->gen >= CYAPA_GEN5) + enable_irq(cyapa->client->irq); + } +} + +static void cyapa_disable_irq_for_cmd(struct cyapa *cyapa) +{ + struct input_dev *input = cyapa->input; + + if (!input || !input->users) { + if (cyapa->gen >= CYAPA_GEN5) + disable_irq(cyapa->client->irq); + if (!input || cyapa->operational) + cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0); + } +} + /* - * Check if device is operational. + * cyapa_sleep_time_to_pwr_cmd and cyapa_pwr_cmd_to_sleep_time * - * An operational device is responding, has exited bootloader, and has - * firmware supported by this driver. + * These are helper functions that convert to and from integer idle + * times and register settings to write to the PowerMode register. + * The trackpad supports between 20ms to 1000ms scan intervals. + * The time will be increased in increments of 10ms from 20ms to 100ms. + * From 100ms to 1000ms, time will be increased in increments of 20ms. * - * Returns: - * -EBUSY no device or in bootloader - * -EIO failure while reading from device - * -EAGAIN device is still in bootloader - * if ->state = CYAPA_STATE_BL_IDLE, device has invalid firmware - * -EINVAL device is in operational mode, but not supported by this driver - * 0 device is supported + * When Idle_Time < 100, the format to convert Idle_Time to Idle_Command is: + * Idle_Command = Idle Time / 10; + * When Idle_Time >= 100, the format to convert Idle_Time to Idle_Command is: + * Idle_Command = Idle Time / 20 + 5; */ -static int cyapa_check_is_operational(struct cyapa *cyapa) +u8 cyapa_sleep_time_to_pwr_cmd(u16 sleep_time) { - struct device *dev = &cyapa->client->dev; - static const char unique_str[] = "CYTRA"; - int error; + u16 encoded_time; - error = cyapa_poll_state(cyapa, 2000); + sleep_time = clamp_val(sleep_time, 20, 1000); + encoded_time = sleep_time < 100 ? sleep_time / 10 : sleep_time / 20 + 5; + return (encoded_time << 2) & PWR_MODE_MASK; +} + +u16 cyapa_pwr_cmd_to_sleep_time(u8 pwr_mode) +{ + u8 encoded_time = pwr_mode >> 2; + + return (encoded_time < 10) ? encoded_time * 10 + : (encoded_time - 5) * 20; +} + +/* 0 on driver initialize and detected successfully, negative on failure. */ +static int cyapa_initialize(struct cyapa *cyapa) +{ + int error = 0; + + cyapa->state = CYAPA_STATE_NO_DEVICE; + cyapa->gen = CYAPA_GEN_UNKNOWN; + mutex_init(&cyapa->state_sync_lock); + + /* + * Set to hard code default, they will be updated with trackpad set + * default values after probe and initialized. + */ + cyapa->suspend_power_mode = PWR_MODE_SLEEP; + cyapa->suspend_sleep_time = + cyapa_pwr_cmd_to_sleep_time(cyapa->suspend_power_mode); + + /* ops.initialize() is aimed to prepare for module communications. */ + error = cyapa_gen3_ops.initialize(cyapa); + if (!error) + error = cyapa_gen5_ops.initialize(cyapa); if (error) return error; - switch (cyapa->state) { - case CYAPA_STATE_BL_ACTIVE: - error = cyapa_bl_deactivate(cyapa); - if (error) - return error; - /* Fallthrough state */ - case CYAPA_STATE_BL_IDLE: - error = cyapa_bl_exit(cyapa); - if (error) - return error; + error = cyapa_detect(cyapa); + if (error) + return error; - /* Fallthrough state */ - case CYAPA_STATE_OP: - error = cyapa_get_query_data(cyapa); - if (error) - return error; + /* Power down the device until we need it. */ + if (cyapa->operational) + cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0); - /* only support firmware protocol gen3 */ - if (cyapa->gen != CYAPA_GEN3) { - dev_err(dev, "unsupported protocol version (%d)", - cyapa->gen); - return -EINVAL; - } + return 0; +} + +static int cyapa_reinitialize(struct cyapa *cyapa) +{ + struct device *dev = &cyapa->client->dev; + struct input_dev *input = cyapa->input; + int error; + + if (pm_runtime_enabled(dev)) + pm_runtime_disable(dev); - /* only support product ID starting with CYTRA */ - if (memcmp(cyapa->product_id, unique_str, - sizeof(unique_str) - 1) != 0) { - dev_err(dev, "unsupported product ID (%s)\n", - cyapa->product_id); - return -EINVAL; + /* Avoid command failures when TP was in OFF state. */ + if (cyapa->operational) + cyapa->ops->set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE, 0); + + error = cyapa_detect(cyapa); + if (error) + goto out; + + if (!input && cyapa->operational) { + error = cyapa_create_input_dev(cyapa); + if (error) { + dev_err(dev, "create input_dev instance failed: %d\n", + error); + goto out; } - return 0; + } - default: - return -EIO; +out: + if (!input || !input->users) { + /* Reset to power OFF state to save power when no user open. */ + if (cyapa->operational) + cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0); + } else if (!error && cyapa->operational) { + /* + * Make sure only enable runtime PM when device is + * in operational mode and input->users > 0. + */ + pm_runtime_set_active(dev); + pm_runtime_enable(dev); } - return 0; + + return error; } static irqreturn_t cyapa_irq(int irq, void *dev_id) { struct cyapa *cyapa = dev_id; struct device *dev = &cyapa->client->dev; - struct input_dev *input = cyapa->input; - struct cyapa_reg_data data; - int i; - int ret; - int num_fingers; + pm_runtime_get_sync(dev); if (device_may_wakeup(dev)) pm_wakeup_event(dev, 0); - ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data); - if (ret != sizeof(data)) - goto out; + /* Interrupt event maybe cuased by host command to trackpad device. */ + if (cyapa->ops->irq_cmd_handler(cyapa)) { + /* + * Interrupt event maybe from trackpad device input reporting. + */ + if (!cyapa->input) { + /* + * Still in probling or in firware image + * udpating or reading. + */ + cyapa->ops->sort_empty_output_data(cyapa, + NULL, NULL, NULL); + goto out; + } - if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC || - (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL || - (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) { - goto out; + if (!cyapa->operational || cyapa->ops->irq_handler(cyapa)) { + if (!mutex_trylock(&cyapa->state_sync_lock)) { + cyapa->ops->sort_empty_output_data(cyapa, + NULL, NULL, NULL); + goto out; + } + cyapa_reinitialize(cyapa); + mutex_unlock(&cyapa->state_sync_lock); + } } - num_fingers = (data.finger_btn >> 4) & 0x0f; - for (i = 0; i < num_fingers; i++) { - const struct cyapa_touch *touch = &data.touches[i]; - /* Note: touch->id range is 1 to 15; slots are 0 to 14. */ - int slot = touch->id - 1; +out: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_sync_autosuspend(dev); + return IRQ_HANDLED; +} + +/* + ************************************************************** + * sysfs interface + ************************************************************** +*/ +#ifdef CONFIG_PM_SLEEP +static ssize_t cyapa_show_suspend_scanrate(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + u8 pwr_cmd = cyapa->suspend_power_mode; + u16 sleep_time; + int len; + int error; + + error = mutex_lock_interruptible(&cyapa->state_sync_lock); + if (error) + return error; + + pwr_cmd = cyapa->suspend_power_mode; + sleep_time = cyapa->suspend_sleep_time; + + mutex_unlock(&cyapa->state_sync_lock); + + switch (pwr_cmd) { + case PWR_MODE_BTN_ONLY: + len = scnprintf(buf, PAGE_SIZE, "%s\n", BTN_ONLY_MODE_NAME); + break; - input_mt_slot(input, slot); - input_mt_report_slot_state(input, MT_TOOL_FINGER, true); - input_report_abs(input, ABS_MT_POSITION_X, - ((touch->xy_hi & 0xf0) << 4) | touch->x_lo); - input_report_abs(input, ABS_MT_POSITION_Y, - ((touch->xy_hi & 0x0f) << 8) | touch->y_lo); - input_report_abs(input, ABS_MT_PRESSURE, touch->pressure); + case PWR_MODE_OFF: + len = scnprintf(buf, PAGE_SIZE, "%s\n", OFF_MODE_NAME); + break; + + default: + len = scnprintf(buf, PAGE_SIZE, "%u\n", + cyapa->gen == CYAPA_GEN3 ? + cyapa_pwr_cmd_to_sleep_time(pwr_cmd) : + sleep_time); + break; } - input_mt_sync_frame(input); + return len; +} - if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK) - input_report_key(input, BTN_LEFT, - data.finger_btn & OP_DATA_LEFT_BTN); +static ssize_t cyapa_update_suspend_scanrate(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + u16 sleep_time; + int error; - if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK) - input_report_key(input, BTN_MIDDLE, - data.finger_btn & OP_DATA_MIDDLE_BTN); + error = mutex_lock_interruptible(&cyapa->state_sync_lock); + if (error) + return error; - if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK) - input_report_key(input, BTN_RIGHT, - data.finger_btn & OP_DATA_RIGHT_BTN); + if (sysfs_streq(buf, BTN_ONLY_MODE_NAME)) { + cyapa->suspend_power_mode = PWR_MODE_BTN_ONLY; + } else if (sysfs_streq(buf, OFF_MODE_NAME)) { + cyapa->suspend_power_mode = PWR_MODE_OFF; + } else if (!kstrtou16(buf, 10, &sleep_time)) { + cyapa->suspend_sleep_time = max_t(u16, sleep_time, 1000); + cyapa->suspend_power_mode = + cyapa_sleep_time_to_pwr_cmd(cyapa->suspend_sleep_time); + } else { + count = -EINVAL; + } - input_sync(input); + mutex_unlock(&cyapa->state_sync_lock); -out: - return IRQ_HANDLED; + return count; } -static u8 cyapa_check_adapter_functionality(struct i2c_client *client) +static DEVICE_ATTR(suspend_scanrate_ms, S_IRUGO|S_IWUSR, + cyapa_show_suspend_scanrate, + cyapa_update_suspend_scanrate); + +static struct attribute *cyapa_power_wakeup_entries[] = { + &dev_attr_suspend_scanrate_ms.attr, + NULL, +}; + +static const struct attribute_group cyapa_power_wakeup_group = { + .name = power_group_name, + .attrs = cyapa_power_wakeup_entries, +}; + +static void cyapa_remove_power_wakeup_group(void *data) { - u8 ret = CYAPA_ADAPTER_FUNC_NONE; + struct cyapa *cyapa = data; - if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) - ret |= CYAPA_ADAPTER_FUNC_I2C; - if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_SMBUS_BLOCK_DATA | - I2C_FUNC_SMBUS_I2C_BLOCK)) - ret |= CYAPA_ADAPTER_FUNC_SMBUS; - return ret; + sysfs_unmerge_group(&cyapa->client->dev.kobj, + &cyapa_power_wakeup_group); } -static int cyapa_open(struct input_dev *input) +static int cyapa_prepare_wakeup_controls(struct cyapa *cyapa) { - struct cyapa *cyapa = input_get_drvdata(input); struct i2c_client *client = cyapa->client; + struct device *dev = &client->dev; + int error; + + if (device_can_wakeup(dev)) { + error = sysfs_merge_group(&client->dev.kobj, + &cyapa_power_wakeup_group); + if (error) { + dev_err(dev, "failed to add power wakeup group: %d\n", + error); + return error; + } + + error = devm_add_action(dev, + cyapa_remove_power_wakeup_group, cyapa); + if (error) { + cyapa_remove_power_wakeup_group(cyapa); + dev_err(dev, "failed to add power cleanup action: %d\n", + error); + return error; + } + } + + return 0; +} +#else +static inline int cyapa_prepare_wakeup_controls(struct cyapa *cyapa) +{ + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM +static ssize_t cyapa_show_rt_suspend_scanrate(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + u8 pwr_cmd; + u16 sleep_time; + int error; + + error = mutex_lock_interruptible(&cyapa->state_sync_lock); + if (error) + return error; + + pwr_cmd = cyapa->runtime_suspend_power_mode; + sleep_time = cyapa->runtime_suspend_sleep_time; + + mutex_unlock(&cyapa->state_sync_lock); + + return scnprintf(buf, PAGE_SIZE, "%u\n", + cyapa->gen == CYAPA_GEN3 ? + cyapa_pwr_cmd_to_sleep_time(pwr_cmd) : + sleep_time); +} + +static ssize_t cyapa_update_rt_suspend_scanrate(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + u16 time; + int error; + + if (buf == NULL || count == 0 || kstrtou16(buf, 10, &time)) { + dev_err(dev, "invalid runtime suspend scanrate ms parameter\n"); + return -EINVAL; + } + + /* + * When the suspend scanrate is changed, pm_runtime_get to resume + * a potentially suspended device, update to the new pwr_cmd + * and then pm_runtime_put to suspend into the new power mode. + */ + pm_runtime_get_sync(dev); + + error = mutex_lock_interruptible(&cyapa->state_sync_lock); + if (error) + return error; + + cyapa->runtime_suspend_sleep_time = max_t(u16, time, 1000); + cyapa->runtime_suspend_power_mode = + cyapa_sleep_time_to_pwr_cmd(cyapa->runtime_suspend_sleep_time); + + mutex_unlock(&cyapa->state_sync_lock); + + pm_runtime_put_sync_autosuspend(dev); + + return count; +} + +static DEVICE_ATTR(runtime_suspend_scanrate_ms, S_IRUGO|S_IWUSR, + cyapa_show_rt_suspend_scanrate, + cyapa_update_rt_suspend_scanrate); + +static struct attribute *cyapa_power_runtime_entries[] = { + &dev_attr_runtime_suspend_scanrate_ms.attr, + NULL, +}; + +static const struct attribute_group cyapa_power_runtime_group = { + .name = power_group_name, + .attrs = cyapa_power_runtime_entries, +}; + +static void cyapa_remove_power_runtime_group(void *data) +{ + struct cyapa *cyapa = data; + + sysfs_unmerge_group(&cyapa->client->dev.kobj, + &cyapa_power_runtime_group); +} + +static int cyapa_start_runtime(struct cyapa *cyapa) +{ + struct device *dev = &cyapa->client->dev; int error; - error = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE); + cyapa->runtime_suspend_power_mode = PWR_MODE_IDLE; + cyapa->runtime_suspend_sleep_time = + cyapa_pwr_cmd_to_sleep_time(cyapa->runtime_suspend_power_mode); + + error = sysfs_merge_group(&dev->kobj, &cyapa_power_runtime_group); if (error) { - dev_err(&client->dev, "set active power failed: %d\n", error); + dev_err(dev, + "failed to create power runtime group: %d\n", error); return error; } - enable_irq(client->irq); + error = devm_add_action(dev, cyapa_remove_power_runtime_group, cyapa); + if (error) { + cyapa_remove_power_runtime_group(cyapa); + dev_err(dev, + "failed to add power runtime cleanup action: %d\n", + error); + return error; + } + + /* runtime is enabled until device is operational and opened. */ + pm_runtime_set_suspended(dev); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, AUTOSUSPEND_DELAY); + return 0; } +#else +static inline int cyapa_start_runtime(struct cyapa *cyapa) +{ + return 0; +} +#endif /* CONFIG_PM */ -static void cyapa_close(struct input_dev *input) +static ssize_t cyapa_show_fm_ver(struct device *dev, + struct device_attribute *attr, char *buf) { - struct cyapa *cyapa = input_get_drvdata(input); + int error; + struct cyapa *cyapa = dev_get_drvdata(dev); - disable_irq(cyapa->client->irq); - cyapa_set_power_mode(cyapa, PWR_MODE_OFF); + error = mutex_lock_interruptible(&cyapa->state_sync_lock); + if (error) + return error; + error = scnprintf(buf, PAGE_SIZE, "%d.%d\n", cyapa->fw_maj_ver, + cyapa->fw_min_ver); + mutex_unlock(&cyapa->state_sync_lock); + return error; } -static int cyapa_create_input_dev(struct cyapa *cyapa) +static ssize_t cyapa_show_product_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + int size; + int error; + + error = mutex_lock_interruptible(&cyapa->state_sync_lock); + if (error) + return error; + size = scnprintf(buf, PAGE_SIZE, "%s\n", cyapa->product_id); + mutex_unlock(&cyapa->state_sync_lock); + return size; +} + +static int cyapa_firmware(struct cyapa *cyapa, const char *fw_name) { struct device *dev = &cyapa->client->dev; - struct input_dev *input; + const struct firmware *fw; int error; - if (!cyapa->physical_size_x || !cyapa->physical_size_y) - return -EINVAL; + error = request_firmware(&fw, fw_name, dev); + if (error) { + dev_err(dev, "Could not load firmware from %s: %d\n", + fw_name, error); + return error; + } - input = devm_input_allocate_device(dev); - if (!input) { - dev_err(dev, "failed to allocate memory for input device.\n"); - return -ENOMEM; + error = cyapa->ops->check_fw(cyapa, fw); + if (error) { + dev_err(dev, "Invalid CYAPA firmware image: %s\n", + fw_name); + goto done; } - input->name = CYAPA_NAME; - input->phys = cyapa->phys; - input->id.bustype = BUS_I2C; - input->id.version = 1; - input->id.product = 0; /* Means any product in eventcomm. */ - input->dev.parent = &cyapa->client->dev; + /* + * Resume the potentially suspended device because doing FW + * update on a device not in the FULL mode has a chance to + * fail. + */ + pm_runtime_get_sync(dev); - input->open = cyapa_open; - input->close = cyapa_close; + /* Require IRQ support for firmware update commands. */ + cyapa_enable_irq_for_cmd(cyapa); - input_set_drvdata(input, cyapa); + error = cyapa->ops->bl_enter(cyapa); + if (error) { + dev_err(dev, "bl_enter failed, %d\n", error); + goto err_detect; + } - __set_bit(EV_ABS, input->evbit); + error = cyapa->ops->bl_activate(cyapa); + if (error) { + dev_err(dev, "bl_activate failed, %d\n", error); + goto err_detect; + } - /* Finger position */ - input_set_abs_params(input, ABS_MT_POSITION_X, 0, cyapa->max_abs_x, 0, - 0); - input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cyapa->max_abs_y, 0, - 0); - input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0); + error = cyapa->ops->bl_initiate(cyapa, fw); + if (error) { + dev_err(dev, "bl_initiate failed, %d\n", error); + goto err_detect; + } - input_abs_set_res(input, ABS_MT_POSITION_X, - cyapa->max_abs_x / cyapa->physical_size_x); - input_abs_set_res(input, ABS_MT_POSITION_Y, - cyapa->max_abs_y / cyapa->physical_size_y); + error = cyapa->ops->update_fw(cyapa, fw); + if (error) { + dev_err(dev, "update_fw failed, %d\n", error); + goto err_detect; + } - if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK) - __set_bit(BTN_LEFT, input->keybit); - if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK) - __set_bit(BTN_MIDDLE, input->keybit); - if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK) - __set_bit(BTN_RIGHT, input->keybit); +err_detect: + cyapa_disable_irq_for_cmd(cyapa); + pm_runtime_put_noidle(dev); - if (cyapa->btn_capability == CAPABILITY_LEFT_BTN_MASK) - __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); +done: + release_firmware(fw); + return error; +} - /* Handle pointer emulation and unused slots in core */ - error = input_mt_init_slots(input, CYAPA_MAX_MT_SLOTS, - INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED); +static ssize_t cyapa_update_fw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + char fw_name[NAME_MAX]; + int ret, error; + + if (count >= NAME_MAX) { + dev_err(dev, "File name too long\n"); + return -EINVAL; + } + + memcpy(fw_name, buf, count); + if (fw_name[count - 1] == '\n') + fw_name[count - 1] = '\0'; + else + fw_name[count] = '\0'; + + if (cyapa->input) { + /* + * Force the input device to be registered after the firmware + * image is updated, so if the corresponding parameters updated + * in the new firmware image can taken effect immediately. + */ + input_unregister_device(cyapa->input); + cyapa->input = NULL; + } + + error = mutex_lock_interruptible(&cyapa->state_sync_lock); if (error) { - dev_err(dev, "failed to initialize MT slots: %d\n", error); + /* + * Whatever, do reinitialize to try to recover TP state to + * previous state just as it entered fw update entrance. + */ + cyapa_reinitialize(cyapa); return error; } - cyapa->input = input; - return 0; + error = cyapa_firmware(cyapa, fw_name); + if (error) + dev_err(dev, "firmware update failed: %d\n", error); + else + dev_dbg(dev, "firmware update successfully done.\n"); + + /* + * Redetect trackpad device states because firmware update process + * will reset trackpad device into bootloader mode. + */ + ret = cyapa_reinitialize(cyapa); + if (ret) { + dev_err(dev, "failed to redetect after updated: %d\n", ret); + error = error ? error : ret; + } + + mutex_unlock(&cyapa->state_sync_lock); + + return error ? error : count; +} + +static ssize_t cyapa_calibrate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + int error; + + error = mutex_lock_interruptible(&cyapa->state_sync_lock); + if (error) + return error; + + if (cyapa->operational) { + cyapa_enable_irq_for_cmd(cyapa); + error = cyapa->ops->calibrate_store(dev, attr, buf, count); + cyapa_disable_irq_for_cmd(cyapa); + } else { + error = -EBUSY; /* Still running in bootloader mode. */ + } + + mutex_unlock(&cyapa->state_sync_lock); + return error < 0 ? error : count; +} + +static ssize_t cyapa_show_baseline(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + ssize_t error; + + error = mutex_lock_interruptible(&cyapa->state_sync_lock); + if (error) + return error; + + if (cyapa->operational) { + cyapa_enable_irq_for_cmd(cyapa); + error = cyapa->ops->show_baseline(dev, attr, buf); + cyapa_disable_irq_for_cmd(cyapa); + } else { + error = -EBUSY; /* Still running in bootloader mode. */ + } + + mutex_unlock(&cyapa->state_sync_lock); + return error; +} + +static char *cyapa_state_to_string(struct cyapa *cyapa) +{ + switch (cyapa->state) { + case CYAPA_STATE_BL_BUSY: + return "bootloader busy"; + case CYAPA_STATE_BL_IDLE: + return "bootloader idle"; + case CYAPA_STATE_BL_ACTIVE: + return "bootloader active"; + case CYAPA_STATE_GEN5_BL: + return "bootloader"; + case CYAPA_STATE_OP: + case CYAPA_STATE_GEN5_APP: + return "operational"; /* Normal valid state. */ + default: + return "invalid mode"; + } +} + +static ssize_t cyapa_show_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + int size; + int error; + + error = mutex_lock_interruptible(&cyapa->state_sync_lock); + if (error) + return error; + + size = scnprintf(buf, PAGE_SIZE, "gen%d %s\n", + cyapa->gen, cyapa_state_to_string(cyapa)); + + mutex_unlock(&cyapa->state_sync_lock); + return size; +} + +static DEVICE_ATTR(firmware_version, S_IRUGO, cyapa_show_fm_ver, NULL); +static DEVICE_ATTR(product_id, S_IRUGO, cyapa_show_product_id, NULL); +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, cyapa_update_fw_store); +static DEVICE_ATTR(baseline, S_IRUGO, cyapa_show_baseline, NULL); +static DEVICE_ATTR(calibrate, S_IWUSR, NULL, cyapa_calibrate_store); +static DEVICE_ATTR(mode, S_IRUGO, cyapa_show_mode, NULL); + +static struct attribute *cyapa_sysfs_entries[] = { + &dev_attr_firmware_version.attr, + &dev_attr_product_id.attr, + &dev_attr_update_fw.attr, + &dev_attr_baseline.attr, + &dev_attr_calibrate.attr, + &dev_attr_mode.attr, + NULL, +}; + +static const struct attribute_group cyapa_sysfs_group = { + .attrs = cyapa_sysfs_entries, +}; + +static void cyapa_remove_sysfs_group(void *data) +{ + struct cyapa *cyapa = data; + + sysfs_remove_group(&cyapa->client->dev.kobj, &cyapa_sysfs_group); } static int cyapa_probe(struct i2c_client *client, @@ -848,6 +1181,7 @@ static int cyapa_probe(struct i2c_client *client, struct device *dev = &client->dev; struct cyapa *cyapa; u8 adapter_func; + union i2c_smbus_data dummy; int error; adapter_func = cyapa_check_adapter_functionality(client); @@ -856,38 +1190,54 @@ static int cyapa_probe(struct i2c_client *client, return -EIO; } + /* Make sure there is something at this address */ + if (i2c_smbus_xfer(client->adapter, client->addr, 0, + I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) + return -ENODEV; + cyapa = devm_kzalloc(dev, sizeof(struct cyapa), GFP_KERNEL); if (!cyapa) return -ENOMEM; - cyapa->gen = CYAPA_GEN3; + /* i2c isn't supported, use smbus */ + if (adapter_func == CYAPA_ADAPTER_FUNC_SMBUS) + cyapa->smbus = true; + cyapa->client = client; i2c_set_clientdata(client, cyapa); sprintf(cyapa->phys, "i2c-%d-%04x/input0", client->adapter->nr, client->addr); - /* i2c isn't supported, use smbus */ - if (adapter_func == CYAPA_ADAPTER_FUNC_SMBUS) - cyapa->smbus = true; + error = cyapa_initialize(cyapa); + if (error) { + dev_err(dev, "failed to detect and initialize tp device.\n"); + return error; + } - cyapa->state = CYAPA_STATE_NO_DEVICE; + error = sysfs_create_group(&client->dev.kobj, &cyapa_sysfs_group); + if (error) { + dev_err(dev, "failed to create sysfs entries: %d\n", error); + return error; + } - error = cyapa_check_is_operational(cyapa); + error = devm_add_action(dev, cyapa_remove_sysfs_group, cyapa); if (error) { - dev_err(dev, "device not operational, %d\n", error); + cyapa_remove_sysfs_group(cyapa); + dev_err(dev, "failed to add sysfs cleanup action: %d\n", error); return error; } - /* Power down the device until we need it */ - error = cyapa_set_power_mode(cyapa, PWR_MODE_OFF); + error = cyapa_prepare_wakeup_controls(cyapa); if (error) { - dev_err(dev, "failed to quiesce the device: %d\n", error); + dev_err(dev, "failed to prepare wakeup controls: %d\n", error); return error; } - error = cyapa_create_input_dev(cyapa); - if (error) + error = cyapa_start_runtime(cyapa); + if (error) { + dev_err(dev, "failed to start pm_runtime: %d\n", error); return error; + } error = devm_request_threaded_irq(dev, client->irq, NULL, cyapa_irq, @@ -901,11 +1251,18 @@ static int cyapa_probe(struct i2c_client *client, /* Disable IRQ until the device is opened */ disable_irq(client->irq); - /* Register the device in input subsystem */ - error = input_register_device(cyapa->input); - if (error) { - dev_err(dev, "failed to register input device: %d\n", error); - return error; + /* + * Register the device in the input subsystem when it's operational. + * Otherwise, keep in this driver, so it can be be recovered or updated + * through the sysfs mode and update_fw interfaces by user or apps. + */ + if (cyapa->operational) { + error = cyapa_create_input_dev(cyapa); + if (error) { + dev_err(dev, "create input_dev instance failed: %d\n", + error); + return error; + } } return 0; @@ -915,32 +1272,40 @@ static int __maybe_unused cyapa_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct cyapa *cyapa = i2c_get_clientdata(client); - struct input_dev *input = cyapa->input; u8 power_mode; int error; - error = mutex_lock_interruptible(&input->mutex); + error = mutex_lock_interruptible(&cyapa->state_sync_lock); if (error) return error; + /* + * Runtime PM is enable only when device is in operational mode and + * users in use, so need check it before disable it to + * avoid unbalance warning. + */ + if (pm_runtime_enabled(dev)) + pm_runtime_disable(dev); disable_irq(client->irq); /* * Set trackpad device to idle mode if wakeup is allowed, * otherwise turn off. */ - power_mode = device_may_wakeup(dev) ? PWR_MODE_IDLE - : PWR_MODE_OFF; - error = cyapa_set_power_mode(cyapa, power_mode); - if (error) - dev_err(dev, "resume: set power mode to %d failed: %d\n", - power_mode, error); + if (cyapa->operational) { + power_mode = device_may_wakeup(dev) ? cyapa->suspend_power_mode + : PWR_MODE_OFF; + error = cyapa->ops->set_power_mode(cyapa, power_mode, + cyapa->suspend_sleep_time); + if (error) + dev_err(dev, "suspend set power mode failed: %d\n", + error); + } if (device_may_wakeup(dev)) cyapa->irq_wake = (enable_irq_wake(client->irq) == 0); - mutex_unlock(&input->mutex); - + mutex_unlock(&cyapa->state_sync_lock); return 0; } @@ -948,29 +1313,56 @@ static int __maybe_unused cyapa_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct cyapa *cyapa = i2c_get_clientdata(client); - struct input_dev *input = cyapa->input; - u8 power_mode; int error; - mutex_lock(&input->mutex); + mutex_lock(&cyapa->state_sync_lock); - if (device_may_wakeup(dev) && cyapa->irq_wake) + if (device_may_wakeup(dev) && cyapa->irq_wake) { disable_irq_wake(client->irq); + cyapa->irq_wake = false; + } - power_mode = input->users ? PWR_MODE_FULL_ACTIVE : PWR_MODE_OFF; - error = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE); + /* Update device states and runtime PM states. */ + error = cyapa_reinitialize(cyapa); if (error) - dev_warn(dev, "resume: set power mode to %d failed: %d\n", - power_mode, error); + dev_warn(dev, "failed to reinitialize TP device: %d\n", error); enable_irq(client->irq); - mutex_unlock(&input->mutex); + mutex_unlock(&cyapa->state_sync_lock); + return 0; +} + +static int __maybe_unused cyapa_runtime_suspend(struct device *dev) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + int error; + + error = cyapa->ops->set_power_mode(cyapa, + cyapa->runtime_suspend_power_mode, + cyapa->runtime_suspend_sleep_time); + if (error) + dev_warn(dev, "runtime suspend failed: %d\n", error); + + return 0; +} + +static int __maybe_unused cyapa_runtime_resume(struct device *dev) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + int error; + + error = cyapa->ops->set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE, 0); + if (error) + dev_warn(dev, "runtime resume failed: %d\n", error); return 0; } -static SIMPLE_DEV_PM_OPS(cyapa_pm_ops, cyapa_suspend, cyapa_resume); +static const struct dev_pm_ops cyapa_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(cyapa_suspend, cyapa_resume) + SET_RUNTIME_PM_OPS(cyapa_runtime_suspend, cyapa_runtime_resume, NULL) +}; static const struct i2c_device_id cyapa_id_table[] = { { "cyapa", 0 }, @@ -978,11 +1370,21 @@ static const struct i2c_device_id cyapa_id_table[] = { }; MODULE_DEVICE_TABLE(i2c, cyapa_id_table); +#ifdef CONFIG_ACPI +static const struct acpi_device_id cyapa_acpi_id[] = { + { "CYAP0000", 0 }, /* Gen3 trackpad with 0x67 I2C address. */ + { "CYAP0001", 0 }, /* Gen5 trackpad with 0x24 I2C address. */ + { } +}; +MODULE_DEVICE_TABLE(acpi, cyapa_acpi_id); +#endif + static struct i2c_driver cyapa_driver = { .driver = { .name = "cyapa", .owner = THIS_MODULE, .pm = &cyapa_pm_ops, + .acpi_match_table = ACPI_PTR(cyapa_acpi_id), }, .probe = cyapa_probe, diff --git a/drivers/input/mouse/cyapa.h b/drivers/input/mouse/cyapa.h new file mode 100644 index 000000000000..adc9ed5dcb0e --- /dev/null +++ b/drivers/input/mouse/cyapa.h @@ -0,0 +1,301 @@ +/* + * Cypress APA trackpad with I2C interface + * + * Author: Dudley Du <dudl@cypress.com> + * + * Copyright (C) 2014 Cypress Semiconductor, Inc. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#ifndef _CYAPA_H +#define _CYAPA_H + +#include <linux/firmware.h> + +/* APA trackpad firmware generation number. */ +#define CYAPA_GEN_UNKNOWN 0x00 /* unknown protocol. */ +#define CYAPA_GEN3 0x03 /* support MT-protocol B with tracking ID. */ +#define CYAPA_GEN5 0x05 /* support TrueTouch GEN5 trackpad device. */ + +#define CYAPA_NAME "Cypress APA Trackpad (cyapa)" + +/* + * Macros for SMBus communication + */ +#define SMBUS_READ 0x01 +#define SMBUS_WRITE 0x00 +#define SMBUS_ENCODE_IDX(cmd, idx) ((cmd) | (((idx) & 0x03) << 1)) +#define SMBUS_ENCODE_RW(cmd, rw) ((cmd) | ((rw) & 0x01)) +#define SMBUS_BYTE_BLOCK_CMD_MASK 0x80 +#define SMBUS_GROUP_BLOCK_CMD_MASK 0x40 + +/* Commands for read/write registers of Cypress trackpad */ +#define CYAPA_CMD_SOFT_RESET 0x00 +#define CYAPA_CMD_POWER_MODE 0x01 +#define CYAPA_CMD_DEV_STATUS 0x02 +#define CYAPA_CMD_GROUP_DATA 0x03 +#define CYAPA_CMD_GROUP_CMD 0x04 +#define CYAPA_CMD_GROUP_QUERY 0x05 +#define CYAPA_CMD_BL_STATUS 0x06 +#define CYAPA_CMD_BL_HEAD 0x07 +#define CYAPA_CMD_BL_CMD 0x08 +#define CYAPA_CMD_BL_DATA 0x09 +#define CYAPA_CMD_BL_ALL 0x0a +#define CYAPA_CMD_BLK_PRODUCT_ID 0x0b +#define CYAPA_CMD_BLK_HEAD 0x0c +#define CYAPA_CMD_MAX_BASELINE 0x0d +#define CYAPA_CMD_MIN_BASELINE 0x0e + +#define BL_HEAD_OFFSET 0x00 +#define BL_DATA_OFFSET 0x10 + +#define BL_STATUS_SIZE 3 /* Length of gen3 bootloader status registers */ +#define CYAPA_REG_MAP_SIZE 256 + +/* + * Gen3 Operational Device Status Register + * + * bit 7: Valid interrupt source + * bit 6 - 4: Reserved + * bit 3 - 2: Power status + * bit 1 - 0: Device status + */ +#define REG_OP_STATUS 0x00 +#define OP_STATUS_SRC 0x80 +#define OP_STATUS_POWER 0x0c +#define OP_STATUS_DEV 0x03 +#define OP_STATUS_MASK (OP_STATUS_SRC | OP_STATUS_POWER | OP_STATUS_DEV) + +/* + * Operational Finger Count/Button Flags Register + * + * bit 7 - 4: Number of touched finger + * bit 3: Valid data + * bit 2: Middle Physical Button + * bit 1: Right Physical Button + * bit 0: Left physical Button + */ +#define REG_OP_DATA1 0x01 +#define OP_DATA_VALID 0x08 +#define OP_DATA_MIDDLE_BTN 0x04 +#define OP_DATA_RIGHT_BTN 0x02 +#define OP_DATA_LEFT_BTN 0x01 +#define OP_DATA_BTN_MASK (OP_DATA_MIDDLE_BTN | OP_DATA_RIGHT_BTN | \ + OP_DATA_LEFT_BTN) + +/* + * Write-only command file register used to issue commands and + * parameters to the bootloader. + * The default value read from it is always 0x00. + */ +#define REG_BL_FILE 0x00 +#define BL_FILE 0x00 + +/* + * Bootloader Status Register + * + * bit 7: Busy + * bit 6 - 5: Reserved + * bit 4: Bootloader running + * bit 3 - 2: Reserved + * bit 1: Watchdog Reset + * bit 0: Checksum valid + */ +#define REG_BL_STATUS 0x01 +#define BL_STATUS_REV_6_5 0x60 +#define BL_STATUS_BUSY 0x80 +#define BL_STATUS_RUNNING 0x10 +#define BL_STATUS_REV_3_2 0x0c +#define BL_STATUS_WATCHDOG 0x02 +#define BL_STATUS_CSUM_VALID 0x01 +#define BL_STATUS_REV_MASK (BL_STATUS_WATCHDOG | BL_STATUS_REV_3_2 | \ + BL_STATUS_REV_6_5) + +/* + * Bootloader Error Register + * + * bit 7: Invalid + * bit 6: Invalid security key + * bit 5: Bootloading + * bit 4: Command checksum + * bit 3: Flash protection error + * bit 2: Flash checksum error + * bit 1 - 0: Reserved + */ +#define REG_BL_ERROR 0x02 +#define BL_ERROR_INVALID 0x80 +#define BL_ERROR_INVALID_KEY 0x40 +#define BL_ERROR_BOOTLOADING 0x20 +#define BL_ERROR_CMD_CSUM 0x10 +#define BL_ERROR_FLASH_PROT 0x08 +#define BL_ERROR_FLASH_CSUM 0x04 +#define BL_ERROR_RESERVED 0x03 +#define BL_ERROR_NO_ERR_IDLE 0x00 +#define BL_ERROR_NO_ERR_ACTIVE (BL_ERROR_BOOTLOADING) + +#define CAPABILITY_BTN_SHIFT 3 +#define CAPABILITY_LEFT_BTN_MASK (0x01 << 3) +#define CAPABILITY_RIGHT_BTN_MASK (0x01 << 4) +#define CAPABILITY_MIDDLE_BTN_MASK (0x01 << 5) +#define CAPABILITY_BTN_MASK (CAPABILITY_LEFT_BTN_MASK | \ + CAPABILITY_RIGHT_BTN_MASK | \ + CAPABILITY_MIDDLE_BTN_MASK) + +#define PWR_MODE_MASK 0xfc +#define PWR_MODE_FULL_ACTIVE (0x3f << 2) +#define PWR_MODE_IDLE (0x03 << 2) /* Default rt suspend scanrate: 30ms */ +#define PWR_MODE_SLEEP (0x05 << 2) /* Default suspend scanrate: 50ms */ +#define PWR_MODE_BTN_ONLY (0x01 << 2) +#define PWR_MODE_OFF (0x00 << 2) + +#define PWR_STATUS_MASK 0x0c +#define PWR_STATUS_ACTIVE (0x03 << 2) +#define PWR_STATUS_IDLE (0x02 << 2) +#define PWR_STATUS_BTN_ONLY (0x01 << 2) +#define PWR_STATUS_OFF (0x00 << 2) + +#define AUTOSUSPEND_DELAY 2000 /* unit : ms */ + +#define UNINIT_SLEEP_TIME 0xFFFF +#define UNINIT_PWR_MODE 0xFF + +#define BTN_ONLY_MODE_NAME "buttononly" +#define OFF_MODE_NAME "off" + +/* The touch.id is used as the MT slot id, thus max MT slot is 15 */ +#define CYAPA_MAX_MT_SLOTS 15 + +struct cyapa; + +typedef bool (*cb_sort)(struct cyapa *, u8 *, int); + +struct cyapa_dev_ops { + int (*check_fw)(struct cyapa *, const struct firmware *); + int (*bl_enter)(struct cyapa *); + int (*bl_activate)(struct cyapa *); + int (*bl_initiate)(struct cyapa *, const struct firmware *); + int (*update_fw)(struct cyapa *, const struct firmware *); + int (*bl_deactivate)(struct cyapa *); + + ssize_t (*show_baseline)(struct device *, + struct device_attribute *, char *); + ssize_t (*calibrate_store)(struct device *, + struct device_attribute *, const char *, size_t); + + int (*initialize)(struct cyapa *cyapa); + + int (*state_parse)(struct cyapa *cyapa, u8 *reg_status, int len); + int (*operational_check)(struct cyapa *cyapa); + + int (*irq_handler)(struct cyapa *); + bool (*irq_cmd_handler)(struct cyapa *); + int (*sort_empty_output_data)(struct cyapa *, + u8 *, int *, cb_sort); + + int (*set_power_mode)(struct cyapa *, u8, u16); +}; + +struct cyapa_gen5_cmd_states { + struct mutex cmd_lock; + struct completion cmd_ready; + atomic_t cmd_issued; + u8 in_progress_cmd; + bool is_irq_mode; + + cb_sort resp_sort_func; + u8 *resp_data; + int *resp_len; + + u8 irq_cmd_buf[CYAPA_REG_MAP_SIZE]; + u8 empty_buf[CYAPA_REG_MAP_SIZE]; +}; + +union cyapa_cmd_states { + struct cyapa_gen5_cmd_states gen5; +}; + +enum cyapa_state { + CYAPA_STATE_NO_DEVICE, + CYAPA_STATE_BL_BUSY, + CYAPA_STATE_BL_IDLE, + CYAPA_STATE_BL_ACTIVE, + CYAPA_STATE_OP, + CYAPA_STATE_GEN5_BL, + CYAPA_STATE_GEN5_APP, +}; + +/* The main device structure */ +struct cyapa { + enum cyapa_state state; + u8 status[BL_STATUS_SIZE]; + bool operational; /* true: ready for data reporting; false: not. */ + + struct i2c_client *client; + struct input_dev *input; + char phys[32]; /* Device physical location */ + bool irq_wake; /* Irq wake is enabled */ + bool smbus; + + /* power mode settings */ + u8 suspend_power_mode; + u16 suspend_sleep_time; + u8 runtime_suspend_power_mode; + u16 runtime_suspend_sleep_time; + u8 dev_pwr_mode; + u16 dev_sleep_time; + + /* Read from query data region. */ + char product_id[16]; + u8 fw_maj_ver; /* Firmware major version. */ + u8 fw_min_ver; /* Firmware minor version. */ + u8 btn_capability; + u8 gen; + int max_abs_x; + int max_abs_y; + int physical_size_x; + int physical_size_y; + + /* Used in ttsp and truetouch based trackpad devices. */ + u8 x_origin; /* X Axis Origin: 0 = left side; 1 = rigth side. */ + u8 y_origin; /* Y Axis Origin: 0 = top; 1 = bottom. */ + int electrodes_x; /* Number of electrodes on the X Axis*/ + int electrodes_y; /* Number of electrodes on the Y Axis*/ + int electrodes_rx; /* Number of Rx electrodes */ + int aligned_electrodes_rx; /* 4 aligned */ + int max_z; + + /* + * Used to synchronize the access or update the device state. + * And since update firmware and read firmware image process will take + * quite long time, maybe more than 10 seconds, so use mutex_lock + * to sync and wait other interface and detecting are done or ready. + */ + struct mutex state_sync_lock; + + const struct cyapa_dev_ops *ops; + + union cyapa_cmd_states cmd_states; +}; + + +ssize_t cyapa_i2c_reg_read_block(struct cyapa *cyapa, u8 reg, size_t len, + u8 *values); +ssize_t cyapa_smbus_read_block(struct cyapa *cyapa, u8 cmd, size_t len, + u8 *values); + +ssize_t cyapa_read_block(struct cyapa *cyapa, u8 cmd_idx, u8 *values); + +int cyapa_poll_state(struct cyapa *cyapa, unsigned int timeout); + +u8 cyapa_sleep_time_to_pwr_cmd(u16 sleep_time); +u16 cyapa_pwr_cmd_to_sleep_time(u8 pwr_mode); + + +extern const char product_id[]; +extern const struct cyapa_dev_ops cyapa_gen3_ops; +extern const struct cyapa_dev_ops cyapa_gen5_ops; + +#endif diff --git a/drivers/input/mouse/cyapa_gen3.c b/drivers/input/mouse/cyapa_gen3.c new file mode 100644 index 000000000000..77e9d70a986b --- /dev/null +++ b/drivers/input/mouse/cyapa_gen3.c @@ -0,0 +1,1247 @@ +/* + * Cypress APA trackpad with I2C interface + * + * Author: Dudley Du <dudl@cypress.com> + * Further cleanup and restructuring by: + * Daniel Kurtz <djkurtz@chromium.org> + * Benson Leung <bleung@chromium.org> + * + * Copyright (C) 2011-2014 Cypress Semiconductor, Inc. + * Copyright (C) 2011-2012 Google, Inc. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/unaligned/access_ok.h> +#include "cyapa.h" + + +#define GEN3_MAX_FINGERS 5 +#define GEN3_FINGER_NUM(x) (((x) >> 4) & 0x07) + +#define BLK_HEAD_BYTES 32 + +/* Macro for register map group offset. */ +#define PRODUCT_ID_SIZE 16 +#define QUERY_DATA_SIZE 27 +#define REG_PROTOCOL_GEN_QUERY_OFFSET 20 + +#define REG_OFFSET_DATA_BASE 0x0000 +#define REG_OFFSET_COMMAND_BASE 0x0028 +#define REG_OFFSET_QUERY_BASE 0x002a + +#define CYAPA_OFFSET_SOFT_RESET REG_OFFSET_COMMAND_BASE +#define OP_RECALIBRATION_MASK 0x80 +#define OP_REPORT_BASELINE_MASK 0x40 +#define REG_OFFSET_MAX_BASELINE 0x0026 +#define REG_OFFSET_MIN_BASELINE 0x0027 + +#define REG_OFFSET_POWER_MODE (REG_OFFSET_COMMAND_BASE + 1) +#define SET_POWER_MODE_DELAY 10000 /* Unit: us */ +#define SET_POWER_MODE_TRIES 5 + +#define GEN3_BL_CMD_CHECKSUM_SEED 0xff +#define GEN3_BL_CMD_INITIATE_BL 0x38 +#define GEN3_BL_CMD_WRITE_BLOCK 0x39 +#define GEN3_BL_CMD_VERIFY_BLOCK 0x3a +#define GEN3_BL_CMD_TERMINATE_BL 0x3b +#define GEN3_BL_CMD_LAUNCH_APP 0xa5 + +/* + * CYAPA trackpad device states. + * Used in register 0x00, bit1-0, DeviceStatus field. + * Other values indicate device is in an abnormal state and must be reset. + */ +#define CYAPA_DEV_NORMAL 0x03 +#define CYAPA_DEV_BUSY 0x01 + +#define CYAPA_FW_BLOCK_SIZE 64 +#define CYAPA_FW_READ_SIZE 16 +#define CYAPA_FW_HDR_START 0x0780 +#define CYAPA_FW_HDR_BLOCK_COUNT 2 +#define CYAPA_FW_HDR_BLOCK_START (CYAPA_FW_HDR_START / CYAPA_FW_BLOCK_SIZE) +#define CYAPA_FW_HDR_SIZE (CYAPA_FW_HDR_BLOCK_COUNT * \ + CYAPA_FW_BLOCK_SIZE) +#define CYAPA_FW_DATA_START 0x0800 +#define CYAPA_FW_DATA_BLOCK_COUNT 480 +#define CYAPA_FW_DATA_BLOCK_START (CYAPA_FW_DATA_START / CYAPA_FW_BLOCK_SIZE) +#define CYAPA_FW_DATA_SIZE (CYAPA_FW_DATA_BLOCK_COUNT * \ + CYAPA_FW_BLOCK_SIZE) +#define CYAPA_FW_SIZE (CYAPA_FW_HDR_SIZE + CYAPA_FW_DATA_SIZE) +#define CYAPA_CMD_LEN 16 + +#define GEN3_BL_IDLE_FW_MAJ_VER_OFFSET 0x0b +#define GEN3_BL_IDLE_FW_MIN_VER_OFFSET (GEN3_BL_IDLE_FW_MAJ_VER_OFFSET + 1) + + +struct cyapa_touch { + /* + * high bits or x/y position value + * bit 7 - 4: high 4 bits of x position value + * bit 3 - 0: high 4 bits of y position value + */ + u8 xy_hi; + u8 x_lo; /* low 8 bits of x position value. */ + u8 y_lo; /* low 8 bits of y position value. */ + u8 pressure; + /* id range is 1 - 15. It is incremented with every new touch. */ + u8 id; +} __packed; + +struct cyapa_reg_data { + /* + * bit 0 - 1: device status + * bit 3 - 2: power mode + * bit 6 - 4: reserved + * bit 7: interrupt valid bit + */ + u8 device_status; + /* + * bit 7 - 4: number of fingers currently touching pad + * bit 3: valid data check bit + * bit 2: middle mechanism button state if exists + * bit 1: right mechanism button state if exists + * bit 0: left mechanism button state if exists + */ + u8 finger_btn; + /* CYAPA reports up to 5 touches per packet. */ + struct cyapa_touch touches[5]; +} __packed; + +struct gen3_write_block_cmd { + u8 checksum_seed; /* Always be 0xff */ + u8 cmd_code; /* command code: 0x39 */ + u8 key[8]; /* 8-byte security key */ + __be16 block_num; + u8 block_data[CYAPA_FW_BLOCK_SIZE]; + u8 block_checksum; /* Calculated using bytes 12 - 75 */ + u8 cmd_checksum; /* Calculated using bytes 0-76 */ +} __packed; + +static const u8 security_key[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; +static const u8 bl_activate[] = { 0x00, 0xff, 0x38, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07 }; +static const u8 bl_deactivate[] = { 0x00, 0xff, 0x3b, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07 }; +static const u8 bl_exit[] = { 0x00, 0xff, 0xa5, 0x00, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07 }; + + + /* for byte read/write command */ +#define CMD_RESET 0 +#define CMD_POWER_MODE 1 +#define CMD_DEV_STATUS 2 +#define CMD_REPORT_MAX_BASELINE 3 +#define CMD_REPORT_MIN_BASELINE 4 +#define SMBUS_BYTE_CMD(cmd) (((cmd) & 0x3f) << 1) +#define CYAPA_SMBUS_RESET SMBUS_BYTE_CMD(CMD_RESET) +#define CYAPA_SMBUS_POWER_MODE SMBUS_BYTE_CMD(CMD_POWER_MODE) +#define CYAPA_SMBUS_DEV_STATUS SMBUS_BYTE_CMD(CMD_DEV_STATUS) +#define CYAPA_SMBUS_MAX_BASELINE SMBUS_BYTE_CMD(CMD_REPORT_MAX_BASELINE) +#define CYAPA_SMBUS_MIN_BASELINE SMBUS_BYTE_CMD(CMD_REPORT_MIN_BASELINE) + + /* for group registers read/write command */ +#define REG_GROUP_DATA 0 +#define REG_GROUP_CMD 2 +#define REG_GROUP_QUERY 3 +#define SMBUS_GROUP_CMD(grp) (0x80 | (((grp) & 0x07) << 3)) +#define CYAPA_SMBUS_GROUP_DATA SMBUS_GROUP_CMD(REG_GROUP_DATA) +#define CYAPA_SMBUS_GROUP_CMD SMBUS_GROUP_CMD(REG_GROUP_CMD) +#define CYAPA_SMBUS_GROUP_QUERY SMBUS_GROUP_CMD(REG_GROUP_QUERY) + + /* for register block read/write command */ +#define CMD_BL_STATUS 0 +#define CMD_BL_HEAD 1 +#define CMD_BL_CMD 2 +#define CMD_BL_DATA 3 +#define CMD_BL_ALL 4 +#define CMD_BLK_PRODUCT_ID 5 +#define CMD_BLK_HEAD 6 +#define SMBUS_BLOCK_CMD(cmd) (0xc0 | (((cmd) & 0x1f) << 1)) + +/* register block read/write command in bootloader mode */ +#define CYAPA_SMBUS_BL_STATUS SMBUS_BLOCK_CMD(CMD_BL_STATUS) +#define CYAPA_SMBUS_BL_HEAD SMBUS_BLOCK_CMD(CMD_BL_HEAD) +#define CYAPA_SMBUS_BL_CMD SMBUS_BLOCK_CMD(CMD_BL_CMD) +#define CYAPA_SMBUS_BL_DATA SMBUS_BLOCK_CMD(CMD_BL_DATA) +#define CYAPA_SMBUS_BL_ALL SMBUS_BLOCK_CMD(CMD_BL_ALL) + +/* register block read/write command in operational mode */ +#define CYAPA_SMBUS_BLK_PRODUCT_ID SMBUS_BLOCK_CMD(CMD_BLK_PRODUCT_ID) +#define CYAPA_SMBUS_BLK_HEAD SMBUS_BLOCK_CMD(CMD_BLK_HEAD) + + /* for byte read/write command */ +#define CMD_RESET 0 +#define CMD_POWER_MODE 1 +#define CMD_DEV_STATUS 2 +#define CMD_REPORT_MAX_BASELINE 3 +#define CMD_REPORT_MIN_BASELINE 4 +#define SMBUS_BYTE_CMD(cmd) (((cmd) & 0x3f) << 1) +#define CYAPA_SMBUS_RESET SMBUS_BYTE_CMD(CMD_RESET) +#define CYAPA_SMBUS_POWER_MODE SMBUS_BYTE_CMD(CMD_POWER_MODE) +#define CYAPA_SMBUS_DEV_STATUS SMBUS_BYTE_CMD(CMD_DEV_STATUS) +#define CYAPA_SMBUS_MAX_BASELINE SMBUS_BYTE_CMD(CMD_REPORT_MAX_BASELINE) +#define CYAPA_SMBUS_MIN_BASELINE SMBUS_BYTE_CMD(CMD_REPORT_MIN_BASELINE) + + /* for group registers read/write command */ +#define REG_GROUP_DATA 0 +#define REG_GROUP_CMD 2 +#define REG_GROUP_QUERY 3 +#define SMBUS_GROUP_CMD(grp) (0x80 | (((grp) & 0x07) << 3)) +#define CYAPA_SMBUS_GROUP_DATA SMBUS_GROUP_CMD(REG_GROUP_DATA) +#define CYAPA_SMBUS_GROUP_CMD SMBUS_GROUP_CMD(REG_GROUP_CMD) +#define CYAPA_SMBUS_GROUP_QUERY SMBUS_GROUP_CMD(REG_GROUP_QUERY) + + /* for register block read/write command */ +#define CMD_BL_STATUS 0 +#define CMD_BL_HEAD 1 +#define CMD_BL_CMD 2 +#define CMD_BL_DATA 3 +#define CMD_BL_ALL 4 +#define CMD_BLK_PRODUCT_ID 5 +#define CMD_BLK_HEAD 6 +#define SMBUS_BLOCK_CMD(cmd) (0xc0 | (((cmd) & 0x1f) << 1)) + +/* register block read/write command in bootloader mode */ +#define CYAPA_SMBUS_BL_STATUS SMBUS_BLOCK_CMD(CMD_BL_STATUS) +#define CYAPA_SMBUS_BL_HEAD SMBUS_BLOCK_CMD(CMD_BL_HEAD) +#define CYAPA_SMBUS_BL_CMD SMBUS_BLOCK_CMD(CMD_BL_CMD) +#define CYAPA_SMBUS_BL_DATA SMBUS_BLOCK_CMD(CMD_BL_DATA) +#define CYAPA_SMBUS_BL_ALL SMBUS_BLOCK_CMD(CMD_BL_ALL) + +/* register block read/write command in operational mode */ +#define CYAPA_SMBUS_BLK_PRODUCT_ID SMBUS_BLOCK_CMD(CMD_BLK_PRODUCT_ID) +#define CYAPA_SMBUS_BLK_HEAD SMBUS_BLOCK_CMD(CMD_BLK_HEAD) + +struct cyapa_cmd_len { + u8 cmd; + u8 len; +}; + +/* maps generic CYAPA_CMD_* code to the I2C equivalent */ +static const struct cyapa_cmd_len cyapa_i2c_cmds[] = { + { CYAPA_OFFSET_SOFT_RESET, 1 }, /* CYAPA_CMD_SOFT_RESET */ + { REG_OFFSET_COMMAND_BASE + 1, 1 }, /* CYAPA_CMD_POWER_MODE */ + { REG_OFFSET_DATA_BASE, 1 }, /* CYAPA_CMD_DEV_STATUS */ + { REG_OFFSET_DATA_BASE, sizeof(struct cyapa_reg_data) }, + /* CYAPA_CMD_GROUP_DATA */ + { REG_OFFSET_COMMAND_BASE, 0 }, /* CYAPA_CMD_GROUP_CMD */ + { REG_OFFSET_QUERY_BASE, QUERY_DATA_SIZE }, /* CYAPA_CMD_GROUP_QUERY */ + { BL_HEAD_OFFSET, 3 }, /* CYAPA_CMD_BL_STATUS */ + { BL_HEAD_OFFSET, 16 }, /* CYAPA_CMD_BL_HEAD */ + { BL_HEAD_OFFSET, 16 }, /* CYAPA_CMD_BL_CMD */ + { BL_DATA_OFFSET, 16 }, /* CYAPA_CMD_BL_DATA */ + { BL_HEAD_OFFSET, 32 }, /* CYAPA_CMD_BL_ALL */ + { REG_OFFSET_QUERY_BASE, PRODUCT_ID_SIZE }, + /* CYAPA_CMD_BLK_PRODUCT_ID */ + { REG_OFFSET_DATA_BASE, 32 }, /* CYAPA_CMD_BLK_HEAD */ + { REG_OFFSET_MAX_BASELINE, 1 }, /* CYAPA_CMD_MAX_BASELINE */ + { REG_OFFSET_MIN_BASELINE, 1 }, /* CYAPA_CMD_MIN_BASELINE */ +}; + +static const struct cyapa_cmd_len cyapa_smbus_cmds[] = { + { CYAPA_SMBUS_RESET, 1 }, /* CYAPA_CMD_SOFT_RESET */ + { CYAPA_SMBUS_POWER_MODE, 1 }, /* CYAPA_CMD_POWER_MODE */ + { CYAPA_SMBUS_DEV_STATUS, 1 }, /* CYAPA_CMD_DEV_STATUS */ + { CYAPA_SMBUS_GROUP_DATA, sizeof(struct cyapa_reg_data) }, + /* CYAPA_CMD_GROUP_DATA */ + { CYAPA_SMBUS_GROUP_CMD, 2 }, /* CYAPA_CMD_GROUP_CMD */ + { CYAPA_SMBUS_GROUP_QUERY, QUERY_DATA_SIZE }, + /* CYAPA_CMD_GROUP_QUERY */ + { CYAPA_SMBUS_BL_STATUS, 3 }, /* CYAPA_CMD_BL_STATUS */ + { CYAPA_SMBUS_BL_HEAD, 16 }, /* CYAPA_CMD_BL_HEAD */ + { CYAPA_SMBUS_BL_CMD, 16 }, /* CYAPA_CMD_BL_CMD */ + { CYAPA_SMBUS_BL_DATA, 16 }, /* CYAPA_CMD_BL_DATA */ + { CYAPA_SMBUS_BL_ALL, 32 }, /* CYAPA_CMD_BL_ALL */ + { CYAPA_SMBUS_BLK_PRODUCT_ID, PRODUCT_ID_SIZE }, + /* CYAPA_CMD_BLK_PRODUCT_ID */ + { CYAPA_SMBUS_BLK_HEAD, 16 }, /* CYAPA_CMD_BLK_HEAD */ + { CYAPA_SMBUS_MAX_BASELINE, 1 }, /* CYAPA_CMD_MAX_BASELINE */ + { CYAPA_SMBUS_MIN_BASELINE, 1 }, /* CYAPA_CMD_MIN_BASELINE */ +}; + + +/* + * cyapa_smbus_read_block - perform smbus block read command + * @cyapa - private data structure of the driver + * @cmd - the properly encoded smbus command + * @len - expected length of smbus command result + * @values - buffer to store smbus command result + * + * Returns negative errno, else the number of bytes written. + * + * Note: + * In trackpad device, the memory block allocated for I2C register map + * is 256 bytes, so the max read block for I2C bus is 256 bytes. + */ +ssize_t cyapa_smbus_read_block(struct cyapa *cyapa, u8 cmd, size_t len, + u8 *values) +{ + ssize_t ret; + u8 index; + u8 smbus_cmd; + u8 *buf; + struct i2c_client *client = cyapa->client; + + if (!(SMBUS_BYTE_BLOCK_CMD_MASK & cmd)) + return -EINVAL; + + if (SMBUS_GROUP_BLOCK_CMD_MASK & cmd) { + /* read specific block registers command. */ + smbus_cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ); + ret = i2c_smbus_read_block_data(client, smbus_cmd, values); + goto out; + } + + ret = 0; + for (index = 0; index * I2C_SMBUS_BLOCK_MAX < len; index++) { + smbus_cmd = SMBUS_ENCODE_IDX(cmd, index); + smbus_cmd = SMBUS_ENCODE_RW(smbus_cmd, SMBUS_READ); + buf = values + I2C_SMBUS_BLOCK_MAX * index; + ret = i2c_smbus_read_block_data(client, smbus_cmd, buf); + if (ret < 0) + goto out; + } + +out: + return ret > 0 ? len : ret; +} + +static s32 cyapa_read_byte(struct cyapa *cyapa, u8 cmd_idx) +{ + u8 cmd; + + if (cyapa->smbus) { + cmd = cyapa_smbus_cmds[cmd_idx].cmd; + cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ); + } else { + cmd = cyapa_i2c_cmds[cmd_idx].cmd; + } + return i2c_smbus_read_byte_data(cyapa->client, cmd); +} + +static s32 cyapa_write_byte(struct cyapa *cyapa, u8 cmd_idx, u8 value) +{ + u8 cmd; + + if (cyapa->smbus) { + cmd = cyapa_smbus_cmds[cmd_idx].cmd; + cmd = SMBUS_ENCODE_RW(cmd, SMBUS_WRITE); + } else { + cmd = cyapa_i2c_cmds[cmd_idx].cmd; + } + return i2c_smbus_write_byte_data(cyapa->client, cmd, value); +} + +ssize_t cyapa_i2c_reg_read_block(struct cyapa *cyapa, u8 reg, size_t len, + u8 *values) +{ + return i2c_smbus_read_i2c_block_data(cyapa->client, reg, len, values); +} + +static ssize_t cyapa_i2c_reg_write_block(struct cyapa *cyapa, u8 reg, + size_t len, const u8 *values) +{ + return i2c_smbus_write_i2c_block_data(cyapa->client, reg, len, values); +} + +ssize_t cyapa_read_block(struct cyapa *cyapa, u8 cmd_idx, u8 *values) +{ + u8 cmd; + size_t len; + + if (cyapa->smbus) { + cmd = cyapa_smbus_cmds[cmd_idx].cmd; + len = cyapa_smbus_cmds[cmd_idx].len; + return cyapa_smbus_read_block(cyapa, cmd, len, values); + } + cmd = cyapa_i2c_cmds[cmd_idx].cmd; + len = cyapa_i2c_cmds[cmd_idx].len; + return cyapa_i2c_reg_read_block(cyapa, cmd, len, values); +} + +/* + * Determine the Gen3 trackpad device's current operating state. + * + */ +static int cyapa_gen3_state_parse(struct cyapa *cyapa, u8 *reg_data, int len) +{ + cyapa->state = CYAPA_STATE_NO_DEVICE; + + /* Parse based on Gen3 characteristic registers and bits */ + if (reg_data[REG_BL_FILE] == BL_FILE && + reg_data[REG_BL_ERROR] == BL_ERROR_NO_ERR_IDLE && + (reg_data[REG_BL_STATUS] == + (BL_STATUS_RUNNING | BL_STATUS_CSUM_VALID) || + reg_data[REG_BL_STATUS] == BL_STATUS_RUNNING)) { + /* + * Normal state after power on or reset, + * REG_BL_STATUS == 0x11, firmware image checksum is valid. + * REG_BL_STATUS == 0x10, firmware image checksum is invalid. + */ + cyapa->gen = CYAPA_GEN3; + cyapa->state = CYAPA_STATE_BL_IDLE; + } else if (reg_data[REG_BL_FILE] == BL_FILE && + (reg_data[REG_BL_STATUS] & BL_STATUS_RUNNING) == + BL_STATUS_RUNNING) { + cyapa->gen = CYAPA_GEN3; + if (reg_data[REG_BL_STATUS] & BL_STATUS_BUSY) { + cyapa->state = CYAPA_STATE_BL_BUSY; + } else { + if ((reg_data[REG_BL_ERROR] & BL_ERROR_BOOTLOADING) == + BL_ERROR_BOOTLOADING) + cyapa->state = CYAPA_STATE_BL_ACTIVE; + else + cyapa->state = CYAPA_STATE_BL_IDLE; + } + } else if ((reg_data[REG_OP_STATUS] & OP_STATUS_SRC) && + (reg_data[REG_OP_DATA1] & OP_DATA_VALID)) { + /* + * Normal state when running in operational mode, + * may also not in full power state or + * busying in command process. + */ + if (GEN3_FINGER_NUM(reg_data[REG_OP_DATA1]) <= + GEN3_MAX_FINGERS) { + /* Finger number data is valid. */ + cyapa->gen = CYAPA_GEN3; + cyapa->state = CYAPA_STATE_OP; + } + } else if (reg_data[REG_OP_STATUS] == 0x0C && + reg_data[REG_OP_DATA1] == 0x08) { + /* Op state when first two registers overwritten with 0x00 */ + cyapa->gen = CYAPA_GEN3; + cyapa->state = CYAPA_STATE_OP; + } else if (reg_data[REG_BL_STATUS] & + (BL_STATUS_RUNNING | BL_STATUS_BUSY)) { + cyapa->gen = CYAPA_GEN3; + cyapa->state = CYAPA_STATE_BL_BUSY; + } + + if (cyapa->gen == CYAPA_GEN3 && (cyapa->state == CYAPA_STATE_OP || + cyapa->state == CYAPA_STATE_BL_IDLE || + cyapa->state == CYAPA_STATE_BL_ACTIVE || + cyapa->state == CYAPA_STATE_BL_BUSY)) + return 0; + + return -EAGAIN; +} + +/* + * Enter bootloader by soft resetting the device. + * + * If device is already in the bootloader, the function just returns. + * Otherwise, reset the device; after reset, device enters bootloader idle + * state immediately. + * + * Returns: + * 0 on success + * -EAGAIN device was reset, but is not now in bootloader idle state + * < 0 if the device never responds within the timeout + */ +static int cyapa_gen3_bl_enter(struct cyapa *cyapa) +{ + int error; + int waiting_time; + + error = cyapa_poll_state(cyapa, 500); + if (error) + return error; + if (cyapa->state == CYAPA_STATE_BL_IDLE) { + /* Already in BL_IDLE. Skipping reset. */ + return 0; + } + + if (cyapa->state != CYAPA_STATE_OP) + return -EAGAIN; + + cyapa->operational = false; + cyapa->state = CYAPA_STATE_NO_DEVICE; + error = cyapa_write_byte(cyapa, CYAPA_CMD_SOFT_RESET, 0x01); + if (error) + return -EIO; + + usleep_range(25000, 50000); + waiting_time = 2000; /* For some shipset, max waiting time is 1~2s. */ + do { + error = cyapa_poll_state(cyapa, 500); + if (error) { + if (error == -ETIMEDOUT) { + waiting_time -= 500; + continue; + } + return error; + } + + if ((cyapa->state == CYAPA_STATE_BL_IDLE) && + !(cyapa->status[REG_BL_STATUS] & BL_STATUS_WATCHDOG)) + break; + + msleep(100); + waiting_time -= 100; + } while (waiting_time > 0); + + if ((cyapa->state != CYAPA_STATE_BL_IDLE) || + (cyapa->status[REG_BL_STATUS] & BL_STATUS_WATCHDOG)) + return -EAGAIN; + + return 0; +} + +static int cyapa_gen3_bl_activate(struct cyapa *cyapa) +{ + int error; + + error = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_activate), + bl_activate); + if (error) + return error; + + /* Wait for bootloader to activate; takes between 2 and 12 seconds */ + msleep(2000); + error = cyapa_poll_state(cyapa, 11000); + if (error) + return error; + if (cyapa->state != CYAPA_STATE_BL_ACTIVE) + return -EAGAIN; + + return 0; +} + +static int cyapa_gen3_bl_deactivate(struct cyapa *cyapa) +{ + int error; + + error = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_deactivate), + bl_deactivate); + if (error) + return error; + + /* Wait for bootloader to switch to idle state; should take < 100ms */ + msleep(100); + error = cyapa_poll_state(cyapa, 500); + if (error) + return error; + if (cyapa->state != CYAPA_STATE_BL_IDLE) + return -EAGAIN; + return 0; +} + +/* + * Exit bootloader + * + * Send bl_exit command, then wait 50 - 100 ms to let device transition to + * operational mode. If this is the first time the device's firmware is + * running, it can take up to 2 seconds to calibrate its sensors. So, poll + * the device's new state for up to 2 seconds. + * + * Returns: + * -EIO failure while reading from device + * -EAGAIN device is stuck in bootloader, b/c it has invalid firmware + * 0 device is supported and in operational mode + */ +static int cyapa_gen3_bl_exit(struct cyapa *cyapa) +{ + int error; + + error = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_exit), bl_exit); + if (error) + return error; + + /* + * Wait for bootloader to exit, and operation mode to start. + * Normally, this takes at least 50 ms. + */ + usleep_range(50000, 100000); + /* + * In addition, when a device boots for the first time after being + * updated to new firmware, it must first calibrate its sensors, which + * can take up to an additional 2 seconds. If the device power is + * running low, this may take even longer. + */ + error = cyapa_poll_state(cyapa, 4000); + if (error < 0) + return error; + if (cyapa->state != CYAPA_STATE_OP) + return -EAGAIN; + + return 0; +} + +static u16 cyapa_gen3_csum(const u8 *buf, size_t count) +{ + int i; + u16 csum = 0; + + for (i = 0; i < count; i++) + csum += buf[i]; + + return csum; +} + +/* + * Verify the integrity of a CYAPA firmware image file. + * + * The firmware image file is 30848 bytes, composed of 482 64-byte blocks. + * + * The first 2 blocks are the firmware header. + * The next 480 blocks are the firmware image. + * + * The first two bytes of the header hold the header checksum, computed by + * summing the other 126 bytes of the header. + * The last two bytes of the header hold the firmware image checksum, computed + * by summing the 30720 bytes of the image modulo 0xffff. + * + * Both checksums are stored little-endian. + */ +static int cyapa_gen3_check_fw(struct cyapa *cyapa, const struct firmware *fw) +{ + struct device *dev = &cyapa->client->dev; + u16 csum; + u16 csum_expected; + + /* Firmware must match exact 30848 bytes = 482 64-byte blocks. */ + if (fw->size != CYAPA_FW_SIZE) { + dev_err(dev, "invalid firmware size = %zu, expected %u.\n", + fw->size, CYAPA_FW_SIZE); + return -EINVAL; + } + + /* Verify header block */ + csum_expected = (fw->data[0] << 8) | fw->data[1]; + csum = cyapa_gen3_csum(&fw->data[2], CYAPA_FW_HDR_SIZE - 2); + if (csum != csum_expected) { + dev_err(dev, "%s %04x, expected: %04x\n", + "invalid firmware header checksum = ", + csum, csum_expected); + return -EINVAL; + } + + /* Verify firmware image */ + csum_expected = (fw->data[CYAPA_FW_HDR_SIZE - 2] << 8) | + fw->data[CYAPA_FW_HDR_SIZE - 1]; + csum = cyapa_gen3_csum(&fw->data[CYAPA_FW_HDR_SIZE], + CYAPA_FW_DATA_SIZE); + if (csum != csum_expected) { + dev_err(dev, "%s %04x, expected: %04x\n", + "invalid firmware header checksum = ", + csum, csum_expected); + return -EINVAL; + } + return 0; +} + +/* + * Write a |len| byte long buffer |buf| to the device, by chopping it up into a + * sequence of smaller |CYAPA_CMD_LEN|-length write commands. + * + * The data bytes for a write command are prepended with the 1-byte offset + * of the data relative to the start of |buf|. + */ +static int cyapa_gen3_write_buffer(struct cyapa *cyapa, + const u8 *buf, size_t len) +{ + int error; + size_t i; + unsigned char cmd[CYAPA_CMD_LEN + 1]; + size_t cmd_len; + + for (i = 0; i < len; i += CYAPA_CMD_LEN) { + const u8 *payload = &buf[i]; + + cmd_len = (len - i >= CYAPA_CMD_LEN) ? CYAPA_CMD_LEN : len - i; + cmd[0] = i; + memcpy(&cmd[1], payload, cmd_len); + + error = cyapa_i2c_reg_write_block(cyapa, 0, cmd_len + 1, cmd); + if (error) + return error; + } + return 0; +} + +/* + * A firmware block write command writes 64 bytes of data to a single flash + * page in the device. The 78-byte block write command has the format: + * <0xff> <CMD> <Key> <Start> <Data> <Data-Checksum> <CMD Checksum> + * + * <0xff> - every command starts with 0xff + * <CMD> - the write command value is 0x39 + * <Key> - write commands include an 8-byte key: { 00 01 02 03 04 05 06 07 } + * <Block> - Memory Block number (address / 64) (16-bit, big-endian) + * <Data> - 64 bytes of firmware image data + * <Data Checksum> - sum of 64 <Data> bytes, modulo 0xff + * <CMD Checksum> - sum of 77 bytes, from 0xff to <Data Checksum> + * + * Each write command is split into 5 i2c write transactions of up to 16 bytes. + * Each transaction starts with an i2c register offset: (00, 10, 20, 30, 40). + */ +static int cyapa_gen3_write_fw_block(struct cyapa *cyapa, + u16 block, const u8 *data) +{ + int ret; + struct gen3_write_block_cmd write_block_cmd; + u8 status[BL_STATUS_SIZE]; + int tries; + u8 bl_status, bl_error; + + /* Set write command and security key bytes. */ + write_block_cmd.checksum_seed = GEN3_BL_CMD_CHECKSUM_SEED; + write_block_cmd.cmd_code = GEN3_BL_CMD_WRITE_BLOCK; + memcpy(write_block_cmd.key, security_key, sizeof(security_key)); + put_unaligned_be16(block, &write_block_cmd.block_num); + memcpy(write_block_cmd.block_data, data, CYAPA_FW_BLOCK_SIZE); + write_block_cmd.block_checksum = cyapa_gen3_csum( + write_block_cmd.block_data, CYAPA_FW_BLOCK_SIZE); + write_block_cmd.cmd_checksum = cyapa_gen3_csum((u8 *)&write_block_cmd, + sizeof(write_block_cmd) - 1); + + ret = cyapa_gen3_write_buffer(cyapa, (u8 *)&write_block_cmd, + sizeof(write_block_cmd)); + if (ret) + return ret; + + /* Wait for write to finish */ + tries = 11; /* Programming for one block can take about 100ms. */ + do { + usleep_range(10000, 20000); + + /* Check block write command result status. */ + ret = cyapa_i2c_reg_read_block(cyapa, BL_HEAD_OFFSET, + BL_STATUS_SIZE, status); + if (ret != BL_STATUS_SIZE) + return (ret < 0) ? ret : -EIO; + } while ((status[REG_BL_STATUS] & BL_STATUS_BUSY) && --tries); + + /* Ignore WATCHDOG bit and reserved bits. */ + bl_status = status[REG_BL_STATUS] & ~BL_STATUS_REV_MASK; + bl_error = status[REG_BL_ERROR] & ~BL_ERROR_RESERVED; + + if (bl_status & BL_STATUS_BUSY) + ret = -ETIMEDOUT; + else if (bl_status != BL_STATUS_RUNNING || + bl_error != BL_ERROR_BOOTLOADING) + ret = -EIO; + else + ret = 0; + + return ret; +} + +static int cyapa_gen3_write_blocks(struct cyapa *cyapa, + size_t start_block, size_t block_count, + const u8 *image_data) +{ + int error; + int i; + + for (i = 0; i < block_count; i++) { + size_t block = start_block + i; + size_t addr = i * CYAPA_FW_BLOCK_SIZE; + const u8 *data = &image_data[addr]; + + error = cyapa_gen3_write_fw_block(cyapa, block, data); + if (error) + return error; + } + return 0; +} + +static int cyapa_gen3_do_fw_update(struct cyapa *cyapa, + const struct firmware *fw) +{ + struct device *dev = &cyapa->client->dev; + int error; + + /* First write data, starting at byte 128 of fw->data */ + error = cyapa_gen3_write_blocks(cyapa, + CYAPA_FW_DATA_BLOCK_START, CYAPA_FW_DATA_BLOCK_COUNT, + &fw->data[CYAPA_FW_HDR_BLOCK_COUNT * CYAPA_FW_BLOCK_SIZE]); + if (error) { + dev_err(dev, "FW update aborted, write image: %d\n", error); + return error; + } + + /* Then write checksum */ + error = cyapa_gen3_write_blocks(cyapa, + CYAPA_FW_HDR_BLOCK_START, CYAPA_FW_HDR_BLOCK_COUNT, + &fw->data[0]); + if (error) { + dev_err(dev, "FW update aborted, write checksum: %d\n", error); + return error; + } + + return 0; +} + +static ssize_t cyapa_gen3_do_calibrate(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + int tries; + int ret; + + ret = cyapa_read_byte(cyapa, CYAPA_CMD_DEV_STATUS); + if (ret < 0) { + dev_err(dev, "Error reading dev status: %d\n", ret); + goto out; + } + if ((ret & CYAPA_DEV_NORMAL) != CYAPA_DEV_NORMAL) { + dev_warn(dev, "Trackpad device is busy, device state: 0x%02x\n", + ret); + ret = -EAGAIN; + goto out; + } + + ret = cyapa_write_byte(cyapa, CYAPA_CMD_SOFT_RESET, + OP_RECALIBRATION_MASK); + if (ret < 0) { + dev_err(dev, "Failed to send calibrate command: %d\n", + ret); + goto out; + } + + tries = 20; /* max recalibration timeout 2s. */ + do { + /* + * For this recalibration, the max time will not exceed 2s. + * The average time is approximately 500 - 700 ms, and we + * will check the status every 100 - 200ms. + */ + usleep_range(100000, 200000); + + ret = cyapa_read_byte(cyapa, CYAPA_CMD_DEV_STATUS); + if (ret < 0) { + dev_err(dev, "Error reading dev status: %d\n", + ret); + goto out; + } + if ((ret & CYAPA_DEV_NORMAL) == CYAPA_DEV_NORMAL) + break; + } while (--tries); + + if (tries == 0) { + dev_err(dev, "Failed to calibrate. Timeout.\n"); + ret = -ETIMEDOUT; + goto out; + } + dev_dbg(dev, "Calibration successful.\n"); + +out: + return ret < 0 ? ret : count; +} + +static ssize_t cyapa_gen3_show_baseline(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + int max_baseline, min_baseline; + int tries; + int ret; + + ret = cyapa_read_byte(cyapa, CYAPA_CMD_DEV_STATUS); + if (ret < 0) { + dev_err(dev, "Error reading dev status. err = %d\n", ret); + goto out; + } + if ((ret & CYAPA_DEV_NORMAL) != CYAPA_DEV_NORMAL) { + dev_warn(dev, "Trackpad device is busy. device state = 0x%x\n", + ret); + ret = -EAGAIN; + goto out; + } + + ret = cyapa_write_byte(cyapa, CYAPA_CMD_SOFT_RESET, + OP_REPORT_BASELINE_MASK); + if (ret < 0) { + dev_err(dev, "Failed to send report baseline command. %d\n", + ret); + goto out; + } + + tries = 3; /* Try for 30 to 60 ms */ + do { + usleep_range(10000, 20000); + + ret = cyapa_read_byte(cyapa, CYAPA_CMD_DEV_STATUS); + if (ret < 0) { + dev_err(dev, "Error reading dev status. err = %d\n", + ret); + goto out; + } + if ((ret & CYAPA_DEV_NORMAL) == CYAPA_DEV_NORMAL) + break; + } while (--tries); + + if (tries == 0) { + dev_err(dev, "Device timed out going to Normal state.\n"); + ret = -ETIMEDOUT; + goto out; + } + + ret = cyapa_read_byte(cyapa, CYAPA_CMD_MAX_BASELINE); + if (ret < 0) { + dev_err(dev, "Failed to read max baseline. err = %d\n", ret); + goto out; + } + max_baseline = ret; + + ret = cyapa_read_byte(cyapa, CYAPA_CMD_MIN_BASELINE); + if (ret < 0) { + dev_err(dev, "Failed to read min baseline. err = %d\n", ret); + goto out; + } + min_baseline = ret; + + dev_dbg(dev, "Baseline report successful. Max: %d Min: %d\n", + max_baseline, min_baseline); + ret = scnprintf(buf, PAGE_SIZE, "%d %d\n", max_baseline, min_baseline); + +out: + return ret; +} + +/* + * cyapa_get_wait_time_for_pwr_cmd + * + * Compute the amount of time we need to wait after updating the touchpad + * power mode. The touchpad needs to consume the incoming power mode set + * command at the current clock rate. + */ + +static u16 cyapa_get_wait_time_for_pwr_cmd(u8 pwr_mode) +{ + switch (pwr_mode) { + case PWR_MODE_FULL_ACTIVE: return 20; + case PWR_MODE_BTN_ONLY: return 20; + case PWR_MODE_OFF: return 20; + default: return cyapa_pwr_cmd_to_sleep_time(pwr_mode) + 50; + } +} + +/* + * Set device power mode + * + * Write to the field to configure power state. Power states include : + * Full : Max scans and report rate. + * Idle : Report rate set by user specified time. + * ButtonOnly : No scans for fingers. When the button is triggered, + * a slave interrupt is asserted to notify host to wake up. + * Off : Only awake for i2c commands from host. No function for button + * or touch sensors. + * + * The power_mode command should conform to the following : + * Full : 0x3f + * Idle : Configurable from 20 to 1000ms. See note below for + * cyapa_sleep_time_to_pwr_cmd and cyapa_pwr_cmd_to_sleep_time + * ButtonOnly : 0x01 + * Off : 0x00 + * + * Device power mode can only be set when device is in operational mode. + */ +static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode, + u16 always_unused) +{ + int ret; + u8 power; + int tries; + u16 sleep_time; + + always_unused = 0; + if (cyapa->state != CYAPA_STATE_OP) + return 0; + + tries = SET_POWER_MODE_TRIES; + while (tries--) { + ret = cyapa_read_byte(cyapa, CYAPA_CMD_POWER_MODE); + if (ret >= 0) + break; + usleep_range(SET_POWER_MODE_DELAY, 2 * SET_POWER_MODE_DELAY); + } + if (ret < 0) + return ret; + + /* + * Return early if the power mode to set is the same as the current + * one. + */ + if ((ret & PWR_MODE_MASK) == power_mode) + return 0; + + sleep_time = cyapa_get_wait_time_for_pwr_cmd(ret & PWR_MODE_MASK); + power = ret; + power &= ~PWR_MODE_MASK; + power |= power_mode & PWR_MODE_MASK; + tries = SET_POWER_MODE_TRIES; + while (tries--) { + ret = cyapa_write_byte(cyapa, CYAPA_CMD_POWER_MODE, power); + if (!ret) + break; + usleep_range(SET_POWER_MODE_DELAY, 2 * SET_POWER_MODE_DELAY); + } + + /* + * Wait for the newly set power command to go in at the previous + * clock speed (scanrate) used by the touchpad firmware. Not + * doing so before issuing the next command may result in errors + * depending on the command's content. + */ + msleep(sleep_time); + return ret; +} + +static int cyapa_gen3_get_query_data(struct cyapa *cyapa) +{ + u8 query_data[QUERY_DATA_SIZE]; + int ret; + + if (cyapa->state != CYAPA_STATE_OP) + return -EBUSY; + + ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_QUERY, query_data); + if (ret != QUERY_DATA_SIZE) + return (ret < 0) ? ret : -EIO; + + memcpy(&cyapa->product_id[0], &query_data[0], 5); + cyapa->product_id[5] = '-'; + memcpy(&cyapa->product_id[6], &query_data[5], 6); + cyapa->product_id[12] = '-'; + memcpy(&cyapa->product_id[13], &query_data[11], 2); + cyapa->product_id[15] = '\0'; + + cyapa->fw_maj_ver = query_data[15]; + cyapa->fw_min_ver = query_data[16]; + + cyapa->btn_capability = query_data[19] & CAPABILITY_BTN_MASK; + + cyapa->gen = query_data[20] & 0x0f; + + cyapa->max_abs_x = ((query_data[21] & 0xf0) << 4) | query_data[22]; + cyapa->max_abs_y = ((query_data[21] & 0x0f) << 8) | query_data[23]; + + cyapa->physical_size_x = + ((query_data[24] & 0xf0) << 4) | query_data[25]; + cyapa->physical_size_y = + ((query_data[24] & 0x0f) << 8) | query_data[26]; + + cyapa->max_z = 255; + + return 0; +} + +static int cyapa_gen3_bl_query_data(struct cyapa *cyapa) +{ + u8 bl_data[CYAPA_CMD_LEN]; + int ret; + + ret = cyapa_i2c_reg_read_block(cyapa, 0, CYAPA_CMD_LEN, bl_data); + if (ret != CYAPA_CMD_LEN) + return (ret < 0) ? ret : -EIO; + + /* + * This value will be updated again when entered application mode. + * If TP failed to enter application mode, this fw version values + * can be used as a reference. + * This firmware version valid when fw image checksum is valid. + */ + if (bl_data[REG_BL_STATUS] == + (BL_STATUS_RUNNING | BL_STATUS_CSUM_VALID)) { + cyapa->fw_maj_ver = bl_data[GEN3_BL_IDLE_FW_MAJ_VER_OFFSET]; + cyapa->fw_min_ver = bl_data[GEN3_BL_IDLE_FW_MIN_VER_OFFSET]; + } + + return 0; +} + +/* + * Check if device is operational. + * + * An operational device is responding, has exited bootloader, and has + * firmware supported by this driver. + * + * Returns: + * -EBUSY no device or in bootloader + * -EIO failure while reading from device + * -EAGAIN device is still in bootloader + * if ->state = CYAPA_STATE_BL_IDLE, device has invalid firmware + * -EINVAL device is in operational mode, but not supported by this driver + * 0 device is supported + */ +static int cyapa_gen3_do_operational_check(struct cyapa *cyapa) +{ + struct device *dev = &cyapa->client->dev; + int error; + + switch (cyapa->state) { + case CYAPA_STATE_BL_ACTIVE: + error = cyapa_gen3_bl_deactivate(cyapa); + if (error) { + dev_err(dev, "failed to bl_deactivate: %d\n", error); + return error; + } + + /* Fallthrough state */ + case CYAPA_STATE_BL_IDLE: + /* Try to get firmware version in bootloader mode. */ + cyapa_gen3_bl_query_data(cyapa); + + error = cyapa_gen3_bl_exit(cyapa); + if (error) { + dev_err(dev, "failed to bl_exit: %d\n", error); + return error; + } + + /* Fallthrough state */ + case CYAPA_STATE_OP: + /* + * Reading query data before going back to the full mode + * may cause problems, so we set the power mode first here. + */ + error = cyapa_gen3_set_power_mode(cyapa, + PWR_MODE_FULL_ACTIVE, 0); + if (error) + dev_err(dev, "%s: set full power mode failed: %d\n", + __func__, error); + error = cyapa_gen3_get_query_data(cyapa); + if (error < 0) + return error; + + /* Only support firmware protocol gen3 */ + if (cyapa->gen != CYAPA_GEN3) { + dev_err(dev, "unsupported protocol version (%d)", + cyapa->gen); + return -EINVAL; + } + + /* Only support product ID starting with CYTRA */ + if (memcmp(cyapa->product_id, product_id, + strlen(product_id)) != 0) { + dev_err(dev, "unsupported product ID (%s)\n", + cyapa->product_id); + return -EINVAL; + } + + return 0; + + default: + return -EIO; + } + return 0; +} + +/* + * Return false, do not continue process + * Return true, continue process. + */ +static bool cyapa_gen3_irq_cmd_handler(struct cyapa *cyapa) +{ + /* Not gen3 irq command response, skip for continue. */ + if (cyapa->gen != CYAPA_GEN3) + return true; + + if (cyapa->operational) + return true; + + /* + * Driver in detecting or other interface function processing, + * so, stop cyapa_gen3_irq_handler to continue process to + * avoid unwanted to error detecting and processing. + * + * And also, avoid the periodicly accerted interrupts to be processed + * as touch inputs when gen3 failed to launch into application mode, + * which will cause gen3 stays in bootloader mode. + */ + return false; +} + +static int cyapa_gen3_irq_handler(struct cyapa *cyapa) +{ + struct input_dev *input = cyapa->input; + struct device *dev = &cyapa->client->dev; + struct cyapa_reg_data data; + int num_fingers; + int ret; + int i; + + ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data); + if (ret != sizeof(data)) { + dev_err(dev, "failed to read report data, (%d)\n", ret); + return -EINVAL; + } + + if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC || + (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL || + (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) { + dev_err(dev, "invalid device state bytes, %02x %02x\n", + data.device_status, data.finger_btn); + return -EINVAL; + } + + num_fingers = (data.finger_btn >> 4) & 0x0f; + for (i = 0; i < num_fingers; i++) { + const struct cyapa_touch *touch = &data.touches[i]; + /* Note: touch->id range is 1 to 15; slots are 0 to 14. */ + int slot = touch->id - 1; + + input_mt_slot(input, slot); + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); + input_report_abs(input, ABS_MT_POSITION_X, + ((touch->xy_hi & 0xf0) << 4) | touch->x_lo); + input_report_abs(input, ABS_MT_POSITION_Y, + ((touch->xy_hi & 0x0f) << 8) | touch->y_lo); + input_report_abs(input, ABS_MT_PRESSURE, touch->pressure); + } + + input_mt_sync_frame(input); + + if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK) + input_report_key(input, BTN_LEFT, + !!(data.finger_btn & OP_DATA_LEFT_BTN)); + if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK) + input_report_key(input, BTN_MIDDLE, + !!(data.finger_btn & OP_DATA_MIDDLE_BTN)); + if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK) + input_report_key(input, BTN_RIGHT, + !!(data.finger_btn & OP_DATA_RIGHT_BTN)); + input_sync(input); + + return 0; +} + +static int cyapa_gen3_initialize(struct cyapa *cyapa) { return 0; } +static int cyapa_gen3_bl_initiate(struct cyapa *cyapa, + const struct firmware *fw) { return 0; } +static int cyapa_gen3_empty_output_data(struct cyapa *cyapa, + u8 *buf, int *len, cb_sort func) { return 0; } + +const struct cyapa_dev_ops cyapa_gen3_ops = { + .check_fw = cyapa_gen3_check_fw, + .bl_enter = cyapa_gen3_bl_enter, + .bl_activate = cyapa_gen3_bl_activate, + .update_fw = cyapa_gen3_do_fw_update, + .bl_deactivate = cyapa_gen3_bl_deactivate, + .bl_initiate = cyapa_gen3_bl_initiate, + + .show_baseline = cyapa_gen3_show_baseline, + .calibrate_store = cyapa_gen3_do_calibrate, + + .initialize = cyapa_gen3_initialize, + + .state_parse = cyapa_gen3_state_parse, + .operational_check = cyapa_gen3_do_operational_check, + + .irq_handler = cyapa_gen3_irq_handler, + .irq_cmd_handler = cyapa_gen3_irq_cmd_handler, + .sort_empty_output_data = cyapa_gen3_empty_output_data, + .set_power_mode = cyapa_gen3_set_power_mode, +}; diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c new file mode 100644 index 000000000000..ddf5393a1180 --- /dev/null +++ b/drivers/input/mouse/cyapa_gen5.c @@ -0,0 +1,2777 @@ +/* + * Cypress APA trackpad with I2C interface + * + * Author: Dudley Du <dudl@cypress.com> + * + * Copyright (C) 2014 Cypress Semiconductor, Inc. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/mutex.h> +#include <linux/completion.h> +#include <linux/slab.h> +#include <linux/unaligned/access_ok.h> +#include <linux/crc-itu-t.h> +#include "cyapa.h" + + +/* Macro of Gen5 */ +#define RECORD_EVENT_NONE 0 +#define RECORD_EVENT_TOUCHDOWN 1 +#define RECORD_EVENT_DISPLACE 2 +#define RECORD_EVENT_LIFTOFF 3 + +#define CYAPA_TSG_FLASH_MAP_BLOCK_SIZE 0x80 +#define CYAPA_TSG_IMG_FW_HDR_SIZE 13 +#define CYAPA_TSG_FW_ROW_SIZE (CYAPA_TSG_FLASH_MAP_BLOCK_SIZE) +#define CYAPA_TSG_IMG_START_ROW_NUM 0x002e +#define CYAPA_TSG_IMG_END_ROW_NUM 0x01fe +#define CYAPA_TSG_IMG_APP_INTEGRITY_ROW_NUM 0x01ff +#define CYAPA_TSG_IMG_MAX_RECORDS (CYAPA_TSG_IMG_END_ROW_NUM - \ + CYAPA_TSG_IMG_START_ROW_NUM + 1 + 1) +#define CYAPA_TSG_IMG_READ_SIZE (CYAPA_TSG_FLASH_MAP_BLOCK_SIZE / 2) +#define CYAPA_TSG_START_OF_APPLICATION 0x1700 +#define CYAPA_TSG_APP_INTEGRITY_SIZE 60 +#define CYAPA_TSG_FLASH_MAP_METADATA_SIZE 60 +#define CYAPA_TSG_BL_KEY_SIZE 8 + +#define CYAPA_TSG_MAX_CMD_SIZE 256 + +#define GEN5_BL_CMD_VERIFY_APP_INTEGRITY 0x31 +#define GEN5_BL_CMD_GET_BL_INFO 0x38 +#define GEN5_BL_CMD_PROGRAM_VERIFY_ROW 0x39 +#define GEN5_BL_CMD_LAUNCH_APP 0x3b +#define GEN5_BL_CMD_INITIATE_BL 0x48 + +#define GEN5_HID_DESCRIPTOR_ADDR 0x0001 +#define GEN5_REPORT_DESCRIPTOR_ADDR 0x0002 +#define GEN5_INPUT_REPORT_ADDR 0x0003 +#define GEN5_OUTPUT_REPORT_ADDR 0x0004 +#define GEN5_CMD_DATA_ADDR 0x0006 + +#define GEN5_TOUCH_REPORT_HEAD_SIZE 7 +#define GEN5_TOUCH_REPORT_MAX_SIZE 127 +#define GEN5_BTN_REPORT_HEAD_SIZE 6 +#define GEN5_BTN_REPORT_MAX_SIZE 14 +#define GEN5_WAKEUP_EVENT_SIZE 4 +#define GEN5_RAW_DATA_HEAD_SIZE 24 + +#define GEN5_BL_CMD_REPORT_ID 0x40 +#define GEN5_BL_RESP_REPORT_ID 0x30 +#define GEN5_APP_CMD_REPORT_ID 0x2f +#define GEN5_APP_RESP_REPORT_ID 0x1f + +#define GEN5_APP_DEEP_SLEEP_REPORT_ID 0xf0 +#define GEN5_DEEP_SLEEP_RESP_LENGTH 5 + +#define GEN5_CMD_GET_PARAMETER 0x05 +#define GEN5_CMD_SET_PARAMETER 0x06 +#define GEN5_PARAMETER_ACT_INTERVL_ID 0x4d +#define GEN5_PARAMETER_ACT_INTERVL_SIZE 1 +#define GEN5_PARAMETER_ACT_LFT_INTERVL_ID 0x4f +#define GEN5_PARAMETER_ACT_LFT_INTERVL_SIZE 2 +#define GEN5_PARAMETER_LP_INTRVL_ID 0x4c +#define GEN5_PARAMETER_LP_INTRVL_SIZE 2 + +#define GEN5_PARAMETER_DISABLE_PIP_REPORT 0x08 + +#define GEN5_POWER_STATE_ACTIVE 0x01 +#define GEN5_POWER_STATE_LOOK_FOR_TOUCH 0x02 +#define GEN5_POWER_STATE_READY 0x03 +#define GEN5_POWER_STATE_IDLE 0x04 +#define GEN5_POWER_STATE_BTN_ONLY 0x05 +#define GEN5_POWER_STATE_OFF 0x06 + +#define GEN5_DEEP_SLEEP_STATE_MASK 0x03 +#define GEN5_DEEP_SLEEP_STATE_ON 0x00 +#define GEN5_DEEP_SLEEP_STATE_OFF 0x01 + +#define GEN5_DEEP_SLEEP_OPCODE 0x08 +#define GEN5_DEEP_SLEEP_OPCODE_MASK 0x0f + +#define GEN5_POWER_READY_MAX_INTRVL_TIME 50 /* Unit: ms */ +#define GEN5_POWER_IDLE_MAX_INTRVL_TIME 250 /* Unit: ms */ + +#define GEN5_CMD_REPORT_ID_OFFSET 4 + +#define GEN5_RESP_REPORT_ID_OFFSET 2 +#define GEN5_RESP_RSVD_OFFSET 3 +#define GEN5_RESP_RSVD_KEY 0x00 +#define GEN5_RESP_BL_SOP_OFFSET 4 +#define GEN5_SOP_KEY 0x01 /* Start of Packet */ +#define GEN5_EOP_KEY 0x17 /* End of Packet */ +#define GEN5_RESP_APP_CMD_OFFSET 4 +#define GET_GEN5_CMD_CODE(reg) ((reg) & 0x7f) + +#define VALID_CMD_RESP_HEADER(resp, cmd) \ + (((resp)[GEN5_RESP_REPORT_ID_OFFSET] == GEN5_APP_RESP_REPORT_ID) && \ + ((resp)[GEN5_RESP_RSVD_OFFSET] == GEN5_RESP_RSVD_KEY) && \ + (GET_GEN5_CMD_CODE((resp)[GEN5_RESP_APP_CMD_OFFSET]) == (cmd))) + +#define GEN5_MIN_BL_CMD_LENGTH 13 +#define GEN5_MIN_BL_RESP_LENGTH 11 +#define GEN5_MIN_APP_CMD_LENGTH 7 +#define GEN5_MIN_APP_RESP_LENGTH 5 +#define GEN5_UNSUPPORTED_CMD_RESP_LENGTH 6 + +#define GEN5_RESP_LENGTH_OFFSET 0x00 +#define GEN5_RESP_LENGTH_SIZE 2 + +#define GEN5_HID_DESCRIPTOR_SIZE 32 +#define GEN5_BL_HID_REPORT_ID 0xff +#define GEN5_APP_HID_REPORT_ID 0xf7 +#define GEN5_BL_MAX_OUTPUT_LENGTH 0x0100 +#define GEN5_APP_MAX_OUTPUT_LENGTH 0x00fe + +#define GEN5_BL_REPORT_DESCRIPTOR_SIZE 0x1d +#define GEN5_BL_REPORT_DESCRIPTOR_ID 0xfe +#define GEN5_APP_REPORT_DESCRIPTOR_SIZE 0xee +#define GEN5_APP_CONTRACT_REPORT_DESCRIPTOR_SIZE 0xfa +#define GEN5_APP_REPORT_DESCRIPTOR_ID 0xf6 + +#define GEN5_TOUCH_REPORT_ID 0x01 +#define GEN5_BTN_REPORT_ID 0x03 +#define GEN5_WAKEUP_EVENT_REPORT_ID 0x04 +#define GEN5_OLD_PUSH_BTN_REPORT_ID 0x05 +#define GEN5_PUSH_BTN_REPORT_ID 0x06 + +#define GEN5_CMD_COMPLETE_SUCCESS(status) ((status) == 0x00) + +#define GEN5_BL_INITIATE_RESP_LEN 11 +#define GEN5_BL_FAIL_EXIT_RESP_LEN 11 +#define GEN5_BL_FAIL_EXIT_STATUS_CODE 0x0c +#define GEN5_BL_VERIFY_INTEGRITY_RESP_LEN 12 +#define GEN5_BL_INTEGRITY_CHEKC_PASS 0x00 +#define GEN5_BL_BLOCK_WRITE_RESP_LEN 11 +#define GEN5_BL_READ_APP_INFO_RESP_LEN 31 +#define GEN5_CMD_CALIBRATE 0x28 +#define CYAPA_SENSING_MODE_MUTUAL_CAP_FINE 0x00 +#define CYAPA_SENSING_MODE_SELF_CAP 0x02 + +#define GEN5_CMD_RETRIEVE_DATA_STRUCTURE 0x24 +#define GEN5_RETRIEVE_MUTUAL_PWC_DATA 0x00 +#define GEN5_RETRIEVE_SELF_CAP_PWC_DATA 0x01 + +#define GEN5_RETRIEVE_DATA_ELEMENT_SIZE_MASK 0x07 + +#define GEN5_CMD_EXECUTE_PANEL_SCAN 0x2a +#define GEN5_CMD_RETRIEVE_PANEL_SCAN 0x2b +#define GEN5_PANEL_SCAN_MUTUAL_RAW_DATA 0x00 +#define GEN5_PANEL_SCAN_MUTUAL_BASELINE 0x01 +#define GEN5_PANEL_SCAN_MUTUAL_DIFFCOUNT 0x02 +#define GEN5_PANEL_SCAN_SELF_RAW_DATA 0x03 +#define GEN5_PANEL_SCAN_SELF_BASELINE 0x04 +#define GEN5_PANEL_SCAN_SELF_DIFFCOUNT 0x05 + +/* The offset only valid for reterive PWC and panel scan commands */ +#define GEN5_RESP_DATA_STRUCTURE_OFFSET 10 +#define GEN5_PWC_DATA_ELEMENT_SIZE_MASK 0x07 + +#define GEN5_NUMBER_OF_TOUCH_OFFSET 5 +#define GEN5_NUMBER_OF_TOUCH_MASK 0x1f +#define GEN5_BUTTONS_OFFSET 5 +#define GEN5_BUTTONS_MASK 0x0f +#define GEN5_GET_EVENT_ID(reg) (((reg) >> 5) & 0x03) +#define GEN5_GET_TOUCH_ID(reg) ((reg) & 0x1f) + +#define GEN5_PRODUCT_FAMILY_MASK 0xf000 +#define GEN5_PRODUCT_FAMILY_TRACKPAD 0x1000 + +#define TSG_INVALID_CMD 0xff + +struct cyapa_gen5_touch_record { + /* + * Bit 7 - 3: reserved + * Bit 2 - 0: touch type; + * 0 : standard finger; + * 1 - 15 : reserved. + */ + u8 touch_type; + + /* + * Bit 7: indicates touch liftoff status. + * 0 : touch is currently on the panel. + * 1 : touch record indicates a liftoff. + * Bit 6 - 5: indicates an event associated with this touch instance + * 0 : no event + * 1 : touchdown + * 2 : significant displacement (> active distance) + * 3 : liftoff (record reports last known coordinates) + * Bit 4 - 0: An arbitrary ID tag associated with a finger + * to allow tracking a touch as it moves around the panel. + */ + u8 touch_tip_event_id; + + /* Bit 7 - 0 of X-axis coordinate of the touch in pixel. */ + u8 x_lo; + + /* Bit 15 - 8 of X-axis coordinate of the touch in pixel. */ + u8 x_hi; + + /* Bit 7 - 0 of Y-axis coordinate of the touch in pixel. */ + u8 y_lo; + + /* Bit 15 - 8 of Y-axis coordinate of the touch in pixel. */ + u8 y_hi; + + /* Touch intensity in counts, pressure value. */ + u8 z; + + /* + * The length of the major axis of the ellipse of contact between + * the finger and the panel (ABS_MT_TOUCH_MAJOR). + */ + u8 major_axis_len; + + /* + * The length of the minor axis of the ellipse of contact between + * the finger and the panel (ABS_MT_TOUCH_MINOR). + */ + u8 minor_axis_len; + + /* + * The length of the major axis of the approaching tool. + * (ABS_MT_WIDTH_MAJOR) + */ + u8 major_tool_len; + + /* + * The length of the minor axis of the approaching tool. + * (ABS_MT_WIDTH_MINOR) + */ + u8 minor_tool_len; + + /* + * The angle between the panel vertical axis and + * the major axis of the contact ellipse. This value is an 8-bit + * signed integer. The range is -127 to +127 (corresponding to + * -90 degree and +90 degree respectively). + * The positive direction is clockwise from the vertical axis. + * If the ellipse of contact degenerates into a circle, + * orientation is reported as 0. + */ + u8 orientation; +} __packed; + +struct cyapa_gen5_report_data { + u8 report_head[GEN5_TOUCH_REPORT_HEAD_SIZE]; + struct cyapa_gen5_touch_record touch_records[10]; +} __packed; + +struct cyapa_tsg_bin_image_head { + u8 head_size; /* Unit: bytes, including itself. */ + u8 ttda_driver_major_version; /* Reserved as 0. */ + u8 ttda_driver_minor_version; /* Reserved as 0. */ + u8 fw_major_version; + u8 fw_minor_version; + u8 fw_revision_control_number[8]; +} __packed; + +struct cyapa_tsg_bin_image_data_record { + u8 flash_array_id; + __be16 row_number; + /* The number of bytes of flash data contained in this record. */ + __be16 record_len; + /* The flash program data. */ + u8 record_data[CYAPA_TSG_FW_ROW_SIZE]; +} __packed; + +struct cyapa_tsg_bin_image { + struct cyapa_tsg_bin_image_head image_head; + struct cyapa_tsg_bin_image_data_record records[0]; +} __packed; + +struct gen5_bl_packet_start { + u8 sop; /* Start of packet, must be 01h */ + u8 cmd_code; + __le16 data_length; /* Size of data parameter start from data[0] */ +} __packed; + +struct gen5_bl_packet_end { + __le16 crc; + u8 eop; /* End of packet, must be 17h */ +} __packed; + +struct gen5_bl_cmd_head { + __le16 addr; /* Output report register address, must be 0004h */ + /* Size of packet not including output report register address */ + __le16 length; + u8 report_id; /* Bootloader output report id, must be 40h */ + u8 rsvd; /* Reserved, must be 0 */ + struct gen5_bl_packet_start packet_start; + u8 data[0]; /* Command data variable based on commands */ +} __packed; + +/* Initiate bootload command data structure. */ +struct gen5_bl_initiate_cmd_data { + /* Key must be "A5h 01h 02h 03h FFh FEh FDh 5Ah" */ + u8 key[CYAPA_TSG_BL_KEY_SIZE]; + u8 metadata_raw_parameter[CYAPA_TSG_FLASH_MAP_METADATA_SIZE]; + __le16 metadata_crc; +} __packed; + +struct gen5_bl_metadata_row_params { + __le16 size; + __le16 maximum_size; + __le32 app_start; + __le16 app_len; + __le16 app_crc; + __le32 app_entry; + __le32 upgrade_start; + __le16 upgrade_len; + __le16 entry_row_crc; + u8 padding[36]; /* Padding data must be 0 */ + __le16 metadata_crc; /* CRC starts at offset of 60 */ +} __packed; + +/* Bootload program and verify row command data structure */ +struct gen5_bl_flash_row_head { + u8 flash_array_id; + __le16 flash_row_id; + u8 flash_data[0]; +} __packed; + +struct gen5_app_cmd_head { + __le16 addr; /* Output report register address, must be 0004h */ + /* Size of packet not including output report register address */ + __le16 length; + u8 report_id; /* Application output report id, must be 2Fh */ + u8 rsvd; /* Reserved, must be 0 */ + /* + * Bit 7: reserved, must be 0. + * Bit 6-0: command code. + */ + u8 cmd_code; + u8 parameter_data[0]; /* Parameter data variable based on cmd_code */ +} __packed; + +/* Applicaton get/set parameter command data structure */ +struct gen5_app_set_parameter_data { + u8 parameter_id; + u8 parameter_size; + __le32 value; +} __packed; + +struct gen5_app_get_parameter_data { + u8 parameter_id; +} __packed; + +struct gen5_retrieve_panel_scan_data { + __le16 read_offset; + __le16 read_elements; + u8 data_id; +} __packed; + +/* Variables to record latest gen5 trackpad power states. */ +#define GEN5_DEV_SET_PWR_STATE(cyapa, s) ((cyapa)->dev_pwr_mode = (s)) +#define GEN5_DEV_GET_PWR_STATE(cyapa) ((cyapa)->dev_pwr_mode) +#define GEN5_DEV_SET_SLEEP_TIME(cyapa, t) ((cyapa)->dev_sleep_time = (t)) +#define GEN5_DEV_GET_SLEEP_TIME(cyapa) ((cyapa)->dev_sleep_time) +#define GEN5_DEV_UNINIT_SLEEP_TIME(cyapa) \ + (((cyapa)->dev_sleep_time) == UNINIT_SLEEP_TIME) + + +static u8 cyapa_gen5_bl_cmd_key[] = { 0xa5, 0x01, 0x02, 0x03, + 0xff, 0xfe, 0xfd, 0x5a }; + +static int cyapa_gen5_initialize(struct cyapa *cyapa) +{ + struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5; + + init_completion(&gen5_pip->cmd_ready); + atomic_set(&gen5_pip->cmd_issued, 0); + mutex_init(&gen5_pip->cmd_lock); + + gen5_pip->resp_sort_func = NULL; + gen5_pip->in_progress_cmd = TSG_INVALID_CMD; + gen5_pip->resp_data = NULL; + gen5_pip->resp_len = NULL; + + cyapa->dev_pwr_mode = UNINIT_PWR_MODE; + cyapa->dev_sleep_time = UNINIT_SLEEP_TIME; + + return 0; +} + +/* Return negative errno, or else the number of bytes read. */ +static ssize_t cyapa_i2c_pip_read(struct cyapa *cyapa, u8 *buf, size_t size) +{ + int ret; + + if (size == 0) + return 0; + + if (!buf || size > CYAPA_REG_MAP_SIZE) + return -EINVAL; + + ret = i2c_master_recv(cyapa->client, buf, size); + + if (ret != size) + return (ret < 0) ? ret : -EIO; + + return size; +} + +/** + * Return a negative errno code else zero on success. + */ +static ssize_t cyapa_i2c_pip_write(struct cyapa *cyapa, u8 *buf, size_t size) +{ + int ret; + + if (!buf || !size) + return -EINVAL; + + ret = i2c_master_send(cyapa->client, buf, size); + + if (ret != size) + return (ret < 0) ? ret : -EIO; + + return 0; +} + +/** + * This function is aimed to dump all not read data in Gen5 trackpad + * before send any command, otherwise, the interrupt line will be blocked. + */ +static int cyapa_empty_pip_output_data(struct cyapa *cyapa, + u8 *buf, int *len, cb_sort func) +{ + struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5; + int length; + int report_count; + int empty_count; + int buf_len; + int error; + + buf_len = 0; + if (len) { + buf_len = (*len < CYAPA_REG_MAP_SIZE) ? + *len : CYAPA_REG_MAP_SIZE; + *len = 0; + } + + report_count = 8; /* max 7 pending data before command response data */ + empty_count = 0; + do { + /* + * Depending on testing in cyapa driver, there are max 5 "02 00" + * packets between two valid buffered data report in firmware. + * So in order to dump all buffered data out and + * make interrupt line release for reassert again, + * we must set the empty_count check value bigger than 5 to + * make it work. Otherwise, in some situation, + * the interrupt line may unable to reactive again, + * which will cause trackpad device unable to + * report data any more. + * for example, it may happen in EFT and ESD testing. + */ + if (empty_count > 5) + return 0; + + error = cyapa_i2c_pip_read(cyapa, gen5_pip->empty_buf, + GEN5_RESP_LENGTH_SIZE); + if (error < 0) + return error; + + length = get_unaligned_le16(gen5_pip->empty_buf); + if (length == GEN5_RESP_LENGTH_SIZE) { + empty_count++; + continue; + } else if (length > CYAPA_REG_MAP_SIZE) { + /* Should not happen */ + return -EINVAL; + } else if (length == 0) { + /* Application or bootloader launch data polled out. */ + length = GEN5_RESP_LENGTH_SIZE; + if (buf && buf_len && func && + func(cyapa, gen5_pip->empty_buf, length)) { + length = min(buf_len, length); + memcpy(buf, gen5_pip->empty_buf, length); + *len = length; + /* Response found, success. */ + return 0; + } + continue; + } + + error = cyapa_i2c_pip_read(cyapa, gen5_pip->empty_buf, length); + if (error < 0) + return error; + + report_count--; + empty_count = 0; + length = get_unaligned_le16(gen5_pip->empty_buf); + if (length <= GEN5_RESP_LENGTH_SIZE) { + empty_count++; + } else if (buf && buf_len && func && + func(cyapa, gen5_pip->empty_buf, length)) { + length = min(buf_len, length); + memcpy(buf, gen5_pip->empty_buf, length); + *len = length; + /* Response found, success. */ + return 0; + } + + error = -EINVAL; + } while (report_count); + + return error; +} + +static int cyapa_do_i2c_pip_cmd_irq_sync( + struct cyapa *cyapa, + u8 *cmd, size_t cmd_len, + unsigned long timeout) +{ + struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5; + int error; + + /* Wait for interrupt to set ready completion */ + init_completion(&gen5_pip->cmd_ready); + + atomic_inc(&gen5_pip->cmd_issued); + error = cyapa_i2c_pip_write(cyapa, cmd, cmd_len); + if (error) { + atomic_dec(&gen5_pip->cmd_issued); + return (error < 0) ? error : -EIO; + } + + /* Wait for interrupt to indicate command is completed. */ + timeout = wait_for_completion_timeout(&gen5_pip->cmd_ready, + msecs_to_jiffies(timeout)); + if (timeout == 0) { + atomic_dec(&gen5_pip->cmd_issued); + return -ETIMEDOUT; + } + + return 0; +} + +static int cyapa_do_i2c_pip_cmd_polling( + struct cyapa *cyapa, + u8 *cmd, size_t cmd_len, + u8 *resp_data, int *resp_len, + unsigned long timeout, + cb_sort func) +{ + struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5; + int tries; + int length; + int error; + + atomic_inc(&gen5_pip->cmd_issued); + error = cyapa_i2c_pip_write(cyapa, cmd, cmd_len); + if (error) { + atomic_dec(&gen5_pip->cmd_issued); + return error < 0 ? error : -EIO; + } + + length = resp_len ? *resp_len : 0; + if (resp_data && resp_len && length != 0 && func) { + tries = timeout / 5; + do { + usleep_range(3000, 5000); + *resp_len = length; + error = cyapa_empty_pip_output_data(cyapa, + resp_data, resp_len, func); + if (error || *resp_len == 0) + continue; + else + break; + } while (--tries > 0); + if ((error || *resp_len == 0) || tries <= 0) + error = error ? error : -ETIMEDOUT; + } + + atomic_dec(&gen5_pip->cmd_issued); + return error; +} + +static int cyapa_i2c_pip_cmd_irq_sync( + struct cyapa *cyapa, + u8 *cmd, int cmd_len, + u8 *resp_data, int *resp_len, + unsigned long timeout, + cb_sort func, + bool irq_mode) +{ + struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5; + int error; + + if (!cmd || !cmd_len) + return -EINVAL; + + /* Commands must be serialized. */ + error = mutex_lock_interruptible(&gen5_pip->cmd_lock); + if (error) + return error; + + gen5_pip->resp_sort_func = func; + gen5_pip->resp_data = resp_data; + gen5_pip->resp_len = resp_len; + + if (cmd_len >= GEN5_MIN_APP_CMD_LENGTH && + cmd[4] == GEN5_APP_CMD_REPORT_ID) { + /* Application command */ + gen5_pip->in_progress_cmd = cmd[6] & 0x7f; + } else if (cmd_len >= GEN5_MIN_BL_CMD_LENGTH && + cmd[4] == GEN5_BL_CMD_REPORT_ID) { + /* Bootloader command */ + gen5_pip->in_progress_cmd = cmd[7]; + } + + /* Send command data, wait and read output response data's length. */ + if (irq_mode) { + gen5_pip->is_irq_mode = true; + error = cyapa_do_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len, + timeout); + if (error == -ETIMEDOUT && resp_data && + resp_len && *resp_len != 0 && func) { + /* + * For some old version, there was no interrupt for + * the command response data, so need to poll here + * to try to get the response data. + */ + error = cyapa_empty_pip_output_data(cyapa, + resp_data, resp_len, func); + if (error || *resp_len == 0) + error = error ? error : -ETIMEDOUT; + } + } else { + gen5_pip->is_irq_mode = false; + error = cyapa_do_i2c_pip_cmd_polling(cyapa, cmd, cmd_len, + resp_data, resp_len, timeout, func); + } + + gen5_pip->resp_sort_func = NULL; + gen5_pip->resp_data = NULL; + gen5_pip->resp_len = NULL; + gen5_pip->in_progress_cmd = TSG_INVALID_CMD; + + mutex_unlock(&gen5_pip->cmd_lock); + return error; +} + +static bool cyapa_gen5_sort_tsg_pip_bl_resp_data(struct cyapa *cyapa, + u8 *data, int len) +{ + if (!data || len < GEN5_MIN_BL_RESP_LENGTH) + return false; + + /* Bootloader input report id 30h */ + if (data[GEN5_RESP_REPORT_ID_OFFSET] == GEN5_BL_RESP_REPORT_ID && + data[GEN5_RESP_RSVD_OFFSET] == GEN5_RESP_RSVD_KEY && + data[GEN5_RESP_BL_SOP_OFFSET] == GEN5_SOP_KEY) + return true; + + return false; +} + +static bool cyapa_gen5_sort_tsg_pip_app_resp_data(struct cyapa *cyapa, + u8 *data, int len) +{ + struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5; + int resp_len; + + if (!data || len < GEN5_MIN_APP_RESP_LENGTH) + return false; + + if (data[GEN5_RESP_REPORT_ID_OFFSET] == GEN5_APP_RESP_REPORT_ID && + data[GEN5_RESP_RSVD_OFFSET] == GEN5_RESP_RSVD_KEY) { + resp_len = get_unaligned_le16(&data[GEN5_RESP_LENGTH_OFFSET]); + if (GET_GEN5_CMD_CODE(data[GEN5_RESP_APP_CMD_OFFSET]) == 0x00 && + resp_len == GEN5_UNSUPPORTED_CMD_RESP_LENGTH && + data[5] == gen5_pip->in_progress_cmd) { + /* Unsupported command code */ + return false; + } else if (GET_GEN5_CMD_CODE(data[GEN5_RESP_APP_CMD_OFFSET]) == + gen5_pip->in_progress_cmd) { + /* Correct command response received */ + return true; + } + } + + return false; +} + +static bool cyapa_gen5_sort_application_launch_data(struct cyapa *cyapa, + u8 *buf, int len) +{ + if (buf == NULL || len < GEN5_RESP_LENGTH_SIZE) + return false; + + /* + * After reset or power on, trackpad device always sets to 0x00 0x00 + * to indicate a reset or power on event. + */ + if (buf[0] == 0 && buf[1] == 0) + return true; + + return false; +} + +static bool cyapa_gen5_sort_hid_descriptor_data(struct cyapa *cyapa, + u8 *buf, int len) +{ + int resp_len; + int max_output_len; + + /* Check hid descriptor. */ + if (len != GEN5_HID_DESCRIPTOR_SIZE) + return false; + + resp_len = get_unaligned_le16(&buf[GEN5_RESP_LENGTH_OFFSET]); + max_output_len = get_unaligned_le16(&buf[16]); + if (resp_len == GEN5_HID_DESCRIPTOR_SIZE) { + if (buf[GEN5_RESP_REPORT_ID_OFFSET] == GEN5_BL_HID_REPORT_ID && + max_output_len == GEN5_BL_MAX_OUTPUT_LENGTH) { + /* BL mode HID Descriptor */ + return true; + } else if ((buf[GEN5_RESP_REPORT_ID_OFFSET] == + GEN5_APP_HID_REPORT_ID) && + max_output_len == GEN5_APP_MAX_OUTPUT_LENGTH) { + /* APP mode HID Descriptor */ + return true; + } + } + + return false; +} + +static bool cyapa_gen5_sort_deep_sleep_data(struct cyapa *cyapa, + u8 *buf, int len) +{ + if (len == GEN5_DEEP_SLEEP_RESP_LENGTH && + buf[GEN5_RESP_REPORT_ID_OFFSET] == + GEN5_APP_DEEP_SLEEP_REPORT_ID && + (buf[4] & GEN5_DEEP_SLEEP_OPCODE_MASK) == + GEN5_DEEP_SLEEP_OPCODE) + return true; + return false; +} + +static int gen5_idle_state_parse(struct cyapa *cyapa) +{ + u8 resp_data[GEN5_HID_DESCRIPTOR_SIZE]; + int max_output_len; + int length; + u8 cmd[2]; + int ret; + int error; + + /* + * Dump all buffered data firstly for the situation + * when the trackpad is just power on the cyapa go here. + */ + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + memset(resp_data, 0, sizeof(resp_data)); + ret = cyapa_i2c_pip_read(cyapa, resp_data, 3); + if (ret != 3) + return ret < 0 ? ret : -EIO; + + length = get_unaligned_le16(&resp_data[GEN5_RESP_LENGTH_OFFSET]); + if (length == GEN5_RESP_LENGTH_SIZE) { + /* Normal state of Gen5 with no data to respose */ + cyapa->gen = CYAPA_GEN5; + + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + /* Read description from trackpad device */ + cmd[0] = 0x01; + cmd[1] = 0x00; + length = GEN5_HID_DESCRIPTOR_SIZE; + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, + cmd, GEN5_RESP_LENGTH_SIZE, + resp_data, &length, + 300, + cyapa_gen5_sort_hid_descriptor_data, + false); + if (error) + return error; + + length = get_unaligned_le16( + &resp_data[GEN5_RESP_LENGTH_OFFSET]); + max_output_len = get_unaligned_le16(&resp_data[16]); + if ((length == GEN5_HID_DESCRIPTOR_SIZE || + length == GEN5_RESP_LENGTH_SIZE) && + (resp_data[GEN5_RESP_REPORT_ID_OFFSET] == + GEN5_BL_HID_REPORT_ID) && + max_output_len == GEN5_BL_MAX_OUTPUT_LENGTH) { + /* BL mode HID Description read */ + cyapa->state = CYAPA_STATE_GEN5_BL; + } else if ((length == GEN5_HID_DESCRIPTOR_SIZE || + length == GEN5_RESP_LENGTH_SIZE) && + (resp_data[GEN5_RESP_REPORT_ID_OFFSET] == + GEN5_APP_HID_REPORT_ID) && + max_output_len == GEN5_APP_MAX_OUTPUT_LENGTH) { + /* APP mode HID Description read */ + cyapa->state = CYAPA_STATE_GEN5_APP; + } else { + /* Should not happen!!! */ + cyapa->state = CYAPA_STATE_NO_DEVICE; + } + } + + return 0; +} + +static int gen5_hid_description_header_parse(struct cyapa *cyapa, u8 *reg_data) +{ + int length; + u8 resp_data[32]; + int max_output_len; + int ret; + + /* 0x20 0x00 0xF7 is Gen5 Application HID Description Header; + * 0x20 0x00 0xFF is Gen5 Booloader HID Description Header. + * + * Must read HID Description content through out, + * otherwise Gen5 trackpad cannot response next command + * or report any touch or button data. + */ + ret = cyapa_i2c_pip_read(cyapa, resp_data, + GEN5_HID_DESCRIPTOR_SIZE); + if (ret != GEN5_HID_DESCRIPTOR_SIZE) + return ret < 0 ? ret : -EIO; + length = get_unaligned_le16(&resp_data[GEN5_RESP_LENGTH_OFFSET]); + max_output_len = get_unaligned_le16(&resp_data[16]); + if (length == GEN5_RESP_LENGTH_SIZE) { + if (reg_data[GEN5_RESP_REPORT_ID_OFFSET] == + GEN5_BL_HID_REPORT_ID) { + /* + * BL mode HID Description has been previously + * read out. + */ + cyapa->gen = CYAPA_GEN5; + cyapa->state = CYAPA_STATE_GEN5_BL; + } else { + /* + * APP mode HID Description has been previously + * read out. + */ + cyapa->gen = CYAPA_GEN5; + cyapa->state = CYAPA_STATE_GEN5_APP; + } + } else if (length == GEN5_HID_DESCRIPTOR_SIZE && + resp_data[2] == GEN5_BL_HID_REPORT_ID && + max_output_len == GEN5_BL_MAX_OUTPUT_LENGTH) { + /* BL mode HID Description read. */ + cyapa->gen = CYAPA_GEN5; + cyapa->state = CYAPA_STATE_GEN5_BL; + } else if (length == GEN5_HID_DESCRIPTOR_SIZE && + (resp_data[GEN5_RESP_REPORT_ID_OFFSET] == + GEN5_APP_HID_REPORT_ID) && + max_output_len == GEN5_APP_MAX_OUTPUT_LENGTH) { + /* APP mode HID Description read. */ + cyapa->gen = CYAPA_GEN5; + cyapa->state = CYAPA_STATE_GEN5_APP; + } else { + /* Should not happen!!! */ + cyapa->state = CYAPA_STATE_NO_DEVICE; + } + + return 0; +} + +static int gen5_report_data_header_parse(struct cyapa *cyapa, u8 *reg_data) +{ + int length; + + length = get_unaligned_le16(®_data[GEN5_RESP_LENGTH_OFFSET]); + switch (reg_data[GEN5_RESP_REPORT_ID_OFFSET]) { + case GEN5_TOUCH_REPORT_ID: + if (length < GEN5_TOUCH_REPORT_HEAD_SIZE || + length > GEN5_TOUCH_REPORT_MAX_SIZE) + return -EINVAL; + break; + case GEN5_BTN_REPORT_ID: + case GEN5_OLD_PUSH_BTN_REPORT_ID: + case GEN5_PUSH_BTN_REPORT_ID: + if (length < GEN5_BTN_REPORT_HEAD_SIZE || + length > GEN5_BTN_REPORT_MAX_SIZE) + return -EINVAL; + break; + case GEN5_WAKEUP_EVENT_REPORT_ID: + if (length != GEN5_WAKEUP_EVENT_SIZE) + return -EINVAL; + break; + default: + return -EINVAL; + } + + cyapa->gen = CYAPA_GEN5; + cyapa->state = CYAPA_STATE_GEN5_APP; + return 0; +} + +static int gen5_cmd_resp_header_parse(struct cyapa *cyapa, u8 *reg_data) +{ + struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5; + int length; + int ret; + + /* + * Must read report data through out, + * otherwise Gen5 trackpad cannot response next command + * or report any touch or button data. + */ + length = get_unaligned_le16(®_data[GEN5_RESP_LENGTH_OFFSET]); + ret = cyapa_i2c_pip_read(cyapa, gen5_pip->empty_buf, length); + if (ret != length) + return ret < 0 ? ret : -EIO; + + if (length == GEN5_RESP_LENGTH_SIZE) { + /* Previous command has read the data through out. */ + if (reg_data[GEN5_RESP_REPORT_ID_OFFSET] == + GEN5_BL_RESP_REPORT_ID) { + /* Gen5 BL command response data detected */ + cyapa->gen = CYAPA_GEN5; + cyapa->state = CYAPA_STATE_GEN5_BL; + } else { + /* Gen5 APP command response data detected */ + cyapa->gen = CYAPA_GEN5; + cyapa->state = CYAPA_STATE_GEN5_APP; + } + } else if ((gen5_pip->empty_buf[GEN5_RESP_REPORT_ID_OFFSET] == + GEN5_BL_RESP_REPORT_ID) && + (gen5_pip->empty_buf[GEN5_RESP_RSVD_OFFSET] == + GEN5_RESP_RSVD_KEY) && + (gen5_pip->empty_buf[GEN5_RESP_BL_SOP_OFFSET] == + GEN5_SOP_KEY) && + (gen5_pip->empty_buf[length - 1] == + GEN5_EOP_KEY)) { + /* Gen5 BL command response data detected */ + cyapa->gen = CYAPA_GEN5; + cyapa->state = CYAPA_STATE_GEN5_BL; + } else if (gen5_pip->empty_buf[GEN5_RESP_REPORT_ID_OFFSET] == + GEN5_APP_RESP_REPORT_ID && + gen5_pip->empty_buf[GEN5_RESP_RSVD_OFFSET] == + GEN5_RESP_RSVD_KEY) { + /* Gen5 APP command response data detected */ + cyapa->gen = CYAPA_GEN5; + cyapa->state = CYAPA_STATE_GEN5_APP; + } else { + /* Should not happen!!! */ + cyapa->state = CYAPA_STATE_NO_DEVICE; + } + + return 0; +} + +static int cyapa_gen5_state_parse(struct cyapa *cyapa, u8 *reg_data, int len) +{ + int length; + + if (!reg_data || len < 3) + return -EINVAL; + + cyapa->state = CYAPA_STATE_NO_DEVICE; + + /* Parse based on Gen5 characteristic registers and bits */ + length = get_unaligned_le16(®_data[GEN5_RESP_LENGTH_OFFSET]); + if (length == 0 || length == GEN5_RESP_LENGTH_SIZE) { + gen5_idle_state_parse(cyapa); + } else if (length == GEN5_HID_DESCRIPTOR_SIZE && + (reg_data[2] == GEN5_BL_HID_REPORT_ID || + reg_data[2] == GEN5_APP_HID_REPORT_ID)) { + gen5_hid_description_header_parse(cyapa, reg_data); + } else if ((length == GEN5_APP_REPORT_DESCRIPTOR_SIZE || + length == GEN5_APP_CONTRACT_REPORT_DESCRIPTOR_SIZE) && + reg_data[2] == GEN5_APP_REPORT_DESCRIPTOR_ID) { + /* 0xEE 0x00 0xF6 is Gen5 APP report description header. */ + cyapa->gen = CYAPA_GEN5; + cyapa->state = CYAPA_STATE_GEN5_APP; + } else if (length == GEN5_BL_REPORT_DESCRIPTOR_SIZE && + reg_data[2] == GEN5_BL_REPORT_DESCRIPTOR_ID) { + /* 0x1D 0x00 0xFE is Gen5 BL report descriptior header. */ + cyapa->gen = CYAPA_GEN5; + cyapa->state = CYAPA_STATE_GEN5_BL; + } else if (reg_data[2] == GEN5_TOUCH_REPORT_ID || + reg_data[2] == GEN5_BTN_REPORT_ID || + reg_data[2] == GEN5_OLD_PUSH_BTN_REPORT_ID || + reg_data[2] == GEN5_PUSH_BTN_REPORT_ID || + reg_data[2] == GEN5_WAKEUP_EVENT_REPORT_ID) { + gen5_report_data_header_parse(cyapa, reg_data); + } else if (reg_data[2] == GEN5_BL_RESP_REPORT_ID || + reg_data[2] == GEN5_APP_RESP_REPORT_ID) { + gen5_cmd_resp_header_parse(cyapa, reg_data); + } + + if (cyapa->gen == CYAPA_GEN5) { + /* + * Must read the content (e.g.: report description and so on) + * from trackpad device throughout. Otherwise, + * Gen5 trackpad cannot response to next command or + * report any touch or button data later. + */ + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + if (cyapa->state == CYAPA_STATE_GEN5_APP || + cyapa->state == CYAPA_STATE_GEN5_BL) + return 0; + } + + return -EAGAIN; +} + +static int cyapa_gen5_bl_initiate(struct cyapa *cyapa, + const struct firmware *fw) +{ + struct cyapa_tsg_bin_image *image; + struct gen5_bl_cmd_head *bl_cmd_head; + struct gen5_bl_packet_start *bl_packet_start; + struct gen5_bl_initiate_cmd_data *cmd_data; + struct gen5_bl_packet_end *bl_packet_end; + u8 cmd[CYAPA_TSG_MAX_CMD_SIZE]; + int cmd_len; + u16 cmd_data_len; + u16 cmd_crc = 0; + u16 meta_data_crc = 0; + u8 resp_data[11]; + int resp_len; + int records_num; + u8 *data; + int error; + + /* Try to dump all buffered report data before any send command. */ + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + memset(cmd, 0, CYAPA_TSG_MAX_CMD_SIZE); + bl_cmd_head = (struct gen5_bl_cmd_head *)cmd; + cmd_data_len = CYAPA_TSG_BL_KEY_SIZE + CYAPA_TSG_FLASH_MAP_BLOCK_SIZE; + cmd_len = sizeof(struct gen5_bl_cmd_head) + cmd_data_len + + sizeof(struct gen5_bl_packet_end); + + put_unaligned_le16(GEN5_OUTPUT_REPORT_ADDR, &bl_cmd_head->addr); + put_unaligned_le16(cmd_len - 2, &bl_cmd_head->length); + bl_cmd_head->report_id = GEN5_BL_CMD_REPORT_ID; + + bl_packet_start = &bl_cmd_head->packet_start; + bl_packet_start->sop = GEN5_SOP_KEY; + bl_packet_start->cmd_code = GEN5_BL_CMD_INITIATE_BL; + /* 8 key bytes and 128 bytes block size */ + put_unaligned_le16(cmd_data_len, &bl_packet_start->data_length); + + cmd_data = (struct gen5_bl_initiate_cmd_data *)bl_cmd_head->data; + memcpy(cmd_data->key, cyapa_gen5_bl_cmd_key, CYAPA_TSG_BL_KEY_SIZE); + + /* Copy 60 bytes Meta Data Row Parameters */ + image = (struct cyapa_tsg_bin_image *)fw->data; + records_num = (fw->size - sizeof(struct cyapa_tsg_bin_image_head)) / + sizeof(struct cyapa_tsg_bin_image_data_record); + /* APP_INTEGRITY row is always the last row block */ + data = image->records[records_num - 1].record_data; + memcpy(cmd_data->metadata_raw_parameter, data, + CYAPA_TSG_FLASH_MAP_METADATA_SIZE); + + meta_data_crc = crc_itu_t(0xffff, cmd_data->metadata_raw_parameter, + CYAPA_TSG_FLASH_MAP_METADATA_SIZE); + put_unaligned_le16(meta_data_crc, &cmd_data->metadata_crc); + + bl_packet_end = (struct gen5_bl_packet_end *)(bl_cmd_head->data + + cmd_data_len); + cmd_crc = crc_itu_t(0xffff, (u8 *)bl_packet_start, + sizeof(struct gen5_bl_packet_start) + cmd_data_len); + put_unaligned_le16(cmd_crc, &bl_packet_end->crc); + bl_packet_end->eop = GEN5_EOP_KEY; + + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, + cmd, cmd_len, + resp_data, &resp_len, 12000, + cyapa_gen5_sort_tsg_pip_bl_resp_data, true); + if (error || resp_len != GEN5_BL_INITIATE_RESP_LEN || + resp_data[2] != GEN5_BL_RESP_REPORT_ID || + !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5])) + return error ? error : -EAGAIN; + + return 0; +} + +static bool cyapa_gen5_sort_bl_exit_data(struct cyapa *cyapa, u8 *buf, int len) +{ + if (buf == NULL || len < GEN5_RESP_LENGTH_SIZE) + return false; + + if (buf[0] == 0 && buf[1] == 0) + return true; + + /* Exit bootloader failed for some reason. */ + if (len == GEN5_BL_FAIL_EXIT_RESP_LEN && + buf[GEN5_RESP_REPORT_ID_OFFSET] == + GEN5_BL_RESP_REPORT_ID && + buf[GEN5_RESP_RSVD_OFFSET] == GEN5_RESP_RSVD_KEY && + buf[GEN5_RESP_BL_SOP_OFFSET] == GEN5_SOP_KEY && + buf[10] == GEN5_EOP_KEY) + return true; + + return false; +} + +static int cyapa_gen5_bl_exit(struct cyapa *cyapa) +{ + + u8 bl_gen5_bl_exit[] = { 0x04, 0x00, + 0x0B, 0x00, 0x40, 0x00, 0x01, 0x3b, 0x00, 0x00, + 0x20, 0xc7, 0x17 + }; + u8 resp_data[11]; + int resp_len; + int error; + + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, + bl_gen5_bl_exit, sizeof(bl_gen5_bl_exit), + resp_data, &resp_len, + 5000, cyapa_gen5_sort_bl_exit_data, false); + if (error) + return error; + + if (resp_len == GEN5_BL_FAIL_EXIT_RESP_LEN || + resp_data[GEN5_RESP_REPORT_ID_OFFSET] == + GEN5_BL_RESP_REPORT_ID) + return -EAGAIN; + + if (resp_data[0] == 0x00 && resp_data[1] == 0x00) + return 0; + + return -ENODEV; +} + +static int cyapa_gen5_bl_enter(struct cyapa *cyapa) +{ + u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2F, 0x00, 0x01 }; + u8 resp_data[2]; + int resp_len; + int error; + + error = cyapa_poll_state(cyapa, 500); + if (error < 0) + return error; + if (cyapa->gen != CYAPA_GEN5) + return -EINVAL; + + /* Already in Gen5 BL. Skipping exit. */ + if (cyapa->state == CYAPA_STATE_GEN5_BL) + return 0; + + if (cyapa->state != CYAPA_STATE_GEN5_APP) + return -EAGAIN; + + /* Try to dump all buffered report data before any send command. */ + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + /* + * Send bootloader enter command to trackpad device, + * after enter bootloader, the response data is two bytes of 0x00 0x00. + */ + resp_len = sizeof(resp_data); + memset(resp_data, 0, resp_len); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, + cmd, sizeof(cmd), + resp_data, &resp_len, + 5000, cyapa_gen5_sort_application_launch_data, + true); + if (error || resp_data[0] != 0x00 || resp_data[1] != 0x00) + return error < 0 ? error : -EAGAIN; + + cyapa->operational = false; + cyapa->state = CYAPA_STATE_GEN5_BL; + return 0; +} + +static int cyapa_gen5_check_fw(struct cyapa *cyapa, const struct firmware *fw) +{ + struct device *dev = &cyapa->client->dev; + const struct cyapa_tsg_bin_image *image = (const void *)fw->data; + const struct cyapa_tsg_bin_image_data_record *app_integrity; + const struct gen5_bl_metadata_row_params *metadata; + size_t flash_records_count; + u32 fw_app_start, fw_upgrade_start; + u16 fw_app_len, fw_upgrade_len; + u16 app_crc; + u16 app_integrity_crc; + int record_index; + int i; + + flash_records_count = (fw->size - + sizeof(struct cyapa_tsg_bin_image_head)) / + sizeof(struct cyapa_tsg_bin_image_data_record); + + /* + * APP_INTEGRITY row is always the last row block, + * and the row id must be 0x01ff. + */ + app_integrity = &image->records[flash_records_count - 1]; + + if (app_integrity->flash_array_id != 0x00 || + get_unaligned_be16(&app_integrity->row_number) != 0x01ff) { + dev_err(dev, "%s: invalid app_integrity data.\n", __func__); + return -EINVAL; + } + + metadata = (const void *)app_integrity->record_data; + + /* Verify app_integrity crc */ + app_integrity_crc = crc_itu_t(0xffff, app_integrity->record_data, + CYAPA_TSG_APP_INTEGRITY_SIZE); + if (app_integrity_crc != get_unaligned_le16(&metadata->metadata_crc)) { + dev_err(dev, "%s: invalid app_integrity crc.\n", __func__); + return -EINVAL; + } + + fw_app_start = get_unaligned_le32(&metadata->app_start); + fw_app_len = get_unaligned_le16(&metadata->app_len); + fw_upgrade_start = get_unaligned_le32(&metadata->upgrade_start); + fw_upgrade_len = get_unaligned_le16(&metadata->upgrade_len); + + if (fw_app_start % CYAPA_TSG_FW_ROW_SIZE || + fw_app_len % CYAPA_TSG_FW_ROW_SIZE || + fw_upgrade_start % CYAPA_TSG_FW_ROW_SIZE || + fw_upgrade_len % CYAPA_TSG_FW_ROW_SIZE) { + dev_err(dev, "%s: invalid image alignment.\n", __func__); + return -EINVAL; + } + + /* + * Verify application image CRC + */ + record_index = fw_app_start / CYAPA_TSG_FW_ROW_SIZE - + CYAPA_TSG_IMG_START_ROW_NUM; + app_crc = 0xffffU; + for (i = 0; i < fw_app_len / CYAPA_TSG_FW_ROW_SIZE; i++) { + const u8 *data = image->records[record_index + i].record_data; + app_crc = crc_itu_t(app_crc, data, CYAPA_TSG_FW_ROW_SIZE); + } + + if (app_crc != get_unaligned_le16(&metadata->app_crc)) { + dev_err(dev, "%s: invalid firmware app crc check.\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int cyapa_gen5_write_fw_block(struct cyapa *cyapa, + struct cyapa_tsg_bin_image_data_record *flash_record) +{ + struct gen5_bl_cmd_head *bl_cmd_head; + struct gen5_bl_packet_start *bl_packet_start; + struct gen5_bl_flash_row_head *flash_row_head; + struct gen5_bl_packet_end *bl_packet_end; + u8 cmd[CYAPA_TSG_MAX_CMD_SIZE]; + u16 cmd_len; + u8 flash_array_id; + u16 flash_row_id; + u16 record_len; + u8 *record_data; + u16 data_len; + u16 crc; + u8 resp_data[11]; + int resp_len; + int error; + + flash_array_id = flash_record->flash_array_id; + flash_row_id = get_unaligned_be16(&flash_record->row_number); + record_len = get_unaligned_be16(&flash_record->record_len); + record_data = flash_record->record_data; + + memset(cmd, 0, CYAPA_TSG_MAX_CMD_SIZE); + bl_cmd_head = (struct gen5_bl_cmd_head *)cmd; + bl_packet_start = &bl_cmd_head->packet_start; + cmd_len = sizeof(struct gen5_bl_cmd_head) + + sizeof(struct gen5_bl_flash_row_head) + + CYAPA_TSG_FLASH_MAP_BLOCK_SIZE + + sizeof(struct gen5_bl_packet_end); + + put_unaligned_le16(GEN5_OUTPUT_REPORT_ADDR, &bl_cmd_head->addr); + /* Don't include 2 bytes register address */ + put_unaligned_le16(cmd_len - 2, &bl_cmd_head->length); + bl_cmd_head->report_id = GEN5_BL_CMD_REPORT_ID; + bl_packet_start->sop = GEN5_SOP_KEY; + bl_packet_start->cmd_code = GEN5_BL_CMD_PROGRAM_VERIFY_ROW; + + /* 1 (Flash Array ID) + 2 (Flash Row ID) + 128 (flash data) */ + data_len = sizeof(struct gen5_bl_flash_row_head) + record_len; + put_unaligned_le16(data_len, &bl_packet_start->data_length); + + flash_row_head = (struct gen5_bl_flash_row_head *)bl_cmd_head->data; + flash_row_head->flash_array_id = flash_array_id; + put_unaligned_le16(flash_row_id, &flash_row_head->flash_row_id); + memcpy(flash_row_head->flash_data, record_data, record_len); + + bl_packet_end = (struct gen5_bl_packet_end *)(bl_cmd_head->data + + data_len); + crc = crc_itu_t(0xffff, (u8 *)bl_packet_start, + sizeof(struct gen5_bl_packet_start) + data_len); + put_unaligned_le16(crc, &bl_packet_end->crc); + bl_packet_end->eop = GEN5_EOP_KEY; + + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len, + resp_data, &resp_len, + 500, cyapa_gen5_sort_tsg_pip_bl_resp_data, true); + if (error || resp_len != GEN5_BL_BLOCK_WRITE_RESP_LEN || + resp_data[2] != GEN5_BL_RESP_REPORT_ID || + !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5])) + return error < 0 ? error : -EAGAIN; + + return 0; +} + +static int cyapa_gen5_do_fw_update(struct cyapa *cyapa, + const struct firmware *fw) +{ + struct device *dev = &cyapa->client->dev; + struct cyapa_tsg_bin_image_data_record *flash_record; + struct cyapa_tsg_bin_image *image = + (struct cyapa_tsg_bin_image *)fw->data; + int flash_records_count; + int i; + int error; + + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + flash_records_count = + (fw->size - sizeof(struct cyapa_tsg_bin_image_head)) / + sizeof(struct cyapa_tsg_bin_image_data_record); + /* + * The last flash row 0x01ff has been written through bl_initiate + * command, so DO NOT write flash 0x01ff to trackpad device. + */ + for (i = 0; i < (flash_records_count - 1); i++) { + flash_record = &image->records[i]; + error = cyapa_gen5_write_fw_block(cyapa, flash_record); + if (error) { + dev_err(dev, "%s: Gen5 FW update aborted: %d\n", + __func__, error); + return error; + } + } + + return 0; +} + +static int cyapa_gen5_change_power_state(struct cyapa *cyapa, u8 power_state) +{ + u8 cmd[8] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, 0x08, 0x01 }; + u8 resp_data[6]; + int resp_len; + int error; + + cmd[7] = power_state; + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd), + resp_data, &resp_len, + 500, cyapa_gen5_sort_tsg_pip_app_resp_data, false); + if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x08) || + !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5])) + return error < 0 ? error : -EINVAL; + + return 0; +} + +static int cyapa_gen5_set_interval_time(struct cyapa *cyapa, + u8 parameter_id, u16 interval_time) +{ + struct gen5_app_cmd_head *app_cmd_head; + struct gen5_app_set_parameter_data *parameter_data; + u8 cmd[CYAPA_TSG_MAX_CMD_SIZE]; + int cmd_len; + u8 resp_data[7]; + int resp_len; + u8 parameter_size; + int error; + + memset(cmd, 0, CYAPA_TSG_MAX_CMD_SIZE); + app_cmd_head = (struct gen5_app_cmd_head *)cmd; + parameter_data = (struct gen5_app_set_parameter_data *) + app_cmd_head->parameter_data; + cmd_len = sizeof(struct gen5_app_cmd_head) + + sizeof(struct gen5_app_set_parameter_data); + + switch (parameter_id) { + case GEN5_PARAMETER_ACT_INTERVL_ID: + parameter_size = GEN5_PARAMETER_ACT_INTERVL_SIZE; + break; + case GEN5_PARAMETER_ACT_LFT_INTERVL_ID: + parameter_size = GEN5_PARAMETER_ACT_LFT_INTERVL_SIZE; + break; + case GEN5_PARAMETER_LP_INTRVL_ID: + parameter_size = GEN5_PARAMETER_LP_INTRVL_SIZE; + break; + default: + return -EINVAL; + } + + put_unaligned_le16(GEN5_OUTPUT_REPORT_ADDR, &app_cmd_head->addr); + /* + * Don't include unused parameter value bytes and + * 2 bytes register address. + */ + put_unaligned_le16(cmd_len - (4 - parameter_size) - 2, + &app_cmd_head->length); + app_cmd_head->report_id = GEN5_APP_CMD_REPORT_ID; + app_cmd_head->cmd_code = GEN5_CMD_SET_PARAMETER; + parameter_data->parameter_id = parameter_id; + parameter_data->parameter_size = parameter_size; + put_unaligned_le32((u32)interval_time, ¶meter_data->value); + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len, + resp_data, &resp_len, + 500, cyapa_gen5_sort_tsg_pip_app_resp_data, false); + if (error || resp_data[5] != parameter_id || + resp_data[6] != parameter_size || + !VALID_CMD_RESP_HEADER(resp_data, GEN5_CMD_SET_PARAMETER)) + return error < 0 ? error : -EINVAL; + + return 0; +} + +static int cyapa_gen5_get_interval_time(struct cyapa *cyapa, + u8 parameter_id, u16 *interval_time) +{ + struct gen5_app_cmd_head *app_cmd_head; + struct gen5_app_get_parameter_data *parameter_data; + u8 cmd[CYAPA_TSG_MAX_CMD_SIZE]; + int cmd_len; + u8 resp_data[11]; + int resp_len; + u8 parameter_size; + u16 mask, i; + int error; + + memset(cmd, 0, CYAPA_TSG_MAX_CMD_SIZE); + app_cmd_head = (struct gen5_app_cmd_head *)cmd; + parameter_data = (struct gen5_app_get_parameter_data *) + app_cmd_head->parameter_data; + cmd_len = sizeof(struct gen5_app_cmd_head) + + sizeof(struct gen5_app_get_parameter_data); + + *interval_time = 0; + switch (parameter_id) { + case GEN5_PARAMETER_ACT_INTERVL_ID: + parameter_size = GEN5_PARAMETER_ACT_INTERVL_SIZE; + break; + case GEN5_PARAMETER_ACT_LFT_INTERVL_ID: + parameter_size = GEN5_PARAMETER_ACT_LFT_INTERVL_SIZE; + break; + case GEN5_PARAMETER_LP_INTRVL_ID: + parameter_size = GEN5_PARAMETER_LP_INTRVL_SIZE; + break; + default: + return -EINVAL; + } + + put_unaligned_le16(GEN5_HID_DESCRIPTOR_ADDR, &app_cmd_head->addr); + /* Don't include 2 bytes register address */ + put_unaligned_le16(cmd_len - 2, &app_cmd_head->length); + app_cmd_head->report_id = GEN5_APP_CMD_REPORT_ID; + app_cmd_head->cmd_code = GEN5_CMD_GET_PARAMETER; + parameter_data->parameter_id = parameter_id; + + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len, + resp_data, &resp_len, + 500, cyapa_gen5_sort_tsg_pip_app_resp_data, false); + if (error || resp_data[5] != parameter_id || resp_data[6] == 0 || + !VALID_CMD_RESP_HEADER(resp_data, GEN5_CMD_GET_PARAMETER)) + return error < 0 ? error : -EINVAL; + + mask = 0; + for (i = 0; i < parameter_size; i++) + mask |= (0xff << (i * 8)); + *interval_time = get_unaligned_le16(&resp_data[7]) & mask; + + return 0; +} + +static int cyapa_gen5_disable_pip_report(struct cyapa *cyapa) +{ + struct gen5_app_cmd_head *app_cmd_head; + u8 cmd[10]; + u8 resp_data[7]; + int resp_len; + int error; + + memset(cmd, 0, sizeof(cmd)); + app_cmd_head = (struct gen5_app_cmd_head *)cmd; + + put_unaligned_le16(GEN5_HID_DESCRIPTOR_ADDR, &app_cmd_head->addr); + put_unaligned_le16(sizeof(cmd) - 2, &app_cmd_head->length); + app_cmd_head->report_id = GEN5_APP_CMD_REPORT_ID; + app_cmd_head->cmd_code = GEN5_CMD_SET_PARAMETER; + app_cmd_head->parameter_data[0] = GEN5_PARAMETER_DISABLE_PIP_REPORT; + app_cmd_head->parameter_data[1] = 0x01; + app_cmd_head->parameter_data[2] = 0x01; + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd), + resp_data, &resp_len, + 500, cyapa_gen5_sort_tsg_pip_app_resp_data, false); + if (error || resp_data[5] != GEN5_PARAMETER_DISABLE_PIP_REPORT || + !VALID_CMD_RESP_HEADER(resp_data, GEN5_CMD_SET_PARAMETER) || + resp_data[6] != 0x01) + return error < 0 ? error : -EINVAL; + + return 0; +} + +static int cyapa_gen5_deep_sleep(struct cyapa *cyapa, u8 state) +{ + u8 cmd[] = { 0x05, 0x00, 0x00, 0x08}; + u8 resp_data[5]; + int resp_len; + int error; + + cmd[2] = state & GEN5_DEEP_SLEEP_STATE_MASK; + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd), + resp_data, &resp_len, + 500, cyapa_gen5_sort_deep_sleep_data, false); + if (error || ((resp_data[3] & GEN5_DEEP_SLEEP_STATE_MASK) != state)) + return -EINVAL; + + return 0; +} + +static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, + u8 power_mode, u16 sleep_time) +{ + struct device *dev = &cyapa->client->dev; + u8 power_state; + int error; + + if (cyapa->state != CYAPA_STATE_GEN5_APP) + return 0; + + /* Dump all the report data before do power mode commmands. */ + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + if (GEN5_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) { + /* + * Assume TP in deep sleep mode when driver is loaded, + * avoid driver unload and reload command IO issue caused by TP + * has been set into deep sleep mode when unloading. + */ + GEN5_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF); + } + + if (GEN5_DEV_UNINIT_SLEEP_TIME(cyapa) && + GEN5_DEV_GET_PWR_STATE(cyapa) != PWR_MODE_OFF) + if (cyapa_gen5_get_interval_time(cyapa, + GEN5_PARAMETER_LP_INTRVL_ID, + &cyapa->dev_sleep_time) != 0) + GEN5_DEV_SET_SLEEP_TIME(cyapa, UNINIT_SLEEP_TIME); + + if (GEN5_DEV_GET_PWR_STATE(cyapa) == power_mode) { + if (power_mode == PWR_MODE_OFF || + power_mode == PWR_MODE_FULL_ACTIVE || + power_mode == PWR_MODE_BTN_ONLY || + GEN5_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) { + /* Has in correct power mode state, early return. */ + return 0; + } + } + + if (power_mode == PWR_MODE_OFF) { + error = cyapa_gen5_deep_sleep(cyapa, GEN5_DEEP_SLEEP_STATE_OFF); + if (error) { + dev_err(dev, "enter deep sleep fail: %d\n", error); + return error; + } + + GEN5_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF); + return 0; + } + + /* + * When trackpad in power off mode, it cannot change to other power + * state directly, must be wake up from sleep firstly, then + * continue to do next power sate change. + */ + if (GEN5_DEV_GET_PWR_STATE(cyapa) == PWR_MODE_OFF) { + error = cyapa_gen5_deep_sleep(cyapa, GEN5_DEEP_SLEEP_STATE_ON); + if (error) { + dev_err(dev, "deep sleep wake fail: %d\n", error); + return error; + } + } + + if (power_mode == PWR_MODE_FULL_ACTIVE) { + error = cyapa_gen5_change_power_state(cyapa, + GEN5_POWER_STATE_ACTIVE); + if (error) { + dev_err(dev, "change to active fail: %d\n", error); + return error; + } + + GEN5_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE); + } else if (power_mode == PWR_MODE_BTN_ONLY) { + error = cyapa_gen5_change_power_state(cyapa, + GEN5_POWER_STATE_BTN_ONLY); + if (error) { + dev_err(dev, "fail to button only mode: %d\n", error); + return error; + } + + GEN5_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY); + } else { + /* + * Continue to change power mode even failed to set + * interval time, it won't affect the power mode change. + * except the sleep interval time is not correct. + */ + if (GEN5_DEV_UNINIT_SLEEP_TIME(cyapa) || + sleep_time != GEN5_DEV_GET_SLEEP_TIME(cyapa)) + if (cyapa_gen5_set_interval_time(cyapa, + GEN5_PARAMETER_LP_INTRVL_ID, + sleep_time) == 0) + GEN5_DEV_SET_SLEEP_TIME(cyapa, sleep_time); + + if (sleep_time <= GEN5_POWER_READY_MAX_INTRVL_TIME) + power_state = GEN5_POWER_STATE_READY; + else + power_state = GEN5_POWER_STATE_IDLE; + error = cyapa_gen5_change_power_state(cyapa, power_state); + if (error) { + dev_err(dev, "set power state to 0x%02x failed: %d\n", + power_state, error); + return error; + } + + /* + * Disable pip report for a little time, firmware will + * re-enable it automatically. It's used to fix the issue + * that trackpad unable to report signal to wake system up + * in the special situation that system is in suspending, and + * at the same time, user touch trackpad to wake system up. + * This function can avoid the data to be buffured when system + * is suspending which may cause interrput line unable to be + * asserted again. + */ + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + cyapa_gen5_disable_pip_report(cyapa); + + GEN5_DEV_SET_PWR_STATE(cyapa, + cyapa_sleep_time_to_pwr_cmd(sleep_time)); + } + + return 0; +} + +static int cyapa_gen5_resume_scanning(struct cyapa *cyapa) +{ + u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x04 }; + u8 resp_data[6]; + int resp_len; + int error; + + /* Try to dump all buffered data before doing command. */ + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, + cmd, sizeof(cmd), + resp_data, &resp_len, + 500, cyapa_gen5_sort_tsg_pip_app_resp_data, true); + if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x04)) + return -EINVAL; + + /* Try to dump all buffered data when resuming scanning. */ + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + return 0; +} + +static int cyapa_gen5_suspend_scanning(struct cyapa *cyapa) +{ + u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x03 }; + u8 resp_data[6]; + int resp_len; + int error; + + /* Try to dump all buffered data before doing command. */ + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, + cmd, sizeof(cmd), + resp_data, &resp_len, + 500, cyapa_gen5_sort_tsg_pip_app_resp_data, true); + if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x03)) + return -EINVAL; + + /* Try to dump all buffered data when suspending scanning. */ + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + return 0; +} + +static int cyapa_gen5_calibrate_pwcs(struct cyapa *cyapa, + u8 calibrate_sensing_mode_type) +{ + struct gen5_app_cmd_head *app_cmd_head; + u8 cmd[8]; + u8 resp_data[6]; + int resp_len; + int error; + + /* Try to dump all buffered data before doing command. */ + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + memset(cmd, 0, sizeof(cmd)); + app_cmd_head = (struct gen5_app_cmd_head *)cmd; + put_unaligned_le16(GEN5_OUTPUT_REPORT_ADDR, &app_cmd_head->addr); + put_unaligned_le16(sizeof(cmd) - 2, &app_cmd_head->length); + app_cmd_head->report_id = GEN5_APP_CMD_REPORT_ID; + app_cmd_head->cmd_code = GEN5_CMD_CALIBRATE; + app_cmd_head->parameter_data[0] = calibrate_sensing_mode_type; + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, + cmd, sizeof(cmd), + resp_data, &resp_len, + 5000, cyapa_gen5_sort_tsg_pip_app_resp_data, true); + if (error || !VALID_CMD_RESP_HEADER(resp_data, GEN5_CMD_CALIBRATE) || + !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5])) + return error < 0 ? error : -EAGAIN; + + return 0; +} + +static ssize_t cyapa_gen5_do_calibrate(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + int error, calibrate_error; + + /* 1. Suspend Scanning*/ + error = cyapa_gen5_suspend_scanning(cyapa); + if (error) + return error; + + /* 2. Do mutual capacitance fine calibrate. */ + calibrate_error = cyapa_gen5_calibrate_pwcs(cyapa, + CYAPA_SENSING_MODE_MUTUAL_CAP_FINE); + if (calibrate_error) + goto resume_scanning; + + /* 3. Do self capacitance calibrate. */ + calibrate_error = cyapa_gen5_calibrate_pwcs(cyapa, + CYAPA_SENSING_MODE_SELF_CAP); + if (calibrate_error) + goto resume_scanning; + +resume_scanning: + /* 4. Resume Scanning*/ + error = cyapa_gen5_resume_scanning(cyapa); + if (error || calibrate_error) + return error ? error : calibrate_error; + + return count; +} + +static s32 twos_complement_to_s32(s32 value, int num_bits) +{ + if (value >> (num_bits - 1)) + value |= -1 << num_bits; + return value; +} + +static s32 cyapa_parse_structure_data(u8 data_format, u8 *buf, int buf_len) +{ + int data_size; + bool big_endian; + bool unsigned_type; + s32 value; + + data_size = (data_format & 0x07); + big_endian = ((data_format & 0x10) == 0x00); + unsigned_type = ((data_format & 0x20) == 0x00); + + if (buf_len < data_size) + return 0; + + switch (data_size) { + case 1: + value = buf[0]; + break; + case 2: + if (big_endian) + value = get_unaligned_be16(buf); + else + value = get_unaligned_le16(buf); + break; + case 4: + if (big_endian) + value = get_unaligned_be32(buf); + else + value = get_unaligned_le32(buf); + break; + default: + /* Should not happen, just as default case here. */ + value = 0; + break; + } + + if (!unsigned_type) + value = twos_complement_to_s32(value, data_size * 8); + + return value; +} + +static void cyapa_gen5_guess_electrodes(struct cyapa *cyapa, + int *electrodes_rx, int *electrodes_tx) +{ + if (cyapa->electrodes_rx != 0) { + *electrodes_rx = cyapa->electrodes_rx; + *electrodes_tx = (cyapa->electrodes_x == *electrodes_rx) ? + cyapa->electrodes_y : cyapa->electrodes_x; + } else { + *electrodes_tx = min(cyapa->electrodes_x, cyapa->electrodes_y); + *electrodes_rx = max(cyapa->electrodes_x, cyapa->electrodes_y); + } +} + +/* + * Read all the global mutual or self idac data or mutual or self local PWC + * data based on the @idac_data_type. + * If the input value of @data_size is 0, then means read global mutual or + * self idac data. For read global mutual idac data, @idac_max, @idac_min and + * @idac_ave are in order used to return the max value of global mutual idac + * data, the min value of global mutual idac and the average value of the + * global mutual idac data. For read global self idac data, @idac_max is used + * to return the global self cap idac data in Rx direction, @idac_min is used + * to return the global self cap idac data in Tx direction. @idac_ave is not + * used. + * If the input value of @data_size is not 0, than means read the mutual or + * self local PWC data. The @idac_max, @idac_min and @idac_ave are used to + * return the max, min and average value of the mutual or self local PWC data. + * Note, in order to raed mutual local PWC data, must read invoke this function + * to read the mutual global idac data firstly to set the correct Rx number + * value, otherwise, the read mutual idac and PWC data may not correct. + */ +static int cyapa_gen5_read_idac_data(struct cyapa *cyapa, + u8 cmd_code, u8 idac_data_type, int *data_size, + int *idac_max, int *idac_min, int *idac_ave) +{ + struct gen5_app_cmd_head *cmd_head; + u8 cmd[12]; + u8 resp_data[256]; + int resp_len; + int read_len; + int value; + u16 offset; + int read_elements; + bool read_global_idac; + int sum, count, max_element_cnt; + int tmp_max, tmp_min, tmp_ave, tmp_sum, tmp_count; + int electrodes_rx, electrodes_tx; + int i; + int error; + + if (cmd_code != GEN5_CMD_RETRIEVE_DATA_STRUCTURE || + (idac_data_type != GEN5_RETRIEVE_MUTUAL_PWC_DATA && + idac_data_type != GEN5_RETRIEVE_SELF_CAP_PWC_DATA) || + !data_size || !idac_max || !idac_min || !idac_ave) + return -EINVAL; + + *idac_max = INT_MIN; + *idac_min = INT_MAX; + sum = count = tmp_count = 0; + electrodes_rx = electrodes_tx = 0; + if (*data_size == 0) { + /* + * Read global idac values firstly. + * Currently, no idac data exceed 4 bytes. + */ + read_global_idac = true; + offset = 0; + *data_size = 4; + tmp_max = INT_MIN; + tmp_min = INT_MAX; + tmp_ave = tmp_sum = tmp_count = 0; + + if (idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA) { + if (cyapa->aligned_electrodes_rx == 0) { + cyapa_gen5_guess_electrodes(cyapa, + &electrodes_rx, &electrodes_tx); + cyapa->aligned_electrodes_rx = + (electrodes_rx + 3) & ~3u; + } + max_element_cnt = + (cyapa->aligned_electrodes_rx + 7) & ~7u; + } else { + max_element_cnt = 2; + } + } else { + read_global_idac = false; + if (*data_size > 4) + *data_size = 4; + /* Calculate the start offset in bytes of local PWC data. */ + if (idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA) { + offset = cyapa->aligned_electrodes_rx * (*data_size); + if (cyapa->electrodes_rx == cyapa->electrodes_x) + electrodes_tx = cyapa->electrodes_y; + else + electrodes_tx = cyapa->electrodes_x; + max_element_cnt = ((cyapa->aligned_electrodes_rx + 7) & + ~7u) * electrodes_tx; + } else if (idac_data_type == GEN5_RETRIEVE_SELF_CAP_PWC_DATA) { + offset = 2; + max_element_cnt = cyapa->electrodes_x + + cyapa->electrodes_y; + max_element_cnt = (max_element_cnt + 3) & ~3u; + } + } + + memset(cmd, 0, sizeof(cmd)); + cmd_head = (struct gen5_app_cmd_head *)cmd; + put_unaligned_le16(GEN5_OUTPUT_REPORT_ADDR, &cmd_head->addr); + put_unaligned_le16(sizeof(cmd) - 2, &cmd_head->length); + cmd_head->report_id = GEN5_APP_CMD_REPORT_ID; + cmd_head->cmd_code = cmd_code; + do { + read_elements = (256 - GEN5_RESP_DATA_STRUCTURE_OFFSET) / + (*data_size); + read_elements = min(read_elements, max_element_cnt - count); + read_len = read_elements * (*data_size); + + put_unaligned_le16(offset, &cmd_head->parameter_data[0]); + put_unaligned_le16(read_len, &cmd_head->parameter_data[2]); + cmd_head->parameter_data[4] = idac_data_type; + resp_len = GEN5_RESP_DATA_STRUCTURE_OFFSET + read_len; + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, + cmd, sizeof(cmd), + resp_data, &resp_len, + 500, cyapa_gen5_sort_tsg_pip_app_resp_data, + true); + if (error || resp_len < GEN5_RESP_DATA_STRUCTURE_OFFSET || + !VALID_CMD_RESP_HEADER(resp_data, cmd_code) || + !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]) || + resp_data[6] != idac_data_type) + return (error < 0) ? error : -EAGAIN; + read_len = get_unaligned_le16(&resp_data[7]); + if (read_len == 0) + break; + + *data_size = (resp_data[9] & GEN5_PWC_DATA_ELEMENT_SIZE_MASK); + if (read_len < *data_size) + return -EINVAL; + + if (read_global_idac && + idac_data_type == GEN5_RETRIEVE_SELF_CAP_PWC_DATA) { + /* Rx's self global idac data. */ + *idac_max = cyapa_parse_structure_data( + resp_data[9], + &resp_data[GEN5_RESP_DATA_STRUCTURE_OFFSET], + *data_size); + /* Tx's self global idac data. */ + *idac_min = cyapa_parse_structure_data( + resp_data[9], + &resp_data[GEN5_RESP_DATA_STRUCTURE_OFFSET + + *data_size], + *data_size); + break; + } + + /* Read mutual global idac or local mutual/self PWC data. */ + offset += read_len; + for (i = 10; i < (read_len + GEN5_RESP_DATA_STRUCTURE_OFFSET); + i += *data_size) { + value = cyapa_parse_structure_data(resp_data[9], + &resp_data[i], *data_size); + *idac_min = min(value, *idac_min); + *idac_max = max(value, *idac_max); + + if (idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA && + tmp_count < cyapa->aligned_electrodes_rx && + read_global_idac) { + /* + * The value gap betwen global and local mutual + * idac data must bigger than 50%. + * Normally, global value bigger than 50, + * local values less than 10. + */ + if (!tmp_ave || value > tmp_ave / 2) { + tmp_min = min(value, tmp_min); + tmp_max = max(value, tmp_max); + tmp_sum += value; + tmp_count++; + + tmp_ave = tmp_sum / tmp_count; + } + } + + sum += value; + count++; + + if (count >= max_element_cnt) + goto out; + } + } while (true); + +out: + *idac_ave = count ? (sum / count) : 0; + + if (read_global_idac && + idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA) { + if (tmp_count == 0) + return 0; + + if (tmp_count == cyapa->aligned_electrodes_rx) { + cyapa->electrodes_rx = cyapa->electrodes_rx ? + cyapa->electrodes_rx : electrodes_rx; + } else if (tmp_count == electrodes_rx) { + cyapa->electrodes_rx = cyapa->electrodes_rx ? + cyapa->electrodes_rx : electrodes_rx; + cyapa->aligned_electrodes_rx = electrodes_rx; + } else { + cyapa->electrodes_rx = cyapa->electrodes_rx ? + cyapa->electrodes_rx : electrodes_tx; + cyapa->aligned_electrodes_rx = tmp_count; + } + + *idac_min = tmp_min; + *idac_max = tmp_max; + *idac_ave = tmp_ave; + } + + return 0; +} + +static int cyapa_gen5_read_mutual_idac_data(struct cyapa *cyapa, + int *gidac_mutual_max, int *gidac_mutual_min, int *gidac_mutual_ave, + int *lidac_mutual_max, int *lidac_mutual_min, int *lidac_mutual_ave) +{ + int data_size; + int error; + + *gidac_mutual_max = *gidac_mutual_min = *gidac_mutual_ave = 0; + *lidac_mutual_max = *lidac_mutual_min = *lidac_mutual_ave = 0; + + data_size = 0; + error = cyapa_gen5_read_idac_data(cyapa, + GEN5_CMD_RETRIEVE_DATA_STRUCTURE, + GEN5_RETRIEVE_MUTUAL_PWC_DATA, + &data_size, + gidac_mutual_max, gidac_mutual_min, gidac_mutual_ave); + if (error) + return error; + + error = cyapa_gen5_read_idac_data(cyapa, + GEN5_CMD_RETRIEVE_DATA_STRUCTURE, + GEN5_RETRIEVE_MUTUAL_PWC_DATA, + &data_size, + lidac_mutual_max, lidac_mutual_min, lidac_mutual_ave); + return error; +} + +static int cyapa_gen5_read_self_idac_data(struct cyapa *cyapa, + int *gidac_self_rx, int *gidac_self_tx, + int *lidac_self_max, int *lidac_self_min, int *lidac_self_ave) +{ + int data_size; + int error; + + *gidac_self_rx = *gidac_self_tx = 0; + *lidac_self_max = *lidac_self_min = *lidac_self_ave = 0; + + data_size = 0; + error = cyapa_gen5_read_idac_data(cyapa, + GEN5_CMD_RETRIEVE_DATA_STRUCTURE, + GEN5_RETRIEVE_SELF_CAP_PWC_DATA, + &data_size, + lidac_self_max, lidac_self_min, lidac_self_ave); + if (error) + return error; + *gidac_self_rx = *lidac_self_max; + *gidac_self_tx = *lidac_self_min; + + error = cyapa_gen5_read_idac_data(cyapa, + GEN5_CMD_RETRIEVE_DATA_STRUCTURE, + GEN5_RETRIEVE_SELF_CAP_PWC_DATA, + &data_size, + lidac_self_max, lidac_self_min, lidac_self_ave); + return error; +} + +static ssize_t cyapa_gen5_execute_panel_scan(struct cyapa *cyapa) +{ + struct gen5_app_cmd_head *app_cmd_head; + u8 cmd[7]; + u8 resp_data[6]; + int resp_len; + int error; + + memset(cmd, 0, sizeof(cmd)); + app_cmd_head = (struct gen5_app_cmd_head *)cmd; + put_unaligned_le16(GEN5_OUTPUT_REPORT_ADDR, &app_cmd_head->addr); + put_unaligned_le16(sizeof(cmd) - 2, &app_cmd_head->length); + app_cmd_head->report_id = GEN5_APP_CMD_REPORT_ID; + app_cmd_head->cmd_code = GEN5_CMD_EXECUTE_PANEL_SCAN; + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, + cmd, sizeof(cmd), + resp_data, &resp_len, + 500, cyapa_gen5_sort_tsg_pip_app_resp_data, true); + if (error || resp_len != sizeof(resp_data) || + !VALID_CMD_RESP_HEADER(resp_data, + GEN5_CMD_EXECUTE_PANEL_SCAN) || + !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5])) + return error ? error : -EAGAIN; + + return 0; +} + +static int cyapa_gen5_read_panel_scan_raw_data(struct cyapa *cyapa, + u8 cmd_code, u8 raw_data_type, int raw_data_max_num, + int *raw_data_max, int *raw_data_min, int *raw_data_ave, + u8 *buffer) +{ + struct gen5_app_cmd_head *app_cmd_head; + struct gen5_retrieve_panel_scan_data *panel_sacn_data; + u8 cmd[12]; + u8 resp_data[256]; /* Max bytes can transfer one time. */ + int resp_len; + int read_elements; + int read_len; + u16 offset; + s32 value; + int sum, count; + int data_size; + s32 *intp; + int i; + int error; + + if (cmd_code != GEN5_CMD_RETRIEVE_PANEL_SCAN || + (raw_data_type > GEN5_PANEL_SCAN_SELF_DIFFCOUNT) || + !raw_data_max || !raw_data_min || !raw_data_ave) + return -EINVAL; + + intp = (s32 *)buffer; + *raw_data_max = INT_MIN; + *raw_data_min = INT_MAX; + sum = count = 0; + offset = 0; + /* Assume max element size is 4 currently. */ + read_elements = (256 - GEN5_RESP_DATA_STRUCTURE_OFFSET) / 4; + read_len = read_elements * 4; + app_cmd_head = (struct gen5_app_cmd_head *)cmd; + put_unaligned_le16(GEN5_OUTPUT_REPORT_ADDR, &app_cmd_head->addr); + put_unaligned_le16(sizeof(cmd) - 2, &app_cmd_head->length); + app_cmd_head->report_id = GEN5_APP_CMD_REPORT_ID; + app_cmd_head->cmd_code = cmd_code; + panel_sacn_data = (struct gen5_retrieve_panel_scan_data *) + app_cmd_head->parameter_data; + do { + put_unaligned_le16(offset, &panel_sacn_data->read_offset); + put_unaligned_le16(read_elements, + &panel_sacn_data->read_elements); + panel_sacn_data->data_id = raw_data_type; + + resp_len = GEN5_RESP_DATA_STRUCTURE_OFFSET + read_len; + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, + cmd, sizeof(cmd), + resp_data, &resp_len, + 500, cyapa_gen5_sort_tsg_pip_app_resp_data, true); + if (error || resp_len < GEN5_RESP_DATA_STRUCTURE_OFFSET || + !VALID_CMD_RESP_HEADER(resp_data, cmd_code) || + !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]) || + resp_data[6] != raw_data_type) + return error ? error : -EAGAIN; + + read_elements = get_unaligned_le16(&resp_data[7]); + if (read_elements == 0) + break; + + data_size = (resp_data[9] & GEN5_PWC_DATA_ELEMENT_SIZE_MASK); + offset += read_elements; + if (read_elements) { + for (i = GEN5_RESP_DATA_STRUCTURE_OFFSET; + i < (read_elements * data_size + + GEN5_RESP_DATA_STRUCTURE_OFFSET); + i += data_size) { + value = cyapa_parse_structure_data(resp_data[9], + &resp_data[i], data_size); + *raw_data_min = min(value, *raw_data_min); + *raw_data_max = max(value, *raw_data_max); + + if (intp) + put_unaligned_le32(value, &intp[count]); + + sum += value; + count++; + + } + } + + if (count >= raw_data_max_num) + break; + + read_elements = (sizeof(resp_data) - + GEN5_RESP_DATA_STRUCTURE_OFFSET) / data_size; + read_len = read_elements * data_size; + } while (true); + + *raw_data_ave = count ? (sum / count) : 0; + + return 0; +} + +static ssize_t cyapa_gen5_show_baseline(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + int gidac_mutual_max, gidac_mutual_min, gidac_mutual_ave; + int lidac_mutual_max, lidac_mutual_min, lidac_mutual_ave; + int gidac_self_rx, gidac_self_tx; + int lidac_self_max, lidac_self_min, lidac_self_ave; + int raw_cap_mutual_max, raw_cap_mutual_min, raw_cap_mutual_ave; + int raw_cap_self_max, raw_cap_self_min, raw_cap_self_ave; + int mutual_diffdata_max, mutual_diffdata_min, mutual_diffdata_ave; + int self_diffdata_max, self_diffdata_min, self_diffdata_ave; + int mutual_baseline_max, mutual_baseline_min, mutual_baseline_ave; + int self_baseline_max, self_baseline_min, self_baseline_ave; + int error, resume_error; + int size; + + if (cyapa->state != CYAPA_STATE_GEN5_APP) + return -EBUSY; + + /* 1. Suspend Scanning*/ + error = cyapa_gen5_suspend_scanning(cyapa); + if (error) + return error; + + /* 2. Read global and local mutual IDAC data. */ + gidac_self_rx = gidac_self_tx = 0; + error = cyapa_gen5_read_mutual_idac_data(cyapa, + &gidac_mutual_max, &gidac_mutual_min, + &gidac_mutual_ave, &lidac_mutual_max, + &lidac_mutual_min, &lidac_mutual_ave); + if (error) + goto resume_scanning; + + /* 3. Read global and local self IDAC data. */ + error = cyapa_gen5_read_self_idac_data(cyapa, + &gidac_self_rx, &gidac_self_tx, + &lidac_self_max, &lidac_self_min, + &lidac_self_ave); + if (error) + goto resume_scanning; + + /* 4. Execuate panel scan. It must be executed before read data. */ + error = cyapa_gen5_execute_panel_scan(cyapa); + if (error) + goto resume_scanning; + + /* 5. Retrieve panel scan, mutual cap raw data. */ + error = cyapa_gen5_read_panel_scan_raw_data(cyapa, + GEN5_CMD_RETRIEVE_PANEL_SCAN, + GEN5_PANEL_SCAN_MUTUAL_RAW_DATA, + cyapa->electrodes_x * cyapa->electrodes_y, + &raw_cap_mutual_max, &raw_cap_mutual_min, + &raw_cap_mutual_ave, + NULL); + if (error) + goto resume_scanning; + + /* 6. Retrieve panel scan, self cap raw data. */ + error = cyapa_gen5_read_panel_scan_raw_data(cyapa, + GEN5_CMD_RETRIEVE_PANEL_SCAN, + GEN5_PANEL_SCAN_SELF_RAW_DATA, + cyapa->electrodes_x + cyapa->electrodes_y, + &raw_cap_self_max, &raw_cap_self_min, + &raw_cap_self_ave, + NULL); + if (error) + goto resume_scanning; + + /* 7. Retrieve panel scan, mutual cap diffcount raw data. */ + error = cyapa_gen5_read_panel_scan_raw_data(cyapa, + GEN5_CMD_RETRIEVE_PANEL_SCAN, + GEN5_PANEL_SCAN_MUTUAL_DIFFCOUNT, + cyapa->electrodes_x * cyapa->electrodes_y, + &mutual_diffdata_max, &mutual_diffdata_min, + &mutual_diffdata_ave, + NULL); + if (error) + goto resume_scanning; + + /* 8. Retrieve panel scan, self cap diffcount raw data. */ + error = cyapa_gen5_read_panel_scan_raw_data(cyapa, + GEN5_CMD_RETRIEVE_PANEL_SCAN, + GEN5_PANEL_SCAN_SELF_DIFFCOUNT, + cyapa->electrodes_x + cyapa->electrodes_y, + &self_diffdata_max, &self_diffdata_min, + &self_diffdata_ave, + NULL); + if (error) + goto resume_scanning; + + /* 9. Retrieve panel scan, mutual cap baseline raw data. */ + error = cyapa_gen5_read_panel_scan_raw_data(cyapa, + GEN5_CMD_RETRIEVE_PANEL_SCAN, + GEN5_PANEL_SCAN_MUTUAL_BASELINE, + cyapa->electrodes_x * cyapa->electrodes_y, + &mutual_baseline_max, &mutual_baseline_min, + &mutual_baseline_ave, + NULL); + if (error) + goto resume_scanning; + + /* 10. Retrieve panel scan, self cap baseline raw data. */ + error = cyapa_gen5_read_panel_scan_raw_data(cyapa, + GEN5_CMD_RETRIEVE_PANEL_SCAN, + GEN5_PANEL_SCAN_SELF_BASELINE, + cyapa->electrodes_x + cyapa->electrodes_y, + &self_baseline_max, &self_baseline_min, + &self_baseline_ave, + NULL); + if (error) + goto resume_scanning; + +resume_scanning: + /* 11. Resume Scanning*/ + resume_error = cyapa_gen5_resume_scanning(cyapa); + if (resume_error || error) + return resume_error ? resume_error : error; + + /* 12. Output data strings */ + size = scnprintf(buf, PAGE_SIZE, "%d %d %d %d %d %d %d %d %d %d %d ", + gidac_mutual_min, gidac_mutual_max, gidac_mutual_ave, + lidac_mutual_min, lidac_mutual_max, lidac_mutual_ave, + gidac_self_rx, gidac_self_tx, + lidac_self_min, lidac_self_max, lidac_self_ave); + size += scnprintf(buf + size, PAGE_SIZE - size, + "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", + raw_cap_mutual_min, raw_cap_mutual_max, raw_cap_mutual_ave, + raw_cap_self_min, raw_cap_self_max, raw_cap_self_ave, + mutual_diffdata_min, mutual_diffdata_max, mutual_diffdata_ave, + self_diffdata_min, self_diffdata_max, self_diffdata_ave, + mutual_baseline_min, mutual_baseline_max, mutual_baseline_ave, + self_baseline_min, self_baseline_max, self_baseline_ave); + return size; +} + +static bool cyapa_gen5_sort_system_info_data(struct cyapa *cyapa, + u8 *buf, int len) +{ + /* Check the report id and command code */ + if (VALID_CMD_RESP_HEADER(buf, 0x02)) + return true; + + return false; +} + +static int cyapa_gen5_bl_query_data(struct cyapa *cyapa) +{ + u8 bl_query_data_cmd[] = { 0x04, 0x00, 0x0b, 0x00, 0x40, 0x00, + 0x01, 0x3c, 0x00, 0x00, 0xb0, 0x42, 0x17 + }; + u8 resp_data[GEN5_BL_READ_APP_INFO_RESP_LEN]; + int resp_len; + int error; + + resp_len = GEN5_BL_READ_APP_INFO_RESP_LEN; + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, + bl_query_data_cmd, sizeof(bl_query_data_cmd), + resp_data, &resp_len, + 500, cyapa_gen5_sort_tsg_pip_bl_resp_data, false); + if (error || resp_len != GEN5_BL_READ_APP_INFO_RESP_LEN || + !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5])) + return error ? error : -EIO; + + memcpy(&cyapa->product_id[0], &resp_data[8], 5); + cyapa->product_id[5] = '-'; + memcpy(&cyapa->product_id[6], &resp_data[13], 6); + cyapa->product_id[12] = '-'; + memcpy(&cyapa->product_id[13], &resp_data[19], 2); + cyapa->product_id[15] = '\0'; + + cyapa->fw_maj_ver = resp_data[22]; + cyapa->fw_min_ver = resp_data[23]; + + return 0; +} + +static int cyapa_gen5_get_query_data(struct cyapa *cyapa) +{ + u8 get_system_information[] = { + 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x02 + }; + u8 resp_data[71]; + int resp_len; + u16 product_family; + int error; + + resp_len = sizeof(resp_data); + error = cyapa_i2c_pip_cmd_irq_sync(cyapa, + get_system_information, sizeof(get_system_information), + resp_data, &resp_len, + 2000, cyapa_gen5_sort_system_info_data, false); + if (error || resp_len < sizeof(resp_data)) + return error ? error : -EIO; + + product_family = get_unaligned_le16(&resp_data[7]); + if ((product_family & GEN5_PRODUCT_FAMILY_MASK) != + GEN5_PRODUCT_FAMILY_TRACKPAD) + return -EINVAL; + + cyapa->fw_maj_ver = resp_data[15]; + cyapa->fw_min_ver = resp_data[16]; + + cyapa->electrodes_x = resp_data[52]; + cyapa->electrodes_y = resp_data[53]; + + cyapa->physical_size_x = get_unaligned_le16(&resp_data[54]) / 100; + cyapa->physical_size_y = get_unaligned_le16(&resp_data[56]) / 100; + + cyapa->max_abs_x = get_unaligned_le16(&resp_data[58]); + cyapa->max_abs_y = get_unaligned_le16(&resp_data[60]); + + cyapa->max_z = get_unaligned_le16(&resp_data[62]); + + cyapa->x_origin = resp_data[64] & 0x01; + cyapa->y_origin = resp_data[65] & 0x01; + + cyapa->btn_capability = (resp_data[70] << 3) & CAPABILITY_BTN_MASK; + + memcpy(&cyapa->product_id[0], &resp_data[33], 5); + cyapa->product_id[5] = '-'; + memcpy(&cyapa->product_id[6], &resp_data[38], 6); + cyapa->product_id[12] = '-'; + memcpy(&cyapa->product_id[13], &resp_data[44], 2); + cyapa->product_id[15] = '\0'; + + if (!cyapa->electrodes_x || !cyapa->electrodes_y || + !cyapa->physical_size_x || !cyapa->physical_size_y || + !cyapa->max_abs_x || !cyapa->max_abs_y || !cyapa->max_z) + return -EINVAL; + + return 0; +} + +static int cyapa_gen5_do_operational_check(struct cyapa *cyapa) +{ + struct device *dev = &cyapa->client->dev; + int error; + + if (cyapa->gen != CYAPA_GEN5) + return -ENODEV; + + switch (cyapa->state) { + case CYAPA_STATE_GEN5_BL: + error = cyapa_gen5_bl_exit(cyapa); + if (error) { + /* Rry to update trackpad product information. */ + cyapa_gen5_bl_query_data(cyapa); + goto out; + } + + cyapa->state = CYAPA_STATE_GEN5_APP; + + case CYAPA_STATE_GEN5_APP: + /* + * If trackpad device in deep sleep mode, + * the app command will fail. + * So always try to reset trackpad device to full active when + * the device state is requeried. + */ + error = cyapa_gen5_set_power_mode(cyapa, + PWR_MODE_FULL_ACTIVE, 0); + if (error) + dev_warn(dev, "%s: failed to set power active mode.\n", + __func__); + + /* Get trackpad product information. */ + error = cyapa_gen5_get_query_data(cyapa); + if (error) + goto out; + /* Only support product ID starting with CYTRA */ + if (memcmp(cyapa->product_id, product_id, + strlen(product_id)) != 0) { + dev_err(dev, "%s: unknown product ID (%s)\n", + __func__, cyapa->product_id); + error = -EINVAL; + } + break; + default: + error = -EINVAL; + } + +out: + return error; +} + +/* + * Return false, do not continue process + * Return true, continue process. + */ +static bool cyapa_gen5_irq_cmd_handler(struct cyapa *cyapa) +{ + struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5; + int length; + + if (atomic_read(&gen5_pip->cmd_issued)) { + /* Polling command response data. */ + if (gen5_pip->is_irq_mode == false) + return false; + + /* + * Read out all none command response data. + * these output data may caused by user put finger on + * trackpad when host waiting the command response. + */ + cyapa_i2c_pip_read(cyapa, gen5_pip->irq_cmd_buf, + GEN5_RESP_LENGTH_SIZE); + length = get_unaligned_le16(gen5_pip->irq_cmd_buf); + length = (length <= GEN5_RESP_LENGTH_SIZE) ? + GEN5_RESP_LENGTH_SIZE : length; + if (length > GEN5_RESP_LENGTH_SIZE) + cyapa_i2c_pip_read(cyapa, + gen5_pip->irq_cmd_buf, length); + + if (!(gen5_pip->resp_sort_func && + gen5_pip->resp_sort_func(cyapa, + gen5_pip->irq_cmd_buf, length))) { + /* + * Cover the Gen5 V1 firmware issue. + * The issue is there is no interrut will be + * asserted to notityf host to read a command + * data out when always has finger touch on + * trackpad during the command is issued to + * trackad device. + * This issue has the scenario is that, + * user always has his fingers touched on + * trackpad device when booting/rebooting + * their chrome book. + */ + length = 0; + if (gen5_pip->resp_len) + length = *gen5_pip->resp_len; + cyapa_empty_pip_output_data(cyapa, + gen5_pip->resp_data, + &length, + gen5_pip->resp_sort_func); + if (gen5_pip->resp_len && length != 0) { + *gen5_pip->resp_len = length; + atomic_dec(&gen5_pip->cmd_issued); + complete(&gen5_pip->cmd_ready); + } + return false; + } + + if (gen5_pip->resp_data && gen5_pip->resp_len) { + *gen5_pip->resp_len = (*gen5_pip->resp_len < length) ? + *gen5_pip->resp_len : length; + memcpy(gen5_pip->resp_data, gen5_pip->irq_cmd_buf, + *gen5_pip->resp_len); + } + atomic_dec(&gen5_pip->cmd_issued); + complete(&gen5_pip->cmd_ready); + return false; + } + + return true; +} + +static void cyapa_gen5_report_buttons(struct cyapa *cyapa, + const struct cyapa_gen5_report_data *report_data) +{ + struct input_dev *input = cyapa->input; + u8 buttons = report_data->report_head[GEN5_BUTTONS_OFFSET]; + + buttons = (buttons << CAPABILITY_BTN_SHIFT) & CAPABILITY_BTN_MASK; + + if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK) { + input_report_key(input, BTN_LEFT, + !!(buttons & CAPABILITY_LEFT_BTN_MASK)); + } + if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK) { + input_report_key(input, BTN_MIDDLE, + !!(buttons & CAPABILITY_MIDDLE_BTN_MASK)); + } + if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK) { + input_report_key(input, BTN_RIGHT, + !!(buttons & CAPABILITY_RIGHT_BTN_MASK)); + } + + input_sync(input); +} + +static void cyapa_gen5_report_slot_data(struct cyapa *cyapa, + const struct cyapa_gen5_touch_record *touch) +{ + struct input_dev *input = cyapa->input; + u8 event_id = GEN5_GET_EVENT_ID(touch->touch_tip_event_id); + int slot = GEN5_GET_TOUCH_ID(touch->touch_tip_event_id); + int x, y; + + if (event_id == RECORD_EVENT_LIFTOFF) + return; + + input_mt_slot(input, slot); + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); + x = (touch->x_hi << 8) | touch->x_lo; + if (cyapa->x_origin) + x = cyapa->max_abs_x - x; + input_report_abs(input, ABS_MT_POSITION_X, x); + y = (touch->y_hi << 8) | touch->y_lo; + if (cyapa->y_origin) + y = cyapa->max_abs_y - y; + input_report_abs(input, ABS_MT_POSITION_Y, y); + input_report_abs(input, ABS_MT_PRESSURE, + touch->z); + input_report_abs(input, ABS_MT_TOUCH_MAJOR, + touch->major_axis_len); + input_report_abs(input, ABS_MT_TOUCH_MINOR, + touch->minor_axis_len); + + input_report_abs(input, ABS_MT_WIDTH_MAJOR, + touch->major_tool_len); + input_report_abs(input, ABS_MT_WIDTH_MINOR, + touch->minor_tool_len); + + input_report_abs(input, ABS_MT_ORIENTATION, + touch->orientation); +} + +static void cyapa_gen5_report_touches(struct cyapa *cyapa, + const struct cyapa_gen5_report_data *report_data) +{ + struct input_dev *input = cyapa->input; + unsigned int touch_num; + int i; + + touch_num = report_data->report_head[GEN5_NUMBER_OF_TOUCH_OFFSET] & + GEN5_NUMBER_OF_TOUCH_MASK; + + for (i = 0; i < touch_num; i++) + cyapa_gen5_report_slot_data(cyapa, + &report_data->touch_records[i]); + + input_mt_sync_frame(input); + input_sync(input); +} + +static int cyapa_gen5_irq_handler(struct cyapa *cyapa) +{ + struct device *dev = &cyapa->client->dev; + struct cyapa_gen5_report_data report_data; + int ret; + u8 report_id; + unsigned int report_len; + + if (cyapa->gen != CYAPA_GEN5 || + cyapa->state != CYAPA_STATE_GEN5_APP) { + dev_err(dev, "invalid device state, gen=%d, state=0x%02x\n", + cyapa->gen, cyapa->state); + return -EINVAL; + } + + ret = cyapa_i2c_pip_read(cyapa, (u8 *)&report_data, + GEN5_RESP_LENGTH_SIZE); + if (ret != GEN5_RESP_LENGTH_SIZE) { + dev_err(dev, "failed to read length bytes, (%d)\n", ret); + return -EINVAL; + } + + report_len = get_unaligned_le16( + &report_data.report_head[GEN5_RESP_LENGTH_OFFSET]); + if (report_len < GEN5_RESP_LENGTH_SIZE) { + /* Invliad length or internal reset happened. */ + dev_err(dev, "invalid report_len=%d. bytes: %02x %02x\n", + report_len, report_data.report_head[0], + report_data.report_head[1]); + return -EINVAL; + } + + /* Idle, no data for report. */ + if (report_len == GEN5_RESP_LENGTH_SIZE) + return 0; + + ret = cyapa_i2c_pip_read(cyapa, (u8 *)&report_data, report_len); + if (ret != report_len) { + dev_err(dev, "failed to read %d bytes report data, (%d)\n", + report_len, ret); + return -EINVAL; + } + + report_id = report_data.report_head[GEN5_RESP_REPORT_ID_OFFSET]; + if (report_id == GEN5_WAKEUP_EVENT_REPORT_ID && + report_len == GEN5_WAKEUP_EVENT_SIZE) { + /* + * Device wake event from deep sleep mode for touch. + * This interrupt event is used to wake system up. + */ + return 0; + } else if (report_id != GEN5_TOUCH_REPORT_ID && + report_id != GEN5_BTN_REPORT_ID && + report_id != GEN5_OLD_PUSH_BTN_REPORT_ID && + report_id != GEN5_PUSH_BTN_REPORT_ID) { + /* Running in BL mode or unknown response data read. */ + dev_err(dev, "invalid report_id=0x%02x\n", report_id); + return -EINVAL; + } + + if (report_id == GEN5_TOUCH_REPORT_ID && + (report_len < GEN5_TOUCH_REPORT_HEAD_SIZE || + report_len > GEN5_TOUCH_REPORT_MAX_SIZE)) { + /* Invalid report data length for finger packet. */ + dev_err(dev, "invalid touch packet length=%d\n", report_len); + return 0; + } + + if ((report_id == GEN5_BTN_REPORT_ID || + report_id == GEN5_OLD_PUSH_BTN_REPORT_ID || + report_id == GEN5_PUSH_BTN_REPORT_ID) && + (report_len < GEN5_BTN_REPORT_HEAD_SIZE || + report_len > GEN5_BTN_REPORT_MAX_SIZE)) { + /* Invalid report data length of button packet. */ + dev_err(dev, "invalid button packet length=%d\n", report_len); + return 0; + } + + if (report_id == GEN5_TOUCH_REPORT_ID) + cyapa_gen5_report_touches(cyapa, &report_data); + else + cyapa_gen5_report_buttons(cyapa, &report_data); + + return 0; +} + +static int cyapa_gen5_bl_activate(struct cyapa *cyapa) { return 0; } +static int cyapa_gen5_bl_deactivate(struct cyapa *cyapa) { return 0; } + +const struct cyapa_dev_ops cyapa_gen5_ops = { + .check_fw = cyapa_gen5_check_fw, + .bl_enter = cyapa_gen5_bl_enter, + .bl_initiate = cyapa_gen5_bl_initiate, + .update_fw = cyapa_gen5_do_fw_update, + .bl_activate = cyapa_gen5_bl_activate, + .bl_deactivate = cyapa_gen5_bl_deactivate, + + .show_baseline = cyapa_gen5_show_baseline, + .calibrate_store = cyapa_gen5_do_calibrate, + + .initialize = cyapa_gen5_initialize, + + .state_parse = cyapa_gen5_state_parse, + .operational_check = cyapa_gen5_do_operational_check, + + .irq_handler = cyapa_gen5_irq_handler, + .irq_cmd_handler = cyapa_gen5_irq_cmd_handler, + .sort_empty_output_data = cyapa_empty_pip_output_data, + .set_power_mode = cyapa_gen5_set_power_mode, +}; diff --git a/drivers/input/mouse/cypress_ps2.c b/drivers/input/mouse/cypress_ps2.c index 8af34ffe208b..9118a1861a45 100644 --- a/drivers/input/mouse/cypress_ps2.c +++ b/drivers/input/mouse/cypress_ps2.c @@ -538,7 +538,7 @@ static void cypress_process_packet(struct psmouse *psmouse, bool zero_pkt) pos[i].y = contact->y; } - input_mt_assign_slots(input, slots, pos, n); + input_mt_assign_slots(input, slots, pos, n, 0); for (i = 0; i < n; i++) { contact = &report_data.contacts[i]; diff --git a/drivers/input/mouse/elan_i2c.h b/drivers/input/mouse/elan_i2c.h index 2e838626205f..e100c1b31597 100644 --- a/drivers/input/mouse/elan_i2c.h +++ b/drivers/input/mouse/elan_i2c.h @@ -4,7 +4,6 @@ * Copyright (c) 2013 ELAN Microelectronics Corp. * * Author: ćž—ć”żç¶ (Duson Lin) <dusonlin@emc.com.tw> - * Version: 1.5.5 * * Based on cyapa driver: * copyright (c) 2011-2012 Cypress Semiconductor, Inc. @@ -33,8 +32,9 @@ #define ETP_FW_IAP_PAGE_ERR (1 << 5) #define ETP_FW_IAP_INTF_ERR (1 << 4) #define ETP_FW_PAGE_SIZE 64 -#define ETP_FW_PAGE_COUNT 768 -#define ETP_FW_SIZE (ETP_FW_PAGE_SIZE * ETP_FW_PAGE_COUNT) +#define ETP_FW_VAILDPAGE_COUNT 768 +#define ETP_FW_SIGNATURE_SIZE 6 +#define ETP_FW_SIGNATURE_ADDRESS 0xBFFA struct i2c_client; struct completion; diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 0cb2be48d537..7ce8bfe22d7e 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -4,7 +4,7 @@ * Copyright (c) 2013 ELAN Microelectronics Corp. * * Author: ćž—ć”żç¶ (Duson Lin) <dusonlin@emc.com.tw> - * Version: 1.5.5 + * Version: 1.5.6 * * Based on cyapa driver: * copyright (c) 2011-2012 Cypress Semiconductor, Inc. @@ -40,7 +40,7 @@ #include "elan_i2c.h" #define DRIVER_NAME "elan_i2c" -#define ELAN_DRIVER_VERSION "1.5.5" +#define ELAN_DRIVER_VERSION "1.5.6" #define ETP_PRESSURE_OFFSET 25 #define ETP_MAX_PRESSURE 255 #define ETP_FWIDTH_REDUCE 90 @@ -312,7 +312,7 @@ static int __elan_update_firmware(struct elan_tp_data *data, iap_start_addr = get_unaligned_le16(&fw->data[ETP_IAP_START_ADDR * 2]); boot_page_count = (iap_start_addr * 2) / ETP_FW_PAGE_SIZE; - for (i = boot_page_count; i < ETP_FW_PAGE_COUNT; i++) { + for (i = boot_page_count; i < ETP_FW_VAILDPAGE_COUNT; i++) { u16 checksum = 0; const u8 *page = &fw->data[i * ETP_FW_PAGE_SIZE]; @@ -434,10 +434,11 @@ static ssize_t elan_sysfs_update_fw(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct elan_tp_data *data = i2c_get_clientdata(client); + struct elan_tp_data *data = dev_get_drvdata(dev); const struct firmware *fw; int error; + const u8 *fw_signature; + static const u8 signature[] = {0xAA, 0x55, 0xCC, 0x33, 0xFF, 0xFF}; error = request_firmware(&fw, ETP_FW_NAME, dev); if (error) { @@ -446,10 +447,12 @@ static ssize_t elan_sysfs_update_fw(struct device *dev, return error; } - /* Firmware must be exactly PAGE_NUM * PAGE_SIZE bytes */ - if (fw->size != ETP_FW_SIZE) { - dev_err(dev, "invalid firmware size = %zu, expected %d.\n", - fw->size, ETP_FW_SIZE); + /* Firmware file must match signature data */ + fw_signature = &fw->data[ETP_FW_SIGNATURE_ADDRESS]; + if (memcmp(fw_signature, signature, sizeof(signature)) != 0) { + dev_err(dev, "signature mismatch (expected %*ph, got %*ph)\n", + (int)sizeof(signature), signature, + (int)sizeof(signature), fw_signature); error = -EBADF; goto out_release_fw; } diff --git a/drivers/input/mouse/elan_i2c_i2c.c b/drivers/input/mouse/elan_i2c_i2c.c index 97d4937fc244..029941f861af 100644 --- a/drivers/input/mouse/elan_i2c_i2c.c +++ b/drivers/input/mouse/elan_i2c_i2c.c @@ -4,7 +4,6 @@ * Copyright (c) 2013 ELAN Microelectronics Corp. * * Author: ćž—ć”żç¶ (Duson Lin) <dusonlin@emc.com.tw> - * Version: 1.5.5 * * Based on cyapa driver: * copyright (c) 2011-2012 Cypress Semiconductor, Inc. diff --git a/drivers/input/mouse/elan_i2c_smbus.c b/drivers/input/mouse/elan_i2c_smbus.c index 359bf8583d54..06a2bcd1cda2 100644 --- a/drivers/input/mouse/elan_i2c_smbus.c +++ b/drivers/input/mouse/elan_i2c_smbus.c @@ -4,7 +4,6 @@ * Copyright (c) 2013 ELAN Microelectronics Corp. * * Author: ćž—ć”żç¶ (Duson Lin) <dusonlin@emc.com.tw> - * Version: 1.5.5 * * Based on cyapa driver: * copyright (c) 2011-2012 Cypress Semiconductor, Inc. @@ -71,7 +70,7 @@ static int elan_smbus_initialize(struct i2c_client *client) /* compare hello packet */ if (memcmp(values, check, ETP_SMBUS_HELLOPACKET_LEN)) { - dev_err(&client->dev, "hello packet fail [%*px]\n", + dev_err(&client->dev, "hello packet fail [%*ph]\n", ETP_SMBUS_HELLOPACKET_LEN, values); return -ENXIO; } diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index f2b978026407..6e22682c8255 100644 --- a/drivers/input/mouse/elantech.c +++ b/drivers/input/mouse/elantech.c @@ -1097,6 +1097,8 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse, * Asus UX31 0x361f00 20, 15, 0e clickpad * Asus UX32VD 0x361f02 00, 15, 0e clickpad * Avatar AVIU-145A2 0x361f00 ? clickpad + * Fujitsu LIFEBOOK E544 0x470f00 d0, 12, 09 2 hw buttons + * Fujitsu LIFEBOOK E554 0x570f01 40, 14, 0c 2 hw buttons * Fujitsu H730 0x570f00 c0, 14, 0c 3 hw buttons (**) * Gigabyte U2442 0x450f01 58, 17, 0c 2 hw buttons * Lenovo L430 0x350f02 b9, 15, 0c 2 hw buttons (*) @@ -1475,6 +1477,20 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = { DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"), }, }, + { + /* Fujitsu LIFEBOOK E554 does not work with crc_enabled == 0 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E554"), + }, + }, + { + /* Fujitsu LIFEBOOK E544 does not work with crc_enabled == 0 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E544"), + }, + }, #endif { } }; @@ -1520,6 +1536,8 @@ static int elantech_set_properties(struct elantech_data *etd) case 7: case 8: case 9: + case 10: + case 13: etd->hw_version = 4; break; default: diff --git a/drivers/input/mouse/focaltech.c b/drivers/input/mouse/focaltech.c index f4d657ee1cc0..fca38ba63bbe 100644 --- a/drivers/input/mouse/focaltech.c +++ b/drivers/input/mouse/focaltech.c @@ -2,6 +2,7 @@ * Focaltech TouchPad PS/2 mouse driver * * Copyright (c) 2014 Red Hat Inc. + * Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.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 @@ -13,15 +14,14 @@ * Hans de Goede <hdegoede@redhat.com> */ -/* - * The Focaltech PS/2 touchpad protocol is unknown. This drivers deals with - * detection only, to avoid further detection attempts confusing the touchpad - * this way it at least works in PS/2 mouse compatibility mode. - */ #include <linux/device.h> #include <linux/libps2.h> +#include <linux/input/mt.h> +#include <linux/serio.h> +#include <linux/slab.h> #include "psmouse.h" +#include "focaltech.h" static const char * const focaltech_pnp_ids[] = { "FLT0101", @@ -30,6 +30,12 @@ static const char * const focaltech_pnp_ids[] = { NULL }; +/* + * Even if the kernel is built without support for Focaltech PS/2 touchpads (or + * when the real driver fails to recognize the device), we still have to detect + * them in order to avoid further detection attempts confusing the touchpad. + * This way it at least works in PS/2 mouse compatibility mode. + */ int focaltech_detect(struct psmouse *psmouse, bool set_properties) { if (!psmouse_matches_pnp_id(psmouse, focaltech_pnp_ids)) @@ -37,16 +43,404 @@ int focaltech_detect(struct psmouse *psmouse, bool set_properties) if (set_properties) { psmouse->vendor = "FocalTech"; - psmouse->name = "FocalTech Touchpad in mouse emulation mode"; + psmouse->name = "FocalTech Touchpad"; } return 0; } -int focaltech_init(struct psmouse *psmouse) +static void focaltech_reset(struct psmouse *psmouse) { ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS); psmouse_reset(psmouse); +} + +#ifdef CONFIG_MOUSE_PS2_FOCALTECH + +/* + * Packet types - the numbers are not consecutive, so we might be missing + * something here. + */ +#define FOC_TOUCH 0x3 /* bitmap of active fingers */ +#define FOC_ABS 0x6 /* absolute position of one finger */ +#define FOC_REL 0x9 /* relative position of 1-2 fingers */ + +#define FOC_MAX_FINGERS 5 + +#define FOC_MAX_X 2431 +#define FOC_MAX_Y 1663 + +/* + * Current state of a single finger on the touchpad. + */ +struct focaltech_finger_state { + /* The touchpad has generated a touch event for the finger */ + bool active; + + /* + * The touchpad has sent position data for the finger. The + * flag is 0 when the finger is not active, and there is a + * time between the first touch event for the finger and the + * following absolute position packet for the finger where the + * touchpad has declared the finger to be valid, but we do not + * have any valid position yet. + */ + bool valid; + + /* + * Absolute position (from the bottom left corner) of the + * finger. + */ + unsigned int x; + unsigned int y; +}; + +/* + * Description of the current state of the touchpad hardware. + */ +struct focaltech_hw_state { + /* + * The touchpad tracks the positions of the fingers for us, + * the array indices correspond to the finger indices returned + * in the report packages. + */ + struct focaltech_finger_state fingers[FOC_MAX_FINGERS]; + + /* True if the clickpad has been pressed. */ + bool pressed; +}; + +struct focaltech_data { + unsigned int x_max, y_max; + struct focaltech_hw_state state; +}; + +static void focaltech_report_state(struct psmouse *psmouse) +{ + struct focaltech_data *priv = psmouse->private; + struct focaltech_hw_state *state = &priv->state; + struct input_dev *dev = psmouse->dev; + int i; + + for (i = 0; i < FOC_MAX_FINGERS; i++) { + struct focaltech_finger_state *finger = &state->fingers[i]; + bool active = finger->active && finger->valid; + + input_mt_slot(dev, i); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, active); + if (active) { + input_report_abs(dev, ABS_MT_POSITION_X, finger->x); + input_report_abs(dev, ABS_MT_POSITION_Y, + FOC_MAX_Y - finger->y); + } + } + input_mt_report_pointer_emulation(dev, true); + + input_report_key(psmouse->dev, BTN_LEFT, state->pressed); + input_sync(psmouse->dev); +} + +static void focaltech_process_touch_packet(struct psmouse *psmouse, + unsigned char *packet) +{ + struct focaltech_data *priv = psmouse->private; + struct focaltech_hw_state *state = &priv->state; + unsigned char fingers = packet[1]; + int i; + + state->pressed = (packet[0] >> 4) & 1; + + /* the second byte contains a bitmap of all fingers touching the pad */ + for (i = 0; i < FOC_MAX_FINGERS; i++) { + state->fingers[i].active = fingers & 0x1; + if (!state->fingers[i].active) { + /* + * Even when the finger becomes active again, we still + * will have to wait for the first valid position. + */ + state->fingers[i].valid = false; + } + fingers >>= 1; + } +} + +static void focaltech_process_abs_packet(struct psmouse *psmouse, + unsigned char *packet) +{ + struct focaltech_data *priv = psmouse->private; + struct focaltech_hw_state *state = &priv->state; + unsigned int finger; + + finger = (packet[1] >> 4) - 1; + if (finger >= FOC_MAX_FINGERS) { + psmouse_err(psmouse, "Invalid finger in abs packet: %d\n", + finger); + return; + } + + state->pressed = (packet[0] >> 4) & 1; + + /* + * packet[5] contains some kind of tool size in the most + * significant nibble. 0xff is a special value (latching) that + * signals a large contact area. + */ + if (packet[5] == 0xff) { + state->fingers[finger].valid = false; + return; + } + + state->fingers[finger].x = ((packet[1] & 0xf) << 8) | packet[2]; + state->fingers[finger].y = (packet[3] << 8) | packet[4]; + state->fingers[finger].valid = true; +} + +static void focaltech_process_rel_packet(struct psmouse *psmouse, + unsigned char *packet) +{ + struct focaltech_data *priv = psmouse->private; + struct focaltech_hw_state *state = &priv->state; + int finger1, finger2; + + state->pressed = packet[0] >> 7; + finger1 = ((packet[0] >> 4) & 0x7) - 1; + if (finger1 < FOC_MAX_FINGERS) { + state->fingers[finger1].x += (char)packet[1]; + state->fingers[finger1].y += (char)packet[2]; + } else { + psmouse_err(psmouse, "First finger in rel packet invalid: %d\n", + finger1); + } + + /* + * If there is an odd number of fingers, the last relative + * packet only contains one finger. In this case, the second + * finger index in the packet is 0 (we subtract 1 in the lines + * above to create array indices, so the finger will overflow + * and be above FOC_MAX_FINGERS). + */ + finger2 = ((packet[3] >> 4) & 0x7) - 1; + if (finger2 < FOC_MAX_FINGERS) { + state->fingers[finger2].x += (char)packet[4]; + state->fingers[finger2].y += (char)packet[5]; + } +} + +static void focaltech_process_packet(struct psmouse *psmouse) +{ + unsigned char *packet = psmouse->packet; + + switch (packet[0] & 0xf) { + case FOC_TOUCH: + focaltech_process_touch_packet(psmouse, packet); + break; + + case FOC_ABS: + focaltech_process_abs_packet(psmouse, packet); + break; + + case FOC_REL: + focaltech_process_rel_packet(psmouse, packet); + break; + + default: + psmouse_err(psmouse, "Unknown packet type: %02x\n", packet[0]); + break; + } + + focaltech_report_state(psmouse); +} + +static psmouse_ret_t focaltech_process_byte(struct psmouse *psmouse) +{ + if (psmouse->pktcnt >= 6) { /* Full packet received */ + focaltech_process_packet(psmouse); + return PSMOUSE_FULL_PACKET; + } + + /* + * We might want to do some validation of the data here, but + * we do not know the protocol well enough + */ + return PSMOUSE_GOOD_DATA; +} + +static int focaltech_switch_protocol(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + unsigned char param[3]; + + param[0] = 0; + if (ps2_command(ps2dev, param, 0x10f8)) + return -EIO; + + if (ps2_command(ps2dev, param, 0x10f8)) + return -EIO; + + if (ps2_command(ps2dev, param, 0x10f8)) + return -EIO; + + param[0] = 1; + if (ps2_command(ps2dev, param, 0x10f8)) + return -EIO; + + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11)) + return -EIO; + + if (ps2_command(ps2dev, param, PSMOUSE_CMD_ENABLE)) + return -EIO; + + return 0; +} + +static void focaltech_disconnect(struct psmouse *psmouse) +{ + focaltech_reset(psmouse); + kfree(psmouse->private); + psmouse->private = NULL; +} + +static int focaltech_reconnect(struct psmouse *psmouse) +{ + int error; + + focaltech_reset(psmouse); + + error = focaltech_switch_protocol(psmouse); + if (error) { + psmouse_err(psmouse, "Unable to initialize the device\n"); + return error; + } + + return 0; +} + +static void focaltech_set_input_params(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + struct focaltech_data *priv = psmouse->private; + + /* + * Undo part of setup done for us by psmouse core since touchpad + * is not a relative device. + */ + __clear_bit(EV_REL, dev->evbit); + __clear_bit(REL_X, dev->relbit); + __clear_bit(REL_Y, dev->relbit); + __clear_bit(BTN_RIGHT, dev->keybit); + __clear_bit(BTN_MIDDLE, dev->keybit); + + /* + * Now set up our capabilities. + */ + __set_bit(EV_ABS, dev->evbit); + input_set_abs_params(dev, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0); + input_mt_init_slots(dev, 5, INPUT_MT_POINTER); + __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); +} + +static int focaltech_read_register(struct ps2dev *ps2dev, int reg, + unsigned char *param) +{ + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETSCALE11)) + return -EIO; + + param[0] = 0; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -EIO; + + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -EIO; + + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -EIO; + + param[0] = reg; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -EIO; + + if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + return -EIO; + + return 0; +} + +static int focaltech_read_size(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + struct focaltech_data *priv = psmouse->private; + char param[3]; + + if (focaltech_read_register(ps2dev, 2, param)) + return -EIO; + + /* not sure whether this is 100% correct */ + priv->x_max = (unsigned char)param[1] * 128; + priv->y_max = (unsigned char)param[2] * 128; + + return 0; +} +int focaltech_init(struct psmouse *psmouse) +{ + struct focaltech_data *priv; + int error; + + psmouse->private = priv = kzalloc(sizeof(struct focaltech_data), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + focaltech_reset(psmouse); + + error = focaltech_read_size(psmouse); + if (error) { + psmouse_err(psmouse, + "Unable to read the size of the touchpad\n"); + goto fail; + } + + error = focaltech_switch_protocol(psmouse); + if (error) { + psmouse_err(psmouse, "Unable to initialize the device\n"); + goto fail; + } + + focaltech_set_input_params(psmouse); + + psmouse->protocol_handler = focaltech_process_byte; + psmouse->pktsize = 6; + psmouse->disconnect = focaltech_disconnect; + psmouse->reconnect = focaltech_reconnect; + psmouse->cleanup = focaltech_reset; + /* resync is not supported yet */ + psmouse->resync_time = 0; return 0; + +fail: + focaltech_reset(psmouse); + kfree(priv); + return error; } + +bool focaltech_supported(void) +{ + return true; +} + +#else /* CONFIG_MOUSE_PS2_FOCALTECH */ + +int focaltech_init(struct psmouse *psmouse) +{ + focaltech_reset(psmouse); + + return 0; +} + +bool focaltech_supported(void) +{ + return false; +} + +#endif /* CONFIG_MOUSE_PS2_FOCALTECH */ diff --git a/drivers/input/mouse/focaltech.h b/drivers/input/mouse/focaltech.h index 498650c61e28..71870a9b548a 100644 --- a/drivers/input/mouse/focaltech.h +++ b/drivers/input/mouse/focaltech.h @@ -2,6 +2,7 @@ * Focaltech TouchPad PS/2 mouse driver * * Copyright (c) 2014 Red Hat Inc. + * Copyright (c) 2014 Mathias Gottschlag <mgottschlag@gmail.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 @@ -18,5 +19,6 @@ int focaltech_detect(struct psmouse *psmouse, bool set_properties); int focaltech_init(struct psmouse *psmouse); +bool focaltech_supported(void); #endif diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 95a3a6e2faf6..68469feda470 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -725,16 +725,19 @@ static int psmouse_extensions(struct psmouse *psmouse, /* Always check for focaltech, this is safe as it uses pnp-id matching */ if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) { - if (!set_properties || focaltech_init(psmouse) == 0) { - /* - * Not supported yet, use bare protocol. - * Note that we need to also restrict - * psmouse_max_proto so that psmouse_initialize() - * does not try to reset rate and resolution, - * because even that upsets the device. - */ - psmouse_max_proto = PSMOUSE_PS2; - return PSMOUSE_PS2; + if (max_proto > PSMOUSE_IMEX) { + if (!set_properties || focaltech_init(psmouse) == 0) { + if (focaltech_supported()) + return PSMOUSE_FOCALTECH; + /* + * Note that we need to also restrict + * psmouse_max_proto so that psmouse_initialize() + * does not try to reset rate and resolution, + * because even that upsets the device. + */ + psmouse_max_proto = PSMOUSE_PS2; + return PSMOUSE_PS2; + } } } @@ -1063,6 +1066,15 @@ static const struct psmouse_protocol psmouse_protocols[] = { .alias = "cortps", .detect = cortron_detect, }, +#ifdef CONFIG_MOUSE_PS2_FOCALTECH + { + .type = PSMOUSE_FOCALTECH, + .name = "FocalTechPS/2", + .alias = "focaltech", + .detect = focaltech_detect, + .init = focaltech_init, + }, +#endif { .type = PSMOUSE_AUTO, .name = "auto", diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index f4cf664c7db3..c2ff137ecbdb 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -96,6 +96,7 @@ enum psmouse_type { PSMOUSE_FSP, PSMOUSE_SYNAPTICS_RELATIVE, PSMOUSE_CYPRESS, + PSMOUSE_FOCALTECH, PSMOUSE_AUTO /* This one should always be last */ }; diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index f9472920d986..7e705ee90b86 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -67,6 +67,9 @@ #define X_MAX_POSITIVE 8176 #define Y_MAX_POSITIVE 8176 +/* maximum ABS_MT_POSITION displacement (in mm) */ +#define DMAX 10 + /***************************************************************************** * Stuff we need even when we do not want native Synaptics support ****************************************************************************/ @@ -135,8 +138,9 @@ static const struct min_max_quirk min_max_pnpid_table[] = { 1232, 5710, 1156, 4696 }, { - (const char * const []){"LEN0034", "LEN0036", "LEN0039", - "LEN2002", "LEN2004", NULL}, + (const char * const []){"LEN0034", "LEN0036", "LEN0037", + "LEN0039", "LEN2002", "LEN2004", + NULL}, 1024, 5112, 2024, 4832 }, { @@ -165,7 +169,7 @@ static const char * const topbuttonpad_pnp_ids[] = { "LEN0034", /* T431s, L440, L540, T540, W540, X1 Carbon 2nd */ "LEN0035", /* X240 */ "LEN0036", /* T440 */ - "LEN0037", + "LEN0037", /* X1 Carbon 2nd */ "LEN0038", "LEN0039", /* T440s */ "LEN0041", @@ -574,14 +578,6 @@ static void synaptics_pt_create(struct psmouse *psmouse) * Functions to interpret the absolute mode packets ****************************************************************************/ -static void synaptics_mt_state_set(struct synaptics_mt_state *state, int count, - int sgm, int agm) -{ - state->count = count; - state->sgm = sgm; - state->agm = agm; -} - static void synaptics_parse_agm(const unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw) @@ -600,16 +596,13 @@ static void synaptics_parse_agm(const unsigned char buf[], break; case 2: - /* AGM-CONTACT packet: (count, sgm, agm) */ - synaptics_mt_state_set(&agm->mt_state, buf[1], buf[2], buf[4]); + /* AGM-CONTACT packet: we are only interested in the count */ + priv->agm_count = buf[1]; break; default: break; } - - /* Record that at least one AGM has been received since last SGM */ - priv->agm_pending = true; } static bool is_forcepad; @@ -803,424 +796,68 @@ static void synaptics_report_buttons(struct psmouse *psmouse, input_report_key(dev, BTN_0 + i, hw->ext_buttons & (1 << i)); } -static void synaptics_report_slot(struct input_dev *dev, int slot, - const struct synaptics_hw_state *hw) -{ - input_mt_slot(dev, slot); - input_mt_report_slot_state(dev, MT_TOOL_FINGER, (hw != NULL)); - if (!hw) - return; - - input_report_abs(dev, ABS_MT_POSITION_X, hw->x); - input_report_abs(dev, ABS_MT_POSITION_Y, synaptics_invert_y(hw->y)); - input_report_abs(dev, ABS_MT_PRESSURE, hw->z); -} - static void synaptics_report_mt_data(struct psmouse *psmouse, - struct synaptics_mt_state *mt_state, - const struct synaptics_hw_state *sgm) + const struct synaptics_hw_state *sgm, + int num_fingers) { struct input_dev *dev = psmouse->dev; struct synaptics_data *priv = psmouse->private; - struct synaptics_hw_state *agm = &priv->agm; - struct synaptics_mt_state *old = &priv->mt_state; + const struct synaptics_hw_state *hw[2] = { sgm, &priv->agm }; + struct input_mt_pos pos[2]; + int slot[2], nsemi, i; - switch (mt_state->count) { - case 0: - synaptics_report_slot(dev, 0, NULL); - synaptics_report_slot(dev, 1, NULL); - break; - case 1: - if (mt_state->sgm == -1) { - synaptics_report_slot(dev, 0, NULL); - synaptics_report_slot(dev, 1, NULL); - } else if (mt_state->sgm == 0) { - synaptics_report_slot(dev, 0, sgm); - synaptics_report_slot(dev, 1, NULL); - } else { - synaptics_report_slot(dev, 0, NULL); - synaptics_report_slot(dev, 1, sgm); - } - break; - default: - /* - * If the finger slot contained in SGM is valid, and either - * hasn't changed, or is new, or the old SGM has now moved to - * AGM, then report SGM in MTB slot 0. - * Otherwise, empty MTB slot 0. - */ - if (mt_state->sgm != -1 && - (mt_state->sgm == old->sgm || - old->sgm == -1 || mt_state->agm == old->sgm)) - synaptics_report_slot(dev, 0, sgm); - else - synaptics_report_slot(dev, 0, NULL); + nsemi = clamp_val(num_fingers, 0, 2); - /* - * If the finger slot contained in AGM is valid, and either - * hasn't changed, or is new, then report AGM in MTB slot 1. - * Otherwise, empty MTB slot 1. - * - * However, in the case where the AGM is new, make sure that - * that it is either the same as the old SGM, or there was no - * SGM. - * - * Otherwise, if the SGM was just 1, and the new AGM is 2, then - * the new AGM will keep the old SGM's tracking ID, which can - * cause apparent drumroll. This happens if in the following - * valid finger sequence: - * - * Action SGM AGM (MTB slot:Contact) - * 1. Touch contact 0 (0:0) - * 2. Touch contact 1 (0:0, 1:1) - * 3. Lift contact 0 (1:1) - * 4. Touch contacts 2,3 (0:2, 1:3) - * - * In step 4, contact 3, in AGM must not be given the same - * tracking ID as contact 1 had in step 3. To avoid this, - * the first agm with contact 3 is dropped and slot 1 is - * invalidated (tracking ID = -1). - */ - if (mt_state->agm != -1 && - (mt_state->agm == old->agm || - (old->agm == -1 && - (old->sgm == -1 || mt_state->agm == old->sgm)))) - synaptics_report_slot(dev, 1, agm); - else - synaptics_report_slot(dev, 1, NULL); - break; + for (i = 0; i < nsemi; i++) { + pos[i].x = hw[i]->x; + pos[i].y = synaptics_invert_y(hw[i]->y); } + input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->x_res); + + for (i = 0; i < nsemi; i++) { + input_mt_slot(dev, slot[i]); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, true); + input_report_abs(dev, ABS_MT_POSITION_X, pos[i].x); + input_report_abs(dev, ABS_MT_POSITION_Y, pos[i].y); + input_report_abs(dev, ABS_MT_PRESSURE, hw[i]->z); + } + + input_mt_drop_unused(dev); + /* Don't use active slot count to generate BTN_TOOL events. */ input_mt_report_pointer_emulation(dev, false); /* Send the number of fingers reported by touchpad itself. */ - input_mt_report_finger_count(dev, mt_state->count); + input_mt_report_finger_count(dev, num_fingers); synaptics_report_buttons(psmouse, sgm); input_sync(dev); } -/* Handle case where mt_state->count = 0 */ -static void synaptics_image_sensor_0f(struct synaptics_data *priv, - struct synaptics_mt_state *mt_state) -{ - synaptics_mt_state_set(mt_state, 0, -1, -1); - priv->mt_state_lost = false; -} - -/* Handle case where mt_state->count = 1 */ -static void synaptics_image_sensor_1f(struct synaptics_data *priv, - struct synaptics_mt_state *mt_state) -{ - struct synaptics_hw_state *agm = &priv->agm; - struct synaptics_mt_state *old = &priv->mt_state; - - /* - * If the last AGM was (0,0,0), and there is only one finger left, - * then we absolutely know that SGM contains slot 0, and all other - * fingers have been removed. - */ - if (priv->agm_pending && agm->z == 0) { - synaptics_mt_state_set(mt_state, 1, 0, -1); - priv->mt_state_lost = false; - return; - } - - switch (old->count) { - case 0: - synaptics_mt_state_set(mt_state, 1, 0, -1); - break; - case 1: - /* - * If mt_state_lost, then the previous transition was 3->1, - * and SGM now contains either slot 0 or 1, but we don't know - * which. So, we just assume that the SGM now contains slot 1. - * - * If pending AGM and either: - * (a) the previous SGM slot contains slot 0, or - * (b) there was no SGM slot - * then, the SGM now contains slot 1 - * - * Case (a) happens with very rapid "drum roll" gestures, where - * slot 0 finger is lifted and a new slot 1 finger touches - * within one reporting interval. - * - * Case (b) happens if initially two or more fingers tap - * briefly, and all but one lift before the end of the first - * reporting interval. - * - * (In both these cases, slot 0 will becomes empty, so SGM - * contains slot 1 with the new finger) - * - * Else, if there was no previous SGM, it now contains slot 0. - * - * Otherwise, SGM still contains the same slot. - */ - if (priv->mt_state_lost || - (priv->agm_pending && old->sgm <= 0)) - synaptics_mt_state_set(mt_state, 1, 1, -1); - else if (old->sgm == -1) - synaptics_mt_state_set(mt_state, 1, 0, -1); - break; - case 2: - /* - * If mt_state_lost, we don't know which finger SGM contains. - * - * So, report 1 finger, but with both slots empty. - * We will use slot 1 on subsequent 1->1 - */ - if (priv->mt_state_lost) { - synaptics_mt_state_set(mt_state, 1, -1, -1); - break; - } - /* - * Since the last AGM was NOT (0,0,0), it was the finger in - * slot 0 that has been removed. - * So, SGM now contains previous AGM's slot, and AGM is now - * empty. - */ - synaptics_mt_state_set(mt_state, 1, old->agm, -1); - break; - case 3: - /* - * Since last AGM was not (0,0,0), we don't know which finger - * is left. - * - * So, report 1 finger, but with both slots empty. - * We will use slot 1 on subsequent 1->1 - */ - synaptics_mt_state_set(mt_state, 1, -1, -1); - priv->mt_state_lost = true; - break; - case 4: - case 5: - /* mt_state was updated by AGM-CONTACT packet */ - break; - } -} - -/* Handle case where mt_state->count = 2 */ -static void synaptics_image_sensor_2f(struct synaptics_data *priv, - struct synaptics_mt_state *mt_state) -{ - struct synaptics_mt_state *old = &priv->mt_state; - - switch (old->count) { - case 0: - synaptics_mt_state_set(mt_state, 2, 0, 1); - break; - case 1: - /* - * If previous SGM contained slot 1 or higher, SGM now contains - * slot 0 (the newly touching finger) and AGM contains SGM's - * previous slot. - * - * Otherwise, SGM still contains slot 0 and AGM now contains - * slot 1. - */ - if (old->sgm >= 1) - synaptics_mt_state_set(mt_state, 2, 0, old->sgm); - else - synaptics_mt_state_set(mt_state, 2, 0, 1); - break; - case 2: - /* - * If mt_state_lost, SGM now contains either finger 1 or 2, but - * we don't know which. - * So, we just assume that the SGM contains slot 0 and AGM 1. - */ - if (priv->mt_state_lost) - synaptics_mt_state_set(mt_state, 2, 0, 1); - /* - * Otherwise, use the same mt_state, since it either hasn't - * changed, or was updated by a recently received AGM-CONTACT - * packet. - */ - break; - case 3: - /* - * 3->2 transitions have two unsolvable problems: - * 1) no indication is given which finger was removed - * 2) no way to tell if agm packet was for finger 3 - * before 3->2, or finger 2 after 3->2. - * - * So, report 2 fingers, but empty all slots. - * We will guess slots [0,1] on subsequent 2->2. - */ - synaptics_mt_state_set(mt_state, 2, -1, -1); - priv->mt_state_lost = true; - break; - case 4: - case 5: - /* mt_state was updated by AGM-CONTACT packet */ - break; - } -} - -/* Handle case where mt_state->count = 3 */ -static void synaptics_image_sensor_3f(struct synaptics_data *priv, - struct synaptics_mt_state *mt_state) -{ - struct synaptics_mt_state *old = &priv->mt_state; - - switch (old->count) { - case 0: - synaptics_mt_state_set(mt_state, 3, 0, 2); - break; - case 1: - /* - * If previous SGM contained slot 2 or higher, SGM now contains - * slot 0 (one of the newly touching fingers) and AGM contains - * SGM's previous slot. - * - * Otherwise, SGM now contains slot 0 and AGM contains slot 2. - */ - if (old->sgm >= 2) - synaptics_mt_state_set(mt_state, 3, 0, old->sgm); - else - synaptics_mt_state_set(mt_state, 3, 0, 2); - break; - case 2: - /* - * If the AGM previously contained slot 3 or higher, then the - * newly touching finger is in the lowest available slot. - * - * If SGM was previously 1 or higher, then the new SGM is - * now slot 0 (with a new finger), otherwise, the new finger - * is now in a hidden slot between 0 and AGM's slot. - * - * In all such cases, the SGM now contains slot 0, and the AGM - * continues to contain the same slot as before. - */ - if (old->agm >= 3) { - synaptics_mt_state_set(mt_state, 3, 0, old->agm); - break; - } - - /* - * After some 3->1 and all 3->2 transitions, we lose track - * of which slot is reported by SGM and AGM. - * - * For 2->3 in this state, report 3 fingers, but empty all - * slots, and we will guess (0,2) on a subsequent 0->3. - * - * To userspace, the resulting transition will look like: - * 2:[0,1] -> 3:[-1,-1] -> 3:[0,2] - */ - if (priv->mt_state_lost) { - synaptics_mt_state_set(mt_state, 3, -1, -1); - break; - } - - /* - * If the (SGM,AGM) really previously contained slots (0, 1), - * then we cannot know what slot was just reported by the AGM, - * because the 2->3 transition can occur either before or after - * the AGM packet. Thus, this most recent AGM could contain - * either the same old slot 1 or the new slot 2. - * Subsequent AGMs will be reporting slot 2. - * - * To userspace, the resulting transition will look like: - * 2:[0,1] -> 3:[0,-1] -> 3:[0,2] - */ - synaptics_mt_state_set(mt_state, 3, 0, -1); - break; - case 3: - /* - * If, for whatever reason, the previous agm was invalid, - * Assume SGM now contains slot 0, AGM now contains slot 2. - */ - if (old->agm <= 2) - synaptics_mt_state_set(mt_state, 3, 0, 2); - /* - * mt_state either hasn't changed, or was updated by a recently - * received AGM-CONTACT packet. - */ - break; - - case 4: - case 5: - /* mt_state was updated by AGM-CONTACT packet */ - break; - } -} - -/* Handle case where mt_state->count = 4, or = 5 */ -static void synaptics_image_sensor_45f(struct synaptics_data *priv, - struct synaptics_mt_state *mt_state) -{ - /* mt_state was updated correctly by AGM-CONTACT packet */ - priv->mt_state_lost = false; -} - static void synaptics_image_sensor_process(struct psmouse *psmouse, struct synaptics_hw_state *sgm) { struct synaptics_data *priv = psmouse->private; - struct synaptics_hw_state *agm = &priv->agm; - struct synaptics_mt_state mt_state; - - /* Initialize using current mt_state (as updated by last agm) */ - mt_state = agm->mt_state; + int num_fingers; /* * Update mt_state using the new finger count and current mt_state. */ if (sgm->z == 0) - synaptics_image_sensor_0f(priv, &mt_state); + num_fingers = 0; else if (sgm->w >= 4) - synaptics_image_sensor_1f(priv, &mt_state); + num_fingers = 1; else if (sgm->w == 0) - synaptics_image_sensor_2f(priv, &mt_state); - else if (sgm->w == 1 && mt_state.count <= 3) - synaptics_image_sensor_3f(priv, &mt_state); + num_fingers = 2; + else if (sgm->w == 1) + num_fingers = priv->agm_count ? priv->agm_count : 3; else - synaptics_image_sensor_45f(priv, &mt_state); + num_fingers = 4; /* Send resulting input events to user space */ - synaptics_report_mt_data(psmouse, &mt_state, sgm); - - /* Store updated mt_state */ - priv->mt_state = agm->mt_state = mt_state; - priv->agm_pending = false; -} - -static void synaptics_profile_sensor_process(struct psmouse *psmouse, - struct synaptics_hw_state *sgm, - int num_fingers) -{ - struct input_dev *dev = psmouse->dev; - struct synaptics_data *priv = psmouse->private; - struct synaptics_hw_state *hw[2] = { sgm, &priv->agm }; - struct input_mt_pos pos[2]; - int slot[2], nsemi, i; - - nsemi = clamp_val(num_fingers, 0, 2); - - for (i = 0; i < nsemi; i++) { - pos[i].x = hw[i]->x; - pos[i].y = synaptics_invert_y(hw[i]->y); - } - - input_mt_assign_slots(dev, slot, pos, nsemi); - - for (i = 0; i < nsemi; i++) { - input_mt_slot(dev, slot[i]); - input_mt_report_slot_state(dev, MT_TOOL_FINGER, true); - input_report_abs(dev, ABS_MT_POSITION_X, pos[i].x); - input_report_abs(dev, ABS_MT_POSITION_Y, pos[i].y); - input_report_abs(dev, ABS_MT_PRESSURE, hw[i]->z); - } - - input_mt_drop_unused(dev); - input_mt_report_pointer_emulation(dev, false); - input_mt_report_finger_count(dev, num_fingers); - - synaptics_report_buttons(psmouse, sgm); - - input_sync(dev); + synaptics_report_mt_data(psmouse, sgm, num_fingers); } /* @@ -1287,7 +924,7 @@ static void synaptics_process_packet(struct psmouse *psmouse) } if (cr48_profile_sensor) { - synaptics_profile_sensor_process(psmouse, &hw, num_fingers); + synaptics_report_mt_data(psmouse, &hw, num_fingers); return; } @@ -1444,7 +1081,7 @@ static void set_input_params(struct psmouse *psmouse, ABS_MT_POSITION_Y); /* Image sensors can report per-contact pressure */ input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); - input_mt_init_slots(dev, 2, INPUT_MT_POINTER); + input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK); /* Image sensors can signal 4 and 5 finger clicks */ __set_bit(BTN_TOOL_QUADTAP, dev->keybit); diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index 1bd01f21783b..6faf9bb7c117 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -119,16 +119,6 @@ #define SYN_REDUCED_FILTER_FUZZ 8 /* - * A structure to describe which internal touchpad finger slots are being - * reported in raw packets. - */ -struct synaptics_mt_state { - int count; /* num fingers being tracked */ - int sgm; /* which slot is reported by sgm pkt */ - int agm; /* which slot is reported by agm pkt*/ -}; - -/* * A structure to describe the state of the touchpad hardware (buttons and pad) */ struct synaptics_hw_state { @@ -143,9 +133,6 @@ struct synaptics_hw_state { unsigned int down:1; unsigned char ext_buttons; signed char scroll; - - /* As reported in last AGM-CONTACT packets */ - struct synaptics_mt_state mt_state; }; struct synaptics_data { @@ -170,15 +157,12 @@ struct synaptics_data { struct serio *pt_port; /* Pass-through serio port */ - struct synaptics_mt_state mt_state; /* Current mt finger state */ - bool mt_state_lost; /* mt_state may be incorrect */ - /* * Last received Advanced Gesture Mode (AGM) packet. An AGM packet * contains position data for a second contact, at half resolution. */ struct synaptics_hw_state agm; - bool agm_pending; /* new AGM packet received */ + unsigned int agm_count; /* finger count reported by agm */ /* ForcePad handling */ unsigned long press_start; diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index bc2d47431bdc..77833d7a004b 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -281,4 +281,14 @@ config HYPERV_KEYBOARD To compile this driver as a module, choose M here: the module will be called hyperv_keyboard. +config SERIO_SUN4I_PS2 + tristate "Allwinner A10 PS/2 controller support" + depends on ARCH_SUNXI || COMPILE_TEST + help + This selects support for the PS/2 Host Controller on + Allwinner A10. + + To compile this driver as a module, choose M here: the + module will be called sun4i-ps2. + endif diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index 815d874fe724..c600089b7a34 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -29,3 +29,4 @@ obj-$(CONFIG_SERIO_ARC_PS2) += arc_ps2.o obj-$(CONFIG_SERIO_APBPS2) += apbps2.o obj-$(CONFIG_SERIO_OLPC_APSP) += olpc_apsp.o obj-$(CONFIG_HYPERV_KEYBOARD) += hyperv-keyboard.o +obj-$(CONFIG_SERIO_SUN4I_PS2) += sun4i-ps2.o diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c index 8d9ba0c3827c..94ab494a6ade 100644 --- a/drivers/input/serio/gscps2.c +++ b/drivers/input/serio/gscps2.c @@ -40,7 +40,6 @@ MODULE_AUTHOR("Laurent Canet <canetl@esiee.fr>, Thibaut Varene <varenet@parisc-linux.org>, Helge Deller <deller@gmx.de>"); MODULE_DESCRIPTION("HP GSC PS2 port driver"); MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(parisc, gscps2_device_tbl); #define PFX "gscps2.c: " @@ -439,6 +438,7 @@ static struct parisc_device_id gscps2_device_tbl[] = { #endif { 0, } /* 0 terminated list */ }; +MODULE_DEVICE_TABLE(parisc, gscps2_device_tbl); static struct parisc_driver parisc_ps2_driver = { .name = "gsc_ps2", diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h index c66d1b53843e..c11556563ef0 100644 --- a/drivers/input/serio/i8042-x86ia64io.h +++ b/drivers/input/serio/i8042-x86ia64io.h @@ -152,6 +152,14 @@ static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = { }, }, { + /* Medion Akoya E7225 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Medion"), + DMI_MATCH(DMI_PRODUCT_NAME, "Akoya E7225"), + DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"), + }, + }, + { /* Blue FB5601 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "blue"), @@ -415,6 +423,13 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { }, }, { + /* Acer Aspire 7738 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7738"), + }, + }, + { /* Gericom Bellagio */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Gericom"), @@ -745,6 +760,35 @@ static const struct dmi_system_id __initconst i8042_dmi_dritek_table[] = { { } }; +/* + * Some laptops need keyboard reset before probing for the trackpad to get + * it detected, initialised & finally work. + */ +static const struct dmi_system_id __initconst i8042_dmi_kbdreset_table[] = { + { + /* Gigabyte P35 v2 - Elantech touchpad */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "P35V2"), + }, + }, + { + /* Aorus branded Gigabyte X3 Plus - Elantech touchpad */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "X3"), + }, + }, + { + /* Gigabyte P34 - Elantech touchpad */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"), + DMI_MATCH(DMI_PRODUCT_NAME, "P34"), + }, + }, + { } +}; + #endif /* CONFIG_X86 */ #ifdef CONFIG_PNP @@ -1040,6 +1084,9 @@ static int __init i8042_platform_init(void) if (dmi_check_system(i8042_dmi_dritek_table)) i8042_dritek = true; + if (dmi_check_system(i8042_dmi_kbdreset_table)) + i8042_kbdreset = true; + /* * A20 was already enabled during early kernel init. But some buggy * BIOSes (in MSI Laptops) require A20 to be enabled using 8042 to diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 924e4bf357fb..986a71c614b0 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -67,6 +67,10 @@ static bool i8042_notimeout; module_param_named(notimeout, i8042_notimeout, bool, 0); MODULE_PARM_DESC(notimeout, "Ignore timeouts signalled by i8042"); +static bool i8042_kbdreset; +module_param_named(kbdreset, i8042_kbdreset, bool, 0); +MODULE_PARM_DESC(kbdreset, "Reset device connected to KBD port"); + #ifdef CONFIG_X86 static bool i8042_dritek; module_param_named(dritek, i8042_dritek, bool, 0); @@ -790,6 +794,16 @@ static int __init i8042_check_aux(void) return -1; /* + * Reset keyboard (needed on some laptops to successfully detect + * touchpad, e.g., some Gigabyte laptop models with Elantech + * touchpads). + */ + if (i8042_kbdreset) { + pr_warn("Attempting to reset device connected to KBD port\n"); + i8042_kbd_write(NULL, (unsigned char) 0xff); + } + +/* * Test AUX IRQ delivery to make sure BIOS did not grab the IRQ and * used it for a PCI card or somethig else. */ diff --git a/drivers/input/serio/sun4i-ps2.c b/drivers/input/serio/sun4i-ps2.c new file mode 100644 index 000000000000..04b96fe39339 --- /dev/null +++ b/drivers/input/serio/sun4i-ps2.c @@ -0,0 +1,340 @@ +/* + * Driver for Allwinner A10 PS2 host controller + * + * Author: Vishnu Patekar <vishnupatekar0510@gmail.com> + * Aaron.maoye <leafy.myeh@newbietech.com> + */ + +#include <linux/module.h> +#include <linux/serio.h> +#include <linux/interrupt.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> + +#define DRIVER_NAME "sun4i-ps2" + +/* register offset definitions */ +#define PS2_REG_GCTL 0x00 /* PS2 Module Global Control Reg */ +#define PS2_REG_DATA 0x04 /* PS2 Module Data Reg */ +#define PS2_REG_LCTL 0x08 /* PS2 Module Line Control Reg */ +#define PS2_REG_LSTS 0x0C /* PS2 Module Line Status Reg */ +#define PS2_REG_FCTL 0x10 /* PS2 Module FIFO Control Reg */ +#define PS2_REG_FSTS 0x14 /* PS2 Module FIFO Status Reg */ +#define PS2_REG_CLKDR 0x18 /* PS2 Module Clock Divider Reg*/ + +/* PS2 GLOBAL CONTROL REGISTER PS2_GCTL */ +#define PS2_GCTL_INTFLAG BIT(4) +#define PS2_GCTL_INTEN BIT(3) +#define PS2_GCTL_RESET BIT(2) +#define PS2_GCTL_MASTER BIT(1) +#define PS2_GCTL_BUSEN BIT(0) + +/* PS2 LINE CONTROL REGISTER */ +#define PS2_LCTL_NOACK BIT(18) +#define PS2_LCTL_TXDTOEN BIT(8) +#define PS2_LCTL_STOPERREN BIT(3) +#define PS2_LCTL_ACKERREN BIT(2) +#define PS2_LCTL_PARERREN BIT(1) +#define PS2_LCTL_RXDTOEN BIT(0) + +/* PS2 LINE STATUS REGISTER */ +#define PS2_LSTS_TXTDO BIT(8) +#define PS2_LSTS_STOPERR BIT(3) +#define PS2_LSTS_ACKERR BIT(2) +#define PS2_LSTS_PARERR BIT(1) +#define PS2_LSTS_RXTDO BIT(0) + +#define PS2_LINE_ERROR_BIT \ + (PS2_LSTS_TXTDO | PS2_LSTS_STOPERR | PS2_LSTS_ACKERR | \ + PS2_LSTS_PARERR | PS2_LSTS_RXTDO) + +/* PS2 FIFO CONTROL REGISTER */ +#define PS2_FCTL_TXRST BIT(17) +#define PS2_FCTL_RXRST BIT(16) +#define PS2_FCTL_TXUFIEN BIT(10) +#define PS2_FCTL_TXOFIEN BIT(9) +#define PS2_FCTL_TXRDYIEN BIT(8) +#define PS2_FCTL_RXUFIEN BIT(2) +#define PS2_FCTL_RXOFIEN BIT(1) +#define PS2_FCTL_RXRDYIEN BIT(0) + +/* PS2 FIFO STATUS REGISTER */ +#define PS2_FSTS_TXUF BIT(10) +#define PS2_FSTS_TXOF BIT(9) +#define PS2_FSTS_TXRDY BIT(8) +#define PS2_FSTS_RXUF BIT(2) +#define PS2_FSTS_RXOF BIT(1) +#define PS2_FSTS_RXRDY BIT(0) + +#define PS2_FIFO_ERROR_BIT \ + (PS2_FSTS_TXUF | PS2_FSTS_TXOF | PS2_FSTS_RXUF | PS2_FSTS_RXOF) + +#define PS2_SAMPLE_CLK 1000000 +#define PS2_SCLK 125000 + +struct sun4i_ps2data { + struct serio *serio; + struct device *dev; + + /* IO mapping base */ + void __iomem *reg_base; + + /* clock management */ + struct clk *clk; + + /* irq */ + spinlock_t lock; + int irq; +}; + +static irqreturn_t sun4i_ps2_interrupt(int irq, void *dev_id) +{ + struct sun4i_ps2data *drvdata = dev_id; + u32 intr_status; + u32 fifo_status; + unsigned char byte; + unsigned int rxflags = 0; + u32 rval; + + spin_lock(&drvdata->lock); + + /* Get the PS/2 interrupts and clear them */ + intr_status = readl(drvdata->reg_base + PS2_REG_LSTS); + fifo_status = readl(drvdata->reg_base + PS2_REG_FSTS); + + /* Check line status register */ + if (intr_status & PS2_LINE_ERROR_BIT) { + rxflags = (intr_status & PS2_LINE_ERROR_BIT) ? SERIO_FRAME : 0; + rxflags |= (intr_status & PS2_LSTS_PARERR) ? SERIO_PARITY : 0; + rxflags |= (intr_status & PS2_LSTS_PARERR) ? SERIO_TIMEOUT : 0; + + rval = PS2_LSTS_TXTDO | PS2_LSTS_STOPERR | PS2_LSTS_ACKERR | + PS2_LSTS_PARERR | PS2_LSTS_RXTDO; + writel(rval, drvdata->reg_base + PS2_REG_LSTS); + } + + /* Check FIFO status register */ + if (fifo_status & PS2_FIFO_ERROR_BIT) { + rval = PS2_FSTS_TXUF | PS2_FSTS_TXOF | PS2_FSTS_TXRDY | + PS2_FSTS_RXUF | PS2_FSTS_RXOF | PS2_FSTS_RXRDY; + writel(rval, drvdata->reg_base + PS2_REG_FSTS); + } + + rval = (fifo_status >> 16) & 0x3; + while (rval--) { + byte = readl(drvdata->reg_base + PS2_REG_DATA) & 0xff; + serio_interrupt(drvdata->serio, byte, rxflags); + } + + writel(intr_status, drvdata->reg_base + PS2_REG_LSTS); + writel(fifo_status, drvdata->reg_base + PS2_REG_FSTS); + + spin_unlock(&drvdata->lock); + + return IRQ_HANDLED; +} + +static int sun4i_ps2_open(struct serio *serio) +{ + struct sun4i_ps2data *drvdata = serio->port_data; + u32 src_clk = 0; + u32 clk_scdf; + u32 clk_pcdf; + u32 rval; + unsigned long flags; + + /* Set line control and enable interrupt */ + rval = PS2_LCTL_STOPERREN | PS2_LCTL_ACKERREN + | PS2_LCTL_PARERREN | PS2_LCTL_RXDTOEN; + writel(rval, drvdata->reg_base + PS2_REG_LCTL); + + /* Reset FIFO */ + rval = PS2_FCTL_TXRST | PS2_FCTL_RXRST | PS2_FCTL_TXUFIEN + | PS2_FCTL_TXOFIEN | PS2_FCTL_RXUFIEN + | PS2_FCTL_RXOFIEN | PS2_FCTL_RXRDYIEN; + + writel(rval, drvdata->reg_base + PS2_REG_FCTL); + + src_clk = clk_get_rate(drvdata->clk); + /* Set clock divider register */ + clk_scdf = src_clk / PS2_SAMPLE_CLK - 1; + clk_pcdf = PS2_SAMPLE_CLK / PS2_SCLK - 1; + rval = (clk_scdf << 8) | clk_pcdf; + writel(rval, drvdata->reg_base + PS2_REG_CLKDR); + + /* Set global control register */ + rval = PS2_GCTL_RESET | PS2_GCTL_INTEN | PS2_GCTL_MASTER + | PS2_GCTL_BUSEN; + + spin_lock_irqsave(&drvdata->lock, flags); + writel(rval, drvdata->reg_base + PS2_REG_GCTL); + spin_unlock_irqrestore(&drvdata->lock, flags); + + return 0; +} + +static void sun4i_ps2_close(struct serio *serio) +{ + struct sun4i_ps2data *drvdata = serio->port_data; + u32 rval; + + /* Shut off the interrupt */ + rval = readl(drvdata->reg_base + PS2_REG_GCTL); + writel(rval & ~(PS2_GCTL_INTEN), drvdata->reg_base + PS2_REG_GCTL); + + synchronize_irq(drvdata->irq); +} + +static int sun4i_ps2_write(struct serio *serio, unsigned char val) +{ + unsigned long expire = jiffies + msecs_to_jiffies(10000); + struct sun4i_ps2data *drvdata = serio->port_data; + + do { + if (readl(drvdata->reg_base + PS2_REG_FSTS) & PS2_FSTS_TXRDY) { + writel(val, drvdata->reg_base + PS2_REG_DATA); + return 0; + } + } while (time_before(jiffies, expire)); + + return SERIO_TIMEOUT; +} + +static int sun4i_ps2_probe(struct platform_device *pdev) +{ + struct resource *res; /* IO mem resources */ + struct sun4i_ps2data *drvdata; + struct serio *serio; + struct device *dev = &pdev->dev; + unsigned int irq; + int error; + + drvdata = kzalloc(sizeof(struct sun4i_ps2data), GFP_KERNEL); + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + if (!drvdata || !serio) { + error = -ENOMEM; + goto err_free_mem; + } + + spin_lock_init(&drvdata->lock); + + /* IO */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "failed to locate registers\n"); + error = -ENXIO; + goto err_free_mem; + } + + drvdata->reg_base = ioremap(res->start, resource_size(res)); + if (!drvdata->reg_base) { + dev_err(dev, "failed to map registers\n"); + error = -ENOMEM; + goto err_free_mem; + } + + drvdata->clk = clk_get(dev, NULL); + if (IS_ERR(drvdata->clk)) { + error = PTR_ERR(drvdata->clk); + dev_err(dev, "couldn't get clock %d\n", error); + goto err_ioremap; + } + + error = clk_prepare_enable(drvdata->clk); + if (error) { + dev_err(dev, "failed to enable clock %d\n", error); + goto err_clk; + } + + serio->id.type = SERIO_8042; + serio->write = sun4i_ps2_write; + serio->open = sun4i_ps2_open; + serio->close = sun4i_ps2_close; + serio->port_data = drvdata; + serio->dev.parent = dev; + strlcpy(serio->name, dev_name(dev), sizeof(serio->name)); + strlcpy(serio->phys, dev_name(dev), sizeof(serio->phys)); + + /* shutoff interrupt */ + writel(0, drvdata->reg_base + PS2_REG_GCTL); + + /* Get IRQ for the device */ + irq = platform_get_irq(pdev, 0); + if (!irq) { + dev_err(dev, "no IRQ found\n"); + error = -ENXIO; + goto err_disable_clk; + } + + drvdata->irq = irq; + drvdata->serio = serio; + drvdata->dev = dev; + + error = request_irq(drvdata->irq, sun4i_ps2_interrupt, 0, + DRIVER_NAME, drvdata); + if (error) { + dev_err(drvdata->dev, "failed to allocate interrupt %d: %d\n", + drvdata->irq, error); + goto err_disable_clk; + } + + serio_register_port(serio); + platform_set_drvdata(pdev, drvdata); + + return 0; /* success */ + +err_disable_clk: + clk_disable_unprepare(drvdata->clk); +err_clk: + clk_put(drvdata->clk); +err_ioremap: + iounmap(drvdata->reg_base); +err_free_mem: + kfree(serio); + kfree(drvdata); + return error; +} + +static int sun4i_ps2_remove(struct platform_device *pdev) +{ + struct sun4i_ps2data *drvdata = platform_get_drvdata(pdev); + + serio_unregister_port(drvdata->serio); + + free_irq(drvdata->irq, drvdata); + + clk_disable_unprepare(drvdata->clk); + clk_put(drvdata->clk); + + iounmap(drvdata->reg_base); + + kfree(drvdata); + + return 0; +} + +static const struct of_device_id sun4i_ps2_match[] = { + { .compatible = "allwinner,sun4i-a10-ps2", }, + { }, +}; + +MODULE_DEVICE_TABLE(of, sun4i_ps2_match); + +static struct platform_driver sun4i_ps2_driver = { + .probe = sun4i_ps2_probe, + .remove = sun4i_ps2_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = sun4i_ps2_match, + }, +}; +module_platform_driver(sun4i_ps2_driver); + +MODULE_AUTHOR("Vishnu Patekar <vishnupatekar0510@gmail.com>"); +MODULE_AUTHOR("Aaron.maoye <leafy.myeh@newbietech.com>"); +MODULE_DESCRIPTION("Allwinner A10/Sun4i PS/2 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/tablet/gtco.c b/drivers/input/tablet/gtco.c index 858045694e9d..3a7f3a4a4396 100644 --- a/drivers/input/tablet/gtco.c +++ b/drivers/input/tablet/gtco.c @@ -59,7 +59,7 @@ Scott Hill shill@gtcocalcomp.com #include <asm/uaccess.h> #include <asm/unaligned.h> #include <asm/byteorder.h> - +#include <linux/bitops.h> #include <linux/usb/input.h> @@ -614,7 +614,6 @@ static void gtco_urb_callback(struct urb *urbinfo) struct input_dev *inputdev; int rc; u32 val = 0; - s8 valsigned = 0; char le_buffer[2]; inputdev = device->inputdevice; @@ -665,20 +664,11 @@ static void gtco_urb_callback(struct urb *urbinfo) /* Fall thru */ case 4: /* Tilt */ + input_report_abs(inputdev, ABS_TILT_X, + sign_extend32(device->buffer[6], 6)); - /* Sign extend these 7 bit numbers. */ - if (device->buffer[6] & 0x40) - device->buffer[6] |= 0x80; - - if (device->buffer[7] & 0x40) - device->buffer[7] |= 0x80; - - - valsigned = (device->buffer[6]); - input_report_abs(inputdev, ABS_TILT_X, (s32)valsigned); - - valsigned = (device->buffer[7]); - input_report_abs(inputdev, ABS_TILT_Y, (s32)valsigned); + input_report_abs(inputdev, ABS_TILT_Y, + sign_extend32(device->buffer[7], 6)); /* Fall thru */ case 2: diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index a510f7ef9b66..926c58e540c0 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -33,10 +33,8 @@ #include <linux/delay.h> #include <linux/uaccess.h> #include <linux/buffer_head.h> -#include <linux/version.h> #include <linux/slab.h> #include <linux/firmware.h> -#include <linux/version.h> #include <linux/input/mt.h> #include <linux/acpi.h> #include <linux/of.h> diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c index 4fb5537fdd42..2c2107147319 100644 --- a/drivers/input/touchscreen/pixcir_i2c_ts.c +++ b/drivers/input/touchscreen/pixcir_i2c_ts.c @@ -126,7 +126,7 @@ static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts, pos[i].y = touch->y; } - input_mt_assign_slots(ts->input, slots, pos, n); + input_mt_assign_slots(ts->input, slots, pos, n, 0); } for (i = 0; i < n; i++) { diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c index 28a06749ae42..b93a28b955fd 100644 --- a/drivers/input/touchscreen/sun4i-ts.c +++ b/drivers/input/touchscreen/sun4i-ts.c @@ -34,6 +34,7 @@ #include <linux/err.h> #include <linux/hwmon.h> +#include <linux/thermal.h> #include <linux/init.h> #include <linux/input.h> #include <linux/interrupt.h> @@ -71,6 +72,9 @@ #define TP_ADC_SELECT(x) ((x) << 3) #define ADC_CHAN_SELECT(x) ((x) << 0) /* 3 bits */ +/* on sun6i, bits 3~6 are left shifted by 1 to 4~7 */ +#define SUN6I_TP_MODE_EN(x) ((x) << 5) + /* TP_CTRL2 bits */ #define TP_SENSITIVE_ADJUST(x) ((x) << 28) /* 4 bits */ #define TP_MODE_SELECT(x) ((x) << 26) /* 2 bits */ @@ -107,10 +111,13 @@ struct sun4i_ts_data { struct device *dev; struct input_dev *input; + struct thermal_zone_device *tz; void __iomem *base; unsigned int irq; bool ignore_fifo_data; int temp_data; + int temp_offset; + int temp_step; }; static void sun4i_ts_irq_handle_input(struct sun4i_ts_data *ts, u32 reg_val) @@ -180,16 +187,38 @@ 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) +{ + /* No temp_data until the first irq */ + if (ts->temp_data == -1) + return -EAGAIN; + + *temp = (ts->temp_data - ts->temp_offset) * ts->temp_step; + + return 0; +} + +static int sun4i_get_tz_temp(void *data, long *temp) +{ + return sun4i_get_temp(data, temp); +} + +static struct thermal_zone_of_device_ops sun4i_ts_tz_ops = { + .get_temp = sun4i_get_tz_temp, +}; + 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 error; - /* No temp_data until the first irq */ - if (ts->temp_data == -1) - return -EAGAIN; + error = sun4i_get_temp(ts, &temp); + if (error) + return error; - return sprintf(buf, "%d\n", (ts->temp_data - 1447) * 100); + return sprintf(buf, "%ld\n", temp); } static ssize_t show_temp_label(struct device *dev, @@ -215,6 +244,7 @@ static int sun4i_ts_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct device *hwmon; int error; + u32 reg; bool ts_attached; ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL); @@ -224,6 +254,25 @@ static int sun4i_ts_probe(struct platform_device *pdev) ts->dev = dev; ts->ignore_fifo_data = true; ts->temp_data = -1; + if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts")) { + /* Allwinner SDK has temperature = -271 + (value / 6) (C) */ + ts->temp_offset = 1626; + ts->temp_step = 167; + } else { + /* + * The user manuals do not contain the formula for calculating + * the temperature. The formula used here is from the AXP209, + * which is designed by X-Powers, an affiliate of Allwinner: + * + * temperature = -144.7 + (value * 0.1) + * + * Allwinner does not have any documentation whatsoever for + * this hardware. Moreover, it is claimed that the sensor + * is inaccurate and cannot work properly. + */ + ts->temp_offset = 1447; + ts->temp_step = 100; + } ts_attached = of_property_read_bool(np, "allwinner,ts-attached"); if (ts_attached) { @@ -280,20 +329,34 @@ static int sun4i_ts_probe(struct platform_device *pdev) * Set stylus up debounce to aprox 10 ms, enable debounce, and * finally enable tp mode. */ - writel(STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1) | TP_MODE_EN(1), - ts->base + TP_CTRL1); + reg = STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1); + if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts")) + reg |= TP_MODE_EN(1); + else + reg |= SUN6I_TP_MODE_EN(1); + writel(reg, ts->base + TP_CTRL1); + /* + * The thermal core does not register hwmon devices for DT-based + * thermal zone sensors, such as this one. + */ hwmon = devm_hwmon_device_register_with_groups(ts->dev, "sun4i_ts", ts, sun4i_ts_groups); if (IS_ERR(hwmon)) return PTR_ERR(hwmon); + ts->tz = thermal_zone_of_sensor_register(ts->dev, 0, ts, + &sun4i_ts_tz_ops); + if (IS_ERR(ts->tz)) + ts->tz = NULL; + writel(TEMP_IRQ_EN(1), ts->base + TP_INT_FIFOC); if (ts_attached) { error = input_register_device(ts->input); if (error) { writel(0, ts->base + TP_INT_FIFOC); + thermal_zone_of_sensor_unregister(ts->dev, ts->tz); return error; } } @@ -310,6 +373,8 @@ static int sun4i_ts_remove(struct platform_device *pdev) if (ts->input) input_unregister_device(ts->input); + thermal_zone_of_sensor_unregister(ts->dev, ts->tz); + /* Deactivate all IRQs */ writel(0, ts->base + TP_INT_FIFOC); @@ -318,6 +383,7 @@ static int sun4i_ts_remove(struct platform_device *pdev) static const struct of_device_id sun4i_ts_of_match[] = { { .compatible = "allwinner,sun4i-a10-ts", }, + { .compatible = "allwinner,sun6i-a31-ts", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sun4i_ts_of_match); diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 004f1346a957..191a1b87895f 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -26,6 +26,7 @@ #include <linux/delay.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/sort.h> #include <linux/mfd/ti_am335x_tscadc.h> @@ -52,6 +53,7 @@ struct titsc { u32 bit_xp, bit_xn, bit_yp, bit_yn; u32 inp_xp, inp_xn, inp_yp, inp_yn; u32 step_mask; + u32 charge_delay; }; static unsigned int titsc_readl(struct titsc *ts, unsigned int reg) @@ -121,7 +123,7 @@ static void titsc_step_config(struct titsc *ts_dev) { unsigned int config; int i; - int end_step; + int end_step, first_step, tsc_steps; u32 stepenable; config = STEPCONFIG_MODE_HWSYNC | @@ -140,9 +142,11 @@ static void titsc_step_config(struct titsc *ts_dev) break; } - /* 1 … coordinate_readouts is for X */ - end_step = ts_dev->coordinate_readouts; - for (i = 0; i < end_step; i++) { + tsc_steps = ts_dev->coordinate_readouts * 2 + 2; + first_step = TOTAL_STEPS - tsc_steps; + /* Steps 16 to 16-coordinate_readouts is for X */ + end_step = first_step + tsc_steps; + for (i = end_step - ts_dev->coordinate_readouts; i < end_step; i++) { titsc_writel(ts_dev, REG_STEPCONFIG(i), config); titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); } @@ -164,22 +168,20 @@ static void titsc_step_config(struct titsc *ts_dev) break; } - /* coordinate_readouts … coordinate_readouts * 2 is for Y */ - end_step = ts_dev->coordinate_readouts * 2; - for (i = ts_dev->coordinate_readouts; i < end_step; i++) { + /* 1 ... coordinate_readouts is for Y */ + end_step = first_step + ts_dev->coordinate_readouts; + for (i = first_step; i < end_step; i++) { titsc_writel(ts_dev, REG_STEPCONFIG(i), config); titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); } - /* Charge step configuration */ - config = ts_dev->bit_xp | ts_dev->bit_yn | - STEPCHARGE_RFP_XPUL | STEPCHARGE_RFM_XNUR | - STEPCHARGE_INM_AN1 | STEPCHARGE_INP(ts_dev->inp_yp); + /* Make CHARGECONFIG same as IDLECONFIG */ + config = titsc_readl(ts_dev, REG_IDLECONFIG); titsc_writel(ts_dev, REG_CHARGECONFIG, config); - titsc_writel(ts_dev, REG_CHARGEDELAY, CHARGEDLY_OPENDLY); + titsc_writel(ts_dev, REG_CHARGEDELAY, ts_dev->charge_delay); - /* coordinate_readouts * 2 … coordinate_readouts * 2 + 2 is for Z */ + /* coordinate_readouts + 1 ... coordinate_readouts + 2 is for Z */ config = STEPCONFIG_MODE_HWSYNC | STEPCONFIG_AVG_16 | ts_dev->bit_yp | ts_dev->bit_xn | STEPCONFIG_INM_ADCREFM | @@ -194,73 +196,104 @@ static void titsc_step_config(struct titsc *ts_dev) titsc_writel(ts_dev, REG_STEPDELAY(end_step), STEPCONFIG_OPENDLY); - /* The steps1 … end and bit 0 for TS_Charge */ - stepenable = (1 << (end_step + 2)) - 1; + /* The steps end ... end - readouts * 2 + 2 and bit 0 for TS_Charge */ + stepenable = 1; + for (i = 0; i < tsc_steps; i++) + stepenable |= 1 << (first_step + i + 1); + ts_dev->step_mask = stepenable; am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask); } +static int titsc_cmp_coord(const void *a, const void *b) +{ + return *(int *)a - *(int *)b; +} + static void titsc_read_coordinates(struct titsc *ts_dev, u32 *x, u32 *y, u32 *z1, u32 *z2) { - unsigned int fifocount = titsc_readl(ts_dev, REG_FIFO0CNT); - unsigned int prev_val_x = ~0, prev_val_y = ~0; - unsigned int prev_diff_x = ~0, prev_diff_y = ~0; - unsigned int read, diff; - unsigned int i, channel; + unsigned int yvals[7], xvals[7]; + unsigned int i, xsum = 0, ysum = 0; unsigned int creads = ts_dev->coordinate_readouts; - *z1 = *z2 = 0; - if (fifocount % (creads * 2 + 2)) - fifocount -= fifocount % (creads * 2 + 2); - /* - * Delta filter is used to remove large variations in sampled - * values from ADC. The filter tries to predict where the next - * coordinate could be. This is done by taking a previous - * coordinate and subtracting it form current one. Further the - * algorithm compares the difference with that of a present value, - * if true the value is reported to the sub system. - */ - for (i = 0; i < fifocount; i++) { - read = titsc_readl(ts_dev, REG_FIFO0); - - channel = (read & 0xf0000) >> 16; - read &= 0xfff; - if (channel < creads) { - diff = abs(read - prev_val_x); - if (diff < prev_diff_x) { - prev_diff_x = diff; - *x = read; - } - prev_val_x = read; + for (i = 0; i < creads; i++) { + yvals[i] = titsc_readl(ts_dev, REG_FIFO0); + yvals[i] &= 0xfff; + } - } else if (channel < creads * 2) { - diff = abs(read - prev_val_y); - if (diff < prev_diff_y) { - prev_diff_y = diff; - *y = read; - } - prev_val_y = read; + *z1 = titsc_readl(ts_dev, REG_FIFO0); + *z1 &= 0xfff; + *z2 = titsc_readl(ts_dev, REG_FIFO0); + *z2 &= 0xfff; - } else if (channel < creads * 2 + 1) { - *z1 = read; + for (i = 0; i < creads; i++) { + xvals[i] = titsc_readl(ts_dev, REG_FIFO0); + xvals[i] &= 0xfff; + } - } else if (channel < creads * 2 + 2) { - *z2 = read; + /* + * If co-ordinates readouts is less than 4 then + * report the average. In case of 4 or more + * readouts, sort the co-ordinate samples, drop + * min and max values and report the average of + * remaining values. + */ + if (creads <= 3) { + for (i = 0; i < creads; i++) { + ysum += yvals[i]; + xsum += xvals[i]; } + ysum /= creads; + xsum /= creads; + } else { + sort(yvals, creads, sizeof(unsigned int), + titsc_cmp_coord, NULL); + sort(xvals, creads, sizeof(unsigned int), + titsc_cmp_coord, NULL); + for (i = 1; i < creads - 1; i++) { + ysum += yvals[i]; + xsum += xvals[i]; + } + ysum /= creads - 2; + xsum /= creads - 2; } + *y = ysum; + *x = xsum; } static irqreturn_t titsc_irq(int irq, void *dev) { struct titsc *ts_dev = dev; struct input_dev *input_dev = ts_dev->input; - unsigned int status, irqclr = 0; + unsigned int fsm, status, irqclr = 0; unsigned int x = 0, y = 0; unsigned int z1, z2, z; - unsigned int fsm; - status = titsc_readl(ts_dev, REG_IRQSTATUS); + status = titsc_readl(ts_dev, REG_RAWIRQSTATUS); + if (status & IRQENB_HW_PEN) { + ts_dev->pen_down = true; + titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00); + titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN); + irqclr |= IRQENB_HW_PEN; + } + + if (status & IRQENB_PENUP) { + fsm = titsc_readl(ts_dev, REG_ADCFSM); + if (fsm == ADCFSM_STEPID) { + ts_dev->pen_down = false; + input_report_key(input_dev, BTN_TOUCH, 0); + input_report_abs(input_dev, ABS_PRESSURE, 0); + input_sync(input_dev); + } else { + ts_dev->pen_down = true; + } + irqclr |= IRQENB_PENUP; + } + + if (status & IRQENB_EOS) + irqclr |= IRQENB_EOS; + /* * ADC and touchscreen share the IRQ line. * FIFO1 interrupts are used by ADC. Handle FIFO0 IRQs here only @@ -291,37 +324,11 @@ static irqreturn_t titsc_irq(int irq, void *dev) } irqclr |= IRQENB_FIFO0THRES; } - - /* - * Time for sequencer to settle, to read - * correct state of the sequencer. - */ - udelay(SEQ_SETTLE); - - status = titsc_readl(ts_dev, REG_RAWIRQSTATUS); - if (status & IRQENB_PENUP) { - /* Pen up event */ - fsm = titsc_readl(ts_dev, REG_ADCFSM); - if (fsm == ADCFSM_STEPID) { - ts_dev->pen_down = false; - input_report_key(input_dev, BTN_TOUCH, 0); - input_report_abs(input_dev, ABS_PRESSURE, 0); - input_sync(input_dev); - } else { - ts_dev->pen_down = true; - } - irqclr |= IRQENB_PENUP; - } - - if (status & IRQENB_HW_PEN) { - - titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00); - titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN); - } - if (irqclr) { titsc_writel(ts_dev, REG_IRQSTATUS, irqclr); - am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask); + if (status & IRQENB_EOS) + am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, + ts_dev->step_mask); return IRQ_HANDLED; } return IRQ_NONE; @@ -368,6 +375,23 @@ static int titsc_parse_dt(struct platform_device *pdev, if (err < 0) return err; + if (ts_dev->coordinate_readouts <= 0) { + dev_warn(&pdev->dev, + "invalid co-ordinate readouts, resetting it to 5\n"); + ts_dev->coordinate_readouts = 5; + } + + err = of_property_read_u32(node, "ti,charge-delay", + &ts_dev->charge_delay); + /* + * If ti,charge-delay value is not specified, then use + * CHARGEDLY_OPENDLY as the default value. + */ + if (err < 0) { + ts_dev->charge_delay = CHARGEDLY_OPENDLY; + dev_warn(&pdev->dev, "ti,charge-delay not specified\n"); + } + return of_property_read_u32_array(node, "ti,wire-config", ts_dev->config_inp, ARRAY_SIZE(ts_dev->config_inp)); } @@ -411,6 +435,7 @@ static int titsc_probe(struct platform_device *pdev) } titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES); + titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_EOS); err = titsc_config_wires(ts_dev); if (err) { dev_err(&pdev->dev, "wrong i/p wire configuration\n"); diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c index 98024856df07..59de6364a910 100644 --- a/drivers/iommu/amd_iommu.c +++ b/drivers/iommu/amd_iommu.c @@ -4284,7 +4284,6 @@ static int alloc_hpet_msi(unsigned int irq, unsigned int id) } struct irq_remap_ops amd_iommu_irq_ops = { - .supported = amd_iommu_supported, .prepare = amd_iommu_prepare, .enable = amd_iommu_enable, .disable = amd_iommu_disable, diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c index b0522f15730f..9a20248e7068 100644 --- a/drivers/iommu/amd_iommu_init.c +++ b/drivers/iommu/amd_iommu_init.c @@ -2014,9 +2014,6 @@ static bool detect_ivrs(void) /* Make sure ACS will be enabled during PCI probe */ pci_request_acs(); - if (!disable_irq_remap) - amd_iommu_irq_remap = true; - return true; } @@ -2123,12 +2120,14 @@ static int __init iommu_go_to_state(enum iommu_init_state state) #ifdef CONFIG_IRQ_REMAP int __init amd_iommu_prepare(void) { - return iommu_go_to_state(IOMMU_ACPI_FINISHED); -} + int ret; -int __init amd_iommu_supported(void) -{ - return amd_iommu_irq_remap ? 1 : 0; + amd_iommu_irq_remap = true; + + ret = iommu_go_to_state(IOMMU_ACPI_FINISHED); + if (ret) + return ret; + return amd_iommu_irq_remap ? 0 : -ENODEV; } int __init amd_iommu_enable(void) diff --git a/drivers/iommu/amd_iommu_proto.h b/drivers/iommu/amd_iommu_proto.h index 95ed6deae47f..861af9d8338a 100644 --- a/drivers/iommu/amd_iommu_proto.h +++ b/drivers/iommu/amd_iommu_proto.h @@ -33,7 +33,6 @@ extern void amd_iommu_init_notifier(void); extern void amd_iommu_init_api(void); /* Needed for interrupt remapping */ -extern int amd_iommu_supported(void); extern int amd_iommu_prepare(void); extern int amd_iommu_enable(void); extern void amd_iommu_disable(void); diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index 1232336b960e..40dfbc0444c0 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -4029,14 +4029,6 @@ static int device_notifier(struct notifier_block *nb, if (action != BUS_NOTIFY_REMOVED_DEVICE) return 0; - /* - * If the device is still attached to a device driver we can't - * tear down the domain yet as DMA mappings may still be in use. - * Wait for the BUS_NOTIFY_UNBOUND_DRIVER event to do that. - */ - if (action == BUS_NOTIFY_DEL_DEVICE && dev->driver != NULL) - return 0; - domain = find_domain(dev); if (!domain) return 0; @@ -4428,6 +4420,10 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, domain_remove_one_dev_info(old_domain, dev); else domain_remove_dev_info(old_domain); + + if (!domain_type_is_vm_or_si(old_domain) && + list_empty(&old_domain->devices)) + domain_exit(old_domain); } } diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c index a55b207b9425..14de1ab223c8 100644 --- a/drivers/iommu/intel_irq_remapping.c +++ b/drivers/iommu/intel_irq_remapping.c @@ -32,8 +32,9 @@ struct hpet_scope { }; #define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0) -#define IRTE_DEST(dest) ((x2apic_mode) ? dest : dest << 8) +#define IRTE_DEST(dest) ((eim_mode) ? dest : dest << 8) +static int __read_mostly eim_mode; static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; static struct hpet_scope ir_hpet[MAX_HPET_TBS]; @@ -481,11 +482,11 @@ static int intel_setup_irq_remapping(struct intel_iommu *iommu) if (iommu->ir_table) return 0; - ir_table = kzalloc(sizeof(struct ir_table), GFP_ATOMIC); + ir_table = kzalloc(sizeof(struct ir_table), GFP_KERNEL); if (!ir_table) return -ENOMEM; - pages = alloc_pages_node(iommu->node, GFP_ATOMIC | __GFP_ZERO, + pages = alloc_pages_node(iommu->node, GFP_KERNEL | __GFP_ZERO, INTR_REMAP_PAGE_ORDER); if (!pages) { @@ -566,13 +567,27 @@ static int __init dmar_x2apic_optout(void) return dmar->flags & DMAR_X2APIC_OPT_OUT; } -static int __init intel_irq_remapping_supported(void) +static void __init intel_cleanup_irq_remapping(void) +{ + struct dmar_drhd_unit *drhd; + struct intel_iommu *iommu; + + for_each_iommu(iommu, drhd) { + if (ecap_ir_support(iommu->ecap)) { + iommu_disable_irq_remapping(iommu); + intel_teardown_irq_remapping(iommu); + } + } + + if (x2apic_supported()) + pr_warn("Failed to enable irq remapping. You are vulnerable to irq-injection attacks.\n"); +} + +static int __init intel_prepare_irq_remapping(void) { struct dmar_drhd_unit *drhd; struct intel_iommu *iommu; - if (disable_irq_remap) - return 0; if (irq_remap_broken) { printk(KERN_WARNING "This system BIOS has enabled interrupt remapping\n" @@ -581,38 +596,45 @@ static int __init intel_irq_remapping_supported(void) "interrupt remapping is being disabled. Please\n" "contact your BIOS vendor for an update\n"); add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK); - disable_irq_remap = 1; - return 0; + return -ENODEV; } + if (dmar_table_init() < 0) + return -ENODEV; + if (!dmar_ir_support()) - return 0; + return -ENODEV; + + if (parse_ioapics_under_ir() != 1) { + printk(KERN_INFO "Not enabling interrupt remapping\n"); + goto error; + } + /* First make sure all IOMMUs support IRQ remapping */ for_each_iommu(iommu, drhd) if (!ecap_ir_support(iommu->ecap)) - return 0; + goto error; - return 1; + /* Do the allocations early */ + for_each_iommu(iommu, drhd) + if (intel_setup_irq_remapping(iommu)) + goto error; + + return 0; + +error: + intel_cleanup_irq_remapping(); + return -ENODEV; } static int __init intel_enable_irq_remapping(void) { struct dmar_drhd_unit *drhd; struct intel_iommu *iommu; - bool x2apic_present; int setup = 0; int eim = 0; - x2apic_present = x2apic_supported(); - - if (parse_ioapics_under_ir() != 1) { - printk(KERN_INFO "Not enable interrupt remapping\n"); - goto error; - } - - if (x2apic_present) { - pr_info("Queued invalidation will be enabled to support x2apic and Intr-remapping.\n"); - + if (x2apic_supported()) { eim = !dmar_x2apic_optout(); if (!eim) printk(KERN_WARNING @@ -646,16 +668,15 @@ static int __init intel_enable_irq_remapping(void) /* * check for the Interrupt-remapping support */ - for_each_iommu(iommu, drhd) { - if (!ecap_ir_support(iommu->ecap)) - continue; - + for_each_iommu(iommu, drhd) if (eim && !ecap_eim_support(iommu->ecap)) { printk(KERN_INFO "DRHD %Lx: EIM not supported by DRHD, " " ecap %Lx\n", drhd->reg_base_addr, iommu->ecap); - goto error; + eim = 0; } - } + eim_mode = eim; + if (eim) + pr_info("Queued invalidation will be enabled to support x2apic and Intr-remapping.\n"); /* * Enable queued invalidation for all the DRHD's. @@ -675,12 +696,6 @@ static int __init intel_enable_irq_remapping(void) * Setup Interrupt-remapping for all the DRHD's now. */ for_each_iommu(iommu, drhd) { - if (!ecap_ir_support(iommu->ecap)) - continue; - - if (intel_setup_irq_remapping(iommu)) - goto error; - iommu_set_irq_remapping(iommu, eim); setup = 1; } @@ -702,15 +717,7 @@ static int __init intel_enable_irq_remapping(void) return eim ? IRQ_REMAP_X2APIC_MODE : IRQ_REMAP_XAPIC_MODE; error: - for_each_iommu(iommu, drhd) - if (ecap_ir_support(iommu->ecap)) { - iommu_disable_irq_remapping(iommu); - intel_teardown_irq_remapping(iommu); - } - - if (x2apic_present) - pr_warn("Failed to enable irq remapping. You are vulnerable to irq-injection attacks.\n"); - + intel_cleanup_irq_remapping(); return -1; } @@ -1199,8 +1206,7 @@ static int intel_alloc_hpet_msi(unsigned int irq, unsigned int id) } struct irq_remap_ops intel_irq_remap_ops = { - .supported = intel_irq_remapping_supported, - .prepare = dmar_table_init, + .prepare = intel_prepare_irq_remapping, .enable = intel_enable_irq_remapping, .disable = disable_irq_remapping, .reenable = reenable_irq_remapping, diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c index 68dfb0fd5ee9..748693192c20 100644 --- a/drivers/iommu/ipmmu-vmsa.c +++ b/drivers/iommu/ipmmu-vmsa.c @@ -558,7 +558,7 @@ static pmd_t *ipmmu_alloc_pmd(struct ipmmu_vmsa_device *mmu, pgd_t *pgd, static u64 ipmmu_page_prot(unsigned int prot, u64 type) { - u64 pgprot = ARM_VMSA_PTE_XN | ARM_VMSA_PTE_nG | ARM_VMSA_PTE_AF + u64 pgprot = ARM_VMSA_PTE_nG | ARM_VMSA_PTE_AF | ARM_VMSA_PTE_SH_IS | ARM_VMSA_PTE_AP_UNPRIV | ARM_VMSA_PTE_NS | type; @@ -568,8 +568,8 @@ static u64 ipmmu_page_prot(unsigned int prot, u64 type) if (prot & IOMMU_CACHE) pgprot |= IMMAIR_ATTR_IDX_WBRWA << ARM_VMSA_PTE_ATTRINDX_SHIFT; - if (prot & IOMMU_EXEC) - pgprot &= ~ARM_VMSA_PTE_XN; + if (prot & IOMMU_NOEXEC) + pgprot |= ARM_VMSA_PTE_XN; else if (!(prot & (IOMMU_READ | IOMMU_WRITE))) /* If no access create a faulting entry to avoid TLB fills. */ pgprot &= ~ARM_VMSA_PTE_PAGE; diff --git a/drivers/iommu/irq_remapping.c b/drivers/iommu/irq_remapping.c index 89c4846683be..390079ee1350 100644 --- a/drivers/iommu/irq_remapping.c +++ b/drivers/iommu/irq_remapping.c @@ -17,12 +17,11 @@ #include "irq_remapping.h" int irq_remapping_enabled; - -int disable_irq_remap; int irq_remap_broken; int disable_sourceid_checking; int no_x2apic_optout; +static int disable_irq_remap; static struct irq_remap_ops *remap_ops; static int msi_alloc_remapped_irq(struct pci_dev *pdev, int irq, int nvec); @@ -194,45 +193,32 @@ static __init int setup_irqremap(char *str) } early_param("intremap", setup_irqremap); -void __init setup_irq_remapping_ops(void) -{ - remap_ops = &intel_irq_remap_ops; - -#ifdef CONFIG_AMD_IOMMU - if (amd_iommu_irq_ops.prepare() == 0) - remap_ops = &amd_iommu_irq_ops; -#endif -} - void set_irq_remapping_broken(void) { irq_remap_broken = 1; } -int irq_remapping_supported(void) +int __init irq_remapping_prepare(void) { if (disable_irq_remap) - return 0; - - if (!remap_ops || !remap_ops->supported) - return 0; - - return remap_ops->supported(); -} + return -ENOSYS; -int __init irq_remapping_prepare(void) -{ - if (!remap_ops || !remap_ops->prepare) - return -ENODEV; + if (intel_irq_remap_ops.prepare() == 0) + remap_ops = &intel_irq_remap_ops; + else if (IS_ENABLED(CONFIG_AMD_IOMMU) && + amd_iommu_irq_ops.prepare() == 0) + remap_ops = &amd_iommu_irq_ops; + else + return -ENOSYS; - return remap_ops->prepare(); + return 0; } int __init irq_remapping_enable(void) { int ret; - if (!remap_ops || !remap_ops->enable) + if (!remap_ops->enable) return -ENODEV; ret = remap_ops->enable(); @@ -245,22 +231,16 @@ int __init irq_remapping_enable(void) void irq_remapping_disable(void) { - if (!irq_remapping_enabled || - !remap_ops || - !remap_ops->disable) - return; - - remap_ops->disable(); + if (irq_remapping_enabled && remap_ops->disable) + remap_ops->disable(); } int irq_remapping_reenable(int mode) { - if (!irq_remapping_enabled || - !remap_ops || - !remap_ops->reenable) - return 0; + if (irq_remapping_enabled && remap_ops->reenable) + return remap_ops->reenable(mode); - return remap_ops->reenable(mode); + return 0; } int __init irq_remap_enable_fault_handling(void) @@ -268,7 +248,7 @@ int __init irq_remap_enable_fault_handling(void) if (!irq_remapping_enabled) return 0; - if (!remap_ops || !remap_ops->enable_faulting) + if (!remap_ops->enable_faulting) return -ENODEV; return remap_ops->enable_faulting(); @@ -279,7 +259,7 @@ int setup_ioapic_remapped_entry(int irq, unsigned int destination, int vector, struct io_apic_irq_attr *attr) { - if (!remap_ops || !remap_ops->setup_ioapic_entry) + if (!remap_ops->setup_ioapic_entry) return -ENODEV; return remap_ops->setup_ioapic_entry(irq, entry, destination, @@ -289,8 +269,7 @@ int setup_ioapic_remapped_entry(int irq, static int set_remapped_irq_affinity(struct irq_data *data, const struct cpumask *mask, bool force) { - if (!config_enabled(CONFIG_SMP) || !remap_ops || - !remap_ops->set_affinity) + if (!config_enabled(CONFIG_SMP) || !remap_ops->set_affinity) return 0; return remap_ops->set_affinity(data, mask, force); @@ -300,10 +279,7 @@ void free_remapped_irq(int irq) { struct irq_cfg *cfg = irq_cfg(irq); - if (!remap_ops || !remap_ops->free_irq) - return; - - if (irq_remapped(cfg)) + if (irq_remapped(cfg) && remap_ops->free_irq) remap_ops->free_irq(irq); } @@ -315,13 +291,13 @@ void compose_remapped_msi_msg(struct pci_dev *pdev, if (!irq_remapped(cfg)) native_compose_msi_msg(pdev, irq, dest, msg, hpet_id); - else if (remap_ops && remap_ops->compose_msi_msg) + else if (remap_ops->compose_msi_msg) remap_ops->compose_msi_msg(pdev, irq, dest, msg, hpet_id); } static int msi_alloc_remapped_irq(struct pci_dev *pdev, int irq, int nvec) { - if (!remap_ops || !remap_ops->msi_alloc_irq) + if (!remap_ops->msi_alloc_irq) return -ENODEV; return remap_ops->msi_alloc_irq(pdev, irq, nvec); @@ -330,7 +306,7 @@ static int msi_alloc_remapped_irq(struct pci_dev *pdev, int irq, int nvec) static int msi_setup_remapped_irq(struct pci_dev *pdev, unsigned int irq, int index, int sub_handle) { - if (!remap_ops || !remap_ops->msi_setup_irq) + if (!remap_ops->msi_setup_irq) return -ENODEV; return remap_ops->msi_setup_irq(pdev, irq, index, sub_handle); @@ -340,7 +316,7 @@ int setup_hpet_msi_remapped(unsigned int irq, unsigned int id) { int ret; - if (!remap_ops || !remap_ops->alloc_hpet_msi) + if (!remap_ops->alloc_hpet_msi) return -ENODEV; ret = remap_ops->alloc_hpet_msi(irq, id); diff --git a/drivers/iommu/irq_remapping.h b/drivers/iommu/irq_remapping.h index fde250f86e60..c448eb48340a 100644 --- a/drivers/iommu/irq_remapping.h +++ b/drivers/iommu/irq_remapping.h @@ -31,16 +31,12 @@ struct cpumask; struct pci_dev; struct msi_msg; -extern int disable_irq_remap; extern int irq_remap_broken; extern int disable_sourceid_checking; extern int no_x2apic_optout; extern int irq_remapping_enabled; struct irq_remap_ops { - /* Check whether Interrupt Remapping is supported */ - int (*supported)(void); - /* Initializes hardware and makes it ready for remapping interrupts */ int (*prepare)(void); @@ -89,7 +85,6 @@ extern struct irq_remap_ops amd_iommu_irq_ops; #else /* CONFIG_IRQ_REMAP */ #define irq_remapping_enabled 0 -#define disable_irq_remap 1 #define irq_remap_broken 0 #endif /* CONFIG_IRQ_REMAP */ diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c index b2023af384b9..6a8b1ec4a48a 100644 --- a/drivers/iommu/rockchip-iommu.c +++ b/drivers/iommu/rockchip-iommu.c @@ -1009,7 +1009,6 @@ static struct platform_driver rk_iommu_driver = { .remove = rk_iommu_remove, .driver = { .name = "rk_iommu", - .owner = THIS_MODULE, .of_match_table = of_match_ptr(rk_iommu_dt_ids), }, }; diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c index f722a0c466cf..c48da057dbb1 100644 --- a/drivers/iommu/tegra-gart.c +++ b/drivers/iommu/tegra-gart.c @@ -315,6 +315,7 @@ static const struct iommu_ops gart_iommu_ops = { .attach_dev = gart_iommu_attach_dev, .detach_dev = gart_iommu_detach_dev, .map = gart_iommu_map, + .map_sg = default_iommu_map_sg, .unmap = gart_iommu_unmap, .iova_to_phys = gart_iommu_iova_to_phys, .pgsize_bitmap = GART_IOMMU_PGSIZES, @@ -395,7 +396,7 @@ static int tegra_gart_probe(struct platform_device *pdev) do_gart_setup(gart, NULL); gart_handle = gart; - bus_set_iommu(&platform_bus_type, &gart_iommu_ops); + return 0; } diff --git a/drivers/irqchip/irq-atmel-aic-common.c b/drivers/irqchip/irq-atmel-aic-common.c index d111ac779c40..63cd031b2c28 100644 --- a/drivers/irqchip/irq-atmel-aic-common.c +++ b/drivers/irqchip/irq-atmel-aic-common.c @@ -28,7 +28,7 @@ #define AT91_AIC_IRQ_MIN_PRIORITY 0 #define AT91_AIC_IRQ_MAX_PRIORITY 7 -#define AT91_AIC_SRCTYPE GENMASK(7, 6) +#define AT91_AIC_SRCTYPE GENMASK(6, 5) #define AT91_AIC_SRCTYPE_LOW (0 << 5) #define AT91_AIC_SRCTYPE_FALLING (1 << 5) #define AT91_AIC_SRCTYPE_HIGH (2 << 5) @@ -74,7 +74,7 @@ int aic_common_set_type(struct irq_data *d, unsigned type, unsigned *val) return -EINVAL; } - *val &= AT91_AIC_SRCTYPE; + *val &= ~AT91_AIC_SRCTYPE; *val |= aic_type; return 0; diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 86e4684adeb1..d8996bdf0f61 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -1053,7 +1053,7 @@ static struct its_device *its_create_device(struct its_node *its, u32 dev_id, * of two entries. No, the architecture doesn't let you * express an ITT with a single entry. */ - nr_ites = max(2, roundup_pow_of_two(nvecs)); + nr_ites = max(2UL, roundup_pow_of_two(nvecs)); sz = nr_ites * its->ite_size; sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1; itt = kmalloc(sz, GFP_KERNEL); diff --git a/drivers/irqchip/irq-hip04.c b/drivers/irqchip/irq-hip04.c index 29b8f21b74d0..6bc2deb73d53 100644 --- a/drivers/irqchip/irq-hip04.c +++ b/drivers/irqchip/irq-hip04.c @@ -381,7 +381,7 @@ hip04_of_init(struct device_node *node, struct device_node *parent) * It will be refined as each CPU probes its ID. */ for (i = 0; i < NR_HIP04_CPU_IF; i++) - hip04_cpu_map[i] = 0xff; + hip04_cpu_map[i] = 0xffff; /* * Find out how many interrupts are supported. diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index 2b0468e3df6a..56b96c63dc4b 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c @@ -37,6 +37,7 @@ static struct irq_domain *gic_irq_domain; static int gic_shared_intrs; static int gic_vpes; static unsigned int gic_cpu_pin; +static unsigned int timer_cpu_pin; static struct irq_chip gic_level_irq_controller, gic_edge_irq_controller; static void __gic_irq_dispatch(void); @@ -616,6 +617,8 @@ static int gic_local_irq_domain_map(struct irq_domain *d, unsigned int virq, gic_write(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_MAP), val); break; case GIC_LOCAL_INT_TIMER: + /* CONFIG_MIPS_CMP workaround (see __gic_init) */ + val = GIC_MAP_TO_PIN_MSK | timer_cpu_pin; gic_write(GIC_REG(VPE_OTHER, GIC_VPE_TIMER_MAP), val); break; case GIC_LOCAL_INT_PERFCTR: @@ -713,12 +716,36 @@ static void __init __gic_init(unsigned long gic_base_addr, if (cpu_has_veic) { /* Always use vector 1 in EIC mode */ gic_cpu_pin = 0; + timer_cpu_pin = gic_cpu_pin; set_vi_handler(gic_cpu_pin + GIC_PIN_TO_VEC_OFFSET, __gic_irq_dispatch); } else { gic_cpu_pin = cpu_vec - GIC_CPU_PIN_OFFSET; irq_set_chained_handler(MIPS_CPU_IRQ_BASE + cpu_vec, gic_irq_dispatch); + /* + * With the CMP implementation of SMP (deprecated), other CPUs + * are started by the bootloader and put into a timer based + * waiting poll loop. We must not re-route those CPU's local + * timer interrupts as the wait instruction will never finish, + * so just handle whatever CPU interrupt it is routed to by + * default. + * + * This workaround should be removed when CMP support is + * dropped. + */ + if (IS_ENABLED(CONFIG_MIPS_CMP) && + gic_local_irq_is_routable(GIC_LOCAL_INT_TIMER)) { + timer_cpu_pin = gic_read(GIC_REG(VPE_LOCAL, + GIC_VPE_TIMER_MAP)) & + GIC_MAP_MSK; + irq_set_chained_handler(MIPS_CPU_IRQ_BASE + + GIC_CPU_PIN_OFFSET + + timer_cpu_pin, + gic_irq_dispatch); + } else { + timer_cpu_pin = gic_cpu_pin; + } } gic_irq_domain = irq_domain_add_simple(node, GIC_NUM_LOCAL_INTRS + diff --git a/drivers/irqchip/irq-mtk-sysirq.c b/drivers/irqchip/irq-mtk-sysirq.c index 7e342df6a62f..0b0d2c00a2df 100644 --- a/drivers/irqchip/irq-mtk-sysirq.c +++ b/drivers/irqchip/irq-mtk-sysirq.c @@ -137,9 +137,9 @@ static int __init mtk_sysirq_of_init(struct device_node *node, return -ENOMEM; chip_data->intpol_base = of_io_request_and_map(node, 0, "intpol"); - if (!chip_data->intpol_base) { + if (IS_ERR(chip_data->intpol_base)) { pr_err("mtk_sysirq: unable to map sysirq register\n"); - ret = -ENOMEM; + ret = PTR_ERR(chip_data->intpol_base); goto out_free; } diff --git a/drivers/irqchip/irq-omap-intc.c b/drivers/irqchip/irq-omap-intc.c index 28718d3e8281..c03f140acbae 100644 --- a/drivers/irqchip/irq-omap-intc.c +++ b/drivers/irqchip/irq-omap-intc.c @@ -263,7 +263,7 @@ static int __init omap_init_irq_of(struct device_node *node) return ret; } -static int __init omap_init_irq_legacy(u32 base) +static int __init omap_init_irq_legacy(u32 base, struct device_node *node) { int j, irq_base; @@ -277,7 +277,7 @@ static int __init omap_init_irq_legacy(u32 base) irq_base = 0; } - domain = irq_domain_add_legacy(NULL, omap_nr_irqs, irq_base, 0, + domain = irq_domain_add_legacy(node, omap_nr_irqs, irq_base, 0, &irq_domain_simple_ops, NULL); omap_irq_soft_reset(); @@ -301,10 +301,26 @@ static int __init omap_init_irq(u32 base, struct device_node *node) { int ret; - if (node) + /* + * FIXME legacy OMAP DMA driver sitting under arch/arm/plat-omap/dma.c + * depends is still not ready for linear IRQ domains; because of that + * we need to temporarily "blacklist" OMAP2 and OMAP3 devices from using + * linear IRQ Domain until that driver is finally fixed. + */ + if (of_device_is_compatible(node, "ti,omap2-intc") || + of_device_is_compatible(node, "ti,omap3-intc")) { + struct resource res; + + if (of_address_to_resource(node, 0, &res)) + return -ENOMEM; + + base = res.start; + ret = omap_init_irq_legacy(base, node); + } else if (node) { ret = omap_init_irq_of(node); - else - ret = omap_init_irq_legacy(base); + } else { + ret = omap_init_irq_legacy(base, NULL); + } if (ret == 0) omap_irq_enable_protection(); diff --git a/drivers/isdn/hardware/eicon/message.c b/drivers/isdn/hardware/eicon/message.c index a82e542ffc21..d7c286656a25 100644 --- a/drivers/isdn/hardware/eicon/message.c +++ b/drivers/isdn/hardware/eicon/message.c @@ -1474,7 +1474,7 @@ static byte connect_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, add_ai(plci, &parms[5]); sig_req(plci, REJECT, 0); } - else if (Reject == 1 || Reject > 9) + else if (Reject == 1 || Reject >= 9) { add_ai(plci, &parms[5]); sig_req(plci, HANGUP, 0); @@ -4880,7 +4880,7 @@ static void sig_ind(PLCI *plci) byte SS_Ind[] = "\x05\x02\x00\x02\x00\x00"; /* Hold_Ind struct*/ byte CF_Ind[] = "\x09\x02\x00\x06\x00\x00\x00\x00\x00\x00"; byte Interr_Err_Ind[] = "\x0a\x02\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; - byte CONF_Ind[] = "\x09\x16\x00\x06\x00\x00\0x00\0x00\0x00\0x00"; + byte CONF_Ind[] = "\x09\x16\x00\x06\x00\x00\x00\x00\x00\x00"; byte force_mt_info = false; byte dir; dword d; diff --git a/drivers/isdn/hardware/mISDN/mISDNipac.c b/drivers/isdn/hardware/mISDN/mISDNipac.c index ccd7d851be26..a77eea594b69 100644 --- a/drivers/isdn/hardware/mISDN/mISDNipac.c +++ b/drivers/isdn/hardware/mISDN/mISDNipac.c @@ -754,10 +754,10 @@ dbusy_timer_handler(struct isac_hw *isac) } static int -open_dchannel(struct isac_hw *isac, struct channel_req *rq) +open_dchannel_caller(struct isac_hw *isac, struct channel_req *rq, void *caller) { pr_debug("%s: %s dev(%d) open from %p\n", isac->name, __func__, - isac->dch.dev.id, __builtin_return_address(1)); + isac->dch.dev.id, caller); if (rq->protocol != ISDN_P_TE_S0) return -EINVAL; if (rq->adr.channel == 1) @@ -771,6 +771,12 @@ open_dchannel(struct isac_hw *isac, struct channel_req *rq) return 0; } +static int +open_dchannel(struct isac_hw *isac, struct channel_req *rq) +{ + return open_dchannel_caller(isac, rq, __builtin_return_address(0)); +} + static const char *ISACVer[] = {"2086/2186 V1.1", "2085 B1", "2085 B2", "2085 V2.3"}; @@ -1548,7 +1554,7 @@ ipac_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) case OPEN_CHANNEL: rq = arg; if (rq->protocol == ISDN_P_TE_S0) - err = open_dchannel(isac, rq); + err = open_dchannel_caller(isac, rq, __builtin_return_address(0)); else err = open_bchannel(ipac, rq); if (err) diff --git a/drivers/isdn/hardware/mISDN/w6692.c b/drivers/isdn/hardware/mISDN/w6692.c index de69f6828c76..741675525b53 100644 --- a/drivers/isdn/hardware/mISDN/w6692.c +++ b/drivers/isdn/hardware/mISDN/w6692.c @@ -1176,10 +1176,10 @@ w6692_l1callback(struct dchannel *dch, u32 cmd) } static int -open_dchannel(struct w6692_hw *card, struct channel_req *rq) +open_dchannel(struct w6692_hw *card, struct channel_req *rq, void *caller) { pr_debug("%s: %s dev(%d) open from %p\n", card->name, __func__, - card->dch.dev.id, __builtin_return_address(1)); + card->dch.dev.id, caller); if (rq->protocol != ISDN_P_TE_S0) return -EINVAL; if (rq->adr.channel == 1) @@ -1207,7 +1207,7 @@ w6692_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) case OPEN_CHANNEL: rq = arg; if (rq->protocol == ISDN_P_TE_S0) - err = open_dchannel(card, rq); + err = open_dchannel(card, rq, __builtin_return_address(0)); else err = open_bchannel(card, rq); if (err) diff --git a/drivers/isdn/hisax/hfc4s8s_l1.c b/drivers/isdn/hisax/hfc4s8s_l1.c index fc9f9d03fa13..0e5d673871c0 100644 --- a/drivers/isdn/hisax/hfc4s8s_l1.c +++ b/drivers/isdn/hisax/hfc4s8s_l1.c @@ -225,20 +225,6 @@ fWrite_hfc8(hfc4s8s_hw *a, u_char c) } static inline void -Write_hfc16(hfc4s8s_hw *a, u_char b, u_short c) -{ - SetRegAddr(a, b); - outw(c, a->iobase); -} - -static inline void -Write_hfc32(hfc4s8s_hw *a, u_char b, u_long c) -{ - SetRegAddr(a, b); - outl(c, a->iobase); -} - -static inline void fWrite_hfc32(hfc4s8s_hw *a, u_long c) { outl(c, a->iobase); @@ -266,13 +252,6 @@ Read_hfc16(hfc4s8s_hw *a, u_char b) } static inline u_long -Read_hfc32(hfc4s8s_hw *a, u_char b) -{ - SetRegAddr(a, b); - return (inl((volatile u_int) a->iobase)); -} - -static inline u_long fRead_hfc32(hfc4s8s_hw *a) { return (inl((volatile u_int) a->iobase)); diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c index 5a4da94aefb0..ef9c8e4f1fa2 100644 --- a/drivers/isdn/isdnloop/isdnloop.c +++ b/drivers/isdn/isdnloop/isdnloop.c @@ -59,7 +59,8 @@ isdnloop_bchan_send(isdnloop_card *card, int ch) isdn_ctrl cmd; while (card->sndcount[ch]) { - if ((skb = skb_dequeue(&card->bqueue[ch]))) { + skb = skb_dequeue(&card->bqueue[ch]); + if (skb) { len = skb->len; card->sndcount[ch] -= len; ack = *(skb->head); /* used as scratch area */ @@ -149,8 +150,7 @@ typedef struct isdnloop_stat { int action; } isdnloop_stat; /* *INDENT-OFF* */ -static isdnloop_stat isdnloop_stat_table[] = -{ +static isdnloop_stat isdnloop_stat_table[] = { {"BCON_", ISDN_STAT_BCONN, 1}, /* B-Channel connected */ {"BDIS_", ISDN_STAT_BHUP, 2}, /* B-Channel disconnected */ {"DCON_", ISDN_STAT_DCONN, 0}, /* D-Channel connected */ @@ -317,7 +317,8 @@ isdnloop_polldchan(unsigned long data) u_char *p; isdn_ctrl cmd; - if ((skb = skb_dequeue(&card->dqueue))) + skb = skb_dequeue(&card->dqueue); + if (skb) avail = skb->len; else avail = 0; @@ -471,8 +472,8 @@ isdnloop_fake(isdnloop_card *card, char *s, int ch) { struct sk_buff *skb; int len = strlen(s) + ((ch >= 0) ? 3 : 0); - - if (!(skb = dev_alloc_skb(len))) { + skb = dev_alloc_skb(len); + if (!skb) { printk(KERN_WARNING "isdnloop: Out of memory in isdnloop_fake\n"); return 1; } @@ -483,8 +484,7 @@ isdnloop_fake(isdnloop_card *card, char *s, int ch) return 0; } /* *INDENT-OFF* */ -static isdnloop_stat isdnloop_cmd_table[] = -{ +static isdnloop_stat isdnloop_cmd_table[] = { {"BCON_R", 0, 1}, /* B-Channel connect */ {"BCON_I", 0, 17}, /* B-Channel connect ind */ {"BDIS_R", 0, 2}, /* B-Channel disconnect */ @@ -525,10 +525,8 @@ isdnloop_fake_err(isdnloop_card *card) isdnloop_fake(card, "NAK", -1); } -static u_char ctable_eu[] = -{0x00, 0x11, 0x01, 0x12}; -static u_char ctable_1t[] = -{0x00, 0x3b, 0x01, 0x3a}; +static u_char ctable_eu[] = {0x00, 0x11, 0x01, 0x12}; +static u_char ctable_1t[] = {0x00, 0x3b, 0x01, 0x3a}; /* * Assemble a simplified cause message depending on the @@ -554,9 +552,9 @@ isdnloop_unicause(isdnloop_card *card, int loc, int cau) sprintf(buf, "%02X44", ctable_1t[cau]); break; default: - return ("0000"); + return "0000"; } - return (buf); + return buf; } /* @@ -647,10 +645,8 @@ isdnloop_kill_ctimer(isdnloop_card *card, int ch) spin_unlock_irqrestore(&card->isdnloop_lock, flags); } -static u_char si2bit[] = -{0, 1, 0, 0, 0, 2, 0, 4, 0, 0}; -static u_char bit2si[] = -{1, 5, 7}; +static u_char si2bit[] = {0, 1, 0, 0, 0, 2, 0, 4, 0, 0}; +static u_char bit2si[] = {1, 5, 7}; /* * Try finding a listener for an outgoing call. @@ -754,17 +750,17 @@ isdnloop_vstphone(isdnloop_card *card, char *phone, int caller) if (caller) { for (i = 0; i < 2; i++) if (!(strcmp(card->s0num[i], phone))) - return (phone); - return (card->s0num[0]); + return phone; + return card->s0num[0]; } - return (phone); + return phone; break; case ISDN_PTYPE_1TR6: if (caller) { sprintf(nphone, "%s%c", card->s0num[0], phone[0]); - return (nphone); + return nphone; } else - return (&phone[strlen(phone) - 1]); + return &phone[strlen(phone) - 1]; break; } return ""; @@ -1148,14 +1144,14 @@ isdnloop_command(isdn_ctrl *c, isdnloop_card *card) case ISDNLOOP_IOCTL_STARTUP: if (!access_ok(VERIFY_READ, (void *) a, sizeof(isdnloop_sdef))) return -EFAULT; - return (isdnloop_start(card, (isdnloop_sdef *) a)); + return isdnloop_start(card, (isdnloop_sdef *) a); break; case ISDNLOOP_IOCTL_ADDCARD: if (copy_from_user((char *)&cdef, (char *)a, sizeof(cdef))) return -EFAULT; - return (isdnloop_addcard(cdef.id1)); + return isdnloop_addcard(cdef.id1); break; case ISDNLOOP_IOCTL_LEASEDCFG: if (a) { @@ -1377,7 +1373,7 @@ if_command(isdn_ctrl *c) isdnloop_card *card = isdnloop_findcard(c->driver); if (card) - return (isdnloop_command(c, card)); + return isdnloop_command(c, card); printk(KERN_ERR "isdnloop: if_command called with invalid driverId!\n"); return -ENODEV; @@ -1391,7 +1387,7 @@ if_writecmd(const u_char __user *buf, int len, int id, int channel) if (card) { if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) return -ENODEV; - return (isdnloop_writecmd(buf, len, 1, card)); + return isdnloop_writecmd(buf, len, 1, card); } printk(KERN_ERR "isdnloop: if_writecmd called with invalid driverId!\n"); @@ -1406,7 +1402,7 @@ if_readstatus(u_char __user *buf, int len, int id, int channel) if (card) { if (!(card->flags & ISDNLOOP_FLAGS_RUNNING)) return -ENODEV; - return (isdnloop_readstatus(buf, len, card)); + return isdnloop_readstatus(buf, len, card); } printk(KERN_ERR "isdnloop: if_readstatus called with invalid driverId!\n"); @@ -1423,7 +1419,7 @@ if_sendbuf(int id, int channel, int ack, struct sk_buff *skb) return -ENODEV; /* ack request stored in skb scratch area */ *(skb->head) = ack; - return (isdnloop_sendbuf(channel, skb, card)); + return isdnloop_sendbuf(channel, skb, card); } printk(KERN_ERR "isdnloop: if_sendbuf called with invalid driverId!\n"); @@ -1439,8 +1435,8 @@ isdnloop_initcard(char *id) { isdnloop_card *card; int i; - - if (!(card = kzalloc(sizeof(isdnloop_card), GFP_KERNEL))) { + card = kzalloc(sizeof(isdnloop_card), GFP_KERNEL); + if (!card) { printk(KERN_WARNING "isdnloop: (%s) Could not allocate card-struct.\n", id); return (isdnloop_card *) 0; @@ -1489,8 +1485,8 @@ static int isdnloop_addcard(char *id1) { isdnloop_card *card; - - if (!(card = isdnloop_initcard(id1))) { + card = isdnloop_initcard(id1); + if (!card) { return -EIO; } printk(KERN_INFO @@ -1503,7 +1499,7 @@ static int __init isdnloop_init(void) { if (isdnloop_id) - return (isdnloop_addcard(isdnloop_id)); + return isdnloop_addcard(isdnloop_id); return 0; } diff --git a/drivers/isdn/sc/init.c b/drivers/isdn/sc/init.c index d6f19b168e8a..3597ef47b28a 100644 --- a/drivers/isdn/sc/init.c +++ b/drivers/isdn/sc/init.c @@ -30,7 +30,7 @@ static const char *boardname[] = { "DataCommute/BRI", "DataCommute/PRI", "TeleCo static unsigned int io[] = {0, 0, 0, 0}; static unsigned char irq[] = {0, 0, 0, 0}; static unsigned long ram[] = {0, 0, 0, 0}; -static bool do_reset = 0; +static bool do_reset; module_param_array(io, int, NULL, 0); module_param_array(irq, byte, NULL, 0); @@ -104,13 +104,12 @@ static int __init sc_init(void) io[b] + 0x400 * EXP_PAGE0); continue; } - } - else { + } else { /* * Yes, probe for I/O Base */ if (probe_exhasted) { - pr_debug("All probe addresses exhasted, skipping\n"); + pr_debug("All probe addresses exhausted, skipping\n"); continue; } pr_debug("Probing for I/O...\n"); @@ -169,8 +168,7 @@ static int __init sc_init(void) model = identify_board(ram[b], io[b]); release_region(ram[b], SRAM_PAGESIZE); } - } - else { + } else { /* * Yes, probe for free RAM and look for * a signature and id the board model @@ -187,7 +185,7 @@ static int __init sc_init(void) ram[b] = i; break; } - pr_debug(" Unidentifed or inaccessible\n"); + pr_debug(" Unidentified or inaccessible\n"); continue; } pr_debug(" request failed\n"); @@ -337,8 +335,7 @@ static int __init sc_init(void) sc_adapter[cinst]->interrupt = irq[b]; if (request_irq(sc_adapter[cinst]->interrupt, interrupt_handler, 0, interface->id, - (void *)(unsigned long) cinst)) - { + (void *)(unsigned long) cinst)) { kfree(sc_adapter[cinst]->channel); indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL); /* Fix me */ kfree(interface); diff --git a/drivers/leds/leds-netxbig.c b/drivers/leds/leds-netxbig.c index 26515c27ea8c..25e419752a7b 100644 --- a/drivers/leds/leds-netxbig.c +++ b/drivers/leds/leds-netxbig.c @@ -330,18 +330,18 @@ create_netxbig_led(struct platform_device *pdev, led_dat->sata = 0; led_dat->cdev.brightness = LED_OFF; led_dat->cdev.flags |= LED_CORE_SUSPENDRESUME; - /* - * If available, expose the SATA activity blink capability through - * a "sata" sysfs attribute. - */ - if (led_dat->mode_val[NETXBIG_LED_SATA] != NETXBIG_LED_INVALID_MODE) - led_dat->cdev.groups = netxbig_led_groups; led_dat->mode_addr = template->mode_addr; led_dat->mode_val = template->mode_val; led_dat->bright_addr = template->bright_addr; led_dat->bright_max = (1 << pdata->gpio_ext->num_data) - 1; led_dat->timer = pdata->timer; led_dat->num_timer = pdata->num_timer; + /* + * If available, expose the SATA activity blink capability through + * a "sata" sysfs attribute. + */ + if (led_dat->mode_val[NETXBIG_LED_SATA] != NETXBIG_LED_INVALID_MODE) + led_dat->cdev.groups = netxbig_led_groups; return led_classdev_register(&pdev->dev, &led_dat->cdev); } diff --git a/drivers/mailbox/pcc.c b/drivers/mailbox/pcc.c index 6dbf6fcbdfaf..e8902f8dddfc 100644 --- a/drivers/mailbox/pcc.c +++ b/drivers/mailbox/pcc.c @@ -386,7 +386,7 @@ static int __init pcc_init(void) ret = acpi_pcc_probe(); if (ret) { - pr_err("ACPI PCC probe failed.\n"); + pr_debug("ACPI PCC probe failed.\n"); return -ENODEV; } @@ -394,7 +394,7 @@ static int __init pcc_init(void) pcc_mbox_probe, NULL, 0, NULL, 0); if (!pcc_pdev) { - pr_err("Err creating PCC platform bundle\n"); + pr_debug("Err creating PCC platform bundle\n"); return -ENODEV; } diff --git a/drivers/mcb/mcb-internal.h b/drivers/mcb/mcb-internal.h index f956ef26c0ce..fb7493dcfb79 100644 --- a/drivers/mcb/mcb-internal.h +++ b/drivers/mcb/mcb-internal.h @@ -7,6 +7,7 @@ #define PCI_DEVICE_ID_MEN_CHAMELEON 0x4d45 #define CHAMELEON_FILENAME_LEN 12 #define CHAMELEONV2_MAGIC 0xabce +#define CHAM_HEADER_SIZE 0x200 enum chameleon_descriptor_type { CHAMELEON_DTYPE_GENERAL = 0x0, diff --git a/drivers/mcb/mcb-pci.c b/drivers/mcb/mcb-pci.c index b59181965643..5e1bd5db02c8 100644 --- a/drivers/mcb/mcb-pci.c +++ b/drivers/mcb/mcb-pci.c @@ -17,6 +17,7 @@ struct priv { struct mcb_bus *bus; + phys_addr_t mapbase; void __iomem *base; }; @@ -31,8 +32,8 @@ static int mcb_pci_get_irq(struct mcb_device *mdev) static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { + struct resource *res; struct priv *priv; - phys_addr_t mapbase; int ret; int num_cells; unsigned long flags; @@ -47,19 +48,21 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) return -ENODEV; } - mapbase = pci_resource_start(pdev, 0); - if (!mapbase) { + priv->mapbase = pci_resource_start(pdev, 0); + if (!priv->mapbase) { dev_err(&pdev->dev, "No PCI resource\n"); goto err_start; } - ret = pci_request_region(pdev, 0, KBUILD_MODNAME); - if (ret) { - dev_err(&pdev->dev, "Failed to request PCI BARs\n"); + res = request_mem_region(priv->mapbase, CHAM_HEADER_SIZE, + KBUILD_MODNAME); + if (IS_ERR(res)) { + dev_err(&pdev->dev, "Failed to request PCI memory\n"); + ret = PTR_ERR(res); goto err_start; } - priv->base = pci_iomap(pdev, 0, 0); + priv->base = ioremap(priv->mapbase, CHAM_HEADER_SIZE); if (!priv->base) { dev_err(&pdev->dev, "Cannot ioremap\n"); ret = -ENOMEM; @@ -84,7 +87,7 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) priv->bus->get_irq = mcb_pci_get_irq; - ret = chameleon_parse_cells(priv->bus, mapbase, priv->base); + ret = chameleon_parse_cells(priv->bus, priv->mapbase, priv->base); if (ret < 0) goto err_drvdata; num_cells = ret; @@ -93,8 +96,10 @@ static int mcb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) mcb_bus_add_devices(priv->bus); + return 0; + err_drvdata: - pci_iounmap(pdev, priv->base); + iounmap(priv->base); err_ioremap: pci_release_region(pdev, 0); err_start: @@ -107,6 +112,10 @@ static void mcb_pci_remove(struct pci_dev *pdev) struct priv *priv = pci_get_drvdata(pdev); mcb_release_bus(priv->bus); + + iounmap(priv->base); + release_region(priv->mapbase, CHAM_HEADER_SIZE); + pci_disable_device(pdev); } static const struct pci_device_id mcb_pci_tbl[] = { diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 5bdedf6df153..c355a226a024 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -5,6 +5,7 @@ menuconfig MD bool "Multiple devices driver support (RAID and LVM)" depends on BLOCK + select SRCU help Support multiple physical spindles through a single logical device. Required for RAID and logical volume management. diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index da3604e73e8a..1695ee5f3ffc 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -72,6 +72,19 @@ __acquires(bitmap->lock) /* this page has not been allocated yet */ spin_unlock_irq(&bitmap->lock); + /* It is possible that this is being called inside a + * prepare_to_wait/finish_wait loop from raid5c:make_request(). + * In general it is not permitted to sleep in that context as it + * can cause the loop to spin freely. + * That doesn't apply here as we can only reach this point + * once with any loop. + * When this function completes, either bp[page].map or + * bp[page].hijacked. In either case, this function will + * abort before getting to this point again. So there is + * no risk of a free-spin, and so it is safe to assert + * that sleeping here is allowed. + */ + sched_annotate_sleep(); mappage = kzalloc(PAGE_SIZE, GFP_NOIO); spin_lock_irq(&bitmap->lock); diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c index 9fc616c2755e..c1c010498a21 100644 --- a/drivers/md/dm-cache-metadata.c +++ b/drivers/md/dm-cache-metadata.c @@ -94,6 +94,9 @@ struct cache_disk_superblock { } __packed; struct dm_cache_metadata { + atomic_t ref_count; + struct list_head list; + struct block_device *bdev; struct dm_block_manager *bm; struct dm_space_map *metadata_sm; @@ -669,10 +672,10 @@ static void unpack_value(__le64 value_le, dm_oblock_t *block, unsigned *flags) /*----------------------------------------------------------------*/ -struct dm_cache_metadata *dm_cache_metadata_open(struct block_device *bdev, - sector_t data_block_size, - bool may_format_device, - size_t policy_hint_size) +static struct dm_cache_metadata *metadata_open(struct block_device *bdev, + sector_t data_block_size, + bool may_format_device, + size_t policy_hint_size) { int r; struct dm_cache_metadata *cmd; @@ -680,9 +683,10 @@ struct dm_cache_metadata *dm_cache_metadata_open(struct block_device *bdev, cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { DMERR("could not allocate metadata struct"); - return NULL; + return ERR_PTR(-ENOMEM); } + atomic_set(&cmd->ref_count, 1); init_rwsem(&cmd->root_lock); cmd->bdev = bdev; cmd->data_block_size = data_block_size; @@ -705,10 +709,96 @@ struct dm_cache_metadata *dm_cache_metadata_open(struct block_device *bdev, return cmd; } +/* + * We keep a little list of ref counted metadata objects to prevent two + * different target instances creating separate bufio instances. This is + * an issue if a table is reloaded before the suspend. + */ +static DEFINE_MUTEX(table_lock); +static LIST_HEAD(table); + +static struct dm_cache_metadata *lookup(struct block_device *bdev) +{ + struct dm_cache_metadata *cmd; + + list_for_each_entry(cmd, &table, list) + if (cmd->bdev == bdev) { + atomic_inc(&cmd->ref_count); + return cmd; + } + + return NULL; +} + +static struct dm_cache_metadata *lookup_or_open(struct block_device *bdev, + sector_t data_block_size, + bool may_format_device, + size_t policy_hint_size) +{ + struct dm_cache_metadata *cmd, *cmd2; + + mutex_lock(&table_lock); + cmd = lookup(bdev); + mutex_unlock(&table_lock); + + if (cmd) + return cmd; + + cmd = metadata_open(bdev, data_block_size, may_format_device, policy_hint_size); + if (!IS_ERR(cmd)) { + mutex_lock(&table_lock); + cmd2 = lookup(bdev); + if (cmd2) { + mutex_unlock(&table_lock); + __destroy_persistent_data_objects(cmd); + kfree(cmd); + return cmd2; + } + list_add(&cmd->list, &table); + mutex_unlock(&table_lock); + } + + return cmd; +} + +static bool same_params(struct dm_cache_metadata *cmd, sector_t data_block_size) +{ + if (cmd->data_block_size != data_block_size) { + DMERR("data_block_size (%llu) different from that in metadata (%llu)\n", + (unsigned long long) data_block_size, + (unsigned long long) cmd->data_block_size); + return false; + } + + return true; +} + +struct dm_cache_metadata *dm_cache_metadata_open(struct block_device *bdev, + sector_t data_block_size, + bool may_format_device, + size_t policy_hint_size) +{ + struct dm_cache_metadata *cmd = lookup_or_open(bdev, data_block_size, + may_format_device, policy_hint_size); + + if (!IS_ERR(cmd) && !same_params(cmd, data_block_size)) { + dm_cache_metadata_close(cmd); + return ERR_PTR(-EINVAL); + } + + return cmd; +} + void dm_cache_metadata_close(struct dm_cache_metadata *cmd) { - __destroy_persistent_data_objects(cmd); - kfree(cmd); + if (atomic_dec_and_test(&cmd->ref_count)) { + mutex_lock(&table_lock); + list_del(&cmd->list); + mutex_unlock(&table_lock); + + __destroy_persistent_data_objects(cmd); + kfree(cmd); + } } /* diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c index 1e96d7889f51..e1650539cc2f 100644 --- a/drivers/md/dm-cache-target.c +++ b/drivers/md/dm-cache-target.c @@ -221,7 +221,13 @@ struct cache { struct list_head need_commit_migrations; sector_t migration_threshold; wait_queue_head_t migration_wait; - atomic_t nr_migrations; + atomic_t nr_allocated_migrations; + + /* + * The number of in flight migrations that are performing + * background io. eg, promotion, writeback. + */ + atomic_t nr_io_migrations; wait_queue_head_t quiescing_wait; atomic_t quiescing; @@ -258,7 +264,6 @@ struct cache { struct dm_deferred_set *all_io_ds; mempool_t *migration_pool; - struct dm_cache_migration *next_migration; struct dm_cache_policy *policy; unsigned policy_nr_args; @@ -350,10 +355,31 @@ static void free_prison_cell(struct cache *cache, struct dm_bio_prison_cell *cel dm_bio_prison_free_cell(cache->prison, cell); } +static struct dm_cache_migration *alloc_migration(struct cache *cache) +{ + struct dm_cache_migration *mg; + + mg = mempool_alloc(cache->migration_pool, GFP_NOWAIT); + if (mg) { + mg->cache = cache; + atomic_inc(&mg->cache->nr_allocated_migrations); + } + + return mg; +} + +static void free_migration(struct dm_cache_migration *mg) +{ + if (atomic_dec_and_test(&mg->cache->nr_allocated_migrations)) + wake_up(&mg->cache->migration_wait); + + mempool_free(mg, mg->cache->migration_pool); +} + static int prealloc_data_structs(struct cache *cache, struct prealloc *p) { if (!p->mg) { - p->mg = mempool_alloc(cache->migration_pool, GFP_NOWAIT); + p->mg = alloc_migration(cache); if (!p->mg) return -ENOMEM; } @@ -382,7 +408,7 @@ static void prealloc_free_structs(struct cache *cache, struct prealloc *p) free_prison_cell(cache, p->cell1); if (p->mg) - mempool_free(p->mg, cache->migration_pool); + free_migration(p->mg); } static struct dm_cache_migration *prealloc_get_migration(struct prealloc *p) @@ -854,24 +880,14 @@ static void remap_to_origin_then_cache(struct cache *cache, struct bio *bio, * Migration covers moving data from the origin device to the cache, or * vice versa. *--------------------------------------------------------------*/ -static void free_migration(struct dm_cache_migration *mg) -{ - mempool_free(mg, mg->cache->migration_pool); -} - -static void inc_nr_migrations(struct cache *cache) +static void inc_io_migrations(struct cache *cache) { - atomic_inc(&cache->nr_migrations); + atomic_inc(&cache->nr_io_migrations); } -static void dec_nr_migrations(struct cache *cache) +static void dec_io_migrations(struct cache *cache) { - atomic_dec(&cache->nr_migrations); - - /* - * Wake the worker in case we're suspending the target. - */ - wake_up(&cache->migration_wait); + atomic_dec(&cache->nr_io_migrations); } static void __cell_defer(struct cache *cache, struct dm_bio_prison_cell *cell, @@ -894,11 +910,10 @@ static void cell_defer(struct cache *cache, struct dm_bio_prison_cell *cell, wake_worker(cache); } -static void cleanup_migration(struct dm_cache_migration *mg) +static void free_io_migration(struct dm_cache_migration *mg) { - struct cache *cache = mg->cache; + dec_io_migrations(mg->cache); free_migration(mg); - dec_nr_migrations(cache); } static void migration_failure(struct dm_cache_migration *mg) @@ -923,7 +938,7 @@ static void migration_failure(struct dm_cache_migration *mg) cell_defer(cache, mg->new_ocell, true); } - cleanup_migration(mg); + free_io_migration(mg); } static void migration_success_pre_commit(struct dm_cache_migration *mg) @@ -934,7 +949,7 @@ static void migration_success_pre_commit(struct dm_cache_migration *mg) if (mg->writeback) { clear_dirty(cache, mg->old_oblock, mg->cblock); cell_defer(cache, mg->old_ocell, false); - cleanup_migration(mg); + free_io_migration(mg); return; } else if (mg->demote) { @@ -944,14 +959,14 @@ static void migration_success_pre_commit(struct dm_cache_migration *mg) mg->old_oblock); if (mg->promote) cell_defer(cache, mg->new_ocell, true); - cleanup_migration(mg); + free_io_migration(mg); return; } } else { if (dm_cache_insert_mapping(cache->cmd, mg->cblock, mg->new_oblock)) { DMWARN_LIMIT("promotion failed; couldn't update on disk metadata"); policy_remove_mapping(cache->policy, mg->new_oblock); - cleanup_migration(mg); + free_io_migration(mg); return; } } @@ -984,7 +999,7 @@ static void migration_success_post_commit(struct dm_cache_migration *mg) } else { if (mg->invalidate) policy_remove_mapping(cache->policy, mg->old_oblock); - cleanup_migration(mg); + free_io_migration(mg); } } else { @@ -999,7 +1014,7 @@ static void migration_success_post_commit(struct dm_cache_migration *mg) bio_endio(mg->new_ocell->holder, 0); cell_defer(cache, mg->new_ocell, false); } - cleanup_migration(mg); + free_io_migration(mg); } } @@ -1251,7 +1266,7 @@ static void promote(struct cache *cache, struct prealloc *structs, mg->new_ocell = cell; mg->start_jiffies = jiffies; - inc_nr_migrations(cache); + inc_io_migrations(cache); quiesce_migration(mg); } @@ -1275,7 +1290,7 @@ static void writeback(struct cache *cache, struct prealloc *structs, mg->new_ocell = NULL; mg->start_jiffies = jiffies; - inc_nr_migrations(cache); + inc_io_migrations(cache); quiesce_migration(mg); } @@ -1302,7 +1317,7 @@ static void demote_then_promote(struct cache *cache, struct prealloc *structs, mg->new_ocell = new_ocell; mg->start_jiffies = jiffies; - inc_nr_migrations(cache); + inc_io_migrations(cache); quiesce_migration(mg); } @@ -1330,7 +1345,7 @@ static void invalidate(struct cache *cache, struct prealloc *structs, mg->new_ocell = NULL; mg->start_jiffies = jiffies; - inc_nr_migrations(cache); + inc_io_migrations(cache); quiesce_migration(mg); } @@ -1412,7 +1427,7 @@ static void process_discard_bio(struct cache *cache, struct prealloc *structs, static bool spare_migration_bandwidth(struct cache *cache) { - sector_t current_volume = (atomic_read(&cache->nr_migrations) + 1) * + sector_t current_volume = (atomic_read(&cache->nr_io_migrations) + 1) * cache->sectors_per_block; return current_volume < cache->migration_threshold; } @@ -1764,7 +1779,7 @@ static void stop_quiescing(struct cache *cache) static void wait_for_migrations(struct cache *cache) { - wait_event(cache->migration_wait, !atomic_read(&cache->nr_migrations)); + wait_event(cache->migration_wait, !atomic_read(&cache->nr_allocated_migrations)); } static void stop_worker(struct cache *cache) @@ -1876,9 +1891,6 @@ static void destroy(struct cache *cache) { unsigned i; - if (cache->next_migration) - mempool_free(cache->next_migration, cache->migration_pool); - if (cache->migration_pool) mempool_destroy(cache->migration_pool); @@ -2424,7 +2436,8 @@ static int cache_create(struct cache_args *ca, struct cache **result) INIT_LIST_HEAD(&cache->quiesced_migrations); INIT_LIST_HEAD(&cache->completed_migrations); INIT_LIST_HEAD(&cache->need_commit_migrations); - atomic_set(&cache->nr_migrations, 0); + atomic_set(&cache->nr_allocated_migrations, 0); + atomic_set(&cache->nr_io_migrations, 0); init_waitqueue_head(&cache->migration_wait); init_waitqueue_head(&cache->quiescing_wait); @@ -2487,8 +2500,6 @@ static int cache_create(struct cache_args *ca, struct cache **result) goto bad; } - cache->next_migration = NULL; - cache->need_tick_bio = true; cache->sized = false; cache->invalidate = false; diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c index 493478989dbd..07705ee181e3 100644 --- a/drivers/md/dm-thin.c +++ b/drivers/md/dm-thin.c @@ -3385,6 +3385,12 @@ static int pool_message(struct dm_target *ti, unsigned argc, char **argv) struct pool_c *pt = ti->private; struct pool *pool = pt->pool; + if (get_pool_mode(pool) >= PM_READ_ONLY) { + DMERR("%s: unable to service pool target messages in READ_ONLY or FAIL mode", + dm_device_name(pool->pool_md)); + return -EINVAL; + } + if (!strcasecmp(argv[0], "create_thin")) r = process_create_thin_mesg(argc, argv, pool); diff --git a/drivers/md/dm.c b/drivers/md/dm.c index b98cd9d84435..2caf5b374649 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -206,6 +206,9 @@ struct mapped_device { /* zero-length flush that will be cloned and submitted to targets */ struct bio flush_bio; + /* the number of internal suspends */ + unsigned internal_suspend_count; + struct dm_stats stats; }; @@ -2928,7 +2931,7 @@ static void __dm_internal_suspend(struct mapped_device *md, unsigned suspend_fla { struct dm_table *map = NULL; - if (dm_suspended_internally_md(md)) + if (md->internal_suspend_count++) return; /* nested internal suspend */ if (dm_suspended_md(md)) { @@ -2953,7 +2956,9 @@ static void __dm_internal_suspend(struct mapped_device *md, unsigned suspend_fla static void __dm_internal_resume(struct mapped_device *md) { - if (!dm_suspended_internally_md(md)) + BUG_ON(!md->internal_suspend_count); + + if (--md->internal_suspend_count) return; /* resume from nested internal suspend */ if (dm_suspended_md(md)) diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index c1b0d52bfcb0..b98765f6f77f 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -3195,6 +3195,11 @@ static void handle_stripe_dirtying(struct r5conf *conf, (unsigned long long)sh->sector, rcw, qread, test_bit(STRIPE_DELAYED, &sh->state)); } + + if (rcw > disks && rmw > disks && + !test_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) + set_bit(STRIPE_DELAYED, &sh->state); + /* now if nothing is locked, and if we have enough data, * we can start a write request */ diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index b85f88c8ddbd..21154dd87b0b 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig @@ -8,10 +8,6 @@ comment "common driver options" config VIDEO_CX2341X tristate -config VIDEO_BTCX - depends on PCI - tristate - config VIDEO_TVEEPROM tristate depends on I2C diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index d208de3b7cc0..89b795df2cdd 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -1,5 +1,4 @@ obj-y += b2c2/ saa7146/ siano/ obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o -obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o obj-$(CONFIG_CYPRESS_FIRMWARE) += cypress_firmware.o diff --git a/drivers/media/common/btcx-risc.h b/drivers/media/common/btcx-risc.h index f8bc6e8e7b51..03583ef90506 100644 --- a/drivers/media/common/btcx-risc.h +++ b/drivers/media/common/btcx-risc.h @@ -26,9 +26,3 @@ void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips); void btcx_calc_skips(int line, int width, int *maxy, struct btcx_skiplist *skips, unsigned int *nskips, const struct v4l2_clip *clips, unsigned int nclips); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb-core/dvb_net.c b/drivers/media/dvb-core/dvb_net.c index e4041f074909..686d3277dad1 100644 --- a/drivers/media/dvb-core/dvb_net.c +++ b/drivers/media/dvb-core/dvb_net.c @@ -68,13 +68,6 @@ #include "dvb_demux.h" #include "dvb_net.h" -static int dvb_net_debug; -module_param(dvb_net_debug, int, 0444); -MODULE_PARM_DESC(dvb_net_debug, "enable debug messages"); - -#define dprintk(x...) do { if (dvb_net_debug) printk(x); } while (0) - - static inline __u32 iov_crc32( __u32 c, struct kvec *iov, unsigned int cnt ) { unsigned int j; @@ -90,36 +83,9 @@ static inline __u32 iov_crc32( __u32 c, struct kvec *iov, unsigned int cnt ) #ifdef ULE_DEBUG -#define MAC_ADDR_PRINTFMT "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x" -#define MAX_ADDR_PRINTFMT_ARGS(macap) (macap)[0],(macap)[1],(macap)[2],(macap)[3],(macap)[4],(macap)[5] - -#define isprint(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) - -static void hexdump( const unsigned char *buf, unsigned short len ) +static void hexdump(const unsigned char *buf, unsigned short len) { - char str[80], octet[10]; - int ofs, i, l; - - for (ofs = 0; ofs < len; ofs += 16) { - sprintf( str, "%03d: ", ofs ); - - for (i = 0; i < 16; i++) { - if ((i + ofs) < len) - sprintf( octet, "%02x ", buf[ofs + i] ); - else - strcpy( octet, " " ); - - strcat( str, octet ); - } - strcat( str, " " ); - l = strlen( str ); - - for (i = 0; (i < 16) && ((i + ofs) < len); i++) - str[l++] = isprint( buf[ofs + i] ) ? buf[ofs + i] : '.'; - - str[l] = '\0'; - printk( KERN_WARNING "%s\n", str ); - } + print_hex_dump_debug("", DUMP_PREFIX_OFFSET, 16, 1, buf, len, true); } #endif @@ -315,9 +281,9 @@ static int handle_ule_extensions( struct dvb_net_priv *p ) return l; /* Stop extension header processing and discard SNDU. */ total_ext_len += l; #ifdef ULE_DEBUG - dprintk("handle_ule_extensions: ule_next_hdr=%p, ule_sndu_type=%i, " - "l=%i, total_ext_len=%i\n", p->ule_next_hdr, - (int) p->ule_sndu_type, l, total_ext_len); + pr_debug("ule_next_hdr=%p, ule_sndu_type=%i, l=%i, total_ext_len=%i\n", + p->ule_next_hdr, (int)p->ule_sndu_type, + l, total_ext_len); #endif } while (p->ule_sndu_type < ETH_P_802_3_MIN); @@ -700,8 +666,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) if (drop) { #ifdef ULE_DEBUG - dprintk("Dropping SNDU: MAC destination address does not match: dest addr: "MAC_ADDR_PRINTFMT", dev addr: "MAC_ADDR_PRINTFMT"\n", - MAX_ADDR_PRINTFMT_ARGS(priv->ule_skb->data), MAX_ADDR_PRINTFMT_ARGS(dev->dev_addr)); + netdev_dbg(dev, "Dropping SNDU: MAC destination address does not match: dest addr: %pM, dev addr: %pM\n", + priv->ule_skb->data, dev->dev_addr); #endif dev_kfree_skb(priv->ule_skb); goto sndu_done; @@ -964,8 +930,7 @@ static int dvb_net_filter_sec_set(struct net_device *dev, (*secfilter)->filter_mask[10] = mac_mask[1]; (*secfilter)->filter_mask[11]=mac_mask[0]; - dprintk("%s: filter mac=%pM\n", dev->name, mac); - dprintk("%s: filter mask=%pM\n", dev->name, mac_mask); + netdev_dbg(dev, "filter mac=%pM mask=%pM\n", mac, mac_mask); return 0; } @@ -977,7 +942,7 @@ static int dvb_net_feed_start(struct net_device *dev) struct dmx_demux *demux = priv->demux; unsigned char *mac = (unsigned char *) dev->dev_addr; - dprintk("%s: rx_mode %i\n", __func__, priv->rx_mode); + netdev_dbg(dev, "rx_mode %i\n", priv->rx_mode); mutex_lock(&priv->mutex); if (priv->tsfeed || priv->secfeed || priv->secfilter || priv->multi_secfilter[0]) printk("%s: BUG %d\n", __func__, __LINE__); @@ -987,7 +952,7 @@ static int dvb_net_feed_start(struct net_device *dev) priv->tsfeed = NULL; if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) { - dprintk("%s: alloc secfeed\n", __func__); + netdev_dbg(dev, "alloc secfeed\n"); ret=demux->allocate_section_feed(demux, &priv->secfeed, dvb_net_sec_callback); if (ret<0) { @@ -1005,38 +970,38 @@ static int dvb_net_feed_start(struct net_device *dev) } if (priv->rx_mode != RX_MODE_PROMISC) { - dprintk("%s: set secfilter\n", __func__); + netdev_dbg(dev, "set secfilter\n"); dvb_net_filter_sec_set(dev, &priv->secfilter, mac, mask_normal); } switch (priv->rx_mode) { case RX_MODE_MULTI: for (i = 0; i < priv->multi_num; i++) { - dprintk("%s: set multi_secfilter[%d]\n", __func__, i); + netdev_dbg(dev, "set multi_secfilter[%d]\n", i); dvb_net_filter_sec_set(dev, &priv->multi_secfilter[i], priv->multi_macs[i], mask_normal); } break; case RX_MODE_ALL_MULTI: priv->multi_num=1; - dprintk("%s: set multi_secfilter[0]\n", __func__); + netdev_dbg(dev, "set multi_secfilter[0]\n"); dvb_net_filter_sec_set(dev, &priv->multi_secfilter[0], mac_allmulti, mask_allmulti); break; case RX_MODE_PROMISC: priv->multi_num=0; - dprintk("%s: set secfilter\n", __func__); + netdev_dbg(dev, "set secfilter\n"); dvb_net_filter_sec_set(dev, &priv->secfilter, mac, mask_promisc); break; } - dprintk("%s: start filtering\n", __func__); + netdev_dbg(dev, "start filtering\n"); priv->secfeed->start_filtering(priv->secfeed); } else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) { struct timespec timeout = { 0, 10000000 }; // 10 msec /* we have payloads encapsulated in TS */ - dprintk("%s: alloc tsfeed\n", __func__); + netdev_dbg(dev, "alloc tsfeed\n"); ret = demux->allocate_ts_feed(demux, &priv->tsfeed, dvb_net_ts_callback); if (ret < 0) { printk("%s: could not allocate ts feed\n", dev->name); @@ -1060,7 +1025,7 @@ static int dvb_net_feed_start(struct net_device *dev) goto error; } - dprintk("%s: start filtering\n", __func__); + netdev_dbg(dev, "start filtering\n"); priv->tsfeed->start_filtering(priv->tsfeed); } else ret = -EINVAL; @@ -1075,17 +1040,16 @@ static int dvb_net_feed_stop(struct net_device *dev) struct dvb_net_priv *priv = netdev_priv(dev); int i, ret = 0; - dprintk("%s\n", __func__); mutex_lock(&priv->mutex); if (priv->feedtype == DVB_NET_FEEDTYPE_MPE) { if (priv->secfeed) { if (priv->secfeed->is_filtering) { - dprintk("%s: stop secfeed\n", __func__); + netdev_dbg(dev, "stop secfeed\n"); priv->secfeed->stop_filtering(priv->secfeed); } if (priv->secfilter) { - dprintk("%s: release secfilter\n", __func__); + netdev_dbg(dev, "release secfilter\n"); priv->secfeed->release_filter(priv->secfeed, priv->secfilter); priv->secfilter=NULL; @@ -1093,8 +1057,8 @@ static int dvb_net_feed_stop(struct net_device *dev) for (i=0; i<priv->multi_num; i++) { if (priv->multi_secfilter[i]) { - dprintk("%s: release multi_filter[%d]\n", - __func__, i); + netdev_dbg(dev, "release multi_filter[%d]\n", + i); priv->secfeed->release_filter(priv->secfeed, priv->multi_secfilter[i]); priv->multi_secfilter[i] = NULL; @@ -1108,7 +1072,7 @@ static int dvb_net_feed_stop(struct net_device *dev) } else if (priv->feedtype == DVB_NET_FEEDTYPE_ULE) { if (priv->tsfeed) { if (priv->tsfeed->is_filtering) { - dprintk("%s: stop tsfeed\n", __func__); + netdev_dbg(dev, "stop tsfeed\n"); priv->tsfeed->stop_filtering(priv->tsfeed); } priv->demux->release_ts_feed(priv->demux, priv->tsfeed); @@ -1148,16 +1112,16 @@ static void wq_set_multicast_list (struct work_struct *work) netif_addr_lock_bh(dev); if (dev->flags & IFF_PROMISC) { - dprintk("%s: promiscuous mode\n", dev->name); + netdev_dbg(dev, "promiscuous mode\n"); priv->rx_mode = RX_MODE_PROMISC; } else if ((dev->flags & IFF_ALLMULTI)) { - dprintk("%s: allmulti mode\n", dev->name); + netdev_dbg(dev, "allmulti mode\n"); priv->rx_mode = RX_MODE_ALL_MULTI; } else if (!netdev_mc_empty(dev)) { struct netdev_hw_addr *ha; - dprintk("%s: set_mc_list, %d entries\n", - dev->name, netdev_mc_count(dev)); + netdev_dbg(dev, "set_mc_list, %d entries\n", + netdev_mc_count(dev)); priv->rx_mode = RX_MODE_MULTI; priv->multi_num = 0; diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 6c75418222e2..bb76727d924e 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -443,7 +443,8 @@ config DVB_CXD2820R config DVB_RTL2830 tristate "Realtek RTL2830 DVB-T" - depends on DVB_CORE && I2C + depends on DVB_CORE && I2C && I2C_MUX + select REGMAP default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. @@ -451,6 +452,7 @@ config DVB_RTL2830 config DVB_RTL2832 tristate "Realtek RTL2832 DVB-T" depends on DVB_CORE && I2C && I2C_MUX + select REGMAP default m if !MEDIA_SUBDRV_AUTOSELECT help Say Y when you want to support this frontend. diff --git a/drivers/media/dvb-frontends/au8522.h b/drivers/media/dvb-frontends/au8522.h index 83fe9a615619..612251958855 100644 --- a/drivers/media/dvb-frontends/au8522.h +++ b/drivers/media/dvb-frontends/au8522.h @@ -91,8 +91,3 @@ enum au8522_audio_input { }; #endif /* __AU8522_H__ */ - -/* - * Local variables: - * c-basic-offset: 8 - */ diff --git a/drivers/media/dvb-frontends/dib8000.c b/drivers/media/dvb-frontends/dib8000.c index 61e31f2d2f71..8c6663b6399d 100644 --- a/drivers/media/dvb-frontends/dib8000.c +++ b/drivers/media/dvb-frontends/dib8000.c @@ -1263,7 +1263,8 @@ static int dib8000_agc_startup(struct dvb_frontend *fe) struct dib8000_state *state = fe->demodulator_priv; enum frontend_tune_state *tune_state = &state->tune_state; int ret = 0; - u16 reg, upd_demod_gain_period = 0x8000; + u16 reg; + u32 upd_demod_gain_period = 0x8000; switch (*tune_state) { case CT_AGC_START: diff --git a/drivers/media/dvb-frontends/hd29l2.c b/drivers/media/dvb-frontends/hd29l2.c index d7b9d549156d..67c8e6df42e8 100644 --- a/drivers/media/dvb-frontends/hd29l2.c +++ b/drivers/media/dvb-frontends/hd29l2.c @@ -22,20 +22,24 @@ #include "hd29l2_priv.h" +#define HD29L2_MAX_LEN (3) + /* write multiple registers */ static int hd29l2_wr_regs(struct hd29l2_priv *priv, u8 reg, u8 *val, int len) { int ret; - u8 buf[2 + len]; + u8 buf[2 + HD29L2_MAX_LEN]; struct i2c_msg msg[1] = { { .addr = priv->cfg.i2c_addr, .flags = 0, - .len = sizeof(buf), + .len = 2 + len, .buf = buf, } }; + if (len > HD29L2_MAX_LEN) + return -EINVAL; buf[0] = 0x00; buf[1] = reg; memcpy(&buf[2], val, len); @@ -118,7 +122,7 @@ static int hd29l2_wr_reg_mask(struct hd29l2_priv *priv, u8 reg, u8 val, u8 mask) } /* read single register with mask */ -int hd29l2_rd_reg_mask(struct hd29l2_priv *priv, u8 reg, u8 *val, u8 mask) +static int hd29l2_rd_reg_mask(struct hd29l2_priv *priv, u8 reg, u8 *val, u8 mask) { int ret, i; u8 tmp; diff --git a/drivers/media/dvb-frontends/lg2160.c b/drivers/media/dvb-frontends/lg2160.c index 5fd14f840ab0..99efeba3c31a 100644 --- a/drivers/media/dvb-frontends/lg2160.c +++ b/drivers/media/dvb-frontends/lg2160.c @@ -1456,9 +1456,3 @@ MODULE_DESCRIPTION("LG Electronics LG216x ATSC/MH Demodulator Driver"); MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); MODULE_LICENSE("GPL"); MODULE_VERSION("0.3"); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb-frontends/lgdt3305.c b/drivers/media/dvb-frontends/lgdt3305.c index 92c891a571ab..d08570af1c10 100644 --- a/drivers/media/dvb-frontends/lgdt3305.c +++ b/drivers/media/dvb-frontends/lgdt3305.c @@ -236,12 +236,13 @@ static inline int lgdt3305_mpeg_mode(struct lgdt3305_state *state, return lgdt3305_set_reg_bit(state, LGDT3305_TP_CTRL_1, 5, mode); } -static int lgdt3305_mpeg_mode_polarity(struct lgdt3305_state *state, - enum lgdt3305_tp_clock_edge edge, - enum lgdt3305_tp_valid_polarity valid) +static int lgdt3305_mpeg_mode_polarity(struct lgdt3305_state *state) { u8 val; int ret; + enum lgdt3305_tp_clock_edge edge = state->cfg->tpclk_edge; + enum lgdt3305_tp_clock_mode mode = state->cfg->tpclk_mode; + enum lgdt3305_tp_valid_polarity valid = state->cfg->tpvalid_polarity; lg_dbg("edge = %d, valid = %d\n", edge, valid); @@ -253,6 +254,8 @@ static int lgdt3305_mpeg_mode_polarity(struct lgdt3305_state *state, if (edge) val |= 0x08; + if (mode) + val |= 0x40; if (valid) val |= 0x01; @@ -740,9 +743,7 @@ static int lgdt3304_set_parameters(struct dvb_frontend *fe) goto fail; /* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */ - ret = lgdt3305_mpeg_mode_polarity(state, - state->cfg->tpclk_edge, - state->cfg->tpvalid_polarity); + ret = lgdt3305_mpeg_mode_polarity(state); fail: return ret; } @@ -806,9 +807,7 @@ static int lgdt3305_set_parameters(struct dvb_frontend *fe) goto fail; /* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */ - ret = lgdt3305_mpeg_mode_polarity(state, - state->cfg->tpclk_edge, - state->cfg->tpvalid_polarity); + ret = lgdt3305_mpeg_mode_polarity(state); fail: return ret; } @@ -1215,9 +1214,3 @@ MODULE_DESCRIPTION("LG Electronics LGDT3304/5 ATSC/QAM-B Demodulator Driver"); MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); MODULE_LICENSE("GPL"); MODULE_VERSION("0.2"); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb-frontends/lgdt3305.h b/drivers/media/dvb-frontends/lgdt3305.h index d9ab556c1b27..9c03e530e01b 100644 --- a/drivers/media/dvb-frontends/lgdt3305.h +++ b/drivers/media/dvb-frontends/lgdt3305.h @@ -37,6 +37,11 @@ enum lgdt3305_tp_clock_edge { LGDT3305_TPCLK_FALLING_EDGE = 1, }; +enum lgdt3305_tp_clock_mode { + LGDT3305_TPCLK_GATED = 0, + LGDT3305_TPCLK_FIXED = 1, +}; + enum lgdt3305_tp_valid_polarity { LGDT3305_TP_VALID_LOW = 0, LGDT3305_TP_VALID_HIGH = 1, @@ -70,6 +75,7 @@ struct lgdt3305_config { enum lgdt3305_mpeg_mode mpeg_mode; enum lgdt3305_tp_clock_edge tpclk_edge; + enum lgdt3305_tp_clock_mode tpclk_mode; enum lgdt3305_tp_valid_polarity tpvalid_polarity; enum lgdt_demod_chip_type demod_chip; }; diff --git a/drivers/media/dvb-frontends/lgdt330x.c b/drivers/media/dvb-frontends/lgdt330x.c index e046622df0e4..2e1a61893fc1 100644 --- a/drivers/media/dvb-frontends/lgdt330x.c +++ b/drivers/media/dvb-frontends/lgdt330x.c @@ -823,9 +823,3 @@ MODULE_AUTHOR("Wilson Michaels"); MODULE_LICENSE("GPL"); EXPORT_SYMBOL(lgdt330x_attach); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb-frontends/lgdt330x.h b/drivers/media/dvb-frontends/lgdt330x.h index ca0eab562e1e..8bb332219fc4 100644 --- a/drivers/media/dvb-frontends/lgdt330x.h +++ b/drivers/media/dvb-frontends/lgdt330x.h @@ -65,9 +65,3 @@ static inline struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* #endif // CONFIG_DVB_LGDT330X #endif /* LGDT330X_H */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb-frontends/lgdt330x_priv.h b/drivers/media/dvb-frontends/lgdt330x_priv.h index 38c76695abfe..1922f09a02d0 100644 --- a/drivers/media/dvb-frontends/lgdt330x_priv.h +++ b/drivers/media/dvb-frontends/lgdt330x_priv.h @@ -69,9 +69,3 @@ enum I2C_REG { }; #endif /* _LGDT330X_PRIV_ */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb-frontends/mb86a20s.c b/drivers/media/dvb-frontends/mb86a20s.c index e6f165a5b90d..8f54c39ca63f 100644 --- a/drivers/media/dvb-frontends/mb86a20s.c +++ b/drivers/media/dvb-frontends/mb86a20s.c @@ -22,10 +22,6 @@ #define NUM_LAYERS 3 -static int debug = 1; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); - enum mb86a20s_bandwidth { MB86A20S_13SEG = 0, MB86A20S_13SEG_PARTIAL = 1, diff --git a/drivers/media/dvb-frontends/mn88472.h b/drivers/media/dvb-frontends/mn88472.h index da4558bce60f..e4e0b80d3091 100644 --- a/drivers/media/dvb-frontends/mn88472.h +++ b/drivers/media/dvb-frontends/mn88472.h @@ -33,6 +33,12 @@ struct mn88472_config { * DVB frontend. */ struct dvb_frontend **fe; + + /* + * Xtal frequency. + * Hz + */ + u32 xtal; }; #endif diff --git a/drivers/media/dvb-frontends/nxt200x.h b/drivers/media/dvb-frontends/nxt200x.h index b518d545609e..e38d01fb6c2b 100644 --- a/drivers/media/dvb-frontends/nxt200x.h +++ b/drivers/media/dvb-frontends/nxt200x.h @@ -55,9 +55,3 @@ static inline struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* c #endif // CONFIG_DVB_NXT200X #endif /* NXT200X_H */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb-frontends/or51132.c b/drivers/media/dvb-frontends/or51132.c index 5ef921823c15..cbbd259eacfe 100644 --- a/drivers/media/dvb-frontends/or51132.c +++ b/drivers/media/dvb-frontends/or51132.c @@ -623,9 +623,3 @@ MODULE_AUTHOR("Trent Piepho"); MODULE_LICENSE("GPL"); EXPORT_SYMBOL(or51132_attach); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb-frontends/or51132.h b/drivers/media/dvb-frontends/or51132.h index 938958386cb1..cdb5be3c65d6 100644 --- a/drivers/media/dvb-frontends/or51132.h +++ b/drivers/media/dvb-frontends/or51132.h @@ -47,9 +47,3 @@ static inline struct dvb_frontend* or51132_attach(const struct or51132_config* c #endif // CONFIG_DVB_OR51132 #endif // OR51132_H - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb-frontends/rtl2830.c b/drivers/media/dvb-frontends/rtl2830.c index 50e8b63e5169..e1b8df62bd59 100644 --- a/drivers/media/dvb-frontends/rtl2830.c +++ b/drivers/media/dvb-frontends/rtl2830.c @@ -13,261 +13,154 @@ * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - - -/* - * Driver implements own I2C-adapter for tuner I2C access. That's since chip - * have unusual I2C-gate control which closes gate automatically after each - * I2C transfer. Using own I2C adapter we can workaround that. */ #include "rtl2830_priv.h" -/* Max transfer size done by I2C transfer functions */ -#define MAX_XFER_SIZE 64 - -/* write multiple hardware registers */ -static int rtl2830_wr(struct rtl2830_priv *priv, u8 reg, const u8 *val, int len) +/* Our regmap is bypassing I2C adapter lock, thus we do it! */ +static int rtl2830_bulk_write(struct i2c_client *client, unsigned int reg, + const void *val, size_t val_count) { + struct rtl2830_dev *dev = i2c_get_clientdata(client); int ret; - u8 buf[MAX_XFER_SIZE]; - struct i2c_msg msg[1] = { - { - .addr = priv->cfg.i2c_addr, - .flags = 0, - .len = 1 + len, - .buf = buf, - } - }; - - if (1 + len > sizeof(buf)) { - dev_warn(&priv->i2c->dev, - "%s: i2c wr reg=%04x: len=%d is too big!\n", - KBUILD_MODNAME, reg, len); - return -EINVAL; - } - - buf[0] = reg; - memcpy(&buf[1], val, len); - ret = i2c_transfer(priv->i2c, msg, 1); - if (ret == 1) { - ret = 0; - } else { - dev_warn(&priv->i2c->dev, "%s: i2c wr failed=%d reg=%02x " \ - "len=%d\n", KBUILD_MODNAME, ret, reg, len); - ret = -EREMOTEIO; - } + i2c_lock_adapter(client->adapter); + ret = regmap_bulk_write(dev->regmap, reg, val, val_count); + i2c_unlock_adapter(client->adapter); return ret; } -/* read multiple hardware registers */ -static int rtl2830_rd(struct rtl2830_priv *priv, u8 reg, u8 *val, int len) +static int rtl2830_update_bits(struct i2c_client *client, unsigned int reg, + unsigned int mask, unsigned int val) { + struct rtl2830_dev *dev = i2c_get_clientdata(client); int ret; - struct i2c_msg msg[2] = { - { - .addr = priv->cfg.i2c_addr, - .flags = 0, - .len = 1, - .buf = ®, - }, { - .addr = priv->cfg.i2c_addr, - .flags = I2C_M_RD, - .len = len, - .buf = val, - } - }; - ret = i2c_transfer(priv->i2c, msg, 2); - if (ret == 2) { - ret = 0; - } else { - dev_warn(&priv->i2c->dev, "%s: i2c rd failed=%d reg=%02x " \ - "len=%d\n", KBUILD_MODNAME, ret, reg, len); - ret = -EREMOTEIO; - } + i2c_lock_adapter(client->adapter); + ret = regmap_update_bits(dev->regmap, reg, mask, val); + i2c_unlock_adapter(client->adapter); return ret; } -/* write multiple registers */ -static int rtl2830_wr_regs(struct rtl2830_priv *priv, u16 reg, const u8 *val, - int len) +static int rtl2830_bulk_read(struct i2c_client *client, unsigned int reg, + void *val, size_t val_count) { + struct rtl2830_dev *dev = i2c_get_clientdata(client); int ret; - u8 reg2 = (reg >> 0) & 0xff; - u8 page = (reg >> 8) & 0xff; - - /* switch bank if needed */ - if (page != priv->page) { - ret = rtl2830_wr(priv, 0x00, &page, 1); - if (ret) - return ret; - - priv->page = page; - } - - return rtl2830_wr(priv, reg2, val, len); -} - -/* read multiple registers */ -static int rtl2830_rd_regs(struct rtl2830_priv *priv, u16 reg, u8 *val, int len) -{ - int ret; - u8 reg2 = (reg >> 0) & 0xff; - u8 page = (reg >> 8) & 0xff; - - /* switch bank if needed */ - if (page != priv->page) { - ret = rtl2830_wr(priv, 0x00, &page, 1); - if (ret) - return ret; - - priv->page = page; - } - return rtl2830_rd(priv, reg2, val, len); -} - -/* read single register */ -static int rtl2830_rd_reg(struct rtl2830_priv *priv, u16 reg, u8 *val) -{ - return rtl2830_rd_regs(priv, reg, val, 1); -} - -/* write single register with mask */ -static int rtl2830_wr_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 val, u8 mask) -{ - int ret; - u8 tmp; - - /* no need for read if whole reg is written */ - if (mask != 0xff) { - ret = rtl2830_rd_regs(priv, reg, &tmp, 1); - if (ret) - return ret; - - val &= mask; - tmp &= ~mask; - val |= tmp; - } - - return rtl2830_wr_regs(priv, reg, &val, 1); -} - -/* read single register with mask */ -static int rtl2830_rd_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 *val, u8 mask) -{ - int ret, i; - u8 tmp; - - ret = rtl2830_rd_regs(priv, reg, &tmp, 1); - if (ret) - return ret; - - tmp &= mask; - - /* find position of the first bit */ - for (i = 0; i < 8; i++) { - if ((mask >> i) & 0x01) - break; - } - *val = tmp >> i; - - return 0; + i2c_lock_adapter(client->adapter); + ret = regmap_bulk_read(dev->regmap, reg, val, val_count); + i2c_unlock_adapter(client->adapter); + return ret; } static int rtl2830_init(struct dvb_frontend *fe) { - struct rtl2830_priv *priv = fe->demodulator_priv; + struct i2c_client *client = fe->demodulator_priv; + struct rtl2830_dev *dev = i2c_get_clientdata(client); + struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache; int ret, i; struct rtl2830_reg_val_mask tab[] = { - { 0x00d, 0x01, 0x03 }, - { 0x00d, 0x10, 0x10 }, - { 0x104, 0x00, 0x1e }, - { 0x105, 0x80, 0x80 }, - { 0x110, 0x02, 0x03 }, - { 0x110, 0x08, 0x0c }, - { 0x17b, 0x00, 0x40 }, - { 0x17d, 0x05, 0x0f }, - { 0x17d, 0x50, 0xf0 }, - { 0x18c, 0x08, 0x0f }, - { 0x18d, 0x00, 0xc0 }, - { 0x188, 0x05, 0x0f }, - { 0x189, 0x00, 0xfc }, - { 0x2d5, 0x02, 0x02 }, - { 0x2f1, 0x02, 0x06 }, - { 0x2f1, 0x20, 0xf8 }, - { 0x16d, 0x00, 0x01 }, - { 0x1a6, 0x00, 0x80 }, - { 0x106, priv->cfg.vtop, 0x3f }, - { 0x107, priv->cfg.krf, 0x3f }, - { 0x112, 0x28, 0xff }, - { 0x103, priv->cfg.agc_targ_val, 0xff }, - { 0x00a, 0x02, 0x07 }, - { 0x140, 0x0c, 0x3c }, - { 0x140, 0x40, 0xc0 }, - { 0x15b, 0x05, 0x07 }, - { 0x15b, 0x28, 0x38 }, - { 0x15c, 0x05, 0x07 }, - { 0x15c, 0x28, 0x38 }, - { 0x115, priv->cfg.spec_inv, 0x01 }, - { 0x16f, 0x01, 0x07 }, - { 0x170, 0x18, 0x38 }, - { 0x172, 0x0f, 0x0f }, - { 0x173, 0x08, 0x38 }, - { 0x175, 0x01, 0x07 }, - { 0x176, 0x00, 0xc0 }, + {0x00d, 0x01, 0x03}, + {0x00d, 0x10, 0x10}, + {0x104, 0x00, 0x1e}, + {0x105, 0x80, 0x80}, + {0x110, 0x02, 0x03}, + {0x110, 0x08, 0x0c}, + {0x17b, 0x00, 0x40}, + {0x17d, 0x05, 0x0f}, + {0x17d, 0x50, 0xf0}, + {0x18c, 0x08, 0x0f}, + {0x18d, 0x00, 0xc0}, + {0x188, 0x05, 0x0f}, + {0x189, 0x00, 0xfc}, + {0x2d5, 0x02, 0x02}, + {0x2f1, 0x02, 0x06}, + {0x2f1, 0x20, 0xf8}, + {0x16d, 0x00, 0x01}, + {0x1a6, 0x00, 0x80}, + {0x106, dev->pdata->vtop, 0x3f}, + {0x107, dev->pdata->krf, 0x3f}, + {0x112, 0x28, 0xff}, + {0x103, dev->pdata->agc_targ_val, 0xff}, + {0x00a, 0x02, 0x07}, + {0x140, 0x0c, 0x3c}, + {0x140, 0x40, 0xc0}, + {0x15b, 0x05, 0x07}, + {0x15b, 0x28, 0x38}, + {0x15c, 0x05, 0x07}, + {0x15c, 0x28, 0x38}, + {0x115, dev->pdata->spec_inv, 0x01}, + {0x16f, 0x01, 0x07}, + {0x170, 0x18, 0x38}, + {0x172, 0x0f, 0x0f}, + {0x173, 0x08, 0x38}, + {0x175, 0x01, 0x07}, + {0x176, 0x00, 0xc0}, }; for (i = 0; i < ARRAY_SIZE(tab); i++) { - ret = rtl2830_wr_reg_mask(priv, tab[i].reg, tab[i].val, - tab[i].mask); + ret = rtl2830_update_bits(client, tab[i].reg, tab[i].mask, + tab[i].val); if (ret) goto err; } - ret = rtl2830_wr_regs(priv, 0x18f, "\x28\x00", 2); + ret = rtl2830_bulk_write(client, 0x18f, "\x28\x00", 2); if (ret) goto err; - ret = rtl2830_wr_regs(priv, 0x195, - "\x04\x06\x0a\x12\x0a\x12\x1e\x28", 8); + ret = rtl2830_bulk_write(client, 0x195, + "\x04\x06\x0a\x12\x0a\x12\x1e\x28", 8); if (ret) goto err; /* TODO: spec init */ /* soft reset */ - ret = rtl2830_wr_reg_mask(priv, 0x101, 0x04, 0x04); + ret = rtl2830_update_bits(client, 0x101, 0x04, 0x04); if (ret) goto err; - ret = rtl2830_wr_reg_mask(priv, 0x101, 0x00, 0x04); + ret = rtl2830_update_bits(client, 0x101, 0x04, 0x00); if (ret) goto err; - priv->sleeping = false; + /* init stats here in order signal app which stats are supported */ + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + /* start statistics polling */ + schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000)); + + dev->sleeping = false; return ret; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int rtl2830_sleep(struct dvb_frontend *fe) { - struct rtl2830_priv *priv = fe->demodulator_priv; - priv->sleeping = true; + struct i2c_client *client = fe->demodulator_priv; + struct rtl2830_dev *dev = i2c_get_clientdata(client); + + dev->sleeping = true; + /* stop statistics polling */ + cancel_delayed_work_sync(&dev->stat_work); + dev->fe_status = 0; + return 0; } static int rtl2830_get_tune_settings(struct dvb_frontend *fe, - struct dvb_frontend_tune_settings *s) + struct dvb_frontend_tune_settings *s) { s->min_delay_ms = 500; s->step_size = fe->ops.info.frequency_stepsize * 2; @@ -278,11 +171,12 @@ static int rtl2830_get_tune_settings(struct dvb_frontend *fe, static int rtl2830_set_frontend(struct dvb_frontend *fe) { - struct rtl2830_priv *priv = fe->demodulator_priv; + struct i2c_client *client = fe->demodulator_priv; + struct rtl2830_dev *dev = i2c_get_clientdata(client); struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i; u64 num; - u8 buf[3], tmp; + u8 buf[3], u8tmp; u32 if_ctl, if_frequency; static const u8 bw_params1[3][34] = { { @@ -308,9 +202,8 @@ static int rtl2830_set_frontend(struct dvb_frontend *fe) {0xae, 0xba, 0xf3, 0x26, 0x66, 0x64}, /* 8 MHz */ }; - dev_dbg(&priv->i2c->dev, - "%s: frequency=%d bandwidth_hz=%d inversion=%d\n", - __func__, c->frequency, c->bandwidth_hz, c->inversion); + dev_dbg(&client->dev, "frequency=%u bandwidth_hz=%u inversion=%u\n", + c->frequency, c->bandwidth_hz, c->inversion); /* program tuner */ if (fe->ops.tuner_ops.set_params) @@ -327,11 +220,12 @@ static int rtl2830_set_frontend(struct dvb_frontend *fe) i = 2; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid bandwidth\n", __func__); + dev_err(&client->dev, "invalid bandwidth_hz %u\n", + c->bandwidth_hz); return -EINVAL; } - ret = rtl2830_wr_reg_mask(priv, 0x008, i << 1, 0x06); + ret = rtl2830_update_bits(client, 0x008, 0x06, i << 1); if (ret) goto err; @@ -340,70 +234,71 @@ static int rtl2830_set_frontend(struct dvb_frontend *fe) ret = fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); else ret = -EINVAL; - - if (ret < 0) + if (ret) goto err; - num = if_frequency % priv->cfg.xtal; + num = if_frequency % dev->pdata->clk; num *= 0x400000; - num = div_u64(num, priv->cfg.xtal); + num = div_u64(num, dev->pdata->clk); num = -num; if_ctl = num & 0x3fffff; - dev_dbg(&priv->i2c->dev, "%s: if_frequency=%d if_ctl=%08x\n", - __func__, if_frequency, if_ctl); + dev_dbg(&client->dev, "if_frequency=%d if_ctl=%08x\n", + if_frequency, if_ctl); - ret = rtl2830_rd_reg_mask(priv, 0x119, &tmp, 0xc0); /* b[7:6] */ + buf[0] = (if_ctl >> 16) & 0x3f; + buf[1] = (if_ctl >> 8) & 0xff; + buf[2] = (if_ctl >> 0) & 0xff; + + ret = rtl2830_bulk_read(client, 0x119, &u8tmp, 1); if (ret) goto err; - buf[0] = tmp << 6; - buf[0] |= (if_ctl >> 16) & 0x3f; - buf[1] = (if_ctl >> 8) & 0xff; - buf[2] = (if_ctl >> 0) & 0xff; + buf[0] |= u8tmp & 0xc0; /* [7:6] */ - ret = rtl2830_wr_regs(priv, 0x119, buf, 3); + ret = rtl2830_bulk_write(client, 0x119, buf, 3); if (ret) goto err; /* 1/2 split I2C write */ - ret = rtl2830_wr_regs(priv, 0x11c, &bw_params1[i][0], 17); + ret = rtl2830_bulk_write(client, 0x11c, &bw_params1[i][0], 17); if (ret) goto err; /* 2/2 split I2C write */ - ret = rtl2830_wr_regs(priv, 0x12d, &bw_params1[i][17], 17); + ret = rtl2830_bulk_write(client, 0x12d, &bw_params1[i][17], 17); if (ret) goto err; - ret = rtl2830_wr_regs(priv, 0x19d, bw_params2[i], 6); + ret = rtl2830_bulk_write(client, 0x19d, bw_params2[i], 6); if (ret) goto err; return ret; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int rtl2830_get_frontend(struct dvb_frontend *fe) { - struct rtl2830_priv *priv = fe->demodulator_priv; + struct i2c_client *client = fe->demodulator_priv; + struct rtl2830_dev *dev = i2c_get_clientdata(client); struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; u8 buf[3]; - if (priv->sleeping) + if (dev->sleeping) return 0; - ret = rtl2830_rd_regs(priv, 0x33c, buf, 2); + ret = rtl2830_bulk_read(client, 0x33c, buf, 2); if (ret) goto err; - ret = rtl2830_rd_reg(priv, 0x351, &buf[2]); + ret = rtl2830_bulk_read(client, 0x351, &buf[2], 1); if (ret) goto err; - dev_dbg(&priv->i2c->dev, "%s: TPS=%*ph\n", __func__, 3, buf); + dev_dbg(&client->dev, "TPS=%*ph\n", 3, buf); switch ((buf[0] >> 2) & 3) { case 0: @@ -493,280 +388,543 @@ static int rtl2830_get_frontend(struct dvb_frontend *fe) return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int rtl2830_read_status(struct dvb_frontend *fe, fe_status_t *status) { - struct rtl2830_priv *priv = fe->demodulator_priv; + struct i2c_client *client = fe->demodulator_priv; + struct rtl2830_dev *dev = i2c_get_clientdata(client); int ret; - u8 tmp; + u8 u8tmp; + *status = 0; - if (priv->sleeping) + if (dev->sleeping) return 0; - ret = rtl2830_rd_reg_mask(priv, 0x351, &tmp, 0x78); /* [6:3] */ + ret = rtl2830_bulk_read(client, 0x351, &u8tmp, 1); if (ret) goto err; - if (tmp == 11) { + u8tmp = (u8tmp >> 3) & 0x0f; /* [6:3] */ + if (u8tmp == 11) { *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; - } else if (tmp == 10) { + } else if (u8tmp == 10) { *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI; } + dev->fe_status = *status; + return ret; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int rtl2830_read_snr(struct dvb_frontend *fe, u16 *snr) { - struct rtl2830_priv *priv = fe->demodulator_priv; - int ret, hierarchy, constellation; - u8 buf[2], tmp; - u16 tmp16; -#define CONSTELLATION_NUM 3 -#define HIERARCHY_NUM 4 - static const u32 snr_constant[CONSTELLATION_NUM][HIERARCHY_NUM] = { - { 70705899, 70705899, 70705899, 70705899 }, - { 82433173, 82433173, 87483115, 94445660 }, - { 92888734, 92888734, 95487525, 99770748 }, - }; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; - if (priv->sleeping) - return 0; + if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL) + *snr = div_s64(c->cnr.stat[0].svalue, 100); + else + *snr = 0; - /* reports SNR in resolution of 0.1 dB */ + return 0; +} - ret = rtl2830_rd_reg(priv, 0x33c, &tmp); - if (ret) - goto err; +static int rtl2830_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct i2c_client *client = fe->demodulator_priv; + struct rtl2830_dev *dev = i2c_get_clientdata(client); - constellation = (tmp >> 2) & 0x03; /* [3:2] */ - if (constellation > CONSTELLATION_NUM - 1) - goto err; + *ber = (dev->post_bit_error - dev->post_bit_error_prev); + dev->post_bit_error_prev = dev->post_bit_error; - hierarchy = (tmp >> 4) & 0x07; /* [6:4] */ - if (hierarchy > HIERARCHY_NUM - 1) - goto err; + return 0; +} - ret = rtl2830_rd_regs(priv, 0x40c, buf, 2); - if (ret) - goto err; +static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + *ucblocks = 0; - tmp16 = buf[0] << 8 | buf[1]; + return 0; +} + +static int rtl2830_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; - if (tmp16) - *snr = (snr_constant[constellation][hierarchy] - - intlog10(tmp16)) / ((1 << 24) / 100); + if (c->strength.stat[0].scale == FE_SCALE_RELATIVE) + *strength = c->strength.stat[0].uvalue; else - *snr = 0; + *strength = 0; return 0; +} + +static struct dvb_frontend_ops rtl2830_ops = { + .delsys = {SYS_DVBT}, + .info = { + .name = "Realtek RTL2830 (DVB-T)", + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_64 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_RECOVER | + FE_CAN_MUTE_TS + }, + + .init = rtl2830_init, + .sleep = rtl2830_sleep, + + .get_tune_settings = rtl2830_get_tune_settings, + + .set_frontend = rtl2830_set_frontend, + .get_frontend = rtl2830_get_frontend, + + .read_status = rtl2830_read_status, + .read_snr = rtl2830_read_snr, + .read_ber = rtl2830_read_ber, + .read_ucblocks = rtl2830_read_ucblocks, + .read_signal_strength = rtl2830_read_signal_strength, +}; + +static void rtl2830_stat_work(struct work_struct *work) +{ + struct rtl2830_dev *dev = container_of(work, struct rtl2830_dev, stat_work.work); + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache; + int ret, tmp; + u8 u8tmp, buf[2]; + u16 u16tmp; + + dev_dbg(&client->dev, "\n"); + + /* signal strength */ + if (dev->fe_status & FE_HAS_SIGNAL) { + struct {signed int x:14; } s; + + /* read IF AGC */ + ret = rtl2830_bulk_read(client, 0x359, buf, 2); + if (ret) + goto err; + + u16tmp = buf[0] << 8 | buf[1] << 0; + u16tmp &= 0x3fff; /* [13:0] */ + tmp = s.x = u16tmp; /* 14-bit bin to 2 complement */ + u16tmp = clamp_val(-4 * tmp + 32767, 0x0000, 0xffff); + + dev_dbg(&client->dev, "IF AGC=%d\n", tmp); + + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = u16tmp; + } else { + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* CNR */ + if (dev->fe_status & FE_HAS_VITERBI) { + unsigned hierarchy, constellation; + #define CONSTELLATION_NUM 3 + #define HIERARCHY_NUM 4 + static const u32 constant[CONSTELLATION_NUM][HIERARCHY_NUM] = { + {70705899, 70705899, 70705899, 70705899}, + {82433173, 82433173, 87483115, 94445660}, + {92888734, 92888734, 95487525, 99770748}, + }; + + ret = rtl2830_bulk_read(client, 0x33c, &u8tmp, 1); + if (ret) + goto err; + + constellation = (u8tmp >> 2) & 0x03; /* [3:2] */ + if (constellation > CONSTELLATION_NUM - 1) + goto err_schedule_delayed_work; + + hierarchy = (u8tmp >> 4) & 0x07; /* [6:4] */ + if (hierarchy > HIERARCHY_NUM - 1) + goto err_schedule_delayed_work; + + ret = rtl2830_bulk_read(client, 0x40c, buf, 2); + if (ret) + goto err; + + u16tmp = buf[0] << 8 | buf[1] << 0; + if (u16tmp) + tmp = (constant[constellation][hierarchy] - + intlog10(u16tmp)) / ((1 << 24) / 10000); + else + tmp = 0; + + dev_dbg(&client->dev, "CNR raw=%u\n", u16tmp); + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = tmp; + } else { + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* BER */ + if (dev->fe_status & FE_HAS_LOCK) { + ret = rtl2830_bulk_read(client, 0x34e, buf, 2); + if (ret) + goto err; + + u16tmp = buf[0] << 8 | buf[1] << 0; + dev->post_bit_error += u16tmp; + dev->post_bit_count += 1000000; + + dev_dbg(&client->dev, "BER errors=%u total=1000000\n", u16tmp); + + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = dev->post_bit_error; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue = dev->post_bit_count; + } else { + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + +err_schedule_delayed_work: + schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000)); + return; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - return ret; + dev_dbg(&client->dev, "failed=%d\n", ret); } -static int rtl2830_read_ber(struct dvb_frontend *fe, u32 *ber) +static int rtl2830_pid_filter_ctrl(struct dvb_frontend *fe, int onoff) { - struct rtl2830_priv *priv = fe->demodulator_priv; + struct i2c_client *client = fe->demodulator_priv; int ret; - u8 buf[2]; + u8 u8tmp; - if (priv->sleeping) - return 0; + dev_dbg(&client->dev, "onoff=%d\n", onoff); + + /* enable / disable PID filter */ + if (onoff) + u8tmp = 0x80; + else + u8tmp = 0x00; - ret = rtl2830_rd_regs(priv, 0x34e, buf, 2); + ret = rtl2830_update_bits(client, 0x061, 0x80, u8tmp); if (ret) goto err; - *ber = buf[0] << 8 | buf[1]; - return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } -static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +static int rtl2830_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid, int onoff) { - *ucblocks = 0; - return 0; -} - -static int rtl2830_read_signal_strength(struct dvb_frontend *fe, u16 *strength) -{ - struct rtl2830_priv *priv = fe->demodulator_priv; + struct i2c_client *client = fe->demodulator_priv; + struct rtl2830_dev *dev = i2c_get_clientdata(client); int ret; - u8 buf[2]; - u16 if_agc_raw, if_agc; + u8 buf[4]; - if (priv->sleeping) + dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d\n", + index, pid, onoff); + + /* skip invalid PIDs (0x2000) */ + if (pid > 0x1fff || index > 32) return 0; - ret = rtl2830_rd_regs(priv, 0x359, buf, 2); + if (onoff) + set_bit(index, &dev->filters); + else + clear_bit(index, &dev->filters); + + /* enable / disable PIDs */ + buf[0] = (dev->filters >> 0) & 0xff; + buf[1] = (dev->filters >> 8) & 0xff; + buf[2] = (dev->filters >> 16) & 0xff; + buf[3] = (dev->filters >> 24) & 0xff; + ret = rtl2830_bulk_write(client, 0x062, buf, 4); if (ret) goto err; - if_agc_raw = (buf[0] << 8 | buf[1]) & 0x3fff; - - if (if_agc_raw & (1 << 9)) - if_agc = -(~(if_agc_raw - 1) & 0x1ff); - else - if_agc = if_agc_raw; - - *strength = (u8) (55 - if_agc / 182); - *strength |= *strength << 8; + /* add PID */ + buf[0] = (pid >> 8) & 0xff; + buf[1] = (pid >> 0) & 0xff; + ret = rtl2830_bulk_write(client, 0x066 + 2 * index, buf, 2); + if (ret) + goto err; return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } -static struct dvb_frontend_ops rtl2830_ops; - -static u32 rtl2830_tuner_i2c_func(struct i2c_adapter *adapter) -{ - return I2C_FUNC_I2C; -} - -static int rtl2830_tuner_i2c_xfer(struct i2c_adapter *i2c_adap, - struct i2c_msg msg[], int num) +/* + * I2C gate/mux/repeater logic + * We must use unlocked __i2c_transfer() here (through regmap) because of I2C + * adapter lock is already taken by tuner driver. + * Gate is closed automatically after single I2C transfer. + */ +static int rtl2830_select(struct i2c_adapter *adap, void *mux_priv, u32 chan_id) { - struct rtl2830_priv *priv = i2c_get_adapdata(i2c_adap); + struct i2c_client *client = mux_priv; + struct rtl2830_dev *dev = i2c_get_clientdata(client); int ret; - /* open i2c-gate */ - ret = rtl2830_wr_reg_mask(priv, 0x101, 0x08, 0x08); + dev_dbg(&client->dev, "\n"); + + /* open I2C repeater for 1 transfer, closes automatically */ + /* XXX: regmap_update_bits() does not lock I2C adapter */ + ret = regmap_update_bits(dev->regmap, 0x101, 0x08, 0x08); if (ret) goto err; - ret = i2c_transfer(priv->i2c, msg, num); - if (ret < 0) - dev_warn(&priv->i2c->dev, "%s: tuner i2c failed=%d\n", - KBUILD_MODNAME, ret); - - return ret; + return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } -static struct i2c_algorithm rtl2830_tuner_i2c_algo = { - .master_xfer = rtl2830_tuner_i2c_xfer, - .functionality = rtl2830_tuner_i2c_func, -}; +static struct dvb_frontend *rtl2830_get_dvb_frontend(struct i2c_client *client) +{ + struct rtl2830_dev *dev = i2c_get_clientdata(client); -struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(struct dvb_frontend *fe) + dev_dbg(&client->dev, "\n"); + + return &dev->fe; +} + +static struct i2c_adapter *rtl2830_get_i2c_adapter(struct i2c_client *client) { - struct rtl2830_priv *priv = fe->demodulator_priv; - return &priv->tuner_i2c_adapter; + struct rtl2830_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + + return dev->adapter; } -EXPORT_SYMBOL(rtl2830_get_tuner_i2c_adapter); -static void rtl2830_release(struct dvb_frontend *fe) +/* + * We implement own I2C access routines for regmap in order to get manual access + * to I2C adapter lock, which is needed for I2C mux adapter. + */ +static int rtl2830_regmap_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, size_t val_size) { - struct rtl2830_priv *priv = fe->demodulator_priv; + struct i2c_client *client = context; + int ret; + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .flags = 0, + .len = reg_size, + .buf = (u8 *)reg_buf, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .len = val_size, + .buf = val_buf, + } + }; - i2c_del_adapter(&priv->tuner_i2c_adapter); - kfree(priv); + ret = __i2c_transfer(client->adapter, msg, 2); + if (ret != 2) { + dev_warn(&client->dev, "i2c reg read failed %d\n", ret); + if (ret >= 0) + ret = -EREMOTEIO; + return ret; + } + return 0; } -struct dvb_frontend *rtl2830_attach(const struct rtl2830_config *cfg, - struct i2c_adapter *i2c) +static int rtl2830_regmap_write(void *context, const void *data, size_t count) { - struct rtl2830_priv *priv = NULL; - int ret = 0; - u8 tmp; + struct i2c_client *client = context; + int ret; + struct i2c_msg msg[1] = { + { + .addr = client->addr, + .flags = 0, + .len = count, + .buf = (u8 *)data, + } + }; + + ret = __i2c_transfer(client->adapter, msg, 1); + if (ret != 1) { + dev_warn(&client->dev, "i2c reg write failed %d\n", ret); + if (ret >= 0) + ret = -EREMOTEIO; + return ret; + } + return 0; +} + +static int rtl2830_regmap_gather_write(void *context, const void *reg, + size_t reg_len, const void *val, + size_t val_len) +{ + struct i2c_client *client = context; + int ret; + u8 buf[256]; + struct i2c_msg msg[1] = { + { + .addr = client->addr, + .flags = 0, + .len = 1 + val_len, + .buf = buf, + } + }; + + buf[0] = *(u8 const *)reg; + memcpy(&buf[1], val, val_len); + + ret = __i2c_transfer(client->adapter, msg, 1); + if (ret != 1) { + dev_warn(&client->dev, "i2c reg write failed %d\n", ret); + if (ret >= 0) + ret = -EREMOTEIO; + return ret; + } + return 0; +} + +static int rtl2830_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct rtl2830_platform_data *pdata = client->dev.platform_data; + struct rtl2830_dev *dev; + int ret; + u8 u8tmp; + static const struct regmap_bus regmap_bus = { + .read = rtl2830_regmap_read, + .write = rtl2830_regmap_write, + .gather_write = rtl2830_regmap_gather_write, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, + }; + static const struct regmap_range_cfg regmap_range_cfg[] = { + { + .selector_reg = 0x00, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 0x100, + .range_min = 0 * 0x100, + .range_max = 5 * 0x100, + }, + }; + static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 5 * 0x100, + .ranges = regmap_range_cfg, + .num_ranges = ARRAY_SIZE(regmap_range_cfg), + }; + + dev_dbg(&client->dev, "\n"); + + if (pdata == NULL) { + ret = -EINVAL; + goto err; + } /* allocate memory for the internal state */ - priv = kzalloc(sizeof(struct rtl2830_priv), GFP_KERNEL); - if (priv == NULL) + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + ret = -ENOMEM; goto err; + } - /* setup the priv */ - priv->i2c = i2c; - memcpy(&priv->cfg, cfg, sizeof(struct rtl2830_config)); + /* setup the state */ + i2c_set_clientdata(client, dev); + dev->client = client; + dev->pdata = client->dev.platform_data; + dev->sleeping = true; + INIT_DELAYED_WORK(&dev->stat_work, rtl2830_stat_work); + dev->regmap = regmap_init(&client->dev, ®map_bus, client, + ®map_config); + if (IS_ERR(dev->regmap)) { + ret = PTR_ERR(dev->regmap); + goto err_kfree; + } /* check if the demod is there */ - ret = rtl2830_rd_reg(priv, 0x000, &tmp); + ret = rtl2830_bulk_read(client, 0x000, &u8tmp, 1); if (ret) - goto err; - - /* create dvb_frontend */ - memcpy(&priv->fe.ops, &rtl2830_ops, sizeof(struct dvb_frontend_ops)); - priv->fe.demodulator_priv = priv; - - /* create tuner i2c adapter */ - strlcpy(priv->tuner_i2c_adapter.name, "RTL2830 tuner I2C adapter", - sizeof(priv->tuner_i2c_adapter.name)); - priv->tuner_i2c_adapter.algo = &rtl2830_tuner_i2c_algo; - priv->tuner_i2c_adapter.algo_data = NULL; - priv->tuner_i2c_adapter.dev.parent = &i2c->dev; - i2c_set_adapdata(&priv->tuner_i2c_adapter, priv); - if (i2c_add_adapter(&priv->tuner_i2c_adapter) < 0) { - dev_err(&i2c->dev, - "%s: tuner i2c bus could not be initialized\n", - KBUILD_MODNAME); - goto err; + goto err_regmap_exit; + + /* create muxed i2c adapter for tuner */ + dev->adapter = i2c_add_mux_adapter(client->adapter, &client->dev, + client, 0, 0, 0, rtl2830_select, NULL); + if (dev->adapter == NULL) { + ret = -ENODEV; + goto err_regmap_exit; } - priv->sleeping = true; + /* create dvb frontend */ + memcpy(&dev->fe.ops, &rtl2830_ops, sizeof(dev->fe.ops)); + dev->fe.demodulator_priv = client; + + /* setup callbacks */ + pdata->get_dvb_frontend = rtl2830_get_dvb_frontend; + pdata->get_i2c_adapter = rtl2830_get_i2c_adapter; + pdata->pid_filter = rtl2830_pid_filter; + pdata->pid_filter_ctrl = rtl2830_pid_filter_ctrl; - return &priv->fe; + dev_info(&client->dev, "Realtek RTL2830 successfully attached\n"); + + return 0; +err_regmap_exit: + regmap_exit(dev->regmap); +err_kfree: + kfree(dev); err: - dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); - kfree(priv); - return NULL; + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; } -EXPORT_SYMBOL(rtl2830_attach); -static struct dvb_frontend_ops rtl2830_ops = { - .delsys = { SYS_DVBT }, - .info = { - .name = "Realtek RTL2830 (DVB-T)", - .caps = FE_CAN_FEC_1_2 | - FE_CAN_FEC_2_3 | - FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | - FE_CAN_FEC_7_8 | - FE_CAN_FEC_AUTO | - FE_CAN_QPSK | - FE_CAN_QAM_16 | - FE_CAN_QAM_64 | - FE_CAN_QAM_AUTO | - FE_CAN_TRANSMISSION_MODE_AUTO | - FE_CAN_GUARD_INTERVAL_AUTO | - FE_CAN_HIERARCHY_AUTO | - FE_CAN_RECOVER | - FE_CAN_MUTE_TS - }, +static int rtl2830_remove(struct i2c_client *client) +{ + struct rtl2830_dev *dev = i2c_get_clientdata(client); - .release = rtl2830_release, + dev_dbg(&client->dev, "\n"); - .init = rtl2830_init, - .sleep = rtl2830_sleep, + i2c_del_mux_adapter(dev->adapter); + regmap_exit(dev->regmap); + kfree(dev); - .get_tune_settings = rtl2830_get_tune_settings, + return 0; +} - .set_frontend = rtl2830_set_frontend, - .get_frontend = rtl2830_get_frontend, +static const struct i2c_device_id rtl2830_id_table[] = { + {"rtl2830", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rtl2830_id_table); - .read_status = rtl2830_read_status, - .read_snr = rtl2830_read_snr, - .read_ber = rtl2830_read_ber, - .read_ucblocks = rtl2830_read_ucblocks, - .read_signal_strength = rtl2830_read_signal_strength, +static struct i2c_driver rtl2830_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "rtl2830", + }, + .probe = rtl2830_probe, + .remove = rtl2830_remove, + .id_table = rtl2830_id_table, }; +module_i2c_driver(rtl2830_driver); + MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); MODULE_DESCRIPTION("Realtek RTL2830 DVB-T demodulator driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/rtl2830.h b/drivers/media/dvb-frontends/rtl2830.h index 3313847fb0be..0cde151e6608 100644 --- a/drivers/media/dvb-frontends/rtl2830.h +++ b/drivers/media/dvb-frontends/rtl2830.h @@ -13,78 +13,37 @@ * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef RTL2830_H #define RTL2830_H -#include <linux/kconfig.h> #include <linux/dvb/frontend.h> -struct rtl2830_config { - /* - * Demodulator I2C address. - */ - u8 i2c_addr; - - /* - * Xtal frequency. - * Hz - * 4000000, 16000000, 25000000, 28800000 - */ - u32 xtal; - - /* - * TS output mode. - */ - u8 ts_mode; +/** + * struct rtl2830_platform_data - Platform data for the rtl2830 driver + * @clk: Clock frequency (4000000, 16000000, 25000000, 28800000). + * @spec_inv: Spectrum inversion. + * @vtop: AGC take-over point. + * @krf: AGC ratio. + * @agc_targ_val: AGC. + * @get_dvb_frontend: Get DVB frontend. + * @get_i2c_adapter: Get I2C adapter. + * @pid_filter: Set PID to PID filter. + * @pid_filter_ctrl: Control PID filter. + */ - /* - * Spectrum inversion. - */ +struct rtl2830_platform_data { + u32 clk; bool spec_inv; - - /* - */ u8 vtop; - - /* - */ u8 krf; - - /* - */ u8 agc_targ_val; -}; - -#if IS_ENABLED(CONFIG_DVB_RTL2830) -extern struct dvb_frontend *rtl2830_attach( - const struct rtl2830_config *config, - struct i2c_adapter *i2c -); -extern struct i2c_adapter *rtl2830_get_tuner_i2c_adapter( - struct dvb_frontend *fe -); -#else -static inline struct dvb_frontend *rtl2830_attach( - const struct rtl2830_config *config, - struct i2c_adapter *i2c -) -{ - pr_warn("%s: driver disabled by Kconfig\n", __func__); - return NULL; -} - -static inline struct i2c_adapter *rtl2830_get_tuner_i2c_adapter( - struct dvb_frontend *fe -) -{ - return NULL; -} -#endif + struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); + struct i2c_adapter* (*get_i2c_adapter)(struct i2c_client *); + int (*pid_filter)(struct dvb_frontend *, u8, u16, int); + int (*pid_filter_ctrl)(struct dvb_frontend *, int); +}; #endif /* RTL2830_H */ diff --git a/drivers/media/dvb-frontends/rtl2830_priv.h b/drivers/media/dvb-frontends/rtl2830_priv.h index fab10ecb3c3b..d50d5376c9c5 100644 --- a/drivers/media/dvb-frontends/rtl2830_priv.h +++ b/drivers/media/dvb-frontends/rtl2830_priv.h @@ -13,9 +13,6 @@ * 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., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef RTL2830_PRIV_H @@ -24,16 +21,23 @@ #include "dvb_frontend.h" #include "dvb_math.h" #include "rtl2830.h" +#include <linux/i2c-mux.h> +#include <linux/math64.h> +#include <linux/regmap.h> -struct rtl2830_priv { - struct i2c_adapter *i2c; +struct rtl2830_dev { + struct rtl2830_platform_data *pdata; + struct i2c_client *client; + struct regmap *regmap; + struct i2c_adapter *adapter; struct dvb_frontend fe; - struct rtl2830_config cfg; - struct i2c_adapter tuner_i2c_adapter; - bool sleeping; - - u8 page; /* active register page */ + unsigned long filters; + struct delayed_work stat_work; + fe_status_t fe_status; + u64 post_bit_error_prev; /* for old DVBv3 read_ber() calculation */ + u64 post_bit_error; + u64 post_bit_count; }; struct rtl2830_reg_val_mask { diff --git a/drivers/media/dvb-frontends/rtl2832.c b/drivers/media/dvb-frontends/rtl2832.c index 9026e1aee163..5d2d8f45b4b6 100644 --- a/drivers/media/dvb-frontends/rtl2832.c +++ b/drivers/media/dvb-frontends/rtl2832.c @@ -2,6 +2,7 @@ * Realtek RTL2832 DVB-T demodulator driver * * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com> + * Copyright (C) 2012-2014 Antti Palosaari <crope@iki.fi> * * 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 @@ -19,280 +20,191 @@ */ #include "rtl2832_priv.h" -#include "dvb_math.h" -#include <linux/bitops.h> -/* Max transfer size done by I2C transfer functions */ -#define MAX_XFER_SIZE 64 #define REG_MASK(b) (BIT(b + 1) - 1) static const struct rtl2832_reg_entry registers[] = { - [DVBT_SOFT_RST] = {0x1, 0x1, 2, 2}, - [DVBT_IIC_REPEAT] = {0x1, 0x1, 3, 3}, - [DVBT_TR_WAIT_MIN_8K] = {0x1, 0x88, 11, 2}, - [DVBT_RSD_BER_FAIL_VAL] = {0x1, 0x8f, 15, 0}, - [DVBT_EN_BK_TRK] = {0x1, 0xa6, 7, 7}, - [DVBT_AD_EN_REG] = {0x0, 0x8, 7, 7}, - [DVBT_AD_EN_REG1] = {0x0, 0x8, 6, 6}, - [DVBT_EN_BBIN] = {0x1, 0xb1, 0, 0}, - [DVBT_MGD_THD0] = {0x1, 0x95, 7, 0}, - [DVBT_MGD_THD1] = {0x1, 0x96, 7, 0}, - [DVBT_MGD_THD2] = {0x1, 0x97, 7, 0}, - [DVBT_MGD_THD3] = {0x1, 0x98, 7, 0}, - [DVBT_MGD_THD4] = {0x1, 0x99, 7, 0}, - [DVBT_MGD_THD5] = {0x1, 0x9a, 7, 0}, - [DVBT_MGD_THD6] = {0x1, 0x9b, 7, 0}, - [DVBT_MGD_THD7] = {0x1, 0x9c, 7, 0}, - [DVBT_EN_CACQ_NOTCH] = {0x1, 0x61, 4, 4}, - [DVBT_AD_AV_REF] = {0x0, 0x9, 6, 0}, - [DVBT_REG_PI] = {0x0, 0xa, 2, 0}, - [DVBT_PIP_ON] = {0x0, 0x21, 3, 3}, - [DVBT_SCALE1_B92] = {0x2, 0x92, 7, 0}, - [DVBT_SCALE1_B93] = {0x2, 0x93, 7, 0}, - [DVBT_SCALE1_BA7] = {0x2, 0xa7, 7, 0}, - [DVBT_SCALE1_BA9] = {0x2, 0xa9, 7, 0}, - [DVBT_SCALE1_BAA] = {0x2, 0xaa, 7, 0}, - [DVBT_SCALE1_BAB] = {0x2, 0xab, 7, 0}, - [DVBT_SCALE1_BAC] = {0x2, 0xac, 7, 0}, - [DVBT_SCALE1_BB0] = {0x2, 0xb0, 7, 0}, - [DVBT_SCALE1_BB1] = {0x2, 0xb1, 7, 0}, - [DVBT_KB_P1] = {0x1, 0x64, 3, 1}, - [DVBT_KB_P2] = {0x1, 0x64, 6, 4}, - [DVBT_KB_P3] = {0x1, 0x65, 2, 0}, - [DVBT_OPT_ADC_IQ] = {0x0, 0x6, 5, 4}, - [DVBT_AD_AVI] = {0x0, 0x9, 1, 0}, - [DVBT_AD_AVQ] = {0x0, 0x9, 3, 2}, - [DVBT_K1_CR_STEP12] = {0x2, 0xad, 9, 4}, - [DVBT_TRK_KS_P2] = {0x1, 0x6f, 2, 0}, - [DVBT_TRK_KS_I2] = {0x1, 0x70, 5, 3}, - [DVBT_TR_THD_SET2] = {0x1, 0x72, 3, 0}, - [DVBT_TRK_KC_P2] = {0x1, 0x73, 5, 3}, - [DVBT_TRK_KC_I2] = {0x1, 0x75, 2, 0}, - [DVBT_CR_THD_SET2] = {0x1, 0x76, 7, 6}, - [DVBT_PSET_IFFREQ] = {0x1, 0x19, 21, 0}, - [DVBT_SPEC_INV] = {0x1, 0x15, 0, 0}, - [DVBT_RSAMP_RATIO] = {0x1, 0x9f, 27, 2}, - [DVBT_CFREQ_OFF_RATIO] = {0x1, 0x9d, 23, 4}, - [DVBT_FSM_STAGE] = {0x3, 0x51, 6, 3}, - [DVBT_RX_CONSTEL] = {0x3, 0x3c, 3, 2}, - [DVBT_RX_HIER] = {0x3, 0x3c, 6, 4}, - [DVBT_RX_C_RATE_LP] = {0x3, 0x3d, 2, 0}, - [DVBT_RX_C_RATE_HP] = {0x3, 0x3d, 5, 3}, - [DVBT_GI_IDX] = {0x3, 0x51, 1, 0}, - [DVBT_FFT_MODE_IDX] = {0x3, 0x51, 2, 2}, - [DVBT_RSD_BER_EST] = {0x3, 0x4e, 15, 0}, - [DVBT_CE_EST_EVM] = {0x4, 0xc, 15, 0}, - [DVBT_RF_AGC_VAL] = {0x3, 0x5b, 13, 0}, - [DVBT_IF_AGC_VAL] = {0x3, 0x59, 13, 0}, - [DVBT_DAGC_VAL] = {0x3, 0x5, 7, 0}, - [DVBT_SFREQ_OFF] = {0x3, 0x18, 13, 0}, - [DVBT_CFREQ_OFF] = {0x3, 0x5f, 17, 0}, - [DVBT_POLAR_RF_AGC] = {0x0, 0xe, 1, 1}, - [DVBT_POLAR_IF_AGC] = {0x0, 0xe, 0, 0}, - [DVBT_AAGC_HOLD] = {0x1, 0x4, 5, 5}, - [DVBT_EN_RF_AGC] = {0x1, 0x4, 6, 6}, - [DVBT_EN_IF_AGC] = {0x1, 0x4, 7, 7}, - [DVBT_IF_AGC_MIN] = {0x1, 0x8, 7, 0}, - [DVBT_IF_AGC_MAX] = {0x1, 0x9, 7, 0}, - [DVBT_RF_AGC_MIN] = {0x1, 0xa, 7, 0}, - [DVBT_RF_AGC_MAX] = {0x1, 0xb, 7, 0}, - [DVBT_IF_AGC_MAN] = {0x1, 0xc, 6, 6}, - [DVBT_IF_AGC_MAN_VAL] = {0x1, 0xc, 13, 0}, - [DVBT_RF_AGC_MAN] = {0x1, 0xe, 6, 6}, - [DVBT_RF_AGC_MAN_VAL] = {0x1, 0xe, 13, 0}, - [DVBT_DAGC_TRG_VAL] = {0x1, 0x12, 7, 0}, - [DVBT_AGC_TARG_VAL_0] = {0x1, 0x2, 0, 0}, - [DVBT_AGC_TARG_VAL_8_1] = {0x1, 0x3, 7, 0}, - [DVBT_AAGC_LOOP_GAIN] = {0x1, 0xc7, 5, 1}, - [DVBT_LOOP_GAIN2_3_0] = {0x1, 0x4, 4, 1}, - [DVBT_LOOP_GAIN2_4] = {0x1, 0x5, 7, 7}, - [DVBT_LOOP_GAIN3] = {0x1, 0xc8, 4, 0}, - [DVBT_VTOP1] = {0x1, 0x6, 5, 0}, - [DVBT_VTOP2] = {0x1, 0xc9, 5, 0}, - [DVBT_VTOP3] = {0x1, 0xca, 5, 0}, - [DVBT_KRF1] = {0x1, 0xcb, 7, 0}, - [DVBT_KRF2] = {0x1, 0x7, 7, 0}, - [DVBT_KRF3] = {0x1, 0xcd, 7, 0}, - [DVBT_KRF4] = {0x1, 0xce, 7, 0}, - [DVBT_EN_GI_PGA] = {0x1, 0xe5, 0, 0}, - [DVBT_THD_LOCK_UP] = {0x1, 0xd9, 8, 0}, - [DVBT_THD_LOCK_DW] = {0x1, 0xdb, 8, 0}, - [DVBT_THD_UP1] = {0x1, 0xdd, 7, 0}, - [DVBT_THD_DW1] = {0x1, 0xde, 7, 0}, - [DVBT_INTER_CNT_LEN] = {0x1, 0xd8, 3, 0}, - [DVBT_GI_PGA_STATE] = {0x1, 0xe6, 3, 3}, - [DVBT_EN_AGC_PGA] = {0x1, 0xd7, 0, 0}, - [DVBT_CKOUTPAR] = {0x1, 0x7b, 5, 5}, - [DVBT_CKOUT_PWR] = {0x1, 0x7b, 6, 6}, - [DVBT_SYNC_DUR] = {0x1, 0x7b, 7, 7}, - [DVBT_ERR_DUR] = {0x1, 0x7c, 0, 0}, - [DVBT_SYNC_LVL] = {0x1, 0x7c, 1, 1}, - [DVBT_ERR_LVL] = {0x1, 0x7c, 2, 2}, - [DVBT_VAL_LVL] = {0x1, 0x7c, 3, 3}, - [DVBT_SERIAL] = {0x1, 0x7c, 4, 4}, - [DVBT_SER_LSB] = {0x1, 0x7c, 5, 5}, - [DVBT_CDIV_PH0] = {0x1, 0x7d, 3, 0}, - [DVBT_CDIV_PH1] = {0x1, 0x7d, 7, 4}, - [DVBT_MPEG_IO_OPT_2_2] = {0x0, 0x6, 7, 7}, - [DVBT_MPEG_IO_OPT_1_0] = {0x0, 0x7, 7, 6}, - [DVBT_CKOUTPAR_PIP] = {0x0, 0xb7, 4, 4}, - [DVBT_CKOUT_PWR_PIP] = {0x0, 0xb7, 3, 3}, - [DVBT_SYNC_LVL_PIP] = {0x0, 0xb7, 2, 2}, - [DVBT_ERR_LVL_PIP] = {0x0, 0xb7, 1, 1}, - [DVBT_VAL_LVL_PIP] = {0x0, 0xb7, 0, 0}, - [DVBT_CKOUTPAR_PID] = {0x0, 0xb9, 4, 4}, - [DVBT_CKOUT_PWR_PID] = {0x0, 0xb9, 3, 3}, - [DVBT_SYNC_LVL_PID] = {0x0, 0xb9, 2, 2}, - [DVBT_ERR_LVL_PID] = {0x0, 0xb9, 1, 1}, - [DVBT_VAL_LVL_PID] = {0x0, 0xb9, 0, 0}, - [DVBT_SM_PASS] = {0x1, 0x93, 11, 0}, - [DVBT_AD7_SETTING] = {0x0, 0x11, 15, 0}, - [DVBT_RSSI_R] = {0x3, 0x1, 6, 0}, - [DVBT_ACI_DET_IND] = {0x3, 0x12, 0, 0}, - [DVBT_REG_MON] = {0x0, 0xd, 1, 0}, - [DVBT_REG_MONSEL] = {0x0, 0xd, 2, 2}, - [DVBT_REG_GPE] = {0x0, 0xd, 7, 7}, - [DVBT_REG_GPO] = {0x0, 0x10, 0, 0}, - [DVBT_REG_4MSEL] = {0x0, 0x13, 0, 0}, + [DVBT_SOFT_RST] = {0x101, 2, 2}, + [DVBT_IIC_REPEAT] = {0x101, 3, 3}, + [DVBT_TR_WAIT_MIN_8K] = {0x188, 11, 2}, + [DVBT_RSD_BER_FAIL_VAL] = {0x18f, 15, 0}, + [DVBT_EN_BK_TRK] = {0x1a6, 7, 7}, + [DVBT_AD_EN_REG] = {0x008, 7, 7}, + [DVBT_AD_EN_REG1] = {0x008, 6, 6}, + [DVBT_EN_BBIN] = {0x1b1, 0, 0}, + [DVBT_MGD_THD0] = {0x195, 7, 0}, + [DVBT_MGD_THD1] = {0x196, 7, 0}, + [DVBT_MGD_THD2] = {0x197, 7, 0}, + [DVBT_MGD_THD3] = {0x198, 7, 0}, + [DVBT_MGD_THD4] = {0x199, 7, 0}, + [DVBT_MGD_THD5] = {0x19a, 7, 0}, + [DVBT_MGD_THD6] = {0x19b, 7, 0}, + [DVBT_MGD_THD7] = {0x19c, 7, 0}, + [DVBT_EN_CACQ_NOTCH] = {0x161, 4, 4}, + [DVBT_AD_AV_REF] = {0x009, 6, 0}, + [DVBT_REG_PI] = {0x00a, 2, 0}, + [DVBT_PIP_ON] = {0x021, 3, 3}, + [DVBT_SCALE1_B92] = {0x292, 7, 0}, + [DVBT_SCALE1_B93] = {0x293, 7, 0}, + [DVBT_SCALE1_BA7] = {0x2a7, 7, 0}, + [DVBT_SCALE1_BA9] = {0x2a9, 7, 0}, + [DVBT_SCALE1_BAA] = {0x2aa, 7, 0}, + [DVBT_SCALE1_BAB] = {0x2ab, 7, 0}, + [DVBT_SCALE1_BAC] = {0x2ac, 7, 0}, + [DVBT_SCALE1_BB0] = {0x2b0, 7, 0}, + [DVBT_SCALE1_BB1] = {0x2b1, 7, 0}, + [DVBT_KB_P1] = {0x164, 3, 1}, + [DVBT_KB_P2] = {0x164, 6, 4}, + [DVBT_KB_P3] = {0x165, 2, 0}, + [DVBT_OPT_ADC_IQ] = {0x006, 5, 4}, + [DVBT_AD_AVI] = {0x009, 1, 0}, + [DVBT_AD_AVQ] = {0x009, 3, 2}, + [DVBT_K1_CR_STEP12] = {0x2ad, 9, 4}, + [DVBT_TRK_KS_P2] = {0x16f, 2, 0}, + [DVBT_TRK_KS_I2] = {0x170, 5, 3}, + [DVBT_TR_THD_SET2] = {0x172, 3, 0}, + [DVBT_TRK_KC_P2] = {0x173, 5, 3}, + [DVBT_TRK_KC_I2] = {0x175, 2, 0}, + [DVBT_CR_THD_SET2] = {0x176, 7, 6}, + [DVBT_PSET_IFFREQ] = {0x119, 21, 0}, + [DVBT_SPEC_INV] = {0x115, 0, 0}, + [DVBT_RSAMP_RATIO] = {0x19f, 27, 2}, + [DVBT_CFREQ_OFF_RATIO] = {0x19d, 23, 4}, + [DVBT_FSM_STAGE] = {0x351, 6, 3}, + [DVBT_RX_CONSTEL] = {0x33c, 3, 2}, + [DVBT_RX_HIER] = {0x33c, 6, 4}, + [DVBT_RX_C_RATE_LP] = {0x33d, 2, 0}, + [DVBT_RX_C_RATE_HP] = {0x33d, 5, 3}, + [DVBT_GI_IDX] = {0x351, 1, 0}, + [DVBT_FFT_MODE_IDX] = {0x351, 2, 2}, + [DVBT_RSD_BER_EST] = {0x34e, 15, 0}, + [DVBT_CE_EST_EVM] = {0x40c, 15, 0}, + [DVBT_RF_AGC_VAL] = {0x35b, 13, 0}, + [DVBT_IF_AGC_VAL] = {0x359, 13, 0}, + [DVBT_DAGC_VAL] = {0x305, 7, 0}, + [DVBT_SFREQ_OFF] = {0x318, 13, 0}, + [DVBT_CFREQ_OFF] = {0x35f, 17, 0}, + [DVBT_POLAR_RF_AGC] = {0x00e, 1, 1}, + [DVBT_POLAR_IF_AGC] = {0x00e, 0, 0}, + [DVBT_AAGC_HOLD] = {0x104, 5, 5}, + [DVBT_EN_RF_AGC] = {0x104, 6, 6}, + [DVBT_EN_IF_AGC] = {0x104, 7, 7}, + [DVBT_IF_AGC_MIN] = {0x108, 7, 0}, + [DVBT_IF_AGC_MAX] = {0x109, 7, 0}, + [DVBT_RF_AGC_MIN] = {0x10a, 7, 0}, + [DVBT_RF_AGC_MAX] = {0x10b, 7, 0}, + [DVBT_IF_AGC_MAN] = {0x10c, 6, 6}, + [DVBT_IF_AGC_MAN_VAL] = {0x10c, 13, 0}, + [DVBT_RF_AGC_MAN] = {0x10e, 6, 6}, + [DVBT_RF_AGC_MAN_VAL] = {0x10e, 13, 0}, + [DVBT_DAGC_TRG_VAL] = {0x112, 7, 0}, + [DVBT_AGC_TARG_VAL_0] = {0x102, 0, 0}, + [DVBT_AGC_TARG_VAL_8_1] = {0x103, 7, 0}, + [DVBT_AAGC_LOOP_GAIN] = {0x1c7, 5, 1}, + [DVBT_LOOP_GAIN2_3_0] = {0x104, 4, 1}, + [DVBT_LOOP_GAIN2_4] = {0x105, 7, 7}, + [DVBT_LOOP_GAIN3] = {0x1c8, 4, 0}, + [DVBT_VTOP1] = {0x106, 5, 0}, + [DVBT_VTOP2] = {0x1c9, 5, 0}, + [DVBT_VTOP3] = {0x1ca, 5, 0}, + [DVBT_KRF1] = {0x1cb, 7, 0}, + [DVBT_KRF2] = {0x107, 7, 0}, + [DVBT_KRF3] = {0x1cd, 7, 0}, + [DVBT_KRF4] = {0x1ce, 7, 0}, + [DVBT_EN_GI_PGA] = {0x1e5, 0, 0}, + [DVBT_THD_LOCK_UP] = {0x1d9, 8, 0}, + [DVBT_THD_LOCK_DW] = {0x1db, 8, 0}, + [DVBT_THD_UP1] = {0x1dd, 7, 0}, + [DVBT_THD_DW1] = {0x1de, 7, 0}, + [DVBT_INTER_CNT_LEN] = {0x1d8, 3, 0}, + [DVBT_GI_PGA_STATE] = {0x1e6, 3, 3}, + [DVBT_EN_AGC_PGA] = {0x1d7, 0, 0}, + [DVBT_CKOUTPAR] = {0x17b, 5, 5}, + [DVBT_CKOUT_PWR] = {0x17b, 6, 6}, + [DVBT_SYNC_DUR] = {0x17b, 7, 7}, + [DVBT_ERR_DUR] = {0x17c, 0, 0}, + [DVBT_SYNC_LVL] = {0x17c, 1, 1}, + [DVBT_ERR_LVL] = {0x17c, 2, 2}, + [DVBT_VAL_LVL] = {0x17c, 3, 3}, + [DVBT_SERIAL] = {0x17c, 4, 4}, + [DVBT_SER_LSB] = {0x17c, 5, 5}, + [DVBT_CDIV_PH0] = {0x17d, 3, 0}, + [DVBT_CDIV_PH1] = {0x17d, 7, 4}, + [DVBT_MPEG_IO_OPT_2_2] = {0x006, 7, 7}, + [DVBT_MPEG_IO_OPT_1_0] = {0x007, 7, 6}, + [DVBT_CKOUTPAR_PIP] = {0x0b7, 4, 4}, + [DVBT_CKOUT_PWR_PIP] = {0x0b7, 3, 3}, + [DVBT_SYNC_LVL_PIP] = {0x0b7, 2, 2}, + [DVBT_ERR_LVL_PIP] = {0x0b7, 1, 1}, + [DVBT_VAL_LVL_PIP] = {0x0b7, 0, 0}, + [DVBT_CKOUTPAR_PID] = {0x0b9, 4, 4}, + [DVBT_CKOUT_PWR_PID] = {0x0b9, 3, 3}, + [DVBT_SYNC_LVL_PID] = {0x0b9, 2, 2}, + [DVBT_ERR_LVL_PID] = {0x0b9, 1, 1}, + [DVBT_VAL_LVL_PID] = {0x0b9, 0, 0}, + [DVBT_SM_PASS] = {0x193, 11, 0}, + [DVBT_AD7_SETTING] = {0x011, 15, 0}, + [DVBT_RSSI_R] = {0x301, 6, 0}, + [DVBT_ACI_DET_IND] = {0x312, 0, 0}, + [DVBT_REG_MON] = {0x00d, 1, 0}, + [DVBT_REG_MONSEL] = {0x00d, 2, 2}, + [DVBT_REG_GPE] = {0x00d, 7, 7}, + [DVBT_REG_GPO] = {0x010, 0, 0}, + [DVBT_REG_4MSEL] = {0x013, 0, 0}, }; -/* write multiple hardware registers */ -static int rtl2832_wr(struct rtl2832_priv *priv, u8 reg, u8 *val, int len) +/* Our regmap is bypassing I2C adapter lock, thus we do it! */ +static int rtl2832_bulk_write(struct i2c_client *client, unsigned int reg, + const void *val, size_t val_count) { + struct rtl2832_dev *dev = i2c_get_clientdata(client); int ret; - u8 buf[MAX_XFER_SIZE]; - struct i2c_msg msg[1] = { - { - .addr = priv->cfg.i2c_addr, - .flags = 0, - .len = 1 + len, - .buf = buf, - } - }; - if (1 + len > sizeof(buf)) { - dev_warn(&priv->i2c->dev, - "%s: i2c wr reg=%04x: len=%d is too big!\n", - KBUILD_MODNAME, reg, len); - return -EINVAL; - } - - buf[0] = reg; - memcpy(&buf[1], val, len); - - ret = i2c_transfer(priv->i2c_adapter, msg, 1); - if (ret == 1) { - ret = 0; - } else { - dev_warn(&priv->i2c->dev, - "%s: i2c wr failed=%d reg=%02x len=%d\n", - KBUILD_MODNAME, ret, reg, len); - ret = -EREMOTEIO; - } + i2c_lock_adapter(client->adapter); + ret = regmap_bulk_write(dev->regmap, reg, val, val_count); + i2c_unlock_adapter(client->adapter); return ret; } -/* read multiple hardware registers */ -static int rtl2832_rd(struct rtl2832_priv *priv, u8 reg, u8 *val, int len) +static int rtl2832_update_bits(struct i2c_client *client, unsigned int reg, + unsigned int mask, unsigned int val) { + struct rtl2832_dev *dev = i2c_get_clientdata(client); int ret; - struct i2c_msg msg[2] = { - { - .addr = priv->cfg.i2c_addr, - .flags = 0, - .len = 1, - .buf = ®, - }, { - .addr = priv->cfg.i2c_addr, - .flags = I2C_M_RD, - .len = len, - .buf = val, - } - }; - ret = i2c_transfer(priv->i2c_adapter, msg, 2); - if (ret == 2) { - ret = 0; - } else { - dev_warn(&priv->i2c->dev, - "%s: i2c rd failed=%d reg=%02x len=%d\n", - KBUILD_MODNAME, ret, reg, len); - ret = -EREMOTEIO; - } + i2c_lock_adapter(client->adapter); + ret = regmap_update_bits(dev->regmap, reg, mask, val); + i2c_unlock_adapter(client->adapter); return ret; } -/* write multiple registers */ -static int rtl2832_wr_regs(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val, - int len) -{ - int ret; - - /* switch bank if needed */ - if (page != priv->page) { - ret = rtl2832_wr(priv, 0x00, &page, 1); - if (ret) - return ret; - - priv->page = page; -} - -return rtl2832_wr(priv, reg, val, len); -} - -/* read multiple registers */ -static int rtl2832_rd_regs(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val, - int len) +static int rtl2832_bulk_read(struct i2c_client *client, unsigned int reg, + void *val, size_t val_count) { + struct rtl2832_dev *dev = i2c_get_clientdata(client); int ret; - /* switch bank if needed */ - if (page != priv->page) { - ret = rtl2832_wr(priv, 0x00, &page, 1); - if (ret) - return ret; - - priv->page = page; - } - - return rtl2832_rd(priv, reg, val, len); -} - -/* write single register */ -static int rtl2832_wr_reg(struct rtl2832_priv *priv, u8 reg, u8 page, u8 val) -{ - return rtl2832_wr_regs(priv, reg, page, &val, 1); -} - -/* read single register */ -static int rtl2832_rd_reg(struct rtl2832_priv *priv, u8 reg, u8 page, u8 *val) -{ - return rtl2832_rd_regs(priv, reg, page, val, 1); + i2c_lock_adapter(client->adapter); + ret = regmap_bulk_read(dev->regmap, reg, val, val_count); + i2c_unlock_adapter(client->adapter); + return ret; } -static int rtl2832_rd_demod_reg(struct rtl2832_priv *priv, int reg, u32 *val) +static int rtl2832_rd_demod_reg(struct rtl2832_dev *dev, int reg, u32 *val) { - int ret; - - u8 reg_start_addr; - u8 msb, lsb; - u8 page; - u8 reading[4]; - u32 reading_tmp; - int i; - - u8 len; - u32 mask; + struct i2c_client *client = dev->client; + int ret, i; + u16 reg_start_addr; + u8 msb, lsb, reading[4], len; + u32 reading_tmp, mask; reg_start_addr = registers[reg].start_address; msb = registers[reg].msb; lsb = registers[reg].lsb; - page = registers[reg].page; - len = (msb >> 3) + 1; mask = REG_MASK(msb - lsb); - ret = rtl2832_rd_regs(priv, reg_start_addr, page, &reading[0], len); + ret = rtl2832_bulk_read(client, reg_start_addr, reading, len); if (ret) goto err; @@ -302,40 +214,27 @@ static int rtl2832_rd_demod_reg(struct rtl2832_priv *priv, int reg, u32 *val) *val = (reading_tmp >> lsb) & mask; - return ret; - + return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; - } -static int rtl2832_wr_demod_reg(struct rtl2832_priv *priv, int reg, u32 val) +static int rtl2832_wr_demod_reg(struct rtl2832_dev *dev, int reg, u32 val) { + struct i2c_client *client = dev->client; int ret, i; - u8 len; - u8 reg_start_addr; - u8 msb, lsb; - u8 page; - u32 mask; - - - u8 reading[4]; - u8 writing[4]; - u32 reading_tmp; - u32 writing_tmp; - + u16 reg_start_addr; + u8 msb, lsb, reading[4], writing[4], len; + u32 reading_tmp, writing_tmp, mask; reg_start_addr = registers[reg].start_address; msb = registers[reg].msb; lsb = registers[reg].lsb; - page = registers[reg].page; - len = (msb >> 3) + 1; mask = REG_MASK(msb - lsb); - - ret = rtl2832_rd_regs(priv, reg_start_addr, page, &reading[0], len); + ret = rtl2832_bulk_read(client, reg_start_addr, reading, len); if (ret) goto err; @@ -346,49 +245,23 @@ static int rtl2832_wr_demod_reg(struct rtl2832_priv *priv, int reg, u32 val) writing_tmp = reading_tmp & ~(mask << lsb); writing_tmp |= ((val & mask) << lsb); - for (i = 0; i < len; i++) writing[i] = (writing_tmp >> ((len - 1 - i) * 8)) & 0xff; - ret = rtl2832_wr_regs(priv, reg_start_addr, page, &writing[0], len); - if (ret) - goto err; - - return ret; - -err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - return ret; - -} - -static int rtl2832_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) -{ - int ret; - struct rtl2832_priv *priv = fe->demodulator_priv; - - dev_dbg(&priv->i2c->dev, "%s: enable=%d\n", __func__, enable); - - /* gate already open or close */ - if (priv->i2c_gate_state == enable) - return 0; - - ret = rtl2832_wr_demod_reg(priv, DVBT_IIC_REPEAT, (enable ? 0x1 : 0x0)); + ret = rtl2832_bulk_write(client, reg_start_addr, writing, len); if (ret) goto err; - priv->i2c_gate_state = enable; - - return ret; + return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } - static int rtl2832_set_if(struct dvb_frontend *fe, u32 if_freq) { - struct rtl2832_priv *priv = fe->demodulator_priv; + struct rtl2832_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; int ret; u64 pset_iffreq; u8 en_bbin = (if_freq == 0 ? 0x1 : 0x0); @@ -397,30 +270,35 @@ static int rtl2832_set_if(struct dvb_frontend *fe, u32 if_freq) * PSET_IFFREQ = - floor((IfFreqHz % CrystalFreqHz) * pow(2, 22) * / CrystalFreqHz) */ - - pset_iffreq = if_freq % priv->cfg.xtal; + pset_iffreq = if_freq % dev->pdata->clk; pset_iffreq *= 0x400000; - pset_iffreq = div_u64(pset_iffreq, priv->cfg.xtal); + pset_iffreq = div_u64(pset_iffreq, dev->pdata->clk); pset_iffreq = -pset_iffreq; pset_iffreq = pset_iffreq & 0x3fffff; - dev_dbg(&priv->i2c->dev, "%s: if_frequency=%d pset_iffreq=%08x\n", - __func__, if_freq, (unsigned)pset_iffreq); + dev_dbg(&client->dev, "if_frequency=%d pset_iffreq=%08x\n", + if_freq, (unsigned)pset_iffreq); - ret = rtl2832_wr_demod_reg(priv, DVBT_EN_BBIN, en_bbin); + ret = rtl2832_wr_demod_reg(dev, DVBT_EN_BBIN, en_bbin); if (ret) - return ret; + goto err; - ret = rtl2832_wr_demod_reg(priv, DVBT_PSET_IFFREQ, pset_iffreq); + ret = rtl2832_wr_demod_reg(dev, DVBT_PSET_IFFREQ, pset_iffreq); + if (ret) + goto err; + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int rtl2832_init(struct dvb_frontend *fe) { - struct rtl2832_priv *priv = fe->demodulator_priv; + struct rtl2832_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache; const struct rtl2832_reg_value *init; int i, ret, len; - /* initialization values for the demodulator registers */ struct rtl2832_reg_value rtl2832_initial_regs[] = { {DVBT_AD_EN_REG, 0x1}, @@ -467,19 +345,19 @@ static int rtl2832_init(struct dvb_frontend *fe) {DVBT_CR_THD_SET2, 0x1}, }; - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + dev_dbg(&client->dev, "\n"); for (i = 0; i < ARRAY_SIZE(rtl2832_initial_regs); i++) { - ret = rtl2832_wr_demod_reg(priv, rtl2832_initial_regs[i].reg, + ret = rtl2832_wr_demod_reg(dev, rtl2832_initial_regs[i].reg, rtl2832_initial_regs[i].value); if (ret) goto err; } /* load tuner specific settings */ - dev_dbg(&priv->i2c->dev, "%s: load settings for tuner=%02x\n", - __func__, priv->cfg.tuner); - switch (priv->cfg.tuner) { + dev_dbg(&client->dev, "load settings for tuner=%02x\n", + dev->pdata->tuner); + switch (dev->pdata->tuner) { case RTL2832_TUNER_FC0012: case RTL2832_TUNER_FC0013: len = ARRAY_SIZE(rtl2832_tuner_init_fc0012); @@ -504,51 +382,60 @@ static int rtl2832_init(struct dvb_frontend *fe) } for (i = 0; i < len; i++) { - ret = rtl2832_wr_demod_reg(priv, init[i].reg, init[i].value); + ret = rtl2832_wr_demod_reg(dev, init[i].reg, init[i].value); if (ret) goto err; } - /* - * r820t NIM code does a software reset here at the demod - - * may not be needed, as there's already a software reset at - * set_params() - */ -#if 1 - /* soft reset */ - ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x1); - if (ret) - goto err; - - ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x0); - if (ret) - goto err; -#endif - - priv->sleeping = false; - - return ret; + /* init stats here in order signal app which stats are supported */ + c->strength.len = 1; + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->cnr.len = 1; + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_error.len = 1; + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.len = 1; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + /* start statistics polling */ + schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000)); + dev->sleeping = false; + return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int rtl2832_sleep(struct dvb_frontend *fe) { - struct rtl2832_priv *priv = fe->demodulator_priv; + struct rtl2832_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + + dev_dbg(&client->dev, "\n"); + + dev->sleeping = true; + /* stop statistics polling */ + cancel_delayed_work_sync(&dev->stat_work); + dev->fe_status = 0; + + ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1); + if (ret) + goto err; - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); - priv->sleeping = true; return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; } static int rtl2832_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *s) { - struct rtl2832_priv *priv = fe->demodulator_priv; + struct rtl2832_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); + dev_dbg(&client->dev, "\n"); s->min_delay_ms = 1000; s->step_size = fe->ops.info.frequency_stepsize * 2; s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1; @@ -557,7 +444,8 @@ static int rtl2832_get_tune_settings(struct dvb_frontend *fe, static int rtl2832_set_frontend(struct dvb_frontend *fe) { - struct rtl2832_priv *priv = fe->demodulator_priv; + struct rtl2832_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i, j; u64 bw_mode, num, num2; @@ -588,17 +476,15 @@ static int rtl2832_set_frontend(struct dvb_frontend *fe) }, }; - - dev_dbg(&priv->i2c->dev, - "%s: frequency=%d bandwidth_hz=%d inversion=%d\n", - __func__, c->frequency, c->bandwidth_hz, c->inversion); + dev_dbg(&client->dev, "frequency=%u bandwidth_hz=%u inversion=%u\n", + c->frequency, c->bandwidth_hz, c->inversion); /* program tuner */ if (fe->ops.tuner_ops.set_params) fe->ops.tuner_ops.set_params(fe); /* PIP mode related */ - ret = rtl2832_wr_regs(priv, 0x92, 1, "\x00\x0f\xff", 3); + ret = rtl2832_bulk_write(client, 0x192, "\x00\x0f\xff", 3); if (ret) goto err; @@ -629,12 +515,14 @@ static int rtl2832_set_frontend(struct dvb_frontend *fe) bw_mode = 64000000; break; default: - dev_dbg(&priv->i2c->dev, "%s: invalid bandwidth\n", __func__); - return -EINVAL; + dev_err(&client->dev, "invalid bandwidth_hz %u\n", + c->bandwidth_hz); + ret = -EINVAL; + goto err; } for (j = 0; j < sizeof(bw_params[0]); j++) { - ret = rtl2832_wr_regs(priv, 0x1c+j, 1, &bw_params[i][j], 1); + ret = rtl2832_bulk_write(client, 0x11c + j, &bw_params[i][j], 1); if (ret) goto err; } @@ -643,11 +531,11 @@ static int rtl2832_set_frontend(struct dvb_frontend *fe) * RSAMP_RATIO = floor(CrystalFreqHz * 7 * pow(2, 22) * / ConstWithBandwidthMode) */ - num = priv->cfg.xtal * 7; + num = dev->pdata->clk * 7; num *= 0x400000; num = div_u64(num, bw_mode); resamp_ratio = num & 0x3ffffff; - ret = rtl2832_wr_demod_reg(priv, DVBT_RSAMP_RATIO, resamp_ratio); + ret = rtl2832_wr_demod_reg(dev, DVBT_RSAMP_RATIO, resamp_ratio); if (ret) goto err; @@ -656,48 +544,49 @@ static int rtl2832_set_frontend(struct dvb_frontend *fe) * / (CrystalFreqHz * 7)) */ num = bw_mode << 20; - num2 = priv->cfg.xtal * 7; + num2 = dev->pdata->clk * 7; num = div_u64(num, num2); num = -num; cfreq_off_ratio = num & 0xfffff; - ret = rtl2832_wr_demod_reg(priv, DVBT_CFREQ_OFF_RATIO, cfreq_off_ratio); + ret = rtl2832_wr_demod_reg(dev, DVBT_CFREQ_OFF_RATIO, cfreq_off_ratio); if (ret) goto err; /* soft reset */ - ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x1); + ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1); if (ret) goto err; - ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x0); + ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x0); if (ret) goto err; - return ret; + return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int rtl2832_get_frontend(struct dvb_frontend *fe) { - struct rtl2832_priv *priv = fe->demodulator_priv; + struct rtl2832_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; u8 buf[3]; - if (priv->sleeping) + if (dev->sleeping) return 0; - ret = rtl2832_rd_regs(priv, 0x3c, 3, buf, 2); + ret = rtl2832_bulk_read(client, 0x33c, buf, 2); if (ret) goto err; - ret = rtl2832_rd_reg(priv, 0x51, 3, &buf[2]); + ret = rtl2832_bulk_read(client, 0x351, &buf[2], 1); if (ret) goto err; - dev_dbg(&priv->i2c->dev, "%s: TPS=%*ph\n", __func__, 3, buf); + dev_dbg(&client->dev, "TPS=%*ph\n", 3, buf); switch ((buf[0] >> 2) & 3) { case 0: @@ -787,403 +676,652 @@ static int rtl2832_get_frontend(struct dvb_frontend *fe) return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int rtl2832_read_status(struct dvb_frontend *fe, fe_status_t *status) { - struct rtl2832_priv *priv = fe->demodulator_priv; + struct rtl2832_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; int ret; u32 tmp; - *status = 0; - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); - if (priv->sleeping) + dev_dbg(&client->dev, "\n"); + + *status = 0; + if (dev->sleeping) return 0; - ret = rtl2832_rd_demod_reg(priv, DVBT_FSM_STAGE, &tmp); + ret = rtl2832_rd_demod_reg(dev, DVBT_FSM_STAGE, &tmp); if (ret) goto err; if (tmp == 11) { *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; - } - /* TODO find out if this is also true for rtl2832? */ - /*else if (tmp == 10) { + } else if (tmp == 10) { *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI; - }*/ + } - return ret; + dev->fe_status = *status; + return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int rtl2832_read_snr(struct dvb_frontend *fe, u16 *snr) { - struct rtl2832_priv *priv = fe->demodulator_priv; - int ret, hierarchy, constellation; - u8 buf[2], tmp; - u16 tmp16; -#define CONSTELLATION_NUM 3 -#define HIERARCHY_NUM 4 - static const u32 snr_constant[CONSTELLATION_NUM][HIERARCHY_NUM] = { - { 85387325, 85387325, 85387325, 85387325 }, - { 86676178, 86676178, 87167949, 87795660 }, - { 87659938, 87659938, 87885178, 88241743 }, - }; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; - /* reports SNR in resolution of 0.1 dB */ + /* report SNR in resolution of 0.1 dB */ + if (c->cnr.stat[0].scale == FE_SCALE_DECIBEL) + *snr = div_s64(c->cnr.stat[0].svalue, 100); + else + *snr = 0; - ret = rtl2832_rd_reg(priv, 0x3c, 3, &tmp); - if (ret) - goto err; + return 0; +} - constellation = (tmp >> 2) & 0x03; /* [3:2] */ - if (constellation > CONSTELLATION_NUM - 1) - goto err; +static int rtl2832_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct rtl2832_dev *dev = fe->demodulator_priv; - hierarchy = (tmp >> 4) & 0x07; /* [6:4] */ - if (hierarchy > HIERARCHY_NUM - 1) - goto err; + *ber = (dev->post_bit_error - dev->post_bit_error_prev); + dev->post_bit_error_prev = dev->post_bit_error; - ret = rtl2832_rd_regs(priv, 0x0c, 4, buf, 2); - if (ret) - goto err; + return 0; +} - tmp16 = buf[0] << 8 | buf[1]; +static void rtl2832_stat_work(struct work_struct *work) +{ + struct rtl2832_dev *dev = container_of(work, struct rtl2832_dev, stat_work.work); + struct i2c_client *client = dev->client; + struct dtv_frontend_properties *c = &dev->fe.dtv_property_cache; + int ret, tmp; + u8 u8tmp, buf[2]; + u16 u16tmp; + + dev_dbg(&client->dev, "\n"); + + /* signal strength */ + if (dev->fe_status & FE_HAS_SIGNAL) { + /* read digital AGC */ + ret = rtl2832_bulk_read(client, 0x305, &u8tmp, 1); + if (ret) + goto err; - if (tmp16) - *snr = (snr_constant[constellation][hierarchy] - - intlog10(tmp16)) / ((1 << 24) / 100); - else - *snr = 0; + dev_dbg(&client->dev, "digital agc=%02x", u8tmp); - return 0; + u8tmp = ~u8tmp; + u16tmp = u8tmp << 8 | u8tmp << 0; + + c->strength.stat[0].scale = FE_SCALE_RELATIVE; + c->strength.stat[0].uvalue = u16tmp; + } else { + c->strength.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* CNR */ + if (dev->fe_status & FE_HAS_VITERBI) { + unsigned hierarchy, constellation; + #define CONSTELLATION_NUM 3 + #define HIERARCHY_NUM 4 + static const u32 constant[CONSTELLATION_NUM][HIERARCHY_NUM] = { + {85387325, 85387325, 85387325, 85387325}, + {86676178, 86676178, 87167949, 87795660}, + {87659938, 87659938, 87885178, 88241743}, + }; + + ret = rtl2832_bulk_read(client, 0x33c, &u8tmp, 1); + if (ret) + goto err; + + constellation = (u8tmp >> 2) & 0x03; /* [3:2] */ + if (constellation > CONSTELLATION_NUM - 1) + goto err_schedule_delayed_work; + + hierarchy = (u8tmp >> 4) & 0x07; /* [6:4] */ + if (hierarchy > HIERARCHY_NUM - 1) + goto err_schedule_delayed_work; + + ret = rtl2832_bulk_read(client, 0x40c, buf, 2); + if (ret) + goto err; + + u16tmp = buf[0] << 8 | buf[1] << 0; + if (u16tmp) + tmp = (constant[constellation][hierarchy] - + intlog10(u16tmp)) / ((1 << 24) / 10000); + else + tmp = 0; + + dev_dbg(&client->dev, "cnr raw=%u\n", u16tmp); + + c->cnr.stat[0].scale = FE_SCALE_DECIBEL; + c->cnr.stat[0].svalue = tmp; + } else { + c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + + /* BER */ + if (dev->fe_status & FE_HAS_LOCK) { + ret = rtl2832_bulk_read(client, 0x34e, buf, 2); + if (ret) + goto err; + + u16tmp = buf[0] << 8 | buf[1] << 0; + dev->post_bit_error += u16tmp; + dev->post_bit_count += 1000000; + + dev_dbg(&client->dev, "ber errors=%u total=1000000\n", u16tmp); + + c->post_bit_error.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_error.stat[0].uvalue = dev->post_bit_error; + c->post_bit_count.stat[0].scale = FE_SCALE_COUNTER; + c->post_bit_count.stat[0].uvalue = dev->post_bit_count; + } else { + c->post_bit_error.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + c->post_bit_count.stat[0].scale = FE_SCALE_NOT_AVAILABLE; + } + +err_schedule_delayed_work: + schedule_delayed_work(&dev->stat_work, msecs_to_jiffies(2000)); + return; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - return ret; + dev_dbg(&client->dev, "failed=%d\n", ret); } -static int rtl2832_read_ber(struct dvb_frontend *fe, u32 *ber) +/* + * I2C gate/mux/repeater logic + * We must use unlocked __i2c_transfer() here (through regmap) because of I2C + * adapter lock is already taken by tuner driver. + * There is delay mechanism to avoid unneeded I2C gate open / close. Gate close + * is delayed here a little bit in order to see if there is sequence of I2C + * messages sent to same I2C bus. + */ +static void rtl2832_i2c_gate_work(struct work_struct *work) { - struct rtl2832_priv *priv = fe->demodulator_priv; + struct rtl2832_dev *dev = container_of(work, struct rtl2832_dev, i2c_gate_work.work); + struct i2c_client *client = dev->client; int ret; - u8 buf[2]; - ret = rtl2832_rd_regs(priv, 0x4e, 3, buf, 2); + /* close gate */ + ret = rtl2832_update_bits(dev->client, 0x101, 0x08, 0x00); if (ret) goto err; - *ber = buf[0] << 8 | buf[1]; + return; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); +} + +static int rtl2832_select(struct i2c_adapter *adap, void *mux_priv, u32 chan_id) +{ + struct rtl2832_dev *dev = mux_priv; + struct i2c_client *client = dev->client; + int ret; + + /* terminate possible gate closing */ + cancel_delayed_work(&dev->i2c_gate_work); + + /* + * I2C adapter lock is already taken and due to that we will use + * regmap_update_bits() which does not lock again I2C adapter. + */ + ret = regmap_update_bits(dev->regmap, 0x101, 0x08, 0x08); + if (ret) + goto err; return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } -static struct dvb_frontend_ops rtl2832_ops; +static int rtl2832_deselect(struct i2c_adapter *adap, void *mux_priv, + u32 chan_id) +{ + struct rtl2832_dev *dev = mux_priv; + + schedule_delayed_work(&dev->i2c_gate_work, usecs_to_jiffies(100)); + return 0; +} + +static struct dvb_frontend_ops rtl2832_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Realtek RTL2832 (DVB-T)", + .frequency_min = 174000000, + .frequency_max = 862000000, + .frequency_stepsize = 166667, + .caps = FE_CAN_FEC_1_2 | + FE_CAN_FEC_2_3 | + FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | + FE_CAN_FEC_7_8 | + FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | + FE_CAN_QAM_64 | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_RECOVER | + FE_CAN_MUTE_TS + }, -static void rtl2832_release(struct dvb_frontend *fe) + .init = rtl2832_init, + .sleep = rtl2832_sleep, + + .get_tune_settings = rtl2832_get_tune_settings, + + .set_frontend = rtl2832_set_frontend, + .get_frontend = rtl2832_get_frontend, + + .read_status = rtl2832_read_status, + .read_snr = rtl2832_read_snr, + .read_ber = rtl2832_read_ber, +}; + +static bool rtl2832_volatile_reg(struct device *dev, unsigned int reg) { - struct rtl2832_priv *priv = fe->demodulator_priv; + switch (reg) { + case 0x305: + case 0x33c: + case 0x34e: + case 0x351: + case 0x40c ... 0x40d: + return true; + default: + break; + } - dev_dbg(&priv->i2c->dev, "%s:\n", __func__); - cancel_delayed_work_sync(&priv->i2c_gate_work); - i2c_del_mux_adapter(priv->i2c_adapter_tuner); - i2c_del_mux_adapter(priv->i2c_adapter); - kfree(priv); + return false; } /* - * Delay mechanism to avoid unneeded I2C gate open / close. Gate close is - * delayed here a little bit in order to see if there is sequence of I2C - * messages sent to same I2C bus. - * We must use unlocked version of __i2c_transfer() in order to avoid deadlock - * as lock is already taken by calling muxed i2c_transfer(). + * We implement own I2C access routines for regmap in order to get manual access + * to I2C adapter lock, which is needed for I2C mux adapter. */ -static void rtl2832_i2c_gate_work(struct work_struct *work) +static int rtl2832_regmap_read(void *context, const void *reg_buf, + size_t reg_size, void *val_buf, size_t val_size) { - struct rtl2832_priv *priv = container_of(work, - struct rtl2832_priv, i2c_gate_work.work); - struct i2c_adapter *adap = priv->i2c; + struct i2c_client *client = context; int ret; - u8 buf[2]; - struct i2c_msg msg[1] = { + struct i2c_msg msg[2] = { { - .addr = priv->cfg.i2c_addr, + .addr = client->addr, .flags = 0, - .len = sizeof(buf), - .buf = buf, + .len = reg_size, + .buf = (u8 *)reg_buf, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .len = val_size, + .buf = val_buf, } }; - /* select reg bank 1 */ - buf[0] = 0x00; - buf[1] = 0x01; - ret = __i2c_transfer(adap, msg, 1); - if (ret != 1) - goto err; - - priv->page = 1; - - /* close I2C repeater gate */ - buf[0] = 0x01; - buf[1] = 0x10; - ret = __i2c_transfer(adap, msg, 1); - if (ret != 1) - goto err; - - priv->i2c_gate_state = false; - - return; -err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); - - return; + ret = __i2c_transfer(client->adapter, msg, 2); + if (ret != 2) { + dev_warn(&client->dev, "i2c reg read failed %d\n", ret); + if (ret >= 0) + ret = -EREMOTEIO; + return ret; + } + return 0; } -static int rtl2832_select(struct i2c_adapter *adap, void *mux_priv, u32 chan_id) +static int rtl2832_regmap_write(void *context, const void *data, size_t count) { - struct rtl2832_priv *priv = mux_priv; + struct i2c_client *client = context; int ret; - u8 buf[2], val; struct i2c_msg msg[1] = { { - .addr = priv->cfg.i2c_addr, + .addr = client->addr, .flags = 0, - .len = sizeof(buf), - .buf = buf, + .len = count, + .buf = (u8 *)data, } }; - struct i2c_msg msg_rd[2] = { + + ret = __i2c_transfer(client->adapter, msg, 1); + if (ret != 1) { + dev_warn(&client->dev, "i2c reg write failed %d\n", ret); + if (ret >= 0) + ret = -EREMOTEIO; + return ret; + } + return 0; +} + +static int rtl2832_regmap_gather_write(void *context, const void *reg, + size_t reg_len, const void *val, + size_t val_len) +{ + struct i2c_client *client = context; + int ret; + u8 buf[256]; + struct i2c_msg msg[1] = { { - .addr = priv->cfg.i2c_addr, + .addr = client->addr, .flags = 0, - .len = 1, - .buf = "\x01", - }, { - .addr = priv->cfg.i2c_addr, - .flags = I2C_M_RD, - .len = 1, - .buf = &val, + .len = 1 + val_len, + .buf = buf, } }; - /* terminate possible gate closing */ - cancel_delayed_work_sync(&priv->i2c_gate_work); + buf[0] = *(u8 const *)reg; + memcpy(&buf[1], val, val_len); - if (priv->i2c_gate_state == chan_id) - return 0; - - /* select reg bank 1 */ - buf[0] = 0x00; - buf[1] = 0x01; - ret = __i2c_transfer(adap, msg, 1); - if (ret != 1) - goto err; - - priv->page = 1; + ret = __i2c_transfer(client->adapter, msg, 1); + if (ret != 1) { + dev_warn(&client->dev, "i2c reg write failed %d\n", ret); + if (ret >= 0) + ret = -EREMOTEIO; + return ret; + } + return 0; +} - /* we must read that register, otherwise there will be errors */ - ret = __i2c_transfer(adap, msg_rd, 2); - if (ret != 2) - goto err; +/* + * FIXME: Hack. Implement own regmap locking in order to silence lockdep + * recursive lock warning. That happens when regmap I2C client calls I2C mux + * adapter, which leads demod I2C repeater enable via demod regmap. Operation + * takes two regmap locks recursively - but those are different regmap instances + * in a two different I2C drivers, so it is not deadlock. Proper fix is to make + * regmap aware of lockdep. + */ +static void rtl2832_regmap_lock(void *__dev) +{ + struct rtl2832_dev *dev = __dev; + struct i2c_client *client = dev->client; - /* open or close I2C repeater gate */ - buf[0] = 0x01; - if (chan_id == 1) - buf[1] = 0x18; /* open */ - else - buf[1] = 0x10; /* close */ + dev_dbg(&client->dev, "\n"); + mutex_lock(&dev->regmap_mutex); +} - ret = __i2c_transfer(adap, msg, 1); - if (ret != 1) - goto err; +static void rtl2832_regmap_unlock(void *__dev) +{ + struct rtl2832_dev *dev = __dev; + struct i2c_client *client = dev->client; - priv->i2c_gate_state = chan_id; + dev_dbg(&client->dev, "\n"); + mutex_unlock(&dev->regmap_mutex); +} - return 0; -err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); +static struct dvb_frontend *rtl2832_get_dvb_frontend(struct i2c_client *client) +{ + struct rtl2832_dev *dev = i2c_get_clientdata(client); - return -EREMOTEIO; + dev_dbg(&client->dev, "\n"); + return &dev->fe; } -static int rtl2832_deselect(struct i2c_adapter *adap, void *mux_priv, - u32 chan_id) +static struct i2c_adapter *rtl2832_get_i2c_adapter(struct i2c_client *client) { - struct rtl2832_priv *priv = mux_priv; - schedule_delayed_work(&priv->i2c_gate_work, usecs_to_jiffies(100)); - return 0; + struct rtl2832_dev *dev = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "\n"); + return dev->i2c_adapter_tuner; } -int rtl2832_enable_external_ts_if(struct dvb_frontend *fe) +static int rtl2832_enable_slave_ts(struct i2c_client *client) { - struct rtl2832_priv *priv = fe->demodulator_priv; + struct rtl2832_dev *dev = i2c_get_clientdata(client); int ret; - dev_dbg(&priv->i2c->dev, "%s: setting PIP mode\n", __func__); + dev_dbg(&client->dev, "\n"); - ret = rtl2832_wr_regs(priv, 0x0c, 1, "\x5f\xff", 2); + ret = rtl2832_bulk_write(client, 0x10c, "\x5f\xff", 2); if (ret) goto err; - ret = rtl2832_wr_demod_reg(priv, DVBT_PIP_ON, 0x1); + ret = rtl2832_wr_demod_reg(dev, DVBT_PIP_ON, 0x1); if (ret) goto err; - ret = rtl2832_wr_reg(priv, 0xbc, 0, 0x18); + ret = rtl2832_bulk_write(client, 0x0bc, "\x18", 1); if (ret) goto err; - ret = rtl2832_wr_reg(priv, 0x22, 0, 0x01); + ret = rtl2832_bulk_write(client, 0x022, "\x01", 1); if (ret) goto err; - ret = rtl2832_wr_reg(priv, 0x26, 0, 0x1f); + ret = rtl2832_bulk_write(client, 0x026, "\x1f", 1); if (ret) goto err; - ret = rtl2832_wr_reg(priv, 0x27, 0, 0xff); + ret = rtl2832_bulk_write(client, 0x027, "\xff", 1); if (ret) goto err; - ret = rtl2832_wr_regs(priv, 0x92, 1, "\x7f\xf7\xff", 3); + ret = rtl2832_bulk_write(client, 0x192, "\x7f\xf7\xff", 3); if (ret) goto err; /* soft reset */ - ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x1); + ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1); if (ret) goto err; - ret = rtl2832_wr_demod_reg(priv, DVBT_SOFT_RST, 0x0); + ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x0); if (ret) goto err; return 0; err: - dev_dbg(&priv->i2c->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; - } -EXPORT_SYMBOL(rtl2832_enable_external_ts_if); -struct i2c_adapter *rtl2832_get_i2c_adapter(struct dvb_frontend *fe) +static int rtl2832_pid_filter_ctrl(struct dvb_frontend *fe, int onoff) { - struct rtl2832_priv *priv = fe->demodulator_priv; - return priv->i2c_adapter_tuner; + struct rtl2832_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + u8 u8tmp; + + dev_dbg(&client->dev, "onoff=%d\n", onoff); + + /* enable / disable PID filter */ + if (onoff) + u8tmp = 0x80; + else + u8tmp = 0x00; + + ret = rtl2832_update_bits(client, 0x061, 0xc0, u8tmp); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; } -EXPORT_SYMBOL(rtl2832_get_i2c_adapter); -struct i2c_adapter *rtl2832_get_private_i2c_adapter(struct dvb_frontend *fe) +static int rtl2832_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid, + int onoff) { - struct rtl2832_priv *priv = fe->demodulator_priv; - return priv->i2c_adapter; + struct rtl2832_dev *dev = fe->demodulator_priv; + struct i2c_client *client = dev->client; + int ret; + u8 buf[4]; + + dev_dbg(&client->dev, "index=%d pid=%04x onoff=%d\n", + index, pid, onoff); + + /* skip invalid PIDs (0x2000) */ + if (pid > 0x1fff || index > 32) + return 0; + + if (onoff) + set_bit(index, &dev->filters); + else + clear_bit(index, &dev->filters); + + /* enable / disable PIDs */ + buf[0] = (dev->filters >> 0) & 0xff; + buf[1] = (dev->filters >> 8) & 0xff; + buf[2] = (dev->filters >> 16) & 0xff; + buf[3] = (dev->filters >> 24) & 0xff; + ret = rtl2832_bulk_write(client, 0x062, buf, 4); + if (ret) + goto err; + + /* add PID */ + buf[0] = (pid >> 8) & 0xff; + buf[1] = (pid >> 0) & 0xff; + ret = rtl2832_bulk_write(client, 0x066 + 2 * index, buf, 2); + if (ret) + goto err; + + return 0; +err: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; } -EXPORT_SYMBOL(rtl2832_get_private_i2c_adapter); -struct dvb_frontend *rtl2832_attach(const struct rtl2832_config *cfg, - struct i2c_adapter *i2c) +static int rtl2832_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - struct rtl2832_priv *priv = NULL; - int ret = 0; + struct rtl2832_platform_data *pdata = client->dev.platform_data; + struct i2c_adapter *i2c = client->adapter; + struct rtl2832_dev *dev; + int ret; u8 tmp; + static const struct regmap_bus regmap_bus = { + .read = rtl2832_regmap_read, + .write = rtl2832_regmap_write, + .gather_write = rtl2832_regmap_gather_write, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, + }; + static const struct regmap_range_cfg regmap_range_cfg[] = { + { + .selector_reg = 0x00, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 0x100, + .range_min = 0 * 0x100, + .range_max = 5 * 0x100, + }, + }; - dev_dbg(&i2c->dev, "%s:\n", __func__); + dev_dbg(&client->dev, "\n"); /* allocate memory for the internal state */ - priv = kzalloc(sizeof(struct rtl2832_priv), GFP_KERNEL); - if (priv == NULL) + dev = kzalloc(sizeof(struct rtl2832_dev), GFP_KERNEL); + if (dev == NULL) { + ret = -ENOMEM; goto err; + } - /* setup the priv */ - priv->i2c = i2c; - priv->tuner = cfg->tuner; - memcpy(&priv->cfg, cfg, sizeof(struct rtl2832_config)); - INIT_DELAYED_WORK(&priv->i2c_gate_work, rtl2832_i2c_gate_work); - - /* create muxed i2c adapter for demod itself */ - priv->i2c_adapter = i2c_add_mux_adapter(i2c, &i2c->dev, priv, 0, 0, 0, - rtl2832_select, NULL); - if (priv->i2c_adapter == NULL) - goto err; + /* setup the state */ + i2c_set_clientdata(client, dev); + dev->client = client; + dev->pdata = client->dev.platform_data; + dev->sleeping = true; + INIT_DELAYED_WORK(&dev->i2c_gate_work, rtl2832_i2c_gate_work); + INIT_DELAYED_WORK(&dev->stat_work, rtl2832_stat_work); + /* create regmap */ + mutex_init(&dev->regmap_mutex); + dev->regmap_config.reg_bits = 8, + dev->regmap_config.val_bits = 8, + dev->regmap_config.lock = rtl2832_regmap_lock, + dev->regmap_config.unlock = rtl2832_regmap_unlock, + dev->regmap_config.lock_arg = dev, + dev->regmap_config.volatile_reg = rtl2832_volatile_reg, + dev->regmap_config.max_register = 5 * 0x100, + dev->regmap_config.ranges = regmap_range_cfg, + dev->regmap_config.num_ranges = ARRAY_SIZE(regmap_range_cfg), + dev->regmap_config.cache_type = REGCACHE_RBTREE, + dev->regmap = regmap_init(&client->dev, ®map_bus, client, + &dev->regmap_config); + if (IS_ERR(dev->regmap)) { + ret = PTR_ERR(dev->regmap); + goto err_kfree; + } /* check if the demod is there */ - ret = rtl2832_rd_reg(priv, 0x00, 0x0, &tmp); + ret = rtl2832_bulk_read(client, 0x000, &tmp, 1); if (ret) - goto err; + goto err_regmap_exit; /* create muxed i2c adapter for demod tuner bus */ - priv->i2c_adapter_tuner = i2c_add_mux_adapter(i2c, &i2c->dev, priv, - 0, 1, 0, rtl2832_select, rtl2832_deselect); - if (priv->i2c_adapter_tuner == NULL) - goto err; + dev->i2c_adapter_tuner = i2c_add_mux_adapter(i2c, &i2c->dev, dev, + 0, 0, 0, rtl2832_select, rtl2832_deselect); + if (dev->i2c_adapter_tuner == NULL) { + ret = -ENODEV; + goto err_regmap_exit; + } /* create dvb_frontend */ - memcpy(&priv->fe.ops, &rtl2832_ops, sizeof(struct dvb_frontend_ops)); - priv->fe.demodulator_priv = priv; - - /* TODO implement sleep mode */ - priv->sleeping = true; - - return &priv->fe; + memcpy(&dev->fe.ops, &rtl2832_ops, sizeof(struct dvb_frontend_ops)); + dev->fe.demodulator_priv = dev; + + /* setup callbacks */ + pdata->get_dvb_frontend = rtl2832_get_dvb_frontend; + pdata->get_i2c_adapter = rtl2832_get_i2c_adapter; + pdata->enable_slave_ts = rtl2832_enable_slave_ts; + pdata->pid_filter = rtl2832_pid_filter; + pdata->pid_filter_ctrl = rtl2832_pid_filter_ctrl; + pdata->bulk_read = rtl2832_bulk_read; + pdata->bulk_write = rtl2832_bulk_write; + pdata->update_bits = rtl2832_update_bits; + + dev_info(&client->dev, "Realtek RTL2832 successfully attached\n"); + return 0; +err_regmap_exit: + regmap_exit(dev->regmap); +err_kfree: + kfree(dev); err: - dev_dbg(&i2c->dev, "%s: failed=%d\n", __func__, ret); - if (priv && priv->i2c_adapter) - i2c_del_mux_adapter(priv->i2c_adapter); - kfree(priv); - return NULL; + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; } -EXPORT_SYMBOL(rtl2832_attach); -static struct dvb_frontend_ops rtl2832_ops = { - .delsys = { SYS_DVBT }, - .info = { - .name = "Realtek RTL2832 (DVB-T)", - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_stepsize = 166667, - .caps = FE_CAN_FEC_1_2 | - FE_CAN_FEC_2_3 | - FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | - FE_CAN_FEC_7_8 | - FE_CAN_FEC_AUTO | - FE_CAN_QPSK | - FE_CAN_QAM_16 | - FE_CAN_QAM_64 | - FE_CAN_QAM_AUTO | - FE_CAN_TRANSMISSION_MODE_AUTO | - FE_CAN_GUARD_INTERVAL_AUTO | - FE_CAN_HIERARCHY_AUTO | - FE_CAN_RECOVER | - FE_CAN_MUTE_TS - }, +static int rtl2832_remove(struct i2c_client *client) +{ + struct rtl2832_dev *dev = i2c_get_clientdata(client); - .release = rtl2832_release, + dev_dbg(&client->dev, "\n"); - .init = rtl2832_init, - .sleep = rtl2832_sleep, + cancel_delayed_work_sync(&dev->i2c_gate_work); - .get_tune_settings = rtl2832_get_tune_settings, + i2c_del_mux_adapter(dev->i2c_adapter_tuner); - .set_frontend = rtl2832_set_frontend, - .get_frontend = rtl2832_get_frontend, + regmap_exit(dev->regmap); - .read_status = rtl2832_read_status, - .read_snr = rtl2832_read_snr, - .read_ber = rtl2832_read_ber, + kfree(dev); - .i2c_gate_ctrl = rtl2832_i2c_gate_ctrl, + return 0; +} + +static const struct i2c_device_id rtl2832_id_table[] = { + {"rtl2832", 0}, + {} }; +MODULE_DEVICE_TABLE(i2c, rtl2832_id_table); + +static struct i2c_driver rtl2832_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "rtl2832", + }, + .probe = rtl2832_probe, + .remove = rtl2832_remove, + .id_table = rtl2832_id_table, +}; + +module_i2c_driver(rtl2832_driver); MODULE_AUTHOR("Thomas Mair <mair.thomas86@gmail.com>"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); MODULE_DESCRIPTION("Realtek RTL2832 DVB-T demodulator driver"); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.5"); diff --git a/drivers/media/dvb-frontends/rtl2832.h b/drivers/media/dvb-frontends/rtl2832.h index 5254c1dfc8de..a8e912e679a5 100644 --- a/drivers/media/dvb-frontends/rtl2832.h +++ b/drivers/media/dvb-frontends/rtl2832.h @@ -2,6 +2,7 @@ * Realtek RTL2832 DVB-T demodulator driver * * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com> + * Copyright (C) 2012-2014 Antti Palosaari <crope@iki.fi> * * 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 @@ -21,86 +22,42 @@ #ifndef RTL2832_H #define RTL2832_H -#include <linux/kconfig.h> #include <linux/dvb/frontend.h> +#include <linux/i2c-mux.h> + +/** + * struct rtl2832_platform_data - Platform data for the rtl2832 driver + * @clk: Clock frequency (4000000, 16000000, 25000000, 28800000). + * @tuner: Used tuner model. + * @get_dvb_frontend: Get DVB frontend. + * @get_i2c_adapter: Get I2C adapter. + * @enable_slave_ts: Enable slave TS IF. + * @pid_filter: Set PID to PID filter. + * @pid_filter_ctrl: Control PID filter. + */ -struct rtl2832_config { - /* - * Demodulator I2C address. - */ - u8 i2c_addr; - - /* - * Xtal frequency. - * Hz - * 4000000, 16000000, 25000000, 28800000 - */ - u32 xtal; - +struct rtl2832_platform_data { + u32 clk; /* - * tuner - * XXX: This must be keep sync with dvb_usb_rtl28xxu demod driver. + * XXX: This list must be kept sync with dvb_usb_rtl28xxu USB IF driver. */ #define RTL2832_TUNER_TUA9001 0x24 #define RTL2832_TUNER_FC0012 0x26 #define RTL2832_TUNER_E4000 0x27 #define RTL2832_TUNER_FC0013 0x29 -#define RTL2832_TUNER_R820T 0x2a -#define RTL2832_TUNER_R828D 0x2b +#define RTL2832_TUNER_R820T 0x2a +#define RTL2832_TUNER_R828D 0x2b u8 tuner; -}; - -#if IS_ENABLED(CONFIG_DVB_RTL2832) -struct dvb_frontend *rtl2832_attach( - const struct rtl2832_config *cfg, - struct i2c_adapter *i2c -); - -extern struct i2c_adapter *rtl2832_get_i2c_adapter( - struct dvb_frontend *fe -); - -extern struct i2c_adapter *rtl2832_get_private_i2c_adapter( - struct dvb_frontend *fe -); - -extern int rtl2832_enable_external_ts_if( - struct dvb_frontend *fe -); - -#else - -static inline struct dvb_frontend *rtl2832_attach( - const struct rtl2832_config *config, - struct i2c_adapter *i2c -) -{ - pr_warn("%s: driver disabled by Kconfig\n", __func__); - return NULL; -} - -static inline struct i2c_adapter *rtl2832_get_i2c_adapter( - struct dvb_frontend *fe -) -{ - return NULL; -} - -static inline struct i2c_adapter *rtl2832_get_private_i2c_adapter( - struct dvb_frontend *fe -) -{ - return NULL; -} - -static inline int rtl2832_enable_external_ts_if( - struct dvb_frontend *fe -) -{ - return -ENODEV; -} - -#endif + struct dvb_frontend* (*get_dvb_frontend)(struct i2c_client *); + struct i2c_adapter* (*get_i2c_adapter)(struct i2c_client *); + int (*enable_slave_ts)(struct i2c_client *); + int (*pid_filter)(struct dvb_frontend *, u8, u16, int); + int (*pid_filter_ctrl)(struct dvb_frontend *, int); +/* private: Register access for SDR module use only */ + int (*bulk_read)(struct i2c_client *, unsigned int, void *, size_t); + int (*bulk_write)(struct i2c_client *, unsigned int, const void *, size_t); + int (*update_bits)(struct i2c_client *, unsigned int, unsigned int, unsigned int); +}; #endif /* RTL2832_H */ diff --git a/drivers/media/dvb-frontends/rtl2832_priv.h b/drivers/media/dvb-frontends/rtl2832_priv.h index ae469f032fe6..c3a922c37903 100644 --- a/drivers/media/dvb-frontends/rtl2832_priv.h +++ b/drivers/media/dvb-frontends/rtl2832_priv.h @@ -2,6 +2,7 @@ * Realtek RTL2832 DVB-T demodulator driver * * Copyright (C) 2012 Thomas Mair <thomas.mair86@gmail.com> + * Copyright (C) 2012-2014 Antti Palosaari <crope@iki.fi> * * 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 @@ -21,28 +22,34 @@ #ifndef RTL2832_PRIV_H #define RTL2832_PRIV_H +#include <linux/regmap.h> +#include <linux/math64.h> +#include <linux/bitops.h> + #include "dvb_frontend.h" +#include "dvb_math.h" #include "rtl2832.h" -#include <linux/i2c-mux.h> -struct rtl2832_priv { - struct i2c_adapter *i2c; - struct i2c_adapter *i2c_adapter; +struct rtl2832_dev { + struct rtl2832_platform_data *pdata; + struct i2c_client *client; + struct mutex regmap_mutex; + struct regmap_config regmap_config; + struct regmap *regmap; struct i2c_adapter *i2c_adapter_tuner; struct dvb_frontend fe; - struct rtl2832_config cfg; - - bool i2c_gate_state; + struct delayed_work stat_work; + fe_status_t fe_status; + u64 post_bit_error_prev; /* for old DVBv3 read_ber() calculation */ + u64 post_bit_error; + u64 post_bit_count; bool sleeping; - - u8 tuner; - u8 page; /* active register page */ struct delayed_work i2c_gate_work; + unsigned long filters; /* PID filter */ }; struct rtl2832_reg_entry { - u8 page; - u8 start_address; + u16 start_address; u8 msb; u8 lsb; }; @@ -52,7 +59,6 @@ struct rtl2832_reg_value { u32 value; }; - /* Demod register bit names */ enum DVBT_REG_BIT_NAME { DVBT_SOFT_RST, diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.c b/drivers/media/dvb-frontends/rtl2832_sdr.c index 2896b47c29d8..3ff8806ca584 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.c +++ b/drivers/media/dvb-frontends/rtl2832_sdr.c @@ -22,7 +22,6 @@ * */ -#include "dvb_frontend.h" #include "rtl2832_sdr.h" #include "dvb_usb.h" @@ -32,6 +31,7 @@ #include <media/v4l2-event.h> #include <media/videobuf2-vmalloc.h> +#include <linux/platform_device.h> #include <linux/jiffies.h> #include <linux/math64.h> @@ -107,16 +107,12 @@ struct rtl2832_sdr_frame_buf { struct list_head list; }; -struct rtl2832_sdr_state { +struct rtl2832_sdr_dev { #define POWER_ON (1 << 1) #define URB_BUF (1 << 2) unsigned long flags; - const struct rtl2832_config *cfg; - struct dvb_frontend *fe; - struct dvb_usb_device *d; - struct i2c_adapter *i2c; - u8 bank; + struct platform_device *pdev; struct video_device vdev; struct v4l2_device v4l2_dev; @@ -160,200 +156,77 @@ struct rtl2832_sdr_state { unsigned long jiffies_next; }; -/* write multiple hardware registers */ -static int rtl2832_sdr_wr(struct rtl2832_sdr_state *s, u8 reg, const u8 *val, - int len) -{ - int ret; -#define MAX_WR_LEN 24 -#define MAX_WR_XFER_LEN (MAX_WR_LEN + 1) - u8 buf[MAX_WR_XFER_LEN]; - struct i2c_msg msg[1] = { - { - .addr = s->cfg->i2c_addr, - .flags = 0, - .len = 1 + len, - .buf = buf, - } - }; - - if (WARN_ON(len > MAX_WR_LEN)) - return -EINVAL; - - buf[0] = reg; - memcpy(&buf[1], val, len); - - ret = i2c_transfer(s->i2c, msg, 1); - if (ret == 1) { - ret = 0; - } else { - dev_err(&s->i2c->dev, - "%s: I2C wr failed=%d reg=%02x len=%d\n", - KBUILD_MODNAME, ret, reg, len); - ret = -EREMOTEIO; - } - return ret; -} - -/* read multiple hardware registers */ -static int rtl2832_sdr_rd(struct rtl2832_sdr_state *s, u8 reg, u8 *val, int len) -{ - int ret; - struct i2c_msg msg[2] = { - { - .addr = s->cfg->i2c_addr, - .flags = 0, - .len = 1, - .buf = ®, - }, { - .addr = s->cfg->i2c_addr, - .flags = I2C_M_RD, - .len = len, - .buf = val, - } - }; - - ret = i2c_transfer(s->i2c, msg, 2); - if (ret == 2) { - ret = 0; - } else { - dev_err(&s->i2c->dev, - "%s: I2C rd failed=%d reg=%02x len=%d\n", - KBUILD_MODNAME, ret, reg, len); - ret = -EREMOTEIO; - } - return ret; -} - /* write multiple registers */ -static int rtl2832_sdr_wr_regs(struct rtl2832_sdr_state *s, u16 reg, +static int rtl2832_sdr_wr_regs(struct rtl2832_sdr_dev *dev, u16 reg, const u8 *val, int len) { - int ret; - u8 reg2 = (reg >> 0) & 0xff; - u8 bank = (reg >> 8) & 0xff; + struct platform_device *pdev = dev->pdev; + struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; + struct i2c_client *client = pdata->i2c_client; - /* switch bank if needed */ - if (bank != s->bank) { - ret = rtl2832_sdr_wr(s, 0x00, &bank, 1); - if (ret) - return ret; - - s->bank = bank; - } - - return rtl2832_sdr_wr(s, reg2, val, len); + return pdata->bulk_write(client, reg, val, len); } +#if 0 /* read multiple registers */ -static int rtl2832_sdr_rd_regs(struct rtl2832_sdr_state *s, u16 reg, u8 *val, +static int rtl2832_sdr_rd_regs(struct rtl2832_sdr_dev *dev, u16 reg, u8 *val, int len) { - int ret; - u8 reg2 = (reg >> 0) & 0xff; - u8 bank = (reg >> 8) & 0xff; - - /* switch bank if needed */ - if (bank != s->bank) { - ret = rtl2832_sdr_wr(s, 0x00, &bank, 1); - if (ret) - return ret; - - s->bank = bank; - } + struct platform_device *pdev = dev->pdev; + struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; + struct i2c_client *client = pdata->i2c_client; - return rtl2832_sdr_rd(s, reg2, val, len); + return pdata->bulk_read(client, reg, val, len); } +#endif /* write single register */ -static int rtl2832_sdr_wr_reg(struct rtl2832_sdr_state *s, u16 reg, u8 val) +static int rtl2832_sdr_wr_reg(struct rtl2832_sdr_dev *dev, u16 reg, u8 val) { - return rtl2832_sdr_wr_regs(s, reg, &val, 1); + return rtl2832_sdr_wr_regs(dev, reg, &val, 1); } -#if 0 -/* read single register */ -static int rtl2832_sdr_rd_reg(struct rtl2832_sdr_state *s, u16 reg, u8 *val) -{ - return rtl2832_sdr_rd_regs(s, reg, val, 1); -} -#endif - /* write single register with mask */ -static int rtl2832_sdr_wr_reg_mask(struct rtl2832_sdr_state *s, u16 reg, +static int rtl2832_sdr_wr_reg_mask(struct rtl2832_sdr_dev *dev, u16 reg, u8 val, u8 mask) { - int ret; - u8 tmp; - - /* no need for read if whole reg is written */ - if (mask != 0xff) { - ret = rtl2832_sdr_rd_regs(s, reg, &tmp, 1); - if (ret) - return ret; - - val &= mask; - tmp &= ~mask; - val |= tmp; - } - - return rtl2832_sdr_wr_regs(s, reg, &val, 1); -} - -#if 0 -/* read single register with mask */ -static int rtl2832_sdr_rd_reg_mask(struct rtl2832_sdr_state *s, u16 reg, - u8 *val, u8 mask) -{ - int ret, i; - u8 tmp; - - ret = rtl2832_sdr_rd_regs(s, reg, &tmp, 1); - if (ret) - return ret; - - tmp &= mask; - - /* find position of the first bit */ - for (i = 0; i < 8; i++) { - if ((mask >> i) & 0x01) - break; - } - *val = tmp >> i; + struct platform_device *pdev = dev->pdev; + struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; + struct i2c_client *client = pdata->i2c_client; - return 0; + return pdata->update_bits(client, reg, mask, val); } -#endif /* Private functions */ static struct rtl2832_sdr_frame_buf *rtl2832_sdr_get_next_fill_buf( - struct rtl2832_sdr_state *s) + struct rtl2832_sdr_dev *dev) { unsigned long flags; struct rtl2832_sdr_frame_buf *buf = NULL; - spin_lock_irqsave(&s->queued_bufs_lock, flags); - if (list_empty(&s->queued_bufs)) + spin_lock_irqsave(&dev->queued_bufs_lock, flags); + if (list_empty(&dev->queued_bufs)) goto leave; - buf = list_entry(s->queued_bufs.next, + buf = list_entry(dev->queued_bufs.next, struct rtl2832_sdr_frame_buf, list); list_del(&buf->list); leave: - spin_unlock_irqrestore(&s->queued_bufs_lock, flags); + spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); return buf; } -static unsigned int rtl2832_sdr_convert_stream(struct rtl2832_sdr_state *s, +static unsigned int rtl2832_sdr_convert_stream(struct rtl2832_sdr_dev *dev, void *dst, const u8 *src, unsigned int src_len) { + struct platform_device *pdev = dev->pdev; unsigned int dst_len; - if (s->pixelformat == V4L2_SDR_FMT_CU8) { + if (dev->pixelformat == V4L2_SDR_FMT_CU8) { /* native stream, no need to convert */ memcpy(dst, src, src_len); dst_len = src_len; - } else if (s->pixelformat == V4L2_SDR_FMT_CU16LE) { + } else if (dev->pixelformat == V4L2_SDR_FMT_CU16LE) { /* convert u8 to u16 */ unsigned int i; u16 *u16dst = dst; @@ -366,22 +239,21 @@ static unsigned int rtl2832_sdr_convert_stream(struct rtl2832_sdr_state *s, } /* calculate sample rate and output it in 10 seconds intervals */ - if (unlikely(time_is_before_jiffies(s->jiffies_next))) { + if (unlikely(time_is_before_jiffies(dev->jiffies_next))) { #define MSECS 10000UL unsigned int msecs = jiffies_to_msecs(jiffies - - s->jiffies_next + msecs_to_jiffies(MSECS)); - unsigned int samples = s->sample - s->sample_measured; - - s->jiffies_next = jiffies + msecs_to_jiffies(MSECS); - s->sample_measured = s->sample; - dev_dbg(&s->udev->dev, - "slen=%u samples=%u msecs=%u sample rate=%lu\n", - src_len, samples, msecs, - samples * 1000UL / msecs); + dev->jiffies_next + msecs_to_jiffies(MSECS)); + unsigned int samples = dev->sample - dev->sample_measured; + + dev->jiffies_next = jiffies + msecs_to_jiffies(MSECS); + dev->sample_measured = dev->sample; + dev_dbg(&pdev->dev, + "slen=%u samples=%u msecs=%u sample rate=%lu\n", + src_len, samples, msecs, samples * 1000UL / msecs); } /* total number of I+Q pairs */ - s->sample += src_len / 2; + dev->sample += src_len / 2; return dst_len; } @@ -392,13 +264,13 @@ static unsigned int rtl2832_sdr_convert_stream(struct rtl2832_sdr_state *s, */ static void rtl2832_sdr_urb_complete(struct urb *urb) { - struct rtl2832_sdr_state *s = urb->context; + struct rtl2832_sdr_dev *dev = urb->context; + struct platform_device *pdev = dev->pdev; struct rtl2832_sdr_frame_buf *fbuf; - dev_dbg_ratelimited(&s->udev->dev, - "status=%d length=%d/%d errors=%d\n", - urb->status, urb->actual_length, - urb->transfer_buffer_length, urb->error_count); + dev_dbg_ratelimited(&pdev->dev, "status=%d length=%d/%d errors=%d\n", + urb->status, urb->actual_length, + urb->transfer_buffer_length, urb->error_count); switch (urb->status) { case 0: /* success */ @@ -409,8 +281,7 @@ static void rtl2832_sdr_urb_complete(struct urb *urb) case -ESHUTDOWN: return; default: /* error */ - dev_err_ratelimited(&s->udev->dev, "urb failed=%d\n", - urb->status); + dev_err_ratelimited(&pdev->dev, "urb failed=%d\n", urb->status); break; } @@ -418,204 +289,192 @@ static void rtl2832_sdr_urb_complete(struct urb *urb) void *ptr; unsigned int len; /* get free framebuffer */ - fbuf = rtl2832_sdr_get_next_fill_buf(s); + fbuf = rtl2832_sdr_get_next_fill_buf(dev); if (unlikely(fbuf == NULL)) { - s->vb_full++; - dev_notice_ratelimited(&s->udev->dev, - "videobuf is full, %d packets dropped\n", - s->vb_full); + dev->vb_full++; + dev_notice_ratelimited(&pdev->dev, + "videobuf is full, %d packets dropped\n", + dev->vb_full); goto skip; } /* fill framebuffer */ ptr = vb2_plane_vaddr(&fbuf->vb, 0); - len = rtl2832_sdr_convert_stream(s, ptr, urb->transfer_buffer, + len = rtl2832_sdr_convert_stream(dev, ptr, urb->transfer_buffer, urb->actual_length); vb2_set_plane_payload(&fbuf->vb, 0, len); v4l2_get_timestamp(&fbuf->vb.v4l2_buf.timestamp); - fbuf->vb.v4l2_buf.sequence = s->sequence++; + fbuf->vb.v4l2_buf.sequence = dev->sequence++; vb2_buffer_done(&fbuf->vb, VB2_BUF_STATE_DONE); } skip: usb_submit_urb(urb, GFP_ATOMIC); } -static int rtl2832_sdr_kill_urbs(struct rtl2832_sdr_state *s) +static int rtl2832_sdr_kill_urbs(struct rtl2832_sdr_dev *dev) { + struct platform_device *pdev = dev->pdev; int i; - for (i = s->urbs_submitted - 1; i >= 0; i--) { - dev_dbg(&s->udev->dev, "kill urb=%d\n", i); + for (i = dev->urbs_submitted - 1; i >= 0; i--) { + dev_dbg(&pdev->dev, "kill urb=%d\n", i); /* stop the URB */ - usb_kill_urb(s->urb_list[i]); + usb_kill_urb(dev->urb_list[i]); } - s->urbs_submitted = 0; + dev->urbs_submitted = 0; return 0; } -static int rtl2832_sdr_submit_urbs(struct rtl2832_sdr_state *s) +static int rtl2832_sdr_submit_urbs(struct rtl2832_sdr_dev *dev) { + struct platform_device *pdev = dev->pdev; int i, ret; - for (i = 0; i < s->urbs_initialized; i++) { - dev_dbg(&s->udev->dev, "submit urb=%d\n", i); - ret = usb_submit_urb(s->urb_list[i], GFP_ATOMIC); + for (i = 0; i < dev->urbs_initialized; i++) { + dev_dbg(&pdev->dev, "submit urb=%d\n", i); + ret = usb_submit_urb(dev->urb_list[i], GFP_ATOMIC); if (ret) { - dev_err(&s->udev->dev, - "Could not submit urb no. %d - get them all back\n", - i); - rtl2832_sdr_kill_urbs(s); + dev_err(&pdev->dev, + "Could not submit urb no. %d - get them all back\n", + i); + rtl2832_sdr_kill_urbs(dev); return ret; } - s->urbs_submitted++; + dev->urbs_submitted++; } return 0; } -static int rtl2832_sdr_free_stream_bufs(struct rtl2832_sdr_state *s) +static int rtl2832_sdr_free_stream_bufs(struct rtl2832_sdr_dev *dev) { - if (s->flags & USB_STATE_URB_BUF) { - while (s->buf_num) { - s->buf_num--; - dev_dbg(&s->udev->dev, "free buf=%d\n", s->buf_num); - usb_free_coherent(s->udev, s->buf_size, - s->buf_list[s->buf_num], - s->dma_addr[s->buf_num]); + struct platform_device *pdev = dev->pdev; + + if (dev->flags & USB_STATE_URB_BUF) { + while (dev->buf_num) { + dev->buf_num--; + dev_dbg(&pdev->dev, "free buf=%d\n", dev->buf_num); + usb_free_coherent(dev->udev, dev->buf_size, + dev->buf_list[dev->buf_num], + dev->dma_addr[dev->buf_num]); } } - s->flags &= ~USB_STATE_URB_BUF; + dev->flags &= ~USB_STATE_URB_BUF; return 0; } -static int rtl2832_sdr_alloc_stream_bufs(struct rtl2832_sdr_state *s) +static int rtl2832_sdr_alloc_stream_bufs(struct rtl2832_sdr_dev *dev) { - s->buf_num = 0; - s->buf_size = BULK_BUFFER_SIZE; + struct platform_device *pdev = dev->pdev; - dev_dbg(&s->udev->dev, "all in all I will use %u bytes for streaming\n", - MAX_BULK_BUFS * BULK_BUFFER_SIZE); + dev->buf_num = 0; + dev->buf_size = BULK_BUFFER_SIZE; - for (s->buf_num = 0; s->buf_num < MAX_BULK_BUFS; s->buf_num++) { - s->buf_list[s->buf_num] = usb_alloc_coherent(s->udev, + dev_dbg(&pdev->dev, "all in all I will use %u bytes for streaming\n", + MAX_BULK_BUFS * BULK_BUFFER_SIZE); + + for (dev->buf_num = 0; dev->buf_num < MAX_BULK_BUFS; dev->buf_num++) { + dev->buf_list[dev->buf_num] = usb_alloc_coherent(dev->udev, BULK_BUFFER_SIZE, GFP_ATOMIC, - &s->dma_addr[s->buf_num]); - if (!s->buf_list[s->buf_num]) { - dev_dbg(&s->udev->dev, "alloc buf=%d failed\n", - s->buf_num); - rtl2832_sdr_free_stream_bufs(s); + &dev->dma_addr[dev->buf_num]); + if (!dev->buf_list[dev->buf_num]) { + dev_dbg(&pdev->dev, "alloc buf=%d failed\n", + dev->buf_num); + rtl2832_sdr_free_stream_bufs(dev); return -ENOMEM; } - dev_dbg(&s->udev->dev, "alloc buf=%d %p (dma %llu)\n", - s->buf_num, s->buf_list[s->buf_num], - (long long)s->dma_addr[s->buf_num]); - s->flags |= USB_STATE_URB_BUF; + dev_dbg(&pdev->dev, "alloc buf=%d %p (dma %llu)\n", + dev->buf_num, dev->buf_list[dev->buf_num], + (long long)dev->dma_addr[dev->buf_num]); + dev->flags |= USB_STATE_URB_BUF; } return 0; } -static int rtl2832_sdr_free_urbs(struct rtl2832_sdr_state *s) +static int rtl2832_sdr_free_urbs(struct rtl2832_sdr_dev *dev) { + struct platform_device *pdev = dev->pdev; int i; - rtl2832_sdr_kill_urbs(s); + rtl2832_sdr_kill_urbs(dev); - for (i = s->urbs_initialized - 1; i >= 0; i--) { - if (s->urb_list[i]) { - dev_dbg(&s->udev->dev, "free urb=%d\n", i); + for (i = dev->urbs_initialized - 1; i >= 0; i--) { + if (dev->urb_list[i]) { + dev_dbg(&pdev->dev, "free urb=%d\n", i); /* free the URBs */ - usb_free_urb(s->urb_list[i]); + usb_free_urb(dev->urb_list[i]); } } - s->urbs_initialized = 0; + dev->urbs_initialized = 0; return 0; } -static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_state *s) +static int rtl2832_sdr_alloc_urbs(struct rtl2832_sdr_dev *dev) { + struct platform_device *pdev = dev->pdev; int i, j; /* allocate the URBs */ for (i = 0; i < MAX_BULK_BUFS; i++) { - dev_dbg(&s->udev->dev, "alloc urb=%d\n", i); - s->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC); - if (!s->urb_list[i]) { - dev_dbg(&s->udev->dev, "failed\n"); + dev_dbg(&pdev->dev, "alloc urb=%d\n", i); + dev->urb_list[i] = usb_alloc_urb(0, GFP_ATOMIC); + if (!dev->urb_list[i]) { + dev_dbg(&pdev->dev, "failed\n"); for (j = 0; j < i; j++) - usb_free_urb(s->urb_list[j]); + usb_free_urb(dev->urb_list[j]); return -ENOMEM; } - usb_fill_bulk_urb(s->urb_list[i], - s->udev, - usb_rcvbulkpipe(s->udev, 0x81), - s->buf_list[i], + usb_fill_bulk_urb(dev->urb_list[i], + dev->udev, + usb_rcvbulkpipe(dev->udev, 0x81), + dev->buf_list[i], BULK_BUFFER_SIZE, - rtl2832_sdr_urb_complete, s); + rtl2832_sdr_urb_complete, dev); - s->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; - s->urb_list[i]->transfer_dma = s->dma_addr[i]; - s->urbs_initialized++; + dev->urb_list[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + dev->urb_list[i]->transfer_dma = dev->dma_addr[i]; + dev->urbs_initialized++; } return 0; } /* Must be called with vb_queue_lock hold */ -static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_state *s) +static void rtl2832_sdr_cleanup_queued_bufs(struct rtl2832_sdr_dev *dev) { + struct platform_device *pdev = dev->pdev; unsigned long flags; - dev_dbg(&s->udev->dev, "\n"); + dev_dbg(&pdev->dev, "\n"); - spin_lock_irqsave(&s->queued_bufs_lock, flags); - while (!list_empty(&s->queued_bufs)) { + spin_lock_irqsave(&dev->queued_bufs_lock, flags); + while (!list_empty(&dev->queued_bufs)) { struct rtl2832_sdr_frame_buf *buf; - buf = list_entry(s->queued_bufs.next, + buf = list_entry(dev->queued_bufs.next, struct rtl2832_sdr_frame_buf, list); list_del(&buf->list); vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); } - spin_unlock_irqrestore(&s->queued_bufs_lock, flags); -} - -/* The user yanked out the cable... */ -static void rtl2832_sdr_release_sec(struct dvb_frontend *fe) -{ - struct rtl2832_sdr_state *s = fe->sec_priv; - - dev_dbg(&s->udev->dev, "\n"); - - mutex_lock(&s->vb_queue_lock); - mutex_lock(&s->v4l2_lock); - /* No need to keep the urbs around after disconnection */ - s->udev = NULL; - - v4l2_device_disconnect(&s->v4l2_dev); - video_unregister_device(&s->vdev); - mutex_unlock(&s->v4l2_lock); - mutex_unlock(&s->vb_queue_lock); - - v4l2_device_put(&s->v4l2_dev); - - fe->sec_priv = NULL; + spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); } static int rtl2832_sdr_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { - struct rtl2832_sdr_state *s = video_drvdata(file); + struct rtl2832_sdr_dev *dev = video_drvdata(file); + struct platform_device *pdev = dev->pdev; - dev_dbg(&s->udev->dev, "\n"); + dev_dbg(&pdev->dev, "\n"); strlcpy(cap->driver, KBUILD_MODNAME, sizeof(cap->driver)); - strlcpy(cap->card, s->vdev.name, sizeof(cap->card)); - usb_make_path(s->udev, cap->bus_info, sizeof(cap->bus_info)); + strlcpy(cap->card, dev->vdev.name, sizeof(cap->card)); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); cap->device_caps = V4L2_CAP_SDR_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | V4L2_CAP_TUNER; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; @@ -627,26 +486,26 @@ static int rtl2832_sdr_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { - struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq); + struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vq); + struct platform_device *pdev = dev->pdev; - dev_dbg(&s->udev->dev, "nbuffers=%d\n", *nbuffers); + dev_dbg(&pdev->dev, "nbuffers=%d\n", *nbuffers); /* Need at least 8 buffers */ if (vq->num_buffers + *nbuffers < 8) *nbuffers = 8 - vq->num_buffers; *nplanes = 1; - sizes[0] = PAGE_ALIGN(s->buffersize); - dev_dbg(&s->udev->dev, "nbuffers=%d sizes[0]=%d\n", - *nbuffers, sizes[0]); + sizes[0] = PAGE_ALIGN(dev->buffersize); + dev_dbg(&pdev->dev, "nbuffers=%d sizes[0]=%d\n", *nbuffers, sizes[0]); return 0; } static int rtl2832_sdr_buf_prepare(struct vb2_buffer *vb) { - struct rtl2832_sdr_state *s = vb2_get_drv_priv(vb->vb2_queue); + struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vb->vb2_queue); /* Don't allow queing new buffers after device disconnection */ - if (!s->udev) + if (!dev->udev) return -ENODEV; return 0; @@ -654,46 +513,48 @@ static int rtl2832_sdr_buf_prepare(struct vb2_buffer *vb) static void rtl2832_sdr_buf_queue(struct vb2_buffer *vb) { - struct rtl2832_sdr_state *s = vb2_get_drv_priv(vb->vb2_queue); + struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vb->vb2_queue); struct rtl2832_sdr_frame_buf *buf = container_of(vb, struct rtl2832_sdr_frame_buf, vb); unsigned long flags; /* Check the device has not disconnected between prep and queuing */ - if (!s->udev) { + if (!dev->udev) { vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); return; } - spin_lock_irqsave(&s->queued_bufs_lock, flags); - list_add_tail(&buf->list, &s->queued_bufs); - spin_unlock_irqrestore(&s->queued_bufs_lock, flags); + spin_lock_irqsave(&dev->queued_bufs_lock, flags); + list_add_tail(&buf->list, &dev->queued_bufs); + spin_unlock_irqrestore(&dev->queued_bufs_lock, flags); } -static int rtl2832_sdr_set_adc(struct rtl2832_sdr_state *s) +static int rtl2832_sdr_set_adc(struct rtl2832_sdr_dev *dev) { - struct dvb_frontend *fe = s->fe; + struct platform_device *pdev = dev->pdev; + struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; + struct dvb_frontend *fe = pdata->dvb_frontend; int ret; unsigned int f_sr, f_if; u8 buf[4], u8tmp1, u8tmp2; u64 u64tmp; u32 u32tmp; - dev_dbg(&s->udev->dev, "f_adc=%u\n", s->f_adc); + dev_dbg(&pdev->dev, "f_adc=%u\n", dev->f_adc); - if (!test_bit(POWER_ON, &s->flags)) + if (!test_bit(POWER_ON, &dev->flags)) return 0; - if (s->f_adc == 0) + if (dev->f_adc == 0) return 0; - f_sr = s->f_adc; + f_sr = dev->f_adc; - ret = rtl2832_sdr_wr_regs(s, 0x13e, "\x00\x00", 2); + ret = rtl2832_sdr_wr_regs(dev, 0x13e, "\x00\x00", 2); if (ret) goto err; - ret = rtl2832_sdr_wr_regs(s, 0x115, "\x00\x00\x00\x00", 4); + ret = rtl2832_sdr_wr_regs(dev, 0x115, "\x00\x00\x00\x00", 4); if (ret) goto err; @@ -707,19 +568,19 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_state *s) goto err; /* program IF */ - u64tmp = f_if % s->cfg->xtal; + u64tmp = f_if % pdata->clk; u64tmp *= 0x400000; - u64tmp = div_u64(u64tmp, s->cfg->xtal); + u64tmp = div_u64(u64tmp, pdata->clk); u64tmp = -u64tmp; u32tmp = u64tmp & 0x3fffff; - dev_dbg(&s->udev->dev, "f_if=%u if_ctl=%08x\n", f_if, u32tmp); + dev_dbg(&pdev->dev, "f_if=%u if_ctl=%08x\n", f_if, u32tmp); buf[0] = (u32tmp >> 16) & 0xff; buf[1] = (u32tmp >> 8) & 0xff; buf[2] = (u32tmp >> 0) & 0xff; - ret = rtl2832_sdr_wr_regs(s, 0x119, buf, 3); + ret = rtl2832_sdr_wr_regs(dev, 0x119, buf, 3); if (ret) goto err; @@ -733,208 +594,212 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_state *s) u8tmp2 = 0xcd; /* enable ADC I, ADC Q */ } - ret = rtl2832_sdr_wr_reg(s, 0x1b1, u8tmp1); + ret = rtl2832_sdr_wr_reg(dev, 0x1b1, u8tmp1); if (ret) goto err; - ret = rtl2832_sdr_wr_reg(s, 0x008, u8tmp2); + ret = rtl2832_sdr_wr_reg(dev, 0x008, u8tmp2); if (ret) goto err; - ret = rtl2832_sdr_wr_reg(s, 0x006, 0x80); + ret = rtl2832_sdr_wr_reg(dev, 0x006, 0x80); if (ret) goto err; /* program sampling rate (resampling down) */ - u32tmp = div_u64(s->cfg->xtal * 0x400000ULL, f_sr * 4U); + u32tmp = div_u64(pdata->clk * 0x400000ULL, f_sr * 4U); u32tmp <<= 2; buf[0] = (u32tmp >> 24) & 0xff; buf[1] = (u32tmp >> 16) & 0xff; buf[2] = (u32tmp >> 8) & 0xff; buf[3] = (u32tmp >> 0) & 0xff; - ret = rtl2832_sdr_wr_regs(s, 0x19f, buf, 4); + ret = rtl2832_sdr_wr_regs(dev, 0x19f, buf, 4); if (ret) goto err; /* low-pass filter */ - ret = rtl2832_sdr_wr_regs(s, 0x11c, + ret = rtl2832_sdr_wr_regs(dev, 0x11c, "\xca\xdc\xd7\xd8\xe0\xf2\x0e\x35\x06\x50\x9c\x0d\x71\x11\x14\x71\x74\x19\x41\xa5", 20); if (ret) goto err; - ret = rtl2832_sdr_wr_regs(s, 0x017, "\x11\x10", 2); + ret = rtl2832_sdr_wr_regs(dev, 0x017, "\x11\x10", 2); if (ret) goto err; /* mode */ - ret = rtl2832_sdr_wr_regs(s, 0x019, "\x05", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x019, "\x05", 1); if (ret) goto err; - ret = rtl2832_sdr_wr_regs(s, 0x01a, "\x1b\x16\x0d\x06\x01\xff", 6); + ret = rtl2832_sdr_wr_regs(dev, 0x01a, "\x1b\x16\x0d\x06\x01\xff", 6); if (ret) goto err; /* FSM */ - ret = rtl2832_sdr_wr_regs(s, 0x192, "\x00\xf0\x0f", 3); + ret = rtl2832_sdr_wr_regs(dev, 0x192, "\x00\xf0\x0f", 3); if (ret) goto err; /* PID filter */ - ret = rtl2832_sdr_wr_regs(s, 0x061, "\x60", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x061, "\x60", 1); if (ret) goto err; /* used RF tuner based settings */ - switch (s->cfg->tuner) { - case RTL2832_TUNER_E4000: - ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1); - ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1); - ret = rtl2832_sdr_wr_regs(s, 0x103, "\x5a", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x30", 1); - ret = rtl2832_sdr_wr_regs(s, 0x104, "\xd0", 1); - ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x18", 1); - ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1); - ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1); - ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1); - ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1); - ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1); - ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1); - ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1); - ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1); - ret = rtl2832_sdr_wr_regs(s, 0x011, "\xd4", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1e5, "\xf0", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1d9, "\x00", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1db, "\x00", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1dd, "\x14", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1de, "\xec", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1d8, "\x0c", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1e6, "\x02", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1d7, "\x09", 1); - ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x83", 1); - ret = rtl2832_sdr_wr_regs(s, 0x010, "\x49", 1); - ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x87", 1); - ret = rtl2832_sdr_wr_regs(s, 0x00d, "\x85", 1); - ret = rtl2832_sdr_wr_regs(s, 0x013, "\x02", 1); + switch (pdata->tuner) { + case RTL2832_SDR_TUNER_E4000: + ret = rtl2832_sdr_wr_regs(dev, 0x112, "\x5a", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x102, "\x40", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x103, "\x5a", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1c7, "\x30", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x104, "\xd0", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x105, "\xbe", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1c8, "\x18", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x106, "\x35", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1c9, "\x21", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1ca, "\x21", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1cb, "\x00", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x107, "\x40", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1cd, "\x10", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1ce, "\x10", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x108, "\x80", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x109, "\x7f", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x10a, "\x80", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x10b, "\x7f", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x011, "\xd4", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1e5, "\xf0", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1d9, "\x00", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1db, "\x00", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1dd, "\x14", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1de, "\xec", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1d8, "\x0c", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1e6, "\x02", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1d7, "\x09", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x00d, "\x83", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x010, "\x49", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x00d, "\x87", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x00d, "\x85", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x013, "\x02", 1); break; - case RTL2832_TUNER_FC0012: - case RTL2832_TUNER_FC0013: - ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1); - ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1); - ret = rtl2832_sdr_wr_regs(s, 0x103, "\x5a", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x2c", 1); - ret = rtl2832_sdr_wr_regs(s, 0x104, "\xcc", 1); - ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x16", 1); - ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1); - ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1); - ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1); - ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1); - ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1); - ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1); - ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1); - ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1); - ret = rtl2832_sdr_wr_regs(s, 0x011, "\xe9\xbf", 2); - ret = rtl2832_sdr_wr_regs(s, 0x1e5, "\xf0", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1d9, "\x00", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1db, "\x00", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1dd, "\x11", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1de, "\xef", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1d8, "\x0c", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1e6, "\x02", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1d7, "\x09", 1); + case RTL2832_SDR_TUNER_FC0012: + case RTL2832_SDR_TUNER_FC0013: + ret = rtl2832_sdr_wr_regs(dev, 0x112, "\x5a", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x102, "\x40", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x103, "\x5a", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1c7, "\x2c", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x104, "\xcc", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x105, "\xbe", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1c8, "\x16", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x106, "\x35", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1c9, "\x21", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1ca, "\x21", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1cb, "\x00", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x107, "\x40", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1cd, "\x10", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1ce, "\x10", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x108, "\x80", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x109, "\x7f", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x10a, "\x80", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x10b, "\x7f", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x011, "\xe9\xbf", 2); + ret = rtl2832_sdr_wr_regs(dev, 0x1e5, "\xf0", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1d9, "\x00", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1db, "\x00", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1dd, "\x11", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1de, "\xef", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1d8, "\x0c", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1e6, "\x02", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1d7, "\x09", 1); break; - case RTL2832_TUNER_R820T: - ret = rtl2832_sdr_wr_regs(s, 0x112, "\x5a", 1); - ret = rtl2832_sdr_wr_regs(s, 0x102, "\x40", 1); - ret = rtl2832_sdr_wr_regs(s, 0x115, "\x01", 1); - ret = rtl2832_sdr_wr_regs(s, 0x103, "\x80", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1c7, "\x24", 1); - ret = rtl2832_sdr_wr_regs(s, 0x104, "\xcc", 1); - ret = rtl2832_sdr_wr_regs(s, 0x105, "\xbe", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1c8, "\x14", 1); - ret = rtl2832_sdr_wr_regs(s, 0x106, "\x35", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1c9, "\x21", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1ca, "\x21", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1cb, "\x00", 1); - ret = rtl2832_sdr_wr_regs(s, 0x107, "\x40", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1cd, "\x10", 1); - ret = rtl2832_sdr_wr_regs(s, 0x1ce, "\x10", 1); - ret = rtl2832_sdr_wr_regs(s, 0x108, "\x80", 1); - ret = rtl2832_sdr_wr_regs(s, 0x109, "\x7f", 1); - ret = rtl2832_sdr_wr_regs(s, 0x10a, "\x80", 1); - ret = rtl2832_sdr_wr_regs(s, 0x10b, "\x7f", 1); - ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1); - ret = rtl2832_sdr_wr_regs(s, 0x00e, "\xfc", 1); - ret = rtl2832_sdr_wr_regs(s, 0x011, "\xf4", 1); + case RTL2832_SDR_TUNER_R820T: + case RTL2832_SDR_TUNER_R828D: + ret = rtl2832_sdr_wr_regs(dev, 0x112, "\x5a", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x102, "\x40", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x115, "\x01", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x103, "\x80", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1c7, "\x24", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x104, "\xcc", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x105, "\xbe", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1c8, "\x14", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x106, "\x35", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1c9, "\x21", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1ca, "\x21", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1cb, "\x00", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x107, "\x40", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1cd, "\x10", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x1ce, "\x10", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x108, "\x80", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x109, "\x7f", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x10a, "\x80", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x10b, "\x7f", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x011, "\xf4", 1); break; default: - dev_notice(&s->udev->dev, "Unsupported tuner\n"); + dev_notice(&pdev->dev, "Unsupported tuner\n"); } /* software reset */ - ret = rtl2832_sdr_wr_reg_mask(s, 0x101, 0x04, 0x04); + ret = rtl2832_sdr_wr_reg_mask(dev, 0x101, 0x04, 0x04); if (ret) goto err; - ret = rtl2832_sdr_wr_reg_mask(s, 0x101, 0x00, 0x04); + ret = rtl2832_sdr_wr_reg_mask(dev, 0x101, 0x00, 0x04); if (ret) goto err; err: return ret; }; -static void rtl2832_sdr_unset_adc(struct rtl2832_sdr_state *s) +static void rtl2832_sdr_unset_adc(struct rtl2832_sdr_dev *dev) { + struct platform_device *pdev = dev->pdev; int ret; - dev_dbg(&s->udev->dev, "\n"); + dev_dbg(&pdev->dev, "\n"); /* PID filter */ - ret = rtl2832_sdr_wr_regs(s, 0x061, "\xe0", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x061, "\xe0", 1); if (ret) goto err; /* mode */ - ret = rtl2832_sdr_wr_regs(s, 0x019, "\x20", 1); + ret = rtl2832_sdr_wr_regs(dev, 0x019, "\x20", 1); if (ret) goto err; - ret = rtl2832_sdr_wr_regs(s, 0x017, "\x11\x10", 2); + ret = rtl2832_sdr_wr_regs(dev, 0x017, "\x11\x10", 2); if (ret) goto err; /* FSM */ - ret = rtl2832_sdr_wr_regs(s, 0x192, "\x00\x0f\xff", 3); + ret = rtl2832_sdr_wr_regs(dev, 0x192, "\x00\x0f\xff", 3); if (ret) goto err; - ret = rtl2832_sdr_wr_regs(s, 0x13e, "\x40\x00", 2); + ret = rtl2832_sdr_wr_regs(dev, 0x13e, "\x40\x00", 2); if (ret) goto err; - ret = rtl2832_sdr_wr_regs(s, 0x115, "\x06\x3f\xce\xcc", 4); + ret = rtl2832_sdr_wr_regs(dev, 0x115, "\x06\x3f\xce\xcc", 4); if (ret) goto err; err: return; }; -static int rtl2832_sdr_set_tuner_freq(struct rtl2832_sdr_state *s) +static int rtl2832_sdr_set_tuner_freq(struct rtl2832_sdr_dev *dev) { - struct dvb_frontend *fe = s->fe; + struct platform_device *pdev = dev->pdev; + struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; + struct dvb_frontend *fe = pdata->dvb_frontend; struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct v4l2_ctrl *bandwidth_auto; struct v4l2_ctrl *bandwidth; @@ -942,29 +807,29 @@ static int rtl2832_sdr_set_tuner_freq(struct rtl2832_sdr_state *s) /* * tuner RF (Hz) */ - if (s->f_tuner == 0) + if (dev->f_tuner == 0) return 0; /* * bandwidth (Hz) */ - bandwidth_auto = v4l2_ctrl_find(&s->hdl, + bandwidth_auto = v4l2_ctrl_find(&dev->hdl, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO); - bandwidth = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH); + bandwidth = v4l2_ctrl_find(&dev->hdl, V4L2_CID_RF_TUNER_BANDWIDTH); if (v4l2_ctrl_g_ctrl(bandwidth_auto)) { - c->bandwidth_hz = s->f_adc; - v4l2_ctrl_s_ctrl(bandwidth, s->f_adc); + c->bandwidth_hz = dev->f_adc; + v4l2_ctrl_s_ctrl(bandwidth, dev->f_adc); } else { c->bandwidth_hz = v4l2_ctrl_g_ctrl(bandwidth); } - c->frequency = s->f_tuner; + c->frequency = dev->f_tuner; c->delivery_system = SYS_DVBT; - dev_dbg(&s->udev->dev, "frequency=%u bandwidth=%d\n", - c->frequency, c->bandwidth_hz); + dev_dbg(&pdev->dev, "frequency=%u bandwidth=%d\n", + c->frequency, c->bandwidth_hz); - if (!test_bit(POWER_ON, &s->flags)) + if (!test_bit(POWER_ON, &dev->flags)) return 0; if (fe->ops.tuner_ops.set_params) @@ -973,11 +838,13 @@ static int rtl2832_sdr_set_tuner_freq(struct rtl2832_sdr_state *s) return 0; }; -static int rtl2832_sdr_set_tuner(struct rtl2832_sdr_state *s) +static int rtl2832_sdr_set_tuner(struct rtl2832_sdr_dev *dev) { - struct dvb_frontend *fe = s->fe; + struct platform_device *pdev = dev->pdev; + struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; + struct dvb_frontend *fe = pdata->dvb_frontend; - dev_dbg(&s->udev->dev, "\n"); + dev_dbg(&pdev->dev, "\n"); if (fe->ops.tuner_ops.init) fe->ops.tuner_ops.init(fe); @@ -985,11 +852,13 @@ static int rtl2832_sdr_set_tuner(struct rtl2832_sdr_state *s) return 0; }; -static void rtl2832_sdr_unset_tuner(struct rtl2832_sdr_state *s) +static void rtl2832_sdr_unset_tuner(struct rtl2832_sdr_dev *dev) { - struct dvb_frontend *fe = s->fe; + struct platform_device *pdev = dev->pdev; + struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; + struct dvb_frontend *fe = pdata->dvb_frontend; - dev_dbg(&s->udev->dev, "\n"); + dev_dbg(&pdev->dev, "\n"); if (fe->ops.tuner_ops.sleep) fe->ops.tuner_ops.sleep(fe); @@ -999,83 +868,89 @@ static void rtl2832_sdr_unset_tuner(struct rtl2832_sdr_state *s) static int rtl2832_sdr_start_streaming(struct vb2_queue *vq, unsigned int count) { - struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq); + struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vq); + struct platform_device *pdev = dev->pdev; + struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; + struct dvb_usb_device *d = pdata->dvb_usb_device; int ret; - dev_dbg(&s->udev->dev, "\n"); + dev_dbg(&pdev->dev, "\n"); - if (!s->udev) + if (!dev->udev) return -ENODEV; - if (mutex_lock_interruptible(&s->v4l2_lock)) + if (mutex_lock_interruptible(&dev->v4l2_lock)) return -ERESTARTSYS; - if (s->d->props->power_ctrl) - s->d->props->power_ctrl(s->d, 1); + if (d->props->power_ctrl) + d->props->power_ctrl(d, 1); /* enable ADC */ - if (s->d->props->frontend_ctrl) - s->d->props->frontend_ctrl(s->fe, 1); + if (d->props->frontend_ctrl) + d->props->frontend_ctrl(pdata->dvb_frontend, 1); - set_bit(POWER_ON, &s->flags); + set_bit(POWER_ON, &dev->flags); - ret = rtl2832_sdr_set_tuner(s); + ret = rtl2832_sdr_set_tuner(dev); if (ret) goto err; - ret = rtl2832_sdr_set_tuner_freq(s); + ret = rtl2832_sdr_set_tuner_freq(dev); if (ret) goto err; - ret = rtl2832_sdr_set_adc(s); + ret = rtl2832_sdr_set_adc(dev); if (ret) goto err; - ret = rtl2832_sdr_alloc_stream_bufs(s); + ret = rtl2832_sdr_alloc_stream_bufs(dev); if (ret) goto err; - ret = rtl2832_sdr_alloc_urbs(s); + ret = rtl2832_sdr_alloc_urbs(dev); if (ret) goto err; - s->sequence = 0; + dev->sequence = 0; - ret = rtl2832_sdr_submit_urbs(s); + ret = rtl2832_sdr_submit_urbs(dev); if (ret) goto err; err: - mutex_unlock(&s->v4l2_lock); + mutex_unlock(&dev->v4l2_lock); return ret; } static void rtl2832_sdr_stop_streaming(struct vb2_queue *vq) { - struct rtl2832_sdr_state *s = vb2_get_drv_priv(vq); + struct rtl2832_sdr_dev *dev = vb2_get_drv_priv(vq); + struct platform_device *pdev = dev->pdev; + struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; + struct dvb_usb_device *d = pdata->dvb_usb_device; - dev_dbg(&s->udev->dev, "\n"); + dev_dbg(&pdev->dev, "\n"); - mutex_lock(&s->v4l2_lock); + mutex_lock(&dev->v4l2_lock); - rtl2832_sdr_kill_urbs(s); - rtl2832_sdr_free_urbs(s); - rtl2832_sdr_free_stream_bufs(s); - rtl2832_sdr_cleanup_queued_bufs(s); - rtl2832_sdr_unset_adc(s); - rtl2832_sdr_unset_tuner(s); + rtl2832_sdr_kill_urbs(dev); + rtl2832_sdr_free_urbs(dev); + rtl2832_sdr_free_stream_bufs(dev); + rtl2832_sdr_cleanup_queued_bufs(dev); + rtl2832_sdr_unset_adc(dev); + rtl2832_sdr_unset_tuner(dev); - clear_bit(POWER_ON, &s->flags); + clear_bit(POWER_ON, &dev->flags); /* disable ADC */ - if (s->d->props->frontend_ctrl) - s->d->props->frontend_ctrl(s->fe, 0); + if (d->props->frontend_ctrl) + d->props->frontend_ctrl(pdata->dvb_frontend, 0); - if (s->d->props->power_ctrl) - s->d->props->power_ctrl(s->d, 0); + if (d->props->power_ctrl) + d->props->power_ctrl(d, 0); - mutex_unlock(&s->v4l2_lock); + mutex_unlock(&dev->v4l2_lock); } static struct vb2_ops rtl2832_sdr_vb2_ops = { @@ -1091,9 +966,10 @@ static struct vb2_ops rtl2832_sdr_vb2_ops = { static int rtl2832_sdr_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - struct rtl2832_sdr_state *s = video_drvdata(file); + struct rtl2832_sdr_dev *dev = video_drvdata(file); + struct platform_device *pdev = dev->pdev; - dev_dbg(&s->udev->dev, "index=%d type=%d\n", v->index, v->type); + dev_dbg(&pdev->dev, "index=%d type=%d\n", v->index, v->type); if (v->index == 0) { strlcpy(v->name, "ADC: Realtek RTL2832", sizeof(v->name)); @@ -1117,9 +993,10 @@ static int rtl2832_sdr_g_tuner(struct file *file, void *priv, static int rtl2832_sdr_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *v) { - struct rtl2832_sdr_state *s = video_drvdata(file); + struct rtl2832_sdr_dev *dev = video_drvdata(file); + struct platform_device *pdev = dev->pdev; - dev_dbg(&s->udev->dev, "\n"); + dev_dbg(&pdev->dev, "\n"); if (v->index > 1) return -EINVAL; @@ -1129,10 +1006,11 @@ static int rtl2832_sdr_s_tuner(struct file *file, void *priv, static int rtl2832_sdr_enum_freq_bands(struct file *file, void *priv, struct v4l2_frequency_band *band) { - struct rtl2832_sdr_state *s = video_drvdata(file); + struct rtl2832_sdr_dev *dev = video_drvdata(file); + struct platform_device *pdev = dev->pdev; - dev_dbg(&s->udev->dev, "tuner=%d type=%d index=%d\n", - band->tuner, band->type, band->index); + dev_dbg(&pdev->dev, "tuner=%d type=%d index=%d\n", + band->tuner, band->type, band->index); if (band->tuner == 0) { if (band->index >= ARRAY_SIZE(bands_adc)) @@ -1154,17 +1032,17 @@ static int rtl2832_sdr_enum_freq_bands(struct file *file, void *priv, static int rtl2832_sdr_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct rtl2832_sdr_state *s = video_drvdata(file); + struct rtl2832_sdr_dev *dev = video_drvdata(file); + struct platform_device *pdev = dev->pdev; int ret = 0; - dev_dbg(&s->udev->dev, "tuner=%d type=%d\n", - f->tuner, f->type); + dev_dbg(&pdev->dev, "tuner=%d type=%d\n", f->tuner, f->type); if (f->tuner == 0) { - f->frequency = s->f_adc; + f->frequency = dev->f_adc; f->type = V4L2_TUNER_ADC; } else if (f->tuner == 1) { - f->frequency = s->f_tuner; + f->frequency = dev->f_tuner; f->type = V4L2_TUNER_RF; } else { return -EINVAL; @@ -1176,11 +1054,12 @@ static int rtl2832_sdr_g_frequency(struct file *file, void *priv, static int rtl2832_sdr_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *f) { - struct rtl2832_sdr_state *s = video_drvdata(file); + struct rtl2832_sdr_dev *dev = video_drvdata(file); + struct platform_device *pdev = dev->pdev; int ret, band; - dev_dbg(&s->udev->dev, "tuner=%d type=%d frequency=%u\n", - f->tuner, f->type, f->frequency); + dev_dbg(&pdev->dev, "tuner=%d type=%d frequency=%u\n", + f->tuner, f->type, f->frequency); /* ADC band midpoints */ #define BAND_ADC_0 ((bands_adc[0].rangehigh + bands_adc[1].rangelow) / 2) @@ -1194,19 +1073,19 @@ static int rtl2832_sdr_s_frequency(struct file *file, void *priv, else band = 2; - s->f_adc = clamp_t(unsigned int, f->frequency, + dev->f_adc = clamp_t(unsigned int, f->frequency, bands_adc[band].rangelow, bands_adc[band].rangehigh); - dev_dbg(&s->udev->dev, "ADC frequency=%u Hz\n", s->f_adc); - ret = rtl2832_sdr_set_adc(s); + dev_dbg(&pdev->dev, "ADC frequency=%u Hz\n", dev->f_adc); + ret = rtl2832_sdr_set_adc(dev); } else if (f->tuner == 1) { - s->f_tuner = clamp_t(unsigned int, f->frequency, + dev->f_tuner = clamp_t(unsigned int, f->frequency, bands_fm[0].rangelow, bands_fm[0].rangehigh); - dev_dbg(&s->udev->dev, "RF frequency=%u Hz\n", f->frequency); + dev_dbg(&pdev->dev, "RF frequency=%u Hz\n", f->frequency); - ret = rtl2832_sdr_set_tuner_freq(s); + ret = rtl2832_sdr_set_tuner_freq(dev); } else { ret = -EINVAL; } @@ -1217,11 +1096,12 @@ static int rtl2832_sdr_s_frequency(struct file *file, void *priv, static int rtl2832_sdr_enum_fmt_sdr_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { - struct rtl2832_sdr_state *s = video_drvdata(file); + struct rtl2832_sdr_dev *dev = video_drvdata(file); + struct platform_device *pdev = dev->pdev; - dev_dbg(&s->udev->dev, "\n"); + dev_dbg(&pdev->dev, "\n"); - if (f->index >= s->num_formats) + if (f->index >= dev->num_formats) return -EINVAL; strlcpy(f->description, formats[f->index].name, sizeof(f->description)); @@ -1233,12 +1113,13 @@ static int rtl2832_sdr_enum_fmt_sdr_cap(struct file *file, void *priv, static int rtl2832_sdr_g_fmt_sdr_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct rtl2832_sdr_state *s = video_drvdata(file); + struct rtl2832_sdr_dev *dev = video_drvdata(file); + struct platform_device *pdev = dev->pdev; - dev_dbg(&s->udev->dev, "\n"); + dev_dbg(&pdev->dev, "\n"); - f->fmt.sdr.pixelformat = s->pixelformat; - f->fmt.sdr.buffersize = s->buffersize; + f->fmt.sdr.pixelformat = dev->pixelformat; + f->fmt.sdr.buffersize = dev->buffersize; memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); @@ -1248,28 +1129,29 @@ static int rtl2832_sdr_g_fmt_sdr_cap(struct file *file, void *priv, static int rtl2832_sdr_s_fmt_sdr_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct rtl2832_sdr_state *s = video_drvdata(file); - struct vb2_queue *q = &s->vb_queue; + struct rtl2832_sdr_dev *dev = video_drvdata(file); + struct platform_device *pdev = dev->pdev; + struct vb2_queue *q = &dev->vb_queue; int i; - dev_dbg(&s->udev->dev, "pixelformat fourcc %4.4s\n", - (char *)&f->fmt.sdr.pixelformat); + dev_dbg(&pdev->dev, "pixelformat fourcc %4.4s\n", + (char *)&f->fmt.sdr.pixelformat); if (vb2_is_busy(q)) return -EBUSY; memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); - for (i = 0; i < s->num_formats; i++) { + for (i = 0; i < dev->num_formats; i++) { if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { - s->pixelformat = formats[i].pixelformat; - s->buffersize = formats[i].buffersize; + dev->pixelformat = formats[i].pixelformat; + dev->buffersize = formats[i].buffersize; f->fmt.sdr.buffersize = formats[i].buffersize; return 0; } } - s->pixelformat = formats[0].pixelformat; - s->buffersize = formats[0].buffersize; + dev->pixelformat = formats[0].pixelformat; + dev->buffersize = formats[0].buffersize; f->fmt.sdr.pixelformat = formats[0].pixelformat; f->fmt.sdr.buffersize = formats[0].buffersize; @@ -1279,14 +1161,15 @@ static int rtl2832_sdr_s_fmt_sdr_cap(struct file *file, void *priv, static int rtl2832_sdr_try_fmt_sdr_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct rtl2832_sdr_state *s = video_drvdata(file); + struct rtl2832_sdr_dev *dev = video_drvdata(file); + struct platform_device *pdev = dev->pdev; int i; - dev_dbg(&s->udev->dev, "pixelformat fourcc %4.4s\n", - (char *)&f->fmt.sdr.pixelformat); + dev_dbg(&pdev->dev, "pixelformat fourcc %4.4s\n", + (char *)&f->fmt.sdr.pixelformat); memset(f->fmt.sdr.reserved, 0, sizeof(f->fmt.sdr.reserved)); - for (i = 0; i < s->num_formats; i++) { + for (i = 0; i < dev->num_formats; i++) { if (formats[i].pixelformat == f->fmt.sdr.pixelformat) { f->fmt.sdr.buffersize = formats[i].buffersize; return 0; @@ -1348,37 +1231,38 @@ static struct video_device rtl2832_sdr_template = { static int rtl2832_sdr_s_ctrl(struct v4l2_ctrl *ctrl) { - struct rtl2832_sdr_state *s = - container_of(ctrl->handler, struct rtl2832_sdr_state, + struct rtl2832_sdr_dev *dev = + container_of(ctrl->handler, struct rtl2832_sdr_dev, hdl); - struct dvb_frontend *fe = s->fe; + struct platform_device *pdev = dev->pdev; + struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; + struct dvb_frontend *fe = pdata->dvb_frontend; struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; - dev_dbg(&s->udev->dev, - "id=%d name=%s val=%d min=%lld max=%lld step=%lld\n", - ctrl->id, ctrl->name, ctrl->val, - ctrl->minimum, ctrl->maximum, ctrl->step); + dev_dbg(&pdev->dev, "id=%d name=%s val=%d min=%lld max=%lld step=%lld\n", + ctrl->id, ctrl->name, ctrl->val, ctrl->minimum, ctrl->maximum, + ctrl->step); switch (ctrl->id) { case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: case V4L2_CID_RF_TUNER_BANDWIDTH: /* TODO: these controls should be moved to tuner drivers */ - if (s->bandwidth_auto->val) { + if (dev->bandwidth_auto->val) { /* Round towards the closest legal value */ - s32 val = s->f_adc + div_u64(s->bandwidth->step, 2); + s32 val = dev->f_adc + div_u64(dev->bandwidth->step, 2); u32 offset; - val = clamp_t(s32, val, s->bandwidth->minimum, - s->bandwidth->maximum); - offset = val - s->bandwidth->minimum; - offset = s->bandwidth->step * - div_u64(offset, s->bandwidth->step); - s->bandwidth->val = s->bandwidth->minimum + offset; + val = clamp_t(s32, val, dev->bandwidth->minimum, + dev->bandwidth->maximum); + offset = val - dev->bandwidth->minimum; + offset = dev->bandwidth->step * + div_u64(offset, dev->bandwidth->step); + dev->bandwidth->val = dev->bandwidth->minimum + offset; } - c->bandwidth_hz = s->bandwidth->val; + c->bandwidth_hz = dev->bandwidth->val; - if (!test_bit(POWER_ON, &s->flags)) + if (!test_bit(POWER_ON, &dev->flags)) return 0; if (fe->ops.tuner_ops.set_params) @@ -1399,154 +1283,195 @@ static const struct v4l2_ctrl_ops rtl2832_sdr_ctrl_ops = { static void rtl2832_sdr_video_release(struct v4l2_device *v) { - struct rtl2832_sdr_state *s = - container_of(v, struct rtl2832_sdr_state, v4l2_dev); + struct rtl2832_sdr_dev *dev = + container_of(v, struct rtl2832_sdr_dev, v4l2_dev); + struct platform_device *pdev = dev->pdev; + + dev_dbg(&pdev->dev, "\n"); - v4l2_ctrl_handler_free(&s->hdl); - v4l2_device_unregister(&s->v4l2_dev); - kfree(s); + v4l2_ctrl_handler_free(&dev->hdl); + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev); } -struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, const struct rtl2832_config *cfg, - struct v4l2_subdev *sd) +/* Platform driver interface */ +static int rtl2832_sdr_probe(struct platform_device *pdev) { - int ret; - struct rtl2832_sdr_state *s; + struct rtl2832_sdr_dev *dev; + struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data; const struct v4l2_ctrl_ops *ops = &rtl2832_sdr_ctrl_ops; - struct dvb_usb_device *d = i2c_get_adapdata(i2c); + struct v4l2_subdev *subdev; + int ret; + + dev_dbg(&pdev->dev, "\n"); - s = kzalloc(sizeof(struct rtl2832_sdr_state), GFP_KERNEL); - if (s == NULL) { - dev_err(&d->udev->dev, - "Could not allocate memory for rtl2832_sdr_state\n"); - return NULL; + if (!pdata) { + dev_err(&pdev->dev, "Cannot proceed without platform data\n"); + ret = -EINVAL; + goto err; + } + if (!pdev->dev.parent->driver) { + dev_dbg(&pdev->dev, "No parent device\n"); + ret = -EINVAL; + goto err; + } + /* try to refcount host drv since we are the consumer */ + if (!try_module_get(pdev->dev.parent->driver->owner)) { + dev_err(&pdev->dev, "Refcount fail"); + ret = -EINVAL; + goto err; + } + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + ret = -ENOMEM; + goto err_module_put; } /* setup the state */ - s->fe = fe; - s->d = d; - s->udev = d->udev; - s->i2c = i2c; - s->cfg = cfg; - s->f_adc = bands_adc[0].rangelow; - s->f_tuner = bands_fm[0].rangelow; - s->pixelformat = formats[0].pixelformat; - s->buffersize = formats[0].buffersize; - s->num_formats = NUM_FORMATS; + subdev = pdata->v4l2_subdev; + dev->pdev = pdev; + dev->udev = pdata->dvb_usb_device->udev; + dev->f_adc = bands_adc[0].rangelow; + dev->f_tuner = bands_fm[0].rangelow; + dev->pixelformat = formats[0].pixelformat; + dev->buffersize = formats[0].buffersize; + dev->num_formats = NUM_FORMATS; if (!rtl2832_sdr_emulated_fmt) - s->num_formats -= 1; + dev->num_formats -= 1; - mutex_init(&s->v4l2_lock); - mutex_init(&s->vb_queue_lock); - spin_lock_init(&s->queued_bufs_lock); - INIT_LIST_HEAD(&s->queued_bufs); + mutex_init(&dev->v4l2_lock); + mutex_init(&dev->vb_queue_lock); + spin_lock_init(&dev->queued_bufs_lock); + INIT_LIST_HEAD(&dev->queued_bufs); /* Init videobuf2 queue structure */ - s->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE; - s->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; - s->vb_queue.drv_priv = s; - s->vb_queue.buf_struct_size = sizeof(struct rtl2832_sdr_frame_buf); - s->vb_queue.ops = &rtl2832_sdr_vb2_ops; - s->vb_queue.mem_ops = &vb2_vmalloc_memops; - s->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - ret = vb2_queue_init(&s->vb_queue); + dev->vb_queue.type = V4L2_BUF_TYPE_SDR_CAPTURE; + dev->vb_queue.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; + dev->vb_queue.drv_priv = dev; + dev->vb_queue.buf_struct_size = sizeof(struct rtl2832_sdr_frame_buf); + dev->vb_queue.ops = &rtl2832_sdr_vb2_ops; + dev->vb_queue.mem_ops = &vb2_vmalloc_memops; + dev->vb_queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + ret = vb2_queue_init(&dev->vb_queue); if (ret) { - dev_err(&s->udev->dev, "Could not initialize vb2 queue\n"); - goto err_free_mem; + dev_err(&pdev->dev, "Could not initialize vb2 queue\n"); + goto err_kfree; } /* Register controls */ - switch (s->cfg->tuner) { - case RTL2832_TUNER_E4000: - v4l2_ctrl_handler_init(&s->hdl, 9); - if (sd) - v4l2_ctrl_add_handler(&s->hdl, sd->ctrl_handler, NULL); + switch (pdata->tuner) { + case RTL2832_SDR_TUNER_E4000: + v4l2_ctrl_handler_init(&dev->hdl, 9); + if (subdev) + v4l2_ctrl_add_handler(&dev->hdl, subdev->ctrl_handler, NULL); break; - case RTL2832_TUNER_R820T: - v4l2_ctrl_handler_init(&s->hdl, 2); - s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, - V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, - 0, 1, 1, 1); - s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, - V4L2_CID_RF_TUNER_BANDWIDTH, - 0, 8000000, 100000, 0); - v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false); + case RTL2832_SDR_TUNER_R820T: + case RTL2832_SDR_TUNER_R828D: + v4l2_ctrl_handler_init(&dev->hdl, 2); + dev->bandwidth_auto = v4l2_ctrl_new_std(&dev->hdl, ops, + V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, + 0, 1, 1, 1); + dev->bandwidth = v4l2_ctrl_new_std(&dev->hdl, ops, + V4L2_CID_RF_TUNER_BANDWIDTH, + 0, 8000000, 100000, 0); + v4l2_ctrl_auto_cluster(2, &dev->bandwidth_auto, 0, false); break; - case RTL2832_TUNER_FC0012: - case RTL2832_TUNER_FC0013: - v4l2_ctrl_handler_init(&s->hdl, 2); - s->bandwidth_auto = v4l2_ctrl_new_std(&s->hdl, ops, - V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, - 0, 1, 1, 1); - s->bandwidth = v4l2_ctrl_new_std(&s->hdl, ops, - V4L2_CID_RF_TUNER_BANDWIDTH, - 6000000, 8000000, 1000000, - 6000000); - v4l2_ctrl_auto_cluster(2, &s->bandwidth_auto, 0, false); + case RTL2832_SDR_TUNER_FC0012: + case RTL2832_SDR_TUNER_FC0013: + v4l2_ctrl_handler_init(&dev->hdl, 2); + dev->bandwidth_auto = v4l2_ctrl_new_std(&dev->hdl, ops, + V4L2_CID_RF_TUNER_BANDWIDTH_AUTO, + 0, 1, 1, 1); + dev->bandwidth = v4l2_ctrl_new_std(&dev->hdl, ops, + V4L2_CID_RF_TUNER_BANDWIDTH, + 6000000, 8000000, 1000000, + 6000000); + v4l2_ctrl_auto_cluster(2, &dev->bandwidth_auto, 0, false); break; default: - v4l2_ctrl_handler_init(&s->hdl, 0); - dev_notice(&s->udev->dev, "%s: Unsupported tuner\n", - KBUILD_MODNAME); - goto err_free_controls; + v4l2_ctrl_handler_init(&dev->hdl, 0); + dev_err(&pdev->dev, "Unsupported tuner\n"); + goto err_v4l2_ctrl_handler_free; } - - if (s->hdl.error) { - ret = s->hdl.error; - dev_err(&s->udev->dev, "Could not initialize controls\n"); - goto err_free_controls; + if (dev->hdl.error) { + ret = dev->hdl.error; + dev_err(&pdev->dev, "Could not initialize controls\n"); + goto err_v4l2_ctrl_handler_free; } /* Init video_device structure */ - s->vdev = rtl2832_sdr_template; - s->vdev.queue = &s->vb_queue; - s->vdev.queue->lock = &s->vb_queue_lock; - video_set_drvdata(&s->vdev, s); + dev->vdev = rtl2832_sdr_template; + dev->vdev.queue = &dev->vb_queue; + dev->vdev.queue->lock = &dev->vb_queue_lock; + video_set_drvdata(&dev->vdev, dev); /* Register the v4l2_device structure */ - s->v4l2_dev.release = rtl2832_sdr_video_release; - ret = v4l2_device_register(&s->udev->dev, &s->v4l2_dev); + dev->v4l2_dev.release = rtl2832_sdr_video_release; + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); if (ret) { - dev_err(&s->udev->dev, - "Failed to register v4l2-device (%d)\n", ret); - goto err_free_controls; + dev_err(&pdev->dev, "Failed to register v4l2-device %d\n", ret); + goto err_v4l2_ctrl_handler_free; } - s->v4l2_dev.ctrl_handler = &s->hdl; - s->vdev.v4l2_dev = &s->v4l2_dev; - s->vdev.lock = &s->v4l2_lock; - s->vdev.vfl_dir = VFL_DIR_RX; + dev->v4l2_dev.ctrl_handler = &dev->hdl; + dev->vdev.v4l2_dev = &dev->v4l2_dev; + dev->vdev.lock = &dev->v4l2_lock; + dev->vdev.vfl_dir = VFL_DIR_RX; - ret = video_register_device(&s->vdev, VFL_TYPE_SDR, -1); + ret = video_register_device(&dev->vdev, VFL_TYPE_SDR, -1); if (ret) { - dev_err(&s->udev->dev, - "Failed to register as video device (%d)\n", - ret); - goto err_unregister_v4l2_dev; + dev_err(&pdev->dev, "Failed to register as video device %d\n", + ret); + goto err_v4l2_device_unregister; } - dev_info(&s->udev->dev, "Registered as %s\n", - video_device_node_name(&s->vdev)); - - fe->sec_priv = s; - fe->ops.release_sec = rtl2832_sdr_release_sec; - - dev_info(&s->i2c->dev, "%s: Realtek RTL2832 SDR attached\n", - KBUILD_MODNAME); - dev_notice(&s->udev->dev, - "%s: SDR API is still slightly experimental and functionality changes may follow\n", - KBUILD_MODNAME); - return fe; - -err_unregister_v4l2_dev: - v4l2_device_unregister(&s->v4l2_dev); -err_free_controls: - v4l2_ctrl_handler_free(&s->hdl); -err_free_mem: - kfree(s); - return NULL; + dev_info(&pdev->dev, "Registered as %s\n", + video_device_node_name(&dev->vdev)); + dev_info(&pdev->dev, "Realtek RTL2832 SDR attached\n"); + dev_notice(&pdev->dev, + "SDR API is still slightly experimental and functionality changes may follow\n"); + platform_set_drvdata(pdev, dev); + return 0; +err_v4l2_device_unregister: + v4l2_device_unregister(&dev->v4l2_dev); +err_v4l2_ctrl_handler_free: + v4l2_ctrl_handler_free(&dev->hdl); +err_kfree: + kfree(dev); +err_module_put: + module_put(pdev->dev.parent->driver->owner); +err: + return ret; } -EXPORT_SYMBOL(rtl2832_sdr_attach); + +static int rtl2832_sdr_remove(struct platform_device *pdev) +{ + struct rtl2832_sdr_dev *dev = platform_get_drvdata(pdev); + + dev_dbg(&pdev->dev, "\n"); + + mutex_lock(&dev->vb_queue_lock); + mutex_lock(&dev->v4l2_lock); + /* No need to keep the urbs around after disconnection */ + dev->udev = NULL; + v4l2_device_disconnect(&dev->v4l2_dev); + video_unregister_device(&dev->vdev); + mutex_unlock(&dev->v4l2_lock); + mutex_unlock(&dev->vb_queue_lock); + v4l2_device_put(&dev->v4l2_dev); + module_put(pdev->dev.parent->driver->owner); + + return 0; +} + +static struct platform_driver rtl2832_sdr_driver = { + .driver = { + .name = "rtl2832_sdr", + .owner = THIS_MODULE, + }, + .probe = rtl2832_sdr_probe, + .remove = rtl2832_sdr_remove, +}; +module_platform_driver(rtl2832_sdr_driver); MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); MODULE_DESCRIPTION("Realtek RTL2832 SDR driver"); diff --git a/drivers/media/dvb-frontends/rtl2832_sdr.h b/drivers/media/dvb-frontends/rtl2832_sdr.h index b865fadf184f..d2594768bff2 100644 --- a/drivers/media/dvb-frontends/rtl2832_sdr.h +++ b/drivers/media/dvb-frontends/rtl2832_sdr.h @@ -20,35 +20,48 @@ * GNU Radio plugin "gr-kernel" for device usage will be on: * http://git.linuxtv.org/anttip/gr-kernel.git * - * TODO: - * Help is very highly welcome for these + all the others you could imagine: - * - move controls to V4L2 API - * - use libv4l2 for stream format conversions - * - gr-kernel: switch to v4l2_mmap (current read eats a lot of cpu) - * - SDRSharp support */ #ifndef RTL2832_SDR_H #define RTL2832_SDR_H -#include <linux/kconfig.h> +#include <linux/i2c.h> #include <media/v4l2-subdev.h> +#include "dvb_frontend.h" -/* for config struct */ -#include "rtl2832.h" +/** + * struct rtl2832_sdr_platform_data - Platform data for the rtl2832_sdr driver + * @clk: Clock frequency (4000000, 16000000, 25000000, 28800000). + * @tuner: Used tuner model. + * @i2c_client: rtl2832 demod driver I2C client. + * @bulk_read: rtl2832 driver private I/O interface. + * @bulk_write: rtl2832 driver private I/O interface. + * @update_bits: rtl2832 driver private I/O interface. + * @dvb_frontend: rtl2832 DVB frontend. + * @v4l2_subdev: Tuner v4l2 controls. + * @dvb_usb_device: DVB USB interface for USB streaming. + */ + +struct rtl2832_sdr_platform_data { + u32 clk; + /* + * XXX: This list must be kept sync with dvb_usb_rtl28xxu USB IF driver. + */ +#define RTL2832_SDR_TUNER_TUA9001 0x24 +#define RTL2832_SDR_TUNER_FC0012 0x26 +#define RTL2832_SDR_TUNER_E4000 0x27 +#define RTL2832_SDR_TUNER_FC0013 0x29 +#define RTL2832_SDR_TUNER_R820T 0x2a +#define RTL2832_SDR_TUNER_R828D 0x2b + u8 tuner; -#if IS_ENABLED(CONFIG_DVB_RTL2832_SDR) -extern struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, const struct rtl2832_config *cfg, - struct v4l2_subdev *sd); -#else -static inline struct dvb_frontend *rtl2832_sdr_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c, const struct rtl2832_config *cfg, - struct v4l2_subdev *sd) -{ - dev_warn(&i2c->dev, "%s: driver disabled by Kconfig\n", __func__); - return NULL; -} -#endif + struct i2c_client *i2c_client; + int (*bulk_read)(struct i2c_client *, unsigned int, void *, size_t); + int (*bulk_write)(struct i2c_client *, unsigned int, const void *, size_t); + int (*update_bits)(struct i2c_client *, unsigned int, unsigned int, unsigned int); + struct dvb_frontend *dvb_frontend; + struct v4l2_subdev *v4l2_subdev; + struct dvb_usb_device *dvb_usb_device; +}; #endif /* RTL2832_SDR_H */ diff --git a/drivers/media/dvb-frontends/s5h1409.c b/drivers/media/dvb-frontends/s5h1409.c index f71b06221e14..5ff474a7ff29 100644 --- a/drivers/media/dvb-frontends/s5h1409.c +++ b/drivers/media/dvb-frontends/s5h1409.c @@ -1021,9 +1021,3 @@ static struct dvb_frontend_ops s5h1409_ops = { MODULE_DESCRIPTION("Samsung S5H1409 QAM-B/ATSC Demodulator driver"); MODULE_AUTHOR("Steven Toth"); MODULE_LICENSE("GPL"); - - -/* - * Local variables: - * c-basic-offset: 8 - */ diff --git a/drivers/media/dvb-frontends/s5h1409.h b/drivers/media/dvb-frontends/s5h1409.h index 63b1e0a34e4e..9e143f5c8107 100644 --- a/drivers/media/dvb-frontends/s5h1409.h +++ b/drivers/media/dvb-frontends/s5h1409.h @@ -81,8 +81,3 @@ static inline struct dvb_frontend *s5h1409_attach( #endif /* CONFIG_DVB_S5H1409 */ #endif /* __S5H1409_H__ */ - -/* - * Local variables: - * c-basic-offset: 8 - */ diff --git a/drivers/media/dvb-frontends/s5h1411.c b/drivers/media/dvb-frontends/s5h1411.c index 6cc4b7a9dd60..64f35fed7ae1 100644 --- a/drivers/media/dvb-frontends/s5h1411.c +++ b/drivers/media/dvb-frontends/s5h1411.c @@ -944,8 +944,3 @@ MODULE_PARM_DESC(debug, "Enable verbose debug messages"); MODULE_DESCRIPTION("Samsung S5H1411 QAM-B/ATSC Demodulator driver"); MODULE_AUTHOR("Steven Toth"); MODULE_LICENSE("GPL"); - -/* - * Local variables: - * c-basic-offset: 8 - */ diff --git a/drivers/media/dvb-frontends/s5h1411.h b/drivers/media/dvb-frontends/s5h1411.h index e4f56871f982..1d7deb615674 100644 --- a/drivers/media/dvb-frontends/s5h1411.h +++ b/drivers/media/dvb-frontends/s5h1411.h @@ -83,8 +83,3 @@ static inline struct dvb_frontend *s5h1411_attach( #endif /* CONFIG_DVB_S5H1411 */ #endif /* __S5H1411_H__ */ - -/* - * Local variables: - * c-basic-offset: 8 - */ diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index ce9ab442b4b6..5db588ebfc24 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -19,16 +19,17 @@ static const struct dvb_frontend_ops si2168_ops; /* execute firmware command */ -static int si2168_cmd_execute(struct si2168 *s, struct si2168_cmd *cmd) +static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd) { + struct si2168_dev *dev = i2c_get_clientdata(client); int ret; unsigned long timeout; - mutex_lock(&s->i2c_mutex); + mutex_lock(&dev->i2c_mutex); if (cmd->wlen) { /* write cmd and args for firmware */ - ret = i2c_master_send(s->client, cmd->args, cmd->wlen); + ret = i2c_master_send(client, cmd->args, cmd->wlen); if (ret < 0) { goto err_mutex_unlock; } else if (ret != cmd->wlen) { @@ -39,10 +40,10 @@ static int si2168_cmd_execute(struct si2168 *s, struct si2168_cmd *cmd) if (cmd->rlen) { /* wait cmd execution terminate */ - #define TIMEOUT 50 + #define TIMEOUT 70 timeout = jiffies + msecs_to_jiffies(TIMEOUT); while (!time_after(jiffies, timeout)) { - ret = i2c_master_recv(s->client, cmd->args, cmd->rlen); + ret = i2c_master_recv(client, cmd->args, cmd->rlen); if (ret < 0) { goto err_mutex_unlock; } else if (ret != cmd->rlen) { @@ -55,7 +56,7 @@ static int si2168_cmd_execute(struct si2168 *s, struct si2168_cmd *cmd) break; } - dev_dbg(&s->client->dev, "cmd execution took %d ms\n", + dev_dbg(&client->dev, "cmd execution took %d ms\n", jiffies_to_msecs(jiffies) - (jiffies_to_msecs(timeout) - TIMEOUT)); @@ -65,29 +66,26 @@ static int si2168_cmd_execute(struct si2168 *s, struct si2168_cmd *cmd) } } - ret = 0; + mutex_unlock(&dev->i2c_mutex); + return 0; err_mutex_unlock: - mutex_unlock(&s->i2c_mutex); - if (ret) - goto err; - - return 0; -err: - dev_dbg(&s->client->dev, "failed=%d\n", ret); + mutex_unlock(&dev->i2c_mutex); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int si2168_read_status(struct dvb_frontend *fe, fe_status_t *status) { - struct si2168 *s = fe->demodulator_priv; + struct i2c_client *client = fe->demodulator_priv; + struct si2168_dev *dev = i2c_get_clientdata(client); struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; struct si2168_cmd cmd; *status = 0; - if (!s->active) { + if (!dev->active) { ret = -EAGAIN; goto err; } @@ -113,21 +111,10 @@ static int si2168_read_status(struct dvb_frontend *fe, fe_status_t *status) goto err; } - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; - /* - * Possible values seen, in order from strong signal to weak: - * 16 0001 0110 full lock - * 1e 0001 1110 partial lock - * 1a 0001 1010 partial lock - * 18 0001 1000 no lock - * - * [b3:b1] lock bits - * [b4] statistics ready? Set in a few secs after lock is gained. - */ - switch ((cmd.args[2] >> 1) & 0x03) { case 0x01: *status = FE_HAS_SIGNAL | FE_HAS_CARRIER; @@ -138,7 +125,7 @@ static int si2168_read_status(struct dvb_frontend *fe, fe_status_t *status) break; } - s->fe_status = *status; + dev->fe_status = *status; if (*status & FE_HAS_LOCK) { c->cnr.len = 1; @@ -149,30 +136,31 @@ static int si2168_read_status(struct dvb_frontend *fe, fe_status_t *status) c->cnr.stat[0].scale = FE_SCALE_NOT_AVAILABLE; } - dev_dbg(&s->client->dev, "status=%02x args=%*ph\n", + dev_dbg(&client->dev, "status=%02x args=%*ph\n", *status, cmd.rlen, cmd.args); return 0; err: - dev_dbg(&s->client->dev, "failed=%d\n", ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int si2168_set_frontend(struct dvb_frontend *fe) { - struct si2168 *s = fe->demodulator_priv; + struct i2c_client *client = fe->demodulator_priv; + struct si2168_dev *dev = i2c_get_clientdata(client); struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; struct si2168_cmd cmd; u8 bandwidth, delivery_system; - dev_dbg(&s->client->dev, - "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%u, stream_id=%d\n", - c->delivery_system, c->modulation, - c->frequency, c->bandwidth_hz, c->symbol_rate, - c->inversion, c->stream_id); + dev_dbg(&client->dev, + "delivery_system=%u modulation=%u frequency=%u bandwidth_hz=%u symbol_rate=%u inversion=%u stream_id=%u\n", + c->delivery_system, c->modulation, c->frequency, + c->bandwidth_hz, c->symbol_rate, c->inversion, + c->stream_id); - if (!s->active) { + if (!dev->active) { ret = -EAGAIN; goto err; } @@ -192,7 +180,12 @@ static int si2168_set_frontend(struct dvb_frontend *fe) goto err; } - if (c->bandwidth_hz <= 5000000) + if (c->bandwidth_hz == 0) { + ret = -EINVAL; + goto err; + } else if (c->bandwidth_hz <= 2000000) + bandwidth = 0x02; + else if (c->bandwidth_hz <= 5000000) bandwidth = 0x05; else if (c->bandwidth_hz <= 6000000) bandwidth = 0x06; @@ -217,7 +210,7 @@ static int si2168_set_frontend(struct dvb_frontend *fe) memcpy(cmd.args, "\x88\x02\x02\x02\x02", 5); cmd.wlen = 5; cmd.rlen = 5; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; @@ -230,7 +223,7 @@ static int si2168_set_frontend(struct dvb_frontend *fe) memcpy(cmd.args, "\x89\x21\x06\x11\x89\x20", 6); cmd.wlen = 6; cmd.rlen = 3; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; @@ -241,7 +234,7 @@ static int si2168_set_frontend(struct dvb_frontend *fe) cmd.args[2] = c->stream_id == NO_STREAM_ID_FILTER ? 0 : 1; cmd.wlen = 3; cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; } @@ -249,35 +242,35 @@ static int si2168_set_frontend(struct dvb_frontend *fe) memcpy(cmd.args, "\x51\x03", 2); cmd.wlen = 2; cmd.rlen = 12; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; memcpy(cmd.args, "\x12\x08\x04", 3); cmd.wlen = 3; cmd.rlen = 3; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; memcpy(cmd.args, "\x14\x00\x0c\x10\x12\x00", 6); cmd.wlen = 6; cmd.rlen = 4; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; memcpy(cmd.args, "\x14\x00\x06\x10\x24\x00", 6); cmd.wlen = 6; cmd.rlen = 4; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; memcpy(cmd.args, "\x14\x00\x07\x10\x00\x24", 6); cmd.wlen = 6; cmd.rlen = 4; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; @@ -285,18 +278,18 @@ static int si2168_set_frontend(struct dvb_frontend *fe) cmd.args[4] = delivery_system | bandwidth; cmd.wlen = 6; cmd.rlen = 4; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; /* set DVB-C symbol rate */ if (c->delivery_system == SYS_DVBC_ANNEX_A) { memcpy(cmd.args, "\x14\x00\x02\x11", 4); - cmd.args[4] = (c->symbol_rate / 1000) & 0xff; + cmd.args[4] = ((c->symbol_rate / 1000) >> 0) & 0xff; cmd.args[5] = ((c->symbol_rate / 1000) >> 8) & 0xff; cmd.wlen = 6; cmd.rlen = 4; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; } @@ -304,88 +297,88 @@ static int si2168_set_frontend(struct dvb_frontend *fe) memcpy(cmd.args, "\x14\x00\x0f\x10\x10\x00", 6); cmd.wlen = 6; cmd.rlen = 4; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; memcpy(cmd.args, "\x14\x00\x09\x10\xe3\x08", 6); - cmd.args[5] |= s->ts_clock_inv ? 0x00 : 0x10; + cmd.args[5] |= dev->ts_clock_inv ? 0x00 : 0x10; cmd.wlen = 6; cmd.rlen = 4; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; memcpy(cmd.args, "\x14\x00\x08\x10\xd7\x05", 6); - cmd.args[5] |= s->ts_clock_inv ? 0x00 : 0x10; + cmd.args[5] |= dev->ts_clock_inv ? 0x00 : 0x10; cmd.wlen = 6; cmd.rlen = 4; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; memcpy(cmd.args, "\x14\x00\x01\x12\x00\x00", 6); cmd.wlen = 6; cmd.rlen = 4; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; memcpy(cmd.args, "\x14\x00\x01\x03\x0c\x00", 6); cmd.wlen = 6; cmd.rlen = 4; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; memcpy(cmd.args, "\x85", 1); cmd.wlen = 1; cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; - s->delivery_system = c->delivery_system; + dev->delivery_system = c->delivery_system; return 0; err: - dev_dbg(&s->client->dev, "failed=%d\n", ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int si2168_init(struct dvb_frontend *fe) { - struct si2168 *s = fe->demodulator_priv; + struct i2c_client *client = fe->demodulator_priv; + struct si2168_dev *dev = i2c_get_clientdata(client); int ret, len, remaining; - const struct firmware *fw = NULL; - u8 *fw_file; - const unsigned int i2c_wr_max = 8; + const struct firmware *fw; + const char *fw_name; struct si2168_cmd cmd; unsigned int chip_id; - dev_dbg(&s->client->dev, "\n"); + dev_dbg(&client->dev, "\n"); /* initialize */ memcpy(cmd.args, "\xc0\x12\x00\x0c\x00\x0d\x16\x00\x00\x00\x00\x00\x00", 13); cmd.wlen = 13; cmd.rlen = 0; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; - if (s->fw_loaded) { + if (dev->fw_loaded) { /* resume */ memcpy(cmd.args, "\xc0\x06\x08\x0f\x00\x20\x21\x01", 8); cmd.wlen = 8; cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; memcpy(cmd.args, "\x85", 1); cmd.wlen = 1; cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; @@ -396,7 +389,7 @@ static int si2168_init(struct dvb_frontend *fe) memcpy(cmd.args, "\xc0\x06\x01\x0f\x00\x20\x20\x01", 8); cmd.wlen = 8; cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; @@ -404,7 +397,7 @@ static int si2168_init(struct dvb_frontend *fe) memcpy(cmd.args, "\x02", 1); cmd.wlen = 1; cmd.rlen = 13; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; @@ -417,50 +410,48 @@ static int si2168_init(struct dvb_frontend *fe) switch (chip_id) { case SI2168_A20: - fw_file = SI2168_A20_FIRMWARE; + fw_name = SI2168_A20_FIRMWARE; break; case SI2168_A30: - fw_file = SI2168_A30_FIRMWARE; + fw_name = SI2168_A30_FIRMWARE; break; case SI2168_B40: - fw_file = SI2168_B40_FIRMWARE; + fw_name = SI2168_B40_FIRMWARE; break; default: - dev_err(&s->client->dev, - "unknown chip version Si21%d-%c%c%c\n", + dev_err(&client->dev, "unknown chip version Si21%d-%c%c%c\n", cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]); ret = -EINVAL; goto err; } - /* cold state - try to download firmware */ - dev_info(&s->client->dev, "found a '%s' in cold state\n", - si2168_ops.info.name); + dev_info(&client->dev, "found a 'Silicon Labs Si21%d-%c%c%c'\n", + cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]); /* request the firmware, this will block and timeout */ - ret = request_firmware(&fw, fw_file, &s->client->dev); + ret = request_firmware(&fw, fw_name, &client->dev); if (ret) { /* fallback mechanism to handle old name for Si2168 B40 fw */ if (chip_id == SI2168_B40) { - fw_file = SI2168_B40_FIRMWARE_FALLBACK; - ret = request_firmware(&fw, fw_file, &s->client->dev); + fw_name = SI2168_B40_FIRMWARE_FALLBACK; + ret = request_firmware(&fw, fw_name, &client->dev); } if (ret == 0) { - dev_notice(&s->client->dev, + dev_notice(&client->dev, "please install firmware file '%s'\n", SI2168_B40_FIRMWARE); } else { - dev_err(&s->client->dev, + dev_err(&client->dev, "firmware file '%s' not found\n", - fw_file); - goto error_fw_release; + fw_name); + goto err_release_firmware; } } - dev_info(&s->client->dev, "downloading firmware from file '%s'\n", - fw_file); + dev_info(&client->dev, "downloading firmware from file '%s'\n", + fw_name); if ((fw->size % 17 == 0) && (fw->data[0] > 5)) { /* firmware is in the new format */ @@ -469,41 +460,37 @@ static int si2168_init(struct dvb_frontend *fe) memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); cmd.wlen = len; cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); - if (ret) { - dev_err(&s->client->dev, - "firmware download failed=%d\n", - ret); - goto error_fw_release; - } + ret = si2168_cmd_execute(client, &cmd); + if (ret) + break; } - } else { + } else if (fw->size % 8 == 0) { /* firmware is in the old format */ - for (remaining = fw->size; remaining > 0; remaining -= i2c_wr_max) { - len = remaining; - if (len > i2c_wr_max) - len = i2c_wr_max; - + for (remaining = fw->size; remaining > 0; remaining -= 8) { + len = 8; memcpy(cmd.args, &fw->data[fw->size - remaining], len); cmd.wlen = len; cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); - if (ret) { - dev_err(&s->client->dev, - "firmware download failed=%d\n", - ret); - goto error_fw_release; - } + ret = si2168_cmd_execute(client, &cmd); + if (ret) + break; } + } else { + /* bad or unknown firmware format */ + ret = -EINVAL; + } + + if (ret) { + dev_err(&client->dev, "firmware download failed %d\n", ret); + goto err_release_firmware; } release_firmware(fw); - fw = NULL; memcpy(cmd.args, "\x01\x01", 2); cmd.wlen = 2; cmd.rlen = 1; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; @@ -511,58 +498,56 @@ static int si2168_init(struct dvb_frontend *fe) memcpy(cmd.args, "\x11", 1); cmd.wlen = 1; cmd.rlen = 10; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; - dev_dbg(&s->client->dev, "firmware version: %c.%c.%d\n", + dev_info(&client->dev, "firmware version: %c.%c.%d\n", cmd.args[6], cmd.args[7], cmd.args[8]); /* set ts mode */ memcpy(cmd.args, "\x14\x00\x01\x10\x10\x00", 6); - cmd.args[4] |= s->ts_mode; + cmd.args[4] |= dev->ts_mode; cmd.wlen = 6; cmd.rlen = 4; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; - s->fw_loaded = true; - - dev_info(&s->client->dev, "found a '%s' in warm state\n", - si2168_ops.info.name); + dev->fw_loaded = true; warm: - s->active = true; + dev->active = true; return 0; -error_fw_release: +err_release_firmware: release_firmware(fw); err: - dev_dbg(&s->client->dev, "failed=%d\n", ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int si2168_sleep(struct dvb_frontend *fe) { - struct si2168 *s = fe->demodulator_priv; + struct i2c_client *client = fe->demodulator_priv; + struct si2168_dev *dev = i2c_get_clientdata(client); int ret; struct si2168_cmd cmd; - dev_dbg(&s->client->dev, "\n"); + dev_dbg(&client->dev, "\n"); - s->active = false; + dev->active = false; memcpy(cmd.args, "\x13", 1); cmd.wlen = 1; cmd.rlen = 0; - ret = si2168_cmd_execute(s, &cmd); + ret = si2168_cmd_execute(client, &cmd); if (ret) goto err; return 0; err: - dev_dbg(&s->client->dev, "failed=%d\n", ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } @@ -581,21 +566,22 @@ static int si2168_get_tune_settings(struct dvb_frontend *fe, */ static int si2168_select(struct i2c_adapter *adap, void *mux_priv, u32 chan) { - struct si2168 *s = mux_priv; + struct i2c_client *client = mux_priv; + struct si2168_dev *dev = i2c_get_clientdata(client); int ret; struct i2c_msg gate_open_msg = { - .addr = s->client->addr, + .addr = client->addr, .flags = 0, .len = 3, .buf = "\xc0\x0d\x01", }; - mutex_lock(&s->i2c_mutex); + mutex_lock(&dev->i2c_mutex); /* open tuner I2C gate */ - ret = __i2c_transfer(s->client->adapter, &gate_open_msg, 1); + ret = __i2c_transfer(client->adapter, &gate_open_msg, 1); if (ret != 1) { - dev_warn(&s->client->dev, "i2c write failed=%d\n", ret); + dev_warn(&client->dev, "i2c write failed=%d\n", ret); if (ret >= 0) ret = -EREMOTEIO; } else { @@ -607,26 +593,27 @@ static int si2168_select(struct i2c_adapter *adap, void *mux_priv, u32 chan) static int si2168_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan) { - struct si2168 *s = mux_priv; + struct i2c_client *client = mux_priv; + struct si2168_dev *dev = i2c_get_clientdata(client); int ret; struct i2c_msg gate_close_msg = { - .addr = s->client->addr, + .addr = client->addr, .flags = 0, .len = 3, .buf = "\xc0\x0d\x00", }; /* close tuner I2C gate */ - ret = __i2c_transfer(s->client->adapter, &gate_close_msg, 1); + ret = __i2c_transfer(client->adapter, &gate_close_msg, 1); if (ret != 1) { - dev_warn(&s->client->dev, "i2c write failed=%d\n", ret); + dev_warn(&client->dev, "i2c write failed=%d\n", ret); if (ret >= 0) ret = -EREMOTEIO; } else { ret = 0; } - mutex_unlock(&s->i2c_mutex); + mutex_unlock(&dev->i2c_mutex); return ret; } @@ -635,6 +622,8 @@ static const struct dvb_frontend_ops si2168_ops = { .delsys = {SYS_DVBT, SYS_DVBT2, SYS_DVBC_ANNEX_A}, .info = { .name = "Silicon Labs Si2168", + .symbol_rate_min = 1000000, + .symbol_rate_max = 7200000, .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | @@ -670,71 +659,69 @@ static int si2168_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct si2168_config *config = client->dev.platform_data; - struct si2168 *s; + struct si2168_dev *dev; int ret; dev_dbg(&client->dev, "\n"); - s = kzalloc(sizeof(struct si2168), GFP_KERNEL); - if (!s) { + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { ret = -ENOMEM; dev_err(&client->dev, "kzalloc() failed\n"); goto err; } - s->client = client; - mutex_init(&s->i2c_mutex); + mutex_init(&dev->i2c_mutex); /* create mux i2c adapter for tuner */ - s->adapter = i2c_add_mux_adapter(client->adapter, &client->dev, s, - 0, 0, 0, si2168_select, si2168_deselect); - if (s->adapter == NULL) { + dev->adapter = i2c_add_mux_adapter(client->adapter, &client->dev, + client, 0, 0, 0, si2168_select, si2168_deselect); + if (dev->adapter == NULL) { ret = -ENODEV; - goto err; + goto err_kfree; } /* create dvb_frontend */ - memcpy(&s->fe.ops, &si2168_ops, sizeof(struct dvb_frontend_ops)); - s->fe.demodulator_priv = s; - - *config->i2c_adapter = s->adapter; - *config->fe = &s->fe; - s->ts_mode = config->ts_mode; - s->ts_clock_inv = config->ts_clock_inv; - s->fw_loaded = false; + memcpy(&dev->fe.ops, &si2168_ops, sizeof(struct dvb_frontend_ops)); + dev->fe.demodulator_priv = client; + *config->i2c_adapter = dev->adapter; + *config->fe = &dev->fe; + dev->ts_mode = config->ts_mode; + dev->ts_clock_inv = config->ts_clock_inv; + dev->fw_loaded = false; - i2c_set_clientdata(client, s); + i2c_set_clientdata(client, dev); - dev_info(&s->client->dev, - "Silicon Labs Si2168 successfully attached\n"); + dev_info(&client->dev, "Silicon Labs Si2168 successfully attached\n"); return 0; +err_kfree: + kfree(dev); err: - kfree(s); dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int si2168_remove(struct i2c_client *client) { - struct si2168 *s = i2c_get_clientdata(client); + struct si2168_dev *dev = i2c_get_clientdata(client); dev_dbg(&client->dev, "\n"); - i2c_del_mux_adapter(s->adapter); + i2c_del_mux_adapter(dev->adapter); - s->fe.ops.release = NULL; - s->fe.demodulator_priv = NULL; + dev->fe.ops.release = NULL; + dev->fe.demodulator_priv = NULL; - kfree(s); + kfree(dev); return 0; } -static const struct i2c_device_id si2168_id[] = { +static const struct i2c_device_id si2168_id_table[] = { {"si2168", 0}, {} }; -MODULE_DEVICE_TABLE(i2c, si2168_id); +MODULE_DEVICE_TABLE(i2c, si2168_id_table); static struct i2c_driver si2168_driver = { .driver = { @@ -743,7 +730,7 @@ static struct i2c_driver si2168_driver = { }, .probe = si2168_probe, .remove = si2168_remove, - .id_table = si2168_id, + .id_table = si2168_id_table, }; module_i2c_driver(si2168_driver); diff --git a/drivers/media/dvb-frontends/si2168.h b/drivers/media/dvb-frontends/si2168.h index 87bc12146667..70d702ae6f49 100644 --- a/drivers/media/dvb-frontends/si2168.h +++ b/drivers/media/dvb-frontends/si2168.h @@ -36,14 +36,12 @@ struct si2168_config { struct i2c_adapter **i2c_adapter; /* TS mode */ +#define SI2168_TS_PARALLEL 0x06 +#define SI2168_TS_SERIAL 0x03 u8 ts_mode; /* TS clock inverted */ bool ts_clock_inv; - }; -#define SI2168_TS_PARALLEL 0x06 -#define SI2168_TS_SERIAL 0x03 - #endif diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h index 60bc3349b6c3..aadd1367673f 100644 --- a/drivers/media/dvb-frontends/si2168_priv.h +++ b/drivers/media/dvb-frontends/si2168_priv.h @@ -28,8 +28,7 @@ #define SI2168_B40_FIRMWARE_FALLBACK "dvb-demod-si2168-02.fw" /* state struct */ -struct si2168 { - struct i2c_client *client; +struct si2168_dev { struct i2c_adapter *adapter; struct mutex i2c_mutex; struct dvb_frontend fe; diff --git a/drivers/media/dvb-frontends/stb0899_algo.c b/drivers/media/dvb-frontends/stb0899_algo.c index 93596e0e640b..3012f196e9bd 100644 --- a/drivers/media/dvb-frontends/stb0899_algo.c +++ b/drivers/media/dvb-frontends/stb0899_algo.c @@ -19,6 +19,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <linux/bitops.h> #include "stb0899_drv.h" #include "stb0899_priv.h" #include "stb0899_reg.h" @@ -1490,9 +1491,7 @@ enum stb0899_status stb0899_dvbs2_algo(struct stb0899_state *state) /* Store signal parameters */ offsetfreq = STB0899_READ_S2REG(STB0899_S2DEMOD, CRL_FREQ); - /* sign extend 30 bit value before using it in calculations */ - if (offsetfreq & (1 << 29)) - offsetfreq |= -1 << 30; + offsetfreq = sign_extend32(offsetfreq, 29); offsetfreq = offsetfreq / ((1 << 30) / 1000); offsetfreq *= (internal->master_clk / 1000000); diff --git a/drivers/media/dvb-frontends/stb0899_drv.c b/drivers/media/dvb-frontends/stb0899_drv.c index 19646fbb061d..c73899d3a53d 100644 --- a/drivers/media/dvb-frontends/stb0899_drv.c +++ b/drivers/media/dvb-frontends/stb0899_drv.c @@ -20,6 +20,7 @@ */ #include <linux/init.h> +#include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> @@ -691,7 +692,7 @@ static int stb0899_wait_diseqc_fifo_empty(struct stb0899_state *state, int timeo reg = stb0899_read_reg(state, STB0899_DISSTATUS); if (!STB0899_GETFIELD(FIFOFULL, reg)) break; - if ((jiffies - start) > timeout) { + if (time_after(jiffies, start + timeout)) { dprintk(state->verbose, FE_ERROR, 1, "timed out !!"); return -ETIMEDOUT; } @@ -733,7 +734,7 @@ static int stb0899_wait_diseqc_rxidle(struct stb0899_state *state, int timeout) while (!STB0899_GETFIELD(RXEND, reg)) { reg = stb0899_read_reg(state, STB0899_DISRX_ST0); - if (jiffies - start > timeout) { + if (time_after(jiffies, start + timeout)) { dprintk(state->verbose, FE_ERROR, 1, "timed out!!"); return -ETIMEDOUT; } @@ -782,7 +783,7 @@ static int stb0899_wait_diseqc_txidle(struct stb0899_state *state, int timeout) while (!STB0899_GETFIELD(TXIDLE, reg)) { reg = stb0899_read_reg(state, STB0899_DISSTATUS); - if (jiffies - start > timeout) { + if (time_after(jiffies, start + timeout)) { dprintk(state->verbose, FE_ERROR, 1, "timed out!!"); return -ETIMEDOUT; } diff --git a/drivers/media/dvb-frontends/tc90522.c b/drivers/media/dvb-frontends/tc90522.c index b35d65c9cc05..dce22ce35d20 100644 --- a/drivers/media/dvb-frontends/tc90522.c +++ b/drivers/media/dvb-frontends/tc90522.c @@ -214,6 +214,7 @@ static int tc90522s_get_frontend(struct dvb_frontend *fe) state = fe->demodulator_priv; c = &fe->dtv_property_cache; c->delivery_system = SYS_ISDBS; + c->symbol_rate = 28860000; layers = 0; ret = reg_read(state, 0xe6, val, 5); diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 205d71364343..da58c9bb67c2 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -177,7 +177,7 @@ comment "Video decoders" config VIDEO_ADV7180 tristate "Analog Devices ADV7180 decoder" - depends on VIDEO_V4L2 && I2C + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API ---help--- Support for the Analog Devices ADV7180 video decoder. @@ -196,7 +196,7 @@ config VIDEO_ADV7183 config VIDEO_ADV7604 tristate "Analog Devices ADV7604 decoder" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API ---help--- Support for the Analog Devices ADV7604 video decoder. @@ -208,7 +208,8 @@ config VIDEO_ADV7604 config VIDEO_ADV7842 tristate "Analog Devices ADV7842 decoder" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + select HDMI ---help--- Support for the Analog Devices ADV7842 video decoder. @@ -422,7 +423,7 @@ config VIDEO_ADV7393 config VIDEO_ADV7511 tristate "Analog Devices ADV7511 encoder" - depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API ---help--- Support for the Analog Devices ADV7511 video encoder. diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index bffe6eb528a3..b75878c27c2a 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -30,56 +30,60 @@ #include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> #include <linux/mutex.h> - -#define ADV7180_INPUT_CONTROL_REG 0x00 -#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00 -#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10 -#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM 0x20 -#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM 0x30 -#define ADV7180_INPUT_CONTROL_NTSC_J 0x40 -#define ADV7180_INPUT_CONTROL_NTSC_M 0x50 -#define ADV7180_INPUT_CONTROL_PAL60 0x60 -#define ADV7180_INPUT_CONTROL_NTSC_443 0x70 -#define ADV7180_INPUT_CONTROL_PAL_BG 0x80 -#define ADV7180_INPUT_CONTROL_PAL_N 0x90 -#define ADV7180_INPUT_CONTROL_PAL_M 0xa0 -#define ADV7180_INPUT_CONTROL_PAL_M_PED 0xb0 -#define ADV7180_INPUT_CONTROL_PAL_COMB_N 0xc0 -#define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0 -#define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0 -#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0 +#include <linux/delay.h> + +#define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM 0x0 +#define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM_PED 0x1 +#define ADV7180_STD_AD_PAL_N_NTSC_J_SECAM 0x2 +#define ADV7180_STD_AD_PAL_N_NTSC_M_SECAM 0x3 +#define ADV7180_STD_NTSC_J 0x4 +#define ADV7180_STD_NTSC_M 0x5 +#define ADV7180_STD_PAL60 0x6 +#define ADV7180_STD_NTSC_443 0x7 +#define ADV7180_STD_PAL_BG 0x8 +#define ADV7180_STD_PAL_N 0x9 +#define ADV7180_STD_PAL_M 0xa +#define ADV7180_STD_PAL_M_PED 0xb +#define ADV7180_STD_PAL_COMB_N 0xc +#define ADV7180_STD_PAL_COMB_N_PED 0xd +#define ADV7180_STD_PAL_SECAM 0xe +#define ADV7180_STD_PAL_SECAM_PED 0xf + +#define ADV7180_REG_INPUT_CONTROL 0x0000 #define ADV7180_INPUT_CONTROL_INSEL_MASK 0x0f -#define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04 +#define ADV7182_REG_INPUT_VIDSEL 0x0002 + +#define ADV7180_REG_EXTENDED_OUTPUT_CONTROL 0x0004 #define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5 -#define ADV7180_AUTODETECT_ENABLE_REG 0x07 +#define ADV7180_REG_AUTODETECT_ENABLE 0x07 #define ADV7180_AUTODETECT_DEFAULT 0x7f /* Contrast */ -#define ADV7180_CON_REG 0x08 /*Unsigned */ +#define ADV7180_REG_CON 0x0008 /*Unsigned */ #define ADV7180_CON_MIN 0 #define ADV7180_CON_DEF 128 #define ADV7180_CON_MAX 255 /* Brightness*/ -#define ADV7180_BRI_REG 0x0a /*Signed */ +#define ADV7180_REG_BRI 0x000a /*Signed */ #define ADV7180_BRI_MIN -128 #define ADV7180_BRI_DEF 0 #define ADV7180_BRI_MAX 127 /* Hue */ -#define ADV7180_HUE_REG 0x0b /*Signed, inverted */ +#define ADV7180_REG_HUE 0x000b /*Signed, inverted */ #define ADV7180_HUE_MIN -127 #define ADV7180_HUE_DEF 0 #define ADV7180_HUE_MAX 128 -#define ADV7180_ADI_CTRL_REG 0x0e -#define ADV7180_ADI_CTRL_IRQ_SPACE 0x20 +#define ADV7180_REG_CTRL 0x000e +#define ADV7180_CTRL_IRQ_SPACE 0x20 -#define ADV7180_PWR_MAN_REG 0x0f +#define ADV7180_REG_PWR_MAN 0x0f #define ADV7180_PWR_MAN_ON 0x04 #define ADV7180_PWR_MAN_OFF 0x24 #define ADV7180_PWR_MAN_RES 0x80 -#define ADV7180_STATUS1_REG 0x10 +#define ADV7180_REG_STATUS1 0x0010 #define ADV7180_STATUS1_IN_LOCK 0x01 #define ADV7180_STATUS1_AUTOD_MASK 0x70 #define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00 @@ -91,49 +95,161 @@ #define ADV7180_STATUS1_AUTOD_PAL_COMB 0x60 #define ADV7180_STATUS1_AUTOD_SECAM_525 0x70 -#define ADV7180_IDENT_REG 0x11 +#define ADV7180_REG_IDENT 0x0011 #define ADV7180_ID_7180 0x18 -#define ADV7180_ICONF1_ADI 0x40 +#define ADV7180_REG_ICONF1 0x0040 #define ADV7180_ICONF1_ACTIVE_LOW 0x01 #define ADV7180_ICONF1_PSYNC_ONLY 0x10 #define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0 /* Saturation */ -#define ADV7180_SD_SAT_CB_REG 0xe3 /*Unsigned */ -#define ADV7180_SD_SAT_CR_REG 0xe4 /*Unsigned */ +#define ADV7180_REG_SD_SAT_CB 0x00e3 /*Unsigned */ +#define ADV7180_REG_SD_SAT_CR 0x00e4 /*Unsigned */ #define ADV7180_SAT_MIN 0 #define ADV7180_SAT_DEF 128 #define ADV7180_SAT_MAX 255 #define ADV7180_IRQ1_LOCK 0x01 #define ADV7180_IRQ1_UNLOCK 0x02 -#define ADV7180_ISR1_ADI 0x42 -#define ADV7180_ICR1_ADI 0x43 -#define ADV7180_IMR1_ADI 0x44 -#define ADV7180_IMR2_ADI 0x48 +#define ADV7180_REG_ISR1 0x0042 +#define ADV7180_REG_ICR1 0x0043 +#define ADV7180_REG_IMR1 0x0044 +#define ADV7180_REG_IMR2 0x0048 #define ADV7180_IRQ3_AD_CHANGE 0x08 -#define ADV7180_ISR3_ADI 0x4A -#define ADV7180_ICR3_ADI 0x4B -#define ADV7180_IMR3_ADI 0x4C -#define ADV7180_IMR4_ADI 0x50 +#define ADV7180_REG_ISR3 0x004A +#define ADV7180_REG_ICR3 0x004B +#define ADV7180_REG_IMR3 0x004C +#define ADV7180_REG_IMR4 0x50 -#define ADV7180_NTSC_V_BIT_END_REG 0xE6 +#define ADV7180_REG_NTSC_V_BIT_END 0x00E6 #define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F +#define ADV7180_REG_VPP_SLAVE_ADDR 0xFD +#define ADV7180_REG_CSI_SLAVE_ADDR 0xFE + +#define ADV7180_REG_FLCONTROL 0x40e0 +#define ADV7180_FLCONTROL_FL_ENABLE 0x1 + +#define ADV7180_CSI_REG_PWRDN 0x00 +#define ADV7180_CSI_PWRDN 0x80 + +#define ADV7180_INPUT_CVBS_AIN1 0x00 +#define ADV7180_INPUT_CVBS_AIN2 0x01 +#define ADV7180_INPUT_CVBS_AIN3 0x02 +#define ADV7180_INPUT_CVBS_AIN4 0x03 +#define ADV7180_INPUT_CVBS_AIN5 0x04 +#define ADV7180_INPUT_CVBS_AIN6 0x05 +#define ADV7180_INPUT_SVIDEO_AIN1_AIN2 0x06 +#define ADV7180_INPUT_SVIDEO_AIN3_AIN4 0x07 +#define ADV7180_INPUT_SVIDEO_AIN5_AIN6 0x08 +#define ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3 0x09 +#define ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0a + +#define ADV7182_INPUT_CVBS_AIN1 0x00 +#define ADV7182_INPUT_CVBS_AIN2 0x01 +#define ADV7182_INPUT_CVBS_AIN3 0x02 +#define ADV7182_INPUT_CVBS_AIN4 0x03 +#define ADV7182_INPUT_CVBS_AIN5 0x04 +#define ADV7182_INPUT_CVBS_AIN6 0x05 +#define ADV7182_INPUT_CVBS_AIN7 0x06 +#define ADV7182_INPUT_CVBS_AIN8 0x07 +#define ADV7182_INPUT_SVIDEO_AIN1_AIN2 0x08 +#define ADV7182_INPUT_SVIDEO_AIN3_AIN4 0x09 +#define ADV7182_INPUT_SVIDEO_AIN5_AIN6 0x0a +#define ADV7182_INPUT_SVIDEO_AIN7_AIN8 0x0b +#define ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3 0x0c +#define ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0d +#define ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2 0x0e +#define ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4 0x0f +#define ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6 0x10 +#define ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8 0x11 + +#define ADV7180_DEFAULT_CSI_I2C_ADDR 0x44 +#define ADV7180_DEFAULT_VPP_I2C_ADDR 0x42 + +#define V4L2_CID_ADV_FAST_SWITCH (V4L2_CID_USER_ADV7180_BASE + 0x00) + +struct adv7180_state; + +#define ADV7180_FLAG_RESET_POWERED BIT(0) +#define ADV7180_FLAG_V2 BIT(1) +#define ADV7180_FLAG_MIPI_CSI2 BIT(2) +#define ADV7180_FLAG_I2P BIT(3) + +struct adv7180_chip_info { + unsigned int flags; + unsigned int valid_input_mask; + int (*set_std)(struct adv7180_state *st, unsigned int std); + int (*select_input)(struct adv7180_state *st, unsigned int input); + int (*init)(struct adv7180_state *state); +}; + struct adv7180_state { struct v4l2_ctrl_handler ctrl_hdl; struct v4l2_subdev sd; + struct media_pad pad; struct mutex mutex; /* mutual excl. when accessing chip */ int irq; v4l2_std_id curr_norm; bool autodetect; bool powered; u8 input; + + struct i2c_client *client; + unsigned int register_page; + struct i2c_client *csi_client; + struct i2c_client *vpp_client; + const struct adv7180_chip_info *chip_info; + enum v4l2_field field; }; #define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \ struct adv7180_state, \ ctrl_hdl)->sd) +static int adv7180_select_page(struct adv7180_state *state, unsigned int page) +{ + if (state->register_page != page) { + i2c_smbus_write_byte_data(state->client, ADV7180_REG_CTRL, + page); + state->register_page = page; + } + + return 0; +} + +static int adv7180_write(struct adv7180_state *state, unsigned int reg, + unsigned int value) +{ + lockdep_assert_held(&state->mutex); + adv7180_select_page(state, reg >> 8); + return i2c_smbus_write_byte_data(state->client, reg & 0xff, value); +} + +static int adv7180_read(struct adv7180_state *state, unsigned int reg) +{ + lockdep_assert_held(&state->mutex); + adv7180_select_page(state, reg >> 8); + return i2c_smbus_read_byte_data(state->client, reg & 0xff); +} + +static int adv7180_csi_write(struct adv7180_state *state, unsigned int reg, + unsigned int value) +{ + return i2c_smbus_write_byte_data(state->csi_client, reg, value); +} + +static int adv7180_set_video_standard(struct adv7180_state *state, + unsigned int std) +{ + return state->chip_info->set_std(state, std); +} + +static int adv7180_vpp_write(struct adv7180_state *state, unsigned int reg, + unsigned int value) +{ + return i2c_smbus_write_byte_data(state->vpp_client, reg, value); +} + static v4l2_std_id adv7180_std_to_v4l2(u8 status1) { /* in case V4L2_IN_ST_NO_SIGNAL */ @@ -165,22 +281,22 @@ static v4l2_std_id adv7180_std_to_v4l2(u8 status1) static int v4l2_std_to_adv7180(v4l2_std_id std) { if (std == V4L2_STD_PAL_60) - return ADV7180_INPUT_CONTROL_PAL60; + return ADV7180_STD_PAL60; if (std == V4L2_STD_NTSC_443) - return ADV7180_INPUT_CONTROL_NTSC_443; + return ADV7180_STD_NTSC_443; if (std == V4L2_STD_PAL_N) - return ADV7180_INPUT_CONTROL_PAL_N; + return ADV7180_STD_PAL_N; if (std == V4L2_STD_PAL_M) - return ADV7180_INPUT_CONTROL_PAL_M; + return ADV7180_STD_PAL_M; if (std == V4L2_STD_PAL_Nc) - return ADV7180_INPUT_CONTROL_PAL_COMB_N; + return ADV7180_STD_PAL_COMB_N; if (std & V4L2_STD_PAL) - return ADV7180_INPUT_CONTROL_PAL_BG; + return ADV7180_STD_PAL_BG; if (std & V4L2_STD_NTSC) - return ADV7180_INPUT_CONTROL_NTSC_M; + return ADV7180_STD_NTSC_M; if (std & V4L2_STD_SECAM) - return ADV7180_INPUT_CONTROL_PAL_SECAM; + return ADV7180_STD_PAL_SECAM; return -EINVAL; } @@ -193,10 +309,10 @@ static u32 adv7180_status_to_v4l2(u8 status1) return 0; } -static int __adv7180_status(struct i2c_client *client, u32 *status, +static int __adv7180_status(struct adv7180_state *state, u32 *status, v4l2_std_id *std) { - int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG); + int status1 = adv7180_read(state, ADV7180_REG_STATUS1); if (status1 < 0) return status1; @@ -225,7 +341,7 @@ static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) if (!state->autodetect || state->irq > 0) *std = state->curr_norm; else - err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std); + err = __adv7180_status(state, NULL, std); mutex_unlock(&state->mutex); return err; @@ -236,26 +352,19 @@ static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input, { struct adv7180_state *state = to_state(sd); int ret = mutex_lock_interruptible(&state->mutex); - struct i2c_client *client = v4l2_get_subdevdata(sd); if (ret) return ret; - /* We cannot discriminate between LQFP and 40-pin LFCSP, so accept - * all inputs and let the card driver take care of validation - */ - if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input) + if (input > 31 || !(BIT(input) & state->chip_info->valid_input_mask)) { + ret = -EINVAL; goto out; + } - ret = i2c_smbus_read_byte_data(client, ADV7180_INPUT_CONTROL_REG); - - if (ret < 0) - goto out; + ret = state->chip_info->select_input(state, input); - ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK; - ret = i2c_smbus_write_byte_data(client, - ADV7180_INPUT_CONTROL_REG, ret | input); - state->input = input; + if (ret == 0) + state->input = input; out: mutex_unlock(&state->mutex); return ret; @@ -268,74 +377,104 @@ static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) if (ret) return ret; - ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL); + ret = __adv7180_status(state, status, NULL); mutex_unlock(&state->mutex); return ret; } +static int adv7180_program_std(struct adv7180_state *state) +{ + int ret; + + if (state->autodetect) { + ret = adv7180_set_video_standard(state, + ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM); + if (ret < 0) + return ret; + + __adv7180_status(state, NULL, &state->curr_norm); + } else { + ret = v4l2_std_to_adv7180(state->curr_norm); + if (ret < 0) + return ret; + + ret = adv7180_set_video_standard(state, ret); + if (ret < 0) + return ret; + } + + return 0; +} + static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) { struct adv7180_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = mutex_lock_interruptible(&state->mutex); + if (ret) return ret; /* all standards -> autodetect */ if (std == V4L2_STD_ALL) { - ret = - i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, - ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM - | state->input); - if (ret < 0) - goto out; - - __adv7180_status(client, NULL, &state->curr_norm); state->autodetect = true; } else { + /* Make sure we can support this std */ ret = v4l2_std_to_adv7180(std); if (ret < 0) goto out; - ret = i2c_smbus_write_byte_data(client, - ADV7180_INPUT_CONTROL_REG, - ret | state->input); - if (ret < 0) - goto out; - state->curr_norm = std; state->autodetect = false; } - ret = 0; + + ret = adv7180_program_std(state); out: mutex_unlock(&state->mutex); return ret; } -static int adv7180_set_power(struct adv7180_state *state, - struct i2c_client *client, bool on) +static int adv7180_set_power(struct adv7180_state *state, bool on) { u8 val; + int ret; if (on) val = ADV7180_PWR_MAN_ON; else val = ADV7180_PWR_MAN_OFF; - return i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, val); + ret = adv7180_write(state, ADV7180_REG_PWR_MAN, val); + if (ret) + return ret; + + if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { + if (on) { + adv7180_csi_write(state, 0xDE, 0x02); + adv7180_csi_write(state, 0xD2, 0xF7); + adv7180_csi_write(state, 0xD8, 0x65); + adv7180_csi_write(state, 0xE0, 0x09); + adv7180_csi_write(state, 0x2C, 0x00); + if (state->field == V4L2_FIELD_NONE) + adv7180_csi_write(state, 0x1D, 0x80); + adv7180_csi_write(state, 0x00, 0x00); + } else { + adv7180_csi_write(state, 0x00, 0x80); + } + } + + return 0; } static int adv7180_s_power(struct v4l2_subdev *sd, int on) { struct adv7180_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); int ret; ret = mutex_lock_interruptible(&state->mutex); if (ret) return ret; - ret = adv7180_set_power(state, client, on); + ret = adv7180_set_power(state, on); if (ret == 0) state->powered = on; @@ -347,7 +486,6 @@ static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_adv7180_sd(ctrl); struct adv7180_state *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); int ret = mutex_lock_interruptible(&state->mutex); int val; @@ -356,26 +494,36 @@ static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl) val = ctrl->val; switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG, val); + ret = adv7180_write(state, ADV7180_REG_BRI, val); break; case V4L2_CID_HUE: /*Hue is inverted according to HSL chart */ - ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, -val); + ret = adv7180_write(state, ADV7180_REG_HUE, -val); break; case V4L2_CID_CONTRAST: - ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG, val); + ret = adv7180_write(state, ADV7180_REG_CON, val); break; case V4L2_CID_SATURATION: /* *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE *Let's not confuse the user, everybody understands saturation */ - ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG, - val); + ret = adv7180_write(state, ADV7180_REG_SD_SAT_CB, val); if (ret < 0) break; - ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG, - val); + ret = adv7180_write(state, ADV7180_REG_SD_SAT_CR, val); + break; + case V4L2_CID_ADV_FAST_SWITCH: + if (ctrl->val) { + /* ADI required write */ + adv7180_write(state, 0x80d9, 0x44); + adv7180_write(state, ADV7180_REG_FLCONTROL, + ADV7180_FLCONTROL_FL_ENABLE); + } else { + /* ADI required write */ + adv7180_write(state, 0x80d9, 0xc4); + adv7180_write(state, ADV7180_REG_FLCONTROL, 0x00); + } break; default: ret = -EINVAL; @@ -389,6 +537,16 @@ static const struct v4l2_ctrl_ops adv7180_ctrl_ops = { .s_ctrl = adv7180_s_ctrl, }; +static const struct v4l2_ctrl_config adv7180_ctrl_fast_switch = { + .ops = &adv7180_ctrl_ops, + .id = V4L2_CID_ADV_FAST_SWITCH, + .name = "Fast Switching", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .min = 0, + .max = 1, + .step = 1, +}; + static int adv7180_init_controls(struct adv7180_state *state) { v4l2_ctrl_handler_init(&state->ctrl_hdl, 4); @@ -405,6 +563,8 @@ static int adv7180_init_controls(struct adv7180_state *state) v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, V4L2_CID_HUE, ADV7180_HUE_MIN, ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF); + v4l2_ctrl_new_custom(&state->ctrl_hdl, &adv7180_ctrl_fast_switch, NULL); + state->sd.ctrl_handler = &state->ctrl_hdl; if (state->ctrl_hdl.error) { int err = state->ctrl_hdl.error; @@ -421,13 +581,14 @@ static void adv7180_exit_controls(struct adv7180_state *state) v4l2_ctrl_handler_free(&state->ctrl_hdl); } -static int adv7180_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned int index, - u32 *code) +static int adv7180_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) { - if (index > 0) + if (code->index != 0) return -EINVAL; - *code = MEDIA_BUS_FMT_YUYV8_2X8; + code->code = MEDIA_BUS_FMT_YUYV8_2X8; return 0; } @@ -439,23 +600,118 @@ static int adv7180_mbus_fmt(struct v4l2_subdev *sd, fmt->code = MEDIA_BUS_FMT_YUYV8_2X8; fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; - fmt->field = V4L2_FIELD_INTERLACED; fmt->width = 720; fmt->height = state->curr_norm & V4L2_STD_525_60 ? 480 : 576; return 0; } +static int adv7180_set_field_mode(struct adv7180_state *state) +{ + if (!(state->chip_info->flags & ADV7180_FLAG_I2P)) + return 0; + + if (state->field == V4L2_FIELD_NONE) { + if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { + adv7180_csi_write(state, 0x01, 0x20); + adv7180_csi_write(state, 0x02, 0x28); + adv7180_csi_write(state, 0x03, 0x38); + adv7180_csi_write(state, 0x04, 0x30); + adv7180_csi_write(state, 0x05, 0x30); + adv7180_csi_write(state, 0x06, 0x80); + adv7180_csi_write(state, 0x07, 0x70); + adv7180_csi_write(state, 0x08, 0x50); + } + adv7180_vpp_write(state, 0xa3, 0x00); + adv7180_vpp_write(state, 0x5b, 0x00); + adv7180_vpp_write(state, 0x55, 0x80); + } else { + if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { + adv7180_csi_write(state, 0x01, 0x18); + adv7180_csi_write(state, 0x02, 0x18); + adv7180_csi_write(state, 0x03, 0x30); + adv7180_csi_write(state, 0x04, 0x20); + adv7180_csi_write(state, 0x05, 0x28); + adv7180_csi_write(state, 0x06, 0x40); + adv7180_csi_write(state, 0x07, 0x58); + adv7180_csi_write(state, 0x08, 0x30); + } + adv7180_vpp_write(state, 0xa3, 0x70); + adv7180_vpp_write(state, 0x5b, 0x80); + adv7180_vpp_write(state, 0x55, 0x00); + } + + return 0; +} + +static int adv7180_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct adv7180_state *state = to_state(sd); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + format->format = *v4l2_subdev_get_try_format(fh, 0); + } else { + adv7180_mbus_fmt(sd, &format->format); + format->format.field = state->field; + } + + return 0; +} + +static int adv7180_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *format) +{ + struct adv7180_state *state = to_state(sd); + struct v4l2_mbus_framefmt *framefmt; + + switch (format->format.field) { + case V4L2_FIELD_NONE: + if (!(state->chip_info->flags & ADV7180_FLAG_I2P)) + format->format.field = V4L2_FIELD_INTERLACED; + break; + default: + format->format.field = V4L2_FIELD_INTERLACED; + break; + } + + if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + framefmt = &format->format; + if (state->field != format->format.field) { + state->field = format->format.field; + adv7180_set_power(state, false); + adv7180_set_field_mode(state); + adv7180_set_power(state, true); + } + } else { + framefmt = v4l2_subdev_get_try_format(fh, 0); + *framefmt = format->format; + } + + return adv7180_mbus_fmt(sd, framefmt); +} + static int adv7180_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { - /* - * The ADV7180 sensor supports BT.601/656 output modes. - * The BT.656 is default and not yet configurable by s/w. - */ - cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | - V4L2_MBUS_DATA_ACTIVE_HIGH; - cfg->type = V4L2_MBUS_BT656; + struct adv7180_state *state = to_state(sd); + + if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { + cfg->type = V4L2_MBUS_CSI2; + cfg->flags = V4L2_MBUS_CSI2_1_LANE | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + } else { + /* + * The ADV7180 sensor supports BT.601/656 output modes. + * The BT.656 is default and not yet configurable by s/w. + */ + cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | + V4L2_MBUS_DATA_ACTIVE_HIGH; + cfg->type = V4L2_MBUS_BT656; + } return 0; } @@ -465,139 +721,439 @@ static const struct v4l2_subdev_video_ops adv7180_video_ops = { .querystd = adv7180_querystd, .g_input_status = adv7180_g_input_status, .s_routing = adv7180_s_routing, - .enum_mbus_fmt = adv7180_enum_mbus_fmt, - .try_mbus_fmt = adv7180_mbus_fmt, - .g_mbus_fmt = adv7180_mbus_fmt, - .s_mbus_fmt = adv7180_mbus_fmt, .g_mbus_config = adv7180_g_mbus_config, }; + static const struct v4l2_subdev_core_ops adv7180_core_ops = { .s_power = adv7180_s_power, }; +static const struct v4l2_subdev_pad_ops adv7180_pad_ops = { + .enum_mbus_code = adv7180_enum_mbus_code, + .set_fmt = adv7180_set_pad_format, + .get_fmt = adv7180_get_pad_format, +}; + static const struct v4l2_subdev_ops adv7180_ops = { .core = &adv7180_core_ops, .video = &adv7180_video_ops, + .pad = &adv7180_pad_ops, }; static irqreturn_t adv7180_irq(int irq, void *devid) { struct adv7180_state *state = devid; - struct i2c_client *client = v4l2_get_subdevdata(&state->sd); u8 isr3; mutex_lock(&state->mutex); - i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, - ADV7180_ADI_CTRL_IRQ_SPACE); - isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI); + isr3 = adv7180_read(state, ADV7180_REG_ISR3); /* clear */ - i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3); - i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 0); + adv7180_write(state, ADV7180_REG_ICR3, isr3); if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect) - __adv7180_status(client, NULL, &state->curr_norm); + __adv7180_status(state, NULL, &state->curr_norm); mutex_unlock(&state->mutex); return IRQ_HANDLED; } -static int init_device(struct i2c_client *client, struct adv7180_state *state) +static int adv7180_init(struct adv7180_state *state) { int ret; - /* Initialize adv7180 */ - /* Enable autodetection */ - if (state->autodetect) { - ret = - i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, - ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM - | state->input); - if (ret < 0) - return ret; - - ret = - i2c_smbus_write_byte_data(client, - ADV7180_AUTODETECT_ENABLE_REG, - ADV7180_AUTODETECT_DEFAULT); - if (ret < 0) - return ret; - } else { - ret = v4l2_std_to_adv7180(state->curr_norm); - if (ret < 0) - return ret; - - ret = - i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, - ret | state->input); - if (ret < 0) - return ret; - - } /* ITU-R BT.656-4 compatible */ - ret = i2c_smbus_write_byte_data(client, - ADV7180_EXTENDED_OUTPUT_CONTROL_REG, + ret = adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL, ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS); if (ret < 0) return ret; /* Manually set V bit end position in NTSC mode */ - ret = i2c_smbus_write_byte_data(client, - ADV7180_NTSC_V_BIT_END_REG, + return adv7180_write(state, ADV7180_REG_NTSC_V_BIT_END, ADV7180_NTSC_V_BIT_END_MANUAL_NVEND); +} + +static int adv7180_set_std(struct adv7180_state *state, unsigned int std) +{ + return adv7180_write(state, ADV7180_REG_INPUT_CONTROL, + (std << 4) | state->input); +} + +static int adv7180_select_input(struct adv7180_state *state, unsigned int input) +{ + int ret; + + ret = adv7180_read(state, ADV7180_REG_INPUT_CONTROL); if (ret < 0) return ret; - /* read current norm */ - __adv7180_status(client, NULL, &state->curr_norm); + ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK; + ret |= input; + return adv7180_write(state, ADV7180_REG_INPUT_CONTROL, ret); +} - /* register for interrupts */ - if (state->irq > 0) { - ret = request_threaded_irq(state->irq, NULL, adv7180_irq, - IRQF_ONESHOT, KBUILD_MODNAME, state); - if (ret) - return ret; +static int adv7182_init(struct adv7180_state *state) +{ + if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) + adv7180_write(state, ADV7180_REG_CSI_SLAVE_ADDR, + ADV7180_DEFAULT_CSI_I2C_ADDR << 1); + + if (state->chip_info->flags & ADV7180_FLAG_I2P) + adv7180_write(state, ADV7180_REG_VPP_SLAVE_ADDR, + ADV7180_DEFAULT_VPP_I2C_ADDR << 1); + + if (state->chip_info->flags & ADV7180_FLAG_V2) { + /* ADI recommended writes for improved video quality */ + adv7180_write(state, 0x0080, 0x51); + adv7180_write(state, 0x0081, 0x51); + adv7180_write(state, 0x0082, 0x68); + } - ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, - ADV7180_ADI_CTRL_IRQ_SPACE); - if (ret < 0) - goto err; + /* ADI required writes */ + if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { + adv7180_write(state, 0x0003, 0x4e); + adv7180_write(state, 0x0004, 0x57); + adv7180_write(state, 0x001d, 0xc0); + } else { + if (state->chip_info->flags & ADV7180_FLAG_V2) + adv7180_write(state, 0x0004, 0x17); + else + adv7180_write(state, 0x0004, 0x07); + adv7180_write(state, 0x0003, 0x0c); + adv7180_write(state, 0x001d, 0x40); + } + + adv7180_write(state, 0x0013, 0x00); + + return 0; +} + +static int adv7182_set_std(struct adv7180_state *state, unsigned int std) +{ + return adv7180_write(state, ADV7182_REG_INPUT_VIDSEL, std << 4); +} + +enum adv7182_input_type { + ADV7182_INPUT_TYPE_CVBS, + ADV7182_INPUT_TYPE_DIFF_CVBS, + ADV7182_INPUT_TYPE_SVIDEO, + ADV7182_INPUT_TYPE_YPBPR, +}; + +static enum adv7182_input_type adv7182_get_input_type(unsigned int input) +{ + switch (input) { + case ADV7182_INPUT_CVBS_AIN1: + case ADV7182_INPUT_CVBS_AIN2: + case ADV7182_INPUT_CVBS_AIN3: + case ADV7182_INPUT_CVBS_AIN4: + case ADV7182_INPUT_CVBS_AIN5: + case ADV7182_INPUT_CVBS_AIN6: + case ADV7182_INPUT_CVBS_AIN7: + case ADV7182_INPUT_CVBS_AIN8: + return ADV7182_INPUT_TYPE_CVBS; + case ADV7182_INPUT_SVIDEO_AIN1_AIN2: + case ADV7182_INPUT_SVIDEO_AIN3_AIN4: + case ADV7182_INPUT_SVIDEO_AIN5_AIN6: + case ADV7182_INPUT_SVIDEO_AIN7_AIN8: + return ADV7182_INPUT_TYPE_SVIDEO; + case ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3: + case ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6: + return ADV7182_INPUT_TYPE_YPBPR; + case ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2: + case ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4: + case ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6: + case ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8: + return ADV7182_INPUT_TYPE_DIFF_CVBS; + default: /* Will never happen */ + return 0; + } +} + +/* ADI recommended writes to registers 0x52, 0x53, 0x54 */ +static unsigned int adv7182_lbias_settings[][3] = { + [ADV7182_INPUT_TYPE_CVBS] = { 0xCB, 0x4E, 0x80 }, + [ADV7182_INPUT_TYPE_DIFF_CVBS] = { 0xC0, 0x4E, 0x80 }, + [ADV7182_INPUT_TYPE_SVIDEO] = { 0x0B, 0xCE, 0x80 }, + [ADV7182_INPUT_TYPE_YPBPR] = { 0x0B, 0x4E, 0xC0 }, +}; + +static unsigned int adv7280_lbias_settings[][3] = { + [ADV7182_INPUT_TYPE_CVBS] = { 0xCD, 0x4E, 0x80 }, + [ADV7182_INPUT_TYPE_DIFF_CVBS] = { 0xC0, 0x4E, 0x80 }, + [ADV7182_INPUT_TYPE_SVIDEO] = { 0x0B, 0xCE, 0x80 }, + [ADV7182_INPUT_TYPE_YPBPR] = { 0x0B, 0x4E, 0xC0 }, +}; + +static int adv7182_select_input(struct adv7180_state *state, unsigned int input) +{ + enum adv7182_input_type input_type; + unsigned int *lbias; + unsigned int i; + int ret; + + ret = adv7180_write(state, ADV7180_REG_INPUT_CONTROL, input); + if (ret) + return ret; + + /* Reset clamp circuitry - ADI recommended writes */ + adv7180_write(state, 0x809c, 0x00); + adv7180_write(state, 0x809c, 0xff); + + input_type = adv7182_get_input_type(input); + + switch (input_type) { + case ADV7182_INPUT_TYPE_CVBS: + case ADV7182_INPUT_TYPE_DIFF_CVBS: + /* ADI recommends to use the SH1 filter */ + adv7180_write(state, 0x0017, 0x41); + break; + default: + adv7180_write(state, 0x0017, 0x01); + break; + } + + if (state->chip_info->flags & ADV7180_FLAG_V2) + lbias = adv7280_lbias_settings[input_type]; + else + lbias = adv7182_lbias_settings[input_type]; + + for (i = 0; i < ARRAY_SIZE(adv7182_lbias_settings[0]); i++) + adv7180_write(state, 0x0052 + i, lbias[i]); + + if (input_type == ADV7182_INPUT_TYPE_DIFF_CVBS) { + /* ADI required writes to make differential CVBS work */ + adv7180_write(state, 0x005f, 0xa8); + adv7180_write(state, 0x005a, 0x90); + adv7180_write(state, 0x0060, 0xb0); + adv7180_write(state, 0x80b6, 0x08); + adv7180_write(state, 0x80c0, 0xa0); + } else { + adv7180_write(state, 0x005f, 0xf0); + adv7180_write(state, 0x005a, 0xd0); + adv7180_write(state, 0x0060, 0x10); + adv7180_write(state, 0x80b6, 0x9c); + adv7180_write(state, 0x80c0, 0x00); + } + + return 0; +} + +static const struct adv7180_chip_info adv7180_info = { + .flags = ADV7180_FLAG_RESET_POWERED, + /* We cannot discriminate between LQFP and 40-pin LFCSP, so accept + * all inputs and let the card driver take care of validation + */ + .valid_input_mask = BIT(ADV7180_INPUT_CVBS_AIN1) | + BIT(ADV7180_INPUT_CVBS_AIN2) | + BIT(ADV7180_INPUT_CVBS_AIN3) | + BIT(ADV7180_INPUT_CVBS_AIN4) | + BIT(ADV7180_INPUT_CVBS_AIN5) | + BIT(ADV7180_INPUT_CVBS_AIN6) | + BIT(ADV7180_INPUT_SVIDEO_AIN1_AIN2) | + BIT(ADV7180_INPUT_SVIDEO_AIN3_AIN4) | + BIT(ADV7180_INPUT_SVIDEO_AIN5_AIN6) | + BIT(ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3) | + BIT(ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6), + .init = adv7180_init, + .set_std = adv7180_set_std, + .select_input = adv7180_select_input, +}; + +static const struct adv7180_chip_info adv7182_info = { + .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | + BIT(ADV7182_INPUT_CVBS_AIN2) | + BIT(ADV7182_INPUT_CVBS_AIN3) | + BIT(ADV7182_INPUT_CVBS_AIN4) | + BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | + BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) | + BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) | + BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) | + BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4), + .init = adv7182_init, + .set_std = adv7182_set_std, + .select_input = adv7182_select_input, +}; + +static const struct adv7180_chip_info adv7280_info = { + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P, + .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | + BIT(ADV7182_INPUT_CVBS_AIN2) | + BIT(ADV7182_INPUT_CVBS_AIN3) | + BIT(ADV7182_INPUT_CVBS_AIN4) | + BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | + BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) | + BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3), + .init = adv7182_init, + .set_std = adv7182_set_std, + .select_input = adv7182_select_input, +}; + +static const struct adv7180_chip_info adv7280_m_info = { + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P, + .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | + BIT(ADV7182_INPUT_CVBS_AIN2) | + BIT(ADV7182_INPUT_CVBS_AIN3) | + BIT(ADV7182_INPUT_CVBS_AIN4) | + BIT(ADV7182_INPUT_CVBS_AIN5) | + BIT(ADV7182_INPUT_CVBS_AIN6) | + BIT(ADV7182_INPUT_CVBS_AIN7) | + BIT(ADV7182_INPUT_CVBS_AIN8) | + BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | + BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) | + BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) | + BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) | + BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) | + BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6), + .init = adv7182_init, + .set_std = adv7182_set_std, + .select_input = adv7182_select_input, +}; + +static const struct adv7180_chip_info adv7281_info = { + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2, + .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | + BIT(ADV7182_INPUT_CVBS_AIN2) | + BIT(ADV7182_INPUT_CVBS_AIN7) | + BIT(ADV7182_INPUT_CVBS_AIN8) | + BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | + BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) | + BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) | + BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8), + .init = adv7182_init, + .set_std = adv7182_set_std, + .select_input = adv7182_select_input, +}; + +static const struct adv7180_chip_info adv7281_m_info = { + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2, + .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | + BIT(ADV7182_INPUT_CVBS_AIN2) | + BIT(ADV7182_INPUT_CVBS_AIN3) | + BIT(ADV7182_INPUT_CVBS_AIN4) | + BIT(ADV7182_INPUT_CVBS_AIN7) | + BIT(ADV7182_INPUT_CVBS_AIN8) | + BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | + BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) | + BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) | + BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) | + BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) | + BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) | + BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8), + .init = adv7182_init, + .set_std = adv7182_set_std, + .select_input = adv7182_select_input, +}; +static const struct adv7180_chip_info adv7281_ma_info = { + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2, + .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | + BIT(ADV7182_INPUT_CVBS_AIN2) | + BIT(ADV7182_INPUT_CVBS_AIN3) | + BIT(ADV7182_INPUT_CVBS_AIN4) | + BIT(ADV7182_INPUT_CVBS_AIN5) | + BIT(ADV7182_INPUT_CVBS_AIN6) | + BIT(ADV7182_INPUT_CVBS_AIN7) | + BIT(ADV7182_INPUT_CVBS_AIN8) | + BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | + BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) | + BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) | + BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) | + BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) | + BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6) | + BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) | + BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) | + BIT(ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6) | + BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8), + .init = adv7182_init, + .set_std = adv7182_set_std, + .select_input = adv7182_select_input, +}; + +static const struct adv7180_chip_info adv7282_info = { + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P, + .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | + BIT(ADV7182_INPUT_CVBS_AIN2) | + BIT(ADV7182_INPUT_CVBS_AIN7) | + BIT(ADV7182_INPUT_CVBS_AIN8) | + BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | + BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) | + BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) | + BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8), + .init = adv7182_init, + .set_std = adv7182_set_std, + .select_input = adv7182_select_input, +}; + +static const struct adv7180_chip_info adv7282_m_info = { + .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P, + .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | + BIT(ADV7182_INPUT_CVBS_AIN2) | + BIT(ADV7182_INPUT_CVBS_AIN3) | + BIT(ADV7182_INPUT_CVBS_AIN4) | + BIT(ADV7182_INPUT_CVBS_AIN7) | + BIT(ADV7182_INPUT_CVBS_AIN8) | + BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | + BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) | + BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) | + BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) | + BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) | + BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8), + .init = adv7182_init, + .set_std = adv7182_set_std, + .select_input = adv7182_select_input, +}; + +static int init_device(struct adv7180_state *state) +{ + int ret; + + mutex_lock(&state->mutex); + + adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES); + usleep_range(2000, 10000); + + ret = state->chip_info->init(state); + if (ret) + goto out_unlock; + + ret = adv7180_program_std(state); + if (ret) + goto out_unlock; + + adv7180_set_field_mode(state); + + /* register for interrupts */ + if (state->irq > 0) { /* config the Interrupt pin to be active low */ - ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI, + ret = adv7180_write(state, ADV7180_REG_ICONF1, ADV7180_ICONF1_ACTIVE_LOW | ADV7180_ICONF1_PSYNC_ONLY); if (ret < 0) - goto err; + goto out_unlock; - ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0); + ret = adv7180_write(state, ADV7180_REG_IMR1, 0); if (ret < 0) - goto err; + goto out_unlock; - ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0); + ret = adv7180_write(state, ADV7180_REG_IMR2, 0); if (ret < 0) - goto err; + goto out_unlock; /* enable AD change interrupts interrupts */ - ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI, + ret = adv7180_write(state, ADV7180_REG_IMR3, ADV7180_IRQ3_AD_CHANGE); if (ret < 0) - goto err; + goto out_unlock; - ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0); + ret = adv7180_write(state, ADV7180_REG_IMR4, 0); if (ret < 0) - goto err; - - ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, - 0); - if (ret < 0) - goto err; + goto out_unlock; } - return 0; +out_unlock: + mutex_unlock(&state->mutex); -err: - free_irq(state->irq, state); return ret; } @@ -616,26 +1172,63 @@ static int adv7180_probe(struct i2c_client *client, client->addr, client->adapter->name); state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL); - if (state == NULL) { - ret = -ENOMEM; - goto err; + if (state == NULL) + return -ENOMEM; + + state->client = client; + state->field = V4L2_FIELD_INTERLACED; + state->chip_info = (struct adv7180_chip_info *)id->driver_data; + + if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { + state->csi_client = i2c_new_dummy(client->adapter, + ADV7180_DEFAULT_CSI_I2C_ADDR); + if (!state->csi_client) + return -ENOMEM; + } + + if (state->chip_info->flags & ADV7180_FLAG_I2P) { + state->vpp_client = i2c_new_dummy(client->adapter, + ADV7180_DEFAULT_VPP_I2C_ADDR); + if (!state->vpp_client) { + ret = -ENOMEM; + goto err_unregister_csi_client; + } } state->irq = client->irq; mutex_init(&state->mutex); state->autodetect = true; - state->powered = true; + if (state->chip_info->flags & ADV7180_FLAG_RESET_POWERED) + state->powered = true; + else + state->powered = false; state->input = 0; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7180_ops); + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; ret = adv7180_init_controls(state); if (ret) - goto err_unreg_subdev; - ret = init_device(client, state); + goto err_unregister_vpp_client; + + state->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER; + ret = media_entity_init(&sd->entity, 1, &state->pad, 0); if (ret) goto err_free_ctrl; + ret = init_device(state); + if (ret) + goto err_media_entity_cleanup; + + if (state->irq) { + ret = request_threaded_irq(client->irq, NULL, adv7180_irq, + IRQF_ONESHOT | IRQF_TRIGGER_FALLING, + KBUILD_MODNAME, state); + if (ret) + goto err_media_entity_cleanup; + } + ret = v4l2_async_register_subdev(sd); if (ret) goto err_free_irq; @@ -645,11 +1238,17 @@ static int adv7180_probe(struct i2c_client *client, err_free_irq: if (state->irq > 0) free_irq(client->irq, state); +err_media_entity_cleanup: + media_entity_cleanup(&sd->entity); err_free_ctrl: adv7180_exit_controls(state); -err_unreg_subdev: +err_unregister_vpp_client: + if (state->chip_info->flags & ADV7180_FLAG_I2P) + i2c_unregister_device(state->vpp_client); +err_unregister_csi_client: + if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) + i2c_unregister_device(state->csi_client); mutex_destroy(&state->mutex); -err: return ret; } @@ -663,15 +1262,32 @@ static int adv7180_remove(struct i2c_client *client) if (state->irq > 0) free_irq(client->irq, state); + media_entity_cleanup(&sd->entity); adv7180_exit_controls(state); + + if (state->chip_info->flags & ADV7180_FLAG_I2P) + i2c_unregister_device(state->vpp_client); + if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) + i2c_unregister_device(state->csi_client); + mutex_destroy(&state->mutex); + return 0; } static const struct i2c_device_id adv7180_id[] = { - {KBUILD_MODNAME, 0}, + { "adv7180", (kernel_ulong_t)&adv7180_info }, + { "adv7182", (kernel_ulong_t)&adv7182_info }, + { "adv7280", (kernel_ulong_t)&adv7280_info }, + { "adv7280-m", (kernel_ulong_t)&adv7280_m_info }, + { "adv7281", (kernel_ulong_t)&adv7281_info }, + { "adv7281-m", (kernel_ulong_t)&adv7281_m_info }, + { "adv7281-ma", (kernel_ulong_t)&adv7281_ma_info }, + { "adv7282", (kernel_ulong_t)&adv7282_info }, + { "adv7282-m", (kernel_ulong_t)&adv7282_m_info }, {}, }; +MODULE_DEVICE_TABLE(i2c, adv7180_id); #ifdef CONFIG_PM_SLEEP static int adv7180_suspend(struct device *dev) @@ -680,7 +1296,7 @@ static int adv7180_suspend(struct device *dev) struct v4l2_subdev *sd = i2c_get_clientdata(client); struct adv7180_state *state = to_state(sd); - return adv7180_set_power(state, client, false); + return adv7180_set_power(state, false); } static int adv7180_resume(struct device *dev) @@ -690,14 +1306,14 @@ static int adv7180_resume(struct device *dev) struct adv7180_state *state = to_state(sd); int ret; - if (state->powered) { - ret = adv7180_set_power(state, client, true); - if (ret) - return ret; - } - ret = init_device(client, state); + ret = init_device(state); if (ret < 0) return ret; + + ret = adv7180_set_power(state, state->powered); + if (ret) + return ret; + return 0; } @@ -708,8 +1324,6 @@ static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume); #define ADV7180_PM_OPS NULL #endif -MODULE_DEVICE_TABLE(i2c, adv7180_id); - static struct i2c_driver adv7180_driver = { .driver = { .owner = THIS_MODULE, diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index e43dd2e2a38a..d228b7c82310 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -333,21 +333,11 @@ static inline struct adv7604_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct adv7604_state, sd); } -static inline unsigned hblanking(const struct v4l2_bt_timings *t) -{ - return V4L2_DV_BT_BLANKING_WIDTH(t); -} - static inline unsigned htotal(const struct v4l2_bt_timings *t) { return V4L2_DV_BT_FRAME_WIDTH(t); } -static inline unsigned vblanking(const struct v4l2_bt_timings *t) -{ - return V4L2_DV_BT_BLANKING_HEIGHT(t); -} - static inline unsigned vtotal(const struct v4l2_bt_timings *t) { return V4L2_DV_BT_FRAME_HEIGHT(t); @@ -466,11 +456,6 @@ static inline int cec_write(struct v4l2_subdev *sd, u8 reg, u8 val) return adv_smbus_write_byte_data(state, ADV7604_PAGE_CEC, reg, val); } -static inline int cec_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) -{ - return cec_write(sd, reg, (cec_read(sd, reg) & ~mask) | val); -} - static inline int infoframe_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); @@ -486,34 +471,6 @@ static inline int infoframe_write(struct v4l2_subdev *sd, u8 reg, u8 val) reg, val); } -static inline int esdp_read(struct v4l2_subdev *sd, u8 reg) -{ - struct adv7604_state *state = to_state(sd); - - return adv_smbus_read_byte_data(state, ADV7604_PAGE_ESDP, reg); -} - -static inline int esdp_write(struct v4l2_subdev *sd, u8 reg, u8 val) -{ - struct adv7604_state *state = to_state(sd); - - return adv_smbus_write_byte_data(state, ADV7604_PAGE_ESDP, reg, val); -} - -static inline int dpp_read(struct v4l2_subdev *sd, u8 reg) -{ - struct adv7604_state *state = to_state(sd); - - return adv_smbus_read_byte_data(state, ADV7604_PAGE_DPP, reg); -} - -static inline int dpp_write(struct v4l2_subdev *sd, u8 reg, u8 val) -{ - struct adv7604_state *state = to_state(sd); - - return adv_smbus_write_byte_data(state, ADV7604_PAGE_DPP, reg, val); -} - static inline int afe_read(struct v4l2_subdev *sd, u8 reg) { struct adv7604_state *state = to_state(sd); @@ -561,32 +518,6 @@ static inline int edid_write(struct v4l2_subdev *sd, u8 reg, u8 val) return adv_smbus_write_byte_data(state, ADV7604_PAGE_EDID, reg, val); } -static inline int edid_read_block(struct v4l2_subdev *sd, unsigned len, u8 *val) -{ - struct adv7604_state *state = to_state(sd); - struct i2c_client *client = state->i2c_clients[ADV7604_PAGE_EDID]; - u8 msgbuf0[1] = { 0 }; - u8 msgbuf1[256]; - struct i2c_msg msg[2] = { - { - .addr = client->addr, - .len = 1, - .buf = msgbuf0 - }, - { - .addr = client->addr, - .flags = I2C_M_RD, - .len = len, - .buf = msgbuf1 - }, - }; - - if (i2c_transfer(client->adapter, msg, 2) < 0) - return -EIO; - memcpy(val, msgbuf1, len); - return 0; -} - static inline int edid_write_block(struct v4l2_subdev *sd, unsigned len, const u8 *val) { @@ -652,13 +583,6 @@ static inline int hdmi_write_clr_set(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 return hdmi_write(sd, reg, (hdmi_read(sd, reg) & ~mask) | val); } -static inline int test_read(struct v4l2_subdev *sd, u8 reg) -{ - struct adv7604_state *state = to_state(sd); - - return adv_smbus_read_byte_data(state, ADV7604_PAGE_TEST, reg); -} - static inline int test_write(struct v4l2_subdev *sd, u8 reg, u8 val) { struct adv7604_state *state = to_state(sd); diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 75d26dfd0939..7c215ee142c4 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -38,6 +38,7 @@ #include <linux/videodev2.h> #include <linux/workqueue.h> #include <linux/v4l2-dv-timings.h> +#include <linux/hdmi.h> #include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-dv-timings.h> @@ -220,21 +221,11 @@ static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) return &container_of(ctrl->handler, struct adv7842_state, hdl)->sd; } -static inline unsigned hblanking(const struct v4l2_bt_timings *t) -{ - return V4L2_DV_BT_BLANKING_WIDTH(t); -} - static inline unsigned htotal(const struct v4l2_bt_timings *t) { return V4L2_DV_BT_FRAME_WIDTH(t); } -static inline unsigned vblanking(const struct v4l2_bt_timings *t) -{ - return V4L2_DV_BT_BLANKING_HEIGHT(t); -} - static inline unsigned vtotal(const struct v4l2_bt_timings *t) { return V4L2_DV_BT_FRAME_HEIGHT(t); @@ -2108,149 +2099,65 @@ static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *e) return err; } -/*********** avi info frame CEA-861-E **************/ -/* TODO move to common library */ - -struct avi_info_frame { - uint8_t f17; - uint8_t y10; - uint8_t a0; - uint8_t b10; - uint8_t s10; - uint8_t c10; - uint8_t m10; - uint8_t r3210; - uint8_t itc; - uint8_t ec210; - uint8_t q10; - uint8_t sc10; - uint8_t f47; - uint8_t vic; - uint8_t yq10; - uint8_t cn10; - uint8_t pr3210; - uint16_t etb; - uint16_t sbb; - uint16_t elb; - uint16_t srb; -}; - -static const char *y10_txt[4] = { - "RGB", - "YCbCr 4:2:2", - "YCbCr 4:4:4", - "Future", -}; - -static const char *c10_txt[4] = { - "No Data", - "SMPTE 170M", - "ITU-R 709", - "Extended Colorimetry information valied", -}; - -static const char *itc_txt[2] = { - "No Data", - "IT content", -}; - -static const char *ec210_txt[8] = { - "xvYCC601", - "xvYCC709", - "sYCC601", - "AdobeYCC601", - "AdobeRGB", - "5 reserved", - "6 reserved", - "7 reserved", +struct adv7842_cfg_read_infoframe { + const char *desc; + u8 present_mask; + u8 head_addr; + u8 payload_addr; }; -static const char *q10_txt[4] = { - "Default", - "Limited Range", - "Full Range", - "Reserved", -}; - -static void parse_avi_infoframe(struct v4l2_subdev *sd, uint8_t *buf, - struct avi_info_frame *avi) -{ - avi->f17 = (buf[1] >> 7) & 0x1; - avi->y10 = (buf[1] >> 5) & 0x3; - avi->a0 = (buf[1] >> 4) & 0x1; - avi->b10 = (buf[1] >> 2) & 0x3; - avi->s10 = buf[1] & 0x3; - avi->c10 = (buf[2] >> 6) & 0x3; - avi->m10 = (buf[2] >> 4) & 0x3; - avi->r3210 = buf[2] & 0xf; - avi->itc = (buf[3] >> 7) & 0x1; - avi->ec210 = (buf[3] >> 4) & 0x7; - avi->q10 = (buf[3] >> 2) & 0x3; - avi->sc10 = buf[3] & 0x3; - avi->f47 = (buf[4] >> 7) & 0x1; - avi->vic = buf[4] & 0x7f; - avi->yq10 = (buf[5] >> 6) & 0x3; - avi->cn10 = (buf[5] >> 4) & 0x3; - avi->pr3210 = buf[5] & 0xf; - avi->etb = buf[6] + 256*buf[7]; - avi->sbb = buf[8] + 256*buf[9]; - avi->elb = buf[10] + 256*buf[11]; - avi->srb = buf[12] + 256*buf[13]; -} - -static void print_avi_infoframe(struct v4l2_subdev *sd) +static void log_infoframe(struct v4l2_subdev *sd, struct adv7842_cfg_read_infoframe *cri) { int i; - uint8_t buf[14]; - u8 avi_len; - u8 avi_ver; - struct avi_info_frame avi; + uint8_t buffer[32]; + union hdmi_infoframe frame; + u8 len; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct device *dev = &client->dev; - if (!(hdmi_read(sd, 0x05) & 0x80)) { - v4l2_info(sd, "receive DVI-D signal (AVI infoframe not supported)\n"); - return; - } - if (!(io_read(sd, 0x60) & 0x01)) { - v4l2_info(sd, "AVI infoframe not received\n"); + if (!(io_read(sd, 0x60) & cri->present_mask)) { + v4l2_info(sd, "%s infoframe not received\n", cri->desc); return; } - if (io_read(sd, 0x88) & 0x10) { - v4l2_info(sd, "AVI infoframe checksum error has occurred earlier\n"); - io_write(sd, 0x8a, 0x10); /* clear AVI_INF_CKS_ERR_RAW */ - if (io_read(sd, 0x88) & 0x10) { - v4l2_info(sd, "AVI infoframe checksum error still present\n"); - io_write(sd, 0x8a, 0x10); /* clear AVI_INF_CKS_ERR_RAW */ - } - } + for (i = 0; i < 3; i++) + buffer[i] = infoframe_read(sd, cri->head_addr + i); - avi_len = infoframe_read(sd, 0xe2); - avi_ver = infoframe_read(sd, 0xe1); - v4l2_info(sd, "AVI infoframe version %d (%d byte)\n", - avi_ver, avi_len); + len = buffer[2] + 1; - if (avi_ver != 0x02) + if (len + 3 > sizeof(buffer)) { + v4l2_err(sd, "%s: invalid %s infoframe length %d\n", __func__, cri->desc, len); return; + } + + for (i = 0; i < len; i++) + buffer[i + 3] = infoframe_read(sd, cri->payload_addr + i); - for (i = 0; i < 14; i++) - buf[i] = infoframe_read(sd, i); + if (hdmi_infoframe_unpack(&frame, buffer) < 0) { + v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, cri->desc); + return; + } - v4l2_info(sd, "\t%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", - buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], - buf[8], buf[9], buf[10], buf[11], buf[12], buf[13]); + hdmi_infoframe_log(KERN_INFO, dev, &frame); +} - parse_avi_infoframe(sd, buf, &avi); +static void adv7842_log_infoframes(struct v4l2_subdev *sd) +{ + int i; + struct adv7842_cfg_read_infoframe cri[] = { + { "AVI", 0x01, 0xe0, 0x00 }, + { "Audio", 0x02, 0xe3, 0x1c }, + { "SDP", 0x04, 0xe6, 0x2a }, + { "Vendor", 0x10, 0xec, 0x54 } + }; - if (avi.vic) - v4l2_info(sd, "\tVIC: %d\n", avi.vic); - if (avi.itc) - v4l2_info(sd, "\t%s\n", itc_txt[avi.itc]); + if (!(hdmi_read(sd, 0x05) & 0x80)) { + v4l2_info(sd, "receive DVI-D signal, no infoframes\n"); + return; + } - if (avi.y10) - v4l2_info(sd, "\t%s %s\n", y10_txt[avi.y10], !avi.c10 ? "" : - (avi.c10 == 0x3 ? ec210_txt[avi.ec210] : c10_txt[avi.c10])); - else - v4l2_info(sd, "\t%s %s\n", y10_txt[avi.y10], q10_txt[avi.q10]); + for (i = 0; i < ARRAY_SIZE(cri); i++) + log_infoframe(sd, &cri[i]); } static const char * const prim_mode_txt[] = { @@ -2464,7 +2371,8 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd) v4l2_info(sd, "Deep color mode: %s\n", deep_color_mode_txt[hdmi_read(sd, 0x0b) >> 6]); - print_avi_infoframe(sd); + adv7842_log_infoframes(sd); + return 0; } diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c index 2820f7c38cba..6ed16e569bbf 100644 --- a/drivers/media/i2c/m5mols/m5mols_core.c +++ b/drivers/media/i2c/m5mols/m5mols_core.c @@ -125,9 +125,9 @@ static u32 m5mols_swap_byte(u8 *data, u8 length) if (length == 1) return *data; else if (length == 2) - return be16_to_cpu(*((u16 *)data)); + return be16_to_cpu(*((__be16 *)data)); else - return be32_to_cpu(*((u32 *)data)); + return be32_to_cpu(*((__be32 *)data)); } /** @@ -454,11 +454,6 @@ static int m5mols_get_version(struct v4l2_subdev *sd) return ret; } - ver->fw = be16_to_cpu(ver->fw); - ver->hw = be16_to_cpu(ver->hw); - ver->param = be16_to_cpu(ver->param); - ver->awb = be16_to_cpu(ver->awb); - v4l2_info(sd, "Manufacturer\t[%s]\n", is_manufacturer(info, REG_SAMSUNG_ELECTRO) ? "Samsung Electro-Machanics" : diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c index 4d9c6bc34265..dcc68ec71732 100644 --- a/drivers/media/i2c/msp3400-driver.c +++ b/drivers/media/i2c/msp3400-driver.c @@ -904,11 +904,3 @@ static struct i2c_driver msp_driver = { }; module_i2c_driver(msp_driver); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c index 45b3fca188ca..76431223f0ff 100644 --- a/drivers/media/i2c/mt9m032.c +++ b/drivers/media/i2c/mt9m032.c @@ -422,22 +422,25 @@ done: return ret; } -static int mt9m032_get_pad_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int mt9m032_get_pad_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct mt9m032 *sensor = to_mt9m032(subdev); + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + mutex_lock(&sensor->lock); - crop->rect = *__mt9m032_get_pad_crop(sensor, fh, crop->which); + sel->r = *__mt9m032_get_pad_crop(sensor, fh, sel->which); mutex_unlock(&sensor->lock); return 0; } -static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int mt9m032_set_pad_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct mt9m032 *sensor = to_mt9m032(subdev); struct v4l2_mbus_framefmt *format; @@ -445,9 +448,12 @@ static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev, struct v4l2_rect rect; int ret = 0; + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + mutex_lock(&sensor->lock); - if (sensor->streaming && crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (sensor->streaming && sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { ret = -EBUSY; goto done; } @@ -455,13 +461,13 @@ static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev, /* Clamp the crop rectangle boundaries and align them to a multiple of 2 * pixels to ensure a GRBG Bayer pattern. */ - rect.left = clamp(ALIGN(crop->rect.left, 2), MT9M032_COLUMN_START_MIN, + rect.left = clamp(ALIGN(sel->r.left, 2), MT9M032_COLUMN_START_MIN, MT9M032_COLUMN_START_MAX); - rect.top = clamp(ALIGN(crop->rect.top, 2), MT9M032_ROW_START_MIN, + rect.top = clamp(ALIGN(sel->r.top, 2), MT9M032_ROW_START_MIN, MT9M032_ROW_START_MAX); - rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), + rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), MT9M032_COLUMN_SIZE_MIN, MT9M032_COLUMN_SIZE_MAX); - rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), + rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), MT9M032_ROW_SIZE_MIN, MT9M032_ROW_SIZE_MAX); rect.width = min_t(unsigned int, rect.width, @@ -469,21 +475,21 @@ static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev, rect.height = min_t(unsigned int, rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - rect.top); - __crop = __mt9m032_get_pad_crop(sensor, fh, crop->which); + __crop = __mt9m032_get_pad_crop(sensor, fh, sel->which); if (rect.width != __crop->width || rect.height != __crop->height) { /* Reset the output image size if the crop rectangle size has * been modified. */ - format = __mt9m032_get_pad_format(sensor, fh, crop->which); + format = __mt9m032_get_pad_format(sensor, fh, sel->which); format->width = rect.width; format->height = rect.height; } *__crop = rect; - crop->rect = rect; + sel->r = rect; - if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) ret = mt9m032_update_geom_timing(sensor); done: @@ -690,8 +696,8 @@ static const struct v4l2_subdev_pad_ops mt9m032_pad_ops = { .enum_frame_size = mt9m032_enum_frame_size, .get_fmt = mt9m032_get_pad_format, .set_fmt = mt9m032_set_pad_format, - .set_crop = mt9m032_set_pad_crop, - .get_crop = mt9m032_get_pad_crop, + .set_selection = mt9m032_set_pad_selection, + .get_selection = mt9m032_get_pad_selection, }; static const struct v4l2_subdev_ops mt9m032_ops = { diff --git a/drivers/media/i2c/mt9p031.c b/drivers/media/i2c/mt9p031.c index edb76bd33d16..e3acae9a2ec3 100644 --- a/drivers/media/i2c/mt9p031.c +++ b/drivers/media/i2c/mt9p031.c @@ -581,37 +581,42 @@ static int mt9p031_set_format(struct v4l2_subdev *subdev, return 0; } -static int mt9p031_get_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int mt9p031_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); - crop->rect = *__mt9p031_get_pad_crop(mt9p031, fh, crop->pad, - crop->which); + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + sel->r = *__mt9p031_get_pad_crop(mt9p031, fh, sel->pad, sel->which); return 0; } -static int mt9p031_set_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int mt9p031_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct mt9p031 *mt9p031 = to_mt9p031(subdev); struct v4l2_mbus_framefmt *__format; struct v4l2_rect *__crop; struct v4l2_rect rect; + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + /* Clamp the crop rectangle boundaries and align them to a multiple of 2 * pixels to ensure a GRBG Bayer pattern. */ - rect.left = clamp(ALIGN(crop->rect.left, 2), MT9P031_COLUMN_START_MIN, + rect.left = clamp(ALIGN(sel->r.left, 2), MT9P031_COLUMN_START_MIN, MT9P031_COLUMN_START_MAX); - rect.top = clamp(ALIGN(crop->rect.top, 2), MT9P031_ROW_START_MIN, + rect.top = clamp(ALIGN(sel->r.top, 2), MT9P031_ROW_START_MIN, MT9P031_ROW_START_MAX); - rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), + rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), MT9P031_WINDOW_WIDTH_MIN, MT9P031_WINDOW_WIDTH_MAX); - rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), + rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), MT9P031_WINDOW_HEIGHT_MIN, MT9P031_WINDOW_HEIGHT_MAX); @@ -620,20 +625,20 @@ static int mt9p031_set_crop(struct v4l2_subdev *subdev, rect.height = min_t(unsigned int, rect.height, MT9P031_PIXEL_ARRAY_HEIGHT - rect.top); - __crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which); + __crop = __mt9p031_get_pad_crop(mt9p031, fh, sel->pad, sel->which); if (rect.width != __crop->width || rect.height != __crop->height) { /* Reset the output image size if the crop rectangle size has * been modified. */ - __format = __mt9p031_get_pad_format(mt9p031, fh, crop->pad, - crop->which); + __format = __mt9p031_get_pad_format(mt9p031, fh, sel->pad, + sel->which); __format->width = rect.width; __format->height = rect.height; } *__crop = rect; - crop->rect = rect; + sel->r = rect; return 0; } @@ -980,8 +985,8 @@ static struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = { .enum_frame_size = mt9p031_enum_frame_size, .get_fmt = mt9p031_get_format, .set_fmt = mt9p031_set_format, - .get_crop = mt9p031_get_crop, - .set_crop = mt9p031_set_crop, + .get_selection = mt9p031_get_selection, + .set_selection = mt9p031_set_selection, }; static struct v4l2_subdev_ops mt9p031_subdev_ops = { diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c index d9e9889b579f..f6ca636b538d 100644 --- a/drivers/media/i2c/mt9t001.c +++ b/drivers/media/i2c/mt9t001.c @@ -401,39 +401,44 @@ static int mt9t001_set_format(struct v4l2_subdev *subdev, return 0; } -static int mt9t001_get_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int mt9t001_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct mt9t001 *mt9t001 = to_mt9t001(subdev); - crop->rect = *__mt9t001_get_pad_crop(mt9t001, fh, crop->pad, - crop->which); + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + sel->r = *__mt9t001_get_pad_crop(mt9t001, fh, sel->pad, sel->which); return 0; } -static int mt9t001_set_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int mt9t001_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct mt9t001 *mt9t001 = to_mt9t001(subdev); struct v4l2_mbus_framefmt *__format; struct v4l2_rect *__crop; struct v4l2_rect rect; + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + /* Clamp the crop rectangle boundaries and align them to a multiple of 2 * pixels. */ - rect.left = clamp(ALIGN(crop->rect.left, 2), + rect.left = clamp(ALIGN(sel->r.left, 2), MT9T001_COLUMN_START_MIN, MT9T001_COLUMN_START_MAX); - rect.top = clamp(ALIGN(crop->rect.top, 2), + rect.top = clamp(ALIGN(sel->r.top, 2), MT9T001_ROW_START_MIN, MT9T001_ROW_START_MAX); - rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), + rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), MT9T001_WINDOW_WIDTH_MIN + 1, MT9T001_WINDOW_WIDTH_MAX + 1); - rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), + rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), MT9T001_WINDOW_HEIGHT_MIN + 1, MT9T001_WINDOW_HEIGHT_MAX + 1); @@ -442,20 +447,20 @@ static int mt9t001_set_crop(struct v4l2_subdev *subdev, rect.height = min_t(unsigned int, rect.height, MT9T001_PIXEL_ARRAY_HEIGHT - rect.top); - __crop = __mt9t001_get_pad_crop(mt9t001, fh, crop->pad, crop->which); + __crop = __mt9t001_get_pad_crop(mt9t001, fh, sel->pad, sel->which); if (rect.width != __crop->width || rect.height != __crop->height) { /* Reset the output image size if the crop rectangle size has * been modified. */ - __format = __mt9t001_get_pad_format(mt9t001, fh, crop->pad, - crop->which); + __format = __mt9t001_get_pad_format(mt9t001, fh, sel->pad, + sel->which); __format->width = rect.width; __format->height = rect.height; } *__crop = rect; - crop->rect = rect; + sel->r = rect; return 0; } @@ -819,8 +824,8 @@ static struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = { .enum_frame_size = mt9t001_enum_frame_size, .get_fmt = mt9t001_get_format, .set_fmt = mt9t001_set_format, - .get_crop = mt9t001_get_crop, - .set_crop = mt9t001_set_crop, + .get_selection = mt9t001_get_selection, + .set_selection = mt9t001_set_selection, }; static struct v4l2_subdev_ops mt9t001_subdev_ops = { diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index 93687c1e4097..bd3f979a4d49 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -552,39 +552,44 @@ static int mt9v032_set_format(struct v4l2_subdev *subdev, return 0; } -static int mt9v032_get_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int mt9v032_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct mt9v032 *mt9v032 = to_mt9v032(subdev); - crop->rect = *__mt9v032_get_pad_crop(mt9v032, fh, crop->pad, - crop->which); + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + sel->r = *__mt9v032_get_pad_crop(mt9v032, fh, sel->pad, sel->which); return 0; } -static int mt9v032_set_crop(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int mt9v032_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct mt9v032 *mt9v032 = to_mt9v032(subdev); struct v4l2_mbus_framefmt *__format; struct v4l2_rect *__crop; struct v4l2_rect rect; + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + /* Clamp the crop rectangle boundaries and align them to a non multiple * of 2 pixels to ensure a GRBG Bayer pattern. */ - rect.left = clamp(ALIGN(crop->rect.left + 1, 2) - 1, + rect.left = clamp(ALIGN(sel->r.left + 1, 2) - 1, MT9V032_COLUMN_START_MIN, MT9V032_COLUMN_START_MAX); - rect.top = clamp(ALIGN(crop->rect.top + 1, 2) - 1, + rect.top = clamp(ALIGN(sel->r.top + 1, 2) - 1, MT9V032_ROW_START_MIN, MT9V032_ROW_START_MAX); - rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), + rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), MT9V032_WINDOW_WIDTH_MIN, MT9V032_WINDOW_WIDTH_MAX); - rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), + rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), MT9V032_WINDOW_HEIGHT_MIN, MT9V032_WINDOW_HEIGHT_MAX); @@ -593,17 +598,17 @@ static int mt9v032_set_crop(struct v4l2_subdev *subdev, rect.height = min_t(unsigned int, rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); - __crop = __mt9v032_get_pad_crop(mt9v032, fh, crop->pad, crop->which); + __crop = __mt9v032_get_pad_crop(mt9v032, fh, sel->pad, sel->which); if (rect.width != __crop->width || rect.height != __crop->height) { /* Reset the output image size if the crop rectangle size has * been modified. */ - __format = __mt9v032_get_pad_format(mt9v032, fh, crop->pad, - crop->which); + __format = __mt9v032_get_pad_format(mt9v032, fh, sel->pad, + sel->which); __format->width = rect.width; __format->height = rect.height; - if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { mt9v032->hratio = 1; mt9v032->vratio = 1; mt9v032_configure_pixel_rate(mt9v032); @@ -611,7 +616,7 @@ static int mt9v032_set_crop(struct v4l2_subdev *subdev, } *__crop = rect; - crop->rect = rect; + sel->r = rect; return 0; } @@ -844,8 +849,8 @@ static struct v4l2_subdev_pad_ops mt9v032_subdev_pad_ops = { .enum_frame_size = mt9v032_enum_frame_size, .get_fmt = mt9v032_get_format, .set_fmt = mt9v032_set_format, - .get_crop = mt9v032_get_crop, - .set_crop = mt9v032_set_crop, + .get_selection = mt9v032_get_selection, + .set_selection = mt9v032_set_selection, }; static struct v4l2_subdev_ops mt9v032_subdev_ops = { diff --git a/drivers/media/i2c/s5k4ecgx.c b/drivers/media/i2c/s5k4ecgx.c index d1c50c9d43ae..70071314789e 100644 --- a/drivers/media/i2c/s5k4ecgx.c +++ b/drivers/media/i2c/s5k4ecgx.c @@ -220,7 +220,7 @@ static int s5k4ecgx_i2c_read(struct i2c_client *client, u16 addr, u16 *val) msg[1].buf = rbuf; ret = i2c_transfer(client->adapter, msg, 2); - *val = be16_to_cpu(*((u16 *)rbuf)); + *val = be16_to_cpu(*((__be16 *)rbuf)); v4l2_dbg(4, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val); @@ -341,7 +341,7 @@ static int s5k4ecgx_load_firmware(struct v4l2_subdev *sd) v4l2_err(sd, "Failed to read firmware %s\n", S5K4ECGX_FIRMWARE); return err; } - regs_num = le32_to_cpu(get_unaligned_le32(fw->data)); + regs_num = get_unaligned_le32(fw->data); v4l2_dbg(3, debug, sd, "FW: %s size %zu register sets %d\n", S5K4ECGX_FIRMWARE, fw->size, regs_num); @@ -351,8 +351,7 @@ static int s5k4ecgx_load_firmware(struct v4l2_subdev *sd) err = -EINVAL; goto fw_out; } - crc_file = le32_to_cpu(get_unaligned_le32(fw->data + - regs_num * FW_RECORD_SIZE)); + crc_file = get_unaligned_le32(fw->data + regs_num * FW_RECORD_SIZE); crc = crc32_le(~0, fw->data, regs_num * FW_RECORD_SIZE); if (crc != crc_file) { v4l2_err(sd, "FW: invalid crc (%#x:%#x)\n", crc, crc_file); @@ -361,9 +360,9 @@ static int s5k4ecgx_load_firmware(struct v4l2_subdev *sd) } ptr = fw->data + FW_RECORD_SIZE; for (i = 1; i < regs_num; i++) { - addr = le32_to_cpu(get_unaligned_le32(ptr)); + addr = get_unaligned_le32(ptr); ptr += sizeof(u32); - val = le16_to_cpu(get_unaligned_le16(ptr)); + val = get_unaligned_le16(ptr); ptr += sizeof(u16); if (addr - addr_inc != 2) err = s5k4ecgx_write(client, addr, val); diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index 60a74d8d38d5..a3d7d0391302 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -353,7 +353,7 @@ static struct v4l2_rect s5k5baf_cis_rect = { * */ static int s5k5baf_fw_parse(struct device *dev, struct s5k5baf_fw **fw, - size_t count, const u16 *data) + size_t count, const __le16 *data) { struct s5k5baf_fw *f; u16 *d, i, *end; @@ -421,6 +421,7 @@ static u16 s5k5baf_i2c_read(struct s5k5baf *state, u16 addr) { struct i2c_client *c = v4l2_get_subdevdata(&state->sd); __be16 w, r; + u16 res; struct i2c_msg msg[] = { { .addr = c->addr, .flags = 0, .len = 2, .buf = (u8 *)&w }, @@ -434,15 +435,15 @@ static u16 s5k5baf_i2c_read(struct s5k5baf *state, u16 addr) w = cpu_to_be16(addr); ret = i2c_transfer(c->adapter, msg, 2); - r = be16_to_cpu(r); + res = be16_to_cpu(r); - v4l2_dbg(3, debug, c, "i2c_read: 0x%04x : 0x%04x\n", addr, r); + v4l2_dbg(3, debug, c, "i2c_read: 0x%04x : 0x%04x\n", addr, res); if (ret != 2) { v4l2_err(c, "i2c_read: error during transfer (%d)\n", ret); state->error = ret; } - return r; + return res; } static void s5k5baf_i2c_write(struct s5k5baf *state, u16 addr, u16 val) @@ -1037,7 +1038,7 @@ static int s5k5baf_load_setfile(struct s5k5baf *state) } ret = s5k5baf_fw_parse(&c->dev, &state->fw, fw->size / 2, - (u16 *)fw->data); + (__le16 *)fw->data); release_firmware(fw); @@ -1793,7 +1794,7 @@ static const struct v4l2_subdev_ops s5k5baf_subdev_ops = { static int s5k5baf_configure_gpios(struct s5k5baf *state) { - static const char const *name[] = { "S5K5BAF_STBY", "S5K5BAF_RST" }; + static const char * const name[] = { "S5K5BAF_STBY", "S5K5BAF_RST" }; struct i2c_client *c = v4l2_get_subdevdata(&state->sd); struct s5k5baf_gpio *g = state->gpios; int ret, i; diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c index 2851581e0061..b1c583239dab 100644 --- a/drivers/media/i2c/s5k6aa.c +++ b/drivers/media/i2c/s5k6aa.c @@ -348,7 +348,7 @@ static int s5k6aa_i2c_read(struct i2c_client *client, u16 addr, u16 *val) msg[1].buf = rbuf; ret = i2c_transfer(client->adapter, msg, 2); - *val = be16_to_cpu(*((u16 *)rbuf)); + *val = be16_to_cpu(*((__be16 *)rbuf)); v4l2_dbg(3, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val); @@ -1161,17 +1161,21 @@ static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return ret; } -static int s5k6aa_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int s5k6aa_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct s5k6aa *s5k6aa = to_s5k6aa(sd); struct v4l2_rect *rect; - memset(crop->reserved, 0, sizeof(crop->reserved)); + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + memset(sel->reserved, 0, sizeof(sel->reserved)); mutex_lock(&s5k6aa->lock); - rect = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which); - crop->rect = *rect; + rect = __s5k6aa_get_crop_rect(s5k6aa, fh, sel->which); + sel->r = *rect; mutex_unlock(&s5k6aa->lock); v4l2_dbg(1, debug, sd, "Current crop rectangle: (%d,%d)/%dx%d\n", @@ -1180,35 +1184,39 @@ static int s5k6aa_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return 0; } -static int s5k6aa_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int s5k6aa_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct s5k6aa *s5k6aa = to_s5k6aa(sd); struct v4l2_mbus_framefmt *mf; unsigned int max_x, max_y; struct v4l2_rect *crop_r; + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + mutex_lock(&s5k6aa->lock); - crop_r = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which); + crop_r = __s5k6aa_get_crop_rect(s5k6aa, fh, sel->which); - if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { mf = &s5k6aa->preset->mbus_fmt; s5k6aa->apply_crop = 1; } else { mf = v4l2_subdev_get_try_format(fh, 0); } - v4l_bound_align_image(&crop->rect.width, mf->width, + v4l_bound_align_image(&sel->r.width, mf->width, S5K6AA_WIN_WIDTH_MAX, 1, - &crop->rect.height, mf->height, + &sel->r.height, mf->height, S5K6AA_WIN_HEIGHT_MAX, 1, 0); - max_x = (S5K6AA_WIN_WIDTH_MAX - crop->rect.width) & ~1; - max_y = (S5K6AA_WIN_HEIGHT_MAX - crop->rect.height) & ~1; + max_x = (S5K6AA_WIN_WIDTH_MAX - sel->r.width) & ~1; + max_y = (S5K6AA_WIN_HEIGHT_MAX - sel->r.height) & ~1; - crop->rect.left = clamp_t(unsigned int, crop->rect.left, 0, max_x); - crop->rect.top = clamp_t(unsigned int, crop->rect.top, 0, max_y); + sel->r.left = clamp_t(unsigned int, sel->r.left, 0, max_x); + sel->r.top = clamp_t(unsigned int, sel->r.top, 0, max_y); - *crop_r = crop->rect; + *crop_r = sel->r; mutex_unlock(&s5k6aa->lock); @@ -1224,8 +1232,8 @@ static const struct v4l2_subdev_pad_ops s5k6aa_pad_ops = { .enum_frame_interval = s5k6aa_enum_frame_interval, .get_fmt = s5k6aa_get_fmt, .set_fmt = s5k6aa_set_fmt, - .get_crop = s5k6aa_get_crop, - .set_crop = s5k6aa_set_crop, + .get_selection = s5k6aa_get_selection, + .set_selection = s5k6aa_set_selection, }; static const struct v4l2_subdev_video_ops s5k6aa_video_ops = { diff --git a/drivers/media/i2c/smiapp-pll.c b/drivers/media/i2c/smiapp-pll.c index e40d9027df3d..e3348db56c46 100644 --- a/drivers/media/i2c/smiapp-pll.c +++ b/drivers/media/i2c/smiapp-pll.c @@ -14,14 +14,9 @@ * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * */ +#include <linux/device.h> #include <linux/gcd.h> #include <linux/lcm.h> #include <linux/module.h> diff --git a/drivers/media/i2c/smiapp-pll.h b/drivers/media/i2c/smiapp-pll.h index e8f035a50c76..b98d143b64e1 100644 --- a/drivers/media/i2c/smiapp-pll.h +++ b/drivers/media/i2c/smiapp-pll.h @@ -14,19 +14,11 @@ * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * */ #ifndef SMIAPP_PLL_H #define SMIAPP_PLL_H -#include <linux/device.h> - /* CSI-2 or CCP-2 */ #define SMIAPP_PLL_BUS_TYPE_CSI2 0x00 #define SMIAPP_PLL_BUS_TYPE_PARALLEL 0x01 diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 0df5070e73c7..d47eff5d3101 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -18,12 +18,6 @@ * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * */ #include <linux/clk.h> @@ -31,11 +25,13 @@ #include <linux/device.h> #include <linux/gpio.h> #include <linux/module.h> +#include <linux/of_gpio.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <linux/smiapp.h> #include <linux/v4l2-mediabus.h> #include <media/v4l2-device.h> +#include <media/v4l2-of.h> #include "smiapp.h" @@ -523,14 +519,12 @@ static const struct v4l2_ctrl_ops smiapp_ctrl_ops = { static int smiapp_init_controls(struct smiapp_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); - unsigned long *valid_link_freqs = &sensor->valid_link_freqs[ - sensor->csi_format->compressed - SMIAPP_COMPRESSED_BASE]; - unsigned int max, i; int rval; rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 12); if (rval) return rval; + sensor->pixel_array->ctrl_handler.lock = &sensor->mutex; sensor->analog_gain = v4l2_ctrl_new_std( @@ -576,21 +570,11 @@ static int smiapp_init_controls(struct smiapp_sensor *sensor) ARRAY_SIZE(smiapp_test_patterns) - 1, 0, 0, smiapp_test_patterns); - for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++) { - int max_value = (1 << sensor->csi_format->width) - 1; - sensor->test_data[i] = - v4l2_ctrl_new_std( - &sensor->pixel_array->ctrl_handler, - &smiapp_ctrl_ops, V4L2_CID_TEST_PATTERN_RED + i, - 0, max_value, 1, max_value); - } - if (sensor->pixel_array->ctrl_handler.error) { dev_err(&client->dev, "pixel array controls initialization failed (%d)\n", sensor->pixel_array->ctrl_handler.error); - rval = sensor->pixel_array->ctrl_handler.error; - goto error; + return sensor->pixel_array->ctrl_handler.error; } sensor->pixel_array->sd.ctrl_handler = @@ -600,15 +584,9 @@ static int smiapp_init_controls(struct smiapp_sensor *sensor) rval = v4l2_ctrl_handler_init(&sensor->src->ctrl_handler, 0); if (rval) - goto error; - sensor->src->ctrl_handler.lock = &sensor->mutex; - - for (max = 0; sensor->platform_data->op_sys_clock[max + 1]; max++); + return rval; - sensor->link_freq = v4l2_ctrl_new_int_menu( - &sensor->src->ctrl_handler, &smiapp_ctrl_ops, - V4L2_CID_LINK_FREQ, __fls(*valid_link_freqs), - __ffs(*valid_link_freqs), sensor->platform_data->op_sys_clock); + sensor->src->ctrl_handler.lock = &sensor->mutex; sensor->pixel_rate_csi = v4l2_ctrl_new_std( &sensor->src->ctrl_handler, &smiapp_ctrl_ops, @@ -618,20 +596,41 @@ static int smiapp_init_controls(struct smiapp_sensor *sensor) dev_err(&client->dev, "src controls initialization failed (%d)\n", sensor->src->ctrl_handler.error); - rval = sensor->src->ctrl_handler.error; - goto error; + return sensor->src->ctrl_handler.error; } - sensor->src->sd.ctrl_handler = - &sensor->src->ctrl_handler; + sensor->src->sd.ctrl_handler = &sensor->src->ctrl_handler; return 0; +} + +/* + * For controls that require information on available media bus codes + * and linke frequencies. + */ +static int smiapp_init_late_controls(struct smiapp_sensor *sensor) +{ + unsigned long *valid_link_freqs = &sensor->valid_link_freqs[ + sensor->csi_format->compressed - SMIAPP_COMPRESSED_BASE]; + unsigned int max, i; -error: - v4l2_ctrl_handler_free(&sensor->pixel_array->ctrl_handler); - v4l2_ctrl_handler_free(&sensor->src->ctrl_handler); + for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++) { + int max_value = (1 << sensor->csi_format->width) - 1; - return rval; + sensor->test_data[i] = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, + &smiapp_ctrl_ops, V4L2_CID_TEST_PATTERN_RED + i, + 0, max_value, 1, max_value); + } + + for (max = 0; sensor->platform_data->op_sys_clock[max + 1]; max++); + + sensor->link_freq = v4l2_ctrl_new_int_menu( + &sensor->src->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_LINK_FREQ, __fls(*valid_link_freqs), + __ffs(*valid_link_freqs), sensor->platform_data->op_sys_clock); + + return sensor->src->ctrl_handler.error; } static void smiapp_free_controls(struct smiapp_sensor *sensor) @@ -1487,7 +1486,7 @@ static int smiapp_start_streaming(struct smiapp_sensor *sensor) if (rval < 0) goto out; - if ((sensor->flash_capability & + if ((sensor->limits[SMIAPP_LIMIT_FLASH_MODE_CAPABILITY] & (SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE | SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE)) && sensor->platform_data->strobe_setup != NULL && @@ -2338,10 +2337,9 @@ static DEVICE_ATTR(ident, S_IRUGO, smiapp_sysfs_ident_read, NULL); * V4L2 subdev core operations */ -static int smiapp_identify_module(struct v4l2_subdev *subdev) +static int smiapp_identify_module(struct smiapp_sensor *sensor) { - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); struct smiapp_module_info *minfo = &sensor->minfo; unsigned int i; int rval = 0; @@ -2464,8 +2462,6 @@ static int smiapp_identify_module(struct v4l2_subdev *subdev) minfo->name, minfo->manufacturer_id, minfo->model_id, minfo->revision_number_major); - strlcpy(subdev->name, sensor->minfo.name, sizeof(subdev->name)); - return 0; } @@ -2473,13 +2469,71 @@ static const struct v4l2_subdev_ops smiapp_ops; static const struct v4l2_subdev_internal_ops smiapp_internal_ops; static const struct media_entity_operations smiapp_entity_ops; -static int smiapp_registered(struct v4l2_subdev *subdev) +static int smiapp_register_subdevs(struct smiapp_sensor *sensor) { - struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); - struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct smiapp_subdev *ssds[] = { + sensor->scaler, + sensor->binner, + sensor->pixel_array, + }; + unsigned int i; + int rval; + + for (i = 0; i < SMIAPP_SUBDEVS - 1; i++) { + struct smiapp_subdev *this = ssds[i + 1]; + struct smiapp_subdev *last = ssds[i]; + + if (!last) + continue; + + rval = media_entity_init(&this->sd.entity, + this->npads, this->pads, 0); + if (rval) { + dev_err(&client->dev, + "media_entity_init failed\n"); + return rval; + } + + rval = media_entity_create_link(&this->sd.entity, + this->source_pad, + &last->sd.entity, + last->sink_pad, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (rval) { + dev_err(&client->dev, + "media_entity_create_link failed\n"); + return rval; + } + + rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, + &this->sd); + if (rval) { + dev_err(&client->dev, + "v4l2_device_register_subdev failed\n"); + return rval; + } + } + + return 0; +} + +static void smiapp_cleanup(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + + device_remove_file(&client->dev, &dev_attr_nvm); + device_remove_file(&client->dev, &dev_attr_ident); + + smiapp_free_controls(sensor); +} + +static int smiapp_init(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); struct smiapp_pll *pll = &sensor->pll; struct smiapp_subdev *last = NULL; - u32 tmp; unsigned int i; int rval; @@ -2490,7 +2544,7 @@ static int smiapp_registered(struct v4l2_subdev *subdev) } if (!sensor->platform_data->set_xclk) { - sensor->ext_clk = devm_clk_get(&client->dev, "ext_clk"); + sensor->ext_clk = devm_clk_get(&client->dev, NULL); if (IS_ERR(sensor->ext_clk)) { dev_err(&client->dev, "could not get clock\n"); return PTR_ERR(sensor->ext_clk); @@ -2522,7 +2576,7 @@ static int smiapp_registered(struct v4l2_subdev *subdev) if (rval) return -ENODEV; - rval = smiapp_identify_module(subdev); + rval = smiapp_identify_module(sensor); if (rval) { rval = -ENODEV; goto out_power_off; @@ -2602,13 +2656,13 @@ static int smiapp_registered(struct v4l2_subdev *subdev) if (sensor->nvm == NULL) { dev_err(&client->dev, "nvm buf allocation failed\n"); rval = -ENOMEM; - goto out_ident_release; + goto out_cleanup; } if (device_create_file(&client->dev, &dev_attr_nvm) != 0) { dev_err(&client->dev, "sysfs nvm entry failed\n"); rval = -EBUSY; - goto out_ident_release; + goto out_cleanup; } } @@ -2643,18 +2697,11 @@ static int smiapp_registered(struct v4l2_subdev *subdev) pll->bus_type = SMIAPP_PLL_BUS_TYPE_CSI2; pll->csi2.lanes = sensor->platform_data->lanes; pll->ext_clk_freq_hz = sensor->platform_data->ext_clk; - pll->flags = smiapp_call_quirk(sensor, pll_flags); pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; /* Profile 0 sensors have no separate OP clock branch. */ if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS; - rval = smiapp_get_mbus_formats(sensor); - if (rval) { - rval = -ENODEV; - goto out_nvm_release; - } - for (i = 0; i < SMIAPP_SUBDEVS; i++) { struct { struct smiapp_subdev *ssd; @@ -2711,34 +2758,6 @@ static int smiapp_registered(struct v4l2_subdev *subdev) this->sd.owner = THIS_MODULE; v4l2_set_subdevdata(&this->sd, client); - rval = media_entity_init(&this->sd.entity, - this->npads, this->pads, 0); - if (rval) { - dev_err(&client->dev, - "media_entity_init failed\n"); - goto out_nvm_release; - } - - rval = media_entity_create_link(&this->sd.entity, - this->source_pad, - &last->sd.entity, - last->sink_pad, - MEDIA_LNK_FL_ENABLED | - MEDIA_LNK_FL_IMMUTABLE); - if (rval) { - dev_err(&client->dev, - "media_entity_create_link failed\n"); - goto out_nvm_release; - } - - rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, - &this->sd); - if (rval) { - dev_err(&client->dev, - "v4l2_device_register_subdev failed\n"); - goto out_nvm_release; - } - last = this; } @@ -2750,40 +2769,66 @@ static int smiapp_registered(struct v4l2_subdev *subdev) smiapp_read_frame_fmt(sensor); rval = smiapp_init_controls(sensor); if (rval < 0) - goto out_nvm_release; + goto out_cleanup; + + rval = smiapp_call_quirk(sensor, init); + if (rval) + goto out_cleanup; + + rval = smiapp_get_mbus_formats(sensor); + if (rval) { + rval = -ENODEV; + goto out_cleanup; + } + + rval = smiapp_init_late_controls(sensor); + if (rval) { + rval = -ENODEV; + goto out_cleanup; + } mutex_lock(&sensor->mutex); rval = smiapp_update_mode(sensor); mutex_unlock(&sensor->mutex); if (rval) { dev_err(&client->dev, "update mode failed\n"); - goto out_nvm_release; + goto out_cleanup; } sensor->streaming = false; sensor->dev_init_done = true; - /* check flash capability */ - rval = smiapp_read(sensor, SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, &tmp); - sensor->flash_capability = tmp; - if (rval) - goto out_nvm_release; - smiapp_power_off(sensor); return 0; -out_nvm_release: - device_remove_file(&client->dev, &dev_attr_nvm); - -out_ident_release: - device_remove_file(&client->dev, &dev_attr_ident); +out_cleanup: + smiapp_cleanup(sensor); out_power_off: smiapp_power_off(sensor); return rval; } +static int smiapp_registered(struct v4l2_subdev *subdev) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + int rval; + + if (!client->dev.of_node) { + rval = smiapp_init(sensor); + if (rval) + return rval; + } + + rval = smiapp_register_subdevs(sensor); + if (rval) + smiapp_cleanup(sensor); + + return rval; +} + static int smiapp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct smiapp_subdev *ssd = to_smiapp_subdev(sd); @@ -2927,19 +2972,125 @@ static int smiapp_resume(struct device *dev) #endif /* CONFIG_PM */ +static struct smiapp_platform_data *smiapp_get_pdata(struct device *dev) +{ + struct smiapp_platform_data *pdata; + struct v4l2_of_endpoint bus_cfg; + struct device_node *ep; + struct property *prop; + __be32 *val; + uint32_t asize; +#ifdef CONFIG_OF + unsigned int i; +#endif + int rval; + + if (!dev->of_node) + return dev->platform_data; + + ep = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!ep) + return NULL; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + rval = -ENOMEM; + goto out_err; + } + + v4l2_of_parse_endpoint(ep, &bus_cfg); + + switch (bus_cfg.bus_type) { + case V4L2_MBUS_CSI2: + pdata->csi_signalling_mode = SMIAPP_CSI_SIGNALLING_MODE_CSI2; + break; + /* FIXME: add CCP2 support. */ + default: + rval = -EINVAL; + goto out_err; + } + + pdata->lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; + dev_dbg(dev, "lanes %u\n", pdata->lanes); + + /* xshutdown GPIO is optional */ + pdata->xshutdown = of_get_named_gpio(dev->of_node, "reset-gpios", 0); + + /* NVM size is not mandatory */ + of_property_read_u32(dev->of_node, "nokia,nvm-size", + &pdata->nvm_size); + + rval = of_property_read_u32(dev->of_node, "clock-frequency", + &pdata->ext_clk); + if (rval) { + dev_warn(dev, "can't get clock-frequency\n"); + goto out_err; + } + + dev_dbg(dev, "reset %d, nvm %d, clk %d, csi %d\n", pdata->xshutdown, + pdata->nvm_size, pdata->ext_clk, pdata->csi_signalling_mode); + + rval = of_get_property( + dev->of_node, "link-frequencies", &asize) ? 0 : -ENOENT; + if (rval) { + dev_warn(dev, "can't get link-frequencies array size\n"); + goto out_err; + } + + pdata->op_sys_clock = devm_kzalloc(dev, asize, GFP_KERNEL); + if (!pdata->op_sys_clock) { + rval = -ENOMEM; + goto out_err; + } + + asize /= sizeof(*pdata->op_sys_clock); + /* + * Read a 64-bit array --- this will be replaced with a + * of_property_read_u64_array() once it's merged. + */ + prop = of_find_property(dev->of_node, "link-frequencies", NULL); + if (!prop) + goto out_err; + if (!prop->value) + goto out_err; + if (asize * sizeof(*pdata->op_sys_clock) > prop->length) + goto out_err; + val = prop->value; + if (IS_ERR(val)) + goto out_err; + +#ifdef CONFIG_OF + for (i = 0; i < asize; i++) + pdata->op_sys_clock[i] = of_read_number(val + i * 2, 2); +#endif + + for (; asize > 0; asize--) + dev_dbg(dev, "freq %d: %lld\n", asize - 1, + pdata->op_sys_clock[asize - 1]); + + of_node_put(ep); + return pdata; + +out_err: + of_node_put(ep); + return NULL; +} + static int smiapp_probe(struct i2c_client *client, const struct i2c_device_id *devid) { struct smiapp_sensor *sensor; + struct smiapp_platform_data *pdata = smiapp_get_pdata(&client->dev); + int rval; - if (client->dev.platform_data == NULL) + if (pdata == NULL) return -ENODEV; sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); if (sensor == NULL) return -ENOMEM; - sensor->platform_data = client->dev.platform_data; + sensor->platform_data = pdata; mutex_init(&sensor->mutex); mutex_init(&sensor->power_mutex); sensor->src = &sensor->ssds[sensor->ssds_used]; @@ -2950,8 +3101,27 @@ static int smiapp_probe(struct i2c_client *client, sensor->src->sensor = sensor; sensor->src->pads[0].flags = MEDIA_PAD_FL_SOURCE; - return media_entity_init(&sensor->src->sd.entity, 2, + rval = media_entity_init(&sensor->src->sd.entity, 2, sensor->src->pads, 0); + if (rval < 0) + return rval; + + if (client->dev.of_node) { + rval = smiapp_init(sensor); + if (rval) + goto out_media_entity_cleanup; + } + + rval = v4l2_async_register_subdev(&sensor->src->sd); + if (rval < 0) + goto out_media_entity_cleanup; + + return 0; + +out_media_entity_cleanup: + media_entity_cleanup(&sensor->src->sd.entity); + + return rval; } static int smiapp_remove(struct i2c_client *client) @@ -2960,6 +3130,8 @@ static int smiapp_remove(struct i2c_client *client) struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); unsigned int i; + v4l2_async_unregister_subdev(subdev); + if (sensor->power_count) { if (gpio_is_valid(sensor->platform_data->xshutdown)) gpio_set_value(sensor->platform_data->xshutdown, 0); @@ -2970,19 +3142,20 @@ static int smiapp_remove(struct i2c_client *client) sensor->power_count = 0; } - device_remove_file(&client->dev, &dev_attr_ident); - if (sensor->nvm) - device_remove_file(&client->dev, &dev_attr_nvm); - for (i = 0; i < sensor->ssds_used; i++) { v4l2_device_unregister_subdev(&sensor->ssds[i].sd); media_entity_cleanup(&sensor->ssds[i].sd.entity); } - smiapp_free_controls(sensor); + smiapp_cleanup(sensor); return 0; } +static const struct of_device_id smiapp_of_table[] = { + { .compatible = "nokia,smia" }, + { }, +}; + static const struct i2c_device_id smiapp_id_table[] = { { SMIAPP_NAME, 0 }, { }, @@ -2996,6 +3169,7 @@ static const struct dev_pm_ops smiapp_pm_ops = { static struct i2c_driver smiapp_i2c_driver = { .driver = { + .of_match_table = smiapp_of_table, .name = SMIAPP_NAME, .pm = &smiapp_pm_ops, }, diff --git a/drivers/media/i2c/smiapp/smiapp-limits.c b/drivers/media/i2c/smiapp/smiapp-limits.c index 847cb235e198..784b114d3f8b 100644 --- a/drivers/media/i2c/smiapp/smiapp-limits.c +++ b/drivers/media/i2c/smiapp/smiapp-limits.c @@ -14,12 +14,6 @@ * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * */ #include "smiapp.h" diff --git a/drivers/media/i2c/smiapp/smiapp-limits.h b/drivers/media/i2c/smiapp/smiapp-limits.h index 343e9c3827fc..b20124862a14 100644 --- a/drivers/media/i2c/smiapp/smiapp-limits.h +++ b/drivers/media/i2c/smiapp/smiapp-limits.h @@ -14,12 +14,6 @@ * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * */ #define SMIAPP_LIMIT_ANALOGUE_GAIN_CAPABILITY 0 diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.c b/drivers/media/i2c/smiapp/smiapp-quirk.c index e0bee8752122..abf9ea7a0fb7 100644 --- a/drivers/media/i2c/smiapp/smiapp-quirk.c +++ b/drivers/media/i2c/smiapp/smiapp-quirk.c @@ -14,12 +14,6 @@ * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * */ #include <linux/delay.h> @@ -220,9 +214,11 @@ static int jt8ev1_post_streamoff(struct smiapp_sensor *sensor) return smiapp_write_8(sensor, 0x3328, 0x80); } -static unsigned long jt8ev1_pll_flags(struct smiapp_sensor *sensor) +static int jt8ev1_init(struct smiapp_sensor *sensor) { - return SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE; + sensor->pll.flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE; + + return 0; } const struct smiapp_quirk smiapp_jt8ev1_quirk = { @@ -230,7 +226,7 @@ const struct smiapp_quirk smiapp_jt8ev1_quirk = { .post_poweron = jt8ev1_post_poweron, .pre_streamon = jt8ev1_pre_streamon, .post_streamoff = jt8ev1_post_streamoff, - .pll_flags = jt8ev1_pll_flags, + .init = jt8ev1_init, }; static int tcm8500md_limits(struct smiapp_sensor *sensor) diff --git a/drivers/media/i2c/smiapp/smiapp-quirk.h b/drivers/media/i2c/smiapp/smiapp-quirk.h index 46e9ea8bfa08..dac5566a2f7a 100644 --- a/drivers/media/i2c/smiapp/smiapp-quirk.h +++ b/drivers/media/i2c/smiapp/smiapp-quirk.h @@ -14,12 +14,6 @@ * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * */ #ifndef __SMIAPP_QUIRK__ @@ -35,6 +29,9 @@ struct smiapp_sensor; * @post_poweron: Called always after the sensor has been fully powered on. * @pre_streamon: Called just before streaming is enabled. * @post_streamon: Called right after stopping streaming. + * @pll_flags: Return flags for the PLL calculator. + * @init: Quirk initialisation, called the last in probe(). This is + * also appropriate for adding sensor specific controls, for instance. * @reg_access: Register access quirk. The quirk may divert the access * to another register, or no register at all. * @@ -53,6 +50,7 @@ struct smiapp_quirk { int (*pre_streamon)(struct smiapp_sensor *sensor); int (*post_streamoff)(struct smiapp_sensor *sensor); unsigned long (*pll_flags)(struct smiapp_sensor *sensor); + int (*init)(struct smiapp_sensor *sensor); int (*reg_access)(struct smiapp_sensor *sensor, bool write, u32 *reg, u32 *val); unsigned long flags; @@ -74,14 +72,14 @@ void smiapp_replace_limit(struct smiapp_sensor *sensor, .val = _val, \ } -#define smiapp_call_quirk(_sensor, _quirk, ...) \ - (_sensor->minfo.quirk && \ - _sensor->minfo.quirk->_quirk ? \ - _sensor->minfo.quirk->_quirk(_sensor, ##__VA_ARGS__) : 0) +#define smiapp_call_quirk(sensor, _quirk, ...) \ + ((sensor)->minfo.quirk && \ + (sensor)->minfo.quirk->_quirk ? \ + (sensor)->minfo.quirk->_quirk(sensor, ##__VA_ARGS__) : 0) -#define smiapp_needs_quirk(_sensor, _quirk) \ - (_sensor->minfo.quirk ? \ - _sensor->minfo.quirk->flags & _quirk : 0) +#define smiapp_needs_quirk(sensor, _quirk) \ + ((sensor)->minfo.quirk ? \ + (sensor)->minfo.quirk->flags & _quirk : 0) extern const struct smiapp_quirk smiapp_jt8ev1_quirk; extern const struct smiapp_quirk smiapp_imx125es_quirk; diff --git a/drivers/media/i2c/smiapp/smiapp-reg-defs.h b/drivers/media/i2c/smiapp/smiapp-reg-defs.h index c488ef028074..f928d4cc8e26 100644 --- a/drivers/media/i2c/smiapp/smiapp-reg-defs.h +++ b/drivers/media/i2c/smiapp/smiapp-reg-defs.h @@ -14,12 +14,6 @@ * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * */ #define SMIAPP_REG_MK_U8(r) ((SMIAPP_REG_8BIT << 16) | (r)) #define SMIAPP_REG_MK_U16(r) ((SMIAPP_REG_16BIT << 16) | (r)) diff --git a/drivers/media/i2c/smiapp/smiapp-reg.h b/drivers/media/i2c/smiapp/smiapp-reg.h index b0dcbb8fa5e2..4c8b40614969 100644 --- a/drivers/media/i2c/smiapp/smiapp-reg.h +++ b/drivers/media/i2c/smiapp/smiapp-reg.h @@ -14,12 +14,6 @@ * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * */ #ifndef __SMIAPP_REG_H_ diff --git a/drivers/media/i2c/smiapp/smiapp-regs.c b/drivers/media/i2c/smiapp/smiapp-regs.c index a2098007fb70..6b6c20b61397 100644 --- a/drivers/media/i2c/smiapp/smiapp-regs.c +++ b/drivers/media/i2c/smiapp/smiapp-regs.c @@ -14,12 +14,6 @@ * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * */ #include <linux/delay.h> diff --git a/drivers/media/i2c/smiapp/smiapp-regs.h b/drivers/media/i2c/smiapp/smiapp-regs.h index 35521125a2cc..6dd0e499c845 100644 --- a/drivers/media/i2c/smiapp/smiapp-regs.h +++ b/drivers/media/i2c/smiapp/smiapp-regs.h @@ -14,12 +14,6 @@ * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * */ #ifndef SMIAPP_REGS_H diff --git a/drivers/media/i2c/smiapp/smiapp.h b/drivers/media/i2c/smiapp/smiapp.h index f88f8ec344d3..ed010a8a49d7 100644 --- a/drivers/media/i2c/smiapp/smiapp.h +++ b/drivers/media/i2c/smiapp/smiapp.h @@ -14,12 +14,6 @@ * 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., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * */ #ifndef __SMIAPP_PRIV_H_ @@ -222,7 +216,6 @@ struct smiapp_sensor { u8 scaling_mode; u8 hvflip_inv_mask; /* H/VFLIP inversion due to sensor orientation */ - u8 flash_capability; u8 frame_skip; int power_count; diff --git a/drivers/media/i2c/soc_camera/ov2640.c b/drivers/media/i2c/soc_camera/ov2640.c index 6f2dd9093d94..1fdce2f6f880 100644 --- a/drivers/media/i2c/soc_camera/ov2640.c +++ b/drivers/media/i2c/soc_camera/ov2640.c @@ -25,6 +25,7 @@ #include <media/v4l2-clk.h> #include <media/v4l2-subdev.h> #include <media/v4l2-ctrls.h> +#include <media/v4l2-image-sizes.h> #define VAL_SET(x, mask, rshift, lshift) \ ((((x) >> rshift) & mask) << lshift) @@ -268,33 +269,10 @@ struct regval_list { u8 value; }; -/* Supported resolutions */ -enum ov2640_width { - W_QCIF = 176, - W_QVGA = 320, - W_CIF = 352, - W_VGA = 640, - W_SVGA = 800, - W_XGA = 1024, - W_SXGA = 1280, - W_UXGA = 1600, -}; - -enum ov2640_height { - H_QCIF = 144, - H_QVGA = 240, - H_CIF = 288, - H_VGA = 480, - H_SVGA = 600, - H_XGA = 768, - H_SXGA = 1024, - H_UXGA = 1200, -}; - struct ov2640_win_size { char *name; - enum ov2640_width width; - enum ov2640_height height; + u32 width; + u32 height; const struct regval_list *regs; }; @@ -495,17 +473,17 @@ static const struct regval_list ov2640_init_regs[] = { static const struct regval_list ov2640_size_change_preamble_regs[] = { { BANK_SEL, BANK_SEL_DSP }, { RESET, RESET_DVP }, - { HSIZE8, HSIZE8_SET(W_UXGA) }, - { VSIZE8, VSIZE8_SET(H_UXGA) }, + { HSIZE8, HSIZE8_SET(UXGA_WIDTH) }, + { VSIZE8, VSIZE8_SET(UXGA_HEIGHT) }, { CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN | CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN }, - { HSIZE, HSIZE_SET(W_UXGA) }, - { VSIZE, VSIZE_SET(H_UXGA) }, + { HSIZE, HSIZE_SET(UXGA_WIDTH) }, + { VSIZE, VSIZE_SET(UXGA_HEIGHT) }, { XOFFL, XOFFL_SET(0) }, { YOFFL, YOFFL_SET(0) }, - { VHYX, VHYX_HSIZE_SET(W_UXGA) | VHYX_VSIZE_SET(H_UXGA) | + { VHYX, VHYX_HSIZE_SET(UXGA_WIDTH) | VHYX_VSIZE_SET(UXGA_HEIGHT) | VHYX_XOFF_SET(0) | VHYX_YOFF_SET(0)}, - { TEST, TEST_HSIZE_SET(W_UXGA) }, + { TEST, TEST_HSIZE_SET(UXGA_WIDTH) }, ENDMARKER, }; @@ -519,45 +497,45 @@ static const struct regval_list ov2640_size_change_preamble_regs[] = { { RESET, 0x00} static const struct regval_list ov2640_qcif_regs[] = { - PER_SIZE_REG_SEQ(W_QCIF, H_QCIF, 3, 3, 4), + PER_SIZE_REG_SEQ(QCIF_WIDTH, QCIF_HEIGHT, 3, 3, 4), ENDMARKER, }; static const struct regval_list ov2640_qvga_regs[] = { - PER_SIZE_REG_SEQ(W_QVGA, H_QVGA, 2, 2, 4), + PER_SIZE_REG_SEQ(QVGA_WIDTH, QVGA_HEIGHT, 2, 2, 4), ENDMARKER, }; static const struct regval_list ov2640_cif_regs[] = { - PER_SIZE_REG_SEQ(W_CIF, H_CIF, 2, 2, 8), + PER_SIZE_REG_SEQ(CIF_WIDTH, CIF_HEIGHT, 2, 2, 8), ENDMARKER, }; static const struct regval_list ov2640_vga_regs[] = { - PER_SIZE_REG_SEQ(W_VGA, H_VGA, 0, 0, 2), + PER_SIZE_REG_SEQ(VGA_WIDTH, VGA_HEIGHT, 0, 0, 2), ENDMARKER, }; static const struct regval_list ov2640_svga_regs[] = { - PER_SIZE_REG_SEQ(W_SVGA, H_SVGA, 1, 1, 2), + PER_SIZE_REG_SEQ(SVGA_WIDTH, SVGA_HEIGHT, 1, 1, 2), ENDMARKER, }; static const struct regval_list ov2640_xga_regs[] = { - PER_SIZE_REG_SEQ(W_XGA, H_XGA, 0, 0, 2), + PER_SIZE_REG_SEQ(XGA_WIDTH, XGA_HEIGHT, 0, 0, 2), { CTRLI, 0x00}, ENDMARKER, }; static const struct regval_list ov2640_sxga_regs[] = { - PER_SIZE_REG_SEQ(W_SXGA, H_SXGA, 0, 0, 2), + PER_SIZE_REG_SEQ(SXGA_WIDTH, SXGA_HEIGHT, 0, 0, 2), { CTRLI, 0x00}, { R_DVP_SP, 2 | R_DVP_SP_AUTO_MODE }, ENDMARKER, }; static const struct regval_list ov2640_uxga_regs[] = { - PER_SIZE_REG_SEQ(W_UXGA, H_UXGA, 0, 0, 0), + PER_SIZE_REG_SEQ(UXGA_WIDTH, UXGA_HEIGHT, 0, 0, 0), { CTRLI, 0x00}, { R_DVP_SP, 0 | R_DVP_SP_AUTO_MODE }, ENDMARKER, @@ -567,14 +545,14 @@ static const struct regval_list ov2640_uxga_regs[] = { {.name = n, .width = w , .height = h, .regs = r } static const struct ov2640_win_size ov2640_supported_win_sizes[] = { - OV2640_SIZE("QCIF", W_QCIF, H_QCIF, ov2640_qcif_regs), - OV2640_SIZE("QVGA", W_QVGA, H_QVGA, ov2640_qvga_regs), - OV2640_SIZE("CIF", W_CIF, H_CIF, ov2640_cif_regs), - OV2640_SIZE("VGA", W_VGA, H_VGA, ov2640_vga_regs), - OV2640_SIZE("SVGA", W_SVGA, H_SVGA, ov2640_svga_regs), - OV2640_SIZE("XGA", W_XGA, H_XGA, ov2640_xga_regs), - OV2640_SIZE("SXGA", W_SXGA, H_SXGA, ov2640_sxga_regs), - OV2640_SIZE("UXGA", W_UXGA, H_UXGA, ov2640_uxga_regs), + OV2640_SIZE("QCIF", QCIF_WIDTH, QCIF_HEIGHT, ov2640_qcif_regs), + OV2640_SIZE("QVGA", QVGA_WIDTH, QVGA_HEIGHT, ov2640_qvga_regs), + OV2640_SIZE("CIF", CIF_WIDTH, CIF_HEIGHT, ov2640_cif_regs), + OV2640_SIZE("VGA", VGA_WIDTH, VGA_HEIGHT, ov2640_vga_regs), + OV2640_SIZE("SVGA", SVGA_WIDTH, SVGA_HEIGHT, ov2640_svga_regs), + OV2640_SIZE("XGA", XGA_WIDTH, XGA_HEIGHT, ov2640_xga_regs), + OV2640_SIZE("SXGA", SXGA_WIDTH, SXGA_HEIGHT, ov2640_sxga_regs), + OV2640_SIZE("UXGA", UXGA_WIDTH, UXGA_HEIGHT, ov2640_uxga_regs), }; /* @@ -867,7 +845,7 @@ static int ov2640_g_fmt(struct v4l2_subdev *sd, struct ov2640_priv *priv = to_ov2640(client); if (!priv->win) { - u32 width = W_SVGA, height = H_SVGA; + u32 width = SVGA_WIDTH, height = SVGA_HEIGHT; priv->win = ov2640_select_win(&width, &height); priv->cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8; } @@ -954,8 +932,8 @@ static int ov2640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { a->c.left = 0; a->c.top = 0; - a->c.width = W_UXGA; - a->c.height = H_UXGA; + a->c.width = UXGA_WIDTH; + a->c.height = UXGA_HEIGHT; a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; return 0; @@ -965,8 +943,8 @@ static int ov2640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) { a->bounds.left = 0; a->bounds.top = 0; - a->bounds.width = W_UXGA; - a->bounds.height = H_UXGA; + a->bounds.width = UXGA_WIDTH; + a->bounds.height = UXGA_HEIGHT; a->defrect = a->bounds; a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; a->pixelaspect.numerator = 1; diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index 656d889c1c79..4ebd329d7b42 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -58,21 +58,11 @@ static inline struct ths8200_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct ths8200_state, sd); } -static inline unsigned hblanking(const struct v4l2_bt_timings *t) -{ - return V4L2_DV_BT_BLANKING_WIDTH(t); -} - static inline unsigned htotal(const struct v4l2_bt_timings *t) { return V4L2_DV_BT_FRAME_WIDTH(t); } -static inline unsigned vblanking(const struct v4l2_bt_timings *t) -{ - return V4L2_DV_BT_BLANKING_HEIGHT(t); -} - static inline unsigned vtotal(const struct v4l2_bt_timings *t) { return V4L2_DV_BT_FRAME_HEIGHT(t); diff --git a/drivers/media/mmc/siano/Kconfig b/drivers/media/mmc/siano/Kconfig index aa05ad3c1ccb..7693487e2f63 100644 --- a/drivers/media/mmc/siano/Kconfig +++ b/drivers/media/mmc/siano/Kconfig @@ -6,6 +6,8 @@ config SMS_SDIO_DRV tristate "Siano SMS1xxx based MDTV via SDIO interface" depends on DVB_CORE && HAS_DMA depends on MMC + depends on !RC_CORE || RC_CORE select MEDIA_COMMON_OPTIONS + select SMS_SIANO_MDTV ---help--- Choose if you would like to have Siano's support for SDIO interface diff --git a/drivers/media/pci/bt8xx/Kconfig b/drivers/media/pci/bt8xx/Kconfig index 61d09e010814..4a93f6ded100 100644 --- a/drivers/media/pci/bt8xx/Kconfig +++ b/drivers/media/pci/bt8xx/Kconfig @@ -2,15 +2,17 @@ config VIDEO_BT848 tristate "BT848 Video For Linux" depends on VIDEO_DEV && PCI && I2C && VIDEO_V4L2 select I2C_ALGOBIT - select VIDEO_BTCX select VIDEOBUF_DMA_SG depends on RC_CORE + depends on MEDIA_RADIO_SUPPORT select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_MSP3400 if MEDIA_SUBDRV_AUTOSELECT select VIDEO_TVAUDIO if MEDIA_SUBDRV_AUTOSELECT select VIDEO_TDA7432 if MEDIA_SUBDRV_AUTOSELECT select VIDEO_SAA6588 if MEDIA_SUBDRV_AUTOSELECT + select RADIO_ADAPTERS + select RADIO_TEA575X ---help--- Support for BT848 based frame grabber/overlay boards. This includes the Miro, Hauppauge and STB boards. Please read the material in diff --git a/drivers/media/pci/bt8xx/Makefile b/drivers/media/pci/bt8xx/Makefile index f9fe7c4e7d53..2d4c3dd88be1 100644 --- a/drivers/media/pci/bt8xx/Makefile +++ b/drivers/media/pci/bt8xx/Makefile @@ -1,6 +1,6 @@ bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \ bttv-risc.o bttv-vbi.o bttv-i2c.o bttv-gpio.o \ - bttv-input.o bttv-audio-hook.o + bttv-input.o bttv-audio-hook.o btcx-risc.o obj-$(CONFIG_VIDEO_BT848) += bttv.o obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o dst_ca.o diff --git a/drivers/media/pci/bt8xx/bt878.c b/drivers/media/pci/bt8xx/bt878.c index 11765835d7b2..0939d399b774 100644 --- a/drivers/media/pci/bt8xx/bt878.c +++ b/drivers/media/pci/bt8xx/bt878.c @@ -590,9 +590,3 @@ module_init(bt878_init_module); module_exit(bt878_cleanup_module); MODULE_LICENSE("GPL"); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/common/btcx-risc.c b/drivers/media/pci/bt8xx/btcx-risc.c index ac1b2687a20d..00f0880b6d66 100644 --- a/drivers/media/common/btcx-risc.c +++ b/drivers/media/pci/bt8xx/btcx-risc.c @@ -32,13 +32,9 @@ #include "btcx-risc.h" -MODULE_DESCRIPTION("some code shared by bttv and cx88xx drivers"); -MODULE_AUTHOR("Gerd Knorr"); -MODULE_LICENSE("GPL"); - -static unsigned int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug,"debug messages, default is 0 (no)"); +static unsigned int btcx_debug; +module_param(btcx_debug, int, 0644); +MODULE_PARM_DESC(btcx_debug,"debug messages, default is 0 (no)"); /* ---------------------------------------------------------- */ /* allocate/free risc memory */ @@ -50,7 +46,7 @@ void btcx_riscmem_free(struct pci_dev *pci, { if (NULL == risc->cpu) return; - if (debug) { + if (btcx_debug) { memcnt--; printk("btcx: riscmem free [%d] dma=%lx\n", memcnt, (unsigned long)risc->dma); @@ -75,7 +71,7 @@ int btcx_riscmem_alloc(struct pci_dev *pci, risc->cpu = cpu; risc->dma = dma; risc->size = size; - if (debug) { + if (btcx_debug) { memcnt++; printk("btcx: riscmem alloc [%d] dma=%lx cpu=%p size=%d\n", memcnt, (unsigned long)dma, cpu, size); @@ -141,7 +137,7 @@ btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, unsigned int n, int m dx = nx - win->left; win->left = nx; win->width = nw; - if (debug) + if (btcx_debug) printk(KERN_DEBUG "btcx: window align %dx%d+%d+%d [dx=%d]\n", win->width, win->height, win->left, win->top, dx); @@ -153,7 +149,7 @@ btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, unsigned int n, int m nw += mask+1; clips[i].c.left = nx; clips[i].c.width = nw; - if (debug) + if (btcx_debug) printk(KERN_DEBUG "btcx: clip align %dx%d+%d+%d\n", clips[i].c.width, clips[i].c.height, clips[i].c.left, clips[i].c.top); @@ -234,7 +230,7 @@ btcx_calc_skips(int line, int width, int *maxy, *nskips = skip; *maxy = maxline; - if (debug) { + if (btcx_debug) { printk(KERN_DEBUG "btcx: skips line %d-%d:",line,maxline); for (skip = 0; skip < *nskips; skip++) { printk(" %d-%d",skips[skip].start,skips[skip].end); @@ -242,19 +238,3 @@ btcx_calc_skips(int line, int width, int *maxy, printk("\n"); } } - -/* ---------------------------------------------------------- */ - -EXPORT_SYMBOL(btcx_riscmem_alloc); -EXPORT_SYMBOL(btcx_riscmem_free); - -EXPORT_SYMBOL(btcx_screen_clips); -EXPORT_SYMBOL(btcx_align); -EXPORT_SYMBOL(btcx_sort_clips); -EXPORT_SYMBOL(btcx_calc_skips); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/bt8xx/btcx-risc.h b/drivers/media/pci/bt8xx/btcx-risc.h new file mode 100644 index 000000000000..1ed7a000160a --- /dev/null +++ b/drivers/media/pci/bt8xx/btcx-risc.h @@ -0,0 +1,26 @@ +struct btcx_riscmem { + unsigned int size; + __le32 *cpu; + __le32 *jmp; + dma_addr_t dma; +}; + +struct btcx_skiplist { + int start; + int end; +}; + +int btcx_riscmem_alloc(struct pci_dev *pci, + struct btcx_riscmem *risc, + unsigned int size); +void btcx_riscmem_free(struct pci_dev *pci, + struct btcx_riscmem *risc); + +int btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, + struct v4l2_clip *clips, unsigned int n); +int btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, + unsigned int n, int mask); +void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips); +void btcx_calc_skips(int line, int width, int *maxy, + struct btcx_skiplist *skips, unsigned int *nskips, + const struct v4l2_clip *clips, unsigned int nclips); diff --git a/drivers/media/pci/bt8xx/bttv-cards.c b/drivers/media/pci/bt8xx/bttv-cards.c index 41055606b969..4654fb65ca21 100644 --- a/drivers/media/pci/bt8xx/bttv-cards.c +++ b/drivers/media/pci/bt8xx/bttv-cards.c @@ -84,8 +84,7 @@ static void gv800s_init(struct bttv *btv); static void td3116_muxsel(struct bttv *btv, unsigned int input); static int terratec_active_radio_upgrade(struct bttv *btv); -static int tea5757_read(struct bttv *btv); -static int tea5757_write(struct bttv *btv, int value); +static int tea575x_init(struct bttv *btv); static void identify_by_eeprom(struct bttv *btv, unsigned char eeprom_data[256]); static int pvr_boot(struct bttv *btv); @@ -3085,12 +3084,12 @@ static void miro_pinnacle_gpio(struct bttv *btv) if (0 == (gpio & 0x20)) { btv->has_radio = 1; if (!miro_fmtuner[id]) { - btv->has_matchbox = 1; - btv->mbox_we = (1<<6); - btv->mbox_most = (1<<7); - btv->mbox_clk = (1<<8); - btv->mbox_data = (1<<9); - btv->mbox_mask = (1<<6)|(1<<7)|(1<<8)|(1<<9); + btv->has_tea575x = 1; + btv->tea_gpio.wren = 6; + btv->tea_gpio.most = 7; + btv->tea_gpio.clk = 8; + btv->tea_gpio.data = 9; + tea575x_init(btv); } } else { btv->has_radio = 0; @@ -3104,7 +3103,7 @@ static void miro_pinnacle_gpio(struct bttv *btv) pr_info("%d: miro: id=%d tuner=%d radio=%s stereo=%s\n", btv->c.nr, id+1, btv->tuner_type, !btv->has_radio ? "no" : - (btv->has_matchbox ? "matchbox" : "fmtuner"), + (btv->has_tea575x ? "tea575x" : "fmtuner"), (-1 == msp) ? "no" : "yes"); } else { /* new cards with microtune tuner */ @@ -3382,12 +3381,12 @@ void bttv_init_card2(struct bttv *btv) break; case BTTV_BOARD_VHX: btv->has_radio = 1; - btv->has_matchbox = 1; - btv->mbox_we = 0x20; - btv->mbox_most = 0; - btv->mbox_clk = 0x08; - btv->mbox_data = 0x10; - btv->mbox_mask = 0x38; + btv->has_tea575x = 1; + btv->tea_gpio.wren = 5; + btv->tea_gpio.most = 6; + btv->tea_gpio.clk = 3; + btv->tea_gpio.data = 4; + tea575x_init(btv); break; case BTTV_BOARD_VOBIS_BOOSTAR: case BTTV_BOARD_TERRATV: @@ -3745,33 +3744,112 @@ static void hauppauge_eeprom(struct bttv *btv) btv->radio_uses_msp_demodulator = 1; } -static int terratec_active_radio_upgrade(struct bttv *btv) +/* ----------------------------------------------------------------------- */ + +static void bttv_tea575x_set_pins(struct snd_tea575x *tea, u8 pins) +{ + struct bttv *btv = tea->private_data; + struct bttv_tea575x_gpio gpio = btv->tea_gpio; + u16 val = 0; + + val |= (pins & TEA575X_DATA) ? (1 << gpio.data) : 0; + val |= (pins & TEA575X_CLK) ? (1 << gpio.clk) : 0; + val |= (pins & TEA575X_WREN) ? (1 << gpio.wren) : 0; + + gpio_bits((1 << gpio.data) | (1 << gpio.clk) | (1 << gpio.wren), val); + if (btv->mbox_ior) { + /* IOW and CSEL active */ + gpio_bits(btv->mbox_iow | btv->mbox_csel, 0); + udelay(5); + /* all inactive */ + gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, + btv->mbox_ior | btv->mbox_iow | btv->mbox_csel); + } +} + +static u8 bttv_tea575x_get_pins(struct snd_tea575x *tea) +{ + struct bttv *btv = tea->private_data; + struct bttv_tea575x_gpio gpio = btv->tea_gpio; + u8 ret = 0; + u16 val; + + if (btv->mbox_ior) { + /* IOR and CSEL active */ + gpio_bits(btv->mbox_ior | btv->mbox_csel, 0); + udelay(5); + } + val = gpio_read(); + if (btv->mbox_ior) { + /* all inactive */ + gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, + btv->mbox_ior | btv->mbox_iow | btv->mbox_csel); + } + + if (val & (1 << gpio.data)) + ret |= TEA575X_DATA; + if (val & (1 << gpio.most)) + ret |= TEA575X_MOST; + + return ret; +} + +static void bttv_tea575x_set_direction(struct snd_tea575x *tea, bool output) { - int freq; + struct bttv *btv = tea->private_data; + struct bttv_tea575x_gpio gpio = btv->tea_gpio; + u32 mask = (1 << gpio.clk) | (1 << gpio.wren) | (1 << gpio.data) | + (1 << gpio.most); + + if (output) + gpio_inout(mask, (1 << gpio.data) | (1 << gpio.clk) | + (1 << gpio.wren)); + else + gpio_inout(mask, (1 << gpio.clk) | (1 << gpio.wren)); +} + +static struct snd_tea575x_ops bttv_tea_ops = { + .set_pins = bttv_tea575x_set_pins, + .get_pins = bttv_tea575x_get_pins, + .set_direction = bttv_tea575x_set_direction, +}; + +static int tea575x_init(struct bttv *btv) +{ + btv->tea.private_data = btv; + btv->tea.ops = &bttv_tea_ops; + if (!snd_tea575x_hw_init(&btv->tea)) { + pr_info("%d: detected TEA575x radio\n", btv->c.nr); + btv->tea.mute = false; + return 0; + } + + btv->has_tea575x = 0; + btv->has_radio = 0; + return -ENODEV; +} + +/* ----------------------------------------------------------------------- */ + +static int terratec_active_radio_upgrade(struct bttv *btv) +{ btv->has_radio = 1; - btv->has_matchbox = 1; - btv->mbox_we = 0x10; - btv->mbox_most = 0x20; - btv->mbox_clk = 0x08; - btv->mbox_data = 0x04; - btv->mbox_mask = 0x3c; + btv->has_tea575x = 1; + btv->tea_gpio.wren = 4; + btv->tea_gpio.most = 5; + btv->tea_gpio.clk = 3; + btv->tea_gpio.data = 2; btv->mbox_iow = 1 << 8; btv->mbox_ior = 1 << 9; btv->mbox_csel = 1 << 10; - freq=88000/62.5; - tea5757_write(btv, 5 * freq + 0x358); /* write 0x1ed8 */ - if (0x1ed8 == tea5757_read(btv)) { + if (!tea575x_init(btv)) { pr_info("%d: Terratec Active Radio Upgrade found\n", btv->c.nr); - btv->has_radio = 1; - btv->has_saa6588 = 1; - btv->has_matchbox = 1; - } else { - btv->has_radio = 0; - btv->has_matchbox = 0; + btv->has_saa6588 = 1; } + return 0; } @@ -4292,181 +4370,6 @@ init_PCI8604PW(struct bttv *btv) } } - - -/* ----------------------------------------------------------------------- */ -/* Miro Pro radio stuff -- the tea5757 is connected to some GPIO ports */ -/* - * Copyright (c) 1999 Csaba Halasz <qgehali@uni-miskolc.hu> - * This code is placed under the terms of the GNU General Public License - * - * Brutally hacked by Dan Sheridan <dan.sheridan@contact.org.uk> djs52 8/3/00 - */ - -static void bus_low(struct bttv *btv, int bit) -{ - if (btv->mbox_ior) { - gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, - btv->mbox_ior | btv->mbox_iow | btv->mbox_csel); - udelay(5); - } - - gpio_bits(bit,0); - udelay(5); - - if (btv->mbox_ior) { - gpio_bits(btv->mbox_iow | btv->mbox_csel, 0); - udelay(5); - } -} - -static void bus_high(struct bttv *btv, int bit) -{ - if (btv->mbox_ior) { - gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, - btv->mbox_ior | btv->mbox_iow | btv->mbox_csel); - udelay(5); - } - - gpio_bits(bit,bit); - udelay(5); - - if (btv->mbox_ior) { - gpio_bits(btv->mbox_iow | btv->mbox_csel, 0); - udelay(5); - } -} - -static int bus_in(struct bttv *btv, int bit) -{ - if (btv->mbox_ior) { - gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, - btv->mbox_ior | btv->mbox_iow | btv->mbox_csel); - udelay(5); - - gpio_bits(btv->mbox_iow | btv->mbox_csel, 0); - udelay(5); - } - return gpio_read() & (bit); -} - -/* TEA5757 register bits */ -#define TEA_FREQ 0:14 -#define TEA_BUFFER 15:15 - -#define TEA_SIGNAL_STRENGTH 16:17 - -#define TEA_PORT1 18:18 -#define TEA_PORT0 19:19 - -#define TEA_BAND 20:21 -#define TEA_BAND_FM 0 -#define TEA_BAND_MW 1 -#define TEA_BAND_LW 2 -#define TEA_BAND_SW 3 - -#define TEA_MONO 22:22 -#define TEA_ALLOW_STEREO 0 -#define TEA_FORCE_MONO 1 - -#define TEA_SEARCH_DIRECTION 23:23 -#define TEA_SEARCH_DOWN 0 -#define TEA_SEARCH_UP 1 - -#define TEA_STATUS 24:24 -#define TEA_STATUS_TUNED 0 -#define TEA_STATUS_SEARCHING 1 - -/* Low-level stuff */ -static int tea5757_read(struct bttv *btv) -{ - unsigned long timeout; - int value = 0; - int i; - - /* better safe than sorry */ - gpio_inout(btv->mbox_mask, btv->mbox_clk | btv->mbox_we); - - if (btv->mbox_ior) { - gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, - btv->mbox_ior | btv->mbox_iow | btv->mbox_csel); - udelay(5); - } - - if (bttv_gpio) - bttv_gpio_tracking(btv,"tea5757 read"); - - bus_low(btv,btv->mbox_we); - bus_low(btv,btv->mbox_clk); - - udelay(10); - timeout= jiffies + msecs_to_jiffies(1000); - - /* wait for DATA line to go low; error if it doesn't */ - while (bus_in(btv,btv->mbox_data) && time_before(jiffies, timeout)) - schedule(); - if (bus_in(btv,btv->mbox_data)) { - pr_warn("%d: tea5757: read timeout\n", btv->c.nr); - return -1; - } - - dprintk("%d: tea5757:", btv->c.nr); - for (i = 0; i < 24; i++) { - udelay(5); - bus_high(btv,btv->mbox_clk); - udelay(5); - dprintk_cont("%c", - bus_in(btv, btv->mbox_most) == 0 ? 'T' : '-'); - bus_low(btv,btv->mbox_clk); - value <<= 1; - value |= (bus_in(btv,btv->mbox_data) == 0)?0:1; /* MSB first */ - dprintk_cont("%c", - bus_in(btv, btv->mbox_most) == 0 ? 'S' : 'M'); - } - dprintk_cont("\n"); - dprintk("%d: tea5757: read 0x%X\n", btv->c.nr, value); - return value; -} - -static int tea5757_write(struct bttv *btv, int value) -{ - int i; - int reg = value; - - gpio_inout(btv->mbox_mask, btv->mbox_clk | btv->mbox_we | btv->mbox_data); - - if (btv->mbox_ior) { - gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, - btv->mbox_ior | btv->mbox_iow | btv->mbox_csel); - udelay(5); - } - if (bttv_gpio) - bttv_gpio_tracking(btv,"tea5757 write"); - - dprintk("%d: tea5757: write 0x%X\n", btv->c.nr, value); - bus_low(btv,btv->mbox_clk); - bus_high(btv,btv->mbox_we); - for (i = 0; i < 25; i++) { - if (reg & 0x1000000) - bus_high(btv,btv->mbox_data); - else - bus_low(btv,btv->mbox_data); - reg <<= 1; - bus_high(btv,btv->mbox_clk); - udelay(10); - bus_low(btv,btv->mbox_clk); - udelay(10); - } - bus_low(btv,btv->mbox_we); /* unmute !!! */ - return 0; -} - -void tea5757_set_freq(struct bttv *btv, unsigned short freq) -{ - dprintk("tea5757_set_freq %d\n",freq); - tea5757_write(btv, 5 * freq + 0x358); /* add 10.7MHz (see docs) */ -} - /* RemoteVision MX (rv605) muxsel helper [Miguel Freitas] * * This is needed because rv605 don't use a normal multiplex, but a crosspoint @@ -5048,10 +4951,3 @@ int bttv_handle_chipset(struct bttv *btv) pci_write_config_byte(btv->c.pci, PCI_LATENCY_TIMER, latency); return 0; } - - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/bt8xx/bttv-driver.c b/drivers/media/pci/bt8xx/bttv-driver.c index 4a8176c09fc9..4ec2a3c3f23c 100644 --- a/drivers/media/pci/bt8xx/bttv-driver.c +++ b/drivers/media/pci/bt8xx/bttv-driver.c @@ -1874,8 +1874,10 @@ static void bttv_set_frequency(struct bttv *btv, const struct v4l2_frequency *f) if (new_freq.type == V4L2_TUNER_RADIO) { radio_enable(btv); btv->radio_freq = new_freq.frequency; - if (btv->has_matchbox) - tea5757_set_freq(btv, btv->radio_freq); + if (btv->has_tea575x) { + btv->tea.freq = btv->radio_freq; + snd_tea575x_set_freq(&btv->tea); + } } else { btv->tv_freq = new_freq.frequency; } @@ -2513,6 +2515,8 @@ static int bttv_querycap(struct file *file, void *priv, if (btv->has_saa6588) cap->device_caps |= V4L2_CAP_READWRITE | V4L2_CAP_RDS_CAPTURE; + if (btv->has_tea575x) + cap->device_caps |= V4L2_CAP_HW_FREQ_SEEK; } return 0; } @@ -3242,6 +3246,9 @@ static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) if (btv->audio_mode_gpio) btv->audio_mode_gpio(btv, t, 0); + if (btv->has_tea575x) + return snd_tea575x_g_tuner(&btv->tea, t); + return 0; } @@ -3259,6 +3266,30 @@ static int radio_s_tuner(struct file *file, void *priv, return 0; } +static int radio_s_hw_freq_seek(struct file *file, void *priv, + const struct v4l2_hw_freq_seek *a) +{ + struct bttv_fh *fh = priv; + struct bttv *btv = fh->btv; + + if (btv->has_tea575x) + return snd_tea575x_s_hw_freq_seek(file, &btv->tea, a); + + return -ENOTTY; +} + +static int radio_enum_freq_bands(struct file *file, void *priv, + struct v4l2_frequency_band *band) +{ + struct bttv_fh *fh = priv; + struct bttv *btv = fh->btv; + + if (btv->has_tea575x) + return snd_tea575x_enum_freq_bands(&btv->tea, band); + + return -ENOTTY; +} + static ssize_t radio_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { @@ -3316,6 +3347,8 @@ static const struct v4l2_ioctl_ops radio_ioctl_ops = { .vidioc_s_tuner = radio_s_tuner, .vidioc_g_frequency = bttv_g_frequency, .vidioc_s_frequency = bttv_s_frequency, + .vidioc_s_hw_freq_seek = radio_s_hw_freq_seek, + .vidioc_enum_freq_bands = radio_enum_freq_bands, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; @@ -3884,7 +3917,6 @@ static struct video_device *vdev_init(struct bttv *btv, *vfd = *template; vfd->v4l2_dev = &btv->c.v4l2_dev; vfd->release = video_device_release; - vfd->debug = bttv_debug; video_set_drvdata(vfd, btv); snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)", btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "", @@ -4429,9 +4461,3 @@ static void __exit bttv_cleanup_module(void) module_init(bttv_init_module); module_exit(bttv_cleanup_module); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/bt8xx/bttv-gpio.c b/drivers/media/pci/bt8xx/bttv-gpio.c index 3f364b7062b9..25b9916906d5 100644 --- a/drivers/media/pci/bt8xx/bttv-gpio.c +++ b/drivers/media/pci/bt8xx/bttv-gpio.c @@ -181,9 +181,3 @@ void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits) btwrite(data,BT848_GPIO_DATA); spin_unlock_irqrestore(&btv->gpio_lock,flags); } - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/bt8xx/bttv-if.c b/drivers/media/pci/bt8xx/bttv-if.c index a6a540dc9e4b..538652e16a5c 100644 --- a/drivers/media/pci/bt8xx/bttv-if.c +++ b/drivers/media/pci/bt8xx/bttv-if.c @@ -113,9 +113,3 @@ int bttv_write_gpio(unsigned int card, unsigned long mask, unsigned long data) bttv_gpio_tracking(btv,"extern write"); return 0; } - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/bt8xx/bttv-risc.c b/drivers/media/pci/bt8xx/bttv-risc.c index 4d3f05a19af3..3859dde98be2 100644 --- a/drivers/media/pci/bt8xx/bttv-risc.c +++ b/drivers/media/pci/bt8xx/bttv-risc.c @@ -901,9 +901,3 @@ bttv_overlay_risc(struct bttv *btv, buf->vb.field = ov->field; return 0; } - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/bt8xx/bttv-vbi.c b/drivers/media/pci/bt8xx/bttv-vbi.c index b433267d9aa9..e77129c92fa0 100644 --- a/drivers/media/pci/bt8xx/bttv-vbi.c +++ b/drivers/media/pci/bt8xx/bttv-vbi.c @@ -450,10 +450,3 @@ void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, unsigned int norm) /* See bttv_vbi_fmt_set(). */ f->end = tvnorm->vbistart[0] * 2 + 2; } - -/* ----------------------------------------------------------------------- */ -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/bt8xx/bttv.h b/drivers/media/pci/bt8xx/bttv.h index f08126244662..91301c3cad1e 100644 --- a/drivers/media/pci/bt8xx/bttv.h +++ b/drivers/media/pci/bt8xx/bttv.h @@ -378,8 +378,3 @@ extern void bttv_input_fini(struct bttv *dev); extern void bttv_input_irq(struct bttv *dev); #endif /* _BTTV_H_ */ -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/bt8xx/bttvp.h b/drivers/media/pci/bt8xx/bttvp.h index 9fe19488b30b..bc048c586b1f 100644 --- a/drivers/media/pci/bt8xx/bttvp.h +++ b/drivers/media/pci/bt8xx/bttvp.h @@ -42,6 +42,7 @@ #include <media/tveeprom.h> #include <media/rc-core.h> #include <media/ir-kbd-i2c.h> +#include <media/tea575x.h> #include "bt848.h" #include "bttv.h" @@ -359,6 +360,10 @@ struct bttv_suspend_state { struct bttv_buffer *vbi; }; +struct bttv_tea575x_gpio { + u8 data, clk, wren, most; +}; + struct bttv { struct bttv_core c; @@ -445,12 +450,9 @@ struct bttv { /* miro/pinnacle + Aimslab VHX philips matchbox (tea5757 radio tuner) support */ - int has_matchbox; - int mbox_we; - int mbox_data; - int mbox_clk; - int mbox_most; - int mbox_mask; + int has_tea575x; + struct bttv_tea575x_gpio tea_gpio; + struct snd_tea575x tea; /* ISA stuff (Terratec Active Radio Upgrade) */ int mbox_ior; @@ -531,9 +533,3 @@ static inline unsigned int bttv_muxsel(const struct bttv *btv, #define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) #endif /* _BTTVP_H_ */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/pci/cx23885/Kconfig b/drivers/media/pci/cx23885/Kconfig index f613314b360b..74d774e5227b 100644 --- a/drivers/media/pci/cx23885/Kconfig +++ b/drivers/media/pci/cx23885/Kconfig @@ -41,6 +41,7 @@ config VIDEO_CX23885 select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_SI2157 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_M88TS2022 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_M88RS6000T if MEDIA_SUBDRV_AUTOSELECT select DVB_TUNER_DIB0070 if MEDIA_SUBDRV_AUTOSELECT ---help--- This is a video4linux driver for Conexant 23885 based diff --git a/drivers/media/pci/cx23885/cx23885-cards.c b/drivers/media/pci/cx23885/cx23885-cards.c index db99ca2613ba..f384f295676e 100644 --- a/drivers/media/pci/cx23885/cx23885-cards.c +++ b/drivers/media/pci/cx23885/cx23885-cards.c @@ -614,7 +614,7 @@ struct cx23885_board cx23885_boards[] = { .portb = CX23885_MPEG_DVB, }, [CX23885_BOARD_HAUPPAUGE_HVR4400] = { - .name = "Hauppauge WinTV-HVR4400", + .name = "Hauppauge WinTV-HVR4400/HVR5500", .porta = CX23885_ANALOG_VIDEO, .portb = CX23885_MPEG_DVB, .portc = CX23885_MPEG_DVB, @@ -622,6 +622,10 @@ struct cx23885_board cx23885_boards[] = { .tuner_addr = 0x60, /* 0xc0 >> 1 */ .tuner_bus = 1, }, + [CX23885_BOARD_HAUPPAUGE_STARBURST] = { + .name = "Hauppauge WinTV Starburst", + .portb = CX23885_MPEG_DVB, + }, [CX23885_BOARD_AVERMEDIA_HC81R] = { .name = "AVerTV Hybrid Express Slim HC81R", .tuner_type = TUNER_XC2028, @@ -706,6 +710,11 @@ struct cx23885_board cx23885_boards[] = { .portb = CX23885_MPEG_DVB, .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_HAUPPAUGE_HVR5525] = { + .name = "Hauppauge WinTV-HVR5525", + .portb = CX23885_MPEG_DVB, + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -936,19 +945,19 @@ struct cx23885_subid cx23885_subids[] = { }, { .subvendor = 0x0070, .subdevice = 0xc108, - .card = CX23885_BOARD_HAUPPAUGE_HVR4400, + .card = CX23885_BOARD_HAUPPAUGE_HVR4400, /* Hauppauge WinTV HVR-4400 (Model 121xxx, Hybrid DVB-T/S2, IR) */ }, { .subvendor = 0x0070, .subdevice = 0xc138, - .card = CX23885_BOARD_HAUPPAUGE_HVR4400, + .card = CX23885_BOARD_HAUPPAUGE_HVR4400, /* Hauppauge WinTV HVR-5500 (Model 121xxx, Hybrid DVB-T/C/S2, IR) */ }, { .subvendor = 0x0070, .subdevice = 0xc12a, - .card = CX23885_BOARD_HAUPPAUGE_HVR4400, + .card = CX23885_BOARD_HAUPPAUGE_STARBURST, /* Hauppauge WinTV Starburst (Model 121x00, DVB-S2, IR) */ }, { .subvendor = 0x0070, .subdevice = 0xc1f8, - .card = CX23885_BOARD_HAUPPAUGE_HVR4400, + .card = CX23885_BOARD_HAUPPAUGE_HVR4400, /* Hauppauge WinTV HVR-5500 (Model 121xxx, Hybrid DVB-T/C/S2, IR) */ }, { .subvendor = 0x1461, .subdevice = 0xd939, @@ -989,6 +998,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x4254, .subdevice = 0x0982, .card = CX23885_BOARD_DVBSKY_T982, + }, { + .subvendor = 0x0070, + .subdevice = 0xf038, + .card = CX23885_BOARD_HAUPPAUGE_HVR5525, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -1161,6 +1174,8 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) case 85721: /* WinTV-HVR1290 (PCIe, OEM, RCA in, IR, Dual channel ATSC and Basic analog */ + case 150329: + /* WinTV-HVR5525 (PCIe, DVB-S/S2, DVB-T/T2/C) */ break; default: printk(KERN_WARNING "%s: warning: " @@ -1545,8 +1560,9 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/ break; case CX23885_BOARD_HAUPPAUGE_HVR4400: + case CX23885_BOARD_HAUPPAUGE_STARBURST: /* GPIO-8 tda10071 demod reset */ - /* GPIO-9 si2165 demod reset */ + /* GPIO-9 si2165 demod reset (only HVR4400/HVR5500)*/ /* Put the parts into reset and back */ cx23885_gpio_enable(dev, GPIO_8 | GPIO_9, 1); @@ -1632,6 +1648,29 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) msleep(100); cx23885_gpio_set(dev, GPIO_2); break; + case CX23885_BOARD_HAUPPAUGE_HVR5525: + /* + * GPIO-00 IR_WIDE + * GPIO-02 wake# + * GPIO-03 VAUX Pres. + * GPIO-07 PROG# + * GPIO-08 SAT_RESN + * GPIO-09 TER_RESN + * GPIO-10 B2_SENSE + * GPIO-11 B1_SENSE + * GPIO-15 IR_LED_STATUS + * GPIO-19 IR_NARROW + * GPIO-20 Blauster1 + * ALTGPIO VAUX_SWITCH + * AUX_PLL_CLK : Blaster2 + */ + /* Put the parts into reset and back */ + cx23885_gpio_enable(dev, GPIO_8 | GPIO_9, 1); + cx23885_gpio_clear(dev, GPIO_8 | GPIO_9); + msleep(100); + cx23885_gpio_set(dev, GPIO_8 | GPIO_9); + msleep(100); + break; } } @@ -1872,7 +1911,9 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_HAUPPAUGE_HVR1290: case CX23885_BOARD_HAUPPAUGE_HVR4400: + case CX23885_BOARD_HAUPPAUGE_STARBURST: case CX23885_BOARD_HAUPPAUGE_IMPACTVCBE: + case CX23885_BOARD_HAUPPAUGE_HVR5525: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xc0); break; @@ -1980,6 +2021,11 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; + case CX23885_BOARD_HAUPPAUGE_STARBURST: + ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; case CX23885_BOARD_DVBSKY_T9580: case CX23885_BOARD_DVBSKY_T982: ts1->gen_ctrl_val = 0x5; /* Parallel */ @@ -1997,6 +2043,14 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; + case CX23885_BOARD_HAUPPAUGE_HVR5525: + ts1->gen_ctrl_val = 0x5; /* Parallel */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ + ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: diff --git a/drivers/media/pci/cx23885/cx23885-core.c b/drivers/media/pci/cx23885/cx23885-core.c index 1d9d0f86ca8c..1ad49946d7fa 100644 --- a/drivers/media/pci/cx23885/cx23885-core.c +++ b/drivers/media/pci/cx23885/cx23885-core.c @@ -2049,11 +2049,11 @@ static void cx23885_finidev(struct pci_dev *pci_dev) cx23885_shutdown(dev); - pci_disable_device(pci_dev); - /* unregister stuff */ free_irq(pci_dev->irq, dev); + pci_disable_device(pci_dev); + cx23885_dev_unregister(dev); vb2_dma_sg_cleanup_ctx(dev->alloc_ctx); v4l2_ctrl_handler_free(&dev->ctrl_handler); diff --git a/drivers/media/pci/cx23885/cx23885-dvb.c b/drivers/media/pci/cx23885/cx23885-dvb.c index c47d18270cfc..45fbe1e4d2d0 100644 --- a/drivers/media/pci/cx23885/cx23885-dvb.c +++ b/drivers/media/pci/cx23885/cx23885-dvb.c @@ -74,6 +74,7 @@ #include "sp2.h" #include "m88ds3103.h" #include "m88ts2022.h" +#include "m88rs6000t.h" static unsigned int debug; @@ -915,6 +916,16 @@ static const struct m88ds3103_config dvbsky_s952_portc_m88ds3103_config = { .agc = 0x99, }; +static const struct m88ds3103_config hauppauge_hvr5525_m88ds3103_config = { + .i2c_addr = 0x69, + .clock = 27000000, + .i2c_wr_max = 33, + .ts_mode = M88DS3103_TS_PARALLEL, + .ts_clk = 16000, + .ts_clk_pol = 1, + .agc = 0x99, +}; + static int netup_altera_fpga_rw(void *device, int flag, int data, int read) { struct cx23885_dev *dev = (struct cx23885_dev *)device; @@ -1058,6 +1069,116 @@ static struct dib7000p_config dib7070p_dib7000p_config = { .hostbus_diversity = 1, }; +static int dvb_register_ci_mac(struct cx23885_tsport *port) +{ + struct cx23885_dev *dev = port->dev; + struct i2c_client *client_ci = NULL; + struct vb2_dvb_frontend *fe0; + + fe0 = vb2_dvb_get_frontend(&port->frontends, 1); + if (!fe0) + return -EINVAL; + + switch (dev->board) { + case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: { + static struct netup_card_info cinfo; + + netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo); + memcpy(port->frontends.adapter.proposed_mac, + cinfo.port[port->nr - 1].mac, 6); + printk(KERN_INFO "NetUP Dual DVB-S2 CI card port%d MAC=%pM\n", + port->nr, port->frontends.adapter.proposed_mac); + + netup_ci_init(port); + return 0; + } + case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: { + struct altera_ci_config netup_ci_cfg = { + .dev = dev,/* magic number to identify*/ + .adapter = &port->frontends.adapter,/* for CI */ + .demux = &fe0->dvb.demux,/* for hw pid filter */ + .fpga_rw = netup_altera_fpga_rw, + }; + + altera_ci_init(&netup_ci_cfg, port->nr); + return 0; + } + case CX23885_BOARD_TEVII_S470: { + u8 eeprom[256]; /* 24C02 i2c eeprom */ + + if (port->nr != 1) + return 0; + + /* Read entire EEPROM */ + dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1; + tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, sizeof(eeprom)); + printk(KERN_INFO "TeVii S470 MAC= %pM\n", eeprom + 0xa0); + memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xa0, 6); + return 0; + } + case CX23885_BOARD_DVBSKY_T9580: + case CX23885_BOARD_DVBSKY_S950: + case CX23885_BOARD_DVBSKY_S952: + case CX23885_BOARD_DVBSKY_T982: { + u8 eeprom[256]; /* 24C02 i2c eeprom */ + + if (port->nr > 2) + return 0; + + /* Read entire EEPROM */ + dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1; + tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, + sizeof(eeprom)); + printk(KERN_INFO "%s port %d MAC address: %pM\n", + cx23885_boards[dev->board].name, port->nr, + eeprom + 0xc0 + (port->nr-1) * 8); + memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xc0 + + (port->nr-1) * 8, 6); + return 0; + } + case CX23885_BOARD_DVBSKY_S950C: + case CX23885_BOARD_DVBSKY_T980C: + case CX23885_BOARD_TT_CT2_4500_CI: { + u8 eeprom[256]; /* 24C02 i2c eeprom */ + struct sp2_config sp2_config; + struct i2c_board_info info; + struct cx23885_i2c *i2c_bus2 = &dev->i2c_bus[1]; + + /* attach CI */ + memset(&sp2_config, 0, sizeof(sp2_config)); + sp2_config.dvb_adap = &port->frontends.adapter; + sp2_config.priv = port; + sp2_config.ci_control = cx23885_sp2_ci_ctrl; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "sp2", I2C_NAME_SIZE); + info.addr = 0x40; + info.platform_data = &sp2_config; + request_module(info.type); + client_ci = i2c_new_device(&i2c_bus2->i2c_adap, &info); + if (client_ci == NULL || client_ci->dev.driver == NULL) + return -ENODEV; + if (!try_module_get(client_ci->dev.driver->owner)) { + i2c_unregister_device(client_ci); + return -ENODEV; + } + port->i2c_client_ci = client_ci; + + if (port->nr != 1) + return 0; + + /* Read entire EEPROM */ + dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1; + tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, + sizeof(eeprom)); + printk(KERN_INFO "%s MAC address: %pM\n", + cx23885_boards[dev->board].name, eeprom + 0xc0); + memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xc0, 6); + return 0; + } + } + return 0; +} + static int dvb_register(struct cx23885_tsport *port) { struct dib7000p_ops dib7000p_ops; @@ -1066,11 +1187,10 @@ static int dvb_register(struct cx23885_tsport *port) struct vb2_dvb_frontend *fe0, *fe1 = NULL; struct si2168_config si2168_config; struct si2157_config si2157_config; - struct sp2_config sp2_config; struct m88ts2022_config m88ts2022_config; struct i2c_board_info info; struct i2c_adapter *adapter; - struct i2c_client *client_demod = NULL, *client_tuner = NULL, *client_ci = NULL; + struct i2c_client *client_demod = NULL, *client_tuner = NULL; const struct m88ds3103_config *p_m88ds3103_config = NULL; int (*p_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage) = NULL; int mfe_shared = 0; /* bus not shared by default */ @@ -1710,6 +1830,17 @@ static int dvb_register(struct cx23885_tsport *port) break; } break; + case CX23885_BOARD_HAUPPAUGE_STARBURST: + i2c_bus = &dev->i2c_bus[0]; + fe0->dvb.frontend = dvb_attach(tda10071_attach, + &hauppauge_tda10071_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(a8293_attach, fe0->dvb.frontend, + &i2c_bus->i2c_adap, + &hauppauge_a8293_config); + } + break; case CX23885_BOARD_DVBSKY_T9580: case CX23885_BOARD_DVBSKY_S950: i2c_bus = &dev->i2c_bus[0]; @@ -1790,15 +1921,11 @@ static int dvb_register(struct cx23885_tsport *port) request_module(info.type); client_tuner = i2c_new_device(adapter, &info); if (client_tuner == NULL || - client_tuner->dev.driver == NULL) { - module_put(client_demod->dev.driver->owner); - i2c_unregister_device(client_demod); + client_tuner->dev.driver == NULL) goto frontend_detach; - } + if (!try_module_get(client_tuner->dev.driver->owner)) { i2c_unregister_device(client_tuner); - module_put(client_demod->dev.driver->owner); - i2c_unregister_device(client_demod); goto frontend_detach; } port->i2c_client_tuner = client_tuner; @@ -1821,8 +1948,7 @@ static int dvb_register(struct cx23885_tsport *port) info.platform_data = &si2168_config; request_module(info.type); client_demod = i2c_new_device(&i2c_bus->i2c_adap, &info); - if (client_demod == NULL || - client_demod->dev.driver == NULL) + if (client_demod == NULL || client_demod->dev.driver == NULL) goto frontend_detach; if (!try_module_get(client_demod->dev.driver->owner)) { i2c_unregister_device(client_demod); @@ -1840,15 +1966,10 @@ static int dvb_register(struct cx23885_tsport *port) request_module(info.type); client_tuner = i2c_new_device(adapter, &info); if (client_tuner == NULL || - client_tuner->dev.driver == NULL) { - module_put(client_demod->dev.driver->owner); - i2c_unregister_device(client_demod); + client_tuner->dev.driver == NULL) goto frontend_detach; - } if (!try_module_get(client_tuner->dev.driver->owner)) { i2c_unregister_device(client_tuner); - module_put(client_demod->dev.driver->owner); - i2c_unregister_device(client_demod); goto frontend_detach; } port->i2c_client_tuner = client_tuner; @@ -1874,8 +1995,7 @@ static int dvb_register(struct cx23885_tsport *port) info.platform_data = &m88ts2022_config; request_module(info.type); client_tuner = i2c_new_device(adapter, &info); - if (client_tuner == NULL || - client_tuner->dev.driver == NULL) + if (client_tuner == NULL || client_tuner->dev.driver == NULL) goto frontend_detach; if (!try_module_get(client_tuner->dev.driver->owner)) { i2c_unregister_device(client_tuner); @@ -1921,8 +2041,7 @@ static int dvb_register(struct cx23885_tsport *port) info.platform_data = &m88ts2022_config; request_module(info.type); client_tuner = i2c_new_device(adapter, &info); - if (client_tuner == NULL || - client_tuner->dev.driver == NULL) + if (client_tuner == NULL || client_tuner->dev.driver == NULL) goto frontend_detach; if (!try_module_get(client_tuner->dev.driver->owner)) { i2c_unregister_device(client_tuner); @@ -1967,8 +2086,7 @@ static int dvb_register(struct cx23885_tsport *port) info.platform_data = &si2168_config; request_module(info.type); client_demod = i2c_new_device(&i2c_bus->i2c_adap, &info); - if (client_demod == NULL || - client_demod->dev.driver == NULL) + if (client_demod == NULL || client_demod->dev.driver == NULL) goto frontend_detach; if (!try_module_get(client_demod->dev.driver->owner)) { i2c_unregister_device(client_demod); @@ -1986,20 +2104,101 @@ static int dvb_register(struct cx23885_tsport *port) request_module(info.type); client_tuner = i2c_new_device(adapter, &info); if (client_tuner == NULL || - client_tuner->dev.driver == NULL) { - module_put(client_demod->dev.driver->owner); - i2c_unregister_device(client_demod); + client_tuner->dev.driver == NULL) goto frontend_detach; - } if (!try_module_get(client_tuner->dev.driver->owner)) { i2c_unregister_device(client_tuner); - module_put(client_demod->dev.driver->owner); - i2c_unregister_device(client_demod); - port->i2c_client_demod = NULL; goto frontend_detach; } port->i2c_client_tuner = client_tuner; break; + case CX23885_BOARD_HAUPPAUGE_HVR5525: + switch (port->nr) { + struct m88rs6000t_config m88rs6000t_config; + + /* port b - satellite */ + case 1: + /* attach frontend */ + fe0->dvb.frontend = dvb_attach(m88ds3103_attach, + &hauppauge_hvr5525_m88ds3103_config, + &dev->i2c_bus[0].i2c_adap, &adapter); + if (fe0->dvb.frontend == NULL) + break; + + /* attach SEC */ + if (!dvb_attach(a8293_attach, fe0->dvb.frontend, + &dev->i2c_bus[0].i2c_adap, + &hauppauge_a8293_config)) + goto frontend_detach; + + /* attach tuner */ + memset(&m88rs6000t_config, 0, sizeof(m88rs6000t_config)); + m88rs6000t_config.fe = fe0->dvb.frontend; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "m88rs6000t", I2C_NAME_SIZE); + info.addr = 0x21; + info.platform_data = &m88rs6000t_config; + request_module("%s", info.type); + client_tuner = i2c_new_device(adapter, &info); + if (!client_tuner || !client_tuner->dev.driver) + goto frontend_detach; + if (!try_module_get(client_tuner->dev.driver->owner)) { + i2c_unregister_device(client_tuner); + goto frontend_detach; + } + port->i2c_client_tuner = client_tuner; + + /* delegate signal strength measurement to tuner */ + fe0->dvb.frontend->ops.read_signal_strength = + fe0->dvb.frontend->ops.tuner_ops.get_rf_strength; + break; + /* port c - terrestrial/cable */ + case 2: + /* attach frontend */ + memset(&si2168_config, 0, sizeof(si2168_config)); + si2168_config.i2c_adapter = &adapter; + si2168_config.fe = &fe0->dvb.frontend; + si2168_config.ts_mode = SI2168_TS_SERIAL; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2168", I2C_NAME_SIZE); + info.addr = 0x64; + info.platform_data = &si2168_config; + request_module("%s", info.type); + client_demod = i2c_new_device(&dev->i2c_bus[0].i2c_adap, &info); + if (!client_demod || !client_demod->dev.driver) + goto frontend_detach; + if (!try_module_get(client_demod->dev.driver->owner)) { + i2c_unregister_device(client_demod); + goto frontend_detach; + } + port->i2c_client_demod = client_demod; + + /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.fe = fe0->dvb.frontend; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2157", I2C_NAME_SIZE); + info.addr = 0x60; + info.platform_data = &si2157_config; + request_module("%s", info.type); + client_tuner = i2c_new_device(&dev->i2c_bus[1].i2c_adap, &info); + if (!client_tuner || !client_tuner->dev.driver) { + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + port->i2c_client_demod = NULL; + goto frontend_detach; + } + if (!try_module_get(client_tuner->dev.driver->owner)) { + i2c_unregister_device(client_tuner); + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + port->i2c_client_demod = NULL; + goto frontend_detach; + } + port->i2c_client_tuner = client_tuner; + break; + } + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", @@ -2036,123 +2235,29 @@ static int dvb_register(struct cx23885_tsport *port) if (ret) goto frontend_detach; - /* init CI & MAC */ - switch (dev->board) { - case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: { - static struct netup_card_info cinfo; - - netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo); - memcpy(port->frontends.adapter.proposed_mac, - cinfo.port[port->nr - 1].mac, 6); - printk(KERN_INFO "NetUP Dual DVB-S2 CI card port%d MAC=%pM\n", - port->nr, port->frontends.adapter.proposed_mac); - - netup_ci_init(port); - break; - } - case CX23885_BOARD_NETUP_DUAL_DVB_T_C_CI_RF: { - struct altera_ci_config netup_ci_cfg = { - .dev = dev,/* magic number to identify*/ - .adapter = &port->frontends.adapter,/* for CI */ - .demux = &fe0->dvb.demux,/* for hw pid filter */ - .fpga_rw = netup_altera_fpga_rw, - }; - - altera_ci_init(&netup_ci_cfg, port->nr); - break; - } - case CX23885_BOARD_TEVII_S470: { - u8 eeprom[256]; /* 24C02 i2c eeprom */ - - if (port->nr != 1) - break; - - /* Read entire EEPROM */ - dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1; - tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, sizeof(eeprom)); - printk(KERN_INFO "TeVii S470 MAC= %pM\n", eeprom + 0xa0); - memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xa0, 6); - break; - } - case CX23885_BOARD_DVBSKY_T9580: - case CX23885_BOARD_DVBSKY_S950: - case CX23885_BOARD_DVBSKY_S952: - case CX23885_BOARD_DVBSKY_T982: { - u8 eeprom[256]; /* 24C02 i2c eeprom */ - - if (port->nr > 2) - break; - - /* Read entire EEPROM */ - dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1; - tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, - sizeof(eeprom)); - printk(KERN_INFO "%s port %d MAC address: %pM\n", - cx23885_boards[dev->board].name, port->nr, - eeprom + 0xc0 + (port->nr-1) * 8); - memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xc0 + - (port->nr-1) * 8, 6); - break; - } - case CX23885_BOARD_DVBSKY_S950C: - case CX23885_BOARD_DVBSKY_T980C: - case CX23885_BOARD_TT_CT2_4500_CI: { - u8 eeprom[256]; /* 24C02 i2c eeprom */ - - /* attach CI */ - memset(&sp2_config, 0, sizeof(sp2_config)); - sp2_config.dvb_adap = &port->frontends.adapter; - sp2_config.priv = port; - sp2_config.ci_control = cx23885_sp2_ci_ctrl; - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "sp2", I2C_NAME_SIZE); - info.addr = 0x40; - info.platform_data = &sp2_config; - request_module(info.type); - client_ci = i2c_new_device(&i2c_bus2->i2c_adap, &info); - if (client_ci == NULL || - client_ci->dev.driver == NULL) { - if (client_tuner) { - module_put(client_tuner->dev.driver->owner); - i2c_unregister_device(client_tuner); - } - if (client_demod) { - module_put(client_demod->dev.driver->owner); - i2c_unregister_device(client_demod); - } - goto frontend_detach; - } - if (!try_module_get(client_ci->dev.driver->owner)) { - i2c_unregister_device(client_ci); - if (client_tuner) { - module_put(client_tuner->dev.driver->owner); - i2c_unregister_device(client_tuner); - } - if (client_demod) { - module_put(client_demod->dev.driver->owner); - i2c_unregister_device(client_demod); - } - goto frontend_detach; - } - port->i2c_client_ci = client_ci; + ret = dvb_register_ci_mac(port); + if (ret) + goto frontend_detach; - if (port->nr != 1) - break; + return 0; - /* Read entire EEPROM */ - dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1; - tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, - sizeof(eeprom)); - printk(KERN_INFO "%s MAC address: %pM\n", - cx23885_boards[dev->board].name, eeprom + 0xc0); - memcpy(port->frontends.adapter.proposed_mac, eeprom + 0xc0, 6); - break; - } +frontend_detach: + /* remove I2C client for tuner */ + client_tuner = port->i2c_client_tuner; + if (client_tuner) { + module_put(client_tuner->dev.driver->owner); + i2c_unregister_device(client_tuner); + port->i2c_client_tuner = NULL; } - return ret; + /* remove I2C client for demodulator */ + client_demod = port->i2c_client_demod; + if (client_demod) { + module_put(client_demod->dev.driver->owner); + i2c_unregister_device(client_demod); + port->i2c_client_demod = NULL; + } -frontend_detach: port->gate_ctrl = NULL; vb2_dvb_dealloc_frontends(&port->frontends); return -EINVAL; diff --git a/drivers/media/pci/cx23885/cx23885-i2c.c b/drivers/media/pci/cx23885/cx23885-i2c.c index fd71306af6e2..1135ea3f6ce5 100644 --- a/drivers/media/pci/cx23885/cx23885-i2c.c +++ b/drivers/media/pci/cx23885/cx23885-i2c.c @@ -300,8 +300,8 @@ static void do_i2c_scan(char *name, struct i2c_client *c) rc = i2c_master_recv(c, &buf, 0); if (rc < 0) continue; - printk(KERN_INFO "%s: i2c scan: found device @ 0x%x [%s]\n", - name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); + printk(KERN_INFO "%s: i2c scan: found device @ 0x%04x [%s]\n", + name, i, i2c_devs[i] ? i2c_devs[i] : "???"); } } diff --git a/drivers/media/pci/cx23885/cx23885.h b/drivers/media/pci/cx23885/cx23885.h index f55cd12da0fd..aeda8d3990ae 100644 --- a/drivers/media/pci/cx23885/cx23885.h +++ b/drivers/media/pci/cx23885/cx23885.h @@ -99,6 +99,8 @@ #define CX23885_BOARD_DVBSKY_S950 49 #define CX23885_BOARD_DVBSKY_S952 50 #define CX23885_BOARD_DVBSKY_T982 51 +#define CX23885_BOARD_HAUPPAUGE_HVR5525 52 +#define CX23885_BOARD_HAUPPAUGE_STARBURST 53 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 diff --git a/drivers/media/pci/cx25821/Kconfig b/drivers/media/pci/cx25821/Kconfig index 6439a847680c..1755d3d2feaa 100644 --- a/drivers/media/pci/cx25821/Kconfig +++ b/drivers/media/pci/cx25821/Kconfig @@ -2,8 +2,7 @@ config VIDEO_CX25821 tristate "Conexant cx25821 support" depends on VIDEO_DEV && PCI && I2C select I2C_ALGOBIT - select VIDEO_BTCX - select VIDEOBUF_DMA_SG + select VIDEOBUF2_DMA_SG ---help--- This is a video4linux driver for Conexant 25821 based TV cards. diff --git a/drivers/media/pci/cx25821/Makefile b/drivers/media/pci/cx25821/Makefile index fb76c3d3713a..c8f8598a2b86 100644 --- a/drivers/media/pci/cx25821/Makefile +++ b/drivers/media/pci/cx25821/Makefile @@ -1,9 +1,8 @@ cx25821-y := cx25821-core.o cx25821-cards.o cx25821-i2c.o \ cx25821-gpio.o cx25821-medusa-video.o \ - cx25821-video.o cx25821-video-upstream.o + cx25821-video.o obj-$(CONFIG_VIDEO_CX25821) += cx25821.o obj-$(CONFIG_VIDEO_CX25821_ALSA) += cx25821-alsa.o ccflags-y += -Idrivers/media/i2c -ccflags-y += -Idrivers/media/common diff --git a/drivers/media/pci/cx25821/cx25821-alsa.c b/drivers/media/pci/cx25821/cx25821-alsa.c index 2dd5bcaa7e53..24f964bcc53a 100644 --- a/drivers/media/pci/cx25821/cx25821-alsa.c +++ b/drivers/media/pci/cx25821/cx25821-alsa.c @@ -63,8 +63,11 @@ static int devno; struct cx25821_audio_buffer { unsigned int bpl; - struct btcx_riscmem risc; - struct videobuf_dmabuf dma; + struct cx25821_riscmem risc; + void *vaddr; + struct scatterlist *sglist; + int sglen; + int nr_pages; }; struct cx25821_audio_dev { @@ -87,8 +90,6 @@ struct cx25821_audio_dev { unsigned int period_size; unsigned int num_periods; - struct videobuf_dmabuf *dma_risc; - struct cx25821_audio_buffer *buf; struct snd_pcm_substream *substream; @@ -142,6 +143,83 @@ MODULE_PARM_DESC(debug, "enable debug messages"); #define PCI_MSK_AUD_EXT (1 << 4) #define PCI_MSK_AUD_INT (1 << 3) + +static int cx25821_alsa_dma_init(struct cx25821_audio_dev *chip, int nr_pages) +{ + struct cx25821_audio_buffer *buf = chip->buf; + struct page *pg; + int i; + + buf->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT); + if (NULL == buf->vaddr) { + dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages); + return -ENOMEM; + } + + dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n", + (unsigned long)buf->vaddr, + nr_pages << PAGE_SHIFT); + + memset(buf->vaddr, 0, nr_pages << PAGE_SHIFT); + buf->nr_pages = nr_pages; + + buf->sglist = vzalloc(buf->nr_pages * sizeof(*buf->sglist)); + if (NULL == buf->sglist) + goto vzalloc_err; + + sg_init_table(buf->sglist, buf->nr_pages); + for (i = 0; i < buf->nr_pages; i++) { + pg = vmalloc_to_page(buf->vaddr + i * PAGE_SIZE); + if (NULL == pg) + goto vmalloc_to_page_err; + sg_set_page(&buf->sglist[i], pg, PAGE_SIZE, 0); + } + return 0; + +vmalloc_to_page_err: + vfree(buf->sglist); + buf->sglist = NULL; +vzalloc_err: + vfree(buf->vaddr); + buf->vaddr = NULL; + return -ENOMEM; +} + +static int cx25821_alsa_dma_map(struct cx25821_audio_dev *dev) +{ + struct cx25821_audio_buffer *buf = dev->buf; + + buf->sglen = dma_map_sg(&dev->pci->dev, buf->sglist, + buf->nr_pages, PCI_DMA_FROMDEVICE); + + if (0 == buf->sglen) { + pr_warn("%s: cx25821_alsa_map_sg failed\n", __func__); + return -ENOMEM; + } + return 0; +} + +static int cx25821_alsa_dma_unmap(struct cx25821_audio_dev *dev) +{ + struct cx25821_audio_buffer *buf = dev->buf; + + if (!buf->sglen) + return 0; + + dma_unmap_sg(&dev->pci->dev, buf->sglist, buf->sglen, PCI_DMA_FROMDEVICE); + buf->sglen = 0; + return 0; +} + +static int cx25821_alsa_dma_free(struct cx25821_audio_buffer *buf) +{ + vfree(buf->sglist); + buf->sglist = NULL; + vfree(buf->vaddr); + buf->vaddr = NULL; + return 0; +} + /* * BOARD Specific: Sets audio DMA */ @@ -330,15 +408,17 @@ out: static int dsp_buffer_free(struct cx25821_audio_dev *chip) { + struct cx25821_riscmem *risc = &chip->buf->risc; + BUG_ON(!chip->dma_size); dprintk(2, "Freeing buffer\n"); - videobuf_dma_unmap(&chip->pci->dev, chip->dma_risc); - videobuf_dma_free(chip->dma_risc); - btcx_riscmem_free(chip->pci, &chip->buf->risc); + cx25821_alsa_dma_unmap(chip); + cx25821_alsa_dma_free(chip->buf); + pci_free_consistent(chip->pci, risc->size, risc->cpu, risc->dma); kfree(chip->buf); - chip->dma_risc = NULL; + chip->buf = NULL; chip->dma_size = 0; return 0; @@ -430,8 +510,6 @@ static int snd_cx25821_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { struct cx25821_audio_dev *chip = snd_pcm_substream_chip(substream); - struct videobuf_dmabuf *dma; - struct cx25821_audio_buffer *buf; int ret; @@ -455,19 +533,18 @@ static int snd_cx25821_hw_params(struct snd_pcm_substream *substream, chip->period_size = AUDIO_LINE_SIZE; buf->bpl = chip->period_size; + chip->buf = buf; - dma = &buf->dma; - videobuf_dma_init(dma); - ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE, + ret = cx25821_alsa_dma_init(chip, (PAGE_ALIGN(chip->dma_size) >> PAGE_SHIFT)); if (ret < 0) goto error; - ret = videobuf_dma_map(&chip->pci->dev, dma); + ret = cx25821_alsa_dma_map(chip); if (ret < 0) goto error; - ret = cx25821_risc_databuffer_audio(chip->pci, &buf->risc, dma->sglist, + ret = cx25821_risc_databuffer_audio(chip->pci, &buf->risc, buf->sglist, chip->period_size, chip->num_periods, 1); if (ret < 0) { pr_info("DEBUG: ERROR after cx25821_risc_databuffer_audio()\n"); @@ -479,16 +556,14 @@ static int snd_cx25821_hw_params(struct snd_pcm_substream *substream, buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma); buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - chip->buf = buf; - chip->dma_risc = dma; - - substream->runtime->dma_area = chip->dma_risc->vaddr; + substream->runtime->dma_area = chip->buf->vaddr; substream->runtime->dma_bytes = chip->dma_size; substream->runtime->dma_addr = 0; return 0; error: + chip->buf = NULL; kfree(buf); return ret; } diff --git a/drivers/media/pci/cx25821/cx25821-core.c b/drivers/media/pci/cx25821/cx25821-core.c index 389fffd2f36f..559f8293c53a 100644 --- a/drivers/media/pci/cx25821/cx25821-core.c +++ b/drivers/media/pci/cx25821/cx25821-core.c @@ -874,10 +874,9 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) if (dev->pci->device != 0x8210) { pr_info("%s(): Exiting. Incorrect Hardware device = 0x%02x\n", __func__, dev->pci->device); - return -1; - } else { - pr_info("Athena Hardware device = 0x%02x\n", dev->pci->device); + return -ENODEV; } + pr_info("Athena Hardware device = 0x%02x\n", dev->pci->device); /* Apply a sensible clock frequency for the PCIe bridge */ dev->clk_freq = 28000000; @@ -966,11 +965,15 @@ void cx25821_dev_unregister(struct cx25821_dev *dev) release_mem_region(dev->base_io_addr, pci_resource_len(dev->pci, 0)); - for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; i++) { + for (i = 0; i < MAX_VID_CAP_CHANNEL_NUM - 1; i++) { if (i == SRAM_CH08) /* audio channel */ continue; + /* + * TODO: enable when video output is properly + * supported. if (i == SRAM_CH09 || i == SRAM_CH10) cx25821_free_mem_upstream(&dev->channels[i]); + */ cx25821_video_unregister(dev, i); } @@ -979,14 +982,41 @@ void cx25821_dev_unregister(struct cx25821_dev *dev) } EXPORT_SYMBOL(cx25821_dev_unregister); +int cx25821_riscmem_alloc(struct pci_dev *pci, + struct cx25821_riscmem *risc, + unsigned int size) +{ + __le32 *cpu; + dma_addr_t dma = 0; + + if (NULL != risc->cpu && risc->size < size) + pci_free_consistent(pci, risc->size, risc->cpu, risc->dma); + if (NULL == risc->cpu) { + cpu = pci_zalloc_consistent(pci, size, &dma); + if (NULL == cpu) + return -ENOMEM; + risc->cpu = cpu; + risc->dma = dma; + risc->size = size; + } + return 0; +} +EXPORT_SYMBOL(cx25821_riscmem_alloc); + static __le32 *cx25821_risc_field(__le32 * rp, struct scatterlist *sglist, unsigned int offset, u32 sync_line, unsigned int bpl, unsigned int padding, - unsigned int lines) + unsigned int lines, bool jump) { struct scatterlist *sg; unsigned int line, todo; + if (jump) { + *(rp++) = cpu_to_le32(RISC_JUMP); + *(rp++) = cpu_to_le32(0); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + } + /* sync instruction */ if (sync_line != NO_SYNC_LINE) *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); @@ -1035,7 +1065,7 @@ static __le32 *cx25821_risc_field(__le32 * rp, struct scatterlist *sglist, return rp; } -int cx25821_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, +int cx25821_risc_buffer(struct pci_dev *pci, struct cx25821_riscmem *risc, struct scatterlist *sglist, unsigned int top_offset, unsigned int bottom_offset, unsigned int bpl, unsigned int padding, unsigned int lines) @@ -1052,14 +1082,14 @@ int cx25821_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, fields++; /* estimate risc mem: worst case is one write per page border + - one write per scan line + syncs + jump (all 2 dwords). Padding + one write per scan line + syncs + jump (all 3 dwords). Padding can cause next bpl to start close to a page border. First DMA region may be smaller than PAGE_SIZE */ /* write and jump need and extra dword */ instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines); - instructions += 2; - rc = btcx_riscmem_alloc(pci, risc, instructions * 12); + instructions += 5; + rc = cx25821_riscmem_alloc(pci, risc, instructions * 12); if (rc < 0) return rc; @@ -1069,17 +1099,17 @@ int cx25821_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, if (UNSET != top_offset) { rp = cx25821_risc_field(rp, sglist, top_offset, 0, bpl, padding, - lines); + lines, true); } if (UNSET != bottom_offset) { rp = cx25821_risc_field(rp, sglist, bottom_offset, 0x200, bpl, - padding, lines); + padding, lines, UNSET == top_offset); } /* save pointer to jmp instruction address */ risc->jmp = rp; - BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); + BUG_ON((risc->jmp - risc->cpu + 3) * sizeof(*risc->cpu) > risc->size); return 0; } @@ -1146,7 +1176,7 @@ static __le32 *cx25821_risc_field_audio(__le32 * rp, struct scatterlist *sglist, } int cx25821_risc_databuffer_audio(struct pci_dev *pci, - struct btcx_riscmem *risc, + struct cx25821_riscmem *risc, struct scatterlist *sglist, unsigned int bpl, unsigned int lines, unsigned int lpi) @@ -1163,7 +1193,7 @@ int cx25821_risc_databuffer_audio(struct pci_dev *pci, instructions = 1 + (bpl * lines) / PAGE_SIZE + lines; instructions += 1; - rc = btcx_riscmem_alloc(pci, risc, instructions * 12); + rc = cx25821_riscmem_alloc(pci, risc, instructions * 12); if (rc < 0) return rc; @@ -1179,40 +1209,14 @@ int cx25821_risc_databuffer_audio(struct pci_dev *pci, } EXPORT_SYMBOL(cx25821_risc_databuffer_audio); -int cx25821_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, - u32 reg, u32 mask, u32 value) -{ - __le32 *rp; - int rc; - - rc = btcx_riscmem_alloc(pci, risc, 4 * 16); - - if (rc < 0) - return rc; - - /* write risc instructions */ - rp = risc->cpu; - - *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ1); - *(rp++) = cpu_to_le32(reg); - *(rp++) = cpu_to_le32(value); - *(rp++) = cpu_to_le32(mask); - *(rp++) = cpu_to_le32(RISC_JUMP); - *(rp++) = cpu_to_le32(risc->dma); - *(rp++) = cpu_to_le32(0); /* bits 63-32 */ - return 0; -} - -void cx25821_free_buffer(struct videobuf_queue *q, struct cx25821_buffer *buf) +void cx25821_free_buffer(struct cx25821_dev *dev, struct cx25821_buffer *buf) { - struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); - BUG_ON(in_interrupt()); - videobuf_waiton(q, &buf->vb, 0, 0); - videobuf_dma_unmap(q->dev, dma); - videobuf_dma_free(dma); - btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc); - buf->vb.state = VIDEOBUF_NEEDS_INIT; + if (WARN_ON(buf->risc.size == 0)) + return; + pci_free_consistent(dev->pci, + buf->risc.size, buf->risc.cpu, buf->risc.dma); + memset(&buf->risc, 0, sizeof(buf->risc)); } static irqreturn_t cx25821_irq(int irq, void *dev_id) @@ -1297,14 +1301,15 @@ static int cx25821_initdev(struct pci_dev *pci_dev, goto fail_unregister_device; } + dev->alloc_ctx = vb2_dma_sg_init_ctx(&pci_dev->dev); + if (IS_ERR(dev->alloc_ctx)) { + err = PTR_ERR(dev->alloc_ctx); + goto fail_unregister_pci; + } err = cx25821_dev_setup(dev); - if (err) { - if (err == -EBUSY) - goto fail_unregister_device; - else - goto fail_unregister_pci; - } + if (err) + goto fail_free_ctx; /* print pci info */ pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev); @@ -1334,6 +1339,8 @@ fail_irq: pr_info("cx25821_initdev() can't get IRQ !\n"); cx25821_dev_unregister(dev); +fail_free_ctx: + vb2_dma_sg_cleanup_ctx(dev->alloc_ctx); fail_unregister_pci: pci_disable_device(pci_dev); fail_unregister_device: @@ -1357,6 +1364,7 @@ static void cx25821_finidev(struct pci_dev *pci_dev) free_irq(pci_dev->irq, dev); cx25821_dev_unregister(dev); + vb2_dma_sg_cleanup_ctx(dev->alloc_ctx); v4l2_device_unregister(v4l2_dev); kfree(dev); } diff --git a/drivers/media/pci/cx25821/cx25821-video.c b/drivers/media/pci/cx25821/cx25821-video.c index 3a419f134584..7bc495e4ece2 100644 --- a/drivers/media/pci/cx25821/cx25821-video.c +++ b/drivers/media/pci/cx25821/cx25821-video.c @@ -3,7 +3,7 @@ * * Copyright (C) 2009 Conexant Systems Inc. * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> - * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * Based on Steven Toth <stoth@linuxtv.org> cx25821 driver * Parts adapted/taken from Eduardo Moscoso Rubino * Copyright (C) 2009 Eduardo Moscoso Rubino <moscoso@TopoLogica.com> * @@ -46,10 +46,6 @@ static unsigned int irq_debug; module_param(irq_debug, int, 0644); MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]"); -static unsigned int vid_limit = 16; -module_param(vid_limit, int, 0644); -MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); - #define FORMAT_FLAGS_PACKED 0x01 static const struct cx25821_fmt formats[] = { @@ -76,41 +72,6 @@ static const struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc) return NULL; } -void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, - u32 count) -{ - struct cx25821_buffer *buf; - int bc; - - for (bc = 0;; bc++) { - if (list_empty(&q->active)) { - dprintk(1, "bc=%d (=0: active empty)\n", bc); - break; - } - - buf = list_entry(q->active.next, struct cx25821_buffer, - vb.queue); - - /* count comes from the hw and it is 16bit wide -- - * this trick handles wrap-arounds correctly for - * up to 32767 buffers in flight... */ - if ((s16) (count - buf->count) < 0) - break; - - v4l2_get_timestamp(&buf->vb.ts); - buf->vb.state = VIDEOBUF_DONE; - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); - } - - if (list_empty(&q->active)) - del_timer(&q->timeout); - else - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - if (bc != 1) - pr_err("%s: %d buffers handled (should be 1)\n", __func__, bc); -} - int cx25821_start_video_dma(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, struct cx25821_buffer *buf, @@ -123,7 +84,6 @@ int cx25821_start_video_dma(struct cx25821_dev *dev, /* reset counter */ cx_write(channel->gpcnt_ctl, 3); - q->count = 1; /* enable irq */ cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << channel->i)); @@ -139,86 +99,8 @@ int cx25821_start_video_dma(struct cx25821_dev *dev, return 0; } -static int cx25821_restart_video_queue(struct cx25821_dev *dev, - struct cx25821_dmaqueue *q, - const struct sram_channel *channel) -{ - struct cx25821_buffer *buf, *prev; - struct list_head *item; - - if (!list_empty(&q->active)) { - buf = list_entry(q->active.next, struct cx25821_buffer, - vb.queue); - - cx25821_start_video_dma(dev, q, buf, channel); - - list_for_each(item, &q->active) { - buf = list_entry(item, struct cx25821_buffer, vb.queue); - buf->count = q->count++; - } - - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - return 0; - } - - prev = NULL; - for (;;) { - if (list_empty(&q->queued)) - return 0; - - buf = list_entry(q->queued.next, struct cx25821_buffer, - vb.queue); - - if (NULL == prev) { - list_move_tail(&buf->vb.queue, &q->active); - cx25821_start_video_dma(dev, q, buf, channel); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - } else if (prev->vb.width == buf->vb.width && - prev->vb.height == buf->vb.height && - prev->fmt == buf->fmt) { - list_move_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */ - } else { - return 0; - } - prev = buf; - } -} - -static void cx25821_vid_timeout(unsigned long data) -{ - struct cx25821_data *timeout_data = (struct cx25821_data *)data; - struct cx25821_dev *dev = timeout_data->dev; - const struct sram_channel *channel = timeout_data->channel; - struct cx25821_dmaqueue *q = &dev->channels[channel->i].dma_vidq; - struct cx25821_buffer *buf; - unsigned long flags; - - /* cx25821_sram_channel_dump(dev, channel); */ - cx_clear(channel->dma_ctl, 0x11); - - spin_lock_irqsave(&dev->slock, flags); - while (!list_empty(&q->active)) { - buf = list_entry(q->active.next, struct cx25821_buffer, - vb.queue); - list_del(&buf->vb.queue); - - buf->vb.state = VIDEOBUF_ERROR; - wake_up(&buf->vb.done); - } - - cx25821_restart_video_queue(dev, q, channel); - spin_unlock_irqrestore(&dev->slock, flags); -} - int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status) { - u32 count = 0; int handled = 0; u32 mask; const struct sram_channel *channel = dev->channels[chan_num].sram_channels; @@ -239,317 +121,201 @@ int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status) /* risc1 y */ if (status & FLD_VID_DST_RISC1) { - spin_lock(&dev->slock); - count = cx_read(channel->gpcnt); - cx25821_video_wakeup(dev, &dev->channels[channel->i].dma_vidq, - count); - spin_unlock(&dev->slock); - handled++; - } + struct cx25821_dmaqueue *dmaq = + &dev->channels[channel->i].dma_vidq; + struct cx25821_buffer *buf; - /* risc2 y */ - if (status & 0x10) { - dprintk(2, "stopper video\n"); spin_lock(&dev->slock); - cx25821_restart_video_queue(dev, - &dev->channels[channel->i].dma_vidq, channel); + if (!list_empty(&dmaq->active)) { + buf = list_entry(dmaq->active.next, + struct cx25821_buffer, queue); + + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + buf->vb.v4l2_buf.sequence = dmaq->count++; + list_del(&buf->queue); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); + } spin_unlock(&dev->slock); handled++; } return handled; } -static int cx25821_buffer_setup(struct videobuf_queue *q, unsigned int *count, - unsigned int *size) +static int cx25821_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct cx25821_channel *chan = q->priv_data; - - *size = chan->fmt->depth * chan->width * chan->height >> 3; + struct cx25821_channel *chan = q->drv_priv; + unsigned size = (chan->fmt->depth * chan->width * chan->height) >> 3; - if (0 == *count) - *count = 32; - - if (*size * *count > vid_limit * 1024 * 1024) - *count = (vid_limit * 1024 * 1024) / *size; + if (fmt && fmt->fmt.pix.sizeimage < size) + return -EINVAL; + *num_planes = 1; + sizes[0] = fmt ? fmt->fmt.pix.sizeimage : size; + alloc_ctxs[0] = chan->dev->alloc_ctx; return 0; } -static int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, - enum v4l2_field field) +static int cx25821_buffer_prepare(struct vb2_buffer *vb) { - struct cx25821_channel *chan = q->priv_data; + struct cx25821_channel *chan = vb->vb2_queue->drv_priv; struct cx25821_dev *dev = chan->dev; struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb); - int rc, init_buffer = 0; + struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0); u32 line0_offset; - struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); int bpl_local = LINE_SIZE_D1; + int ret; - BUG_ON(NULL == chan->fmt); - if (chan->width < 48 || chan->width > 720 || - chan->height < 32 || chan->height > 576) - return -EINVAL; - - buf->vb.size = (chan->width * chan->height * chan->fmt->depth) >> 3; + if (chan->pixel_formats == PIXEL_FRMT_411) + buf->bpl = (chan->fmt->depth * chan->width) >> 3; + else + buf->bpl = (chan->fmt->depth >> 3) * chan->width; - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + if (vb2_plane_size(vb, 0) < chan->height * buf->bpl) return -EINVAL; + vb2_set_plane_payload(vb, 0, chan->height * buf->bpl); + buf->vb.v4l2_buf.field = chan->field; - if (buf->fmt != chan->fmt || - buf->vb.width != chan->width || - buf->vb.height != chan->height || buf->vb.field != field) { - buf->fmt = chan->fmt; - buf->vb.width = chan->width; - buf->vb.height = chan->height; - buf->vb.field = field; - init_buffer = 1; - } + if (chan->pixel_formats == PIXEL_FRMT_411) { + bpl_local = buf->bpl; + } else { + bpl_local = buf->bpl; /* Default */ - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - init_buffer = 1; - rc = videobuf_iolock(q, &buf->vb, NULL); - if (0 != rc) { - printk(KERN_DEBUG pr_fmt("videobuf_iolock failed!\n")); - goto fail; + if (chan->use_cif_resolution) { + if (dev->tvnorm & V4L2_STD_625_50) + bpl_local = 352 << 1; + else + bpl_local = chan->cif_width << 1; } } - dprintk(1, "init_buffer=%d\n", init_buffer); - - if (init_buffer) { - if (chan->pixel_formats == PIXEL_FRMT_411) - buf->bpl = (buf->fmt->depth * buf->vb.width) >> 3; - else - buf->bpl = (buf->fmt->depth >> 3) * (buf->vb.width); - - if (chan->pixel_formats == PIXEL_FRMT_411) { - bpl_local = buf->bpl; - } else { - bpl_local = buf->bpl; /* Default */ - - if (chan->use_cif_resolution) { - if (dev->tvnorm & V4L2_STD_625_50) - bpl_local = 352 << 1; - else - bpl_local = chan->cif_width << 1; - } - } - - switch (buf->vb.field) { - case V4L2_FIELD_TOP: - cx25821_risc_buffer(dev->pci, &buf->risc, - dma->sglist, 0, UNSET, - buf->bpl, 0, buf->vb.height); - break; - case V4L2_FIELD_BOTTOM: - cx25821_risc_buffer(dev->pci, &buf->risc, - dma->sglist, UNSET, 0, - buf->bpl, 0, buf->vb.height); - break; - case V4L2_FIELD_INTERLACED: - /* All other formats are top field first */ - line0_offset = 0; - dprintk(1, "top field first\n"); - - cx25821_risc_buffer(dev->pci, &buf->risc, - dma->sglist, line0_offset, - bpl_local, bpl_local, bpl_local, - buf->vb.height >> 1); - break; - case V4L2_FIELD_SEQ_TB: - cx25821_risc_buffer(dev->pci, &buf->risc, - dma->sglist, - 0, buf->bpl * (buf->vb.height >> 1), - buf->bpl, 0, buf->vb.height >> 1); - break; - case V4L2_FIELD_SEQ_BT: - cx25821_risc_buffer(dev->pci, &buf->risc, - dma->sglist, - buf->bpl * (buf->vb.height >> 1), 0, - buf->bpl, 0, buf->vb.height >> 1); - break; - default: - BUG(); - } + switch (chan->field) { + case V4L2_FIELD_TOP: + ret = cx25821_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, 0, UNSET, + buf->bpl, 0, chan->height); + break; + case V4L2_FIELD_BOTTOM: + ret = cx25821_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, UNSET, 0, + buf->bpl, 0, chan->height); + break; + case V4L2_FIELD_INTERLACED: + /* All other formats are top field first */ + line0_offset = 0; + dprintk(1, "top field first\n"); + + ret = cx25821_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, line0_offset, + bpl_local, bpl_local, bpl_local, + chan->height >> 1); + break; + case V4L2_FIELD_SEQ_TB: + ret = cx25821_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, + 0, buf->bpl * (chan->height >> 1), + buf->bpl, 0, chan->height >> 1); + break; + case V4L2_FIELD_SEQ_BT: + ret = cx25821_risc_buffer(dev->pci, &buf->risc, + sgt->sgl, + buf->bpl * (chan->height >> 1), 0, + buf->bpl, 0, chan->height >> 1); + break; + default: + WARN_ON(1); + ret = -EINVAL; + break; } dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n", - buf, buf->vb.i, chan->width, chan->height, chan->fmt->depth, - chan->fmt->name, (unsigned long)buf->risc.dma); - - buf->vb.state = VIDEOBUF_PREPARED; - - return 0; + buf, buf->vb.v4l2_buf.index, chan->width, chan->height, + chan->fmt->depth, chan->fmt->name, + (unsigned long)buf->risc.dma); -fail: - cx25821_free_buffer(q, buf); - return rc; + return ret; } -static void cx25821_buffer_release(struct videobuf_queue *q, - struct videobuf_buffer *vb) +static void cx25821_buffer_finish(struct vb2_buffer *vb) { struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb); + struct cx25821_channel *chan = vb->vb2_queue->drv_priv; + struct cx25821_dev *dev = chan->dev; - cx25821_free_buffer(q, buf); -} - -static int cx25821_video_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct cx25821_channel *chan = video_drvdata(file); - - return videobuf_mmap_mapper(&chan->vidq, vma); + cx25821_free_buffer(dev, buf); } - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +static void cx25821_buffer_queue(struct vb2_buffer *vb) { struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb); - struct cx25821_buffer *prev; - struct cx25821_channel *chan = vq->priv_data; + struct cx25821_channel *chan = vb->vb2_queue->drv_priv; struct cx25821_dev *dev = chan->dev; + struct cx25821_buffer *prev; struct cx25821_dmaqueue *q = &dev->channels[chan->id].dma_vidq; - /* add jump to stopper */ - buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); - buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); - buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ - - dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); - - if (!list_empty(&q->queued)) { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, - buf->vb.i); - - } else if (list_empty(&q->active)) { - list_add_tail(&buf->vb.queue, &q->active); - cx25821_start_video_dma(dev, q, buf, chan->sram_channels); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); - dprintk(2, "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", - buf, buf->vb.i, buf->count, q->count); + buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 12); + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 12); + buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + + if (list_empty(&q->active)) { + list_add_tail(&buf->queue, &q->active); } else { + buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1); prev = list_entry(q->active.prev, struct cx25821_buffer, - vb.queue); - if (prev->vb.width == buf->vb.width - && prev->vb.height == buf->vb.height - && prev->fmt == buf->fmt) { - list_add_tail(&buf->vb.queue, &q->active); - buf->vb.state = VIDEOBUF_ACTIVE; - buf->count = q->count++; - prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); - - /* 64 bit bits 63-32 */ - prev->risc.jmp[2] = cpu_to_le32(0); - dprintk(2, "[%p/%d] buffer_queue - append to active, buf->count=%d\n", - buf, buf->vb.i, buf->count); - - } else { - list_add_tail(&buf->vb.queue, &q->queued); - buf->vb.state = VIDEOBUF_QUEUED; - dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, - buf->vb.i); - } + queue); + list_add_tail(&buf->queue, &q->active); + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); } - - if (list_empty(&q->active)) - dprintk(2, "active queue empty!\n"); } -static struct videobuf_queue_ops cx25821_video_qops = { - .buf_setup = cx25821_buffer_setup, - .buf_prepare = cx25821_buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = cx25821_buffer_release, -}; - -static ssize_t video_read(struct file *file, char __user * data, size_t count, - loff_t *ppos) +static int cx25821_start_streaming(struct vb2_queue *q, unsigned int count) { - struct v4l2_fh *fh = file->private_data; - struct cx25821_channel *chan = video_drvdata(file); + struct cx25821_channel *chan = q->drv_priv; struct cx25821_dev *dev = chan->dev; - int err = 0; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - if (chan->streaming_fh && chan->streaming_fh != fh) { - err = -EBUSY; - goto unlock; - } - chan->streaming_fh = fh; + struct cx25821_dmaqueue *dmaq = &dev->channels[chan->id].dma_vidq; + struct cx25821_buffer *buf = list_entry(dmaq->active.next, + struct cx25821_buffer, queue); - err = videobuf_read_one(&chan->vidq, data, count, ppos, - file->f_flags & O_NONBLOCK); -unlock: - mutex_unlock(&dev->lock); - return err; -} - -static unsigned int video_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct cx25821_channel *chan = video_drvdata(file); - unsigned long req_events = poll_requested_events(wait); - unsigned int res = v4l2_ctrl_poll(file, wait); - - if (req_events & (POLLIN | POLLRDNORM)) - res |= videobuf_poll_stream(file, &chan->vidq, wait); - return res; - - /* This doesn't belong in poll(). This can be done - * much better with vb2. We keep this code here as a - * reminder. - if ((res & POLLIN) && buf->vb.state == VIDEOBUF_DONE) { - struct cx25821_dev *dev = chan->dev; - - if (dev && chan->use_cif_resolution) { - u8 cam_id = *((char *)buf->vb.baddr + 3); - memcpy((char *)buf->vb.baddr, - (char *)buf->vb.baddr + (chan->width * 2), - (chan->width * 2)); - *((char *)buf->vb.baddr + 3) = cam_id; - } - } - */ + dmaq->count = 0; + cx25821_start_video_dma(dev, dmaq, buf, chan->sram_channels); + return 0; } -static int video_release(struct file *file) +static void cx25821_stop_streaming(struct vb2_queue *q) { - struct cx25821_channel *chan = video_drvdata(file); - struct v4l2_fh *fh = file->private_data; + struct cx25821_channel *chan = q->drv_priv; struct cx25821_dev *dev = chan->dev; - const struct sram_channel *sram_ch = - dev->channels[0].sram_channels; - - mutex_lock(&dev->lock); - /* stop the risc engine and fifo */ - cx_write(sram_ch->dma_ctl, 0); /* FIFO and RISC disable */ + struct cx25821_dmaqueue *dmaq = &dev->channels[chan->id].dma_vidq; + unsigned long flags; - /* stop video capture */ - if (chan->streaming_fh == fh) { - videobuf_queue_cancel(&chan->vidq); - chan->streaming_fh = NULL; - } + cx_write(chan->sram_channels->dma_ctl, 0); /* FIFO and RISC disable */ + spin_lock_irqsave(&dev->slock, flags); + while (!list_empty(&dmaq->active)) { + struct cx25821_buffer *buf = list_entry(dmaq->active.next, + struct cx25821_buffer, queue); - if (chan->vidq.read_buf) { - cx25821_buffer_release(&chan->vidq, chan->vidq.read_buf); - kfree(chan->vidq.read_buf); + list_del(&buf->queue); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); } - - videobuf_mmap_free(&chan->vidq); - mutex_unlock(&dev->lock); - - return v4l2_fh_release(file); + spin_unlock_irqrestore(&dev->slock, flags); } +static struct vb2_ops cx25821_video_qops = { + .queue_setup = cx25821_queue_setup, + .buf_prepare = cx25821_buffer_prepare, + .buf_finish = cx25821_buffer_finish, + .buf_queue = cx25821_buffer_queue, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = cx25821_start_streaming, + .stop_streaming = cx25821_stop_streaming, +}; + /* VIDEO IOCTLS */ static int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv, @@ -571,7 +337,7 @@ static int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.width = chan->width; f->fmt.pix.height = chan->height; - f->fmt.pix.field = chan->vidq.field; + f->fmt.pix.field = chan->field; f->fmt.pix.pixelformat = chan->fmt->fourcc; f->fmt.pix.bytesperline = (chan->width * chan->fmt->depth) >> 3; f->fmt.pix.sizeimage = chan->height * f->fmt.pix.bytesperline; @@ -632,7 +398,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, return err; chan->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); - chan->vidq.field = f->fmt.pix.field; + chan->field = f->fmt.pix.field; chan->width = f->fmt.pix.width; chan->height = f->fmt.pix.height; @@ -654,47 +420,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, return 0; } -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_channel *chan = video_drvdata(file); - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (chan->streaming_fh && chan->streaming_fh != priv) - return -EBUSY; - chan->streaming_fh = priv; - - return videobuf_streamon(&chan->vidq); -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct cx25821_channel *chan = video_drvdata(file); - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (chan->streaming_fh && chan->streaming_fh != priv) - return -EBUSY; - if (chan->streaming_fh == NULL) - return 0; - - chan->streaming_fh = NULL; - return videobuf_streamoff(&chan->vidq); -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - int ret_val = 0; - struct cx25821_channel *chan = video_drvdata(file); - - ret_val = videobuf_dqbuf(&chan->vidq, p, file->f_flags & O_NONBLOCK); - p->sequence = chan->dma_vidq.count; - - return ret_val; -} - static int vidioc_log_status(struct file *file, void *priv) { struct cx25821_channel *chan = video_drvdata(file); @@ -729,29 +454,6 @@ static int cx25821_vidioc_querycap(struct file *file, void *priv, return 0; } -static int cx25821_vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct cx25821_channel *chan = video_drvdata(file); - - return videobuf_reqbufs(&chan->vidq, p); -} - -static int cx25821_vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *p) -{ - struct cx25821_channel *chan = video_drvdata(file); - - return videobuf_querybuf(&chan->vidq, p); -} - -static int cx25821_vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - struct cx25821_channel *chan = video_drvdata(file); - - return videobuf_qbuf(&chan->vidq, p); -} - static int cx25821_vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorms) { struct cx25821_channel *chan = video_drvdata(file); @@ -880,7 +582,7 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *priv, return err; chan->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat); - chan->vidq.field = f->fmt.pix.field; + chan->field = f->fmt.pix.field; chan->width = f->fmt.pix.width; chan->height = f->fmt.pix.height; if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) @@ -890,52 +592,6 @@ static int vidioc_s_fmt_vid_out(struct file *file, void *priv, return 0; } -static ssize_t video_write(struct file *file, const char __user *data, size_t count, - loff_t *ppos) -{ - struct cx25821_channel *chan = video_drvdata(file); - struct cx25821_dev *dev = chan->dev; - struct v4l2_fh *fh = file->private_data; - int err = 0; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - if (chan->streaming_fh && chan->streaming_fh != fh) { - err = -EBUSY; - goto unlock; - } - if (!chan->streaming_fh) { - err = cx25821_vidupstream_init(chan, chan->pixel_formats); - if (err) - goto unlock; - chan->streaming_fh = fh; - } - - err = cx25821_write_frame(chan, data, count); - count -= err; - *ppos += err; - -unlock: - mutex_unlock(&dev->lock); - return err; -} - -static int video_out_release(struct file *file) -{ - struct cx25821_channel *chan = video_drvdata(file); - struct cx25821_dev *dev = chan->dev; - struct v4l2_fh *fh = file->private_data; - - mutex_lock(&dev->lock); - if (chan->streaming_fh == fh) { - cx25821_stop_upstream_video(chan); - chan->streaming_fh = NULL; - } - mutex_unlock(&dev->lock); - - return v4l2_fh_release(file); -} - static const struct v4l2_ctrl_ops cx25821_ctrl_ops = { .s_ctrl = cx25821_s_ctrl, }; @@ -943,11 +599,11 @@ static const struct v4l2_ctrl_ops cx25821_ctrl_ops = { static const struct v4l2_file_operations video_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, - .release = video_release, - .read = video_read, - .poll = video_poll, - .mmap = cx25821_video_mmap, + .release = vb2_fop_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, }; static const struct v4l2_ioctl_ops video_ioctl_ops = { @@ -956,17 +612,19 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = cx25821_vidioc_reqbufs, - .vidioc_querybuf = cx25821_vidioc_querybuf, - .vidioc_qbuf = cx25821_vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_g_std = cx25821_vidioc_g_std, .vidioc_s_std = cx25821_vidioc_s_std, .vidioc_enum_input = cx25821_vidioc_enum_input, .vidioc_g_input = cx25821_vidioc_g_input, .vidioc_s_input = cx25821_vidioc_s_input, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, .vidioc_log_status = vidioc_log_status, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, @@ -984,9 +642,11 @@ static const struct video_device cx25821_video_device = { static const struct v4l2_file_operations video_out_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, - .write = video_write, - .release = video_out_release, + .release = vb2_fop_release, + .write = vb2_fop_write, + .poll = vb2_fop_poll, .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, }; static const struct v4l2_ioctl_ops video_out_ioctl_ops = { @@ -1019,9 +679,6 @@ void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num) if (video_is_registered(&dev->channels[chan_num].vdev)) { video_unregister_device(&dev->channels[chan_num].vdev); v4l2_ctrl_handler_free(&dev->channels[chan_num].hdl); - - btcx_riscmem_free(dev->pci, - &dev->channels[chan_num].dma_vidq.stopper); } } @@ -1035,10 +692,11 @@ int cx25821_video_register(struct cx25821_dev *dev) spin_lock_init(&dev->slock); - for (i = 0; i < MAX_VID_CHANNEL_NUM - 1; ++i) { + for (i = 0; i < MAX_VID_CAP_CHANNEL_NUM - 1; ++i) { struct cx25821_channel *chan = &dev->channels[i]; struct video_device *vdev = &chan->vdev; struct v4l2_ctrl_handler *hdl = &chan->hdl; + struct vb2_queue *q; bool is_output = i > SRAM_CH08; if (i == SRAM_CH08) /* audio channel */ @@ -1066,11 +724,9 @@ int cx25821_video_register(struct cx25821_dev *dev) chan->out->chan = chan; } - cx25821_risc_stopper(dev->pci, &chan->dma_vidq.stopper, - chan->sram_channels->dma_ctl, 0x11, 0); - chan->sram_channels = &cx25821_sram_channels[i]; chan->width = 720; + chan->field = V4L2_FIELD_INTERLACED; if (dev->tvnorm & V4L2_STD_625_50) chan->height = 576; else @@ -1084,19 +740,27 @@ int cx25821_video_register(struct cx25821_dev *dev) cx_write(chan->sram_channels->int_stat, 0xffffffff); INIT_LIST_HEAD(&chan->dma_vidq.active); - INIT_LIST_HEAD(&chan->dma_vidq.queued); - chan->timeout_data.dev = dev; - chan->timeout_data.channel = &cx25821_sram_channels[i]; - chan->dma_vidq.timeout.function = cx25821_vid_timeout; - chan->dma_vidq.timeout.data = (unsigned long)&chan->timeout_data; - init_timer(&chan->dma_vidq.timeout); + q = &chan->vidq; + + q->type = is_output ? V4L2_BUF_TYPE_VIDEO_OUTPUT : + V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + q->io_modes |= is_output ? VB2_WRITE : VB2_READ; + q->gfp_flags = GFP_DMA32; + q->min_buffers_needed = 2; + q->drv_priv = chan; + q->buf_struct_size = sizeof(struct cx25821_buffer); + q->ops = &cx25821_video_qops; + q->mem_ops = &vb2_dma_sg_memops; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &dev->lock; - if (!is_output) - videobuf_queue_sg_init(&chan->vidq, &cx25821_video_qops, &dev->pci->dev, - &dev->slock, V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, sizeof(struct cx25821_buffer), - chan, &dev->lock); + if (!is_output) { + err = vb2_queue_init(q); + if (err < 0) + goto fail_unreg; + } /* register v4l devices */ *vdev = is_output ? cx25821_video_out_device : cx25821_video_device; @@ -1106,6 +770,7 @@ int cx25821_video_register(struct cx25821_dev *dev) else vdev->vfl_dir = VFL_DIR_TX; vdev->lock = &dev->lock; + vdev->queue = q; snprintf(vdev->name, sizeof(vdev->name), "%s #%d", dev->name, i); video_set_drvdata(vdev, chan); diff --git a/drivers/media/pci/cx25821/cx25821.h b/drivers/media/pci/cx25821/cx25821.h index 90bdc196929f..d81a08a2df4f 100644 --- a/drivers/media/pci/cx25821/cx25821.h +++ b/drivers/media/pci/cx25821/cx25821.h @@ -34,9 +34,8 @@ #include <media/v4l2-common.h> #include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> -#include <media/videobuf-dma-sg.h> +#include <media/videobuf2-dma-sg.h> -#include "btcx-risc.h" #include "cx25821-reg.h" #include "cx25821-medusa-reg.h" #include "cx25821-sram.h" @@ -89,6 +88,13 @@ #define CX25821_BOARD_CONEXANT_ATHENA10 1 #define MAX_VID_CHANNEL_NUM 12 + +/* + * Maximum capture-only channels. This can go away once video/audio output + * is fully supported in this driver. + */ +#define MAX_VID_CAP_CHANNEL_NUM 10 + #define VID_CHANNEL_NUM 8 struct cx25821_fmt { @@ -111,16 +117,23 @@ enum cx25821_src_sel_type { CX25821_SRC_SEL_PARALLEL_MPEG_VIDEO }; +struct cx25821_riscmem { + unsigned int size; + __le32 *cpu; + __le32 *jmp; + dma_addr_t dma; +}; + /* buffer for one video frame */ struct cx25821_buffer { /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; + struct vb2_buffer vb; + struct list_head queue; /* cx25821 specific */ unsigned int bpl; - struct btcx_riscmem risc; + struct cx25821_riscmem risc; const struct cx25821_fmt *fmt; - u32 count; }; enum port { @@ -159,17 +172,9 @@ struct cx25821_i2c { struct cx25821_dmaqueue { struct list_head active; - struct list_head queued; - struct timer_list timeout; - struct btcx_riscmem stopper; u32 count; }; -struct cx25821_data { - struct cx25821_dev *dev; - const struct sram_channel *channel; -}; - struct cx25821_dev; struct cx25821_channel; @@ -207,18 +212,17 @@ struct cx25821_video_out_data { struct cx25821_channel { unsigned id; struct cx25821_dev *dev; - struct v4l2_fh *streaming_fh; struct v4l2_ctrl_handler hdl; - struct cx25821_data timeout_data; struct video_device vdev; struct cx25821_dmaqueue dma_vidq; - struct videobuf_queue vidq; + struct vb2_queue vidq; const struct sram_channel *sram_channels; const struct cx25821_fmt *fmt; + unsigned field; unsigned int width, height; int pixel_formats; int use_cif_resolution; @@ -244,6 +248,7 @@ struct cx25821_dev { int hwrevision; /* used by cx25821-alsa */ struct snd_card *card; + void *alloc_ctx; u32 clk_freq; @@ -405,21 +410,22 @@ extern int cx25821_sram_channel_setup(struct cx25821_dev *dev, const struct sram_channel *ch, unsigned int bpl, u32 risc); -extern int cx25821_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, +extern int cx25821_riscmem_alloc(struct pci_dev *pci, + struct cx25821_riscmem *risc, + unsigned int size); +extern int cx25821_risc_buffer(struct pci_dev *pci, struct cx25821_riscmem *risc, struct scatterlist *sglist, unsigned int top_offset, unsigned int bottom_offset, unsigned int bpl, unsigned int padding, unsigned int lines); extern int cx25821_risc_databuffer_audio(struct pci_dev *pci, - struct btcx_riscmem *risc, + struct cx25821_riscmem *risc, struct scatterlist *sglist, unsigned int bpl, unsigned int lines, unsigned int lpi); -extern void cx25821_free_buffer(struct videobuf_queue *q, +extern void cx25821_free_buffer(struct cx25821_dev *dev, struct cx25821_buffer *buf); -extern int cx25821_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, - u32 reg, u32 mask, u32 value); extern void cx25821_sram_channel_dump(struct cx25821_dev *dev, const struct sram_channel *ch); extern void cx25821_sram_channel_dump_audio(struct cx25821_dev *dev, diff --git a/drivers/media/pci/cx88/cx88-blackbird.c b/drivers/media/pci/cx88/cx88-blackbird.c index d3c79d964f2c..b6be46e94289 100644 --- a/drivers/media/pci/cx88/cx88-blackbird.c +++ b/drivers/media/pci/cx88/cx88-blackbird.c @@ -1234,6 +1234,3 @@ static void __exit blackbird_fini(void) module_init(blackbird_init); module_exit(blackbird_fini); - -module_param_named(video_debug,cx8802_mpeg_template.debug, int, 0644); -MODULE_PARM_DESC(debug,"enable debug messages [video]"); diff --git a/drivers/media/pci/cx88/cx88-core.c b/drivers/media/pci/cx88/cx88-core.c index dee177ed5fe9..c38d5a12e277 100644 --- a/drivers/media/pci/cx88/cx88-core.c +++ b/drivers/media/pci/cx88/cx88-core.c @@ -1091,10 +1091,3 @@ EXPORT_SYMBOL(cx88_core_put); EXPORT_SYMBOL(cx88_ir_start); EXPORT_SYMBOL(cx88_ir_stop); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off - */ diff --git a/drivers/media/pci/cx88/cx88-dvb.c b/drivers/media/pci/cx88/cx88-dvb.c index 5780e2f013b4..1b2ed238cdb6 100644 --- a/drivers/media/pci/cx88/cx88-dvb.c +++ b/drivers/media/pci/cx88/cx88-dvb.c @@ -1504,8 +1504,8 @@ static int dvb_register(struct cx8802_dev *dev) fe0->dvb.frontend = dvb_attach(stv0288_attach, &tevii_tuner_earda_config, &core->i2c_adap); - if (fe0->dvb.frontend != NULL) { - if (!dvb_attach(stb6000_attach, fe0->dvb.frontend, 0x61, + if (fe0->dvb.frontend != NULL) { + if (!dvb_attach(stb6000_attach, fe0->dvb.frontend, 0x61, &core->i2c_adap)) goto frontend_detach; core->prev_set_voltage = fe0->dvb.frontend->ops.set_voltage; diff --git a/drivers/media/pci/cx88/cx88-mpeg.c b/drivers/media/pci/cx88/cx88-mpeg.c index 1c1f69e6b0b9..a369b0840acf 100644 --- a/drivers/media/pci/cx88/cx88-mpeg.c +++ b/drivers/media/pci/cx88/cx88-mpeg.c @@ -833,10 +833,3 @@ EXPORT_SYMBOL(cx8802_start_dma); EXPORT_SYMBOL(cx8802_register_driver); EXPORT_SYMBOL(cx8802_unregister_driver); EXPORT_SYMBOL(cx8802_get_driver); -/* ----------------------------------------------------------- */ -/* - * Local variables: - * c-basic-offset: 8 - * End: - * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off - */ diff --git a/drivers/media/pci/cx88/cx88-tvaudio.c b/drivers/media/pci/cx88/cx88-tvaudio.c index 424fd97495dc..6bbce6ad6295 100644 --- a/drivers/media/pci/cx88/cx88-tvaudio.c +++ b/drivers/media/pci/cx88/cx88-tvaudio.c @@ -1050,10 +1050,3 @@ EXPORT_SYMBOL(cx88_newstation); EXPORT_SYMBOL(cx88_set_stereo); EXPORT_SYMBOL(cx88_get_stereo); EXPORT_SYMBOL(cx88_audio_thread); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - * kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off - */ diff --git a/drivers/media/pci/ivtv/ivtv-irq.c b/drivers/media/pci/ivtv/ivtv-irq.c index ab6d5d25aa6f..e7d701777e53 100644 --- a/drivers/media/pci/ivtv/ivtv-irq.c +++ b/drivers/media/pci/ivtv/ivtv-irq.c @@ -357,7 +357,6 @@ void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock) u32 uv_offset = offset + IVTV_YUV_BUFFER_UV_OFFSET; int y_done = 0; int bytes_written = 0; - unsigned long flags = 0; int idx = 0; IVTV_DEBUG_HI_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset); @@ -407,16 +406,21 @@ void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock) /* Sync Hardware SG List of buffers */ ivtv_stream_sync_for_device(s); - if (lock) + if (lock) { + unsigned long flags = 0; + spin_lock_irqsave(&itv->dma_reg_lock, flags); - if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) { - ivtv_dma_dec_start(s); - } - else { - set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags); - } - if (lock) + if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) + ivtv_dma_dec_start(s); + else + set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags); spin_unlock_irqrestore(&itv->dma_reg_lock, flags); + } else { + if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) + ivtv_dma_dec_start(s); + else + set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags); + } } static void ivtv_dma_enc_start_xfer(struct ivtv_stream *s) diff --git a/drivers/media/pci/mantis/mantis_core.c b/drivers/media/pci/mantis/mantis_core.c index 684d9061fe2a..82220ea72dd3 100644 --- a/drivers/media/pci/mantis/mantis_core.c +++ b/drivers/media/pci/mantis/mantis_core.c @@ -56,29 +56,6 @@ static int read_eeprom_byte(struct mantis_pci *mantis, u8 *data, u8 length) return 0; } -static int write_eeprom_byte(struct mantis_pci *mantis, u8 *data, u8 length) -{ - int err; - - struct i2c_msg msg = { - .addr = 0x50, - .flags = 0, - .buf = data, - .len = length - }; - - err = i2c_transfer(&mantis->adapter, &msg, 1); - if (err < 0) { - dprintk(verbose, MANTIS_ERROR, 1, - "ERROR: i2c write: < err=%i length=0x%02x d0=0x%02x, d1=0x%02x >", - err, length, data[0], data[1]); - - return err; - } - - return 0; -} - static int get_mac_address(struct mantis_pci *mantis) { int err; diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index 701b52f34689..99d09a7566d3 100644 --- a/drivers/media/pci/saa7134/saa7134-video.c +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -1084,11 +1084,6 @@ static int saa7134_s_ctrl(struct v4l2_ctrl *ctrl) /* ------------------------------------------------------------------ */ -static inline struct vb2_queue *saa7134_queue(struct file *file) -{ - return video_devdata(file)->queue; -} - static int video_open(struct file *file) { struct video_device *vdev = video_devdata(file); diff --git a/drivers/media/pci/smipcie/smipcie.c b/drivers/media/pci/smipcie/smipcie.c index f773350e67b9..36c8ed77309c 100644 --- a/drivers/media/pci/smipcie/smipcie.c +++ b/drivers/media/pci/smipcie/smipcie.c @@ -448,16 +448,19 @@ static void smi_port_exit(struct smi_port *port) port->enable = 0; } -static void smi_port_irq(struct smi_port *port, u32 int_status) +static int smi_port_irq(struct smi_port *port, u32 int_status) { u32 port_req_irq = port->_dmaInterruptCH0 | port->_dmaInterruptCH1; + int handled = 0; if (int_status & port_req_irq) { smi_port_disableInterrupt(port); port->_int_status = int_status; smi_port_clearInterrupt(port); tasklet_schedule(&port->tasklet); + handled = 1; } + return handled; } static irqreturn_t smi_irq_handler(int irq, void *dev_id) @@ -465,18 +468,19 @@ static irqreturn_t smi_irq_handler(int irq, void *dev_id) struct smi_dev *dev = dev_id; struct smi_port *port0 = &dev->ts_port[0]; struct smi_port *port1 = &dev->ts_port[1]; + int handled = 0; u32 intr_status = smi_read(MSI_INT_STATUS); /* ts0 interrupt.*/ if (dev->info->ts_0) - smi_port_irq(port0, intr_status); + handled += smi_port_irq(port0, intr_status); /* ts1 interrupt.*/ if (dev->info->ts_1) - smi_port_irq(port1, intr_status); + handled += smi_port_irq(port1, intr_status); - return IRQ_HANDLED; + return IRQ_RETVAL(handled); } static struct i2c_client *smi_add_i2c_client(struct i2c_adapter *adapter, diff --git a/drivers/media/pci/solo6x10/solo6x10-core.c b/drivers/media/pci/solo6x10/solo6x10-core.c index 8cbe6b49f4c2..570d119ea18b 100644 --- a/drivers/media/pci/solo6x10/solo6x10-core.c +++ b/drivers/media/pci/solo6x10/solo6x10-core.c @@ -182,7 +182,7 @@ static ssize_t eeprom_store(struct device *dev, struct device_attribute *attr, { struct solo_dev *solo_dev = container_of(dev, struct solo_dev, dev); - unsigned short *p = (unsigned short *)buf; + u16 *p = (u16 *)buf; int i; if (count & 0x1) @@ -212,7 +212,7 @@ static ssize_t eeprom_show(struct device *dev, struct device_attribute *attr, { struct solo_dev *solo_dev = container_of(dev, struct solo_dev, dev); - unsigned short *p = (unsigned short *)buf; + u16 *p = (u16 *)buf; int count = (full_eeprom ? 128 : 64); int i; diff --git a/drivers/media/pci/solo6x10/solo6x10-eeprom.c b/drivers/media/pci/solo6x10/solo6x10-eeprom.c index da25ce4a6952..8e81186dc785 100644 --- a/drivers/media/pci/solo6x10/solo6x10-eeprom.c +++ b/drivers/media/pci/solo6x10/solo6x10-eeprom.c @@ -103,7 +103,7 @@ unsigned int solo_eeprom_ewen(struct solo_dev *solo_dev, int w_en) __be16 solo_eeprom_read(struct solo_dev *solo_dev, int loc) { int read_cmd = loc | (EE_READ_CMD << ADDR_LEN); - unsigned short retval = 0; + u16 retval = 0; int i; solo_eeprom_cmd(solo_dev, read_cmd); diff --git a/drivers/media/pci/solo6x10/solo6x10-enc.c b/drivers/media/pci/solo6x10/solo6x10-enc.c index d19c0aef5abc..d28211bb9674 100644 --- a/drivers/media/pci/solo6x10/solo6x10-enc.c +++ b/drivers/media/pci/solo6x10/solo6x10-enc.c @@ -136,11 +136,11 @@ static void solo_capture_config(struct solo_dev *solo_dev) int solo_osd_print(struct solo_enc_dev *solo_enc) { struct solo_dev *solo_dev = solo_enc->solo_dev; - unsigned char *str = solo_enc->osd_text; + u8 *str = solo_enc->osd_text; u8 *buf = solo_enc->osd_buf; u32 reg; const struct font_desc *vga = find_font("VGA8x16"); - const unsigned char *vga_data; + const u8 *vga_data; int i, j; if (WARN_ON_ONCE(!vga)) @@ -154,7 +154,7 @@ int solo_osd_print(struct solo_enc_dev *solo_enc) } memset(buf, 0, SOLO_OSD_WRITE_SIZE); - vga_data = (const unsigned char *)vga->data; + vga_data = (const u8 *)vga->data; for (i = 0; *str; i++, str++) { for (j = 0; j < 16; j++) { diff --git a/drivers/media/pci/solo6x10/solo6x10-g723.c b/drivers/media/pci/solo6x10/solo6x10-g723.c index c7141f2e63bd..7ddc76709caa 100644 --- a/drivers/media/pci/solo6x10/solo6x10-g723.c +++ b/drivers/media/pci/solo6x10/solo6x10-g723.c @@ -56,8 +56,8 @@ struct solo_snd_pcm { int on; spinlock_t lock; - struct solo_dev *solo_dev; - unsigned char *g723_buf; + struct solo_dev *solo_dev; + u8 *g723_buf; dma_addr_t g723_dma; }; diff --git a/drivers/media/pci/solo6x10/solo6x10-jpeg.h b/drivers/media/pci/solo6x10/solo6x10-jpeg.h index 1c66a46da514..3c611bd3f2d8 100644 --- a/drivers/media/pci/solo6x10/solo6x10-jpeg.h +++ b/drivers/media/pci/solo6x10/solo6x10-jpeg.h @@ -21,7 +21,7 @@ #ifndef __SOLO6X10_JPEG_H #define __SOLO6X10_JPEG_H -static const unsigned char jpeg_header[] = { +static const u8 jpeg_header[] = { 0xff, 0xd8, 0xff, 0xfe, 0x00, 0x0d, 0x42, 0x6c, 0x75, 0x65, 0x63, 0x68, 0x65, 0x72, 0x72, 0x79, 0x20, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x20, 0x16, @@ -106,7 +106,7 @@ static const unsigned char jpeg_header[] = { /* This is the byte marker for the start of the DQT */ #define DQT_START 17 #define DQT_LEN 138 -static const unsigned char jpeg_dqt[4][DQT_LEN] = { +static const u8 jpeg_dqt[4][DQT_LEN] = { { 0xff, 0xdb, 0x00, 0x43, 0x00, 0x08, 0x06, 0x06, 0x07, 0x06, 0x05, 0x08, 0x07, diff --git a/drivers/media/pci/solo6x10/solo6x10-tw28.c b/drivers/media/pci/solo6x10/solo6x10-tw28.c index edd0781ee4b5..0632d3f7c73c 100644 --- a/drivers/media/pci/solo6x10/solo6x10-tw28.c +++ b/drivers/media/pci/solo6x10/solo6x10-tw28.c @@ -510,7 +510,7 @@ static int tw2815_setup(struct solo_dev *solo_dev, u8 dev_addr) #define FIRST_ACTIVE_LINE 0x0008 #define LAST_ACTIVE_LINE 0x0102 -static void saa712x_write_regs(struct solo_dev *dev, const uint8_t *vals, +static void saa712x_write_regs(struct solo_dev *dev, const u8 *vals, int start, int n) { for (; start < n; start++, vals++) { @@ -532,7 +532,7 @@ static void saa712x_write_regs(struct solo_dev *dev, const uint8_t *vals, static void saa712x_setup(struct solo_dev *dev) { const int reg_start = 0x26; - const uint8_t saa7128_regs_ntsc[] = { + const u8 saa7128_regs_ntsc[] = { /* :0x26 */ 0x0d, 0x00, /* :0x28 */ diff --git a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c index 6e933d383fa2..53fff5425c13 100644 --- a/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c +++ b/drivers/media/pci/solo6x10/solo6x10-v4l2-enc.c @@ -38,28 +38,28 @@ #define DMA_ALIGN 4096 /* 6010 M4V */ -static unsigned char vop_6010_ntsc_d1[] = { +static u8 vop_6010_ntsc_d1[] = { 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, 0x02, 0x48, 0x1d, 0xc0, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, 0x1f, 0x4c, 0x58, 0x10, 0xf0, 0x71, 0x18, 0x3f, }; -static unsigned char vop_6010_ntsc_cif[] = { +static u8 vop_6010_ntsc_cif[] = { 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, 0x02, 0x48, 0x1d, 0xc0, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, 0x1f, 0x4c, 0x2c, 0x10, 0x78, 0x51, 0x18, 0x3f, }; -static unsigned char vop_6010_pal_d1[] = { +static u8 vop_6010_pal_d1[] = { 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, 0x02, 0x48, 0x15, 0xc0, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, 0x1f, 0x4c, 0x58, 0x11, 0x20, 0x71, 0x18, 0x3f, }; -static unsigned char vop_6010_pal_cif[] = { +static u8 vop_6010_pal_cif[] = { 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, 0x02, 0x48, 0x15, 0xc0, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x80, 0x00, 0x97, 0x53, 0x04, @@ -67,25 +67,25 @@ static unsigned char vop_6010_pal_cif[] = { }; /* 6110 h.264 */ -static unsigned char vop_6110_ntsc_d1[] = { +static u8 vop_6110_ntsc_d1[] = { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, 0x9a, 0x74, 0x05, 0x81, 0xec, 0x80, 0x00, 0x00, 0x00, 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, }; -static unsigned char vop_6110_ntsc_cif[] = { +static u8 vop_6110_ntsc_cif[] = { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, 0x9a, 0x74, 0x0b, 0x0f, 0xc8, 0x00, 0x00, 0x00, 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, 0x00, }; -static unsigned char vop_6110_pal_d1[] = { +static u8 vop_6110_pal_d1[] = { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, 0x9a, 0x74, 0x05, 0x80, 0x93, 0x20, 0x00, 0x00, 0x00, 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, }; -static unsigned char vop_6110_pal_cif[] = { +static u8 vop_6110_pal_cif[] = { 0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0x00, 0x1e, 0x9a, 0x74, 0x0b, 0x04, 0xb2, 0x00, 0x00, 0x00, 0x01, 0x68, 0xce, 0x32, 0x28, 0x00, 0x00, 0x00, @@ -149,7 +149,7 @@ void solo_update_mode(struct solo_enc_dev *solo_enc) { struct solo_dev *solo_dev = solo_enc->solo_dev; int vop_len; - unsigned char *vop; + u8 *vop; solo_enc->interlaced = (solo_enc->mode & 0x08) ? 1 : 0; solo_enc->bw_weight = max(solo_dev->fps / solo_enc->interval, 1); @@ -239,8 +239,6 @@ static int solo_enc_on(struct solo_enc_dev *solo_enc) if (solo_enc->bw_weight > solo_dev->enc_bw_remain) return -EBUSY; solo_enc->sequence = 0; - solo_enc->motion_last_state = false; - solo_enc->frames_since_last_motion = 0; solo_dev->enc_bw_remain -= solo_enc->bw_weight; if (solo_enc->type == SOLO_ENC_TYPE_EXT) @@ -529,36 +527,12 @@ static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc, } if (!ret) { - bool send_event = false; - vb->v4l2_buf.sequence = solo_enc->sequence++; vb->v4l2_buf.timestamp.tv_sec = vop_sec(vh); vb->v4l2_buf.timestamp.tv_usec = vop_usec(vh); /* Check for motion flags */ - if (solo_is_motion_on(solo_enc)) { - /* It takes a few frames for the hardware to detect - * motion. Once it does it clears the motion detection - * register and it takes again a few frames before - * motion is seen. This means in practice that when the - * motion field is 1, it will go back to 0 for the next - * frame. This leads to motion detection event being - * sent all the time, which is not what we want. - * Instead wait a few frames before deciding that the - * motion has halted. After some experimentation it - * turns out that waiting for 5 frames works well. - */ - if (enc_buf->motion == 0 && - solo_enc->motion_last_state && - solo_enc->frames_since_last_motion++ > 5) - send_event = true; - else if (enc_buf->motion) { - solo_enc->frames_since_last_motion = 0; - send_event = !solo_enc->motion_last_state; - } - } - - if (send_event) { + if (solo_is_motion_on(solo_enc) && enc_buf->motion) { struct v4l2_event ev = { .type = V4L2_EVENT_MOTION_DET, .u.motion_det = { @@ -568,8 +542,6 @@ static int solo_enc_fillbuf(struct solo_enc_dev *solo_enc, }, }; - solo_enc->motion_last_state = enc_buf->motion; - solo_enc->frames_since_last_motion = 0; v4l2_event_queue(solo_enc->vfd, &ev); } } diff --git a/drivers/media/pci/solo6x10/solo6x10.h b/drivers/media/pci/solo6x10/solo6x10.h index bd8edfa319b8..1ca54b08b3aa 100644 --- a/drivers/media/pci/solo6x10/solo6x10.h +++ b/drivers/media/pci/solo6x10/solo6x10.h @@ -159,8 +159,6 @@ struct solo_enc_dev { u16 motion_thresh; bool motion_global; bool motion_enabled; - bool motion_last_state; - u8 frames_since_last_motion; u16 width; u16 height; @@ -170,9 +168,9 @@ struct solo_enc_dev { __aligned(4); /* VOP stuff */ - unsigned char vop[64]; + u8 vop[64]; int vop_len; - unsigned char jpeg_header[1024]; + u8 jpeg_header[1024]; int jpeg_len; u32 fmt; diff --git a/drivers/media/pci/sta2x11/Kconfig b/drivers/media/pci/sta2x11/Kconfig index f6f30abc088b..e03587b1af71 100644 --- a/drivers/media/pci/sta2x11/Kconfig +++ b/drivers/media/pci/sta2x11/Kconfig @@ -5,6 +5,7 @@ config STA2X11_VIP select VIDEO_ADV7180 if MEDIA_SUBDRV_AUTOSELECT select VIDEOBUF2_DMA_CONTIG depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS + depends on VIDEO_V4L2_SUBDEV_API depends on I2C help Say Y for support for STA2X11 VIP (Video Input Port) capture diff --git a/drivers/media/pci/ttpci/av7110.c b/drivers/media/pci/ttpci/av7110.c index c1f0617a6973..45199a12b9d9 100644 --- a/drivers/media/pci/ttpci/av7110.c +++ b/drivers/media/pci/ttpci/av7110.c @@ -1219,11 +1219,14 @@ static int stop_ts_capture(struct av7110 *budget) static int start_ts_capture(struct av7110 *budget) { + unsigned y; + dprintk(2, "budget: %p\n", budget); if (budget->feeding1) return ++budget->feeding1; - memset(budget->grabbing, 0x00, TS_BUFLEN); + for (y = 0; y < TS_HEIGHT; y++) + memset(budget->grabbing + y * TS_WIDTH, 0x00, TS_WIDTH); budget->ttbp = 0; SAA7146_ISR_CLEAR(budget->dev, MASK_10); /* VPE */ SAA7146_IER_ENABLE(budget->dev, MASK_10); /* VPE */ diff --git a/drivers/media/pci/ttpci/budget-core.c b/drivers/media/pci/ttpci/budget-core.c index 37d02fe09137..23e05499b509 100644 --- a/drivers/media/pci/ttpci/budget-core.c +++ b/drivers/media/pci/ttpci/budget-core.c @@ -231,63 +231,59 @@ static void vpeirq(unsigned long data) } -int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count, - int uselocks, int nobusyloop) +static int ttpci_budget_debiread_nolock(struct budget *budget, u32 config, + int addr, int count, int nobusyloop) { struct saa7146_dev *saa = budget->dev; - int result = 0; - unsigned long flags = 0; - - if (count > 4 || count <= 0) - return 0; - - if (uselocks) - spin_lock_irqsave(&budget->debilock, flags); + int result; - if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { - if (uselocks) - spin_unlock_irqrestore(&budget->debilock, flags); + result = saa7146_wait_for_debi_done(saa, nobusyloop); + if (result < 0) return result; - } saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x10000 | (addr & 0xffff)); saa7146_write(saa, DEBI_CONFIG, config); saa7146_write(saa, DEBI_PAGE, 0); saa7146_write(saa, MC2, (2 << 16) | 2); - if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { - if (uselocks) - spin_unlock_irqrestore(&budget->debilock, flags); + result = saa7146_wait_for_debi_done(saa, nobusyloop); + if (result < 0) return result; - } result = saa7146_read(saa, DEBI_AD); result &= (0xffffffffUL >> ((4 - count) * 8)); - - if (uselocks) - spin_unlock_irqrestore(&budget->debilock, flags); - return result; } -int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, - int count, u32 value, int uselocks, int nobusyloop) +int ttpci_budget_debiread(struct budget *budget, u32 config, int addr, int count, + int uselocks, int nobusyloop) { - struct saa7146_dev *saa = budget->dev; - unsigned long flags = 0; - int result; - if (count > 4 || count <= 0) return 0; - if (uselocks) - spin_lock_irqsave(&budget->debilock, flags); + if (uselocks) { + unsigned long flags; + int result; - if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { - if (uselocks) - spin_unlock_irqrestore(&budget->debilock, flags); + spin_lock_irqsave(&budget->debilock, flags); + result = ttpci_budget_debiread_nolock(budget, config, addr, + count, nobusyloop); + spin_unlock_irqrestore(&budget->debilock, flags); return result; } + return ttpci_budget_debiread_nolock(budget, config, addr, + count, nobusyloop); +} + +static int ttpci_budget_debiwrite_nolock(struct budget *budget, u32 config, + int addr, int count, u32 value, int nobusyloop) +{ + struct saa7146_dev *saa = budget->dev; + int result; + + result = saa7146_wait_for_debi_done(saa, nobusyloop); + if (result < 0) + return result; saa7146_write(saa, DEBI_COMMAND, (count << 17) | 0x00000 | (addr & 0xffff)); saa7146_write(saa, DEBI_CONFIG, config); @@ -295,15 +291,28 @@ int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, saa7146_write(saa, DEBI_AD, value); saa7146_write(saa, MC2, (2 << 16) | 2); - if ((result = saa7146_wait_for_debi_done(saa, nobusyloop)) < 0) { - if (uselocks) - spin_unlock_irqrestore(&budget->debilock, flags); - return result; - } + result = saa7146_wait_for_debi_done(saa, nobusyloop); + return result < 0 ? result : 0; +} - if (uselocks) +int ttpci_budget_debiwrite(struct budget *budget, u32 config, int addr, + int count, u32 value, int uselocks, int nobusyloop) +{ + if (count > 4 || count <= 0) + return 0; + + if (uselocks) { + unsigned long flags; + int result; + + spin_lock_irqsave(&budget->debilock, flags); + result = ttpci_budget_debiwrite_nolock(budget, config, addr, + count, value, nobusyloop); spin_unlock_irqrestore(&budget->debilock, flags); - return 0; + return result; + } + return ttpci_budget_debiwrite_nolock(budget, config, addr, + count, value, nobusyloop); } diff --git a/drivers/media/pci/tw68/tw68.h b/drivers/media/pci/tw68/tw68.h index 7a7501bd165f..93f2335e004b 100644 --- a/drivers/media/pci/tw68/tw68.h +++ b/drivers/media/pci/tw68/tw68.h @@ -25,7 +25,6 @@ * GNU General Public License for more details. */ -#include <linux/version.h> #include <linux/pci.h> #include <linux/videodev2.h> #include <linux/notifier.h> diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index 765bffb49a72..d9b872b9285a 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -56,10 +56,8 @@ config VIDEO_VIU config VIDEO_TIMBERDALE tristate "Support for timberdale Video In/LogiWIN" - depends on VIDEO_V4L2 && I2C && DMADEVICES - depends on MFD_TIMBERDALE || COMPILE_TEST - select DMA_ENGINE - select TIMB_DMA + depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API + depends on (MFD_TIMBERDALE && TIMB_DMA) || COMPILE_TEST select VIDEO_ADV7180 select VIDEOBUF_DMA_CONTIG ---help--- @@ -118,6 +116,7 @@ config VIDEO_S3C_CAMIF source "drivers/media/platform/soc_camera/Kconfig" source "drivers/media/platform/exynos4-is/Kconfig" source "drivers/media/platform/s5p-tv/Kconfig" +source "drivers/media/platform/am437x/Kconfig" endif # V4L_PLATFORM_DRIVERS @@ -140,6 +139,7 @@ config VIDEO_CODA depends on HAS_DMA select SRAM select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_VMALLOC select V4L2_MEM2MEM_DEV select GENERIC_ALLOCATOR ---help--- @@ -213,7 +213,6 @@ config VIDEO_SAMSUNG_EXYNOS_GSC config VIDEO_SH_VEU tristate "SuperH VEU mem2mem video processing driver" depends on VIDEO_DEV && VIDEO_V4L2 && HAS_DMA - depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG select V4L2_MEM2MEM_DEV help @@ -223,7 +222,7 @@ config VIDEO_SH_VEU config VIDEO_RENESAS_VSP1 tristate "Renesas VSP1 Video Processing Engine" depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && HAS_DMA - depends on ARCH_SHMOBILE || COMPILE_TEST + depends on (ARCH_SHMOBILE && OF) || COMPILE_TEST select VIDEOBUF2_DMA_CONTIG ---help--- This is a V4L2 driver for the Renesas VSP1 video processing engine. diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index a49936b8ce8a..3ec154742083 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -46,4 +46,6 @@ obj-$(CONFIG_VIDEO_RENESAS_VSP1) += vsp1/ obj-y += omap/ +obj-$(CONFIG_VIDEO_AM437X_VPFE) += am437x/ + ccflags-y += -I$(srctree)/drivers/media/i2c diff --git a/drivers/media/platform/am437x/Kconfig b/drivers/media/platform/am437x/Kconfig new file mode 100644 index 000000000000..7b023a76e32e --- /dev/null +++ b/drivers/media/platform/am437x/Kconfig @@ -0,0 +1,11 @@ +config VIDEO_AM437X_VPFE + tristate "TI AM437x VPFE video capture driver" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on SOC_AM43XX || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + help + Support for AM437x Video Processing Front End based Video + Capture Driver. + + To compile this driver as a module, choose M here. The module + will be called am437x-vpfe. diff --git a/drivers/media/platform/am437x/Makefile b/drivers/media/platform/am437x/Makefile new file mode 100644 index 000000000000..d11fff16f260 --- /dev/null +++ b/drivers/media/platform/am437x/Makefile @@ -0,0 +1,3 @@ +# Makefile for AM437x VPFE driver + +obj-$(CONFIG_VIDEO_AM437X_VPFE) += am437x-vpfe.o diff --git a/drivers/media/platform/am437x/am437x-vpfe.c b/drivers/media/platform/am437x/am437x-vpfe.c new file mode 100644 index 000000000000..56a5cb0d2152 --- /dev/null +++ b/drivers/media/platform/am437x/am437x-vpfe.c @@ -0,0 +1,2776 @@ +/* + * TI VPFE capture Driver + * + * Copyright (C) 2013 - 2014 Texas Instruments, Inc. + * + * Benoit Parrot <bparrot@ti.com> + * Lad, Prabhakar <prabhakar.csengg@gmail.com> + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/videodev2.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-event.h> +#include <media/v4l2-of.h> + +#include "am437x-vpfe.h" + +#define VPFE_MODULE_NAME "vpfe" +#define VPFE_VERSION "0.1.0" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level 0-8"); + +#define vpfe_dbg(level, dev, fmt, arg...) \ + v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ##arg) +#define vpfe_info(dev, fmt, arg...) \ + v4l2_info(&dev->v4l2_dev, fmt, ##arg) +#define vpfe_err(dev, fmt, arg...) \ + v4l2_err(&dev->v4l2_dev, fmt, ##arg) + +/* standard information */ +struct vpfe_standard { + v4l2_std_id std_id; + unsigned int width; + unsigned int height; + struct v4l2_fract pixelaspect; + int frame_format; +}; + +static const struct vpfe_standard vpfe_standards[] = { + {V4L2_STD_525_60, 720, 480, {11, 10}, 1}, + {V4L2_STD_625_50, 720, 576, {54, 59}, 1}, +}; + +struct bus_format { + unsigned int width; + unsigned int bpp; +}; + +/* + * struct vpfe_fmt - VPFE media bus format information + * @name: V4L2 format description + * @code: V4L2 media bus format code + * @shifted: V4L2 media bus format code for the same pixel layout but + * shifted to be 8 bits per pixel. =0 if format is not shiftable. + * @pixelformat: V4L2 pixel format FCC identifier + * @width: Bits per pixel (when transferred over a bus) + * @bpp: Bytes per pixel (when stored in memory) + * @supported: Indicates format supported by subdev + */ +struct vpfe_fmt { + const char *name; + u32 fourcc; + u32 code; + struct bus_format l; + struct bus_format s; + bool supported; + u32 index; +}; + +static struct vpfe_fmt formats[] = { + { + .name = "YUV 4:2:2 packed, YCbYCr", + .fourcc = V4L2_PIX_FMT_YUYV, + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .l.width = 10, + .l.bpp = 4, + .s.width = 8, + .s.bpp = 2, + .supported = false, + }, { + .name = "YUV 4:2:2 packed, CbYCrY", + .fourcc = V4L2_PIX_FMT_UYVY, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .l.width = 10, + .l.bpp = 4, + .s.width = 8, + .s.bpp = 2, + .supported = false, + }, { + .name = "YUV 4:2:2 packed, YCrYCb", + .fourcc = V4L2_PIX_FMT_YVYU, + .code = MEDIA_BUS_FMT_YVYU8_2X8, + .l.width = 10, + .l.bpp = 4, + .s.width = 8, + .s.bpp = 2, + .supported = false, + }, { + .name = "YUV 4:2:2 packed, CrYCbY", + .fourcc = V4L2_PIX_FMT_VYUY, + .code = MEDIA_BUS_FMT_VYUY8_2X8, + .l.width = 10, + .l.bpp = 4, + .s.width = 8, + .s.bpp = 2, + .supported = false, + }, { + .name = "RAW8 BGGR", + .fourcc = V4L2_PIX_FMT_SBGGR8, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .l.width = 10, + .l.bpp = 2, + .s.width = 8, + .s.bpp = 1, + .supported = false, + }, { + .name = "RAW8 GBRG", + .fourcc = V4L2_PIX_FMT_SGBRG8, + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .l.width = 10, + .l.bpp = 2, + .s.width = 8, + .s.bpp = 1, + .supported = false, + }, { + .name = "RAW8 GRBG", + .fourcc = V4L2_PIX_FMT_SGRBG8, + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .l.width = 10, + .l.bpp = 2, + .s.width = 8, + .s.bpp = 1, + .supported = false, + }, { + .name = "RAW8 RGGB", + .fourcc = V4L2_PIX_FMT_SRGGB8, + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .l.width = 10, + .l.bpp = 2, + .s.width = 8, + .s.bpp = 1, + .supported = false, + }, { + .name = "RGB565 (LE)", + .fourcc = V4L2_PIX_FMT_RGB565, + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .l.width = 10, + .l.bpp = 4, + .s.width = 8, + .s.bpp = 2, + .supported = false, + }, { + .name = "RGB565 (BE)", + .fourcc = V4L2_PIX_FMT_RGB565X, + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .l.width = 10, + .l.bpp = 4, + .s.width = 8, + .s.bpp = 2, + .supported = false, + }, +}; + +static int +__vpfe_get_format(struct vpfe_device *vpfe, + struct v4l2_format *format, unsigned int *bpp); + +static struct vpfe_fmt *find_format_by_code(unsigned int code) +{ + struct vpfe_fmt *fmt; + unsigned int k; + + for (k = 0; k < ARRAY_SIZE(formats); k++) { + fmt = &formats[k]; + if (fmt->code == code) + return fmt; + } + + return NULL; +} + +static struct vpfe_fmt *find_format_by_pix(unsigned int pixelformat) +{ + struct vpfe_fmt *fmt; + unsigned int k; + + for (k = 0; k < ARRAY_SIZE(formats); k++) { + fmt = &formats[k]; + if (fmt->fourcc == pixelformat) + return fmt; + } + + return NULL; +} + +static void +mbus_to_pix(struct vpfe_device *vpfe, + const struct v4l2_mbus_framefmt *mbus, + struct v4l2_pix_format *pix, unsigned int *bpp) +{ + struct vpfe_subdev_info *sdinfo = vpfe->current_subdev; + unsigned int bus_width = sdinfo->vpfe_param.bus_width; + struct vpfe_fmt *fmt; + + fmt = find_format_by_code(mbus->code); + if (WARN_ON(fmt == NULL)) { + pr_err("Invalid mbus code set\n"); + *bpp = 1; + return; + } + + memset(pix, 0, sizeof(*pix)); + v4l2_fill_pix_format(pix, mbus); + pix->pixelformat = fmt->fourcc; + *bpp = (bus_width == 10) ? fmt->l.bpp : fmt->s.bpp; + + /* pitch should be 32 bytes aligned */ + pix->bytesperline = ALIGN(pix->width * *bpp, 32); + pix->sizeimage = pix->bytesperline * pix->height; +} + +static void pix_to_mbus(struct vpfe_device *vpfe, + struct v4l2_pix_format *pix_fmt, + struct v4l2_mbus_framefmt *mbus_fmt) +{ + struct vpfe_fmt *fmt; + + fmt = find_format_by_pix(pix_fmt->pixelformat); + if (!fmt) { + /* default to first entry */ + vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n", + pix_fmt->pixelformat); + fmt = &formats[0]; + } + + memset(mbus_fmt, 0, sizeof(*mbus_fmt)); + v4l2_fill_mbus_format(mbus_fmt, pix_fmt, fmt->code); +} + +/* Print Four-character-code (FOURCC) */ +static char *print_fourcc(u32 fmt) +{ + static char code[5]; + + code[0] = (unsigned char)(fmt & 0xff); + code[1] = (unsigned char)((fmt >> 8) & 0xff); + code[2] = (unsigned char)((fmt >> 16) & 0xff); + code[3] = (unsigned char)((fmt >> 24) & 0xff); + code[4] = '\0'; + + return code; +} + +static int +cmp_v4l2_format(const struct v4l2_format *lhs, const struct v4l2_format *rhs) +{ + return lhs->type == rhs->type && + lhs->fmt.pix.width == rhs->fmt.pix.width && + lhs->fmt.pix.height == rhs->fmt.pix.height && + lhs->fmt.pix.pixelformat == rhs->fmt.pix.pixelformat && + lhs->fmt.pix.field == rhs->fmt.pix.field && + lhs->fmt.pix.colorspace == rhs->fmt.pix.colorspace && + lhs->fmt.pix.ycbcr_enc == rhs->fmt.pix.ycbcr_enc && + lhs->fmt.pix.quantization == rhs->fmt.pix.quantization; +} + +static inline u32 vpfe_reg_read(struct vpfe_ccdc *ccdc, u32 offset) +{ + return ioread32(ccdc->ccdc_cfg.base_addr + offset); +} + +static inline void vpfe_reg_write(struct vpfe_ccdc *ccdc, u32 val, u32 offset) +{ + iowrite32(val, ccdc->ccdc_cfg.base_addr + offset); +} + +static inline struct vpfe_device *to_vpfe(struct vpfe_ccdc *ccdc) +{ + return container_of(ccdc, struct vpfe_device, ccdc); +} + +static inline struct vpfe_cap_buffer *to_vpfe_buffer(struct vb2_buffer *vb) +{ + return container_of(vb, struct vpfe_cap_buffer, vb); +} + +static inline void vpfe_pcr_enable(struct vpfe_ccdc *ccdc, int flag) +{ + vpfe_reg_write(ccdc, !!flag, VPFE_PCR); +} + +static void vpfe_config_enable(struct vpfe_ccdc *ccdc, int flag) +{ + unsigned int cfg; + + if (!flag) { + cfg = vpfe_reg_read(ccdc, VPFE_CONFIG); + cfg &= ~(VPFE_CONFIG_EN_ENABLE << VPFE_CONFIG_EN_SHIFT); + } else { + cfg = VPFE_CONFIG_EN_ENABLE << VPFE_CONFIG_EN_SHIFT; + } + + vpfe_reg_write(ccdc, cfg, VPFE_CONFIG); +} + +static void vpfe_ccdc_setwin(struct vpfe_ccdc *ccdc, + struct v4l2_rect *image_win, + enum ccdc_frmfmt frm_fmt, + int bpp) +{ + int horz_start, horz_nr_pixels; + int vert_start, vert_nr_lines; + int val, mid_img; + + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left * bpp; + horz_nr_pixels = (image_win->width * bpp) - 1; + vpfe_reg_write(ccdc, (horz_start << VPFE_HORZ_INFO_SPH_SHIFT) | + horz_nr_pixels, VPFE_HORZ_INFO); + + vert_start = image_win->top; + + if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* Since first line doesn't have any data */ + vert_start += 1; + /* configure VDINT0 */ + val = (vert_start << VPFE_VDINT_VDINT0_SHIFT); + } else { + /* Since first line doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* + * configure VDINT0 and VDINT1. VDINT1 will be at half + * of image height + */ + mid_img = vert_start + (image_win->height / 2); + val = (vert_start << VPFE_VDINT_VDINT0_SHIFT) | + (mid_img & VPFE_VDINT_VDINT1_MASK); + } + + vpfe_reg_write(ccdc, val, VPFE_VDINT); + + vpfe_reg_write(ccdc, (vert_start << VPFE_VERT_START_SLV0_SHIFT) | + vert_start, VPFE_VERT_START); + vpfe_reg_write(ccdc, vert_nr_lines, VPFE_VERT_LINES); +} + +static void vpfe_reg_dump(struct vpfe_ccdc *ccdc) +{ + struct vpfe_device *vpfe = to_vpfe(ccdc); + + vpfe_dbg(3, vpfe, "ALAW: 0x%x\n", vpfe_reg_read(ccdc, VPFE_ALAW)); + vpfe_dbg(3, vpfe, "CLAMP: 0x%x\n", vpfe_reg_read(ccdc, VPFE_CLAMP)); + vpfe_dbg(3, vpfe, "DCSUB: 0x%x\n", vpfe_reg_read(ccdc, VPFE_DCSUB)); + vpfe_dbg(3, vpfe, "BLKCMP: 0x%x\n", vpfe_reg_read(ccdc, VPFE_BLKCMP)); + vpfe_dbg(3, vpfe, "COLPTN: 0x%x\n", vpfe_reg_read(ccdc, VPFE_COLPTN)); + vpfe_dbg(3, vpfe, "SDOFST: 0x%x\n", vpfe_reg_read(ccdc, VPFE_SDOFST)); + vpfe_dbg(3, vpfe, "SYN_MODE: 0x%x\n", + vpfe_reg_read(ccdc, VPFE_SYNMODE)); + vpfe_dbg(3, vpfe, "HSIZE_OFF: 0x%x\n", + vpfe_reg_read(ccdc, VPFE_HSIZE_OFF)); + vpfe_dbg(3, vpfe, "HORZ_INFO: 0x%x\n", + vpfe_reg_read(ccdc, VPFE_HORZ_INFO)); + vpfe_dbg(3, vpfe, "VERT_START: 0x%x\n", + vpfe_reg_read(ccdc, VPFE_VERT_START)); + vpfe_dbg(3, vpfe, "VERT_LINES: 0x%x\n", + vpfe_reg_read(ccdc, VPFE_VERT_LINES)); +} + +static int +vpfe_ccdc_validate_param(struct vpfe_ccdc *ccdc, + struct vpfe_ccdc_config_params_raw *ccdcparam) +{ + struct vpfe_device *vpfe = to_vpfe(ccdc); + u8 max_gamma, max_data; + + if (!ccdcparam->alaw.enable) + return 0; + + max_gamma = ccdc_gamma_width_max_bit(ccdcparam->alaw.gamma_wd); + max_data = ccdc_data_size_max_bit(ccdcparam->data_sz); + + if (ccdcparam->alaw.gamma_wd > VPFE_CCDC_GAMMA_BITS_09_0 || + ccdcparam->alaw.gamma_wd < VPFE_CCDC_GAMMA_BITS_15_6 || + max_gamma > max_data) { + vpfe_dbg(1, vpfe, "Invalid data line select\n"); + return -EINVAL; + } + + return 0; +} + +static void +vpfe_ccdc_update_raw_params(struct vpfe_ccdc *ccdc, + struct vpfe_ccdc_config_params_raw *raw_params) +{ + struct vpfe_ccdc_config_params_raw *config_params = + &ccdc->ccdc_cfg.bayer.config_params; + + config_params = raw_params; +} + +/* + * vpfe_ccdc_restore_defaults() + * This function will write defaults to all CCDC registers + */ +static void vpfe_ccdc_restore_defaults(struct vpfe_ccdc *ccdc) +{ + int i; + + /* Disable CCDC */ + vpfe_pcr_enable(ccdc, 0); + + /* set all registers to default value */ + for (i = 4; i <= 0x94; i += 4) + vpfe_reg_write(ccdc, 0, i); + + vpfe_reg_write(ccdc, VPFE_NO_CULLING, VPFE_CULLING); + vpfe_reg_write(ccdc, VPFE_CCDC_GAMMA_BITS_11_2, VPFE_ALAW); +} + +static int vpfe_ccdc_close(struct vpfe_ccdc *ccdc, struct device *dev) +{ + int dma_cntl, i, pcr; + + /* If the CCDC module is still busy wait for it to be done */ + for (i = 0; i < 10; i++) { + usleep_range(5000, 6000); + pcr = vpfe_reg_read(ccdc, VPFE_PCR); + if (!pcr) + break; + + /* make sure it it is disabled */ + vpfe_pcr_enable(ccdc, 0); + } + + /* Disable CCDC by resetting all register to default POR values */ + vpfe_ccdc_restore_defaults(ccdc); + + /* if DMA_CNTL overflow bit is set. Clear it + * It appears to take a while for this to become quiescent ~20ms + */ + for (i = 0; i < 10; i++) { + dma_cntl = vpfe_reg_read(ccdc, VPFE_DMA_CNTL); + if (!(dma_cntl & VPFE_DMA_CNTL_OVERFLOW)) + break; + + /* Clear the overflow bit */ + vpfe_reg_write(ccdc, dma_cntl, VPFE_DMA_CNTL); + usleep_range(5000, 6000); + } + + /* Disabled the module at the CONFIG level */ + vpfe_config_enable(ccdc, 0); + + pm_runtime_put_sync(dev); + + return 0; +} + +static int vpfe_ccdc_set_params(struct vpfe_ccdc *ccdc, void __user *params) +{ + struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc); + struct vpfe_ccdc_config_params_raw raw_params; + int x; + + if (ccdc->ccdc_cfg.if_type != VPFE_RAW_BAYER) + return -EINVAL; + + x = copy_from_user(&raw_params, params, sizeof(raw_params)); + if (x) { + vpfe_dbg(1, vpfe, + "vpfe_ccdc_set_params: error in copying ccdc params, %d\n", + x); + return -EFAULT; + } + + if (!vpfe_ccdc_validate_param(ccdc, &raw_params)) { + vpfe_ccdc_update_raw_params(ccdc, &raw_params); + return 0; + } + + return -EINVAL; +} + +/* + * vpfe_ccdc_config_ycbcr() + * This function will configure CCDC for YCbCr video capture + */ +static void vpfe_ccdc_config_ycbcr(struct vpfe_ccdc *ccdc) +{ + struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc); + struct ccdc_params_ycbcr *params = &ccdc->ccdc_cfg.ycbcr; + u32 syn_mode; + + vpfe_dbg(3, vpfe, "vpfe_ccdc_config_ycbcr:\n"); + /* + * first restore the CCDC registers to default values + * This is important since we assume default values to be set in + * a lot of registers that we didn't touch + */ + vpfe_ccdc_restore_defaults(ccdc); + + /* + * configure pixel format, frame format, configure video frame + * format, enable output to SDRAM, enable internal timing generator + * and 8bit pack mode + */ + syn_mode = (((params->pix_fmt & VPFE_SYN_MODE_INPMOD_MASK) << + VPFE_SYN_MODE_INPMOD_SHIFT) | + ((params->frm_fmt & VPFE_SYN_FLDMODE_MASK) << + VPFE_SYN_FLDMODE_SHIFT) | VPFE_VDHDEN_ENABLE | + VPFE_WEN_ENABLE | VPFE_DATA_PACK_ENABLE); + + /* setup BT.656 sync mode */ + if (params->bt656_enable) { + vpfe_reg_write(ccdc, VPFE_REC656IF_BT656_EN, VPFE_REC656IF); + + /* + * configure the FID, VD, HD pin polarity, + * fld,hd pol positive, vd negative, 8-bit data + */ + syn_mode |= VPFE_SYN_MODE_VD_POL_NEGATIVE; + if (ccdc->ccdc_cfg.if_type == VPFE_BT656_10BIT) + syn_mode |= VPFE_SYN_MODE_10BITS; + else + syn_mode |= VPFE_SYN_MODE_8BITS; + } else { + /* y/c external sync mode */ + syn_mode |= (((params->fid_pol & VPFE_FID_POL_MASK) << + VPFE_FID_POL_SHIFT) | + ((params->hd_pol & VPFE_HD_POL_MASK) << + VPFE_HD_POL_SHIFT) | + ((params->vd_pol & VPFE_VD_POL_MASK) << + VPFE_VD_POL_SHIFT)); + } + vpfe_reg_write(ccdc, syn_mode, VPFE_SYNMODE); + + /* configure video window */ + vpfe_ccdc_setwin(ccdc, ¶ms->win, + params->frm_fmt, params->bytesperpixel); + + /* + * configure the order of y cb cr in SDRAM, and disable latch + * internal register on vsync + */ + if (ccdc->ccdc_cfg.if_type == VPFE_BT656_10BIT) + vpfe_reg_write(ccdc, + (params->pix_order << VPFE_CCDCFG_Y8POS_SHIFT) | + VPFE_LATCH_ON_VSYNC_DISABLE | + VPFE_CCDCFG_BW656_10BIT, VPFE_CCDCFG); + else + vpfe_reg_write(ccdc, + (params->pix_order << VPFE_CCDCFG_Y8POS_SHIFT) | + VPFE_LATCH_ON_VSYNC_DISABLE, VPFE_CCDCFG); + + /* + * configure the horizontal line offset. This should be a + * on 32 byte boundary. So clear LSB 5 bits + */ + vpfe_reg_write(ccdc, params->bytesperline, VPFE_HSIZE_OFF); + + /* configure the memory line offset */ + if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) + /* two fields are interleaved in memory */ + vpfe_reg_write(ccdc, VPFE_SDOFST_FIELD_INTERLEAVED, + VPFE_SDOFST); +} + +static void +vpfe_ccdc_config_black_clamp(struct vpfe_ccdc *ccdc, + struct vpfe_ccdc_black_clamp *bclamp) +{ + u32 val; + + if (!bclamp->enable) { + /* configure DCSub */ + val = (bclamp->dc_sub) & VPFE_BLK_DC_SUB_MASK; + vpfe_reg_write(ccdc, val, VPFE_DCSUB); + vpfe_reg_write(ccdc, VPFE_CLAMP_DEFAULT_VAL, VPFE_CLAMP); + return; + } + /* + * Configure gain, Start pixel, No of line to be avg, + * No of pixel/line to be avg, & Enable the Black clamping + */ + val = ((bclamp->sgain & VPFE_BLK_SGAIN_MASK) | + ((bclamp->start_pixel & VPFE_BLK_ST_PXL_MASK) << + VPFE_BLK_ST_PXL_SHIFT) | + ((bclamp->sample_ln & VPFE_BLK_SAMPLE_LINE_MASK) << + VPFE_BLK_SAMPLE_LINE_SHIFT) | + ((bclamp->sample_pixel & VPFE_BLK_SAMPLE_LN_MASK) << + VPFE_BLK_SAMPLE_LN_SHIFT) | VPFE_BLK_CLAMP_ENABLE); + vpfe_reg_write(ccdc, val, VPFE_CLAMP); + /* If Black clamping is enable then make dcsub 0 */ + vpfe_reg_write(ccdc, VPFE_DCSUB_DEFAULT_VAL, VPFE_DCSUB); +} + +static void +vpfe_ccdc_config_black_compense(struct vpfe_ccdc *ccdc, + struct vpfe_ccdc_black_compensation *bcomp) +{ + u32 val; + + val = ((bcomp->b & VPFE_BLK_COMP_MASK) | + ((bcomp->gb & VPFE_BLK_COMP_MASK) << + VPFE_BLK_COMP_GB_COMP_SHIFT) | + ((bcomp->gr & VPFE_BLK_COMP_MASK) << + VPFE_BLK_COMP_GR_COMP_SHIFT) | + ((bcomp->r & VPFE_BLK_COMP_MASK) << + VPFE_BLK_COMP_R_COMP_SHIFT)); + vpfe_reg_write(ccdc, val, VPFE_BLKCMP); +} + +/* + * vpfe_ccdc_config_raw() + * This function will configure CCDC for Raw capture mode + */ +static void vpfe_ccdc_config_raw(struct vpfe_ccdc *ccdc) +{ + struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc); + struct vpfe_ccdc_config_params_raw *config_params = + &ccdc->ccdc_cfg.bayer.config_params; + struct ccdc_params_raw *params = &ccdc->ccdc_cfg.bayer; + unsigned int syn_mode; + unsigned int val; + + vpfe_dbg(3, vpfe, "vpfe_ccdc_config_raw:\n"); + + /* Reset CCDC */ + vpfe_ccdc_restore_defaults(ccdc); + + /* Disable latching function registers on VSYNC */ + vpfe_reg_write(ccdc, VPFE_LATCH_ON_VSYNC_DISABLE, VPFE_CCDCFG); + + /* + * Configure the vertical sync polarity(SYN_MODE.VDPOL), + * horizontal sync polarity (SYN_MODE.HDPOL), frame id polarity + * (SYN_MODE.FLDPOL), frame format(progressive or interlace), + * data size(SYNMODE.DATSIZ), &pixel format (Input mode), output + * SDRAM, enable internal timing generator + */ + syn_mode = (((params->vd_pol & VPFE_VD_POL_MASK) << VPFE_VD_POL_SHIFT) | + ((params->hd_pol & VPFE_HD_POL_MASK) << VPFE_HD_POL_SHIFT) | + ((params->fid_pol & VPFE_FID_POL_MASK) << + VPFE_FID_POL_SHIFT) | ((params->frm_fmt & + VPFE_FRM_FMT_MASK) << VPFE_FRM_FMT_SHIFT) | + ((config_params->data_sz & VPFE_DATA_SZ_MASK) << + VPFE_DATA_SZ_SHIFT) | ((params->pix_fmt & + VPFE_PIX_FMT_MASK) << VPFE_PIX_FMT_SHIFT) | + VPFE_WEN_ENABLE | VPFE_VDHDEN_ENABLE); + + /* Enable and configure aLaw register if needed */ + if (config_params->alaw.enable) { + val = ((config_params->alaw.gamma_wd & + VPFE_ALAW_GAMMA_WD_MASK) | VPFE_ALAW_ENABLE); + vpfe_reg_write(ccdc, val, VPFE_ALAW); + vpfe_dbg(3, vpfe, "\nWriting 0x%x to ALAW...\n", val); + } + + /* Configure video window */ + vpfe_ccdc_setwin(ccdc, ¶ms->win, params->frm_fmt, + params->bytesperpixel); + + /* Configure Black Clamp */ + vpfe_ccdc_config_black_clamp(ccdc, &config_params->blk_clamp); + + /* Configure Black level compensation */ + vpfe_ccdc_config_black_compense(ccdc, &config_params->blk_comp); + + /* If data size is 8 bit then pack the data */ + if ((config_params->data_sz == VPFE_CCDC_DATA_8BITS) || + config_params->alaw.enable) + syn_mode |= VPFE_DATA_PACK_ENABLE; + + /* + * Configure Horizontal offset register. If pack 8 is enabled then + * 1 pixel will take 1 byte + */ + vpfe_reg_write(ccdc, params->bytesperline, VPFE_HSIZE_OFF); + + vpfe_dbg(3, vpfe, "Writing %d (%x) to HSIZE_OFF\n", + params->bytesperline, params->bytesperline); + + /* Set value for SDOFST */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (params->image_invert_enable) { + /* For interlace inverse mode */ + vpfe_reg_write(ccdc, VPFE_INTERLACED_IMAGE_INVERT, + VPFE_SDOFST); + } else { + /* For interlace non inverse mode */ + vpfe_reg_write(ccdc, VPFE_INTERLACED_NO_IMAGE_INVERT, + VPFE_SDOFST); + } + } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + vpfe_reg_write(ccdc, VPFE_PROGRESSIVE_NO_IMAGE_INVERT, + VPFE_SDOFST); + } + + vpfe_reg_write(ccdc, syn_mode, VPFE_SYNMODE); + + vpfe_reg_dump(ccdc); +} + +static inline int +vpfe_ccdc_set_buftype(struct vpfe_ccdc *ccdc, + enum ccdc_buftype buf_type) +{ + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc->ccdc_cfg.bayer.buf_type = buf_type; + else + ccdc->ccdc_cfg.ycbcr.buf_type = buf_type; + + return 0; +} + +static inline enum ccdc_buftype vpfe_ccdc_get_buftype(struct vpfe_ccdc *ccdc) +{ + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) + return ccdc->ccdc_cfg.bayer.buf_type; + + return ccdc->ccdc_cfg.ycbcr.buf_type; +} + +static int vpfe_ccdc_set_pixel_format(struct vpfe_ccdc *ccdc, u32 pixfmt) +{ + struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc); + + vpfe_dbg(1, vpfe, "vpfe_ccdc_set_pixel_format: if_type: %d, pixfmt:%s\n", + ccdc->ccdc_cfg.if_type, print_fourcc(pixfmt)); + + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) { + ccdc->ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; + /* + * Need to clear it in case it was left on + * after the last capture. + */ + ccdc->ccdc_cfg.bayer.config_params.alaw.enable = 0; + + switch (pixfmt) { + case V4L2_PIX_FMT_SBGGR8: + ccdc->ccdc_cfg.bayer.config_params.alaw.enable = 1; + break; + + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUV420: + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_RGB565X: + break; + + case V4L2_PIX_FMT_SBGGR16: + default: + return -EINVAL; + } + } else { + switch (pixfmt) { + case V4L2_PIX_FMT_YUYV: + ccdc->ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + break; + + case V4L2_PIX_FMT_UYVY: + ccdc->ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + break; + + default: + return -EINVAL; + } + } + + return 0; +} + +static u32 vpfe_ccdc_get_pixel_format(struct vpfe_ccdc *ccdc) +{ + u32 pixfmt; + + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) { + pixfmt = V4L2_PIX_FMT_YUYV; + } else { + if (ccdc->ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) + pixfmt = V4L2_PIX_FMT_YUYV; + else + pixfmt = V4L2_PIX_FMT_UYVY; + } + + return pixfmt; +} + +static int +vpfe_ccdc_set_image_window(struct vpfe_ccdc *ccdc, + struct v4l2_rect *win, unsigned int bpp) +{ + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) { + ccdc->ccdc_cfg.bayer.win = *win; + ccdc->ccdc_cfg.bayer.bytesperpixel = bpp; + ccdc->ccdc_cfg.bayer.bytesperline = ALIGN(win->width * bpp, 32); + } else { + ccdc->ccdc_cfg.ycbcr.win = *win; + ccdc->ccdc_cfg.ycbcr.bytesperpixel = bpp; + ccdc->ccdc_cfg.ycbcr.bytesperline = ALIGN(win->width * bpp, 32); + } + + return 0; +} + +static inline void +vpfe_ccdc_get_image_window(struct vpfe_ccdc *ccdc, + struct v4l2_rect *win) +{ + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) + *win = ccdc->ccdc_cfg.bayer.win; + else + *win = ccdc->ccdc_cfg.ycbcr.win; +} + +static inline unsigned int vpfe_ccdc_get_line_length(struct vpfe_ccdc *ccdc) +{ + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) + return ccdc->ccdc_cfg.bayer.bytesperline; + + return ccdc->ccdc_cfg.ycbcr.bytesperline; +} + +static inline int +vpfe_ccdc_set_frame_format(struct vpfe_ccdc *ccdc, + enum ccdc_frmfmt frm_fmt) +{ + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc->ccdc_cfg.bayer.frm_fmt = frm_fmt; + else + ccdc->ccdc_cfg.ycbcr.frm_fmt = frm_fmt; + + return 0; +} + +static inline enum ccdc_frmfmt +vpfe_ccdc_get_frame_format(struct vpfe_ccdc *ccdc) +{ + if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) + return ccdc->ccdc_cfg.bayer.frm_fmt; + + return ccdc->ccdc_cfg.ycbcr.frm_fmt; +} + +static inline int vpfe_ccdc_getfid(struct vpfe_ccdc *ccdc) +{ + return (vpfe_reg_read(ccdc, VPFE_SYNMODE) >> 15) & 1; +} + +static inline void vpfe_set_sdr_addr(struct vpfe_ccdc *ccdc, unsigned long addr) +{ + vpfe_reg_write(ccdc, addr & 0xffffffe0, VPFE_SDR_ADDR); +} + +static int vpfe_ccdc_set_hw_if_params(struct vpfe_ccdc *ccdc, + struct vpfe_hw_if_param *params) +{ + struct vpfe_device *vpfe = container_of(ccdc, struct vpfe_device, ccdc); + + ccdc->ccdc_cfg.if_type = params->if_type; + + switch (params->if_type) { + case VPFE_BT656: + case VPFE_YCBCR_SYNC_16: + case VPFE_YCBCR_SYNC_8: + case VPFE_BT656_10BIT: + ccdc->ccdc_cfg.ycbcr.vd_pol = params->vdpol; + ccdc->ccdc_cfg.ycbcr.hd_pol = params->hdpol; + break; + + case VPFE_RAW_BAYER: + ccdc->ccdc_cfg.bayer.vd_pol = params->vdpol; + ccdc->ccdc_cfg.bayer.hd_pol = params->hdpol; + if (params->bus_width == 10) + ccdc->ccdc_cfg.bayer.config_params.data_sz = + VPFE_CCDC_DATA_10BITS; + else + ccdc->ccdc_cfg.bayer.config_params.data_sz = + VPFE_CCDC_DATA_8BITS; + vpfe_dbg(1, vpfe, "params.bus_width: %d\n", + params->bus_width); + vpfe_dbg(1, vpfe, "config_params.data_sz: %d\n", + ccdc->ccdc_cfg.bayer.config_params.data_sz); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static void vpfe_clear_intr(struct vpfe_ccdc *ccdc, int vdint) +{ + unsigned int vpfe_int_status; + + vpfe_int_status = vpfe_reg_read(ccdc, VPFE_IRQ_STS); + + switch (vdint) { + /* VD0 interrupt */ + case VPFE_VDINT0: + vpfe_int_status &= ~VPFE_VDINT0; + vpfe_int_status |= VPFE_VDINT0; + break; + + /* VD1 interrupt */ + case VPFE_VDINT1: + vpfe_int_status &= ~VPFE_VDINT1; + vpfe_int_status |= VPFE_VDINT1; + break; + + /* VD2 interrupt */ + case VPFE_VDINT2: + vpfe_int_status &= ~VPFE_VDINT2; + vpfe_int_status |= VPFE_VDINT2; + break; + + /* Clear all interrupts */ + default: + vpfe_int_status &= ~(VPFE_VDINT0 | + VPFE_VDINT1 | + VPFE_VDINT2); + vpfe_int_status |= (VPFE_VDINT0 | + VPFE_VDINT1 | + VPFE_VDINT2); + break; + } + /* Clear specific VDINT from the status register */ + vpfe_reg_write(ccdc, vpfe_int_status, VPFE_IRQ_STS); + + vpfe_int_status = vpfe_reg_read(ccdc, VPFE_IRQ_STS); + + /* Acknowledge that we are done with all interrupts */ + vpfe_reg_write(ccdc, 1, VPFE_IRQ_EOI); +} + +static void vpfe_ccdc_config_defaults(struct vpfe_ccdc *ccdc) +{ + ccdc->ccdc_cfg.if_type = VPFE_RAW_BAYER; + + ccdc->ccdc_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT; + ccdc->ccdc_cfg.ycbcr.frm_fmt = CCDC_FRMFMT_INTERLACED; + ccdc->ccdc_cfg.ycbcr.fid_pol = VPFE_PINPOL_POSITIVE; + ccdc->ccdc_cfg.ycbcr.vd_pol = VPFE_PINPOL_POSITIVE; + ccdc->ccdc_cfg.ycbcr.hd_pol = VPFE_PINPOL_POSITIVE; + ccdc->ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + ccdc->ccdc_cfg.ycbcr.buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED; + + ccdc->ccdc_cfg.ycbcr.win.left = 0; + ccdc->ccdc_cfg.ycbcr.win.top = 0; + ccdc->ccdc_cfg.ycbcr.win.width = 720; + ccdc->ccdc_cfg.ycbcr.win.height = 576; + ccdc->ccdc_cfg.ycbcr.bt656_enable = 1; + + ccdc->ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; + ccdc->ccdc_cfg.bayer.frm_fmt = CCDC_FRMFMT_PROGRESSIVE; + ccdc->ccdc_cfg.bayer.fid_pol = VPFE_PINPOL_POSITIVE; + ccdc->ccdc_cfg.bayer.vd_pol = VPFE_PINPOL_POSITIVE; + ccdc->ccdc_cfg.bayer.hd_pol = VPFE_PINPOL_POSITIVE; + + ccdc->ccdc_cfg.bayer.win.left = 0; + ccdc->ccdc_cfg.bayer.win.top = 0; + ccdc->ccdc_cfg.bayer.win.width = 800; + ccdc->ccdc_cfg.bayer.win.height = 600; + ccdc->ccdc_cfg.bayer.config_params.data_sz = VPFE_CCDC_DATA_8BITS; + ccdc->ccdc_cfg.bayer.config_params.alaw.gamma_wd = + VPFE_CCDC_GAMMA_BITS_09_0; +} + +/* + * vpfe_get_ccdc_image_format - Get image parameters based on CCDC settings + */ +static int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe, + struct v4l2_format *f) +{ + struct v4l2_rect image_win; + enum ccdc_buftype buf_type; + enum ccdc_frmfmt frm_fmt; + + memset(f, 0, sizeof(*f)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + vpfe_ccdc_get_image_window(&vpfe->ccdc, &image_win); + f->fmt.pix.width = image_win.width; + f->fmt.pix.height = image_win.height; + f->fmt.pix.bytesperline = vpfe_ccdc_get_line_length(&vpfe->ccdc); + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * + f->fmt.pix.height; + buf_type = vpfe_ccdc_get_buftype(&vpfe->ccdc); + f->fmt.pix.pixelformat = vpfe_ccdc_get_pixel_format(&vpfe->ccdc); + frm_fmt = vpfe_ccdc_get_frame_format(&vpfe->ccdc); + + if (frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + f->fmt.pix.field = V4L2_FIELD_NONE; + } else if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) { + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + } else if (buf_type == CCDC_BUFTYPE_FLD_SEPARATED) { + f->fmt.pix.field = V4L2_FIELD_SEQ_TB; + } else { + vpfe_err(vpfe, "Invalid buf_type\n"); + return -EINVAL; + } + } else { + vpfe_err(vpfe, "Invalid frm_fmt\n"); + return -EINVAL; + } + return 0; +} + +static int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe) +{ + enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED; + int ret; + + vpfe_dbg(2, vpfe, "vpfe_config_ccdc_image_format\n"); + + vpfe_dbg(1, vpfe, "pixelformat: %s\n", + print_fourcc(vpfe->fmt.fmt.pix.pixelformat)); + + if (vpfe_ccdc_set_pixel_format(&vpfe->ccdc, + vpfe->fmt.fmt.pix.pixelformat) < 0) { + vpfe_err(vpfe, "couldn't set pix format in ccdc\n"); + return -EINVAL; + } + + /* configure the image window */ + vpfe_ccdc_set_image_window(&vpfe->ccdc, &vpfe->crop, vpfe->bpp); + + switch (vpfe->fmt.fmt.pix.field) { + case V4L2_FIELD_INTERLACED: + /* do nothing, since it is default */ + ret = vpfe_ccdc_set_buftype( + &vpfe->ccdc, + CCDC_BUFTYPE_FLD_INTERLEAVED); + break; + + case V4L2_FIELD_NONE: + frm_fmt = CCDC_FRMFMT_PROGRESSIVE; + /* buffer type only applicable for interlaced scan */ + break; + + case V4L2_FIELD_SEQ_TB: + ret = vpfe_ccdc_set_buftype( + &vpfe->ccdc, + CCDC_BUFTYPE_FLD_SEPARATED); + break; + + default: + return -EINVAL; + } + + if (ret) + return ret; + + return vpfe_ccdc_set_frame_format(&vpfe->ccdc, frm_fmt); +} + +/* + * vpfe_config_image_format() + * For a given standard, this functions sets up the default + * pix format & crop values in the vpfe device and ccdc. It first + * starts with defaults based values from the standard table. + * It then checks if sub device support g_mbus_fmt and then override the + * values based on that.Sets crop values to match with scan resolution + * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the + * values in ccdc + */ +static int vpfe_config_image_format(struct vpfe_device *vpfe, + v4l2_std_id std_id) +{ + struct v4l2_pix_format *pix = &vpfe->fmt.fmt.pix; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) { + if (vpfe_standards[i].std_id & std_id) { + vpfe->std_info.active_pixels = + vpfe_standards[i].width; + vpfe->std_info.active_lines = + vpfe_standards[i].height; + vpfe->std_info.frame_format = + vpfe_standards[i].frame_format; + vpfe->std_index = i; + + break; + } + } + + if (i == ARRAY_SIZE(vpfe_standards)) { + vpfe_err(vpfe, "standard not supported\n"); + return -EINVAL; + } + + vpfe->crop.top = vpfe->crop.left = 0; + vpfe->crop.width = vpfe->std_info.active_pixels; + vpfe->crop.height = vpfe->std_info.active_lines; + pix->width = vpfe->crop.width; + pix->height = vpfe->crop.height; + pix->pixelformat = V4L2_PIX_FMT_YUYV; + + /* first field and frame format based on standard frame format */ + if (vpfe->std_info.frame_format) + pix->field = V4L2_FIELD_INTERLACED; + else + pix->field = V4L2_FIELD_NONE; + + ret = __vpfe_get_format(vpfe, &vpfe->fmt, &vpfe->bpp); + if (ret) + return ret; + + /* Update the crop window based on found values */ + vpfe->crop.width = pix->width; + vpfe->crop.height = pix->height; + + return vpfe_config_ccdc_image_format(vpfe); +} + +static int vpfe_initialize_device(struct vpfe_device *vpfe) +{ + struct vpfe_subdev_info *sdinfo; + int ret; + + sdinfo = &vpfe->cfg->sub_devs[0]; + sdinfo->sd = vpfe->sd[0]; + vpfe->current_input = 0; + vpfe->std_index = 0; + /* Configure the default format information */ + ret = vpfe_config_image_format(vpfe, + vpfe_standards[vpfe->std_index].std_id); + if (ret) + return ret; + + pm_runtime_get_sync(vpfe->pdev); + + vpfe_config_enable(&vpfe->ccdc, 1); + + vpfe_ccdc_restore_defaults(&vpfe->ccdc); + + /* Clear all VPFE interrupts */ + vpfe_clear_intr(&vpfe->ccdc, -1); + + return ret; +} + +/* + * vpfe_release : This function is based on the vb2_fop_release + * helper function. + * It has been augmented to handle module power management, + * by disabling/enabling h/w module fcntl clock when necessary. + */ +static int vpfe_release(struct file *file) +{ + struct vpfe_device *vpfe = video_drvdata(file); + int ret; + + mutex_lock(&vpfe->lock); + + if (v4l2_fh_is_singular_file(file)) + vpfe_ccdc_close(&vpfe->ccdc, vpfe->pdev); + ret = _vb2_fop_release(file, NULL); + + mutex_unlock(&vpfe->lock); + + return ret; +} + +/* + * vpfe_open : This function is based on the v4l2_fh_open helper function. + * It has been augmented to handle module power management, + * by disabling/enabling h/w module fcntl clock when necessary. + */ +static int vpfe_open(struct file *file) +{ + struct vpfe_device *vpfe = video_drvdata(file); + int ret; + + mutex_lock(&vpfe->lock); + + ret = v4l2_fh_open(file); + if (ret) { + vpfe_err(vpfe, "v4l2_fh_open failed\n"); + goto unlock; + } + + if (!v4l2_fh_is_singular_file(file)) + goto unlock; + + if (vpfe_initialize_device(vpfe)) { + v4l2_fh_release(file); + ret = -ENODEV; + } + +unlock: + mutex_unlock(&vpfe->lock); + return ret; +} + +/** + * vpfe_schedule_next_buffer: set next buffer address for capture + * @vpfe : ptr to vpfe device + * + * This function will get next buffer from the dma queue and + * set the buffer address in the vpfe register for capture. + * the buffer is marked active + * + * Assumes caller is holding vpfe->dma_queue_lock already + */ +static inline void vpfe_schedule_next_buffer(struct vpfe_device *vpfe) +{ + vpfe->next_frm = list_entry(vpfe->dma_queue.next, + struct vpfe_cap_buffer, list); + list_del(&vpfe->next_frm->list); + + vpfe_set_sdr_addr(&vpfe->ccdc, + vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb, 0)); +} + +static inline void vpfe_schedule_bottom_field(struct vpfe_device *vpfe) +{ + unsigned long addr; + + addr = vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb, 0) + + vpfe->field_off; + + vpfe_set_sdr_addr(&vpfe->ccdc, addr); +} + +/* + * vpfe_process_buffer_complete: process a completed buffer + * @vpfe : ptr to vpfe device + * + * This function time stamp the buffer and mark it as DONE. It also + * wake up any process waiting on the QUEUE and set the next buffer + * as current + */ +static inline void vpfe_process_buffer_complete(struct vpfe_device *vpfe) +{ + v4l2_get_timestamp(&vpfe->cur_frm->vb.v4l2_buf.timestamp); + vpfe->cur_frm->vb.v4l2_buf.field = vpfe->fmt.fmt.pix.field; + vpfe->cur_frm->vb.v4l2_buf.sequence = vpfe->sequence++; + vb2_buffer_done(&vpfe->cur_frm->vb, VB2_BUF_STATE_DONE); + vpfe->cur_frm = vpfe->next_frm; +} + +/* + * vpfe_isr : ISR handler for vpfe capture (VINT0) + * @irq: irq number + * @dev_id: dev_id ptr + * + * It changes status of the captured buffer, takes next buffer from the queue + * and sets its address in VPFE registers + */ +static irqreturn_t vpfe_isr(int irq, void *dev) +{ + struct vpfe_device *vpfe = (struct vpfe_device *)dev; + enum v4l2_field field; + int intr_status; + int fid; + + intr_status = vpfe_reg_read(&vpfe->ccdc, VPFE_IRQ_STS); + + if (intr_status & VPFE_VDINT0) { + field = vpfe->fmt.fmt.pix.field; + + if (field == V4L2_FIELD_NONE) { + /* handle progressive frame capture */ + if (vpfe->cur_frm != vpfe->next_frm) + vpfe_process_buffer_complete(vpfe); + goto next_intr; + } + + /* interlaced or TB capture check which field + we are in hardware */ + fid = vpfe_ccdc_getfid(&vpfe->ccdc); + + /* switch the software maintained field id */ + vpfe->field ^= 1; + if (fid == vpfe->field) { + /* we are in-sync here,continue */ + if (fid == 0) { + /* + * One frame is just being captured. If the + * next frame is available, release the + * current frame and move on + */ + if (vpfe->cur_frm != vpfe->next_frm) + vpfe_process_buffer_complete(vpfe); + /* + * based on whether the two fields are stored + * interleave or separately in memory, + * reconfigure the CCDC memory address + */ + if (field == V4L2_FIELD_SEQ_TB) + vpfe_schedule_bottom_field(vpfe); + + goto next_intr; + } + /* + * if one field is just being captured configure + * the next frame get the next frame from the empty + * queue if no frame is available hold on to the + * current buffer + */ + spin_lock(&vpfe->dma_queue_lock); + if (!list_empty(&vpfe->dma_queue) && + vpfe->cur_frm == vpfe->next_frm) + vpfe_schedule_next_buffer(vpfe); + spin_unlock(&vpfe->dma_queue_lock); + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + vpfe->field = fid; + } + } + +next_intr: + if (intr_status & VPFE_VDINT1) { + spin_lock(&vpfe->dma_queue_lock); + if (vpfe->fmt.fmt.pix.field == V4L2_FIELD_NONE && + !list_empty(&vpfe->dma_queue) && + vpfe->cur_frm == vpfe->next_frm) + vpfe_schedule_next_buffer(vpfe); + spin_unlock(&vpfe->dma_queue_lock); + } + + vpfe_clear_intr(&vpfe->ccdc, intr_status); + + return IRQ_HANDLED; +} + +static inline void vpfe_detach_irq(struct vpfe_device *vpfe) +{ + unsigned int intr = VPFE_VDINT0; + enum ccdc_frmfmt frame_format; + + frame_format = vpfe_ccdc_get_frame_format(&vpfe->ccdc); + if (frame_format == CCDC_FRMFMT_PROGRESSIVE) + intr |= VPFE_VDINT1; + + vpfe_reg_write(&vpfe->ccdc, intr, VPFE_IRQ_EN_CLR); +} + +static inline void vpfe_attach_irq(struct vpfe_device *vpfe) +{ + unsigned int intr = VPFE_VDINT0; + enum ccdc_frmfmt frame_format; + + frame_format = vpfe_ccdc_get_frame_format(&vpfe->ccdc); + if (frame_format == CCDC_FRMFMT_PROGRESSIVE) + intr |= VPFE_VDINT1; + + vpfe_reg_write(&vpfe->ccdc, intr, VPFE_IRQ_EN_SET); +} + +static int vpfe_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpfe_device *vpfe = video_drvdata(file); + + vpfe_dbg(2, vpfe, "vpfe_querycap\n"); + + strlcpy(cap->driver, VPFE_MODULE_NAME, sizeof(cap->driver)); + strlcpy(cap->card, "TI AM437x VPFE", sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", vpfe->v4l2_dev.name); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + + return 0; +} + +/* get the format set at output pad of the adjacent subdev */ +static int __vpfe_get_format(struct vpfe_device *vpfe, + struct v4l2_format *format, unsigned int *bpp) +{ + struct v4l2_mbus_framefmt mbus_fmt; + struct vpfe_subdev_info *sdinfo; + struct v4l2_subdev_format fmt; + int ret; + + sdinfo = vpfe->current_subdev; + if (!sdinfo->sd) + return -EINVAL; + + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.pad = 0; + + ret = v4l2_subdev_call(sdinfo->sd, pad, get_fmt, NULL, &fmt); + if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + if (!ret) { + v4l2_fill_pix_format(&format->fmt.pix, &fmt.format); + mbus_to_pix(vpfe, &fmt.format, &format->fmt.pix, bpp); + } else { + ret = v4l2_device_call_until_err(&vpfe->v4l2_dev, + sdinfo->grp_id, + video, g_mbus_fmt, + &mbus_fmt); + if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + v4l2_fill_pix_format(&format->fmt.pix, &mbus_fmt); + mbus_to_pix(vpfe, &mbus_fmt, &format->fmt.pix, bpp); + } + + format->type = vpfe->fmt.type; + + vpfe_dbg(1, vpfe, + "%s size %dx%d (%s) bytesperline = %d, size = %d, bpp = %d\n", + __func__, format->fmt.pix.width, format->fmt.pix.height, + print_fourcc(format->fmt.pix.pixelformat), + format->fmt.pix.bytesperline, format->fmt.pix.sizeimage, *bpp); + + return 0; +} + +/* set the format at output pad of the adjacent subdev */ +static int __vpfe_set_format(struct vpfe_device *vpfe, + struct v4l2_format *format, unsigned int *bpp) +{ + struct v4l2_mbus_framefmt mbus_fmt; + struct vpfe_subdev_info *sdinfo; + struct v4l2_subdev_format fmt; + int ret; + + vpfe_dbg(2, vpfe, "__vpfe_set_format\n"); + + sdinfo = vpfe->current_subdev; + if (!sdinfo->sd) + return -EINVAL; + + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.pad = 0; + + pix_to_mbus(vpfe, &format->fmt.pix, &fmt.format); + + ret = v4l2_subdev_call(sdinfo->sd, pad, set_fmt, NULL, &fmt); + if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + if (!ret) { + v4l2_fill_pix_format(&format->fmt.pix, &fmt.format); + mbus_to_pix(vpfe, &fmt.format, &format->fmt.pix, bpp); + } else { + ret = v4l2_device_call_until_err(&vpfe->v4l2_dev, + sdinfo->grp_id, + video, s_mbus_fmt, + &mbus_fmt); + if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + v4l2_fill_pix_format(&format->fmt.pix, &mbus_fmt); + mbus_to_pix(vpfe, &mbus_fmt, &format->fmt.pix, bpp); + } + + format->type = vpfe->fmt.type; + + vpfe_dbg(1, vpfe, + "%s size %dx%d (%s) bytesperline = %d, size = %d, bpp = %d\n", + __func__, format->fmt.pix.width, format->fmt.pix.height, + print_fourcc(format->fmt.pix.pixelformat), + format->fmt.pix.bytesperline, format->fmt.pix.sizeimage, *bpp); + + return 0; +} + +static int vpfe_g_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_device *vpfe = video_drvdata(file); + + vpfe_dbg(2, vpfe, "vpfe_g_fmt\n"); + + *fmt = vpfe->fmt; + + return 0; +} + +static int vpfe_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct vpfe_device *vpfe = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + struct vpfe_fmt *fmt = NULL; + unsigned int k; + + vpfe_dbg(2, vpfe, "vpfe_enum_format index:%d\n", + f->index); + + sdinfo = vpfe->current_subdev; + if (!sdinfo->sd) + return -EINVAL; + + if (f->index > ARRAY_SIZE(formats)) + return -EINVAL; + + for (k = 0; k < ARRAY_SIZE(formats); k++) { + if (formats[k].index == f->index) { + fmt = &formats[k]; + break; + } + } + if (!fmt) + return -EINVAL; + + strncpy(f->description, fmt->name, sizeof(f->description) - 1); + f->pixelformat = fmt->fourcc; + f->type = vpfe->fmt.type; + + vpfe_dbg(1, vpfe, "vpfe_enum_format: mbus index: %d code: %x pixelformat: %s [%s]\n", + f->index, fmt->code, print_fourcc(fmt->fourcc), fmt->name); + + return 0; +} + +static int vpfe_try_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_device *vpfe = video_drvdata(file); + unsigned int bpp; + + vpfe_dbg(2, vpfe, "vpfe_try_fmt\n"); + + return __vpfe_get_format(vpfe, fmt, &bpp); +} + +static int vpfe_s_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_device *vpfe = video_drvdata(file); + struct v4l2_format format; + unsigned int bpp; + int ret; + + vpfe_dbg(2, vpfe, "vpfe_s_fmt\n"); + + /* If streaming is started, return error */ + if (vb2_is_busy(&vpfe->buffer_queue)) { + vpfe_err(vpfe, "%s device busy\n", __func__); + return -EBUSY; + } + + ret = vpfe_try_fmt(file, priv, fmt); + if (ret) + return ret; + + + if (!cmp_v4l2_format(fmt, &format)) { + /* Sensor format is different from the requested format + * so we need to change it + */ + ret = __vpfe_set_format(vpfe, fmt, &bpp); + if (ret) + return ret; + } else /* Just make sure all of the fields are consistent */ + *fmt = format; + + /* First detach any IRQ if currently attached */ + vpfe_detach_irq(vpfe); + vpfe->fmt = *fmt; + vpfe->bpp = bpp; + + /* Update the crop window based on found values */ + vpfe->crop.width = fmt->fmt.pix.width; + vpfe->crop.height = fmt->fmt.pix.height; + + /* set image capture parameters in the ccdc */ + return vpfe_config_ccdc_image_format(vpfe); +} + +static int vpfe_enum_size(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct vpfe_device *vpfe = video_drvdata(file); + struct v4l2_subdev_frame_size_enum fse; + struct vpfe_subdev_info *sdinfo; + struct v4l2_mbus_framefmt mbus; + struct v4l2_pix_format pix; + struct vpfe_fmt *fmt; + int ret; + + vpfe_dbg(2, vpfe, "vpfe_enum_size\n"); + + /* check for valid format */ + fmt = find_format_by_pix(fsize->pixel_format); + if (!fmt) { + vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n", + fsize->pixel_format); + return -EINVAL; + } + + memset(fsize->reserved, 0x0, sizeof(fsize->reserved)); + + sdinfo = vpfe->current_subdev; + if (!sdinfo->sd) + return -EINVAL; + + memset(&pix, 0x0, sizeof(pix)); + /* Construct pix from parameter and use default for the rest */ + pix.pixelformat = fsize->pixel_format; + pix.width = 640; + pix.height = 480; + pix.colorspace = V4L2_COLORSPACE_SRGB; + pix.field = V4L2_FIELD_NONE; + pix_to_mbus(vpfe, &pix, &mbus); + + memset(&fse, 0x0, sizeof(fse)); + fse.index = fsize->index; + fse.pad = 0; + fse.code = mbus.code; + ret = v4l2_subdev_call(sdinfo->sd, pad, enum_frame_size, NULL, &fse); + if (ret) + return -EINVAL; + + vpfe_dbg(1, vpfe, "vpfe_enum_size: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", + fse.index, fse.code, fse.min_width, fse.max_width, + fse.min_height, fse.max_height); + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = fse.max_width; + fsize->discrete.height = fse.max_height; + + vpfe_dbg(1, vpfe, "vpfe_enum_size: index: %d pixformat: %s size: %dx%d\n", + fsize->index, print_fourcc(fsize->pixel_format), + fsize->discrete.width, fsize->discrete.height); + + return 0; +} + +/* + * vpfe_get_subdev_input_index - Get subdev index and subdev input index for a + * given app input index + */ +static int +vpfe_get_subdev_input_index(struct vpfe_device *vpfe, + int *subdev_index, + int *subdev_input_index, + int app_input_index) +{ + struct vpfe_config *cfg = vpfe->cfg; + struct vpfe_subdev_info *sdinfo; + int i, j = 0; + + for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) { + sdinfo = &cfg->sub_devs[i]; + if (app_input_index < (j + 1)) { + *subdev_index = i; + *subdev_input_index = app_input_index - j; + return 0; + } + j++; + } + return -EINVAL; +} + +/* + * vpfe_get_app_input - Get app input index for a given subdev input index + * driver stores the input index of the current sub device and translate it + * when application request the current input + */ +static int vpfe_get_app_input_index(struct vpfe_device *vpfe, + int *app_input_index) +{ + struct vpfe_config *cfg = vpfe->cfg; + struct vpfe_subdev_info *sdinfo; + int i, j = 0; + + for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) { + sdinfo = &cfg->sub_devs[i]; + if (!strcmp(sdinfo->name, vpfe->current_subdev->name)) { + if (vpfe->current_input >= 1) + return -1; + *app_input_index = j + vpfe->current_input; + return 0; + } + j++; + } + return -EINVAL; +} + +static int vpfe_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct vpfe_device *vpfe = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int subdev, index; + + vpfe_dbg(2, vpfe, "vpfe_enum_input\n"); + + if (vpfe_get_subdev_input_index(vpfe, &subdev, &index, + inp->index) < 0) { + vpfe_dbg(1, vpfe, + "input information not found for the subdev\n"); + return -EINVAL; + } + sdinfo = &vpfe->cfg->sub_devs[subdev]; + *inp = sdinfo->inputs[index]; + + return 0; +} + +static int vpfe_g_input(struct file *file, void *priv, unsigned int *index) +{ + struct vpfe_device *vpfe = video_drvdata(file); + + vpfe_dbg(2, vpfe, "vpfe_g_input\n"); + + return vpfe_get_app_input_index(vpfe, index); +} + +/* Assumes caller is holding vpfe_dev->lock */ +static int vpfe_set_input(struct vpfe_device *vpfe, unsigned int index) +{ + int subdev_index = 0, inp_index = 0; + struct vpfe_subdev_info *sdinfo; + struct vpfe_route *route; + u32 input, output; + int ret; + + vpfe_dbg(2, vpfe, "vpfe_set_input: index: %d\n", index); + + /* If streaming is started, return error */ + if (vb2_is_busy(&vpfe->buffer_queue)) { + vpfe_err(vpfe, "%s device busy\n", __func__); + return -EBUSY; + } + ret = vpfe_get_subdev_input_index(vpfe, + &subdev_index, + &inp_index, + index); + if (ret < 0) { + vpfe_err(vpfe, "invalid input index: %d\n", index); + goto get_out; + } + + sdinfo = &vpfe->cfg->sub_devs[subdev_index]; + sdinfo->sd = vpfe->sd[subdev_index]; + route = &sdinfo->routes[inp_index]; + if (route && sdinfo->can_route) { + input = route->input; + output = route->output; + if (sdinfo->sd) { + ret = v4l2_subdev_call(sdinfo->sd, video, + s_routing, input, output, 0); + if (ret) { + vpfe_err(vpfe, "s_routing failed\n"); + ret = -EINVAL; + goto get_out; + } + } + + } + + vpfe->current_subdev = sdinfo; + if (sdinfo->sd) + vpfe->v4l2_dev.ctrl_handler = sdinfo->sd->ctrl_handler; + vpfe->current_input = index; + vpfe->std_index = 0; + + /* set the bus/interface parameter for the sub device in ccdc */ + ret = vpfe_ccdc_set_hw_if_params(&vpfe->ccdc, &sdinfo->vpfe_param); + if (ret) + return ret; + + /* set the default image parameters in the device */ + return vpfe_config_image_format(vpfe, + vpfe_standards[vpfe->std_index].std_id); + +get_out: + return ret; +} + +static int vpfe_s_input(struct file *file, void *priv, unsigned int index) +{ + struct vpfe_device *vpfe = video_drvdata(file); + + vpfe_dbg(2, vpfe, + "vpfe_s_input: index: %d\n", index); + + return vpfe_set_input(vpfe, index); +} + +static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_device *vpfe = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + + vpfe_dbg(2, vpfe, "vpfe_querystd\n"); + + sdinfo = vpfe->current_subdev; + if (!(sdinfo->inputs[0].capabilities & V4L2_IN_CAP_STD)) + return -ENODATA; + + /* Call querystd function of decoder device */ + return v4l2_device_call_until_err(&vpfe->v4l2_dev, sdinfo->grp_id, + video, querystd, std_id); +} + +static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id) +{ + struct vpfe_device *vpfe = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + int ret; + + vpfe_dbg(2, vpfe, "vpfe_s_std\n"); + + sdinfo = vpfe->current_subdev; + if (!(sdinfo->inputs[0].capabilities & V4L2_IN_CAP_STD)) + return -ENODATA; + + /* If streaming is started, return error */ + if (vb2_is_busy(&vpfe->buffer_queue)) { + vpfe_err(vpfe, "%s device busy\n", __func__); + ret = -EBUSY; + return ret; + } + + ret = v4l2_device_call_until_err(&vpfe->v4l2_dev, sdinfo->grp_id, + video, s_std, std_id); + if (ret < 0) { + vpfe_err(vpfe, "Failed to set standard\n"); + return ret; + } + ret = vpfe_config_image_format(vpfe, std_id); + + return ret; +} + +static int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_device *vpfe = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + + vpfe_dbg(2, vpfe, "vpfe_g_std\n"); + + sdinfo = vpfe->current_subdev; + if (sdinfo->inputs[0].capabilities != V4L2_IN_CAP_STD) + return -ENODATA; + + *std_id = vpfe_standards[vpfe->std_index].std_id; + + return 0; +} + +/* + * vpfe_calculate_offsets : This function calculates buffers offset + * for top and bottom field + */ +static void vpfe_calculate_offsets(struct vpfe_device *vpfe) +{ + struct v4l2_rect image_win; + + vpfe_dbg(2, vpfe, "vpfe_calculate_offsets\n"); + + vpfe_ccdc_get_image_window(&vpfe->ccdc, &image_win); + vpfe->field_off = image_win.height * image_win.width; +} + +/* + * vpfe_queue_setup - Callback function for buffer setup. + * @vq: vb2_queue ptr + * @fmt: v4l2 format + * @nbuffers: ptr to number of buffers requested by application + * @nplanes:: contains number of distinct video planes needed to hold a frame + * @sizes[]: contains the size (in bytes) of each plane. + * @alloc_ctxs: ptr to allocation context + * + * This callback function is called when reqbuf() is called to adjust + * the buffer count and buffer size + */ +static int vpfe_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) +{ + struct vpfe_device *vpfe = vb2_get_drv_priv(vq); + + if (fmt && fmt->fmt.pix.sizeimage < vpfe->fmt.fmt.pix.sizeimage) + return -EINVAL; + + if (vq->num_buffers + *nbuffers < 3) + *nbuffers = 3 - vq->num_buffers; + + *nplanes = 1; + sizes[0] = fmt ? fmt->fmt.pix.sizeimage : vpfe->fmt.fmt.pix.sizeimage; + alloc_ctxs[0] = vpfe->alloc_ctx; + + vpfe_dbg(1, vpfe, + "nbuffers=%d, size=%u\n", *nbuffers, sizes[0]); + + /* Calculate field offset */ + vpfe_calculate_offsets(vpfe); + + return 0; +} + +/* + * vpfe_buffer_prepare : callback function for buffer prepare + * @vb: ptr to vb2_buffer + * + * This is the callback function for buffer prepare when vb2_qbuf() + * function is called. The buffer is prepared and user space virtual address + * or user address is converted into physical address + */ +static int vpfe_buffer_prepare(struct vb2_buffer *vb) +{ + struct vpfe_device *vpfe = vb2_get_drv_priv(vb->vb2_queue); + + vb2_set_plane_payload(vb, 0, vpfe->fmt.fmt.pix.sizeimage); + + if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) + return -EINVAL; + + vb->v4l2_buf.field = vpfe->fmt.fmt.pix.field; + + return 0; +} + +/* + * vpfe_buffer_queue : Callback function to add buffer to DMA queue + * @vb: ptr to vb2_buffer + */ +static void vpfe_buffer_queue(struct vb2_buffer *vb) +{ + struct vpfe_device *vpfe = vb2_get_drv_priv(vb->vb2_queue); + struct vpfe_cap_buffer *buf = to_vpfe_buffer(vb); + unsigned long flags = 0; + + /* add the buffer to the DMA queue */ + spin_lock_irqsave(&vpfe->dma_queue_lock, flags); + list_add_tail(&buf->list, &vpfe->dma_queue); + spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags); +} + +/* + * vpfe_start_streaming : Starts the DMA engine for streaming + * @vb: ptr to vb2_buffer + * @count: number of buffers + */ +static int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct vpfe_device *vpfe = vb2_get_drv_priv(vq); + struct vpfe_cap_buffer *buf, *tmp; + struct vpfe_subdev_info *sdinfo; + unsigned long flags; + unsigned long addr; + int ret; + + spin_lock_irqsave(&vpfe->dma_queue_lock, flags); + + vpfe->field = 0; + vpfe->sequence = 0; + + sdinfo = vpfe->current_subdev; + + vpfe_attach_irq(vpfe); + + if (vpfe->ccdc.ccdc_cfg.if_type == VPFE_RAW_BAYER) + vpfe_ccdc_config_raw(&vpfe->ccdc); + else + vpfe_ccdc_config_ycbcr(&vpfe->ccdc); + + /* Get the next frame from the buffer queue */ + vpfe->next_frm = list_entry(vpfe->dma_queue.next, + struct vpfe_cap_buffer, list); + vpfe->cur_frm = vpfe->next_frm; + /* Remove buffer from the buffer queue */ + list_del(&vpfe->cur_frm->list); + spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags); + + addr = vb2_dma_contig_plane_dma_addr(&vpfe->cur_frm->vb, 0); + + vpfe_set_sdr_addr(&vpfe->ccdc, (unsigned long)(addr)); + + vpfe_pcr_enable(&vpfe->ccdc, 1); + + ret = v4l2_subdev_call(sdinfo->sd, video, s_stream, 1); + if (ret < 0) { + vpfe_err(vpfe, "Error in attaching interrupt handle\n"); + goto err; + } + + return 0; + +err: + list_for_each_entry_safe(buf, tmp, &vpfe->dma_queue, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_QUEUED); + } + + return ret; +} + +/* + * vpfe_stop_streaming : Stop the DMA engine + * @vq: ptr to vb2_queue + * + * This callback stops the DMA engine and any remaining buffers + * in the DMA queue are released. + */ +static void vpfe_stop_streaming(struct vb2_queue *vq) +{ + struct vpfe_device *vpfe = vb2_get_drv_priv(vq); + struct vpfe_subdev_info *sdinfo; + unsigned long flags; + int ret; + + vpfe_pcr_enable(&vpfe->ccdc, 0); + + vpfe_detach_irq(vpfe); + + sdinfo = vpfe->current_subdev; + ret = v4l2_subdev_call(sdinfo->sd, video, s_stream, 0); + if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) + vpfe_dbg(1, vpfe, "stream off failed in subdev\n"); + + /* release all active buffers */ + spin_lock_irqsave(&vpfe->dma_queue_lock, flags); + if (vpfe->cur_frm == vpfe->next_frm) { + vb2_buffer_done(&vpfe->cur_frm->vb, VB2_BUF_STATE_ERROR); + } else { + if (vpfe->cur_frm != NULL) + vb2_buffer_done(&vpfe->cur_frm->vb, + VB2_BUF_STATE_ERROR); + if (vpfe->next_frm != NULL) + vb2_buffer_done(&vpfe->next_frm->vb, + VB2_BUF_STATE_ERROR); + } + + while (!list_empty(&vpfe->dma_queue)) { + vpfe->next_frm = list_entry(vpfe->dma_queue.next, + struct vpfe_cap_buffer, list); + list_del(&vpfe->next_frm->list); + vb2_buffer_done(&vpfe->next_frm->vb, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags); +} + +static int vpfe_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *crop) +{ + struct vpfe_device *vpfe = video_drvdata(file); + + vpfe_dbg(2, vpfe, "vpfe_cropcap\n"); + + if (vpfe->std_index >= ARRAY_SIZE(vpfe_standards)) + return -EINVAL; + + memset(crop, 0, sizeof(struct v4l2_cropcap)); + + crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + crop->defrect.width = vpfe_standards[vpfe->std_index].width; + crop->bounds.width = crop->defrect.width; + crop->defrect.height = vpfe_standards[vpfe->std_index].height; + crop->bounds.height = crop->defrect.height; + crop->pixelaspect = vpfe_standards[vpfe->std_index].pixelaspect; + + return 0; +} + +static int +vpfe_g_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vpfe_device *vpfe = video_drvdata(file); + + switch (s->target) { + case V4L2_SEL_TGT_CROP_BOUNDS: + case V4L2_SEL_TGT_CROP_DEFAULT: + s->r.left = s->r.top = 0; + s->r.width = vpfe->crop.width; + s->r.height = vpfe->crop.height; + break; + + case V4L2_SEL_TGT_CROP: + s->r = vpfe->crop; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) +{ + if (a->left < b->left || a->top < b->top) + return 0; + + if (a->left + a->width > b->left + b->width) + return 0; + + if (a->top + a->height > b->top + b->height) + return 0; + + return 1; +} + +static int +vpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s) +{ + struct vpfe_device *vpfe = video_drvdata(file); + struct v4l2_rect cr = vpfe->crop; + struct v4l2_rect r = s->r; + + /* If streaming is started, return error */ + if (vb2_is_busy(&vpfe->buffer_queue)) { + vpfe_err(vpfe, "%s device busy\n", __func__); + return -EBUSY; + } + + if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + s->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + v4l_bound_align_image(&r.width, 0, cr.width, 0, + &r.height, 0, cr.height, 0, 0); + + r.left = clamp_t(unsigned int, r.left, 0, cr.width - r.width); + r.top = clamp_t(unsigned int, r.top, 0, cr.height - r.height); + + if (s->flags & V4L2_SEL_FLAG_LE && !enclosed_rectangle(&r, &s->r)) + return -ERANGE; + + if (s->flags & V4L2_SEL_FLAG_GE && !enclosed_rectangle(&s->r, &r)) + return -ERANGE; + + s->r = vpfe->crop = r; + + vpfe_ccdc_set_image_window(&vpfe->ccdc, &r, vpfe->bpp); + vpfe->fmt.fmt.pix.width = r.width; + vpfe->fmt.fmt.pix.height = r.height; + vpfe->fmt.fmt.pix.bytesperline = vpfe_ccdc_get_line_length(&vpfe->ccdc); + vpfe->fmt.fmt.pix.sizeimage = vpfe->fmt.fmt.pix.bytesperline * + vpfe->fmt.fmt.pix.height; + + vpfe_dbg(1, vpfe, "cropped (%d,%d)/%dx%d of %dx%d\n", + r.left, r.top, r.width, r.height, cr.width, cr.height); + + return 0; +} + +static long vpfe_ioctl_default(struct file *file, void *priv, + bool valid_prio, unsigned int cmd, void *param) +{ + struct vpfe_device *vpfe = video_drvdata(file); + int ret; + + vpfe_dbg(2, vpfe, "vpfe_ioctl_default\n"); + + if (!valid_prio) { + vpfe_err(vpfe, "%s device busy\n", __func__); + return -EBUSY; + } + + /* If streaming is started, return error */ + if (vb2_is_busy(&vpfe->buffer_queue)) { + vpfe_err(vpfe, "%s device busy\n", __func__); + return -EBUSY; + } + + switch (cmd) { + case VIDIOC_AM437X_CCDC_CFG: + ret = vpfe_ccdc_set_params(&vpfe->ccdc, (void __user *)param); + if (ret) { + vpfe_dbg(2, vpfe, + "Error setting parameters in CCDC\n"); + return ret; + } + ret = vpfe_get_ccdc_image_format(vpfe, + &vpfe->fmt); + if (ret < 0) { + vpfe_dbg(2, vpfe, + "Invalid image format at CCDC\n"); + return ret; + } + break; + + default: + ret = -ENOTTY; + break; + } + + return ret; +} + +static const struct vb2_ops vpfe_video_qops = { + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .queue_setup = vpfe_queue_setup, + .buf_prepare = vpfe_buffer_prepare, + .buf_queue = vpfe_buffer_queue, + .start_streaming = vpfe_start_streaming, + .stop_streaming = vpfe_stop_streaming, +}; + +/* vpfe capture driver file operations */ +static const struct v4l2_file_operations vpfe_fops = { + .owner = THIS_MODULE, + .open = vpfe_open, + .release = vpfe_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +/* vpfe capture ioctl operations */ +static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { + .vidioc_querycap = vpfe_querycap, + .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt, + .vidioc_g_fmt_vid_cap = vpfe_g_fmt, + .vidioc_s_fmt_vid_cap = vpfe_s_fmt, + .vidioc_try_fmt_vid_cap = vpfe_try_fmt, + + .vidioc_enum_framesizes = vpfe_enum_size, + + .vidioc_enum_input = vpfe_enum_input, + .vidioc_g_input = vpfe_g_input, + .vidioc_s_input = vpfe_s_input, + + .vidioc_querystd = vpfe_querystd, + .vidioc_s_std = vpfe_s_std, + .vidioc_g_std = vpfe_g_std, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + + .vidioc_cropcap = vpfe_cropcap, + .vidioc_g_selection = vpfe_g_selection, + .vidioc_s_selection = vpfe_s_selection, + + .vidioc_default = vpfe_ioctl_default, +}; + +static int +vpfe_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct vpfe_device *vpfe = container_of(notifier->v4l2_dev, + struct vpfe_device, v4l2_dev); + struct v4l2_subdev_mbus_code_enum mbus_code; + struct vpfe_subdev_info *sdinfo; + bool found = false; + int i, j; + + vpfe_dbg(1, vpfe, "vpfe_async_bound\n"); + + for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) { + sdinfo = &vpfe->cfg->sub_devs[i]; + + if (!strcmp(sdinfo->name, subdev->name)) { + vpfe->sd[i] = subdev; + vpfe_info(vpfe, + "v4l2 sub device %s registered\n", + subdev->name); + vpfe->sd[i]->grp_id = + sdinfo->grp_id; + /* update tvnorms from the sub devices */ + for (j = 0; j < 1; j++) + vpfe->video_dev->tvnorms |= + sdinfo->inputs[j].std; + + found = true; + break; + } + } + + if (!found) { + vpfe_info(vpfe, "sub device (%s) not matched\n", subdev->name); + return -EINVAL; + } + + /* setup the supported formats & indexes */ + for (j = 0, i = 0; ; ++j) { + struct vpfe_fmt *fmt; + int ret; + + memset(&mbus_code, 0, sizeof(mbus_code)); + mbus_code.index = j; + ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, + NULL, &mbus_code); + if (ret) + break; + + fmt = find_format_by_code(mbus_code.code); + if (!fmt) + continue; + + fmt->supported = true; + fmt->index = i++; + } + + return 0; +} + +static int vpfe_probe_complete(struct vpfe_device *vpfe) +{ + struct video_device *vdev; + struct vb2_queue *q; + int err; + + spin_lock_init(&vpfe->dma_queue_lock); + mutex_init(&vpfe->lock); + + vpfe->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + /* set first sub device as current one */ + vpfe->current_subdev = &vpfe->cfg->sub_devs[0]; + vpfe->v4l2_dev.ctrl_handler = vpfe->sd[0]->ctrl_handler; + + err = vpfe_set_input(vpfe, 0); + if (err) + goto probe_out; + + /* Initialize videobuf2 queue as per the buffer type */ + vpfe->alloc_ctx = vb2_dma_contig_init_ctx(vpfe->pdev); + if (IS_ERR(vpfe->alloc_ctx)) { + vpfe_err(vpfe, "Failed to get the context\n"); + err = PTR_ERR(vpfe->alloc_ctx); + goto probe_out; + } + + q = &vpfe->buffer_queue; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + q->drv_priv = vpfe; + q->ops = &vpfe_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct vpfe_cap_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &vpfe->lock; + q->min_buffers_needed = 1; + + err = vb2_queue_init(q); + if (err) { + vpfe_err(vpfe, "vb2_queue_init() failed\n"); + vb2_dma_contig_cleanup_ctx(vpfe->alloc_ctx); + goto probe_out; + } + + INIT_LIST_HEAD(&vpfe->dma_queue); + + vdev = vpfe->video_dev; + strlcpy(vdev->name, VPFE_MODULE_NAME, sizeof(vdev->name)); + vdev->release = video_device_release; + vdev->fops = &vpfe_fops; + vdev->ioctl_ops = &vpfe_ioctl_ops; + vdev->v4l2_dev = &vpfe->v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + vdev->queue = q; + vdev->lock = &vpfe->lock; + video_set_drvdata(vdev, vpfe); + err = video_register_device(vpfe->video_dev, VFL_TYPE_GRABBER, -1); + if (err) { + vpfe_err(vpfe, + "Unable to register video device.\n"); + goto probe_out; + } + + return 0; + +probe_out: + v4l2_device_unregister(&vpfe->v4l2_dev); + return err; +} + +static int vpfe_async_complete(struct v4l2_async_notifier *notifier) +{ + struct vpfe_device *vpfe = container_of(notifier->v4l2_dev, + struct vpfe_device, v4l2_dev); + + return vpfe_probe_complete(vpfe); +} + +static struct vpfe_config * +vpfe_get_pdata(struct platform_device *pdev) +{ + struct device_node *endpoint = NULL, *rem = NULL; + struct v4l2_of_endpoint bus_cfg; + struct vpfe_subdev_info *sdinfo; + struct vpfe_config *pdata; + unsigned int flags; + unsigned int i; + int err; + + dev_dbg(&pdev->dev, "vpfe_get_pdata\n"); + + if (!IS_ENABLED(CONFIG_OF) || !pdev->dev.of_node) + return pdev->dev.platform_data; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + for (i = 0; ; i++) { + endpoint = of_graph_get_next_endpoint(pdev->dev.of_node, + endpoint); + if (!endpoint) + break; + + sdinfo = &pdata->sub_devs[i]; + sdinfo->grp_id = 0; + + /* we only support camera */ + sdinfo->inputs[0].index = i; + strcpy(sdinfo->inputs[0].name, "Camera"); + sdinfo->inputs[0].type = V4L2_INPUT_TYPE_CAMERA; + sdinfo->inputs[0].std = V4L2_STD_ALL; + sdinfo->inputs[0].capabilities = V4L2_IN_CAP_STD; + + sdinfo->can_route = 0; + sdinfo->routes = NULL; + + of_property_read_u32(endpoint, "ti,am437x-vpfe-interface", + &sdinfo->vpfe_param.if_type); + if (sdinfo->vpfe_param.if_type < 0 || + sdinfo->vpfe_param.if_type > 4) { + sdinfo->vpfe_param.if_type = VPFE_RAW_BAYER; + } + + err = v4l2_of_parse_endpoint(endpoint, &bus_cfg); + if (err) { + dev_err(&pdev->dev, "Could not parse the endpoint\n"); + goto done; + } + + sdinfo->vpfe_param.bus_width = bus_cfg.bus.parallel.bus_width; + + if (sdinfo->vpfe_param.bus_width < 8 || + sdinfo->vpfe_param.bus_width > 16) { + dev_err(&pdev->dev, "Invalid bus width.\n"); + goto done; + } + + flags = bus_cfg.bus.parallel.flags; + + if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) + sdinfo->vpfe_param.hdpol = 1; + + if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) + sdinfo->vpfe_param.vdpol = 1; + + rem = of_graph_get_remote_port_parent(endpoint); + if (!rem) { + dev_err(&pdev->dev, "Remote device at %s not found\n", + endpoint->full_name); + goto done; + } + + strncpy(sdinfo->name, rem->name, sizeof(sdinfo->name)); + + pdata->asd[i] = devm_kzalloc(&pdev->dev, + sizeof(struct v4l2_async_subdev), + GFP_KERNEL); + pdata->asd[i]->match_type = V4L2_ASYNC_MATCH_OF; + pdata->asd[i]->match.of.node = rem; + of_node_put(endpoint); + of_node_put(rem); + } + + of_node_put(endpoint); + return pdata; + +done: + of_node_put(endpoint); + of_node_put(rem); + return NULL; +} + +/* + * vpfe_probe : This function creates device entries by register + * itself to the V4L2 driver and initializes fields of each + * device objects + */ +static int vpfe_probe(struct platform_device *pdev) +{ + struct vpfe_config *vpfe_cfg = vpfe_get_pdata(pdev); + struct vpfe_device *vpfe; + struct vpfe_ccdc *ccdc; + struct resource *res; + int ret; + + if (!vpfe_cfg) { + dev_err(&pdev->dev, "No platform data\n"); + return -EINVAL; + } + + vpfe = devm_kzalloc(&pdev->dev, sizeof(*vpfe), GFP_KERNEL); + if (!vpfe) + return -ENOMEM; + + vpfe->pdev = &pdev->dev; + vpfe->cfg = vpfe_cfg; + ccdc = &vpfe->ccdc; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + ccdc->ccdc_cfg.base_addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ccdc->ccdc_cfg.base_addr)) + return PTR_ERR(ccdc->ccdc_cfg.base_addr); + + vpfe->irq = platform_get_irq(pdev, 0); + if (vpfe->irq <= 0) { + dev_err(&pdev->dev, "No IRQ resource\n"); + return -ENODEV; + } + + ret = devm_request_irq(vpfe->pdev, vpfe->irq, vpfe_isr, 0, + "vpfe_capture0", vpfe); + if (ret) { + dev_err(&pdev->dev, "Unable to request interrupt\n"); + return -EINVAL; + } + + vpfe->video_dev = video_device_alloc(); + if (!vpfe->video_dev) { + dev_err(&pdev->dev, "Unable to allocate video device\n"); + return -ENOMEM; + } + + ret = v4l2_device_register(&pdev->dev, &vpfe->v4l2_dev); + if (ret) { + vpfe_err(vpfe, + "Unable to register v4l2 device.\n"); + goto probe_out_video_release; + } + + /* set the driver data in platform device */ + platform_set_drvdata(pdev, vpfe); + /* Enabling module functional clock */ + pm_runtime_enable(&pdev->dev); + + /* for now just enable it here instead of waiting for the open */ + pm_runtime_get_sync(&pdev->dev); + + vpfe_ccdc_config_defaults(ccdc); + + pm_runtime_put_sync(&pdev->dev); + + vpfe->sd = devm_kzalloc(&pdev->dev, sizeof(struct v4l2_subdev *) * + ARRAY_SIZE(vpfe->cfg->asd), GFP_KERNEL); + if (!vpfe->sd) { + ret = -ENOMEM; + goto probe_out_v4l2_unregister; + } + + vpfe->notifier.subdevs = vpfe->cfg->asd; + vpfe->notifier.num_subdevs = ARRAY_SIZE(vpfe->cfg->asd); + vpfe->notifier.bound = vpfe_async_bound; + vpfe->notifier.complete = vpfe_async_complete; + ret = v4l2_async_notifier_register(&vpfe->v4l2_dev, + &vpfe->notifier); + if (ret) { + vpfe_err(vpfe, "Error registering async notifier\n"); + ret = -EINVAL; + goto probe_out_v4l2_unregister; + } + + return 0; + +probe_out_v4l2_unregister: + v4l2_device_unregister(&vpfe->v4l2_dev); +probe_out_video_release: + if (!video_is_registered(vpfe->video_dev)) + video_device_release(vpfe->video_dev); + return ret; +} + +/* + * vpfe_remove : It un-register device from V4L2 driver + */ +static int vpfe_remove(struct platform_device *pdev) +{ + struct vpfe_device *vpfe = platform_get_drvdata(pdev); + + vpfe_dbg(2, vpfe, "vpfe_remove\n"); + + pm_runtime_disable(&pdev->dev); + + v4l2_async_notifier_unregister(&vpfe->notifier); + v4l2_device_unregister(&vpfe->v4l2_dev); + video_unregister_device(vpfe->video_dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP + +static void vpfe_save_context(struct vpfe_ccdc *ccdc) +{ + ccdc->ccdc_ctx[VPFE_PCR >> 2] = vpfe_reg_read(ccdc, VPFE_PCR); + ccdc->ccdc_ctx[VPFE_SYNMODE >> 2] = vpfe_reg_read(ccdc, VPFE_SYNMODE); + ccdc->ccdc_ctx[VPFE_SDOFST >> 2] = vpfe_reg_read(ccdc, VPFE_SDOFST); + ccdc->ccdc_ctx[VPFE_SDR_ADDR >> 2] = vpfe_reg_read(ccdc, VPFE_SDR_ADDR); + ccdc->ccdc_ctx[VPFE_CLAMP >> 2] = vpfe_reg_read(ccdc, VPFE_CLAMP); + ccdc->ccdc_ctx[VPFE_DCSUB >> 2] = vpfe_reg_read(ccdc, VPFE_DCSUB); + ccdc->ccdc_ctx[VPFE_COLPTN >> 2] = vpfe_reg_read(ccdc, VPFE_COLPTN); + ccdc->ccdc_ctx[VPFE_BLKCMP >> 2] = vpfe_reg_read(ccdc, VPFE_BLKCMP); + ccdc->ccdc_ctx[VPFE_VDINT >> 2] = vpfe_reg_read(ccdc, VPFE_VDINT); + ccdc->ccdc_ctx[VPFE_ALAW >> 2] = vpfe_reg_read(ccdc, VPFE_ALAW); + ccdc->ccdc_ctx[VPFE_REC656IF >> 2] = vpfe_reg_read(ccdc, VPFE_REC656IF); + ccdc->ccdc_ctx[VPFE_CCDCFG >> 2] = vpfe_reg_read(ccdc, VPFE_CCDCFG); + ccdc->ccdc_ctx[VPFE_CULLING >> 2] = vpfe_reg_read(ccdc, VPFE_CULLING); + ccdc->ccdc_ctx[VPFE_HD_VD_WID >> 2] = vpfe_reg_read(ccdc, + VPFE_HD_VD_WID); + ccdc->ccdc_ctx[VPFE_PIX_LINES >> 2] = vpfe_reg_read(ccdc, + VPFE_PIX_LINES); + ccdc->ccdc_ctx[VPFE_HORZ_INFO >> 2] = vpfe_reg_read(ccdc, + VPFE_HORZ_INFO); + ccdc->ccdc_ctx[VPFE_VERT_START >> 2] = vpfe_reg_read(ccdc, + VPFE_VERT_START); + ccdc->ccdc_ctx[VPFE_VERT_LINES >> 2] = vpfe_reg_read(ccdc, + VPFE_VERT_LINES); + ccdc->ccdc_ctx[VPFE_HSIZE_OFF >> 2] = vpfe_reg_read(ccdc, + VPFE_HSIZE_OFF); +} + +static int vpfe_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct vpfe_device *vpfe = platform_get_drvdata(pdev); + struct vpfe_ccdc *ccdc = &vpfe->ccdc; + + /* if streaming has not started we don't care */ + if (!vb2_start_streaming_called(&vpfe->buffer_queue)) + return 0; + + pm_runtime_get_sync(dev); + vpfe_config_enable(ccdc, 1); + + /* Save VPFE context */ + vpfe_save_context(ccdc); + + /* Disable CCDC */ + vpfe_pcr_enable(ccdc, 0); + vpfe_config_enable(ccdc, 0); + + /* Disable both master and slave clock */ + pm_runtime_put_sync(dev); + + /* Select sleep pin state */ + pinctrl_pm_select_sleep_state(dev); + + return 0; +} + +static void vpfe_restore_context(struct vpfe_ccdc *ccdc) +{ + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_SYNMODE >> 2], VPFE_SYNMODE); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_CULLING >> 2], VPFE_CULLING); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_SDOFST >> 2], VPFE_SDOFST); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_SDR_ADDR >> 2], VPFE_SDR_ADDR); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_CLAMP >> 2], VPFE_CLAMP); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_DCSUB >> 2], VPFE_DCSUB); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_COLPTN >> 2], VPFE_COLPTN); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_BLKCMP >> 2], VPFE_BLKCMP); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_VDINT >> 2], VPFE_VDINT); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_ALAW >> 2], VPFE_ALAW); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_REC656IF >> 2], VPFE_REC656IF); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_CCDCFG >> 2], VPFE_CCDCFG); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_PCR >> 2], VPFE_PCR); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_HD_VD_WID >> 2], + VPFE_HD_VD_WID); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_PIX_LINES >> 2], + VPFE_PIX_LINES); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_HORZ_INFO >> 2], + VPFE_HORZ_INFO); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_VERT_START >> 2], + VPFE_VERT_START); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_VERT_LINES >> 2], + VPFE_VERT_LINES); + vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_HSIZE_OFF >> 2], + VPFE_HSIZE_OFF); +} + +static int vpfe_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct vpfe_device *vpfe = platform_get_drvdata(pdev); + struct vpfe_ccdc *ccdc = &vpfe->ccdc; + + /* if streaming has not started we don't care */ + if (!vb2_start_streaming_called(&vpfe->buffer_queue)) + return 0; + + /* Enable both master and slave clock */ + pm_runtime_get_sync(dev); + vpfe_config_enable(ccdc, 1); + + /* Restore VPFE context */ + vpfe_restore_context(ccdc); + + vpfe_config_enable(ccdc, 0); + pm_runtime_put_sync(dev); + + /* Select default pin state */ + pinctrl_pm_select_default_state(dev); + + return 0; +} + +#endif + +static SIMPLE_DEV_PM_OPS(vpfe_pm_ops, vpfe_suspend, vpfe_resume); + +static const struct of_device_id vpfe_of_match[] = { + { .compatible = "ti,am437x-vpfe", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, vpfe_of_match); + +static struct platform_driver vpfe_driver = { + .probe = vpfe_probe, + .remove = vpfe_remove, + .driver = { + .name = VPFE_MODULE_NAME, + .pm = &vpfe_pm_ops, + .of_match_table = of_match_ptr(vpfe_of_match), + }, +}; + +module_platform_driver(vpfe_driver); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("TI AM437x VPFE driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(VPFE_VERSION); diff --git a/drivers/media/platform/am437x/am437x-vpfe.h b/drivers/media/platform/am437x/am437x-vpfe.h new file mode 100644 index 000000000000..0f557352313d --- /dev/null +++ b/drivers/media/platform/am437x/am437x-vpfe.h @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2013 - 2014 Texas Instruments, Inc. + * + * Benoit Parrot <bparrot@ti.com> + * Lad, Prabhakar <prabhakar.csengg@gmail.com> + * + * This program is free software; you may redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef AM437X_VPFE_H +#define AM437X_VPFE_H + +#include <linux/am437x-vpfe.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> + +#include <media/v4l2-dev.h> +#include <media/v4l2-device.h> +#include <media/v4l2-ioctl.h> +#include <media/videobuf2-dma-contig.h> + +#include "am437x-vpfe_regs.h" + +enum vpfe_pin_pol { + VPFE_PINPOL_POSITIVE = 0, + VPFE_PINPOL_NEGATIVE, +}; + +enum vpfe_hw_if_type { + /* Raw Bayer */ + VPFE_RAW_BAYER = 0, + /* BT656 - 8 bit */ + VPFE_BT656, + /* BT656 - 10 bit */ + VPFE_BT656_10BIT, + /* YCbCr - 8 bit with external sync */ + VPFE_YCBCR_SYNC_8, + /* YCbCr - 16 bit with external sync */ + VPFE_YCBCR_SYNC_16, +}; + +/* interface description */ +struct vpfe_hw_if_param { + enum vpfe_hw_if_type if_type; + enum vpfe_pin_pol hdpol; + enum vpfe_pin_pol vdpol; + unsigned int bus_width; +}; + +#define VPFE_MAX_SUBDEV 1 +#define VPFE_MAX_INPUTS 1 + +struct vpfe_pixel_format { + struct v4l2_fmtdesc fmtdesc; + /* bytes per pixel */ + int bpp; +}; + +struct vpfe_std_info { + int active_pixels; + int active_lines; + /* current frame format */ + int frame_format; +}; + +struct vpfe_route { + u32 input; + u32 output; +}; + +struct vpfe_subdev_info { + char name[32]; + /* Sub device group id */ + int grp_id; + /* inputs available at the sub device */ + struct v4l2_input inputs[VPFE_MAX_INPUTS]; + /* Sub dev routing information for each input */ + struct vpfe_route *routes; + /* check if sub dev supports routing */ + int can_route; + /* ccdc bus/interface configuration */ + struct vpfe_hw_if_param vpfe_param; + struct v4l2_subdev *sd; +}; + +struct vpfe_config { + /* information about each subdev */ + struct vpfe_subdev_info sub_devs[VPFE_MAX_SUBDEV]; + /* Flat array, arranged in groups */ + struct v4l2_async_subdev *asd[VPFE_MAX_SUBDEV]; +}; + +struct vpfe_cap_buffer { + struct vb2_buffer vb; + struct list_head list; +}; + +enum ccdc_pixfmt { + CCDC_PIXFMT_RAW = 0, + CCDC_PIXFMT_YCBCR_16BIT, + CCDC_PIXFMT_YCBCR_8BIT, +}; + +enum ccdc_frmfmt { + CCDC_FRMFMT_PROGRESSIVE = 0, + CCDC_FRMFMT_INTERLACED, +}; + +/* PIXEL ORDER IN MEMORY from LSB to MSB */ +/* only applicable for 8-bit input mode */ +enum ccdc_pixorder { + CCDC_PIXORDER_YCBYCR, + CCDC_PIXORDER_CBYCRY, +}; + +enum ccdc_buftype { + CCDC_BUFTYPE_FLD_INTERLEAVED, + CCDC_BUFTYPE_FLD_SEPARATED +}; + + +/* returns the highest bit used for the gamma */ +static inline u8 ccdc_gamma_width_max_bit(enum vpfe_ccdc_gamma_width width) +{ + return 15 - width; +} + +/* returns the highest bit used for this data size */ +static inline u8 ccdc_data_size_max_bit(enum vpfe_ccdc_data_size sz) +{ + return sz == VPFE_CCDC_DATA_8BITS ? 7 : 15 - sz; +} + +/* Structure for CCDC configuration parameters for raw capture mode */ +struct ccdc_params_raw { + /* pixel format */ + enum ccdc_pixfmt pix_fmt; + /* progressive or interlaced frame */ + enum ccdc_frmfmt frm_fmt; + struct v4l2_rect win; + /* Current Format Bytes Per Pixels */ + unsigned int bytesperpixel; + /* Current Format Bytes per Lines + * (Aligned to 32 bytes) used for HORZ_INFO + */ + unsigned int bytesperline; + /* field id polarity */ + enum vpfe_pin_pol fid_pol; + /* vertical sync polarity */ + enum vpfe_pin_pol vd_pol; + /* horizontal sync polarity */ + enum vpfe_pin_pol hd_pol; + /* interleaved or separated fields */ + enum ccdc_buftype buf_type; + /* + * enable to store the image in inverse + * order in memory(bottom to top) + */ + unsigned char image_invert_enable; + /* configurable parameters */ + struct vpfe_ccdc_config_params_raw config_params; +}; + +struct ccdc_params_ycbcr { + /* pixel format */ + enum ccdc_pixfmt pix_fmt; + /* progressive or interlaced frame */ + enum ccdc_frmfmt frm_fmt; + struct v4l2_rect win; + /* Current Format Bytes Per Pixels */ + unsigned int bytesperpixel; + /* Current Format Bytes per Lines + * (Aligned to 32 bytes) used for HORZ_INFO + */ + unsigned int bytesperline; + /* field id polarity */ + enum vpfe_pin_pol fid_pol; + /* vertical sync polarity */ + enum vpfe_pin_pol vd_pol; + /* horizontal sync polarity */ + enum vpfe_pin_pol hd_pol; + /* enable BT.656 embedded sync mode */ + int bt656_enable; + /* cb:y:cr:y or y:cb:y:cr in memory */ + enum ccdc_pixorder pix_order; + /* interleaved or separated fields */ + enum ccdc_buftype buf_type; +}; + +/* + * CCDC operational configuration + */ +struct ccdc_config { + /* CCDC interface type */ + enum vpfe_hw_if_type if_type; + /* Raw Bayer configuration */ + struct ccdc_params_raw bayer; + /* YCbCr configuration */ + struct ccdc_params_ycbcr ycbcr; + /* ccdc base address */ + void __iomem *base_addr; +}; + +struct vpfe_ccdc { + struct ccdc_config ccdc_cfg; + u32 ccdc_ctx[VPFE_REG_END / sizeof(u32)]; +}; + +struct vpfe_device { + /* V4l2 specific parameters */ + /* Identifies video device for this channel */ + struct video_device *video_dev; + /* sub devices */ + struct v4l2_subdev **sd; + /* vpfe cfg */ + struct vpfe_config *cfg; + /* V4l2 device */ + struct v4l2_device v4l2_dev; + /* parent device */ + struct device *pdev; + /* subdevice async Notifier */ + struct v4l2_async_notifier notifier; + /* Indicates id of the field which is being displayed */ + unsigned field; + unsigned sequence; + /* current interface type */ + struct vpfe_hw_if_param vpfe_if_params; + /* ptr to currently selected sub device */ + struct vpfe_subdev_info *current_subdev; + /* current input at the sub device */ + int current_input; + /* Keeps track of the information about the standard */ + struct vpfe_std_info std_info; + /* std index into std table */ + int std_index; + /* IRQs used when CCDC output to SDRAM */ + unsigned int irq; + /* Pointer pointing to current v4l2_buffer */ + struct vpfe_cap_buffer *cur_frm; + /* Pointer pointing to next v4l2_buffer */ + struct vpfe_cap_buffer *next_frm; + /* Used to store pixel format */ + struct v4l2_format fmt; + /* Used to store current bytes per pixel based on current format */ + unsigned int bpp; + /* + * used when IMP is chained to store the crop window which + * is different from the image window + */ + struct v4l2_rect crop; + /* Buffer queue used in video-buf */ + struct vb2_queue buffer_queue; + /* Allocator-specific contexts for each plane */ + struct vb2_alloc_ctx *alloc_ctx; + /* Queue of filled frames */ + struct list_head dma_queue; + /* IRQ lock for DMA queue */ + spinlock_t dma_queue_lock; + /* lock used to access this structure */ + struct mutex lock; + /* + * offset where second field starts from the starting of the + * buffer for field separated YCbCr formats + */ + u32 field_off; + struct vpfe_ccdc ccdc; +}; + +#endif /* AM437X_VPFE_H */ diff --git a/drivers/media/platform/am437x/am437x-vpfe_regs.h b/drivers/media/platform/am437x/am437x-vpfe_regs.h new file mode 100644 index 000000000000..4a0ed29723e8 --- /dev/null +++ b/drivers/media/platform/am437x/am437x-vpfe_regs.h @@ -0,0 +1,140 @@ +/* + * TI AM437x Image Sensor Interface Registers + * + * Copyright (C) 2013 - 2014 Texas Instruments, Inc. + * + * Benoit Parrot <bparrot@ti.com> + * Lad, Prabhakar <prabhakar.csengg@gmail.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. + * + * 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 AM437X_VPFE_REGS_H +#define AM437X_VPFE_REGS_H + +/* VPFE module register offset */ +#define VPFE_REVISION 0x0 +#define VPFE_PCR 0x4 +#define VPFE_SYNMODE 0x8 +#define VPFE_HD_VD_WID 0xc +#define VPFE_PIX_LINES 0x10 +#define VPFE_HORZ_INFO 0x14 +#define VPFE_VERT_START 0x18 +#define VPFE_VERT_LINES 0x1c +#define VPFE_CULLING 0x20 +#define VPFE_HSIZE_OFF 0x24 +#define VPFE_SDOFST 0x28 +#define VPFE_SDR_ADDR 0x2c +#define VPFE_CLAMP 0x30 +#define VPFE_DCSUB 0x34 +#define VPFE_COLPTN 0x38 +#define VPFE_BLKCMP 0x3c +#define VPFE_VDINT 0x48 +#define VPFE_ALAW 0x4c +#define VPFE_REC656IF 0x50 +#define VPFE_CCDCFG 0x54 +#define VPFE_DMA_CNTL 0x98 +#define VPFE_SYSCONFIG 0x104 +#define VPFE_CONFIG 0x108 +#define VPFE_IRQ_EOI 0x110 +#define VPFE_IRQ_STS_RAW 0x114 +#define VPFE_IRQ_STS 0x118 +#define VPFE_IRQ_EN_SET 0x11c +#define VPFE_IRQ_EN_CLR 0x120 +#define VPFE_REG_END 0x124 + +/* Define bit fields within selected registers */ +#define VPFE_FID_POL_MASK 1 +#define VPFE_FID_POL_SHIFT 4 +#define VPFE_HD_POL_MASK 1 +#define VPFE_HD_POL_SHIFT 3 +#define VPFE_VD_POL_MASK 1 +#define VPFE_VD_POL_SHIFT 2 +#define VPFE_HSIZE_OFF_MASK 0xffffffe0 +#define VPFE_32BYTE_ALIGN_VAL 31 +#define VPFE_FRM_FMT_MASK 0x1 +#define VPFE_FRM_FMT_SHIFT 7 +#define VPFE_DATA_SZ_MASK 7 +#define VPFE_DATA_SZ_SHIFT 8 +#define VPFE_PIX_FMT_MASK 3 +#define VPFE_PIX_FMT_SHIFT 12 +#define VPFE_VP2SDR_DISABLE 0xfffbffff +#define VPFE_WEN_ENABLE (1 << 17) +#define VPFE_SDR2RSZ_DISABLE 0xfff7ffff +#define VPFE_VDHDEN_ENABLE (1 << 16) +#define VPFE_LPF_ENABLE (1 << 14) +#define VPFE_ALAW_ENABLE (1 << 3) +#define VPFE_ALAW_GAMMA_WD_MASK 7 +#define VPFE_BLK_CLAMP_ENABLE (1 << 31) +#define VPFE_BLK_SGAIN_MASK 0x1f +#define VPFE_BLK_ST_PXL_MASK 0x7fff +#define VPFE_BLK_ST_PXL_SHIFT 10 +#define VPFE_BLK_SAMPLE_LN_MASK 7 +#define VPFE_BLK_SAMPLE_LN_SHIFT 28 +#define VPFE_BLK_SAMPLE_LINE_MASK 7 +#define VPFE_BLK_SAMPLE_LINE_SHIFT 25 +#define VPFE_BLK_DC_SUB_MASK 0x03fff +#define VPFE_BLK_COMP_MASK 0xff +#define VPFE_BLK_COMP_GB_COMP_SHIFT 8 +#define VPFE_BLK_COMP_GR_COMP_SHIFT 16 +#define VPFE_BLK_COMP_R_COMP_SHIFT 24 +#define VPFE_LATCH_ON_VSYNC_DISABLE (1 << 15) +#define VPFE_DATA_PACK_ENABLE (1 << 11) +#define VPFE_HORZ_INFO_SPH_SHIFT 16 +#define VPFE_VERT_START_SLV0_SHIFT 16 +#define VPFE_VDINT_VDINT0_SHIFT 16 +#define VPFE_VDINT_VDINT1_MASK 0xffff +#define VPFE_PPC_RAW 1 +#define VPFE_DCSUB_DEFAULT_VAL 0 +#define VPFE_CLAMP_DEFAULT_VAL 0 +#define VPFE_COLPTN_VAL 0xbb11bb11 +#define VPFE_TWO_BYTES_PER_PIXEL 2 +#define VPFE_INTERLACED_IMAGE_INVERT 0x4b6d +#define VPFE_INTERLACED_NO_IMAGE_INVERT 0x0249 +#define VPFE_PROGRESSIVE_IMAGE_INVERT 0x4000 +#define VPFE_PROGRESSIVE_NO_IMAGE_INVERT 0 +#define VPFE_INTERLACED_HEIGHT_SHIFT 1 +#define VPFE_SYN_MODE_INPMOD_SHIFT 12 +#define VPFE_SYN_MODE_INPMOD_MASK 3 +#define VPFE_SYN_MODE_8BITS (7 << 8) +#define VPFE_SYN_MODE_10BITS (6 << 8) +#define VPFE_SYN_MODE_11BITS (5 << 8) +#define VPFE_SYN_MODE_12BITS (4 << 8) +#define VPFE_SYN_MODE_13BITS (3 << 8) +#define VPFE_SYN_MODE_14BITS (2 << 8) +#define VPFE_SYN_MODE_15BITS (1 << 8) +#define VPFE_SYN_MODE_16BITS (0 << 8) +#define VPFE_SYN_FLDMODE_MASK 1 +#define VPFE_SYN_FLDMODE_SHIFT 7 +#define VPFE_REC656IF_BT656_EN 3 +#define VPFE_SYN_MODE_VD_POL_NEGATIVE (1 << 2) +#define VPFE_CCDCFG_Y8POS_SHIFT 11 +#define VPFE_CCDCFG_BW656_10BIT (1 << 5) +#define VPFE_SDOFST_FIELD_INTERLEAVED 0x249 +#define VPFE_NO_CULLING 0xffff00ff +#define VPFE_VDINT0 (1 << 0) +#define VPFE_VDINT1 (1 << 1) +#define VPFE_VDINT2 (1 << 2) +#define VPFE_DMA_CNTL_OVERFLOW (1 << 31) + +#define VPFE_CONFIG_PCLK_INV_SHIFT 0 +#define VPFE_CONFIG_PCLK_INV_MASK 1 +#define VPFE_CONFIG_PCLK_INV_NOT_INV 0 +#define VPFE_CONFIG_PCLK_INV_INV 1 +#define VPFE_CONFIG_EN_SHIFT 1 +#define VPFE_CONFIG_EN_MASK 2 +#define VPFE_CONFIG_EN_DISABLE 0 +#define VPFE_CONFIG_EN_ENABLE 1 +#define VPFE_CONFIG_ST_SHIFT 2 +#define VPFE_CONFIG_ST_MASK 4 +#define VPFE_CONFIG_ST_OCP_ACTIVE 0 +#define VPFE_CONFIG_ST_OCP_STANDBY 1 + +#endif /* AM437X_VPFE_REGS_H */ diff --git a/drivers/media/platform/coda/coda-bit.c b/drivers/media/platform/coda/coda-bit.c index b4029ae293d3..856b542b35b9 100644 --- a/drivers/media/platform/coda/coda-bit.c +++ b/drivers/media/platform/coda/coda-bit.c @@ -718,6 +718,7 @@ static int coda_start_encoding(struct coda_ctx *ctx) struct vb2_buffer *buf; int gamma, ret, value; u32 dst_fourcc; + int num_fb; u32 stride; q_data_src = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT); @@ -983,12 +984,14 @@ static int coda_start_encoding(struct coda_ctx *ctx) v4l2_err(v4l2_dev, "failed to allocate framebuffers\n"); goto out; } + num_fb = 2; stride = q_data_src->bytesperline; } else { ctx->num_internal_frames = 0; + num_fb = 0; stride = 0; } - coda_write(dev, ctx->num_internal_frames, CODA_CMD_SET_FRAME_BUF_NUM); + coda_write(dev, num_fb, CODA_CMD_SET_FRAME_BUF_NUM); coda_write(dev, stride, CODA_CMD_SET_FRAME_BUF_STRIDE); if (dev->devtype->product == CODA_7541) { @@ -1316,8 +1319,10 @@ static void coda_seq_end_work(struct work_struct *work) static void coda_bit_release(struct coda_ctx *ctx) { + mutex_lock(&ctx->buffer_mutex); coda_free_framebuffers(ctx); coda_free_context_buffers(ctx); + mutex_unlock(&ctx->buffer_mutex); } const struct coda_context_ops coda_bit_encode_ops = { @@ -1431,9 +1436,10 @@ static int __coda_start_decoding(struct coda_ctx *ctx) height = val & CODA7_PICHEIGHT_MASK; } - if (width > q_data_dst->width || height > q_data_dst->height) { + if (width > q_data_dst->bytesperline || height > q_data_dst->height) { v4l2_err(&dev->v4l2_dev, "stream is %dx%d, not %dx%d\n", - width, height, q_data_dst->width, q_data_dst->height); + width, height, q_data_dst->bytesperline, + q_data_dst->height); return -EINVAL; } @@ -1565,6 +1571,7 @@ static int coda_prepare_decode(struct coda_ctx *ctx) struct vb2_buffer *dst_buf; struct coda_dev *dev = ctx->dev; struct coda_q_data *q_data_dst; + struct coda_buffer_meta *meta; u32 reg_addr, reg_stride; dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); @@ -1643,12 +1650,12 @@ static int coda_prepare_decode(struct coda_ctx *ctx) coda_write(dev, ctx->iram_info.axi_sram_use, CODA7_REG_BIT_AXI_SRAM_USE); - if (ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG) { - struct coda_buffer_meta *meta; + meta = list_first_entry_or_null(&ctx->buffer_meta_list, + struct coda_buffer_meta, list); + + if (meta && ctx->codec->src_fourcc == V4L2_PIX_FMT_JPEG) { /* If this is the last buffer in the bitstream, add padding */ - meta = list_first_entry(&ctx->buffer_meta_list, - struct coda_buffer_meta, list); if (meta->end == (ctx->bitstream_fifo.kfifo.in & ctx->bitstream_fifo.kfifo.mask)) { static unsigned char buf[512]; @@ -1665,6 +1672,9 @@ static int coda_prepare_decode(struct coda_ctx *ctx) coda_kfifo_sync_to_device_full(ctx); + /* Clear decode success flag */ + coda_write(dev, 0, CODA_RET_DEC_PIC_SUCCESS); + coda_command_async(ctx, CODA_COMMAND_PIC_RUN); return 0; @@ -1821,6 +1831,7 @@ static void coda_finish_decode(struct coda_ctx *ctx) memset(&ctx->frame_metas[decoded_idx], 0, sizeof(struct coda_buffer_meta)); ctx->frame_metas[decoded_idx].sequence = val; + ctx->sequence_offset++; } mutex_unlock(&ctx->bitstream_mutex); diff --git a/drivers/media/platform/coda/coda-common.c b/drivers/media/platform/coda/coda-common.c index 39330a70f752..6f32e6d6b156 100644 --- a/drivers/media/platform/coda/coda-common.c +++ b/drivers/media/platform/coda/coda-common.c @@ -37,6 +37,7 @@ #include <media/v4l2-mem2mem.h> #include <media/videobuf2-core.h> #include <media/videobuf2-dma-contig.h> +#include <media/videobuf2-vmalloc.h> #include "coda.h" @@ -180,6 +181,7 @@ struct coda_video_device { const char *name; enum coda_inst_type type; const struct coda_context_ops *ops; + bool direct; u32 src_formats[CODA_MAX_FORMATS]; u32 dst_formats[CODA_MAX_FORMATS]; }; @@ -468,6 +470,18 @@ static int coda_try_pixelformat(struct coda_ctx *ctx, struct v4l2_format *f) return 0; } +static unsigned int coda_estimate_sizeimage(struct coda_ctx *ctx, u32 sizeimage, + u32 width, u32 height) +{ + /* + * This is a rough estimate for sensible compressed buffer + * sizes (between 1 and 16 bits per pixel). This could be + * improved by better format specific worst case estimates. + */ + return round_up(clamp(sizeimage, width * height / 8, + width * height * 2), PAGE_SIZE); +} + static int coda_try_fmt(struct coda_ctx *ctx, const struct coda_codec *codec, struct v4l2_format *f) { @@ -513,15 +527,10 @@ static int coda_try_fmt(struct coda_ctx *ctx, const struct coda_codec *codec, case V4L2_PIX_FMT_H264: case V4L2_PIX_FMT_MPEG4: f->fmt.pix.bytesperline = 0; - /* - * This is a rough estimate for sensible compressed buffer - * sizes (between 1 and 16 bits per pixel). This could be - * improved by better format specific worst case estimates. - */ - f->fmt.pix.sizeimage = round_up(clamp(f->fmt.pix.sizeimage, - f->fmt.pix.width * f->fmt.pix.height / 8, - f->fmt.pix.width * f->fmt.pix.height * 2), - PAGE_SIZE); + f->fmt.pix.sizeimage = coda_estimate_sizeimage(ctx, + f->fmt.pix.sizeimage, + f->fmt.pix.width, + f->fmt.pix.height); break; default: BUG(); @@ -592,7 +601,11 @@ static int coda_try_fmt_vid_out(struct file *file, void *priv, if (ret < 0) return ret; - if (!f->fmt.pix.colorspace) { + switch (f->fmt.pix.colorspace) { + case V4L2_COLORSPACE_REC709: + case V4L2_COLORSPACE_JPEG: + break; + default: if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_JPEG) f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; else @@ -670,6 +683,7 @@ static int coda_s_fmt_vid_out(struct file *file, void *priv, ctx->colorspace = f->fmt.pix.colorspace; + memset(&f_cap, 0, sizeof(f_cap)); f_cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; coda_g_fmt(file, priv, &f_cap); f_cap.fmt.pix.width = f->fmt.pix.width; @@ -908,7 +922,8 @@ static void coda_pic_run_work(struct work_struct *work) ctx->ops->finish_run(ctx); } - if (ctx->aborting || (!ctx->streamon_cap && !ctx->streamon_out)) + if ((ctx->aborting || (!ctx->streamon_cap && !ctx->streamon_out)) && + ctx->ops->seq_end_work) queue_work(dev->workqueue, &ctx->seq_end_work); mutex_unlock(&dev->coda_mutex); @@ -939,15 +954,43 @@ static int coda_job_ready(void *m2m_priv) return 0; } - if (ctx->hold || - ((ctx->inst_type == CODA_INST_DECODER) && - !v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) && - (coda_get_bitstream_payload(ctx) < 512) && - !(ctx->bit_stream_param & CODA_BIT_STREAM_END_FLAG))) { - v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, - "%d: not ready: not enough bitstream data.\n", - ctx->idx); - return 0; + if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) { + struct list_head *meta; + bool stream_end; + int num_metas; + int src_bufs; + + if (ctx->hold && !v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx)) { + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "%d: not ready: on hold for more buffers.\n", + ctx->idx); + return 0; + } + + stream_end = ctx->bit_stream_param & + CODA_BIT_STREAM_END_FLAG; + + num_metas = 0; + list_for_each(meta, &ctx->buffer_meta_list) + num_metas++; + + src_bufs = v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx); + + if (!stream_end && (num_metas + src_bufs) < 2) { + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "%d: not ready: need 2 buffers available (%d, %d)\n", + ctx->idx, num_metas, src_bufs); + return 0; + } + + + if (!v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) && + !stream_end && (coda_get_bitstream_payload(ctx) < 512)) { + v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, + "%d: not ready: not enough bitstream data (%d).\n", + ctx->idx, coda_get_bitstream_payload(ctx)); + return 0; + } } if (ctx->aborting) { @@ -1023,13 +1066,14 @@ static void coda_set_tiled_map_type(struct coda_ctx *ctx, int tiled_map_type) static void set_default_params(struct coda_ctx *ctx) { - unsigned int max_w, max_h, size; + unsigned int max_w, max_h, usize, csize; ctx->codec = coda_find_codec(ctx->dev, ctx->cvd->src_formats[0], ctx->cvd->dst_formats[0]); max_w = min(ctx->codec->max_w, 1920U); max_h = min(ctx->codec->max_h, 1088U); - size = max_w * max_h * 3 / 2; + usize = max_w * max_h * 3 / 2; + csize = coda_estimate_sizeimage(ctx, usize, max_w, max_h); ctx->params.codec_mode = ctx->codec->mode; ctx->colorspace = V4L2_COLORSPACE_REC709; @@ -1044,14 +1088,14 @@ static void set_default_params(struct coda_ctx *ctx) ctx->q_data[V4L2_M2M_DST].height = max_h; if (ctx->codec->src_fourcc == V4L2_PIX_FMT_YUV420) { ctx->q_data[V4L2_M2M_SRC].bytesperline = max_w; - ctx->q_data[V4L2_M2M_SRC].sizeimage = size; + ctx->q_data[V4L2_M2M_SRC].sizeimage = usize; ctx->q_data[V4L2_M2M_DST].bytesperline = 0; - ctx->q_data[V4L2_M2M_DST].sizeimage = round_up(size, PAGE_SIZE); + ctx->q_data[V4L2_M2M_DST].sizeimage = csize; } else { ctx->q_data[V4L2_M2M_SRC].bytesperline = 0; - ctx->q_data[V4L2_M2M_SRC].sizeimage = round_up(size, PAGE_SIZE); + ctx->q_data[V4L2_M2M_SRC].sizeimage = csize; ctx->q_data[V4L2_M2M_DST].bytesperline = max_w; - ctx->q_data[V4L2_M2M_DST].sizeimage = size; + ctx->q_data[V4L2_M2M_DST].sizeimage = usize; } ctx->q_data[V4L2_M2M_SRC].rect.width = max_w; ctx->q_data[V4L2_M2M_SRC].rect.height = max_h; @@ -1080,6 +1124,7 @@ static int coda_queue_setup(struct vb2_queue *vq, *nplanes = 1; sizes[0] = size; + /* Set to vb2-dma-contig allocator context, ignored by vb2-vmalloc */ alloc_ctxs[0] = ctx->dev->alloc_ctx; v4l2_dbg(1, coda_debug, &ctx->dev->v4l2_dev, @@ -1109,6 +1154,7 @@ static int coda_buf_prepare(struct vb2_buffer *vb) static void coda_buf_queue(struct vb2_buffer *vb) { struct coda_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct vb2_queue *vq = vb->vb2_queue; struct coda_q_data *q_data; q_data = get_q_data(ctx, vb->vb2_queue->type); @@ -1117,8 +1163,7 @@ static void coda_buf_queue(struct vb2_buffer *vb) * In the decoder case, immediately try to copy the buffer into the * bitstream ringbuffer and mark it as ready to be dequeued. */ - if (ctx->inst_type == CODA_INST_DECODER && - vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + if (ctx->bitstream.size && vq->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { /* * For backwards compatibility, queuing an empty buffer marks * the stream end @@ -1218,7 +1263,7 @@ static int coda_start_streaming(struct vb2_queue *q, unsigned int count) return 0; /* Allow BIT decoder device_run with no new buffers queued */ - if (ctx->inst_type == CODA_INST_DECODER) + if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) v4l2_m2m_set_src_buffered(ctx->fh.m2m_ctx, true); ctx->gopcounter = ctx->params.gop_size - 1; @@ -1271,7 +1316,7 @@ static void coda_stop_streaming(struct vb2_queue *q) coda_bit_stream_end_flag(ctx); - ctx->isequence = 0; + ctx->qsequence = 0; while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); @@ -1290,6 +1335,10 @@ static void coda_stop_streaming(struct vb2_queue *q) if (!ctx->streamon_out && !ctx->streamon_cap) { struct coda_buffer_meta *meta; + if (ctx->ops->seq_end_work) { + queue_work(dev->workqueue, &ctx->seq_end_work); + flush_work(&ctx->seq_end_work); + } mutex_lock(&ctx->bitstream_mutex); while (!list_empty(&ctx->buffer_meta_list)) { meta = list_first_entry(&ctx->buffer_meta_list, @@ -1300,6 +1349,7 @@ static void coda_stop_streaming(struct vb2_queue *q) mutex_unlock(&ctx->bitstream_mutex); kfifo_init(&ctx->bitstream_fifo, ctx->bitstream.vaddr, ctx->bitstream.size); + ctx->initialized = 0; ctx->runcounter = 0; ctx->aborting = 0; } @@ -1521,8 +1571,8 @@ int coda_decoder_queue_init(void *priv, struct vb2_queue *src_vq, int ret; src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - src_vq->io_modes = VB2_DMABUF | VB2_MMAP; - src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->io_modes = VB2_DMABUF | VB2_MMAP | VB2_USERPTR; + src_vq->mem_ops = &vb2_vmalloc_memops; ret = coda_queue_init(priv, src_vq); if (ret) @@ -1577,9 +1627,11 @@ static int coda_open(struct file *file) ctx->cvd = to_coda_video_device(vdev); ctx->inst_type = ctx->cvd->type; ctx->ops = ctx->cvd->ops; + ctx->use_bit = !ctx->cvd->direct; init_completion(&ctx->completion); INIT_WORK(&ctx->pic_run_work, coda_pic_run_work); - INIT_WORK(&ctx->seq_end_work, ctx->ops->seq_end_work); + if (ctx->ops->seq_end_work) + INIT_WORK(&ctx->seq_end_work, ctx->ops->seq_end_work); v4l2_fh_init(&ctx->fh, video_devdata(file)); file->private_data = &ctx->fh; v4l2_fh_add(&ctx->fh); @@ -1630,22 +1682,25 @@ static int coda_open(struct file *file) ctx->fh.ctrl_handler = &ctx->ctrls; - ret = coda_alloc_context_buf(ctx, &ctx->parabuf, - CODA_PARA_BUF_SIZE, "parabuf"); - if (ret < 0) { - v4l2_err(&dev->v4l2_dev, "failed to allocate parabuf"); - goto err_dma_alloc; + if (ctx->use_bit) { + ret = coda_alloc_context_buf(ctx, &ctx->parabuf, + CODA_PARA_BUF_SIZE, "parabuf"); + if (ret < 0) { + v4l2_err(&dev->v4l2_dev, "failed to allocate parabuf"); + goto err_dma_alloc; + } } - - ctx->bitstream.size = CODA_MAX_FRAME_SIZE; - ctx->bitstream.vaddr = dma_alloc_writecombine( - &dev->plat_dev->dev, ctx->bitstream.size, - &ctx->bitstream.paddr, GFP_KERNEL); - if (!ctx->bitstream.vaddr) { - v4l2_err(&dev->v4l2_dev, - "failed to allocate bitstream ringbuffer"); - ret = -ENOMEM; - goto err_dma_writecombine; + if (ctx->use_bit && ctx->inst_type == CODA_INST_DECODER) { + ctx->bitstream.size = CODA_MAX_FRAME_SIZE; + ctx->bitstream.vaddr = dma_alloc_writecombine( + &dev->plat_dev->dev, ctx->bitstream.size, + &ctx->bitstream.paddr, GFP_KERNEL); + if (!ctx->bitstream.vaddr) { + v4l2_err(&dev->v4l2_dev, + "failed to allocate bitstream ringbuffer"); + ret = -ENOMEM; + goto err_dma_writecombine; + } } kfifo_init(&ctx->bitstream_fifo, ctx->bitstream.vaddr, ctx->bitstream.size); @@ -1693,16 +1748,14 @@ static int coda_release(struct file *file) v4l2_dbg(1, coda_debug, &dev->v4l2_dev, "Releasing instance %p\n", ctx); - debugfs_remove_recursive(ctx->debugfs_entry); - - if (ctx->inst_type == CODA_INST_DECODER) + if (ctx->inst_type == CODA_INST_DECODER && ctx->use_bit) coda_bit_stream_end_flag(ctx); /* If this instance is running, call .job_abort and wait for it to end */ v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); /* In case the instance was not running, we still need to call SEQ_END */ - if (ctx->initialized) { + if (ctx->initialized && ctx->ops->seq_end_work) { queue_work(dev->workqueue, &ctx->seq_end_work); flush_work(&ctx->seq_end_work); } @@ -1728,6 +1781,7 @@ static int coda_release(struct file *file) clear_bit(ctx->idx, &dev->instance_mask); if (ctx->ops->release) ctx->ops->release(ctx); + debugfs_remove_recursive(ctx->debugfs_entry); kfree(ctx); return 0; @@ -1844,10 +1898,11 @@ static int coda_register_device(struct coda_dev *dev, int i) { struct video_device *vfd = &dev->vfd[i]; - if (i > ARRAY_SIZE(dev->vfd)) + if (i >= dev->devtype->num_vdevs) return -EINVAL; - snprintf(vfd->name, sizeof(vfd->name), dev->devtype->vdevs[i]->name); + snprintf(vfd->name, sizeof(vfd->name), "%s", + dev->devtype->vdevs[i]->name); vfd->fops = &coda_fops; vfd->ioctl_ops = &coda_ioctl_ops; vfd->release = video_device_release_empty, @@ -2001,7 +2056,6 @@ static const struct coda_devtype coda_devdata[] = { static struct platform_device_id coda_platform_ids[] = { { .name = "coda-imx27", .driver_data = CODA_IMX27 }, - { .name = "coda-imx53", .driver_data = CODA_IMX53 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(platform, coda_platform_ids); @@ -2142,6 +2196,7 @@ static int coda_probe(struct platform_device *pdev) if (!dev->iram.vaddr) { dev_warn(&pdev->dev, "unable to alloc iram\n"); } else { + memset(dev->iram.vaddr, 0, dev->iram.size); dev->iram.blob.data = dev->iram.vaddr; dev->iram.blob.size = dev->iram.size; dev->iram.dentry = debugfs_create_blob("iram", 0644, diff --git a/drivers/media/platform/coda/coda.h b/drivers/media/platform/coda/coda.h index 5dd47e5f97c1..0c35cd5032ff 100644 --- a/drivers/media/platform/coda/coda.h +++ b/drivers/media/platform/coda/coda.h @@ -198,7 +198,6 @@ struct coda_ctx { int initialized; int streamon_out; int streamon_cap; - u32 isequence; u32 qsequence; u32 osequence; u32 sequence_offset; @@ -236,6 +235,7 @@ struct coda_ctx { u32 frame_mem_ctrl; int display_idx; struct dentry *debugfs_entry; + bool use_bit; }; extern int coda_debug; diff --git a/drivers/media/platform/coda/coda_regs.h b/drivers/media/platform/coda/coda_regs.h index 8e015b8aa8fa..7d026241171b 100644 --- a/drivers/media/platform/coda/coda_regs.h +++ b/drivers/media/platform/coda/coda_regs.h @@ -304,9 +304,9 @@ #define CODA_RATECONTROL_AUTOSKIP_OFFSET 31 #define CODA_RATECONTROL_AUTOSKIP_MASK 0x01 #define CODA_RATECONTROL_INITIALDELAY_OFFSET 16 -#define CODA_RATECONTROL_INITIALDELAY_MASK 0x7f +#define CODA_RATECONTROL_INITIALDELAY_MASK 0x7fff #define CODA_RATECONTROL_BITRATE_OFFSET 1 -#define CODA_RATECONTROL_BITRATE_MASK 0x7f +#define CODA_RATECONTROL_BITRATE_MASK 0x7fff #define CODA_RATECONTROL_ENABLE_OFFSET 0 #define CODA_RATECONTROL_ENABLE_MASK 0x01 #define CODA_CMD_ENC_SEQ_RC_BUF_SIZE 0x1b0 diff --git a/drivers/media/platform/davinci/Kconfig b/drivers/media/platform/davinci/Kconfig index d9e1ddb586b1..469e9d28cec0 100644 --- a/drivers/media/platform/davinci/Kconfig +++ b/drivers/media/platform/davinci/Kconfig @@ -1,6 +1,6 @@ config VIDEO_DAVINCI_VPIF_DISPLAY tristate "TI DaVinci VPIF V4L2-Display driver" - depends on VIDEO_DEV + depends on VIDEO_V4L2 depends on ARCH_DAVINCI || COMPILE_TEST depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG @@ -16,7 +16,7 @@ config VIDEO_DAVINCI_VPIF_DISPLAY config VIDEO_DAVINCI_VPIF_CAPTURE tristate "TI DaVinci VPIF video capture driver" - depends on VIDEO_DEV + depends on VIDEO_V4L2 depends on ARCH_DAVINCI || COMPILE_TEST depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG @@ -75,7 +75,7 @@ config VIDEO_DM365_ISIF config VIDEO_DAVINCI_VPBE_DISPLAY tristate "TI DaVinci VPBE V4L2-Display driver" - depends on ARCH_DAVINCI + depends on VIDEO_V4L2 && ARCH_DAVINCI depends on HAS_DMA select VIDEOBUF2_DMA_CONTIG help diff --git a/drivers/media/platform/exynos-gsc/gsc-core.h b/drivers/media/platform/exynos-gsc/gsc-core.h index 0abdb17fb19c..fa572aacdb3f 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.h +++ b/drivers/media/platform/exynos-gsc/gsc-core.h @@ -466,18 +466,6 @@ static inline void gsc_hw_clear_irq(struct gsc_dev *dev, int irq) writel(cfg, dev->regs + GSC_IRQ); } -static inline void gsc_lock(struct vb2_queue *vq) -{ - struct gsc_ctx *ctx = vb2_get_drv_priv(vq); - mutex_lock(&ctx->gsc_dev->lock); -} - -static inline void gsc_unlock(struct vb2_queue *vq) -{ - struct gsc_ctx *ctx = vb2_get_drv_priv(vq); - mutex_unlock(&ctx->gsc_dev->lock); -} - static inline bool gsc_ctx_state_is_set(u32 mask, struct gsc_ctx *ctx) { unsigned long flags; diff --git a/drivers/media/platform/exynos-gsc/gsc-m2m.c b/drivers/media/platform/exynos-gsc/gsc-m2m.c index 74e1de637e8f..d5cffef2e227 100644 --- a/drivers/media/platform/exynos-gsc/gsc-m2m.c +++ b/drivers/media/platform/exynos-gsc/gsc-m2m.c @@ -267,8 +267,8 @@ static struct vb2_ops gsc_m2m_qops = { .queue_setup = gsc_m2m_queue_setup, .buf_prepare = gsc_m2m_buf_prepare, .buf_queue = gsc_m2m_buf_queue, - .wait_prepare = gsc_unlock, - .wait_finish = gsc_lock, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .stop_streaming = gsc_m2m_stop_streaming, .start_streaming = gsc_m2m_start_streaming, }; @@ -590,6 +590,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &ctx->gsc_dev->lock; ret = vb2_queue_init(src_vq); if (ret) @@ -603,6 +604,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &ctx->gsc_dev->lock; return vb2_queue_init(dst_vq); } diff --git a/drivers/media/platform/marvell-ccic/Kconfig b/drivers/media/platform/marvell-ccic/Kconfig index 6265d36adceb..4bf5bd1e90d6 100644 --- a/drivers/media/platform/marvell-ccic/Kconfig +++ b/drivers/media/platform/marvell-ccic/Kconfig @@ -5,6 +5,7 @@ config VIDEO_CAFE_CCIC select VIDEO_OV7670 select VIDEOBUF2_VMALLOC select VIDEOBUF2_DMA_CONTIG + select VIDEOBUF2_DMA_SG ---help--- This is a video4linux2 driver for the Marvell 88ALP01 integrated CMOS camera controller. This is the controller found on first- @@ -13,7 +14,7 @@ config VIDEO_CAFE_CCIC config VIDEO_MMP_CAMERA tristate "Marvell Armada 610 integrated camera controller support" depends on ARCH_MMP && I2C && VIDEO_V4L2 - depends on HAS_DMA + depends on HAS_DMA && BROKEN select VIDEO_OV7670 select I2C_GPIO select VIDEOBUF2_DMA_SG diff --git a/drivers/media/platform/marvell-ccic/mcam-core.c b/drivers/media/platform/marvell-ccic/mcam-core.c index 193373ff268d..dd5b1415f974 100644 --- a/drivers/media/platform/marvell-ccic/mcam-core.c +++ b/drivers/media/platform/marvell-ccic/mcam-core.c @@ -1913,7 +1913,6 @@ int mccic_register(struct mcam_camera *cam) mutex_lock(&cam->s_mutex); cam->vdev = mcam_v4l_template; - cam->vdev.debug = 0; cam->vdev.v4l2_dev = &cam->v4l2_dev; video_set_drvdata(&cam->vdev, cam); ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); diff --git a/drivers/media/platform/omap3isp/isp.c b/drivers/media/platform/omap3isp/isp.c index 51c2129bdcc6..deca80903c3a 100644 --- a/drivers/media/platform/omap3isp/isp.c +++ b/drivers/media/platform/omap3isp/isp.c @@ -220,6 +220,9 @@ static u32 isp_xclk_calc_divider(unsigned long *rate, unsigned long parent_rate) return ISPTCTRL_CTRL_DIV_BYPASS; } + if (*rate == 0) + *rate = 1; + divider = DIV_ROUND_CLOSEST(parent_rate, *rate); if (divider >= ISPTCTRL_CTRL_DIV_BYPASS) divider = ISPTCTRL_CTRL_DIV_BYPASS - 1; diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index b463fe172d16..3fe9047ef466 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -602,10 +602,13 @@ isp_video_querycap(struct file *file, void *fh, struct v4l2_capability *cap) strlcpy(cap->card, video->video.name, sizeof(cap->card)); strlcpy(cap->bus_info, "media", sizeof(cap->bus_info)); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT + | V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS; + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; else - cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + cap->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; return 0; } diff --git a/drivers/media/platform/s3c-camif/camif-capture.c b/drivers/media/platform/s3c-camif/camif-capture.c index aa40c8269ab8..54479d60cc0d 100644 --- a/drivers/media/platform/s3c-camif/camif-capture.c +++ b/drivers/media/platform/s3c-camif/camif-capture.c @@ -536,24 +536,12 @@ static void buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&camif->slock, flags); } -static void camif_lock(struct vb2_queue *vq) -{ - struct camif_vp *vp = vb2_get_drv_priv(vq); - mutex_lock(&vp->camif->lock); -} - -static void camif_unlock(struct vb2_queue *vq) -{ - struct camif_vp *vp = vb2_get_drv_priv(vq); - mutex_unlock(&vp->camif->lock); -} - static const struct vb2_ops s3c_camif_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, - .wait_prepare = camif_unlock, - .wait_finish = camif_lock, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .start_streaming = start_streaming, .stop_streaming = stop_streaming, }; @@ -1161,6 +1149,7 @@ int s3c_camif_register_video_node(struct camif_dev *camif, int idx) q->buf_struct_size = sizeof(struct camif_buffer); q->drv_priv = vp; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &vp->camif->lock; ret = vb2_queue_init(q); if (ret) diff --git a/drivers/media/platform/s5p-g2d/g2d.c b/drivers/media/platform/s5p-g2d/g2d.c index 47ba8fbb0426..ec3e1248923d 100644 --- a/drivers/media/platform/s5p-g2d/g2d.c +++ b/drivers/media/platform/s5p-g2d/g2d.c @@ -12,7 +12,6 @@ #include <linux/module.h> #include <linux/fs.h> -#include <linux/version.h> #include <linux/timer.h> #include <linux/sched.h> #include <linux/slab.h> diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c index fbfdf03b9054..8e44a59d8ec2 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc.c @@ -810,6 +810,7 @@ static int s5p_mfc_open(struct file *file) q = &ctx->vq_dst; q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; q->drv_priv = &ctx->fh; + q->lock = &dev->mfc_mutex; if (vdev == dev->vfd_dec) { q->io_modes = VB2_MMAP; q->ops = get_dec_queue_ops(); diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c index c6c3452ccca1..aebe4fd7f03a 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_dec.c @@ -18,7 +18,6 @@ #include <linux/platform_device.h> #include <linux/sched.h> #include <linux/slab.h> -#include <linux/version.h> #include <linux/videodev2.h> #include <linux/workqueue.h> #include <media/v4l2-ctrls.h> @@ -813,7 +812,7 @@ static int vidioc_decoder_cmd(struct file *file, void *priv, unsigned long flags; switch (cmd->cmd) { - case V4L2_ENC_CMD_STOP: + case V4L2_DEC_CMD_STOP: if (cmd->flags != 0) return -EINVAL; @@ -944,22 +943,6 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, return 0; } -static void s5p_mfc_unlock(struct vb2_queue *q) -{ - struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv); - struct s5p_mfc_dev *dev = ctx->dev; - - mutex_unlock(&dev->mfc_mutex); -} - -static void s5p_mfc_lock(struct vb2_queue *q) -{ - struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv); - struct s5p_mfc_dev *dev = ctx->dev; - - mutex_lock(&dev->mfc_mutex); -} - static int s5p_mfc_buf_init(struct vb2_buffer *vb) { struct vb2_queue *vq = vb->vb2_queue; @@ -1107,8 +1090,8 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb) static struct vb2_ops s5p_mfc_dec_qops = { .queue_setup = s5p_mfc_queue_setup, - .wait_prepare = s5p_mfc_unlock, - .wait_finish = s5p_mfc_lock, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .buf_init = s5p_mfc_buf_init, .start_streaming = s5p_mfc_start_streaming, .stop_streaming = s5p_mfc_stop_streaming, diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c index bd64f1dcbdb5..e65993f4b901 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_enc.c @@ -19,7 +19,6 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/sched.h> -#include <linux/version.h> #include <linux/videodev2.h> #include <media/v4l2-event.h> #include <linux/workqueue.h> @@ -1867,22 +1866,6 @@ static int s5p_mfc_queue_setup(struct vb2_queue *vq, return 0; } -static void s5p_mfc_unlock(struct vb2_queue *q) -{ - struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv); - struct s5p_mfc_dev *dev = ctx->dev; - - mutex_unlock(&dev->mfc_mutex); -} - -static void s5p_mfc_lock(struct vb2_queue *q) -{ - struct s5p_mfc_ctx *ctx = fh_to_ctx(q->drv_priv); - struct s5p_mfc_dev *dev = ctx->dev; - - mutex_lock(&dev->mfc_mutex); -} - static int s5p_mfc_buf_init(struct vb2_buffer *vb) { struct vb2_queue *vq = vb->vb2_queue; @@ -2052,8 +2035,8 @@ static void s5p_mfc_buf_queue(struct vb2_buffer *vb) static struct vb2_ops s5p_mfc_enc_qops = { .queue_setup = s5p_mfc_queue_setup, - .wait_prepare = s5p_mfc_unlock, - .wait_finish = s5p_mfc_lock, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .buf_init = s5p_mfc_buf_init, .buf_prepare = s5p_mfc_buf_prepare, .start_streaming = s5p_mfc_start_streaming, diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c index 9aea179943ce..d826c58b5d53 100644 --- a/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c +++ b/drivers/media/platform/s5p-mfc/s5p_mfc_opr_v6.c @@ -1340,11 +1340,7 @@ static int s5p_mfc_init_decode_v6(struct s5p_mfc_ctx *ctx) /* FMO_ASO_CTRL - 0: Enable, 1: Disable */ reg |= (fmo_aso_ctrl << S5P_FIMV_D_OPT_FMO_ASO_CTRL_MASK_V6); - /* When user sets desplay_delay to 0, - * It works as "display_delay enable" and delay set to 0. - * If user wants display_delay disable, It should be - * set to negative value. */ - if (ctx->display_delay >= 0) { + if (ctx->display_delay_enable) { reg |= (0x1 << S5P_FIMV_D_OPT_DDELAY_EN_SHIFT_V6); writel(ctx->display_delay, mfc_regs->d_display_delay); } diff --git a/drivers/media/platform/s5p-tv/mixer_video.c b/drivers/media/platform/s5p-tv/mixer_video.c index b4d2696501e4..72d4f2e1efc0 100644 --- a/drivers/media/platform/s5p-tv/mixer_video.c +++ b/drivers/media/platform/s5p-tv/mixer_video.c @@ -926,22 +926,6 @@ static void buf_queue(struct vb2_buffer *vb) mxr_dbg(mdev, "queuing buffer\n"); } -static void wait_lock(struct vb2_queue *vq) -{ - struct mxr_layer *layer = vb2_get_drv_priv(vq); - - mxr_dbg(layer->mdev, "%s\n", __func__); - mutex_lock(&layer->mutex); -} - -static void wait_unlock(struct vb2_queue *vq) -{ - struct mxr_layer *layer = vb2_get_drv_priv(vq); - - mxr_dbg(layer->mdev, "%s\n", __func__); - mutex_unlock(&layer->mutex); -} - static int start_streaming(struct vb2_queue *vq, unsigned int count) { struct mxr_layer *layer = vb2_get_drv_priv(vq); @@ -1040,8 +1024,8 @@ static void stop_streaming(struct vb2_queue *vq) static struct vb2_ops mxr_video_qops = { .queue_setup = queue_setup, .buf_queue = buf_queue, - .wait_prepare = wait_unlock, - .wait_finish = wait_lock, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .start_streaming = start_streaming, .stop_streaming = stop_streaming, }; @@ -1122,6 +1106,7 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, .ops = &mxr_video_qops, .min_buffers_needed = 1, .mem_ops = &vb2_dma_contig_memops, + .lock = &layer->mutex, }; return layer; diff --git a/drivers/media/platform/sh_veu.c b/drivers/media/platform/sh_veu.c index aaa1f6f25a29..a901b6248557 100644 --- a/drivers/media/platform/sh_veu.c +++ b/drivers/media/platform/sh_veu.c @@ -242,20 +242,6 @@ static void sh_veu_job_abort(void *priv) veu->aborting = true; } -static void sh_veu_lock(void *priv) -{ - struct sh_veu_dev *veu = priv; - - mutex_lock(&veu->fop_lock); -} - -static void sh_veu_unlock(void *priv) -{ - struct sh_veu_dev *veu = priv; - - mutex_unlock(&veu->fop_lock); -} - static void sh_veu_process(struct sh_veu_dev *veu, struct vb2_buffer *src_buf, struct vb2_buffer *dst_buf) @@ -950,36 +936,28 @@ static void sh_veu_buf_queue(struct vb2_buffer *vb) v4l2_m2m_buf_queue(veu->m2m_ctx, vb); } -static void sh_veu_wait_prepare(struct vb2_queue *q) -{ - sh_veu_unlock(vb2_get_drv_priv(q)); -} - -static void sh_veu_wait_finish(struct vb2_queue *q) -{ - sh_veu_lock(vb2_get_drv_priv(q)); -} - static const struct vb2_ops sh_veu_qops = { .queue_setup = sh_veu_queue_setup, .buf_prepare = sh_veu_buf_prepare, .buf_queue = sh_veu_buf_queue, - .wait_prepare = sh_veu_wait_prepare, - .wait_finish = sh_veu_wait_finish, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) { + struct sh_veu_dev *veu = priv; int ret; memset(src_vq, 0, sizeof(*src_vq)); src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; src_vq->io_modes = VB2_MMAP | VB2_USERPTR; - src_vq->drv_priv = priv; + src_vq->drv_priv = veu; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &sh_veu_qops; src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->lock = &veu->fop_lock; ret = vb2_queue_init(src_vq); if (ret < 0) @@ -988,10 +966,11 @@ static int sh_veu_queue_init(void *priv, struct vb2_queue *src_vq, memset(dst_vq, 0, sizeof(*dst_vq)); dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; - dst_vq->drv_priv = priv; + dst_vq->drv_priv = veu; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &sh_veu_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->lock = &veu->fop_lock; return vb2_queue_init(dst_vq); } diff --git a/drivers/media/platform/soc_camera/atmel-isi.c b/drivers/media/platform/soc_camera/atmel-isi.c index 8efe40337608..8526bf5c8429 100644 --- a/drivers/media/platform/soc_camera/atmel-isi.c +++ b/drivers/media/platform/soc_camera/atmel-isi.c @@ -455,8 +455,8 @@ static struct vb2_ops isi_video_qops = { .buf_queue = buffer_queue, .start_streaming = start_streaming, .stop_streaming = stop_streaming, - .wait_prepare = soc_camera_unlock, - .wait_finish = soc_camera_lock, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; /* ------------------------------------------------------------------ @@ -465,6 +465,8 @@ static struct vb2_ops isi_video_qops = { static int isi_camera_init_videobuf(struct vb2_queue *q, struct soc_camera_device *icd) { + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP; q->drv_priv = icd; @@ -472,6 +474,7 @@ static int isi_camera_init_videobuf(struct vb2_queue *q, q->ops = &isi_video_qops; q->mem_ops = &vb2_dma_contig_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &ici->host_lock; return vb2_queue_init(q); } @@ -760,8 +763,9 @@ static int isi_camera_querycap(struct soc_camera_host *ici, { strcpy(cap->driver, "atmel-isi"); strcpy(cap->card, "Atmel Image Sensor Interface"); - cap->capabilities = (V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_STREAMING); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; } diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index ce72bd26a6ac..192377f55840 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -1256,7 +1256,8 @@ static int mx2_camera_querycap(struct soc_camera_host *ici, { /* cap->name is set by the friendly caller:-> */ strlcpy(cap->card, MX2_CAM_DRIVER_DESCRIPTION, sizeof(cap->card)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c index a60c3bb0e4cc..3435fd2ca8ec 100644 --- a/drivers/media/platform/soc_camera/mx3_camera.c +++ b/drivers/media/platform/soc_camera/mx3_camera.c @@ -435,14 +435,16 @@ static struct vb2_ops mx3_videobuf_ops = { .buf_queue = mx3_videobuf_queue, .buf_cleanup = mx3_videobuf_release, .buf_init = mx3_videobuf_init, - .wait_prepare = soc_camera_unlock, - .wait_finish = soc_camera_lock, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .stop_streaming = mx3_stop_streaming, }; static int mx3_camera_init_videobuf(struct vb2_queue *q, struct soc_camera_device *icd) { + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_USERPTR; q->drv_priv = icd; @@ -450,6 +452,7 @@ static int mx3_camera_init_videobuf(struct vb2_queue *q, q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct mx3_camera_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &ici->host_lock; return vb2_queue_init(q); } @@ -967,7 +970,8 @@ static int mx3_camera_querycap(struct soc_camera_host *ici, { /* cap->name is set by the firendly caller:-> */ strlcpy(cap->card, "i.MX3x Camera", sizeof(cap->card)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c index e6b93281f246..16f65ecb70a3 100644 --- a/drivers/media/platform/soc_camera/omap1_camera.c +++ b/drivers/media/platform/soc_camera/omap1_camera.c @@ -1427,7 +1427,8 @@ static int omap1_cam_querycap(struct soc_camera_host *ici, { /* cap->name is set by the friendly caller:-> */ strlcpy(cap->card, "OMAP1 Camera", sizeof(cap->card)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c index 951226af0eba..8d6e343fec0f 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/soc_camera/pxa_camera.c @@ -1576,7 +1576,8 @@ static int pxa_camera_querycap(struct soc_camera_host *ici, { /* cap->name is set by the firendly caller:-> */ strlcpy(cap->card, pxa_cam_driver_description, sizeof(cap->card)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } diff --git a/drivers/media/platform/soc_camera/rcar_vin.c b/drivers/media/platform/soc_camera/rcar_vin.c index 0c1f55648106..279ab9f6ae38 100644 --- a/drivers/media/platform/soc_camera/rcar_vin.c +++ b/drivers/media/platform/soc_camera/rcar_vin.c @@ -804,62 +804,26 @@ error: vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); } -static void rcar_vin_videobuf_release(struct vb2_buffer *vb) +/* + * Wait for capture to stop and all in-flight buffers to be finished with by + * the video hardware. This must be called under &priv->lock + * + */ +static void rcar_vin_wait_stop_streaming(struct rcar_vin_priv *priv) { - struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct rcar_vin_priv *priv = ici->priv; - unsigned int i; - int buf_in_use = 0; - - spin_lock_irq(&priv->lock); - - /* Is the buffer in use by the VIN hardware? */ - for (i = 0; i < MAX_BUFFER_NUM; i++) { - if (priv->queue_buf[i] == vb) { - buf_in_use = 1; - break; - } - } - - if (buf_in_use) { - while (priv->state != STOPPED) { - - /* issue stop if running */ - if (priv->state == RUNNING) - rcar_vin_request_capture_stop(priv); + while (priv->state != STOPPED) { + /* issue stop if running */ + if (priv->state == RUNNING) + rcar_vin_request_capture_stop(priv); - /* wait until capturing has been stopped */ - if (priv->state == STOPPING) { - priv->request_to_stop = true; - spin_unlock_irq(&priv->lock); - wait_for_completion(&priv->capture_stop); - spin_lock_irq(&priv->lock); - } - } - /* - * Capturing has now stopped. The buffer we have been asked - * to release could be any of the current buffers in use, so - * release all buffers that are in use by HW - */ - for (i = 0; i < MAX_BUFFER_NUM; i++) { - if (priv->queue_buf[i]) { - vb2_buffer_done(priv->queue_buf[i], - VB2_BUF_STATE_ERROR); - priv->queue_buf[i] = NULL; - } + /* wait until capturing has been stopped */ + if (priv->state == STOPPING) { + priv->request_to_stop = true; + spin_unlock_irq(&priv->lock); + wait_for_completion(&priv->capture_stop); + spin_lock_irq(&priv->lock); } - } else { - list_del_init(to_buf_list(vb)); } - - spin_unlock_irq(&priv->lock); -} - -static int rcar_vin_videobuf_init(struct vb2_buffer *vb) -{ - INIT_LIST_HEAD(to_buf_list(vb)); - return 0; } static void rcar_vin_stop_streaming(struct vb2_queue *vq) @@ -868,21 +832,34 @@ static void rcar_vin_stop_streaming(struct vb2_queue *vq) struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct rcar_vin_priv *priv = ici->priv; struct list_head *buf_head, *tmp; + int i; spin_lock_irq(&priv->lock); - list_for_each_safe(buf_head, tmp, &priv->capture) + rcar_vin_wait_stop_streaming(priv); + + for (i = 0; i < MAX_BUFFER_NUM; i++) { + if (priv->queue_buf[i]) { + vb2_buffer_done(priv->queue_buf[i], + VB2_BUF_STATE_ERROR); + priv->queue_buf[i] = NULL; + } + } + + list_for_each_safe(buf_head, tmp, &priv->capture) { + vb2_buffer_done(&list_entry(buf_head, + struct rcar_vin_buffer, list)->vb, + VB2_BUF_STATE_ERROR); list_del_init(buf_head); + } spin_unlock_irq(&priv->lock); } static struct vb2_ops rcar_vin_vb2_ops = { .queue_setup = rcar_vin_videobuf_setup, - .buf_init = rcar_vin_videobuf_init, - .buf_cleanup = rcar_vin_videobuf_release, .buf_queue = rcar_vin_videobuf_queue, .stop_streaming = rcar_vin_stop_streaming, - .wait_prepare = soc_camera_unlock, - .wait_finish = soc_camera_lock, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; static irqreturn_t rcar_vin_irq(int irq, void *data) @@ -1799,13 +1776,17 @@ static int rcar_vin_querycap(struct soc_camera_host *ici, struct v4l2_capability *cap) { strlcpy(cap->card, "R_Car_VIN", sizeof(cap->card)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; } static int rcar_vin_init_videobuf2(struct vb2_queue *vq, struct soc_camera_device *icd) { + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vq->io_modes = VB2_MMAP | VB2_USERPTR; vq->drv_priv = icd; @@ -1813,6 +1794,7 @@ static int rcar_vin_init_videobuf2(struct vb2_queue *vq, vq->mem_ops = &vb2_dma_contig_memops; vq->buf_struct_size = sizeof(struct rcar_vin_buffer); vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + vq->lock = &ici->host_lock; return vb2_queue_init(vq); } diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 8b27b3eb2b25..9ce202f53934 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -496,8 +496,8 @@ static struct vb2_ops sh_mobile_ceu_videobuf_ops = { .buf_queue = sh_mobile_ceu_videobuf_queue, .buf_cleanup = sh_mobile_ceu_videobuf_release, .buf_init = sh_mobile_ceu_videobuf_init, - .wait_prepare = soc_camera_unlock, - .wait_finish = soc_camera_lock, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .stop_streaming = sh_mobile_ceu_stop_streaming, }; @@ -1652,13 +1652,17 @@ static int sh_mobile_ceu_querycap(struct soc_camera_host *ici, struct v4l2_capability *cap) { strlcpy(cap->card, "SuperH_Mobile_CEU", sizeof(cap->card)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; } static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q, struct soc_camera_device *icd) { + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_USERPTR; q->drv_priv = icd; @@ -1666,6 +1670,7 @@ static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q, q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct sh_mobile_ceu_buffer); q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &ici->host_lock; return vb2_queue_init(q); } diff --git a/drivers/media/platform/soc_camera/soc_camera.c b/drivers/media/platform/soc_camera/soc_camera.c index b3db51c82bde..cee7b56f8404 100644 --- a/drivers/media/platform/soc_camera/soc_camera.c +++ b/drivers/media/platform/soc_camera/soc_camera.c @@ -843,22 +843,6 @@ static unsigned int soc_camera_poll(struct file *file, poll_table *pt) return res; } -void soc_camera_lock(struct vb2_queue *vq) -{ - struct soc_camera_device *icd = vb2_get_drv_priv(vq); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - mutex_lock(&ici->host_lock); -} -EXPORT_SYMBOL(soc_camera_lock); - -void soc_camera_unlock(struct vb2_queue *vq) -{ - struct soc_camera_device *icd = vb2_get_drv_priv(vq); - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - mutex_unlock(&ici->host_lock); -} -EXPORT_SYMBOL(soc_camera_unlock); - static struct v4l2_file_operations soc_camera_fops = { .owner = THIS_MODULE, .open = soc_camera_open, @@ -1813,8 +1797,6 @@ eadddev: mutex_unlock(&ici->clk_lock); } eadd: - video_device_release(icd->vdev); - icd->vdev = NULL; if (icd->vdev) { video_device_release(icd->vdev); icd->vdev = NULL; diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index d628d1a7cf9e..c44760b705da 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -25,6 +25,7 @@ #include <linux/io.h> #include <linux/ioctl.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/sched.h> @@ -74,7 +75,7 @@ #define VPE_DEF_BUFS_PER_JOB 1 /* default one buffer per batch job */ /* - * each VPE context can need up to 3 config desciptors, 7 input descriptors, + * each VPE context can need up to 3 config descriptors, 7 input descriptors, * 3 output descriptors, and 10 control descriptors */ #define VPE_DESC_LIST_SIZE (10 * VPDMA_DTD_DESC_SIZE + \ @@ -373,7 +374,6 @@ struct vpe_dev { struct vpe_ctx { struct v4l2_fh fh; struct vpe_dev *dev; - struct v4l2_m2m_ctx *m2m_ctx; struct v4l2_ctrl_handler hdl; unsigned int field; /* current field */ @@ -887,10 +887,10 @@ static int job_ready(void *priv) if (ctx->deinterlacing && ctx->src_vbs[2] == NULL) needed += 2; /* need additional two most recent fields */ - if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < needed) + if (v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx) < needed) return 0; - if (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) < needed) + if (v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx) < needed) return 0; return 1; @@ -1100,15 +1100,15 @@ static void device_run(void *priv) struct vpe_q_data *d_q_data = &ctx->q_data[Q_DATA_DST]; if (ctx->deinterlacing && ctx->src_vbs[2] == NULL) { - ctx->src_vbs[2] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + ctx->src_vbs[2] = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); WARN_ON(ctx->src_vbs[2] == NULL); - ctx->src_vbs[1] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + ctx->src_vbs[1] = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); WARN_ON(ctx->src_vbs[1] == NULL); } - ctx->src_vbs[0] = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + ctx->src_vbs[0] = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); WARN_ON(ctx->src_vbs[0] == NULL); - ctx->dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + ctx->dst_vb = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); WARN_ON(ctx->dst_vb == NULL); /* config descriptors */ @@ -1334,7 +1334,7 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) finished: vpe_dbg(ctx->dev, "finishing transaction\n"); ctx->bufs_completed = 0; - v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx); + v4l2_m2m_job_finish(dev->m2m_dev, ctx->fh.m2m_ctx); handled: return IRQ_HANDLED; } @@ -1395,7 +1395,7 @@ static int vpe_g_fmt(struct file *file, void *priv, struct v4l2_format *f) struct vpe_q_data *q_data; int i; - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); if (!vq) return -EINVAL; @@ -1527,7 +1527,7 @@ static int __vpe_s_fmt(struct vpe_ctx *ctx, struct v4l2_format *f) struct vb2_queue *vq; int i; - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); if (!vq) return -EINVAL; @@ -1739,52 +1739,6 @@ static int vpe_s_selection(struct file *file, void *fh, return set_srcdst_params(ctx); } -static int vpe_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *reqbufs) -{ - struct vpe_ctx *ctx = file2ctx(file); - - return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); -} - -static int vpe_querybuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct vpe_ctx *ctx = file2ctx(file); - - return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); -} - -static int vpe_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct vpe_ctx *ctx = file2ctx(file); - - return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); -} - -static int vpe_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct vpe_ctx *ctx = file2ctx(file); - - return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); -} - -static int vpe_streamon(struct file *file, void *priv, enum v4l2_buf_type type) -{ - struct vpe_ctx *ctx = file2ctx(file); - - return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); -} - -static int vpe_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) -{ - struct vpe_ctx *ctx = file2ctx(file); - - vpe_dump_regs(ctx->dev); - vpdma_dump_regs(ctx->dev->vpdma); - - return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); -} - /* * defines number of buffers/frames a context can process with VPE before * switching to a different context. default value is 1 buffer per context @@ -1814,14 +1768,14 @@ static const struct v4l2_ctrl_ops vpe_ctrl_ops = { }; static const struct v4l2_ioctl_ops vpe_ioctl_ops = { - .vidioc_querycap = vpe_querycap, + .vidioc_querycap = vpe_querycap, - .vidioc_enum_fmt_vid_cap_mplane = vpe_enum_fmt, + .vidioc_enum_fmt_vid_cap_mplane = vpe_enum_fmt, .vidioc_g_fmt_vid_cap_mplane = vpe_g_fmt, .vidioc_try_fmt_vid_cap_mplane = vpe_try_fmt, .vidioc_s_fmt_vid_cap_mplane = vpe_s_fmt, - .vidioc_enum_fmt_vid_out_mplane = vpe_enum_fmt, + .vidioc_enum_fmt_vid_out_mplane = vpe_enum_fmt, .vidioc_g_fmt_vid_out_mplane = vpe_g_fmt, .vidioc_try_fmt_vid_out_mplane = vpe_try_fmt, .vidioc_s_fmt_vid_out_mplane = vpe_s_fmt, @@ -1829,16 +1783,15 @@ static const struct v4l2_ioctl_ops vpe_ioctl_ops = { .vidioc_g_selection = vpe_g_selection, .vidioc_s_selection = vpe_s_selection, - .vidioc_reqbufs = vpe_reqbufs, - .vidioc_querybuf = vpe_querybuf, + .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, + .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, + .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, + .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, + .vidioc_streamon = v4l2_m2m_ioctl_streamon, + .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, - .vidioc_qbuf = vpe_qbuf, - .vidioc_dqbuf = vpe_dqbuf, - - .vidioc_streamon = vpe_streamon, - .vidioc_streamoff = vpe_streamoff, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /* @@ -1910,33 +1863,40 @@ static int vpe_buf_prepare(struct vb2_buffer *vb) static void vpe_buf_queue(struct vb2_buffer *vb) { struct vpe_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); + + v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vb); } -static void vpe_wait_prepare(struct vb2_queue *q) +static int vpe_start_streaming(struct vb2_queue *q, unsigned int count) { - struct vpe_ctx *ctx = vb2_get_drv_priv(q); - vpe_unlock(ctx); + /* currently we do nothing here */ + + return 0; } -static void vpe_wait_finish(struct vb2_queue *q) +static void vpe_stop_streaming(struct vb2_queue *q) { struct vpe_ctx *ctx = vb2_get_drv_priv(q); - vpe_lock(ctx); + + vpe_dump_regs(ctx->dev); + vpdma_dump_regs(ctx->dev->vpdma); } static struct vb2_ops vpe_qops = { .queue_setup = vpe_queue_setup, .buf_prepare = vpe_buf_prepare, .buf_queue = vpe_buf_queue, - .wait_prepare = vpe_wait_prepare, - .wait_finish = vpe_wait_finish, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .start_streaming = vpe_start_streaming, + .stop_streaming = vpe_stop_streaming, }; static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq) { struct vpe_ctx *ctx = priv; + struct vpe_dev *dev = ctx->dev; int ret; memset(src_vq, 0, sizeof(*src_vq)); @@ -1947,6 +1907,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, src_vq->ops = &vpe_qops; src_vq->mem_ops = &vb2_dma_contig_memops; src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + src_vq->lock = &dev->dev_mutex; ret = vb2_queue_init(src_vq); if (ret) @@ -1960,6 +1921,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, dst_vq->ops = &vpe_qops; dst_vq->mem_ops = &vb2_dma_contig_memops; dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + dst_vq->lock = &dev->dev_mutex; return vb2_queue_init(dst_vq); } @@ -1981,9 +1943,9 @@ static const struct v4l2_ctrl_config vpe_bufs_per_job = { static int vpe_open(struct file *file) { struct vpe_dev *dev = video_drvdata(file); - struct vpe_ctx *ctx = NULL; struct vpe_q_data *s_q_data; struct v4l2_ctrl_handler *hdl; + struct vpe_ctx *ctx; int ret; vpe_dbg(dev, "vpe_open\n"); @@ -2056,10 +2018,10 @@ static int vpe_open(struct file *file) if (ret) goto exit_fh; - ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); + ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init); - if (IS_ERR(ctx->m2m_ctx)) { - ret = PTR_ERR(ctx->m2m_ctx); + if (IS_ERR(ctx->fh.m2m_ctx)) { + ret = PTR_ERR(ctx->fh.m2m_ctx); goto exit_fh; } @@ -2078,7 +2040,7 @@ static int vpe_open(struct file *file) ctx->load_mmrs = true; vpe_dbg(dev, "created instance %p, m2m_ctx: %p\n", - ctx, ctx->m2m_ctx); + ctx, ctx->fh.m2m_ctx); mutex_unlock(&dev->dev_mutex); @@ -2116,7 +2078,7 @@ static int vpe_release(struct file *file) v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); v4l2_ctrl_handler_free(&ctx->hdl); - v4l2_m2m_ctx_release(ctx->m2m_ctx); + v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); kfree(ctx); @@ -2133,39 +2095,13 @@ static int vpe_release(struct file *file) return 0; } -static unsigned int vpe_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct vpe_ctx *ctx = file2ctx(file); - struct vpe_dev *dev = ctx->dev; - int ret; - - mutex_lock(&dev->dev_mutex); - ret = v4l2_m2m_poll(file, ctx->m2m_ctx, wait); - mutex_unlock(&dev->dev_mutex); - return ret; -} - -static int vpe_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct vpe_ctx *ctx = file2ctx(file); - struct vpe_dev *dev = ctx->dev; - int ret; - - if (mutex_lock_interruptible(&dev->dev_mutex)) - return -ERESTARTSYS; - ret = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); - mutex_unlock(&dev->dev_mutex); - return ret; -} - static const struct v4l2_file_operations vpe_fops = { .owner = THIS_MODULE, .open = vpe_open, .release = vpe_release, - .poll = vpe_poll, + .poll = v4l2_m2m_fop_poll, .unlocked_ioctl = video_ioctl2, - .mmap = vpe_mmap, + .mmap = v4l2_m2m_fop_mmap, }; static struct video_device vpe_videodev = { @@ -2367,8 +2303,6 @@ static const struct of_device_id vpe_of_match[] = { }, {}, }; -#else -#define vpe_of_match NULL #endif static struct platform_driver vpe_pdrv = { @@ -2376,7 +2310,7 @@ static struct platform_driver vpe_pdrv = { .remove = vpe_remove, .driver = { .name = VPE_MODULE_NAME, - .of_match_table = vpe_of_match, + .of_match_table = of_match_ptr(vpe_of_match), }, }; diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c index 857e7866e8bc..32a798f2d953 100644 --- a/drivers/media/platform/vivid/vivid-ctrls.c +++ b/drivers/media/platform/vivid/vivid-ctrls.c @@ -689,7 +689,7 @@ static const struct v4l2_ctrl_config vivid_ctrl_max_edid_blocks = { static const char * const vivid_ctrl_colorspace_strings[] = { "SMPTE 170M", - "REC 709", + "Rec. 709", "sRGB", "AdobeRGB", "BT.2020", @@ -716,7 +716,7 @@ static const char * const vivid_ctrl_ycbcr_enc_strings[] = { "xvYCC 601", "xvYCC 709", "sYCC", - "BT.2020 Non-Constant Luminance", + "BT.2020", "BT.2020 Constant Luminance", "SMPTE 240M", NULL, diff --git a/drivers/media/platform/vivid/vivid-tpg.c b/drivers/media/platform/vivid/vivid-tpg.c index fc9c6536ba02..34493f435d5a 100644 --- a/drivers/media/platform/vivid/vivid-tpg.c +++ b/drivers/media/platform/vivid/vivid-tpg.c @@ -352,13 +352,14 @@ static void color_to_ycbcr(struct tpg_data *tpg, int r, int g, int b, { COEFF(0.5, 224), COEFF(-0.4629, 224), COEFF(-0.0405, 224) }, }; bool full = tpg->real_quantization == V4L2_QUANTIZATION_FULL_RANGE; + unsigned y_offset = full ? 0 : 16; int lin_y, yc; switch (tpg->real_ycbcr_enc) { case V4L2_YCBCR_ENC_601: case V4L2_YCBCR_ENC_XV601: case V4L2_YCBCR_ENC_SYCC: - rgb2ycbcr(full ? bt601_full : bt601, r, g, b, 16, y, cb, cr); + rgb2ycbcr(full ? bt601_full : bt601, r, g, b, y_offset, y, cb, cr); break; case V4L2_YCBCR_ENC_BT2020: rgb2ycbcr(bt2020, r, g, b, 16, y, cb, cr); @@ -384,7 +385,7 @@ static void color_to_ycbcr(struct tpg_data *tpg, int r, int g, int b, case V4L2_YCBCR_ENC_709: case V4L2_YCBCR_ENC_XV709: default: - rgb2ycbcr(full ? rec709_full : rec709, r, g, b, 0, y, cb, cr); + rgb2ycbcr(full ? rec709_full : rec709, r, g, b, y_offset, y, cb, cr); break; } } @@ -439,13 +440,14 @@ static void ycbcr_to_color(struct tpg_data *tpg, int y, int cb, int cr, { COEFF(1, 219), COEFF(1.8814, 224), COEFF(0, 224) }, }; bool full = tpg->real_quantization == V4L2_QUANTIZATION_FULL_RANGE; + unsigned y_offset = full ? 0 : 16; int lin_r, lin_g, lin_b, lin_y; switch (tpg->real_ycbcr_enc) { case V4L2_YCBCR_ENC_601: case V4L2_YCBCR_ENC_XV601: case V4L2_YCBCR_ENC_SYCC: - ycbcr2rgb(full ? bt601_full : bt601, y, cb, cr, 16, r, g, b); + ycbcr2rgb(full ? bt601_full : bt601, y, cb, cr, y_offset, r, g, b); break; case V4L2_YCBCR_ENC_BT2020: ycbcr2rgb(bt2020, y, cb, cr, 16, r, g, b); @@ -480,7 +482,7 @@ static void ycbcr_to_color(struct tpg_data *tpg, int y, int cb, int cr, case V4L2_YCBCR_ENC_709: case V4L2_YCBCR_ENC_XV709: default: - ycbcr2rgb(full ? rec709_full : rec709, y, cb, cr, 16, r, g, b); + ycbcr2rgb(full ? rec709_full : rec709, y, cb, cr, y_offset, r, g, b); break; } } diff --git a/drivers/media/platform/vivid/vivid-tpg.h b/drivers/media/platform/vivid/vivid-tpg.h index 9dc463a40ed3..bd8b1c760b3f 100644 --- a/drivers/media/platform/vivid/vivid-tpg.h +++ b/drivers/media/platform/vivid/vivid-tpg.h @@ -20,7 +20,6 @@ #ifndef _VIVID_TPG_H_ #define _VIVID_TPG_H_ -#include <linux/version.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/random.h> diff --git a/drivers/media/platform/vsp1/vsp1.h b/drivers/media/platform/vsp1/vsp1.h index 12467191dff4..989e96f7e360 100644 --- a/drivers/media/platform/vsp1/vsp1.h +++ b/drivers/media/platform/vsp1/vsp1.h @@ -16,7 +16,6 @@ #include <linux/io.h> #include <linux/list.h> #include <linux/mutex.h> -#include <linux/platform_data/vsp1.h> #include <media/media-device.h> #include <media/v4l2-device.h> @@ -40,9 +39,20 @@ struct vsp1_uds; #define VSP1_MAX_UDS 3 #define VSP1_MAX_WPF 4 +#define VSP1_HAS_LIF (1 << 0) +#define VSP1_HAS_LUT (1 << 1) +#define VSP1_HAS_SRU (1 << 2) + +struct vsp1_platform_data { + unsigned int features; + unsigned int rpf_count; + unsigned int uds_count; + unsigned int wpf_count; +}; + struct vsp1_device { struct device *dev; - struct vsp1_platform_data *pdata; + struct vsp1_platform_data pdata; void __iomem *mmio; struct clk *clock; diff --git a/drivers/media/platform/vsp1/vsp1_bru.c b/drivers/media/platform/vsp1/vsp1_bru.c index b21f381a9862..401e2b77a0b6 100644 --- a/drivers/media/platform/vsp1/vsp1_bru.c +++ b/drivers/media/platform/vsp1/vsp1_bru.c @@ -20,7 +20,7 @@ #include "vsp1_bru.h" #include "vsp1_rwpf.h" -#define BRU_MIN_SIZE 4U +#define BRU_MIN_SIZE 1U #define BRU_MAX_SIZE 8190U /* ----------------------------------------------------------------------------- diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index 5eb16e87d53f..913485a90e97 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -40,7 +40,7 @@ static irqreturn_t vsp1_irq_handler(int irq, void *data) irqreturn_t ret = IRQ_NONE; unsigned int i; - for (i = 0; i < vsp1->pdata->wpf_count; ++i) { + for (i = 0; i < vsp1->pdata.wpf_count; ++i) { struct vsp1_rwpf *wpf = vsp1->wpf[i]; struct vsp1_pipeline *pipe; u32 status; @@ -181,7 +181,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->hst->entity.list_dev, &vsp1->entities); - if (vsp1->pdata->features & VSP1_HAS_LIF) { + if (vsp1->pdata.features & VSP1_HAS_LIF) { vsp1->lif = vsp1_lif_create(vsp1); if (IS_ERR(vsp1->lif)) { ret = PTR_ERR(vsp1->lif); @@ -191,7 +191,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->lif->entity.list_dev, &vsp1->entities); } - if (vsp1->pdata->features & VSP1_HAS_LUT) { + if (vsp1->pdata.features & VSP1_HAS_LUT) { vsp1->lut = vsp1_lut_create(vsp1); if (IS_ERR(vsp1->lut)) { ret = PTR_ERR(vsp1->lut); @@ -201,7 +201,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->lut->entity.list_dev, &vsp1->entities); } - for (i = 0; i < vsp1->pdata->rpf_count; ++i) { + for (i = 0; i < vsp1->pdata.rpf_count; ++i) { struct vsp1_rwpf *rpf; rpf = vsp1_rpf_create(vsp1, i); @@ -214,7 +214,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&rpf->entity.list_dev, &vsp1->entities); } - if (vsp1->pdata->features & VSP1_HAS_SRU) { + if (vsp1->pdata.features & VSP1_HAS_SRU) { vsp1->sru = vsp1_sru_create(vsp1); if (IS_ERR(vsp1->sru)) { ret = PTR_ERR(vsp1->sru); @@ -224,7 +224,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&vsp1->sru->entity.list_dev, &vsp1->entities); } - for (i = 0; i < vsp1->pdata->uds_count; ++i) { + for (i = 0; i < vsp1->pdata.uds_count; ++i) { struct vsp1_uds *uds; uds = vsp1_uds_create(vsp1, i); @@ -237,7 +237,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) list_add_tail(&uds->entity.list_dev, &vsp1->entities); } - for (i = 0; i < vsp1->pdata->wpf_count; ++i) { + for (i = 0; i < vsp1->pdata.wpf_count; ++i) { struct vsp1_rwpf *wpf; wpf = vsp1_wpf_create(vsp1, i); @@ -261,7 +261,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) goto done; } - if (vsp1->pdata->features & VSP1_HAS_LIF) { + if (vsp1->pdata.features & VSP1_HAS_LIF) { ret = media_entity_create_link( &vsp1->wpf[0]->entity.subdev.entity, RWPF_PAD_SOURCE, &vsp1->lif->entity.subdev.entity, LIF_PAD_SINK, 0); @@ -294,7 +294,7 @@ static int vsp1_device_init(struct vsp1_device *vsp1) /* Reset any channel that might be running. */ status = vsp1_read(vsp1, VI6_STATUS); - for (i = 0; i < vsp1->pdata->wpf_count; ++i) { + for (i = 0; i < vsp1->pdata.wpf_count; ++i) { unsigned int timeout; if (!(status & VI6_STATUS_SYS_ACT(i))) @@ -318,10 +318,10 @@ static int vsp1_device_init(struct vsp1_device *vsp1) vsp1_write(vsp1, VI6_CLK_DCSWT, (8 << VI6_CLK_DCSWT_CSTPW_SHIFT) | (8 << VI6_CLK_DCSWT_CSTRW_SHIFT)); - for (i = 0; i < vsp1->pdata->rpf_count; ++i) + for (i = 0; i < vsp1->pdata.rpf_count; ++i) vsp1_write(vsp1, VI6_DPR_RPF_ROUTE(i), VI6_DPR_NODE_UNUSED); - for (i = 0; i < vsp1->pdata->uds_count; ++i) + for (i = 0; i < vsp1->pdata.uds_count; ++i) vsp1_write(vsp1, VI6_DPR_UDS_ROUTE(i), VI6_DPR_NODE_UNUSED); vsp1_write(vsp1, VI6_DPR_SRU_ROUTE, VI6_DPR_NODE_UNUSED); @@ -428,28 +428,36 @@ static const struct dev_pm_ops vsp1_pm_ops = { * Platform Driver */ -static int vsp1_validate_platform_data(struct platform_device *pdev, - struct vsp1_platform_data *pdata) +static int vsp1_parse_dt(struct vsp1_device *vsp1) { - if (pdata == NULL) { - dev_err(&pdev->dev, "missing platform data\n"); - return -EINVAL; - } + struct device_node *np = vsp1->dev->of_node; + struct vsp1_platform_data *pdata = &vsp1->pdata; + + if (of_property_read_bool(np, "renesas,has-lif")) + pdata->features |= VSP1_HAS_LIF; + if (of_property_read_bool(np, "renesas,has-lut")) + pdata->features |= VSP1_HAS_LUT; + if (of_property_read_bool(np, "renesas,has-sru")) + pdata->features |= VSP1_HAS_SRU; + + of_property_read_u32(np, "renesas,#rpf", &pdata->rpf_count); + of_property_read_u32(np, "renesas,#uds", &pdata->uds_count); + of_property_read_u32(np, "renesas,#wpf", &pdata->wpf_count); if (pdata->rpf_count <= 0 || pdata->rpf_count > VSP1_MAX_RPF) { - dev_err(&pdev->dev, "invalid number of RPF (%u)\n", + dev_err(vsp1->dev, "invalid number of RPF (%u)\n", pdata->rpf_count); return -EINVAL; } if (pdata->uds_count <= 0 || pdata->uds_count > VSP1_MAX_UDS) { - dev_err(&pdev->dev, "invalid number of UDS (%u)\n", + dev_err(vsp1->dev, "invalid number of UDS (%u)\n", pdata->uds_count); return -EINVAL; } if (pdata->wpf_count <= 0 || pdata->wpf_count > VSP1_MAX_WPF) { - dev_err(&pdev->dev, "invalid number of WPF (%u)\n", + dev_err(vsp1->dev, "invalid number of WPF (%u)\n", pdata->wpf_count); return -EINVAL; } @@ -457,33 +465,6 @@ static int vsp1_validate_platform_data(struct platform_device *pdev, return 0; } -static struct vsp1_platform_data * -vsp1_get_platform_data(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - struct vsp1_platform_data *pdata; - - if (!IS_ENABLED(CONFIG_OF) || np == NULL) - return pdev->dev.platform_data; - - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (pdata == NULL) - return NULL; - - if (of_property_read_bool(np, "renesas,has-lif")) - pdata->features |= VSP1_HAS_LIF; - if (of_property_read_bool(np, "renesas,has-lut")) - pdata->features |= VSP1_HAS_LUT; - if (of_property_read_bool(np, "renesas,has-sru")) - pdata->features |= VSP1_HAS_SRU; - - of_property_read_u32(np, "renesas,#rpf", &pdata->rpf_count); - of_property_read_u32(np, "renesas,#uds", &pdata->uds_count); - of_property_read_u32(np, "renesas,#wpf", &pdata->wpf_count); - - return pdata; -} - static int vsp1_probe(struct platform_device *pdev) { struct vsp1_device *vsp1; @@ -499,11 +480,7 @@ static int vsp1_probe(struct platform_device *pdev) mutex_init(&vsp1->lock); INIT_LIST_HEAD(&vsp1->entities); - vsp1->pdata = vsp1_get_platform_data(pdev); - if (vsp1->pdata == NULL) - return -ENODEV; - - ret = vsp1_validate_platform_data(pdev, vsp1->pdata); + ret = vsp1_parse_dt(vsp1); if (ret < 0) return ret; diff --git a/drivers/media/platform/vsp1/vsp1_hsit.c b/drivers/media/platform/vsp1/vsp1_hsit.c index 80bedc554ee3..0bc0471746c9 100644 --- a/drivers/media/platform/vsp1/vsp1_hsit.c +++ b/drivers/media/platform/vsp1/vsp1_hsit.c @@ -26,11 +26,6 @@ * Device Access */ -static inline u32 vsp1_hsit_read(struct vsp1_hsit *hsit, u32 reg) -{ - return vsp1_read(hsit->entity.vsp1, reg); -} - static inline void vsp1_hsit_write(struct vsp1_hsit *hsit, u32 reg, u32 data) { vsp1_write(hsit->entity.vsp1, reg, data); diff --git a/drivers/media/platform/vsp1/vsp1_regs.h b/drivers/media/platform/vsp1/vsp1_regs.h index 55f163d32d15..da3c573e1efc 100644 --- a/drivers/media/platform/vsp1/vsp1_regs.h +++ b/drivers/media/platform/vsp1/vsp1_regs.h @@ -43,12 +43,12 @@ #define VI6_DISP_IRQ_ENB 0x0078 #define VI6_DISP_IRQ_ENB_DSTE (1 << 8) #define VI6_DISP_IRQ_ENB_MAEE (1 << 5) -#define VI6_DISP_IRQ_ENB_LNEE(n) (1 << ((n) + 4)) +#define VI6_DISP_IRQ_ENB_LNEE(n) (1 << (n)) #define VI6_DISP_IRQ_STA 0x007c #define VI6_DISP_IRQ_STA_DSE (1 << 8) #define VI6_DISP_IRQ_STA_MAE (1 << 5) -#define VI6_DISP_IRQ_STA_LNE(n) (1 << ((n) + 4)) +#define VI6_DISP_IRQ_STA_LNE(n) (1 << (n)) #define VI6_WPF_LINE_COUNT(n) (0x0084 + (n) * 4) #define VI6_WPF_LINE_COUNT_MASK (0x1fffff << 0) diff --git a/drivers/media/platform/vsp1/vsp1_rpf.c b/drivers/media/platform/vsp1/vsp1_rpf.c index d14d26b718ef..3294529a3108 100644 --- a/drivers/media/platform/vsp1/vsp1_rpf.c +++ b/drivers/media/platform/vsp1/vsp1_rpf.c @@ -106,11 +106,22 @@ static int rpf_s_stream(struct v4l2_subdev *subdev, int enable) + crop->left * fmtinfo->bpp[0] / 8; pstride = format->plane_fmt[0].bytesperline << VI6_RPF_SRCM_PSTRIDE_Y_SHIFT; + + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, + rpf->buf_addr[0] + rpf->offsets[0]); + if (format->num_planes > 1) { rpf->offsets[1] = crop->top * format->plane_fmt[1].bytesperline + crop->left * fmtinfo->bpp[1] / 8; pstride |= format->plane_fmt[1].bytesperline << VI6_RPF_SRCM_PSTRIDE_C_SHIFT; + + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C0, + rpf->buf_addr[1] + rpf->offsets[1]); + + if (format->num_planes > 2) + vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_C1, + rpf->buf_addr[2] + rpf->offsets[1]); } vsp1_rpf_write(rpf, VI6_RPF_SRCM_PSTRIDE, pstride); @@ -179,6 +190,13 @@ static void rpf_vdev_queue(struct vsp1_video *video, struct vsp1_video_buffer *buf) { struct vsp1_rwpf *rpf = container_of(video, struct vsp1_rwpf, video); + unsigned int i; + + for (i = 0; i < 3; ++i) + rpf->buf_addr[i] = buf->addr[i]; + + if (!vsp1_entity_is_streaming(&rpf->entity)) + return; vsp1_rpf_write(rpf, VI6_RPF_SRCM_ADDR_Y, buf->addr[0] + rpf->offsets[0]); diff --git a/drivers/media/platform/vsp1/vsp1_rwpf.h b/drivers/media/platform/vsp1/vsp1_rwpf.h index 28dd9e7b3838..2cf1f13d3bf9 100644 --- a/drivers/media/platform/vsp1/vsp1_rwpf.h +++ b/drivers/media/platform/vsp1/vsp1_rwpf.h @@ -39,6 +39,7 @@ struct vsp1_rwpf { struct v4l2_rect crop; unsigned int offsets[2]; + dma_addr_t buf_addr[3]; }; static inline struct vsp1_rwpf *to_rwpf(struct v4l2_subdev *subdev) diff --git a/drivers/media/platform/vsp1/vsp1_wpf.c b/drivers/media/platform/vsp1/vsp1_wpf.c index 6e057762c933..1d2b3a2f1573 100644 --- a/drivers/media/platform/vsp1/vsp1_wpf.c +++ b/drivers/media/platform/vsp1/vsp1_wpf.c @@ -92,19 +92,20 @@ static int wpf_s_stream(struct v4l2_subdev *subdev, int enable) return 0; } - /* Sources. If the pipeline has a single input configure it as the - * master layer. Otherwise configure all inputs as sub-layers and - * select the virtual RPF as the master layer. + /* Sources. If the pipeline has a single input and BRU is not used, + * configure it as the master layer. Otherwise configure all + * inputs as sub-layers and select the virtual RPF as the master + * layer. */ for (i = 0; i < pipe->num_inputs; ++i) { struct vsp1_rwpf *input = pipe->inputs[i]; - srcrpf |= pipe->num_inputs == 1 + srcrpf |= (!pipe->bru && pipe->num_inputs == 1) ? VI6_WPF_SRCRPF_RPF_ACT_MST(input->entity.index) : VI6_WPF_SRCRPF_RPF_ACT_SUB(input->entity.index); } - if (pipe->num_inputs > 1) + if (pipe->bru || pipe->num_inputs > 1) srcrpf |= VI6_WPF_SRCRPF_VIRACT_MST; vsp1_wpf_write(wpf, VI6_WPF_SRCRPF, srcrpf); @@ -280,7 +281,7 @@ struct vsp1_rwpf *vsp1_wpf_create(struct vsp1_device *vsp1, unsigned int index) * except for the WPF0 source link if a LIF is present. */ flags = MEDIA_LNK_FL_ENABLED; - if (!(vsp1->pdata->features & VSP1_HAS_LIF) || index != 0) + if (!(vsp1->pdata.features & VSP1_HAS_LIF) || index != 0) flags |= MEDIA_LNK_FL_IMMUTABLE; ret = media_entity_create_link(&wpf->entity.subdev.entity, diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c index a739ad492e7b..ea9308796741 100644 --- a/drivers/media/radio/radio-aimslab.c +++ b/drivers/media/radio/radio-aimslab.c @@ -129,11 +129,11 @@ static int rtrack_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) } else if (curvol < vol) { outb(0x98, isa->io); /* volume up + sigstr + on */ for (; curvol < vol; curvol++) - udelay(3000); + mdelay(3); } else if (curvol > vol) { outb(0x58, isa->io); /* volume down + sigstr + on */ for (; curvol > vol; curvol--) - udelay(3000); + mdelay(3); } outb(0xd8, isa->io); /* volume steady + sigstr + on */ rt->curvol = vol; diff --git a/drivers/media/radio/tea575x.c b/drivers/media/radio/tea575x.c index f1a0867789fe..43d1ea53cb66 100644 --- a/drivers/media/radio/tea575x.c +++ b/drivers/media/radio/tea575x.c @@ -247,10 +247,9 @@ static int vidioc_querycap(struct file *file, void *priv, return 0; } -static int vidioc_enum_freq_bands(struct file *file, void *priv, - struct v4l2_frequency_band *band) +int snd_tea575x_enum_freq_bands(struct snd_tea575x *tea, + struct v4l2_frequency_band *band) { - struct snd_tea575x *tea = video_drvdata(file); int index; if (band->tuner != 0) @@ -279,18 +278,25 @@ static int vidioc_enum_freq_bands(struct file *file, void *priv, return 0; } +EXPORT_SYMBOL(snd_tea575x_enum_freq_bands); -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *v) +static int vidioc_enum_freq_bands(struct file *file, void *priv, + struct v4l2_frequency_band *band) { struct snd_tea575x *tea = video_drvdata(file); + + return snd_tea575x_enum_freq_bands(tea, band); +} + +int snd_tea575x_g_tuner(struct snd_tea575x *tea, struct v4l2_tuner *v) +{ struct v4l2_frequency_band band_fm = { 0, }; if (v->index > 0) return -EINVAL; snd_tea575x_read(tea); - vidioc_enum_freq_bands(file, priv, &band_fm); + snd_tea575x_enum_freq_bands(tea, &band_fm); memset(v, 0, sizeof(*v)); strlcpy(v->name, tea->has_am ? "FM/AM" : "FM", sizeof(v->name)); @@ -304,6 +310,15 @@ static int vidioc_g_tuner(struct file *file, void *priv, v->signal = tea->tuned ? 0xffff : 0; return 0; } +EXPORT_SYMBOL(snd_tea575x_g_tuner); + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + struct snd_tea575x *tea = video_drvdata(file); + + return snd_tea575x_g_tuner(tea, v); +} static int vidioc_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *v) @@ -356,10 +371,9 @@ static int vidioc_s_frequency(struct file *file, void *priv, return 0; } -static int vidioc_s_hw_freq_seek(struct file *file, void *fh, - const struct v4l2_hw_freq_seek *a) +int snd_tea575x_s_hw_freq_seek(struct file *file, struct snd_tea575x *tea, + const struct v4l2_hw_freq_seek *a) { - struct snd_tea575x *tea = video_drvdata(file); unsigned long timeout; int i, spacing; @@ -442,6 +456,15 @@ static int vidioc_s_hw_freq_seek(struct file *file, void *fh, snd_tea575x_set_freq(tea); return -ENODATA; } +EXPORT_SYMBOL(snd_tea575x_s_hw_freq_seek); + +static int vidioc_s_hw_freq_seek(struct file *file, void *fh, + const struct v4l2_hw_freq_seek *a) +{ + struct snd_tea575x *tea = video_drvdata(file); + + return snd_tea575x_s_hw_freq_seek(file, tea, a); +} static int tea575x_s_ctrl(struct v4l2_ctrl *ctrl) { diff --git a/drivers/media/radio/wl128x/fmdrv_rx.c b/drivers/media/radio/wl128x/fmdrv_rx.c index 09632cb26cb6..cfaeb2417fbb 100644 --- a/drivers/media/radio/wl128x/fmdrv_rx.c +++ b/drivers/media/radio/wl128x/fmdrv_rx.c @@ -785,22 +785,6 @@ int fm_rx_set_rds_system(struct fmdev *fmdev, u8 rds_mode) return 0; } -/* Returns current RDS operation mode */ -int fm_rx_get_rds_system(struct fmdev *fmdev, u8 *rds_mode) -{ - if (fmdev->curr_fmmode != FM_MODE_RX) - return -EPERM; - - if (rds_mode == NULL) { - fmerr("Invalid memory\n"); - return -ENOMEM; - } - - *rds_mode = fmdev->rx.rds_mode; - - return 0; -} - /* Configures Alternate Frequency switch mode */ int fm_rx_set_af_switch(struct fmdev *fmdev, u8 af_mode) { diff --git a/drivers/media/radio/wl128x/fmdrv_rx.h b/drivers/media/radio/wl128x/fmdrv_rx.h index 32add81f8d87..23922188882f 100644 --- a/drivers/media/radio/wl128x/fmdrv_rx.h +++ b/drivers/media/radio/wl128x/fmdrv_rx.h @@ -40,7 +40,6 @@ void fm_rx_reset_station_info(struct fmdev *); int fm_rx_seek(struct fmdev *, u32, u32, u32); int fm_rx_get_rds_mode(struct fmdev *, u8 *); -int fm_rx_get_rds_system(struct fmdev *, u8 *); int fm_rx_get_mute_mode(struct fmdev *, u8 *); int fm_rx_get_volume(struct fmdev *, u16 *); int fm_rx_get_band_freq_range(struct fmdev *, diff --git a/drivers/media/rc/img-ir/Kconfig b/drivers/media/rc/img-ir/Kconfig index 580715c7fc5e..a896d3c83a1c 100644 --- a/drivers/media/rc/img-ir/Kconfig +++ b/drivers/media/rc/img-ir/Kconfig @@ -60,3 +60,18 @@ config IR_IMG_SANYO help Say Y here to enable support for the Sanyo protocol (used by Sanyo, Aiwa, Chinon remotes) in the ImgTec infrared decoder block. + +config IR_IMG_RC5 + bool "Philips RC5 protocol support" + depends on IR_IMG_HW + help + Say Y here to enable support for the RC5 protocol in the ImgTec + infrared decoder block. + +config IR_IMG_RC6 + bool "Philips RC6 protocol support" + depends on IR_IMG_HW + help + Say Y here to enable support for the RC6 protocol in the ImgTec + infrared decoder block. + Note: This version only supports mode 0. diff --git a/drivers/media/rc/img-ir/Makefile b/drivers/media/rc/img-ir/Makefile index 92a459d99509..8e6d458e66ad 100644 --- a/drivers/media/rc/img-ir/Makefile +++ b/drivers/media/rc/img-ir/Makefile @@ -6,6 +6,8 @@ img-ir-$(CONFIG_IR_IMG_JVC) += img-ir-jvc.o img-ir-$(CONFIG_IR_IMG_SONY) += img-ir-sony.o img-ir-$(CONFIG_IR_IMG_SHARP) += img-ir-sharp.o img-ir-$(CONFIG_IR_IMG_SANYO) += img-ir-sanyo.o +img-ir-$(CONFIG_IR_IMG_RC5) += img-ir-rc5.o +img-ir-$(CONFIG_IR_IMG_RC6) += img-ir-rc6.o img-ir-objs := $(img-ir-y) obj-$(CONFIG_IR_IMG) += img-ir.o diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c index 2fd47c9bf5d8..7bb71bc9f534 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.c +++ b/drivers/media/rc/img-ir/img-ir-hw.c @@ -42,6 +42,12 @@ static struct img_ir_decoder *img_ir_decoders[] = { #ifdef CONFIG_IR_IMG_SANYO &img_ir_sanyo, #endif +#ifdef CONFIG_IR_IMG_RC5 + &img_ir_rc5, +#endif +#ifdef CONFIG_IR_IMG_RC6 + &img_ir_rc6, +#endif NULL }; @@ -52,6 +58,11 @@ static struct img_ir_decoder *img_ir_decoders[] = { #define IMG_IR_QUIRK_CODE_BROKEN 0x1 /* Decode is broken */ #define IMG_IR_QUIRK_CODE_LEN_INCR 0x2 /* Bit length needs increment */ +/* + * The decoder generates rapid interrupts without actually having + * received any new data after an incomplete IR code is decoded. + */ +#define IMG_IR_QUIRK_CODE_IRQ 0x4 /* functions for preprocessing timings, ensuring max is set */ @@ -542,6 +553,7 @@ static void img_ir_set_decoder(struct img_ir_priv *priv, */ spin_unlock_irq(&priv->lock); del_timer_sync(&hw->end_timer); + del_timer_sync(&hw->suspend_timer); spin_lock_irq(&priv->lock); hw->stopping = false; @@ -806,20 +818,24 @@ static void img_ir_handle_data(struct img_ir_priv *priv, u32 len, u64 raw) struct img_ir_priv_hw *hw = &priv->hw; const struct img_ir_decoder *dec = hw->decoder; int ret = IMG_IR_SCANCODE; - u32 scancode; - enum rc_type protocol = RC_TYPE_UNKNOWN; + struct img_ir_scancode_req request; + + request.protocol = RC_TYPE_UNKNOWN; + request.toggle = 0; if (dec->scancode) - ret = dec->scancode(len, raw, &protocol, &scancode, hw->enabled_protocols); + ret = dec->scancode(len, raw, hw->enabled_protocols, &request); else if (len >= 32) - scancode = (u32)raw; + request.scancode = (u32)raw; else if (len < 32) - scancode = (u32)raw & ((1 << len)-1); + request.scancode = (u32)raw & ((1 << len)-1); dev_dbg(priv->dev, "data (%u bits) = %#llx\n", len, (unsigned long long)raw); if (ret == IMG_IR_SCANCODE) { - dev_dbg(priv->dev, "decoded scan code %#x\n", scancode); - rc_keydown(hw->rdev, protocol, scancode, 0); + dev_dbg(priv->dev, "decoded scan code %#x, toggle %u\n", + request.scancode, request.toggle); + rc_keydown(hw->rdev, request.protocol, request.scancode, + request.toggle); img_ir_end_repeat(priv); } else if (ret == IMG_IR_REPEATCODE) { if (hw->mode == IMG_IR_M_REPEATING) { @@ -857,6 +873,29 @@ static void img_ir_end_timer(unsigned long arg) spin_unlock_irq(&priv->lock); } +/* + * Timer function to re-enable the current protocol after it had been + * cleared when invalid interrupts were generated due to a quirk in the + * img-ir decoder. + */ +static void img_ir_suspend_timer(unsigned long arg) +{ + struct img_ir_priv *priv = (struct img_ir_priv *)arg; + + spin_lock_irq(&priv->lock); + /* + * Don't overwrite enabled valid/match IRQs if they have already been + * changed by e.g. a filter change. + */ + if ((priv->hw.quirk_suspend_irq & IMG_IR_IRQ_EDGE) == + img_ir_read(priv, IMG_IR_IRQ_ENABLE)) + img_ir_write(priv, IMG_IR_IRQ_ENABLE, + priv->hw.quirk_suspend_irq); + /* enable */ + img_ir_write(priv, IMG_IR_CONTROL, priv->hw.reg_timings.ctrl); + spin_unlock_irq(&priv->lock); +} + #ifdef CONFIG_COMMON_CLK static void img_ir_change_frequency(struct img_ir_priv *priv, struct clk_notifier_data *change) @@ -922,15 +961,38 @@ void img_ir_isr_hw(struct img_ir_priv *priv, u32 irq_status) if (!hw->decoder) return; + ct = hw->decoder->control.code_type; + ir_status = img_ir_read(priv, IMG_IR_STATUS); - if (!(ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2))) + if (!(ir_status & (IMG_IR_RXDVAL | IMG_IR_RXDVALD2))) { + if (!(priv->hw.ct_quirks[ct] & IMG_IR_QUIRK_CODE_IRQ) || + hw->stopping) + return; + /* + * The below functionality is added as a work around to stop + * multiple Interrupts generated when an incomplete IR code is + * received by the decoder. + * The decoder generates rapid interrupts without actually + * having received any new data. After a single interrupt it's + * expected to clear up, but instead multiple interrupts are + * rapidly generated. only way to get out of this loop is to + * reset the control register after a short delay. + */ + img_ir_write(priv, IMG_IR_CONTROL, 0); + hw->quirk_suspend_irq = img_ir_read(priv, IMG_IR_IRQ_ENABLE); + img_ir_write(priv, IMG_IR_IRQ_ENABLE, + hw->quirk_suspend_irq & IMG_IR_IRQ_EDGE); + + /* Timer activated to re-enable the protocol. */ + mod_timer(&hw->suspend_timer, + jiffies + msecs_to_jiffies(5)); return; + } ir_status &= ~(IMG_IR_RXDVAL | IMG_IR_RXDVALD2); img_ir_write(priv, IMG_IR_STATUS, ir_status); len = (ir_status & IMG_IR_RXDLEN) >> IMG_IR_RXDLEN_SHIFT; /* some versions report wrong length for certain code types */ - ct = hw->decoder->control.code_type; if (hw->ct_quirks[ct] & IMG_IR_QUIRK_CODE_LEN_INCR) ++len; @@ -972,7 +1034,7 @@ static void img_ir_probe_hw_caps(struct img_ir_priv *priv) hw->ct_quirks[IMG_IR_CODETYPE_PULSELEN] |= IMG_IR_QUIRK_CODE_LEN_INCR; hw->ct_quirks[IMG_IR_CODETYPE_BIPHASE] - |= IMG_IR_QUIRK_CODE_BROKEN; + |= IMG_IR_QUIRK_CODE_IRQ; hw->ct_quirks[IMG_IR_CODETYPE_2BITPULSEPOS] |= IMG_IR_QUIRK_CODE_BROKEN; } @@ -991,6 +1053,8 @@ int img_ir_probe_hw(struct img_ir_priv *priv) /* Set up the end timer */ setup_timer(&hw->end_timer, img_ir_end_timer, (unsigned long)priv); + setup_timer(&hw->suspend_timer, img_ir_suspend_timer, + (unsigned long)priv); /* Register a clock notifier */ if (!IS_ERR(priv->clk)) { diff --git a/drivers/media/rc/img-ir/img-ir-hw.h b/drivers/media/rc/img-ir/img-ir-hw.h index 5c2b216c5fe3..91a297731661 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.h +++ b/drivers/media/rc/img-ir/img-ir-hw.h @@ -133,6 +133,20 @@ struct img_ir_timing_regvals { #define IMG_IR_REPEATCODE 1 /* repeat the previous code */ /** + * struct img_ir_scancode_req - Scancode request data. + * @protocol: Protocol code of received message (defaults to + * RC_TYPE_UNKNOWN). + * @scancode: Scan code of received message (must be written by + * handler if IMG_IR_SCANCODE is returned). + * @toggle: Toggle bit (defaults to 0). + */ +struct img_ir_scancode_req { + enum rc_type protocol; + u32 scancode; + u8 toggle; +}; + +/** * struct img_ir_decoder - Decoder settings for an IR protocol. * @type: Protocol types bitmap. * @tolerance: Timing tolerance as a percentage (default 10%). @@ -162,8 +176,8 @@ struct img_ir_decoder { struct img_ir_control control; /* scancode logic */ - int (*scancode)(int len, u64 raw, enum rc_type *protocol, - u32 *scancode, u64 enabled_protocols); + int (*scancode)(int len, u64 raw, u64 enabled_protocols, + struct img_ir_scancode_req *request); int (*filter)(const struct rc_scancode_filter *in, struct img_ir_filter *out, u64 protocols); }; @@ -173,6 +187,8 @@ extern struct img_ir_decoder img_ir_jvc; extern struct img_ir_decoder img_ir_sony; extern struct img_ir_decoder img_ir_sharp; extern struct img_ir_decoder img_ir_sanyo; +extern struct img_ir_decoder img_ir_rc5; +extern struct img_ir_decoder img_ir_rc6; /** * struct img_ir_reg_timings - Reg values for decoder timings at clock rate. @@ -204,6 +220,7 @@ enum img_ir_mode { * @rdev: Remote control device * @clk_nb: Notifier block for clock notify events. * @end_timer: Timer until repeat timeout. + * @suspend_timer: Timer to re-enable protocol. * @decoder: Current decoder settings. * @enabled_protocols: Currently enabled protocols. * @clk_hz: Current core clock rate in Hz. @@ -214,12 +231,14 @@ enum img_ir_mode { * @stopping: Indicates that decoder is being taken down and timers * should not be restarted. * @suspend_irqen: Saved IRQ enable mask over suspend. + * @quirk_suspend_irq: Saved IRQ enable mask over quirk suspend timer. */ struct img_ir_priv_hw { unsigned int ct_quirks[4]; struct rc_dev *rdev; struct notifier_block clk_nb; struct timer_list end_timer; + struct timer_list suspend_timer; const struct img_ir_decoder *decoder; u64 enabled_protocols; unsigned long clk_hz; @@ -230,6 +249,7 @@ struct img_ir_priv_hw { enum img_ir_mode mode; bool stopping; u32 suspend_irqen; + u32 quirk_suspend_irq; }; static inline bool img_ir_hw_enabled(struct img_ir_priv_hw *hw) diff --git a/drivers/media/rc/img-ir/img-ir-jvc.c b/drivers/media/rc/img-ir/img-ir-jvc.c index a60dda8bf706..d3e2fc0bcfe1 100644 --- a/drivers/media/rc/img-ir/img-ir-jvc.c +++ b/drivers/media/rc/img-ir/img-ir-jvc.c @@ -12,8 +12,8 @@ #include "img-ir-hw.h" /* Convert JVC data to a scancode */ -static int img_ir_jvc_scancode(int len, u64 raw, enum rc_type *protocol, - u32 *scancode, u64 enabled_protocols) +static int img_ir_jvc_scancode(int len, u64 raw, u64 enabled_protocols, + struct img_ir_scancode_req *request) { unsigned int cust, data; @@ -23,8 +23,8 @@ static int img_ir_jvc_scancode(int len, u64 raw, enum rc_type *protocol, cust = (raw >> 0) & 0xff; data = (raw >> 8) & 0xff; - *protocol = RC_TYPE_JVC; - *scancode = cust << 8 | data; + request->protocol = RC_TYPE_JVC; + request->scancode = cust << 8 | data; return IMG_IR_SCANCODE; } diff --git a/drivers/media/rc/img-ir/img-ir-nec.c b/drivers/media/rc/img-ir/img-ir-nec.c index 739897549b5b..27a7ea8f1260 100644 --- a/drivers/media/rc/img-ir/img-ir-nec.c +++ b/drivers/media/rc/img-ir/img-ir-nec.c @@ -13,8 +13,8 @@ #include <linux/bitrev.h> /* Convert NEC data to a scancode */ -static int img_ir_nec_scancode(int len, u64 raw, enum rc_type *protocol, - u32 *scancode, u64 enabled_protocols) +static int img_ir_nec_scancode(int len, u64 raw, u64 enabled_protocols, + struct img_ir_scancode_req *request) { unsigned int addr, addr_inv, data, data_inv; /* a repeat code has no data */ @@ -30,23 +30,23 @@ static int img_ir_nec_scancode(int len, u64 raw, enum rc_type *protocol, if ((data_inv ^ data) != 0xff) { /* 32-bit NEC (used by Apple and TiVo remotes) */ /* scan encoding: as transmitted, MSBit = first received bit */ - *scancode = bitrev8(addr) << 24 | - bitrev8(addr_inv) << 16 | - bitrev8(data) << 8 | - bitrev8(data_inv); + request->scancode = bitrev8(addr) << 24 | + bitrev8(addr_inv) << 16 | + bitrev8(data) << 8 | + bitrev8(data_inv); } else if ((addr_inv ^ addr) != 0xff) { /* Extended NEC */ /* scan encoding: AAaaDD */ - *scancode = addr << 16 | - addr_inv << 8 | - data; + request->scancode = addr << 16 | + addr_inv << 8 | + data; } else { /* Normal NEC */ /* scan encoding: AADD */ - *scancode = addr << 8 | - data; + request->scancode = addr << 8 | + data; } - *protocol = RC_TYPE_NEC; + request->protocol = RC_TYPE_NEC; return IMG_IR_SCANCODE; } diff --git a/drivers/media/rc/img-ir/img-ir-rc5.c b/drivers/media/rc/img-ir/img-ir-rc5.c new file mode 100644 index 000000000000..a8a28a377eee --- /dev/null +++ b/drivers/media/rc/img-ir/img-ir-rc5.c @@ -0,0 +1,88 @@ +/* + * ImgTec IR Decoder setup for Philips RC-5 protocol. + * + * Copyright 2012-2014 Imagination Technologies Ltd. + * + * 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 "img-ir-hw.h" + +/* Convert RC5 data to a scancode */ +static int img_ir_rc5_scancode(int len, u64 raw, u64 enabled_protocols, + struct img_ir_scancode_req *request) +{ + unsigned int addr, cmd, tgl, start; + + /* Quirk in the decoder shifts everything by 2 to the left. */ + raw >>= 2; + + start = (raw >> 13) & 0x01; + tgl = (raw >> 11) & 0x01; + addr = (raw >> 6) & 0x1f; + cmd = raw & 0x3f; + /* + * 12th bit is used to extend the command in extended RC5 and has + * no effect on standard RC5. + */ + cmd += ((raw >> 12) & 0x01) ? 0 : 0x40; + + if (!start) + return -EINVAL; + + request->protocol = RC_TYPE_RC5; + request->scancode = addr << 8 | cmd; + request->toggle = tgl; + return IMG_IR_SCANCODE; +} + +/* Convert RC5 scancode to RC5 data filter */ +static int img_ir_rc5_filter(const struct rc_scancode_filter *in, + struct img_ir_filter *out, u64 protocols) +{ + /* Not supported by the hw. */ + return -EINVAL; +} + +/* + * RC-5 decoder + * see http://www.sbprojects.com/knowledge/ir/rc5.php + */ +struct img_ir_decoder img_ir_rc5 = { + .type = RC_BIT_RC5, + .control = { + .bitoriend2 = 1, + .code_type = IMG_IR_CODETYPE_BIPHASE, + .decodend2 = 1, + }, + /* main timings */ + .tolerance = 16, + .unit = 888888, /* 1/36k*32=888.888microseconds */ + .timings = { + /* 10 symbol */ + .s10 = { + .pulse = { 1 }, + .space = { 1 }, + }, + + /* 11 symbol */ + .s11 = { + .pulse = { 1 }, + .space = { 1 }, + }, + + /* free time */ + .ft = { + .minlen = 14, + .maxlen = 14, + .ft_min = 5, + }, + }, + + /* scancode logic */ + .scancode = img_ir_rc5_scancode, + .filter = img_ir_rc5_filter, +}; diff --git a/drivers/media/rc/img-ir/img-ir-rc6.c b/drivers/media/rc/img-ir/img-ir-rc6.c new file mode 100644 index 000000000000..de1e27534968 --- /dev/null +++ b/drivers/media/rc/img-ir/img-ir-rc6.c @@ -0,0 +1,117 @@ +/* + * ImgTec IR Decoder setup for Philips RC-6 protocol. + * + * Copyright 2012-2014 Imagination Technologies Ltd. + * + * 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 "img-ir-hw.h" + +/* Convert RC6 data to a scancode */ +static int img_ir_rc6_scancode(int len, u64 raw, u64 enabled_protocols, + struct img_ir_scancode_req *request) +{ + unsigned int addr, cmd, mode, trl1, trl2; + + /* + * Due to a side effect of the decoder handling the double length + * Trailer bit, the header information is a bit scrambled, and the + * raw data is shifted incorrectly. + * This workaround effectively recovers the header bits. + * + * The Header field should look like this: + * + * StartBit ModeBit2 ModeBit1 ModeBit0 TrailerBit + * + * But what we get is: + * + * ModeBit2 ModeBit1 ModeBit0 TrailerBit1 TrailerBit2 + * + * The start bit is not important to recover the scancode. + */ + + raw >>= 27; + + trl1 = (raw >> 17) & 0x01; + trl2 = (raw >> 16) & 0x01; + + mode = (raw >> 18) & 0x07; + addr = (raw >> 8) & 0xff; + cmd = raw & 0xff; + + /* + * Due to the above explained irregularity the trailer bits cannot + * have the same value. + */ + if (trl1 == trl2) + return -EINVAL; + + /* Only mode 0 supported for now */ + if (mode) + return -EINVAL; + + request->protocol = RC_TYPE_RC6_0; + request->scancode = addr << 8 | cmd; + request->toggle = trl2; + return IMG_IR_SCANCODE; +} + +/* Convert RC6 scancode to RC6 data filter */ +static int img_ir_rc6_filter(const struct rc_scancode_filter *in, + struct img_ir_filter *out, u64 protocols) +{ + /* Not supported by the hw. */ + return -EINVAL; +} + +/* + * RC-6 decoder + * see http://www.sbprojects.com/knowledge/ir/rc6.php + */ +struct img_ir_decoder img_ir_rc6 = { + .type = RC_BIT_RC6_0, + .control = { + .bitorien = 1, + .code_type = IMG_IR_CODETYPE_BIPHASE, + .decoden = 1, + .decodinpol = 1, + }, + /* main timings */ + .tolerance = 20, + /* + * Due to a quirk in the img-ir decoder, default header values do + * not work, the values described below were extracted from + * successful RTL test cases. + */ + .timings = { + /* leader symbol */ + .ldr = { + .pulse = { 650 }, + .space = { 660 }, + }, + /* 0 symbol */ + .s00 = { + .pulse = { 370 }, + .space = { 370 }, + }, + /* 01 symbol */ + .s01 = { + .pulse = { 370 }, + .space = { 370 }, + }, + /* free time */ + .ft = { + .minlen = 21, + .maxlen = 21, + .ft_min = 2666, /* 2.666 ms */ + }, + }, + + /* scancode logic */ + .scancode = img_ir_rc6_scancode, + .filter = img_ir_rc6_filter, +}; diff --git a/drivers/media/rc/img-ir/img-ir-sanyo.c b/drivers/media/rc/img-ir/img-ir-sanyo.c index 6b0653ecdf5a..f394994ffc22 100644 --- a/drivers/media/rc/img-ir/img-ir-sanyo.c +++ b/drivers/media/rc/img-ir/img-ir-sanyo.c @@ -23,8 +23,8 @@ #include "img-ir-hw.h" /* Convert Sanyo data to a scancode */ -static int img_ir_sanyo_scancode(int len, u64 raw, enum rc_type *protocol, - u32 *scancode, u64 enabled_protocols) +static int img_ir_sanyo_scancode(int len, u64 raw, u64 enabled_protocols, + struct img_ir_scancode_req *request) { unsigned int addr, addr_inv, data, data_inv; /* a repeat code has no data */ @@ -44,8 +44,8 @@ static int img_ir_sanyo_scancode(int len, u64 raw, enum rc_type *protocol, return -EINVAL; /* Normal Sanyo */ - *protocol = RC_TYPE_SANYO; - *scancode = addr << 8 | data; + request->protocol = RC_TYPE_SANYO; + request->scancode = addr << 8 | data; return IMG_IR_SCANCODE; } diff --git a/drivers/media/rc/img-ir/img-ir-sharp.c b/drivers/media/rc/img-ir/img-ir-sharp.c index 3300a38802ac..fe5acc4f030e 100644 --- a/drivers/media/rc/img-ir/img-ir-sharp.c +++ b/drivers/media/rc/img-ir/img-ir-sharp.c @@ -12,8 +12,8 @@ #include "img-ir-hw.h" /* Convert Sharp data to a scancode */ -static int img_ir_sharp_scancode(int len, u64 raw, enum rc_type *protocol, - u32 *scancode, u64 enabled_protocols) +static int img_ir_sharp_scancode(int len, u64 raw, u64 enabled_protocols, + struct img_ir_scancode_req *request) { unsigned int addr, cmd, exp, chk; @@ -32,8 +32,8 @@ static int img_ir_sharp_scancode(int len, u64 raw, enum rc_type *protocol, /* probably the second half of the message */ return -EINVAL; - *protocol = RC_TYPE_SHARP; - *scancode = addr << 8 | cmd; + request->protocol = RC_TYPE_SHARP; + request->scancode = addr << 8 | cmd; return IMG_IR_SCANCODE; } diff --git a/drivers/media/rc/img-ir/img-ir-sony.c b/drivers/media/rc/img-ir/img-ir-sony.c index 3a0f17b0752c..7f7375f82ed6 100644 --- a/drivers/media/rc/img-ir/img-ir-sony.c +++ b/drivers/media/rc/img-ir/img-ir-sony.c @@ -12,8 +12,8 @@ #include "img-ir-hw.h" /* Convert Sony data to a scancode */ -static int img_ir_sony_scancode(int len, u64 raw, enum rc_type *protocol, - u32 *scancode, u64 enabled_protocols) +static int img_ir_sony_scancode(int len, u64 raw, u64 enabled_protocols, + struct img_ir_scancode_req *request) { unsigned int dev, subdev, func; @@ -25,7 +25,7 @@ static int img_ir_sony_scancode(int len, u64 raw, enum rc_type *protocol, raw >>= 7; dev = raw & 0x1f; /* next 5 bits */ subdev = 0; - *protocol = RC_TYPE_SONY12; + request->protocol = RC_TYPE_SONY12; break; case 15: if (!(enabled_protocols & RC_BIT_SONY15)) @@ -34,7 +34,7 @@ static int img_ir_sony_scancode(int len, u64 raw, enum rc_type *protocol, raw >>= 7; dev = raw & 0xff; /* next 8 bits */ subdev = 0; - *protocol = RC_TYPE_SONY15; + request->protocol = RC_TYPE_SONY15; break; case 20: if (!(enabled_protocols & RC_BIT_SONY20)) @@ -44,12 +44,12 @@ static int img_ir_sony_scancode(int len, u64 raw, enum rc_type *protocol, dev = raw & 0x1f; /* next 5 bits */ raw >>= 5; subdev = raw & 0xff; /* next 8 bits */ - *protocol = RC_TYPE_SONY20; + request->protocol = RC_TYPE_SONY20; break; default: return -EINVAL; } - *scancode = dev << 16 | subdev << 8 | func; + request->scancode = dev << 16 | subdev << 8 | func; return IMG_IR_SCANCODE; } diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 1e0545a67959..4de0e85af805 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -553,14 +553,14 @@ unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait) if (!ir->attached) return POLLERR; - poll_wait(file, &ir->buf->wait_poll, wait); + if (ir->buf) { + poll_wait(file, &ir->buf->wait_poll, wait); - if (ir->buf) if (lirc_buffer_empty(ir->buf)) ret = 0; else ret = POLLIN | POLLRDNORM; - else + } else ret = POLLERR; dev_dbg(ir->d.dev, LOGHEAD "poll result = %d\n", diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 86ffcd54339e..f8c5e47a30aa 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -1021,16 +1021,16 @@ static ssize_t store_protocols(struct device *device, goto out; } - if (new_protocols == old_protocols) { - rc = len; - goto out; + if (new_protocols != old_protocols) { + *current_protocols = new_protocols; + IR_dprintk(1, "Protocols changed to 0x%llx\n", + (long long)new_protocols); } - *current_protocols = new_protocols; - IR_dprintk(1, "Protocols changed to 0x%llx\n", (long long)new_protocols); - /* - * If the protocol is changed the filter needs updating. + * If a protocol change was attempted the filter may need updating, even + * if the actual protocol mask hasn't changed (since the driver may have + * cleared the filter). * Try setting the same filter with the new protocol (if any). * Fall back to clearing the filter. */ diff --git a/drivers/media/rc/sunxi-cir.c b/drivers/media/rc/sunxi-cir.c index 340f7f51eed4..7830aef3db45 100644 --- a/drivers/media/rc/sunxi-cir.c +++ b/drivers/media/rc/sunxi-cir.c @@ -23,6 +23,7 @@ #include <linux/interrupt.h> #include <linux/module.h> #include <linux/of_platform.h> +#include <linux/reset.h> #include <media/rc-core.h> #define SUNXI_IR_DEV "sunxi-ir" @@ -55,12 +56,12 @@ #define REG_RXINT_RAI_EN BIT(4) /* Rx FIFO available byte level */ -#define REG_RXINT_RAL(val) (((val) << 8) & (GENMASK(11, 8))) +#define REG_RXINT_RAL(val) ((val) << 8) /* Rx Interrupt Status */ #define SUNXI_IR_RXSTA_REG 0x30 /* RX FIFO Get Available Counter */ -#define REG_RXSTA_GET_AC(val) (((val) >> 8) & (GENMASK(5, 0))) +#define REG_RXSTA_GET_AC(val) (((val) >> 8) & (ir->fifo_size * 2 - 1)) /* Clear all interrupt status value */ #define REG_RXSTA_CLEARALL 0xff @@ -71,10 +72,6 @@ /* CIR_REG register idle threshold */ #define REG_CIR_ITHR(val) (((val) << 8) & (GENMASK(15, 8))) -/* Hardware supported fifo size */ -#define SUNXI_IR_FIFO_SIZE 16 -/* How many messages in FIFO trigger IRQ */ -#define TRIGGER_LEVEL 8 /* Required frequency for IR0 or IR1 clock in CIR mode */ #define SUNXI_IR_BASE_CLK 8000000 /* Frequency after IR internal divider */ @@ -93,8 +90,10 @@ struct sunxi_ir { struct rc_dev *rc; void __iomem *base; int irq; + int fifo_size; struct clk *clk; struct clk *apb_clk; + struct reset_control *rst; const char *map_name; }; @@ -113,11 +112,11 @@ static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id) /* clean all pending statuses */ writel(status | REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG); - if (status & REG_RXINT_RAI_EN) { + if (status & (REG_RXINT_RAI_EN | REG_RXINT_RPEI_EN)) { /* How many messages in fifo */ rc = REG_RXSTA_GET_AC(status); /* Sanity check */ - rc = rc > SUNXI_IR_FIFO_SIZE ? SUNXI_IR_FIFO_SIZE : rc; + rc = rc > ir->fifo_size ? ir->fifo_size : rc; /* If we have data */ for (cnt = 0; cnt < rc; cnt++) { /* for each bit in fifo */ @@ -154,6 +153,11 @@ static int sunxi_ir_probe(struct platform_device *pdev) if (!ir) return -ENOMEM; + if (of_device_is_compatible(dn, "allwinner,sun5i-a13-ir")) + ir->fifo_size = 64; + else + ir->fifo_size = 16; + /* Clock */ ir->apb_clk = devm_clk_get(dev, "apb"); if (IS_ERR(ir->apb_clk)) { @@ -166,15 +170,29 @@ static int sunxi_ir_probe(struct platform_device *pdev) return PTR_ERR(ir->clk); } + /* Reset (optional) */ + ir->rst = devm_reset_control_get_optional(dev, NULL); + if (IS_ERR(ir->rst)) { + ret = PTR_ERR(ir->rst); + if (ret == -EPROBE_DEFER) + return ret; + ir->rst = NULL; + } else { + ret = reset_control_deassert(ir->rst); + if (ret) + return ret; + } + ret = clk_set_rate(ir->clk, SUNXI_IR_BASE_CLK); if (ret) { dev_err(dev, "set ir base clock failed!\n"); - return ret; + goto exit_reset_assert; } if (clk_prepare_enable(ir->apb_clk)) { dev_err(dev, "try to enable apb_ir_clk failed\n"); - return -EINVAL; + ret = -EINVAL; + goto exit_reset_assert; } if (clk_prepare_enable(ir->clk)) { @@ -255,7 +273,7 @@ static int sunxi_ir_probe(struct platform_device *pdev) * level */ writel(REG_RXINT_ROI_EN | REG_RXINT_RPEI_EN | - REG_RXINT_RAI_EN | REG_RXINT_RAL(TRIGGER_LEVEL - 1), + REG_RXINT_RAI_EN | REG_RXINT_RAL(ir->fifo_size / 2 - 1), ir->base + SUNXI_IR_RXINT_REG); /* Enable IR Module */ @@ -271,6 +289,9 @@ exit_clkdisable_clk: clk_disable_unprepare(ir->clk); exit_clkdisable_apb_clk: clk_disable_unprepare(ir->apb_clk); +exit_reset_assert: + if (ir->rst) + reset_control_assert(ir->rst); return ret; } @@ -282,6 +303,8 @@ static int sunxi_ir_remove(struct platform_device *pdev) clk_disable_unprepare(ir->clk); clk_disable_unprepare(ir->apb_clk); + if (ir->rst) + reset_control_assert(ir->rst); spin_lock_irqsave(&ir->ir_lock, flags); /* disable IR IRQ */ @@ -298,6 +321,7 @@ static int sunxi_ir_remove(struct platform_device *pdev) static const struct of_device_id sunxi_ir_match[] = { { .compatible = "allwinner,sun4i-a10-ir", }, + { .compatible = "allwinner,sun5i-a13-ir", }, {}, }; diff --git a/drivers/media/tuners/mt20xx.c b/drivers/media/tuners/mt20xx.c index 0e74e97e0d1a..9e031040c13f 100644 --- a/drivers/media/tuners/mt20xx.c +++ b/drivers/media/tuners/mt20xx.c @@ -660,11 +660,3 @@ EXPORT_SYMBOL_GPL(microtune_attach); MODULE_DESCRIPTION("Microtune tuner driver"); MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); MODULE_LICENSE("GPL"); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/tuners/mt2131.c b/drivers/media/tuners/mt2131.c index f83b0c1ea6c8..6e2cdd2b6175 100644 --- a/drivers/media/tuners/mt2131.c +++ b/drivers/media/tuners/mt2131.c @@ -294,8 +294,3 @@ EXPORT_SYMBOL(mt2131_attach); MODULE_AUTHOR("Steven Toth"); MODULE_DESCRIPTION("Microtune MT2131 silicon tuner driver"); MODULE_LICENSE("GPL"); - -/* - * Local variables: - * c-basic-offset: 8 - */ diff --git a/drivers/media/tuners/mt2131.h b/drivers/media/tuners/mt2131.h index 09ceaf68e47c..837c854b9c65 100644 --- a/drivers/media/tuners/mt2131.h +++ b/drivers/media/tuners/mt2131.h @@ -47,8 +47,3 @@ static inline struct dvb_frontend* mt2131_attach(struct dvb_frontend *fe, #endif /* CONFIG_MEDIA_TUNER_MT2131 */ #endif /* __MT2131_H__ */ - -/* - * Local variables: - * c-basic-offset: 8 - */ diff --git a/drivers/media/tuners/mt2131_priv.h b/drivers/media/tuners/mt2131_priv.h index 62aeedf5c550..91283b599cb3 100644 --- a/drivers/media/tuners/mt2131_priv.h +++ b/drivers/media/tuners/mt2131_priv.h @@ -41,8 +41,3 @@ struct mt2131_priv { }; #endif /* __MT2131_PRIV_H__ */ - -/* - * Local variables: - * c-basic-offset: 8 - */ diff --git a/drivers/media/tuners/mxl5007t.c b/drivers/media/tuners/mxl5007t.c index 1810ad66888e..f4ae04c3328a 100644 --- a/drivers/media/tuners/mxl5007t.c +++ b/drivers/media/tuners/mxl5007t.c @@ -938,11 +938,3 @@ MODULE_DESCRIPTION("MaxLinear MxL5007T Silicon IC tuner driver"); MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); MODULE_LICENSE("GPL"); MODULE_VERSION("0.2"); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/tuners/mxl5007t.h b/drivers/media/tuners/mxl5007t.h index 37b0942e2385..ae7037d681c5 100644 --- a/drivers/media/tuners/mxl5007t.h +++ b/drivers/media/tuners/mxl5007t.h @@ -93,12 +93,3 @@ static inline struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe, #endif #endif /* __MXL5007T_H__ */ - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ - diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 2180de9d654a..fcf139dfdec6 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -19,16 +19,17 @@ static const struct dvb_tuner_ops si2157_ops; /* execute firmware command */ -static int si2157_cmd_execute(struct si2157 *s, struct si2157_cmd *cmd) +static int si2157_cmd_execute(struct i2c_client *client, struct si2157_cmd *cmd) { + struct si2157_dev *dev = i2c_get_clientdata(client); int ret; unsigned long timeout; - mutex_lock(&s->i2c_mutex); + mutex_lock(&dev->i2c_mutex); if (cmd->wlen) { /* write cmd and args for firmware */ - ret = i2c_master_send(s->client, cmd->args, cmd->wlen); + ret = i2c_master_send(client, cmd->args, cmd->wlen); if (ret < 0) { goto err_mutex_unlock; } else if (ret != cmd->wlen) { @@ -42,7 +43,7 @@ static int si2157_cmd_execute(struct si2157 *s, struct si2157_cmd *cmd) #define TIMEOUT 80 timeout = jiffies + msecs_to_jiffies(TIMEOUT); while (!time_after(jiffies, timeout)) { - ret = i2c_master_recv(s->client, cmd->args, cmd->rlen); + ret = i2c_master_recv(client, cmd->args, cmd->rlen); if (ret < 0) { goto err_mutex_unlock; } else if (ret != cmd->rlen) { @@ -55,7 +56,7 @@ static int si2157_cmd_execute(struct si2157 *s, struct si2157_cmd *cmd) break; } - dev_dbg(&s->client->dev, "cmd execution took %d ms\n", + dev_dbg(&client->dev, "cmd execution took %d ms\n", jiffies_to_msecs(jiffies) - (jiffies_to_msecs(timeout) - TIMEOUT)); @@ -65,35 +66,32 @@ static int si2157_cmd_execute(struct si2157 *s, struct si2157_cmd *cmd) } } - ret = 0; + mutex_unlock(&dev->i2c_mutex); + return 0; err_mutex_unlock: - mutex_unlock(&s->i2c_mutex); - if (ret) - goto err; - - return 0; -err: - dev_dbg(&s->client->dev, "failed=%d\n", ret); + mutex_unlock(&dev->i2c_mutex); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int si2157_init(struct dvb_frontend *fe) { - struct si2157 *s = fe->tuner_priv; + struct i2c_client *client = fe->tuner_priv; + struct si2157_dev *dev = i2c_get_clientdata(client); int ret, len, remaining; struct si2157_cmd cmd; - const struct firmware *fw = NULL; - u8 *fw_file; + const struct firmware *fw; + const char *fw_name; unsigned int chip_id; - dev_dbg(&s->client->dev, "\n"); + dev_dbg(&client->dev, "\n"); - if (s->fw_loaded) + if (dev->fw_loaded) goto warm; /* power up */ - if (s->chiptype == SI2157_CHIPTYPE_SI2146) { + if (dev->chiptype == SI2157_CHIPTYPE_SI2146) { memcpy(cmd.args, "\xc0\x05\x01\x00\x00\x0b\x00\x00\x01", 9); cmd.wlen = 9; } else { @@ -101,7 +99,7 @@ static int si2157_init(struct dvb_frontend *fe) cmd.wlen = 15; } cmd.rlen = 1; - ret = si2157_cmd_execute(s, &cmd); + ret = si2157_cmd_execute(client, &cmd); if (ret) goto err; @@ -109,7 +107,7 @@ static int si2157_init(struct dvb_frontend *fe) memcpy(cmd.args, "\x02", 1); cmd.wlen = 1; cmd.rlen = 13; - ret = si2157_cmd_execute(s, &cmd); + ret = si2157_cmd_execute(client, &cmd); if (ret) goto err; @@ -125,121 +123,133 @@ static int si2157_init(struct dvb_frontend *fe) switch (chip_id) { case SI2158_A20: case SI2148_A20: - fw_file = SI2158_A20_FIRMWARE; + fw_name = SI2158_A20_FIRMWARE; break; case SI2157_A30: case SI2147_A30: case SI2146_A10: - goto skip_fw_download; + fw_name = NULL; + break; default: - dev_err(&s->client->dev, - "unknown chip version Si21%d-%c%c%c\n", + dev_err(&client->dev, "unknown chip version Si21%d-%c%c%c\n", cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]); ret = -EINVAL; goto err; } - /* cold state - try to download firmware */ - dev_info(&s->client->dev, "found a '%s' in cold state\n", - si2157_ops.info.name); + dev_info(&client->dev, "found a 'Silicon Labs Si21%d-%c%c%c'\n", + cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]); + + if (fw_name == NULL) + goto skip_fw_download; /* request the firmware, this will block and timeout */ - ret = request_firmware(&fw, fw_file, &s->client->dev); + ret = request_firmware(&fw, fw_name, &client->dev); if (ret) { - dev_err(&s->client->dev, "firmware file '%s' not found\n", - fw_file); + dev_err(&client->dev, "firmware file '%s' not found\n", + fw_name); goto err; } /* firmware should be n chunks of 17 bytes */ if (fw->size % 17 != 0) { - dev_err(&s->client->dev, "firmware file '%s' is invalid\n", - fw_file); + dev_err(&client->dev, "firmware file '%s' is invalid\n", + fw_name); ret = -EINVAL; - goto fw_release_exit; + goto err_release_firmware; } - dev_info(&s->client->dev, "downloading firmware from file '%s'\n", - fw_file); + dev_info(&client->dev, "downloading firmware from file '%s'\n", + fw_name); for (remaining = fw->size; remaining > 0; remaining -= 17) { len = fw->data[fw->size - remaining]; memcpy(cmd.args, &fw->data[(fw->size - remaining) + 1], len); cmd.wlen = len; cmd.rlen = 1; - ret = si2157_cmd_execute(s, &cmd); + ret = si2157_cmd_execute(client, &cmd); if (ret) { - dev_err(&s->client->dev, - "firmware download failed=%d\n", + dev_err(&client->dev, "firmware download failed %d\n", ret); - goto fw_release_exit; + goto err_release_firmware; } } release_firmware(fw); - fw = NULL; skip_fw_download: /* reboot the tuner with new firmware? */ memcpy(cmd.args, "\x01\x01", 2); cmd.wlen = 2; cmd.rlen = 1; - ret = si2157_cmd_execute(s, &cmd); + ret = si2157_cmd_execute(client, &cmd); if (ret) goto err; - s->fw_loaded = true; + /* query firmware version */ + memcpy(cmd.args, "\x11", 1); + cmd.wlen = 1; + cmd.rlen = 10; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + + dev_info(&client->dev, "firmware version: %c.%c.%d\n", + cmd.args[6], cmd.args[7], cmd.args[8]); + + dev->fw_loaded = true; warm: - s->active = true; + dev->active = true; return 0; -fw_release_exit: +err_release_firmware: release_firmware(fw); err: - dev_dbg(&s->client->dev, "failed=%d\n", ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int si2157_sleep(struct dvb_frontend *fe) { - struct si2157 *s = fe->tuner_priv; + struct i2c_client *client = fe->tuner_priv; + struct si2157_dev *dev = i2c_get_clientdata(client); int ret; struct si2157_cmd cmd; - dev_dbg(&s->client->dev, "\n"); + dev_dbg(&client->dev, "\n"); - s->active = false; + dev->active = false; /* standby */ memcpy(cmd.args, "\x16\x00", 2); cmd.wlen = 2; cmd.rlen = 1; - ret = si2157_cmd_execute(s, &cmd); + ret = si2157_cmd_execute(client, &cmd); if (ret) goto err; return 0; err: - dev_dbg(&s->client->dev, "failed=%d\n", ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } static int si2157_set_params(struct dvb_frontend *fe) { - struct si2157 *s = fe->tuner_priv; + struct i2c_client *client = fe->tuner_priv; + struct si2157_dev *dev = i2c_get_clientdata(client); struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret; struct si2157_cmd cmd; u8 bandwidth, delivery_system; - dev_dbg(&s->client->dev, + dev_dbg(&client->dev, "delivery_system=%d frequency=%u bandwidth_hz=%u\n", - c->delivery_system, c->frequency, - c->bandwidth_hz); + c->delivery_system, c->frequency, c->bandwidth_hz); - if (!s->active) { + if (!dev->active) { ret = -EAGAIN; goto err; } @@ -274,21 +284,21 @@ static int si2157_set_params(struct dvb_frontend *fe) memcpy(cmd.args, "\x14\x00\x03\x07\x00\x00", 6); cmd.args[4] = delivery_system | bandwidth; - if (s->inversion) + if (dev->inversion) cmd.args[5] = 0x01; cmd.wlen = 6; cmd.rlen = 4; - ret = si2157_cmd_execute(s, &cmd); + ret = si2157_cmd_execute(client, &cmd); if (ret) goto err; - if (s->chiptype == SI2157_CHIPTYPE_SI2146) + if (dev->chiptype == SI2157_CHIPTYPE_SI2146) memcpy(cmd.args, "\x14\x00\x02\x07\x00\x01", 6); else memcpy(cmd.args, "\x14\x00\x02\x07\x01\x00", 6); cmd.wlen = 6; cmd.rlen = 4; - ret = si2157_cmd_execute(s, &cmd); + ret = si2157_cmd_execute(client, &cmd); if (ret) goto err; @@ -300,13 +310,13 @@ static int si2157_set_params(struct dvb_frontend *fe) cmd.args[7] = (c->frequency >> 24) & 0xff; cmd.wlen = 8; cmd.rlen = 1; - ret = si2157_cmd_execute(s, &cmd); + ret = si2157_cmd_execute(client, &cmd); if (ret) goto err; return 0; err: - dev_dbg(&s->client->dev, "failed=%d\n", ret); + dev_dbg(&client->dev, "failed=%d\n", ret); return ret; } @@ -334,70 +344,67 @@ static int si2157_probe(struct i2c_client *client, { struct si2157_config *cfg = client->dev.platform_data; struct dvb_frontend *fe = cfg->fe; - struct si2157 *s; + struct si2157_dev *dev; struct si2157_cmd cmd; int ret; - s = kzalloc(sizeof(struct si2157), GFP_KERNEL); - if (!s) { + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { ret = -ENOMEM; dev_err(&client->dev, "kzalloc() failed\n"); goto err; } - s->client = client; - s->fe = cfg->fe; - s->inversion = cfg->inversion; - s->fw_loaded = false; - s->chiptype = (u8)id->driver_data; - mutex_init(&s->i2c_mutex); + i2c_set_clientdata(client, dev); + dev->fe = cfg->fe; + dev->inversion = cfg->inversion; + dev->fw_loaded = false; + dev->chiptype = (u8)id->driver_data; + mutex_init(&dev->i2c_mutex); /* check if the tuner is there */ cmd.wlen = 0; cmd.rlen = 1; - ret = si2157_cmd_execute(s, &cmd); + ret = si2157_cmd_execute(client, &cmd); if (ret) - goto err; + goto err_kfree; - fe->tuner_priv = s; - memcpy(&fe->ops.tuner_ops, &si2157_ops, - sizeof(struct dvb_tuner_ops)); + memcpy(&fe->ops.tuner_ops, &si2157_ops, sizeof(struct dvb_tuner_ops)); + fe->tuner_priv = client; - i2c_set_clientdata(client, s); - - dev_info(&s->client->dev, - "Silicon Labs %s successfully attached\n", - s->chiptype == SI2157_CHIPTYPE_SI2146 ? + dev_info(&client->dev, "Silicon Labs %s successfully attached\n", + dev->chiptype == SI2157_CHIPTYPE_SI2146 ? "Si2146" : "Si2147/2148/2157/2158"); return 0; + +err_kfree: + kfree(dev); err: dev_dbg(&client->dev, "failed=%d\n", ret); - kfree(s); - return ret; } static int si2157_remove(struct i2c_client *client) { - struct si2157 *s = i2c_get_clientdata(client); - struct dvb_frontend *fe = s->fe; + struct si2157_dev *dev = i2c_get_clientdata(client); + struct dvb_frontend *fe = dev->fe; dev_dbg(&client->dev, "\n"); memset(&fe->ops.tuner_ops, 0, sizeof(struct dvb_tuner_ops)); fe->tuner_priv = NULL; - kfree(s); + kfree(dev); return 0; } -static const struct i2c_device_id si2157_id[] = { - {"si2157", 0}, - {"si2146", 1}, +static const struct i2c_device_id si2157_id_table[] = { + {"si2157", SI2157_CHIPTYPE_SI2157}, + {"si2146", SI2157_CHIPTYPE_SI2146}, {} }; -MODULE_DEVICE_TABLE(i2c, si2157_id); +MODULE_DEVICE_TABLE(i2c, si2157_id_table); static struct i2c_driver si2157_driver = { .driver = { @@ -406,7 +413,7 @@ static struct i2c_driver si2157_driver = { }, .probe = si2157_probe, .remove = si2157_remove, - .id_table = si2157_id, + .id_table = si2157_id_table, }; module_i2c_driver(si2157_driver); diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h index d6e07cdd2a07..7aa53bce5593 100644 --- a/drivers/media/tuners/si2157_priv.h +++ b/drivers/media/tuners/si2157_priv.h @@ -21,9 +21,8 @@ #include "si2157.h" /* state struct */ -struct si2157 { +struct si2157_dev { struct mutex i2c_mutex; - struct i2c_client *client; struct dvb_frontend *fe; bool active; bool fw_loaded; diff --git a/drivers/media/tuners/tda18271-fe.c b/drivers/media/tuners/tda18271-fe.c index 4995b890c164..f8620741bb5f 100644 --- a/drivers/media/tuners/tda18271-fe.c +++ b/drivers/media/tuners/tda18271-fe.c @@ -1355,11 +1355,3 @@ MODULE_DESCRIPTION("NXP TDA18271HD analog / digital tuner driver"); MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); MODULE_LICENSE("GPL"); MODULE_VERSION("0.4"); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/tuners/tda18271-maps.c b/drivers/media/tuners/tda18271-maps.c index b62e925f643f..1e89dd93c4bb 100644 --- a/drivers/media/tuners/tda18271-maps.c +++ b/drivers/media/tuners/tda18271-maps.c @@ -1305,11 +1305,3 @@ int tda18271_assign_map_layout(struct dvb_frontend *fe) return ret; } - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/tuners/tda18271-priv.h b/drivers/media/tuners/tda18271-priv.h index b36a7b754772..cc80f544af34 100644 --- a/drivers/media/tuners/tda18271-priv.h +++ b/drivers/media/tuners/tda18271-priv.h @@ -226,11 +226,3 @@ extern int tda18271_calc_ir_measure(struct dvb_frontend *fe, u32 *freq); extern int tda18271_calc_rf_cal(struct dvb_frontend *fe, u32 *freq); #endif /* __TDA18271_PRIV_H__ */ - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/tuners/tda827x.c b/drivers/media/tuners/tda827x.c index 73453a255cdc..edcb4a723aa1 100644 --- a/drivers/media/tuners/tda827x.c +++ b/drivers/media/tuners/tda827x.c @@ -907,11 +907,3 @@ MODULE_DESCRIPTION("DVB TDA827x driver"); MODULE_AUTHOR("Hartmut Hackmann <hartmut.hackmann@t-online.de>"); MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); MODULE_LICENSE("GPL"); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/tuners/tda8290.c b/drivers/media/tuners/tda8290.c index ab4106c17b4c..998e82bba9c0 100644 --- a/drivers/media/tuners/tda8290.c +++ b/drivers/media/tuners/tda8290.c @@ -881,11 +881,3 @@ EXPORT_SYMBOL_GPL(tda829x_probe); MODULE_DESCRIPTION("Philips/NXP TDA8290/TDA8295 analog IF demodulator driver"); MODULE_AUTHOR("Gerd Knorr, Hartmut Hackmann, Michael Krufky"); MODULE_LICENSE("GPL"); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/tuners/tda9887.c b/drivers/media/tuners/tda9887.c index 9823248d743f..56be6c29399b 100644 --- a/drivers/media/tuners/tda9887.c +++ b/drivers/media/tuners/tda9887.c @@ -707,11 +707,3 @@ struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe, EXPORT_SYMBOL_GPL(tda9887_attach); MODULE_LICENSE("GPL"); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/tuners/tuner-simple.c b/drivers/media/tuners/tuner-simple.c index ca274c2d8c70..8e9ce144da9a 100644 --- a/drivers/media/tuners/tuner-simple.c +++ b/drivers/media/tuners/tuner-simple.c @@ -1148,11 +1148,3 @@ EXPORT_SYMBOL_GPL(simple_tuner_attach); MODULE_DESCRIPTION("Simple 4-control-bytes style tuner driver"); MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer"); MODULE_LICENSE("GPL"); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/au0828/Kconfig b/drivers/media/usb/au0828/Kconfig index 1d410ac8f9a8..78b797e0b434 100644 --- a/drivers/media/usb/au0828/Kconfig +++ b/drivers/media/usb/au0828/Kconfig @@ -4,7 +4,7 @@ config VIDEO_AU0828 depends on I2C && INPUT && DVB_CORE && USB select I2C_ALGOBIT select VIDEO_TVEEPROM - select VIDEOBUF_VMALLOC + select VIDEOBUF2_VMALLOC select DVB_AU8522_DTV if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT diff --git a/drivers/media/usb/au0828/au0828-cards.c b/drivers/media/usb/au0828/au0828-cards.c index da87f1cc31a9..edc27355f271 100644 --- a/drivers/media/usb/au0828/au0828-cards.c +++ b/drivers/media/usb/au0828/au0828-cards.c @@ -44,7 +44,7 @@ static void hvr950q_cs5340_audio(void *priv, int enable) struct au0828_board au0828_boards[] = { [AU0828_BOARD_UNKNOWN] = { .name = "Unknown board", - .tuner_type = UNSET, + .tuner_type = -1U, .tuner_addr = ADDR_UNSET, }, [AU0828_BOARD_HAUPPAUGE_HVR850] = { diff --git a/drivers/media/usb/au0828/au0828-vbi.c b/drivers/media/usb/au0828/au0828-vbi.c index 932d24f42b24..f67247cf1a5a 100644 --- a/drivers/media/usb/au0828/au0828-vbi.c +++ b/drivers/media/usb/au0828/au0828-vbi.c @@ -28,111 +28,67 @@ #include <linux/init.h> #include <linux/slab.h> -static unsigned int vbibufs = 5; -module_param(vbibufs, int, 0644); -MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32"); - /* ------------------------------------------------------------------ */ -static void -free_buffer(struct videobuf_queue *vq, struct au0828_buffer *buf) +static int vbi_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct au0828_fh *fh = vq->priv_data; - struct au0828_dev *dev = fh->dev; - unsigned long flags = 0; - if (in_interrupt()) - BUG(); - - /* We used to wait for the buffer to finish here, but this didn't work - because, as we were keeping the state as VIDEOBUF_QUEUED, - videobuf_queue_cancel marked it as finished for us. - (Also, it could wedge forever if the hardware was misconfigured.) - - This should be safe; by the time we get here, the buffer isn't - queued anymore. If we ever start marking the buffers as - VIDEOBUF_ACTIVE, it won't be, though. - */ - spin_lock_irqsave(&dev->slock, flags); - if (dev->isoc_ctl.vbi_buf == buf) - dev->isoc_ctl.vbi_buf = NULL; - spin_unlock_irqrestore(&dev->slock, flags); + struct au0828_dev *dev = vb2_get_drv_priv(vq); + unsigned long img_size = dev->vbi_width * dev->vbi_height * 2; + unsigned long size; - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} - -static int -vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) -{ - struct au0828_fh *fh = q->priv_data; - struct au0828_dev *dev = fh->dev; + size = fmt ? (fmt->fmt.vbi.samples_per_line * + (fmt->fmt.vbi.count[0] + fmt->fmt.vbi.count[1])) : img_size; + if (size < img_size) + return -EINVAL; - *size = dev->vbi_width * dev->vbi_height * 2; + *nplanes = 1; + sizes[0] = size; - if (0 == *count) - *count = vbibufs; - if (*count < 2) - *count = 2; - if (*count > 32) - *count = 32; return 0; } -static int -vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, - enum v4l2_field field) +static int vbi_buffer_prepare(struct vb2_buffer *vb) { - struct au0828_fh *fh = q->priv_data; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = vb2_get_drv_priv(vb->vb2_queue); struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb); - int rc = 0; + unsigned long size; - buf->vb.size = dev->vbi_width * dev->vbi_height * 2; + size = dev->vbi_width * dev->vbi_height * 2; - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + if (vb2_plane_size(vb, 0) < size) { + pr_err("%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), size); return -EINVAL; - - buf->vb.width = dev->vbi_width; - buf->vb.height = dev->vbi_height; - buf->vb.field = field; - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(q, &buf->vb, NULL); - if (rc < 0) - goto fail; } + vb2_set_plane_payload(&buf->vb, 0, size); - buf->vb.state = VIDEOBUF_PREPARED; return 0; - -fail: - free_buffer(q, buf); - return rc; } static void -vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct au0828_buffer *buf = container_of(vb, - struct au0828_buffer, - vb); - struct au0828_fh *fh = vq->priv_data; - struct au0828_dev *dev = fh->dev; - struct au0828_dmaqueue *vbiq = &dev->vbiq; - - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vbiq->active); -} - -static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +vbi_buffer_queue(struct vb2_buffer *vb) { + struct au0828_dev *dev = vb2_get_drv_priv(vb->vb2_queue); struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb); - free_buffer(q, buf); + struct au0828_dmaqueue *vbiq = &dev->vbiq; + unsigned long flags = 0; + + buf->mem = vb2_plane_vaddr(vb, 0); + buf->length = vb2_plane_size(vb, 0); + + spin_lock_irqsave(&dev->slock, flags); + list_add_tail(&buf->list, &vbiq->active); + spin_unlock_irqrestore(&dev->slock, flags); } -struct videobuf_queue_ops au0828_vbi_qops = { - .buf_setup = vbi_setup, - .buf_prepare = vbi_prepare, - .buf_queue = vbi_queue, - .buf_release = vbi_release, +struct vb2_ops au0828_vbi_qops = { + .queue_setup = vbi_queue_setup, + .buf_prepare = vbi_buffer_prepare, + .buf_queue = vbi_buffer_queue, + .start_streaming = au0828_start_analog_streaming, + .stop_streaming = au0828_stop_vbi_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; diff --git a/drivers/media/usb/au0828/au0828-video.c b/drivers/media/usb/au0828/au0828-video.c index 5f337b118bff..a27cb5fcdef8 100644 --- a/drivers/media/usb/au0828/au0828-video.c +++ b/drivers/media/usb/au0828/au0828-video.c @@ -22,7 +22,6 @@ /* Developer Notes: * - * VBI support is not yet working * The hardware scaler supported is unimplemented * AC97 audio support is unimplemented (only i2s audio mode) * @@ -219,9 +218,6 @@ static int au0828_init_isoc(struct au0828_dev *dev, int max_packets, au0828_isocdbg("au0828: called au0828_prepare_isoc\n"); - /* De-allocates all pending stuff */ - au0828_uninit_isoc(dev); - dev->isoc_ctl.isoc_copy = isoc_copy; dev->isoc_ctl.num_bufs = num_bufs; @@ -285,8 +281,6 @@ static int au0828_init_isoc(struct au0828_dev *dev, int max_packets, } } - init_waitqueue_head(&dma_q->wq); - /* submit urbs and enables IRQ */ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); @@ -309,16 +303,12 @@ static inline void buffer_filled(struct au0828_dev *dev, struct au0828_buffer *buf) { /* Advice that buffer was filled */ - au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); - - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - v4l2_get_timestamp(&buf->vb.ts); + au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->top_field); - dev->isoc_ctl.buf = NULL; - - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); + buf->vb.v4l2_buf.sequence = dev->frame_count++; + buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); } static inline void vbi_buffer_filled(struct au0828_dev *dev, @@ -326,16 +316,12 @@ static inline void vbi_buffer_filled(struct au0828_dev *dev, struct au0828_buffer *buf) { /* Advice that buffer was filled */ - au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); - - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - v4l2_get_timestamp(&buf->vb.ts); + au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->top_field); - dev->isoc_ctl.vbi_buf = NULL; - - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); + buf->vb.v4l2_buf.sequence = dev->vbi_frame_count++; + buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); } /* @@ -354,8 +340,8 @@ static void au0828_copy_video(struct au0828_dev *dev, if (len == 0) return; - if (dma_q->pos + len > buf->vb.size) - len = buf->vb.size - dma_q->pos; + if (dma_q->pos + len > buf->length) + len = buf->length - dma_q->pos; startread = p; remain = len; @@ -373,11 +359,11 @@ static void au0828_copy_video(struct au0828_dev *dev, lencopy = bytesperline - currlinedone; lencopy = lencopy > remain ? remain : lencopy; - if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) { + if ((char *)startwrite + lencopy > (char *)outp + buf->length) { au0828_isocdbg("Overflow of %zi bytes past buffer end (1)\n", ((char *)startwrite + lencopy) - - ((char *)outp + buf->vb.size)); - remain = (char *)outp + buf->vb.size - (char *)startwrite; + ((char *)outp + buf->length)); + remain = (char *)outp + buf->length - (char *)startwrite; lencopy = remain; } if (lencopy <= 0) @@ -395,11 +381,11 @@ static void au0828_copy_video(struct au0828_dev *dev, lencopy = bytesperline; if ((char *)startwrite + lencopy > (char *)outp + - buf->vb.size) { + buf->length) { au0828_isocdbg("Overflow %zi bytes past buf end (2)\n", ((char *)startwrite + lencopy) - - ((char *)outp + buf->vb.size)); - lencopy = remain = (char *)outp + buf->vb.size - + ((char *)outp + buf->length)); + lencopy = remain = (char *)outp + buf->length - (char *)startwrite; } if (lencopy <= 0) @@ -435,7 +421,11 @@ static inline void get_next_buf(struct au0828_dmaqueue *dma_q, } /* Get the next buffer */ - *buf = list_entry(dma_q->active.next, struct au0828_buffer, vb.queue); + *buf = list_entry(dma_q->active.next, struct au0828_buffer, list); + /* Cleans up buffer - Useful for testing for frame/URB loss */ + list_del(&(*buf)->list); + dma_q->pos = 0; + (*buf)->vb_buf = (*buf)->mem; dev->isoc_ctl.buf = *buf; return; @@ -473,8 +463,8 @@ static void au0828_copy_vbi(struct au0828_dev *dev, bytesperline = dev->vbi_width; - if (dma_q->pos + len > buf->vb.size) - len = buf->vb.size - dma_q->pos; + if (dma_q->pos + len > buf->length) + len = buf->length - dma_q->pos; startread = p; startwrite = outp + (dma_q->pos / 2); @@ -497,7 +487,6 @@ static inline void vbi_get_next_buf(struct au0828_dmaqueue *dma_q, struct au0828_buffer **buf) { struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vbiq); - char *outp; if (list_empty(&dma_q->active)) { au0828_isocdbg("No active queue to serve\n"); @@ -507,13 +496,12 @@ static inline void vbi_get_next_buf(struct au0828_dmaqueue *dma_q, } /* Get the next buffer */ - *buf = list_entry(dma_q->active.next, struct au0828_buffer, vb.queue); + *buf = list_entry(dma_q->active.next, struct au0828_buffer, list); /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&(*buf)->vb); - memset(outp, 0x00, (*buf)->vb.size); - + list_del(&(*buf)->list); + dma_q->pos = 0; + (*buf)->vb_buf = (*buf)->mem; dev->isoc_ctl.vbi_buf = *buf; - return; } @@ -549,11 +537,11 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) buf = dev->isoc_ctl.buf; if (buf != NULL) - outp = videobuf_to_vmalloc(&buf->vb); + outp = vb2_plane_vaddr(&buf->vb, 0); vbi_buf = dev->isoc_ctl.vbi_buf; if (vbi_buf != NULL) - vbioutp = videobuf_to_vmalloc(&vbi_buf->vb); + vbioutp = vb2_plane_vaddr(&vbi_buf->vb, 0); for (i = 0; i < urb->number_of_packets; i++) { int status = urb->iso_frame_desc[i].status; @@ -593,8 +581,8 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) if (vbi_buf == NULL) vbioutp = NULL; else - vbioutp = videobuf_to_vmalloc( - &vbi_buf->vb); + vbioutp = vb2_plane_vaddr( + &vbi_buf->vb, 0); /* Video */ if (buf != NULL) @@ -603,7 +591,7 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) if (buf == NULL) outp = NULL; else - outp = videobuf_to_vmalloc(&buf->vb); + outp = vb2_plane_vaddr(&buf->vb, 0); /* As long as isoc traffic is arriving, keep resetting the timer */ @@ -657,130 +645,59 @@ static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) return rc; } -static int -buffer_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct au0828_fh *fh = vq->priv_data; - *size = (fh->dev->width * fh->dev->height * 16 + 7) >> 3; + struct au0828_dev *dev = vb2_get_drv_priv(vq); + unsigned long img_size = dev->height * dev->bytesperline; + unsigned long size; - if (0 == *count) - *count = AU0828_DEF_BUF; + size = fmt ? fmt->fmt.pix.sizeimage : img_size; + if (size < img_size) + return -EINVAL; - if (*count < AU0828_MIN_BUF) - *count = AU0828_MIN_BUF; - return 0; -} + *nplanes = 1; + sizes[0] = size; -/* This is called *without* dev->slock held; please keep it that way */ -static void free_buffer(struct videobuf_queue *vq, struct au0828_buffer *buf) -{ - struct au0828_fh *fh = vq->priv_data; - struct au0828_dev *dev = fh->dev; - unsigned long flags = 0; - if (in_interrupt()) - BUG(); - - /* We used to wait for the buffer to finish here, but this didn't work - because, as we were keeping the state as VIDEOBUF_QUEUED, - videobuf_queue_cancel marked it as finished for us. - (Also, it could wedge forever if the hardware was misconfigured.) - - This should be safe; by the time we get here, the buffer isn't - queued anymore. If we ever start marking the buffers as - VIDEOBUF_ACTIVE, it won't be, though. - */ - spin_lock_irqsave(&dev->slock, flags); - if (dev->isoc_ctl.buf == buf) - dev->isoc_ctl.buf = NULL; - spin_unlock_irqrestore(&dev->slock, flags); - - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; + return 0; } static int -buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) +buffer_prepare(struct vb2_buffer *vb) { - struct au0828_fh *fh = vq->priv_data; struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb); - struct au0828_dev *dev = fh->dev; - int rc = 0, urb_init = 0; + struct au0828_dev *dev = vb2_get_drv_priv(vb->vb2_queue); - buf->vb.size = (fh->dev->width * fh->dev->height * 16 + 7) >> 3; + buf->length = dev->height * dev->bytesperline; - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + if (vb2_plane_size(vb, 0) < buf->length) { + pr_err("%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), buf->length); return -EINVAL; - - buf->vb.width = dev->width; - buf->vb.height = dev->height; - buf->vb.field = field; - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(vq, &buf->vb, NULL); - if (rc < 0) { - pr_info("videobuf_iolock failed\n"); - goto fail; - } } - - if (!dev->isoc_ctl.num_bufs) - urb_init = 1; - - if (urb_init) { - rc = au0828_init_isoc(dev, AU0828_ISO_PACKETS_PER_URB, - AU0828_MAX_ISO_BUFS, dev->max_pkt_size, - au0828_isoc_copy); - if (rc < 0) { - pr_info("au0828_init_isoc failed\n"); - goto fail; - } - } - - buf->vb.state = VIDEOBUF_PREPARED; + vb2_set_plane_payload(&buf->vb, 0, buf->length); return 0; - -fail: - free_buffer(vq, buf); - return rc; } static void -buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +buffer_queue(struct vb2_buffer *vb) { struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb); - struct au0828_fh *fh = vq->priv_data; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = vb2_get_drv_priv(vb->vb2_queue); struct au0828_dmaqueue *vidq = &dev->vidq; + unsigned long flags = 0; - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vidq->active); -} - -static void buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct au0828_buffer *buf = container_of(vb, - struct au0828_buffer, - vb); + buf->mem = vb2_plane_vaddr(vb, 0); + buf->length = vb2_plane_size(vb, 0); - free_buffer(vq, buf); + spin_lock_irqsave(&dev->slock, flags); + list_add_tail(&buf->list, &vidq->active); + spin_unlock_irqrestore(&dev->slock, flags); } -static struct videobuf_queue_ops au0828_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, -}; - -/* ------------------------------------------------------------------ - V4L2 interface - ------------------------------------------------------------------*/ - static int au0828_i2s_init(struct au0828_dev *dev) { /* Enable i2s mode */ @@ -829,7 +746,7 @@ static int au0828_analog_stream_enable(struct au0828_dev *d) return 0; } -int au0828_analog_stream_disable(struct au0828_dev *d) +static int au0828_analog_stream_disable(struct au0828_dev *d) { dprintk(1, "au0828_analog_stream_disable called\n"); au0828_writereg(d, AU0828_SENSORCTRL_100, 0x0); @@ -862,78 +779,133 @@ static int au0828_stream_interrupt(struct au0828_dev *dev) return 0; } -/* - * au0828_release_resources - * unregister v4l2 devices - */ -void au0828_analog_unregister(struct au0828_dev *dev) +int au0828_start_analog_streaming(struct vb2_queue *vq, unsigned int count) { - dprintk(1, "au0828_release_resources called\n"); - mutex_lock(&au0828_sysfs_lock); + struct au0828_dev *dev = vb2_get_drv_priv(vq); + int rc = 0; - if (dev->vdev) - video_unregister_device(dev->vdev); - if (dev->vbi_dev) - video_unregister_device(dev->vbi_dev); + dprintk(1, "au0828_start_analog_streaming called %d\n", + dev->streaming_users); - mutex_unlock(&au0828_sysfs_lock); -} + if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + dev->frame_count = 0; + else + dev->vbi_frame_count = 0; + + if (dev->streaming_users == 0) { + /* If we were doing ac97 instead of i2s, it would go here...*/ + au0828_i2s_init(dev); + rc = au0828_init_isoc(dev, AU0828_ISO_PACKETS_PER_URB, + AU0828_MAX_ISO_BUFS, dev->max_pkt_size, + au0828_isoc_copy); + if (rc < 0) { + pr_info("au0828_init_isoc failed\n"); + return rc; + } + if (vq->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + v4l2_device_call_all(&dev->v4l2_dev, 0, video, + s_stream, 1); + dev->vid_timeout_running = 1; + mod_timer(&dev->vid_timeout, jiffies + (HZ / 10)); + } else if (vq->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + dev->vbi_timeout_running = 1; + mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10)); + } + } + dev->streaming_users++; + return rc; +} -/* Usage lock check functions */ -static int res_get(struct au0828_fh *fh, unsigned int bit) +static void au0828_stop_streaming(struct vb2_queue *vq) { - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = vb2_get_drv_priv(vq); + struct au0828_dmaqueue *vidq = &dev->vidq; + unsigned long flags = 0; - if (fh->resources & bit) - /* have it already allocated */ - return 1; + dprintk(1, "au0828_stop_streaming called %d\n", dev->streaming_users); - /* is it free? */ - if (dev->resources & bit) { - /* no, someone else uses it */ - return 0; + if (dev->streaming_users-- == 1) + au0828_uninit_isoc(dev); + + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); + dev->vid_timeout_running = 0; + del_timer_sync(&dev->vid_timeout); + + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.buf != NULL) { + vb2_buffer_done(&dev->isoc_ctl.buf->vb, VB2_BUF_STATE_ERROR); + dev->isoc_ctl.buf = NULL; } - /* it's free, grab it */ - fh->resources |= bit; - dev->resources |= bit; - dprintk(1, "res: get %d\n", bit); + while (!list_empty(&vidq->active)) { + struct au0828_buffer *buf; - return 1; + buf = list_entry(vidq->active.next, struct au0828_buffer, list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + list_del(&buf->list); + } + spin_unlock_irqrestore(&dev->slock, flags); } -static int res_check(struct au0828_fh *fh, unsigned int bit) +void au0828_stop_vbi_streaming(struct vb2_queue *vq) { - return fh->resources & bit; -} + struct au0828_dev *dev = vb2_get_drv_priv(vq); + struct au0828_dmaqueue *vbiq = &dev->vbiq; + unsigned long flags = 0; -static int res_locked(struct au0828_dev *dev, unsigned int bit) -{ - return dev->resources & bit; -} + dprintk(1, "au0828_stop_vbi_streaming called %d\n", + dev->streaming_users); -static void res_free(struct au0828_fh *fh, unsigned int bits) -{ - struct au0828_dev *dev = fh->dev; + if (dev->streaming_users-- == 1) + au0828_uninit_isoc(dev); - BUG_ON((fh->resources & bits) != bits); + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.vbi_buf != NULL) { + vb2_buffer_done(&dev->isoc_ctl.vbi_buf->vb, + VB2_BUF_STATE_ERROR); + dev->isoc_ctl.vbi_buf = NULL; + } + while (!list_empty(&vbiq->active)) { + struct au0828_buffer *buf; + + buf = list_entry(vbiq->active.next, struct au0828_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + spin_unlock_irqrestore(&dev->slock, flags); - fh->resources &= ~bits; - dev->resources &= ~bits; - dprintk(1, "res: put %d\n", bits); + dev->vbi_timeout_running = 0; + del_timer_sync(&dev->vbi_timeout); } -static int get_ressource(struct au0828_fh *fh) +static struct vb2_ops au0828_video_qops = { + .queue_setup = queue_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .start_streaming = au0828_start_analog_streaming, + .stop_streaming = au0828_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, +}; + +/* ------------------------------------------------------------------ + V4L2 interface + ------------------------------------------------------------------*/ +/* + * au0828_analog_unregister + * unregister v4l2 devices + */ +void au0828_analog_unregister(struct au0828_dev *dev) { - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - return AU0828_RESOURCE_VIDEO; - case V4L2_BUF_TYPE_VBI_CAPTURE: - return AU0828_RESOURCE_VBI; - default: - BUG(); - return 0; - } + dprintk(1, "au0828_analog_unregister called\n"); + mutex_lock(&au0828_sysfs_lock); + + if (dev->vdev) + video_unregister_device(dev->vdev); + if (dev->vbi_dev) + video_unregister_device(dev->vbi_dev); + + mutex_unlock(&au0828_sysfs_lock); } /* This function ensures that video frames continue to be delivered even if @@ -951,8 +923,8 @@ static void au0828_vid_buffer_timeout(unsigned long data) buf = dev->isoc_ctl.buf; if (buf != NULL) { - vid_data = videobuf_to_vmalloc(&buf->vb); - memset(vid_data, 0x00, buf->vb.size); /* Blank green frame */ + vid_data = vb2_plane_vaddr(&buf->vb, 0); + memset(vid_data, 0x00, buf->length); /* Blank green frame */ buffer_filled(dev, dma_q, buf); } get_next_buf(dma_q, &buf); @@ -975,8 +947,8 @@ static void au0828_vbi_buffer_timeout(unsigned long data) buf = dev->isoc_ctl.vbi_buf; if (buf != NULL) { - vbi_data = videobuf_to_vmalloc(&buf->vb); - memset(vbi_data, 0x00, buf->vb.size); + vbi_data = vb2_plane_vaddr(&buf->vb, 0); + memset(vbi_data, 0x00, buf->length); vbi_buffer_filled(dev, dma_q, buf); } vbi_get_next_buf(dma_q, &buf); @@ -986,105 +958,65 @@ static void au0828_vbi_buffer_timeout(unsigned long data) spin_unlock_irqrestore(&dev->slock, flags); } - static int au0828_v4l2_open(struct file *filp) { - int ret = 0; - struct video_device *vdev = video_devdata(filp); struct au0828_dev *dev = video_drvdata(filp); - struct au0828_fh *fh; - int type; - - switch (vdev->vfl_type) { - case VFL_TYPE_GRABBER: - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - break; - case VFL_TYPE_VBI: - type = V4L2_BUF_TYPE_VBI_CAPTURE; - break; - default: - return -EINVAL; - } - - fh = kzalloc(sizeof(struct au0828_fh), GFP_KERNEL); - if (NULL == fh) { - dprintk(1, "Failed allocate au0828_fh struct!\n"); - return -ENOMEM; - } + int ret; - fh->type = type; - fh->dev = dev; - v4l2_fh_init(&fh->fh, vdev); - filp->private_data = fh; + dprintk(1, + "%s called std_set %d dev_state %d stream users %d users %d\n", + __func__, dev->std_set_in_tuner_core, dev->dev_state, + dev->streaming_users, dev->users); - if (mutex_lock_interruptible(&dev->lock)) { - kfree(fh); + if (mutex_lock_interruptible(&dev->lock)) return -ERESTARTSYS; + + ret = v4l2_fh_open(filp); + if (ret) { + au0828_isocdbg("%s: v4l2_fh_open() returned error %d\n", + __func__, ret); + mutex_unlock(&dev->lock); + return ret; } + if (dev->users == 0) { au0828_analog_stream_enable(dev); au0828_analog_stream_reset(dev); - - /* If we were doing ac97 instead of i2s, it would go here...*/ - au0828_i2s_init(dev); - dev->stream_state = STREAM_OFF; dev->dev_state |= DEV_INITIALIZED; } - dev->users++; mutex_unlock(&dev->lock); - - videobuf_queue_vmalloc_init(&fh->vb_vidq, &au0828_video_qops, - NULL, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct au0828_buffer), fh, - &dev->lock); - - /* VBI Setup */ - videobuf_queue_vmalloc_init(&fh->vb_vbiq, &au0828_vbi_qops, - NULL, &dev->slock, - V4L2_BUF_TYPE_VBI_CAPTURE, - V4L2_FIELD_SEQ_TB, - sizeof(struct au0828_buffer), fh, - &dev->lock); - v4l2_fh_add(&fh->fh); return ret; } static int au0828_v4l2_close(struct file *filp) { int ret; - struct au0828_fh *fh = filp->private_data; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = video_drvdata(filp); + struct video_device *vdev = video_devdata(filp); + + dprintk(1, + "%s called std_set %d dev_state %d stream users %d users %d\n", + __func__, dev->std_set_in_tuner_core, dev->dev_state, + dev->streaming_users, dev->users); - v4l2_fh_del(&fh->fh); - v4l2_fh_exit(&fh->fh); mutex_lock(&dev->lock); - if (res_check(fh, AU0828_RESOURCE_VIDEO)) { + if (vdev->vfl_type == VFL_TYPE_GRABBER && dev->vid_timeout_running) { /* Cancel timeout thread in case they didn't call streamoff */ dev->vid_timeout_running = 0; del_timer_sync(&dev->vid_timeout); - - videobuf_stop(&fh->vb_vidq); - res_free(fh, AU0828_RESOURCE_VIDEO); - } - - if (res_check(fh, AU0828_RESOURCE_VBI)) { + } else if (vdev->vfl_type == VFL_TYPE_VBI && + dev->vbi_timeout_running) { /* Cancel timeout thread in case they didn't call streamoff */ dev->vbi_timeout_running = 0; del_timer_sync(&dev->vbi_timeout); - - videobuf_stop(&fh->vb_vbiq); - res_free(fh, AU0828_RESOURCE_VBI); } - if (dev->users == 1 && video_is_registered(video_devdata(filp))) { - au0828_analog_stream_disable(dev); - - au0828_uninit_isoc(dev); + if (dev->dev_state == DEV_DISCONNECTED) + goto end; + if (dev->users == 1) { /* Save some power by putting tuner to sleep */ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); dev->std_set_in_tuner_core = 0; @@ -1095,13 +1027,10 @@ static int au0828_v4l2_close(struct file *filp) if (ret < 0) pr_info("Au0828 can't set alternate to 0!\n"); } - mutex_unlock(&dev->lock); - - videobuf_mmap_free(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vbiq); - kfree(fh); +end: + _vb2_fop_release(filp, NULL); dev->users--; - wake_up_interruptible_nr(&dev->open, 1); + mutex_unlock(&dev->lock); return 0; } @@ -1113,6 +1042,9 @@ static void au0828_init_tuner(struct au0828_dev *dev) .type = V4L2_TUNER_ANALOG_TV, }; + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dev->std_set_in_tuner_core, dev->dev_state); + if (dev->std_set_in_tuner_core) return; dev->std_set_in_tuner_core = 1; @@ -1125,98 +1057,6 @@ static void au0828_init_tuner(struct au0828_dev *dev) i2c_gate_ctrl(dev, 0); } -static ssize_t au0828_v4l2_read(struct file *filp, char __user *buf, - size_t count, loff_t *pos) -{ - struct au0828_fh *fh = filp->private_data; - struct au0828_dev *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - au0828_init_tuner(dev); - mutex_unlock(&dev->lock); - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (res_locked(dev, AU0828_RESOURCE_VIDEO)) - return -EBUSY; - - return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, - filp->f_flags & O_NONBLOCK); - } - - if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (!res_get(fh, AU0828_RESOURCE_VBI)) - return -EBUSY; - - if (dev->vbi_timeout_running == 0) { - /* Handle case where caller tries to read without - calling streamon first */ - dev->vbi_timeout_running = 1; - mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10)); - } - - return videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0, - filp->f_flags & O_NONBLOCK); - } - - return 0; -} - -static unsigned int au0828_v4l2_poll(struct file *filp, poll_table *wait) -{ - struct au0828_fh *fh = filp->private_data; - struct au0828_dev *dev = fh->dev; - unsigned long req_events = poll_requested_events(wait); - unsigned int res; - - if (check_dev(dev) < 0) - return POLLERR; - - res = v4l2_ctrl_poll(filp, wait); - if (!(req_events & (POLLIN | POLLRDNORM))) - return res; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - au0828_init_tuner(dev); - mutex_unlock(&dev->lock); - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (!res_get(fh, AU0828_RESOURCE_VIDEO)) - return POLLERR; - return res | videobuf_poll_stream(filp, &fh->vb_vidq, wait); - } - if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (!res_get(fh, AU0828_RESOURCE_VBI)) - return POLLERR; - return res | videobuf_poll_stream(filp, &fh->vb_vbiq, wait); - } - return POLLERR; -} - -static int au0828_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct au0828_fh *fh = filp->private_data; - struct au0828_dev *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); - else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) - rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma); - - return rc; -} - static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd, struct v4l2_format *format) { @@ -1268,13 +1108,14 @@ static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd, return 0; } - static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct video_device *vdev = video_devdata(file); - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = video_drvdata(file); + + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dev->std_set_in_tuner_core, dev->dev_state); strlcpy(cap->driver, "au0828", sizeof(cap->driver)); strlcpy(cap->card, dev->board.name, sizeof(cap->card)); @@ -1300,6 +1141,8 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, if (f->index) return -EINVAL; + dprintk(1, "%s called\n", __func__); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; strcpy(f->description, "Packed YUV2"); @@ -1312,8 +1155,10 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = video_drvdata(file); + + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dev->std_set_in_tuner_core, dev->dev_state); f->fmt.pix.width = dev->width; f->fmt.pix.height = dev->height; @@ -1329,8 +1174,10 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = video_drvdata(file); + + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dev->std_set_in_tuner_core, dev->dev_state); return au0828_set_format(dev, VIDIOC_TRY_FMT, f); } @@ -1338,15 +1185,17 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = video_drvdata(file); int rc; + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dev->std_set_in_tuner_core, dev->dev_state); + rc = check_dev(dev); if (rc < 0) return rc; - if (videobuf_queue_is_busy(&fh->vb_vidq)) { + if (vb2_is_busy(&dev->vb_vidq)) { pr_info("%s queue busy\n", __func__); rc = -EBUSY; goto out; @@ -1359,8 +1208,16 @@ out: static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = video_drvdata(file); + + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dev->std_set_in_tuner_core, dev->dev_state); + + if (norm == dev->std) + return 0; + + if (dev->streaming_users > 0) + return -EBUSY; dev->std = norm; @@ -1383,8 +1240,10 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = video_drvdata(file); + + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dev->std_set_in_tuner_core, dev->dev_state); *norm = dev->std; return 0; @@ -1393,8 +1252,7 @@ static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *input) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = video_drvdata(file); unsigned int tmp; static const char *inames[] = { @@ -1407,6 +1265,9 @@ static int vidioc_enum_input(struct file *file, void *priv, [AU0828_VMUX_DEBUG] = "tv debug" }; + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dev->std_set_in_tuner_core, dev->dev_state); + tmp = input->index; if (tmp >= AU0828_MAX_INPUT) @@ -1432,8 +1293,11 @@ static int vidioc_enum_input(struct file *file, void *priv, static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = video_drvdata(file); + + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dev->std_set_in_tuner_core, dev->dev_state); + *i = dev->ctrl_input; return 0; } @@ -1442,6 +1306,9 @@ static void au0828_s_input(struct au0828_dev *dev, int index) { int i; + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dev->std_set_in_tuner_core, dev->dev_state); + switch (AUVI_INPUT(index).type) { case AU0828_VMUX_SVIDEO: dev->input_type = AU0828_VMUX_SVIDEO; @@ -1491,8 +1358,7 @@ static void au0828_s_input(struct au0828_dev *dev, int index) static int vidioc_s_input(struct file *file, void *priv, unsigned int index) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = video_drvdata(file); dprintk(1, "VIDIOC_S_INPUT in function %s, input=%d\n", __func__, index); @@ -1510,6 +1376,8 @@ static int vidioc_enumaudio(struct file *file, void *priv, struct v4l2_audio *a) if (a->index > 1) return -EINVAL; + dprintk(1, "%s called\n", __func__); + if (a->index == 0) strcpy(a->name, "Television"); else @@ -1521,8 +1389,10 @@ static int vidioc_enumaudio(struct file *file, void *priv, struct v4l2_audio *a) static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = video_drvdata(file); + + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dev->std_set_in_tuner_core, dev->dev_state); a->index = dev->ctrl_ainput; if (a->index == 0) @@ -1536,22 +1406,26 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = video_drvdata(file); if (a->index != dev->ctrl_ainput) return -EINVAL; + + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dev->std_set_in_tuner_core, dev->dev_state); return 0; } static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = video_drvdata(file); if (t->index != 0) return -EINVAL; + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dev->std_set_in_tuner_core, dev->dev_state); + strcpy(t->name, "Auvitek tuner"); au0828_init_tuner(dev); @@ -1564,12 +1438,14 @@ static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) static int vidioc_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *t) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = video_drvdata(file); if (t->index != 0) return -EINVAL; + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dev->std_set_in_tuner_core, dev->dev_state); + au0828_init_tuner(dev); i2c_gate_ctrl(dev, 1); v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); @@ -1585,11 +1461,12 @@ static int vidioc_s_tuner(struct file *file, void *priv, static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *freq) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = video_drvdata(file); if (freq->tuner != 0) return -EINVAL; + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dev->std_set_in_tuner_core, dev->dev_state); freq->frequency = dev->ctrl_freq; return 0; } @@ -1597,13 +1474,15 @@ static int vidioc_g_frequency(struct file *file, void *priv, static int vidioc_s_frequency(struct file *file, void *priv, const struct v4l2_frequency *freq) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = video_drvdata(file); struct v4l2_frequency new_freq = *freq; if (freq->tuner != 0) return -EINVAL; + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dev->std_set_in_tuner_core, dev->dev_state); + au0828_init_tuner(dev); i2c_gate_ctrl(dev, 1); @@ -1625,8 +1504,10 @@ static int vidioc_s_frequency(struct file *file, void *priv, static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, struct v4l2_format *format) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = video_drvdata(file); + + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dev->std_set_in_tuner_core, dev->dev_state); format->fmt.vbi.samples_per_line = dev->vbi_width; format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; @@ -1646,12 +1527,14 @@ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, static int vidioc_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cc) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = video_drvdata(file); if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dev->std_set_in_tuner_core, dev->dev_state); + cc->bounds.left = 0; cc->bounds.top = 0; cc->bounds.width = dev->width; @@ -1665,105 +1548,14 @@ static int vidioc_cropcap(struct file *file, void *priv, return 0; } -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - int rc = -EINVAL; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (unlikely(type != fh->type)) - return -EINVAL; - - dprintk(1, "vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n", - fh, type, fh->resources, dev->resources); - - if (unlikely(!res_get(fh, get_ressource(fh)))) - return -EBUSY; - - au0828_init_tuner(dev); - if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - au0828_analog_stream_enable(dev); - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1); - } - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - rc = videobuf_streamon(&fh->vb_vidq); - dev->vid_timeout_running = 1; - mod_timer(&dev->vid_timeout, jiffies + (HZ / 10)); - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - rc = videobuf_streamon(&fh->vb_vbiq); - dev->vbi_timeout_running = 1; - mod_timer(&dev->vbi_timeout, jiffies + (HZ / 10)); - } - - return rc; -} - -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - int rc; - int i; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) - return -EINVAL; - if (type != fh->type) - return -EINVAL; - - dprintk(1, "vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n", - fh, type, fh->resources, dev->resources); - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dev->vid_timeout_running = 0; - del_timer_sync(&dev->vid_timeout); - - au0828_analog_stream_disable(dev); - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); - rc = au0828_stream_interrupt(dev); - if (rc != 0) - return rc; - - for (i = 0; i < AU0828_MAX_INPUT; i++) { - if (AUVI_INPUT(i).audio_setup == NULL) - continue; - (AUVI_INPUT(i).audio_setup)(dev, 0); - } - - if (res_check(fh, AU0828_RESOURCE_VIDEO)) { - videobuf_streamoff(&fh->vb_vidq); - res_free(fh, AU0828_RESOURCE_VIDEO); - } - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - dev->vbi_timeout_running = 0; - del_timer_sync(&dev->vbi_timeout); - - if (res_check(fh, AU0828_RESOURCE_VBI)) { - videobuf_streamoff(&fh->vb_vbiq); - res_free(fh, AU0828_RESOURCE_VBI); - } - } - - return 0; -} - #ifdef CONFIG_VIDEO_ADV_DEBUG static int vidioc_g_register(struct file *file, void *priv, struct v4l2_dbg_register *reg) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = video_drvdata(file); + + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dev->std_set_in_tuner_core, dev->dev_state); reg->val = au0828_read(dev, reg->reg); reg->size = 1; @@ -1773,8 +1565,10 @@ static int vidioc_g_register(struct file *file, void *priv, static int vidioc_s_register(struct file *file, void *priv, const struct v4l2_dbg_register *reg) { - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; + struct au0828_dev *dev = video_drvdata(file); + + dprintk(1, "%s called std_set %d dev_state %d\n", __func__, + dev->std_set_in_tuner_core, dev->dev_state); return au0828_writereg(dev, reg->reg, reg->val); } @@ -1784,93 +1578,13 @@ static int vidioc_log_status(struct file *file, void *fh) { struct video_device *vdev = video_devdata(file); + dprintk(1, "%s called\n", __func__); + v4l2_ctrl_log_status(file, fh); v4l2_device_call_all(vdev->v4l2_dev, 0, core, log_status); return 0; } -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *rb) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - rc = videobuf_reqbufs(&fh->vb_vidq, rb); - else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) - rc = videobuf_reqbufs(&fh->vb_vbiq, rb); - - return rc; -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *b) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - rc = videobuf_querybuf(&fh->vb_vidq, b); - else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) - rc = videobuf_querybuf(&fh->vb_vbiq, b); - - return rc; -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - rc = videobuf_qbuf(&fh->vb_vidq, b); - else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) - rc = videobuf_qbuf(&fh->vb_vbiq, b); - - return rc; -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct au0828_fh *fh = priv; - struct au0828_dev *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - /* Workaround for a bug in the au0828 hardware design that sometimes - results in the colorspace being inverted */ - if (dev->greenscreen_detected == 1) { - dprintk(1, "Detected green frame. Resetting stream...\n"); - au0828_analog_stream_reset(dev); - dev->greenscreen_detected = 0; - } - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) - rc = videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK); - else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) - rc = videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags & O_NONBLOCK); - - return rc; -} - void au0828_v4l2_suspend(struct au0828_dev *dev) { struct urb *urb; @@ -1938,9 +1652,9 @@ static struct v4l2_file_operations au0828_v4l_fops = { .owner = THIS_MODULE, .open = au0828_v4l2_open, .release = au0828_v4l2_close, - .read = au0828_v4l2_read, - .poll = au0828_v4l2_poll, - .mmap = au0828_v4l2_mmap, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, .unlocked_ioctl = video_ioctl2, }; @@ -1957,17 +1671,24 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, .vidioc_cropcap = vidioc_cropcap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_s_std = vidioc_s_std, .vidioc_g_std = vidioc_g_std, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, + + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, @@ -1988,6 +1709,42 @@ static const struct video_device au0828_video_template = { .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_M, }; +static int au0828_vb2_setup(struct au0828_dev *dev) +{ + int rc; + struct vb2_queue *q; + + /* Setup Videobuf2 for Video capture */ + q = &dev->vb_vidq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct au0828_buffer); + q->ops = &au0828_video_qops; + q->mem_ops = &vb2_vmalloc_memops; + + rc = vb2_queue_init(q); + if (rc < 0) + return rc; + + /* Setup Videobuf2 for VBI capture */ + q = &dev->vb_vbiq; + q->type = V4L2_BUF_TYPE_VBI_CAPTURE; + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct au0828_buffer); + q->ops = &au0828_vbi_qops; + q->mem_ops = &vb2_vmalloc_memops; + + rc = vb2_queue_init(q); + if (rc < 0) + return rc; + + return 0; +} + /**************************************************************************/ int au0828_analog_register(struct au0828_dev *dev, @@ -2030,7 +1787,6 @@ int au0828_analog_register(struct au0828_dev *dev, } if (!(dev->isoc_in_endpointaddr)) { pr_info("Could not locate isoc endpoint\n"); - kfree(dev); return -ENODEV; } @@ -2039,17 +1795,12 @@ int au0828_analog_register(struct au0828_dev *dev, /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); - INIT_LIST_HEAD(&dev->vidq.queued); INIT_LIST_HEAD(&dev->vbiq.active); - INIT_LIST_HEAD(&dev->vbiq.queued); - - dev->vid_timeout.function = au0828_vid_buffer_timeout; - dev->vid_timeout.data = (unsigned long) dev; - init_timer(&dev->vid_timeout); - dev->vbi_timeout.function = au0828_vbi_buffer_timeout; - dev->vbi_timeout.data = (unsigned long) dev; - init_timer(&dev->vbi_timeout); + setup_timer(&dev->vid_timeout, au0828_vid_buffer_timeout, + (unsigned long)dev); + setup_timer(&dev->vbi_timeout, au0828_vbi_buffer_timeout, + (unsigned long)dev); dev->width = NTSC_STD_W; dev->height = NTSC_STD_H; @@ -2078,18 +1829,34 @@ int au0828_analog_register(struct au0828_dev *dev, goto err_vdev; } + mutex_init(&dev->vb_queue_lock); + mutex_init(&dev->vb_vbi_queue_lock); + /* Fill the video capture device struct */ *dev->vdev = au0828_video_template; dev->vdev->v4l2_dev = &dev->v4l2_dev; dev->vdev->lock = &dev->lock; + dev->vdev->queue = &dev->vb_vidq; + dev->vdev->queue->lock = &dev->vb_queue_lock; strcpy(dev->vdev->name, "au0828a video"); /* Setup the VBI device */ *dev->vbi_dev = au0828_video_template; dev->vbi_dev->v4l2_dev = &dev->v4l2_dev; dev->vbi_dev->lock = &dev->lock; + dev->vbi_dev->queue = &dev->vb_vbiq; + dev->vbi_dev->queue->lock = &dev->vb_vbi_queue_lock; strcpy(dev->vbi_dev->name, "au0828a vbi"); + /* initialize videobuf2 stuff */ + retval = au0828_vb2_setup(dev); + if (retval != 0) { + dprintk(1, "unable to setup videobuf2 queues (error = %d).\n", + retval); + ret = -ENODEV; + goto err_vbi_dev; + } + /* Register the v4l2 device */ video_set_drvdata(dev->vdev, dev); retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, -1); @@ -2097,7 +1864,7 @@ int au0828_analog_register(struct au0828_dev *dev, dprintk(1, "unable to register video device (error = %d).\n", retval); ret = -ENODEV; - goto err_vbi_dev; + goto err_reg_vdev; } /* Register the vbi device */ @@ -2107,13 +1874,18 @@ int au0828_analog_register(struct au0828_dev *dev, dprintk(1, "unable to register vbi device (error = %d).\n", retval); ret = -ENODEV; - goto err_vbi_dev; + goto err_reg_vbi_dev; } dprintk(1, "%s completed!\n", __func__); return 0; +err_reg_vbi_dev: + video_unregister_device(dev->vdev); +err_reg_vdev: + vb2_queue_release(&dev->vb_vidq); + vb2_queue_release(&dev->vb_vbiq); err_vbi_dev: video_device_release(dev->vbi_dev); err_vdev: diff --git a/drivers/media/usb/au0828/au0828.h b/drivers/media/usb/au0828/au0828.h index 36815a369c68..eb1518742ae6 100644 --- a/drivers/media/usb/au0828/au0828.h +++ b/drivers/media/usb/au0828/au0828.h @@ -28,7 +28,7 @@ /* Analog */ #include <linux/videodev2.h> -#include <media/videobuf-vmalloc.h> +#include <media/videobuf2-vmalloc.h> #include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-fh.h> @@ -126,17 +126,7 @@ enum au0828_dev_state { DEV_MISCONFIGURED = 0x04 }; -struct au0828_fh { - /* must be the first field of this struct! */ - struct v4l2_fh fh; - - struct au0828_dev *dev; - unsigned int resources; - - struct videobuf_queue vb_vidq; - struct videobuf_queue vb_vbiq; - enum v4l2_buf_type type; -}; +struct au0828_dev; struct au0828_usb_isoc_ctl { /* max packet size of isoc transaction */ @@ -177,21 +167,20 @@ struct au0828_usb_isoc_ctl { /* buffer for one video frame */ struct au0828_buffer { /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; + struct vb2_buffer vb; + struct list_head list; - struct list_head frame; + void *mem; + unsigned long length; int top_field; - int receiving; + /* pointer to vmalloc memory address in vb */ + char *vb_buf; }; struct au0828_dmaqueue { struct list_head active; - struct list_head queued; - - wait_queue_head_t wq; - /* Counters to control buffer fill */ - int pos; + int pos; }; struct au0828_dev { @@ -220,14 +209,26 @@ struct au0828_dev { struct au0828_rc *ir; #endif - int users; - unsigned int resources; /* resources in use */ struct video_device *vdev; struct video_device *vbi_dev; + + /* Videobuf2 */ + struct vb2_queue vb_vidq; + struct vb2_queue vb_vbiq; + struct mutex vb_queue_lock; + struct mutex vb_vbi_queue_lock; + + unsigned int frame_count; + unsigned int vbi_frame_count; + struct timer_list vid_timeout; int vid_timeout_running; struct timer_list vbi_timeout; int vbi_timeout_running; + + int users; + int streaming_users; + int width; int height; int vbi_width; @@ -242,7 +243,6 @@ struct au0828_dev { __u8 isoc_in_endpointaddr; u8 isoc_init_ok; int greenscreen_detected; - unsigned int frame_count; int ctrl_freq; int input_type; int std_set_in_tuner_core; @@ -277,6 +277,7 @@ struct au0828_dev { char *dig_transfer_buffer[URB_COUNT]; }; + /* ----------------------------------------------------------- */ #define au0828_read(dev, reg) au0828_readreg(dev, reg) #define au0828_write(dev, reg, value) au0828_writereg(dev, reg, value) @@ -309,13 +310,15 @@ extern int au0828_i2c_unregister(struct au0828_dev *dev); /* ----------------------------------------------------------- */ /* au0828-video.c */ -int au0828_analog_register(struct au0828_dev *dev, +extern int au0828_analog_register(struct au0828_dev *dev, struct usb_interface *interface); -int au0828_analog_stream_disable(struct au0828_dev *d); -void au0828_analog_unregister(struct au0828_dev *dev); +extern void au0828_analog_unregister(struct au0828_dev *dev); +extern int au0828_start_analog_streaming(struct vb2_queue *vq, + unsigned int count); +extern void au0828_stop_vbi_streaming(struct vb2_queue *vq); #ifdef CONFIG_VIDEO_AU0828_V4L2 -void au0828_v4l2_suspend(struct au0828_dev *dev); -void au0828_v4l2_resume(struct au0828_dev *dev); +extern void au0828_v4l2_suspend(struct au0828_dev *dev); +extern void au0828_v4l2_resume(struct au0828_dev *dev); #else static inline void au0828_v4l2_suspend(struct au0828_dev *dev) { }; static inline void au0828_v4l2_resume(struct au0828_dev *dev) { }; @@ -329,7 +332,7 @@ void au0828_dvb_suspend(struct au0828_dev *dev); void au0828_dvb_resume(struct au0828_dev *dev); /* au0828-vbi.c */ -extern struct videobuf_queue_ops au0828_vbi_qops; +extern struct vb2_ops au0828_vbi_qops; #define dprintk(level, fmt, arg...)\ do { if (au0828_debug & level)\ diff --git a/drivers/media/usb/cx231xx/cx231xx-cards.c b/drivers/media/usb/cx231xx/cx231xx-cards.c index ae05d591f228..da03733690bd 100644 --- a/drivers/media/usb/cx231xx/cx231xx-cards.c +++ b/drivers/media/usb/cx231xx/cx231xx-cards.c @@ -1403,7 +1403,6 @@ static int cx231xx_usb_probe(struct usb_interface *interface, struct usb_interface_assoc_descriptor *assoc_desc; ifnum = interface->altsetting[0].desc.bInterfaceNumber; - udev = usb_get_dev(interface_to_usbdev(interface)); /* * Interface number 0 - IR interface (handled by mceusb driver) @@ -1424,11 +1423,13 @@ static int cx231xx_usb_probe(struct usb_interface *interface, } } while (test_and_set_bit(nr, &cx231xx_devused)); + udev = usb_get_dev(interface_to_usbdev(interface)); + /* allocate memory for our device state and initialize it */ dev = devm_kzalloc(&udev->dev, sizeof(*dev), GFP_KERNEL); if (dev == NULL) { - clear_bit(nr, &cx231xx_devused); - return -ENOMEM; + retval = -ENOMEM; + goto err_if; } snprintf(dev->name, 29, "cx231xx #%d", nr); @@ -1582,7 +1583,7 @@ err_v4l2: usb_set_intfdata(interface, NULL); err_if: usb_put_dev(udev); - clear_bit(dev->devno, &cx231xx_devused); + clear_bit(nr, &cx231xx_devused); return retval; } diff --git a/drivers/media/usb/cx231xx/cx231xx-video.c b/drivers/media/usb/cx231xx/cx231xx-video.c index 53ca12c1ff69..ecea76fe07f6 100644 --- a/drivers/media/usb/cx231xx/cx231xx-video.c +++ b/drivers/media/usb/cx231xx/cx231xx-video.c @@ -2062,7 +2062,6 @@ static struct video_device *cx231xx_vdev_init(struct cx231xx *dev, *vfd = *template; vfd->v4l2_dev = &dev->v4l2_dev; vfd->release = video_device_release; - vfd->debug = video_debug; vfd->lock = &dev->lock; snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); diff --git a/drivers/media/usb/cx231xx/cx231xx.h b/drivers/media/usb/cx231xx/cx231xx.h index f9e262eb0db9..6d6f3ee812f6 100644 --- a/drivers/media/usb/cx231xx/cx231xx.h +++ b/drivers/media/usb/cx231xx/cx231xx.h @@ -532,15 +532,7 @@ struct cx231xx_video_mode { unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ u16 end_point_addr; }; -/* -struct cx23885_dmaqueue { - struct list_head active; - struct list_head queued; - struct timer_list timeout; - struct btcx_riscmem stopper; - u32 count; -}; -*/ + struct cx231xx_tsport { struct cx231xx *dev; diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb.h b/drivers/media/usb/dvb-usb-v2/dvb_usb.h index 14e111e13e54..41c6363dff08 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb.h +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb.h @@ -354,6 +354,7 @@ struct dvb_usb_adapter { * @name: device name * @rc_map: name of rc codes table * @rc_polling_active: set when RC polling is active + * @intf: pointer to the device's struct usb_interface * @udev: pointer to the device's struct usb_device * @rc: remote controller configuration * @powered: indicated whether the device is power or not @@ -370,6 +371,7 @@ struct dvb_usb_device { const char *name; const char *rc_map; bool rc_polling_active; + struct usb_interface *intf; struct usb_device *udev; struct dvb_usb_rc rc; int powered; diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index 1950f37df835..9913e0f59485 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -868,6 +868,7 @@ int dvb_usbv2_probe(struct usb_interface *intf, goto err; } + d->intf = intf; d->name = driver_info->name; d->rc_map = driver_info->rc_map; d->udev = udev; diff --git a/drivers/media/usb/dvb-usb-v2/lmedm04.c b/drivers/media/usb/dvb-usb-v2/lmedm04.c index 994de53a574b..5de6f7c04d09 100644 --- a/drivers/media/usb/dvb-usb-v2/lmedm04.c +++ b/drivers/media/usb/dvb-usb-v2/lmedm04.c @@ -126,9 +126,9 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); struct lme2510_state { unsigned long int_urb_due; + fe_status_t lock_status; u8 id; u8 tuner_config; - u8 signal_lock; u8 signal_level; u8 signal_sn; u8 time_key; @@ -143,6 +143,12 @@ struct lme2510_state { void *buffer; struct urb *lme_urb; void *usb_buffer; + /* Frontend original calls */ + int (*fe_read_status)(struct dvb_frontend *, fe_status_t *); + int (*fe_read_signal_strength)(struct dvb_frontend *, u16 *); + int (*fe_read_snr)(struct dvb_frontend *, u16 *); + int (*fe_read_ber)(struct dvb_frontend *, u32 *); + int (*fe_read_ucblocks)(struct dvb_frontend *, u32 *); int (*fe_set_voltage)(struct dvb_frontend *, fe_sec_voltage_t); u8 dvb_usb_lme2510_firmware; }; @@ -258,6 +264,7 @@ static void lme2510_int_response(struct urb *lme_urb) static u8 *ibuf, *rbuf; int i = 0, offset; u32 key; + u8 signal_lock = 0; switch (lme_urb->status) { case 0: @@ -298,8 +305,7 @@ static void lme2510_int_response(struct urb *lme_urb) case 0xbb: switch (st->tuner_config) { case TUNER_LG: - if (ibuf[2] > 0) - st->signal_lock = ibuf[2]; + signal_lock = ibuf[2] & BIT(5); st->signal_level = ibuf[4]; st->signal_sn = ibuf[3]; st->time_key = ibuf[7]; @@ -308,29 +314,29 @@ static void lme2510_int_response(struct urb *lme_urb) case TUNER_S0194: /* Tweak for earlier firmware*/ if (ibuf[1] == 0x03) { - if (ibuf[2] > 1) - st->signal_lock = ibuf[2]; + signal_lock = ibuf[2] & BIT(4); st->signal_level = ibuf[3]; st->signal_sn = ibuf[4]; } else { st->signal_level = ibuf[4]; st->signal_sn = ibuf[5]; - st->signal_lock = - (st->signal_lock & 0xf7) + - ((ibuf[2] & 0x01) << 0x03); } break; case TUNER_RS2000: - if (ibuf[2] & 0x1) - st->signal_lock = 0xff; - else - st->signal_lock = 0x00; + signal_lock = ibuf[2] & 0xee; st->signal_level = ibuf[5]; st->signal_sn = ibuf[4]; st->time_key = ibuf[7]; default: break; } + + /* Interrupt will also throw just BIT 0 as lock */ + signal_lock |= ibuf[2] & BIT(0); + + if (!signal_lock) + st->lock_status &= ~FE_HAS_LOCK; + debug_data_snipet(5, "INT Remote data snipet in", ibuf); break; case 0xcc: @@ -344,15 +350,17 @@ static void lme2510_int_response(struct urb *lme_urb) usb_submit_urb(lme_urb, GFP_ATOMIC); - /* interrupt urb is due every 48 msecs while streaming - * add 12msecs for system lag */ - st->int_urb_due = jiffies + msecs_to_jiffies(60); + /* Interrupt urb is due every 48 msecs while streaming the buffer + * stores up to 4 periods if missed. Allow 200 msec for next interrupt. + */ + st->int_urb_due = jiffies + msecs_to_jiffies(200); } static int lme2510_int_read(struct dvb_usb_adapter *adap) { struct dvb_usb_device *d = adap_to_d(adap); struct lme2510_state *lme_int = adap_to_priv(adap); + struct usb_host_endpoint *ep; lme_int->lme_urb = usb_alloc_urb(0, GFP_ATOMIC); @@ -374,6 +382,12 @@ static int lme2510_int_read(struct dvb_usb_adapter *adap) adap, 8); + /* Quirk of pipe reporting PIPE_BULK but behaves as interrupt */ + ep = usb_pipe_endpoint(d->udev, lme_int->lme_urb->pipe); + + if (usb_endpoint_type(&ep->desc) == USB_ENDPOINT_XFER_BULK) + lme_int->lme_urb->pipe = usb_rcvbulkpipe(d->udev, 0xa), + lme_int->lme_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; usb_submit_urb(lme_int->lme_urb, GFP_ATOMIC); @@ -449,170 +463,13 @@ static int lme2510_return_status(struct dvb_usb_device *d) static int lme2510_msg(struct dvb_usb_device *d, u8 *wbuf, int wlen, u8 *rbuf, int rlen) { - int ret = 0; struct lme2510_state *st = d->priv; - if (st->i2c_talk_onoff == 1) { - - ret = lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); - - switch (st->tuner_config) { - case TUNER_LG: - if (wbuf[2] == 0x1c) { - if (wbuf[3] == 0x0e) { - st->signal_lock = rbuf[1]; - if ((st->stream_on & 1) && - (st->signal_lock & 0x10)) { - lme2510_stream_restart(d); - st->i2c_talk_onoff = 0; - } - msleep(80); - } - } - break; - case TUNER_S7395: - if (wbuf[2] == 0xd0) { - if (wbuf[3] == 0x24) { - st->signal_lock = rbuf[1]; - if ((st->stream_on & 1) && - (st->signal_lock & 0x8)) { - lme2510_stream_restart(d); - st->i2c_talk_onoff = 0; - } - } - } - break; - case TUNER_S0194: - if (wbuf[2] == 0xd0) { - if (wbuf[3] == 0x1b) { - st->signal_lock = rbuf[1]; - if ((st->stream_on & 1) && - (st->signal_lock & 0x8)) { - lme2510_stream_restart(d); - st->i2c_talk_onoff = 0; - } - } - } - break; - case TUNER_RS2000: - default: - break; - } - } else { - /* TODO rewrite this section */ - switch (st->tuner_config) { - case TUNER_LG: - switch (wbuf[3]) { - case 0x0e: - rbuf[0] = 0x55; - rbuf[1] = st->signal_lock; - break; - case 0x43: - rbuf[0] = 0x55; - rbuf[1] = st->signal_level; - break; - case 0x1c: - rbuf[0] = 0x55; - rbuf[1] = st->signal_sn; - break; - case 0x15: - case 0x16: - case 0x17: - case 0x18: - rbuf[0] = 0x55; - rbuf[1] = 0x00; - break; - default: - lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); - st->i2c_talk_onoff = 1; - break; - } - break; - case TUNER_S7395: - switch (wbuf[3]) { - case 0x10: - rbuf[0] = 0x55; - rbuf[1] = (st->signal_level & 0x80) - ? 0 : (st->signal_level * 2); - break; - case 0x2d: - rbuf[0] = 0x55; - rbuf[1] = st->signal_sn; - break; - case 0x24: - rbuf[0] = 0x55; - rbuf[1] = st->signal_lock; - break; - case 0x2e: - case 0x26: - case 0x27: - rbuf[0] = 0x55; - rbuf[1] = 0x00; - break; - default: - lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); - st->i2c_talk_onoff = 1; - break; - } - break; - case TUNER_S0194: - switch (wbuf[3]) { - case 0x18: - rbuf[0] = 0x55; - rbuf[1] = (st->signal_level & 0x80) - ? 0 : (st->signal_level * 2); - break; - case 0x24: - rbuf[0] = 0x55; - rbuf[1] = st->signal_sn; - break; - case 0x1b: - rbuf[0] = 0x55; - rbuf[1] = st->signal_lock; - break; - case 0x19: - case 0x25: - case 0x1e: - case 0x1d: - rbuf[0] = 0x55; - rbuf[1] = 0x00; - break; - default: - lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); - st->i2c_talk_onoff = 1; - break; - } - break; - case TUNER_RS2000: - switch (wbuf[3]) { - case 0x8c: - rbuf[0] = 0x55; - rbuf[1] = st->signal_lock; - - /* If int_urb_due overdue - * set rbuf[1] to 0 to clear lock */ - if (time_after(jiffies, st->int_urb_due)) - rbuf[1] = 0; - - break; - default: - lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); - st->i2c_talk_onoff = 1; - break; - } - default: - break; - } - - deb_info(4, "I2C From Interrupt Message out(%02x) in(%02x)", - wbuf[3], rbuf[1]); - - } + st->i2c_talk_onoff = 1; - return ret; + return lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen); } - static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { @@ -935,26 +792,8 @@ static struct stv0299_config sharp_z0194_config = { .set_symbol_rate = sharp_z0194a_set_symbol_rate, }; -static int dm04_rs2000_set_ts_param(struct dvb_frontend *fe, - int caller) -{ - struct dvb_usb_adapter *adap = fe_to_adap(fe); - struct dvb_usb_device *d = adap_to_d(adap); - struct lme2510_state *st = d->priv; - - mutex_lock(&d->i2c_mutex); - if ((st->i2c_talk_onoff == 1) && (st->stream_on & 1)) { - st->i2c_talk_onoff = 0; - lme2510_stream_restart(d); - } - mutex_unlock(&d->i2c_mutex); - - return 0; -} - static struct m88rs2000_config m88rs2000_config = { - .demod_addr = 0x68, - .set_ts_params = dm04_rs2000_set_ts_param, + .demod_addr = 0x68 }; static struct ts2020_config ts2020_config = { @@ -998,27 +837,101 @@ static int dm04_lme2510_set_voltage(struct dvb_frontend *fe, return (ret < 0) ? -ENODEV : 0; } -static int dm04_rs2000_read_signal_strength(struct dvb_frontend *fe, - u16 *strength) +static int dm04_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct dvb_usb_device *d = fe_to_d(fe); + struct lme2510_state *st = d->priv; + int ret = 0; + + if (st->i2c_talk_onoff) { + if (st->fe_read_status) { + ret = st->fe_read_status(fe, status); + if (ret < 0) + return ret; + } + + st->lock_status = *status; + + if (*status & FE_HAS_LOCK && st->stream_on) { + mutex_lock(&d->i2c_mutex); + + st->i2c_talk_onoff = 0; + ret = lme2510_stream_restart(d); + + mutex_unlock(&d->i2c_mutex); + } + + return ret; + } + + /* Timeout of interrupt reached on RS2000 */ + if (st->tuner_config == TUNER_RS2000 && + time_after(jiffies, st->int_urb_due)) + st->lock_status &= ~FE_HAS_LOCK; + + *status = st->lock_status; + + if (!(*status & FE_HAS_LOCK)) + st->i2c_talk_onoff = 1; + + return ret; +} + +static int dm04_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { struct lme2510_state *st = fe_to_priv(fe); - *strength = (u16)((u32)st->signal_level * 0xffff / 0xff); + if (st->fe_read_signal_strength && !st->stream_on) + return st->fe_read_signal_strength(fe, strength); + + switch (st->tuner_config) { + case TUNER_LG: + *strength = 0xff - st->signal_level; + *strength |= *strength << 8; + break; + /* fall through */ + case TUNER_S7395: + case TUNER_S0194: + *strength = 0xffff - (((st->signal_level * 2) << 8) * 5 / 4); + break; + case TUNER_RS2000: + *strength = (u16)((u32)st->signal_level * 0xffff / 0xff); + } return 0; } -static int dm04_rs2000_read_snr(struct dvb_frontend *fe, u16 *snr) +static int dm04_read_snr(struct dvb_frontend *fe, u16 *snr) { struct lme2510_state *st = fe_to_priv(fe); - *snr = (u16)((u32)st->signal_sn * 0xffff / 0x7f); + if (st->fe_read_snr && !st->stream_on) + return st->fe_read_snr(fe, snr); + + switch (st->tuner_config) { + case TUNER_LG: + *snr = 0xff - st->signal_sn; + *snr |= *snr << 8; + break; + /* fall through */ + case TUNER_S7395: + case TUNER_S0194: + *snr = (u16)((0xff - st->signal_sn - 0xa1) * 3) << 8; + break; + case TUNER_RS2000: + *snr = (u16)((u32)st->signal_sn * 0xffff / 0x7f); + } return 0; } static int dm04_read_ber(struct dvb_frontend *fe, u32 *ber) { + struct lme2510_state *st = fe_to_priv(fe); + + if (st->fe_read_ber && !st->stream_on) + return st->fe_read_ber(fe, ber); + *ber = 0; return 0; @@ -1026,6 +939,11 @@ static int dm04_read_ber(struct dvb_frontend *fe, u32 *ber) static int dm04_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { + struct lme2510_state *st = fe_to_priv(fe); + + if (st->fe_read_ucblocks && !st->stream_on) + return st->fe_read_ucblocks(fe, ucblocks); + *ucblocks = 0; return 0; @@ -1119,15 +1037,6 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) st->tuner_config = TUNER_RS2000; st->fe_set_voltage = adap->fe[0]->ops.set_voltage; - - adap->fe[0]->ops.read_signal_strength = - dm04_rs2000_read_signal_strength; - adap->fe[0]->ops.read_snr = - dm04_rs2000_read_snr; - adap->fe[0]->ops.read_ber = - dm04_read_ber; - adap->fe[0]->ops.read_ucblocks = - dm04_read_ucblocks; } break; } @@ -1146,7 +1055,19 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap) return -ENODEV; } + st->fe_read_status = adap->fe[0]->ops.read_status; + st->fe_read_signal_strength = adap->fe[0]->ops.read_signal_strength; + st->fe_read_snr = adap->fe[0]->ops.read_snr; + st->fe_read_ber = adap->fe[0]->ops.read_ber; + st->fe_read_ucblocks = adap->fe[0]->ops.read_ucblocks; + + adap->fe[0]->ops.read_status = dm04_read_status; + adap->fe[0]->ops.read_signal_strength = dm04_read_signal_strength; + adap->fe[0]->ops.read_snr = dm04_read_snr; + adap->fe[0]->ops.read_ber = dm04_read_ber; + adap->fe[0]->ops.read_ucblocks = dm04_read_ucblocks; adap->fe[0]->ops.set_voltage = dm04_lme2510_set_voltage; + ret = lme_name(adap); return ret; } @@ -1288,7 +1209,6 @@ static void *lme2510_exit_int(struct dvb_usb_device *d) if (st->usb_buffer != NULL) { st->i2c_talk_onoff = 1; - st->signal_lock = 0; st->signal_level = 0; st->signal_sn = 0; buffer = st->usb_buffer; diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c index 0a98d04c53e4..ecefa5c477fa 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.c @@ -604,9 +604,3 @@ MODULE_DESCRIPTION("MaxLinear MxL111SF DVB-T demodulator driver"); MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); MODULE_LICENSE("GPL"); MODULE_VERSION("0.1"); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h index 2d4530f5be54..0bd83e52669c 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-demod.h @@ -47,9 +47,3 @@ struct dvb_frontend *mxl111sf_demod_attach(struct mxl111sf_state *mxl_state, #endif /* CONFIG_DVB_USB_MXL111SF */ #endif /* __MXL111SF_DEMOD_H__ */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c index a619410adde4..2180c13a6dcc 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.c @@ -755,9 +755,3 @@ int mxl111sf_gpio_mode_switch(struct mxl111sf_state *state, unsigned int mode) } return 0; } - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h index b85a5772d771..16fa4d4daf88 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-gpio.h @@ -48,9 +48,3 @@ int mxl111sf_config_pin_mux_modes(struct mxl111sf_state *state, enum mxl111sf_mux_config pin_mux_config); #endif /* _DVB_USB_MXL111SF_GPIO_H_ */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c index a101d06eb143..283495c84ba3 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.c @@ -842,9 +842,3 @@ int mxl111sf_i2c_xfer(struct i2c_adapter *adap, return i == num ? num : -EREMOTEIO; } - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h index 465762145ad2..c486fe02f018 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-i2c.h @@ -27,9 +27,3 @@ int mxl111sf_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num); #endif /* _DVB_USB_MXL111SF_I2C_H_ */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c index f6b348024bec..5b0191178f9f 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.c @@ -335,9 +335,3 @@ int mxl111sf_idac_config(struct mxl111sf_state *state, return ret; } - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h index 0643738de7de..25aa4a1ea755 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-phy.h @@ -45,9 +45,3 @@ int mxl111sf_idac_config(struct mxl111sf_state *state, u8 current_value, u8 hysteresis_value); #endif /* _DVB_USB_MXL111SF_PHY_H_ */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h index 89bf115e927e..1f4bfbcdbabb 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-reg.h @@ -171,9 +171,3 @@ #define V6_DIG_RF_PWR_MSB_REG 0x47 #endif /* _DVB_USB_MXL111SF_REG_H_ */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c index a8d2c7053674..444579be0b77 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.c @@ -515,11 +515,3 @@ MODULE_DESCRIPTION("MaxLinear MxL111SF CMOS tuner driver"); MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); MODULE_LICENSE("GPL"); MODULE_VERSION("0.1"); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h index 2046db22519e..e6caab21a197 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf-tuner.h @@ -77,12 +77,3 @@ struct dvb_frontend *mxl111sf_tuner_attach(struct dvb_frontend *fe, #endif #endif /* __MXL111SF_TUNER_H__ */ - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ - diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.c b/drivers/media/usb/dvb-usb-v2/mxl111sf.c index c3447eaf1104..bec12b0e076b 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf.c +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.c @@ -1425,9 +1425,3 @@ MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); MODULE_DESCRIPTION("Driver for MaxLinear MxL111SF"); MODULE_VERSION("1.0"); MODULE_LICENSE("GPL"); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/mxl111sf.h b/drivers/media/usb/dvb-usb-v2/mxl111sf.h index 8516c011b7cc..ee70df1f1e94 100644 --- a/drivers/media/usb/dvb-usb-v2/mxl111sf.h +++ b/drivers/media/usb/dvb-usb-v2/mxl111sf.h @@ -152,9 +152,3 @@ extern int dvb_usb_mxl111sf_debug; }) #endif /* _DVB_USB_MXL111SF_H_ */ - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index 896a225ee011..77dcfdf547ac 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -22,42 +22,6 @@ #include "rtl28xxu.h" -#include "rtl2830.h" -#include "rtl2832.h" -#include "rtl2832_sdr.h" -#include "mn88472.h" -#include "mn88473.h" - -#include "qt1010.h" -#include "mt2060.h" -#include "mxl5005s.h" -#include "fc0012.h" -#include "fc0013.h" -#include "e4000.h" -#include "fc2580.h" -#include "tua9001.h" -#include "r820t.h" - - -#ifdef CONFIG_MEDIA_ATTACH -#define dvb_attach_sdr(FUNCTION, ARGS...) ({ \ - void *__r = NULL; \ - typeof(&FUNCTION) __a = symbol_request(FUNCTION); \ - if (__a) { \ - __r = (void *) __a(ARGS); \ - if (__r == NULL) \ - symbol_put(FUNCTION); \ - } \ - __r; \ -}) - -#else -#define dvb_attach_sdr(FUNCTION, ARGS...) ({ \ - FUNCTION(ARGS); \ -}) - -#endif - static int rtl28xxu_disable_rc; module_param_named(disable_rc, rtl28xxu_disable_rc, int, 0644); MODULE_PARM_DESC(disable_rc, "disable RTL2832U remote controller"); @@ -65,20 +29,14 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req) { + struct rtl28xxu_dev *dev = d->priv; int ret; unsigned int pipe; u8 requesttype; - u8 *buf; - - buf = kmalloc(req->size, GFP_KERNEL); - if (!buf) { - ret = -ENOMEM; - goto err; - } if (req->index & CMD_WR_FLAG) { /* write */ - memcpy(buf, req->data, req->size); + memcpy(dev->buf, req->data, req->size); requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); pipe = usb_sndctrlpipe(d->udev, 0); } else { @@ -88,30 +46,23 @@ static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req) } ret = usb_control_msg(d->udev, pipe, 0, requesttype, req->value, - req->index, buf, req->size, 1000); - + req->index, dev->buf, req->size, 1000); dvb_usb_dbg_usb_control_msg(d->udev, 0, requesttype, req->value, - req->index, buf, req->size); - - if (ret > 0) - ret = 0; + req->index, dev->buf, req->size); + if (ret < 0) + goto err; /* read request, copy returned data to return buf */ - if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN)) - memcpy(req->data, buf, req->size); + if (requesttype == (USB_TYPE_VENDOR | USB_DIR_IN)) + memcpy(req->data, dev->buf, req->size); - kfree(buf); - - if (ret) - goto err; - - return ret; + return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&d->intf->dev, "failed=%d\n", ret); return ret; } -static int rtl28xx_wr_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len) +static int rtl28xxu_wr_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len) { struct rtl28xxu_req req; @@ -129,7 +80,7 @@ static int rtl28xx_wr_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len) return rtl28xxu_ctrl_msg(d, &req); } -static int rtl2831_rd_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len) +static int rtl28xxu_rd_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len) { struct rtl28xxu_req req; @@ -147,17 +98,17 @@ static int rtl2831_rd_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len) return rtl28xxu_ctrl_msg(d, &req); } -static int rtl28xx_wr_reg(struct dvb_usb_device *d, u16 reg, u8 val) +static int rtl28xxu_wr_reg(struct dvb_usb_device *d, u16 reg, u8 val) { - return rtl28xx_wr_regs(d, reg, &val, 1); + return rtl28xxu_wr_regs(d, reg, &val, 1); } -static int rtl28xx_rd_reg(struct dvb_usb_device *d, u16 reg, u8 *val) +static int rtl28xxu_rd_reg(struct dvb_usb_device *d, u16 reg, u8 *val) { - return rtl2831_rd_regs(d, reg, val, 1); + return rtl28xxu_rd_regs(d, reg, val, 1); } -static int rtl28xx_wr_reg_mask(struct dvb_usb_device *d, u16 reg, u8 val, +static int rtl28xxu_wr_reg_mask(struct dvb_usb_device *d, u16 reg, u8 val, u8 mask) { int ret; @@ -165,7 +116,7 @@ static int rtl28xx_wr_reg_mask(struct dvb_usb_device *d, u16 reg, u8 val, /* no need for read if whole reg is written */ if (mask != 0xff) { - ret = rtl28xx_rd_reg(d, reg, &tmp); + ret = rtl28xxu_rd_reg(d, reg, &tmp); if (ret) return ret; @@ -174,7 +125,7 @@ static int rtl28xx_wr_reg_mask(struct dvb_usb_device *d, u16 reg, u8 val, val |= tmp; } - return rtl28xx_wr_reg(d, reg, val); + return rtl28xxu_wr_reg(d, reg, val); } /* I2C */ @@ -183,7 +134,7 @@ static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], { int ret; struct dvb_usb_device *d = i2c_get_adapdata(adap); - struct rtl28xxu_priv *priv = d->priv; + struct rtl28xxu_dev *dev = d->priv; struct rtl28xxu_req req; /* @@ -220,7 +171,7 @@ static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], } else if (msg[0].addr == 0x10) { /* method 1 - integrated demod */ req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1); - req.index = CMD_DEMOD_RD | priv->page; + req.index = CMD_DEMOD_RD | dev->page; req.size = msg[1].len; req.data = &msg[1].buf[0]; ret = rtl28xxu_ctrl_msg(d, &req); @@ -256,12 +207,12 @@ static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], /* method 1 - integrated demod */ if (msg[0].buf[0] == 0x00) { /* save demod page for later demod access */ - priv->page = msg[0].buf[1]; + dev->page = msg[0].buf[1]; ret = 0; } else { req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1); - req.index = CMD_DEMOD_WR | priv->page; + req.index = CMD_DEMOD_WR | dev->page; req.size = msg[0].len-1; req.data = &msg[0].buf[1]; ret = rtl28xxu_ctrl_msg(d, &req); @@ -303,7 +254,7 @@ static struct i2c_algorithm rtl28xxu_i2c_algo = { static int rtl2831u_read_config(struct dvb_usb_device *d) { - struct rtl28xxu_priv *priv = d_to_priv(d); + struct rtl28xxu_dev *dev = d_to_priv(d); int ret; u8 buf[1]; /* open RTL2831U/RTL2830 I2C gate */ @@ -312,7 +263,7 @@ static int rtl2831u_read_config(struct dvb_usb_device *d) struct rtl28xxu_req req_mt2060 = {0x00c0, CMD_I2C_RD, 1, buf}; struct rtl28xxu_req req_qt1010 = {0x0fc4, CMD_I2C_RD, 1, buf}; - dev_dbg(&d->udev->dev, "%s:\n", __func__); + dev_dbg(&d->intf->dev, "\n"); /* * RTL2831U GPIOs @@ -323,12 +274,12 @@ static int rtl2831u_read_config(struct dvb_usb_device *d) */ /* GPIO direction */ - ret = rtl28xx_wr_reg(d, SYS_GPIO_DIR, 0x0a); + ret = rtl28xxu_wr_reg(d, SYS_GPIO_DIR, 0x0a); if (ret) goto err; /* enable as output GPIO0, GPIO2, GPIO4 */ - ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_EN, 0x15); + ret = rtl28xxu_wr_reg(d, SYS_GPIO_OUT_EN, 0x15); if (ret) goto err; @@ -340,7 +291,7 @@ static int rtl2831u_read_config(struct dvb_usb_device *d) /* demod needs some time to wake up */ msleep(20); - priv->tuner_name = "NONE"; + dev->tuner_name = "NONE"; /* open demod I2C gate */ ret = rtl28xxu_ctrl_msg(d, &req_gate_open); @@ -350,8 +301,8 @@ static int rtl2831u_read_config(struct dvb_usb_device *d) /* check QT1010 ID(?) register; reg=0f val=2c */ ret = rtl28xxu_ctrl_msg(d, &req_qt1010); if (ret == 0 && buf[0] == 0x2c) { - priv->tuner = TUNER_RTL2830_QT1010; - priv->tuner_name = "QT1010"; + dev->tuner = TUNER_RTL2830_QT1010; + dev->tuner_name = "QT1010"; goto found; } @@ -363,28 +314,28 @@ static int rtl2831u_read_config(struct dvb_usb_device *d) /* check MT2060 ID register; reg=00 val=63 */ ret = rtl28xxu_ctrl_msg(d, &req_mt2060); if (ret == 0 && buf[0] == 0x63) { - priv->tuner = TUNER_RTL2830_MT2060; - priv->tuner_name = "MT2060"; + dev->tuner = TUNER_RTL2830_MT2060; + dev->tuner_name = "MT2060"; goto found; } /* assume MXL5005S */ - priv->tuner = TUNER_RTL2830_MXL5005S; - priv->tuner_name = "MXL5005S"; + dev->tuner = TUNER_RTL2830_MXL5005S; + dev->tuner_name = "MXL5005S"; goto found; found: - dev_dbg(&d->udev->dev, "%s: tuner=%s\n", __func__, priv->tuner_name); + dev_dbg(&d->intf->dev, "tuner=%s\n", dev->tuner_name); return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&d->intf->dev, "failed=%d\n", ret); return ret; } static int rtl2832u_read_config(struct dvb_usb_device *d) { - struct rtl28xxu_priv *priv = d_to_priv(d); + struct rtl28xxu_dev *dev = d_to_priv(d); int ret; u8 buf[2]; /* open RTL2832U/RTL2832 I2C gate */ @@ -407,14 +358,14 @@ static int rtl2832u_read_config(struct dvb_usb_device *d) struct rtl28xxu_req req_mn88472 = {0xff38, CMD_I2C_RD, 1, buf}; struct rtl28xxu_req req_mn88473 = {0xff38, CMD_I2C_RD, 1, buf}; - dev_dbg(&d->udev->dev, "%s:\n", __func__); + dev_dbg(&d->intf->dev, "\n"); /* enable GPIO3 and GPIO6 as output */ - ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x40); + ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x40); if (ret) goto err; - ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x48, 0x48); + ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x48, 0x48); if (ret) goto err; @@ -428,134 +379,134 @@ static int rtl2832u_read_config(struct dvb_usb_device *d) if (ret) goto err; - priv->tuner_name = "NONE"; + dev->tuner_name = "NONE"; /* check FC0012 ID register; reg=00 val=a1 */ ret = rtl28xxu_ctrl_msg(d, &req_fc0012); if (ret == 0 && buf[0] == 0xa1) { - priv->tuner = TUNER_RTL2832_FC0012; - priv->tuner_name = "FC0012"; + dev->tuner = TUNER_RTL2832_FC0012; + dev->tuner_name = "FC0012"; goto tuner_found; } /* check FC0013 ID register; reg=00 val=a3 */ ret = rtl28xxu_ctrl_msg(d, &req_fc0013); if (ret == 0 && buf[0] == 0xa3) { - priv->tuner = TUNER_RTL2832_FC0013; - priv->tuner_name = "FC0013"; + dev->tuner = TUNER_RTL2832_FC0013; + dev->tuner_name = "FC0013"; goto tuner_found; } /* check MT2266 ID register; reg=00 val=85 */ ret = rtl28xxu_ctrl_msg(d, &req_mt2266); if (ret == 0 && buf[0] == 0x85) { - priv->tuner = TUNER_RTL2832_MT2266; - priv->tuner_name = "MT2266"; + dev->tuner = TUNER_RTL2832_MT2266; + dev->tuner_name = "MT2266"; goto tuner_found; } /* check FC2580 ID register; reg=01 val=56 */ ret = rtl28xxu_ctrl_msg(d, &req_fc2580); if (ret == 0 && buf[0] == 0x56) { - priv->tuner = TUNER_RTL2832_FC2580; - priv->tuner_name = "FC2580"; + dev->tuner = TUNER_RTL2832_FC2580; + dev->tuner_name = "FC2580"; goto tuner_found; } /* check MT2063 ID register; reg=00 val=9e || 9c */ ret = rtl28xxu_ctrl_msg(d, &req_mt2063); if (ret == 0 && (buf[0] == 0x9e || buf[0] == 0x9c)) { - priv->tuner = TUNER_RTL2832_MT2063; - priv->tuner_name = "MT2063"; + dev->tuner = TUNER_RTL2832_MT2063; + dev->tuner_name = "MT2063"; goto tuner_found; } /* check MAX3543 ID register; reg=00 val=38 */ ret = rtl28xxu_ctrl_msg(d, &req_max3543); if (ret == 0 && buf[0] == 0x38) { - priv->tuner = TUNER_RTL2832_MAX3543; - priv->tuner_name = "MAX3543"; + dev->tuner = TUNER_RTL2832_MAX3543; + dev->tuner_name = "MAX3543"; goto tuner_found; } /* check TUA9001 ID register; reg=7e val=2328 */ ret = rtl28xxu_ctrl_msg(d, &req_tua9001); if (ret == 0 && buf[0] == 0x23 && buf[1] == 0x28) { - priv->tuner = TUNER_RTL2832_TUA9001; - priv->tuner_name = "TUA9001"; + dev->tuner = TUNER_RTL2832_TUA9001; + dev->tuner_name = "TUA9001"; goto tuner_found; } /* check MXL5007R ID register; reg=d9 val=14 */ ret = rtl28xxu_ctrl_msg(d, &req_mxl5007t); if (ret == 0 && buf[0] == 0x14) { - priv->tuner = TUNER_RTL2832_MXL5007T; - priv->tuner_name = "MXL5007T"; + dev->tuner = TUNER_RTL2832_MXL5007T; + dev->tuner_name = "MXL5007T"; goto tuner_found; } /* check E4000 ID register; reg=02 val=40 */ ret = rtl28xxu_ctrl_msg(d, &req_e4000); if (ret == 0 && buf[0] == 0x40) { - priv->tuner = TUNER_RTL2832_E4000; - priv->tuner_name = "E4000"; + dev->tuner = TUNER_RTL2832_E4000; + dev->tuner_name = "E4000"; goto tuner_found; } /* check TDA18272 ID register; reg=00 val=c760 */ ret = rtl28xxu_ctrl_msg(d, &req_tda18272); if (ret == 0 && (buf[0] == 0xc7 || buf[1] == 0x60)) { - priv->tuner = TUNER_RTL2832_TDA18272; - priv->tuner_name = "TDA18272"; + dev->tuner = TUNER_RTL2832_TDA18272; + dev->tuner_name = "TDA18272"; goto tuner_found; } /* check R820T ID register; reg=00 val=69 */ ret = rtl28xxu_ctrl_msg(d, &req_r820t); if (ret == 0 && buf[0] == 0x69) { - priv->tuner = TUNER_RTL2832_R820T; - priv->tuner_name = "R820T"; + dev->tuner = TUNER_RTL2832_R820T; + dev->tuner_name = "R820T"; goto tuner_found; } /* check R828D ID register; reg=00 val=69 */ ret = rtl28xxu_ctrl_msg(d, &req_r828d); if (ret == 0 && buf[0] == 0x69) { - priv->tuner = TUNER_RTL2832_R828D; - priv->tuner_name = "R828D"; + dev->tuner = TUNER_RTL2832_R828D; + dev->tuner_name = "R828D"; goto tuner_found; } tuner_found: - dev_dbg(&d->udev->dev, "%s: tuner=%s\n", __func__, priv->tuner_name); + dev_dbg(&d->intf->dev, "tuner=%s\n", dev->tuner_name); /* probe slave demod */ - if (priv->tuner == TUNER_RTL2832_R828D) { + if (dev->tuner == TUNER_RTL2832_R828D) { /* power on MN88472 demod on GPIO0 */ - ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x01, 0x01); + ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x01, 0x01); if (ret) goto err; - ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x01); + ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x01); if (ret) goto err; - ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x01, 0x01); + ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x01, 0x01); if (ret) goto err; /* check MN88472 answers */ ret = rtl28xxu_ctrl_msg(d, &req_mn88472); if (ret == 0 && buf[0] == 0x02) { - dev_dbg(&d->udev->dev, "%s: MN88472 found\n", __func__); - priv->slave_demod = SLAVE_DEMOD_MN88472; + dev_dbg(&d->intf->dev, "MN88472 found\n"); + dev->slave_demod = SLAVE_DEMOD_MN88472; goto demod_found; } ret = rtl28xxu_ctrl_msg(d, &req_mn88473); if (ret == 0 && buf[0] == 0x03) { - dev_dbg(&d->udev->dev, "%s: MN88473 found\n", __func__); - priv->slave_demod = SLAVE_DEMOD_MN88473; + dev_dbg(&d->intf->dev, "MN88473 found\n"); + dev->slave_demod = SLAVE_DEMOD_MN88473; goto demod_found; } } @@ -568,14 +519,51 @@ demod_found: return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&d->intf->dev, "failed=%d\n", ret); return ret; } -static const struct rtl2830_config rtl28xxu_rtl2830_mt2060_config = { - .i2c_addr = 0x10, /* 0x20 */ - .xtal = 28800000, - .ts_mode = 0, +static int rtl28xxu_read_config(struct dvb_usb_device *d) +{ + struct rtl28xxu_dev *dev = d_to_priv(d); + + if (dev->chip_id == CHIP_ID_RTL2831U) + return rtl2831u_read_config(d); + else + return rtl2832u_read_config(d); +} + +static int rtl28xxu_identify_state(struct dvb_usb_device *d, const char **name) +{ + struct rtl28xxu_dev *dev = d_to_priv(d); + int ret; + struct rtl28xxu_req req_demod_i2c = {0x0020, CMD_I2C_DA_RD, 0, NULL}; + + dev_dbg(&d->intf->dev, "\n"); + + /* + * Detect chip type using I2C command that is not supported + * by old RTL2831U. + */ + ret = rtl28xxu_ctrl_msg(d, &req_demod_i2c); + if (ret == -EPIPE) { + dev->chip_id = CHIP_ID_RTL2831U; + } else if (ret == 0) { + dev->chip_id = CHIP_ID_RTL2832U; + } else { + dev_err(&d->intf->dev, "chip type detection failed %d\n", ret); + goto err; + } + dev_dbg(&d->intf->dev, "chip_id=%u\n", dev->chip_id); + + return WARM; +err: + dev_dbg(&d->intf->dev, "failed=%d\n", ret); + return ret; +} + +static const struct rtl2830_platform_data rtl2830_mt2060_platform_data = { + .clk = 28800000, .spec_inv = 1, .vtop = 0x20, .krf = 0x04, @@ -583,20 +571,16 @@ static const struct rtl2830_config rtl28xxu_rtl2830_mt2060_config = { }; -static const struct rtl2830_config rtl28xxu_rtl2830_qt1010_config = { - .i2c_addr = 0x10, /* 0x20 */ - .xtal = 28800000, - .ts_mode = 0, +static const struct rtl2830_platform_data rtl2830_qt1010_platform_data = { + .clk = 28800000, .spec_inv = 1, .vtop = 0x20, .krf = 0x04, .agc_targ_val = 0x2d, }; -static const struct rtl2830_config rtl28xxu_rtl2830_mxl5005s_config = { - .i2c_addr = 0x10, /* 0x20 */ - .xtal = 28800000, - .ts_mode = 0, +static const struct rtl2830_platform_data rtl2830_mxl5005s_platform_data = { + .clk = 28800000, .spec_inv = 0, .vtop = 0x3f, .krf = 0x04, @@ -606,69 +590,81 @@ static const struct rtl2830_config rtl28xxu_rtl2830_mxl5005s_config = { static int rtl2831u_frontend_attach(struct dvb_usb_adapter *adap) { struct dvb_usb_device *d = adap_to_d(adap); - struct rtl28xxu_priv *priv = d_to_priv(d); - const struct rtl2830_config *rtl2830_config; + struct rtl28xxu_dev *dev = d_to_priv(d); + struct rtl2830_platform_data *pdata = &dev->rtl2830_platform_data; + struct i2c_board_info board_info; + struct i2c_client *client; int ret; - dev_dbg(&d->udev->dev, "%s:\n", __func__); + dev_dbg(&d->intf->dev, "\n"); - switch (priv->tuner) { + switch (dev->tuner) { case TUNER_RTL2830_QT1010: - rtl2830_config = &rtl28xxu_rtl2830_qt1010_config; + *pdata = rtl2830_qt1010_platform_data; break; case TUNER_RTL2830_MT2060: - rtl2830_config = &rtl28xxu_rtl2830_mt2060_config; + *pdata = rtl2830_mt2060_platform_data; break; case TUNER_RTL2830_MXL5005S: - rtl2830_config = &rtl28xxu_rtl2830_mxl5005s_config; + *pdata = rtl2830_mxl5005s_platform_data; break; default: - dev_err(&d->udev->dev, "%s: unknown tuner=%s\n", - KBUILD_MODNAME, priv->tuner_name); + dev_err(&d->intf->dev, "unknown tuner %s\n", dev->tuner_name); ret = -ENODEV; goto err; } /* attach demodulator */ - adap->fe[0] = dvb_attach(rtl2830_attach, rtl2830_config, &d->i2c_adap); - if (!adap->fe[0]) { + memset(&board_info, 0, sizeof(board_info)); + strlcpy(board_info.type, "rtl2830", I2C_NAME_SIZE); + board_info.addr = 0x10; + board_info.platform_data = pdata; + request_module("%s", board_info.type); + client = i2c_new_device(&d->i2c_adap, &board_info); + if (client == NULL || client->dev.driver == NULL) { + ret = -ENODEV; + goto err; + } + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); ret = -ENODEV; goto err; } + adap->fe[0] = pdata->get_dvb_frontend(client); + dev->demod_i2c_adapter = pdata->get_i2c_adapter(client); + + dev->i2c_client_demod = client; + return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&d->intf->dev, "failed=%d\n", ret); return ret; } -static const struct rtl2832_config rtl28xxu_rtl2832_fc0012_config = { - .i2c_addr = 0x10, /* 0x20 */ - .xtal = 28800000, +static const struct rtl2832_platform_data rtl2832_fc0012_platform_data = { + .clk = 28800000, .tuner = TUNER_RTL2832_FC0012 }; -static const struct rtl2832_config rtl28xxu_rtl2832_fc0013_config = { - .i2c_addr = 0x10, /* 0x20 */ - .xtal = 28800000, +static const struct rtl2832_platform_data rtl2832_fc0013_platform_data = { + .clk = 28800000, .tuner = TUNER_RTL2832_FC0013 }; -static const struct rtl2832_config rtl28xxu_rtl2832_tua9001_config = { - .i2c_addr = 0x10, /* 0x20 */ - .xtal = 28800000, +static const struct rtl2832_platform_data rtl2832_tua9001_platform_data = { + .clk = 28800000, .tuner = TUNER_RTL2832_TUA9001, }; -static const struct rtl2832_config rtl28xxu_rtl2832_e4000_config = { - .i2c_addr = 0x10, /* 0x20 */ - .xtal = 28800000, +static const struct rtl2832_platform_data rtl2832_e4000_platform_data = { + .clk = 28800000, .tuner = TUNER_RTL2832_E4000, }; -static const struct rtl2832_config rtl28xxu_rtl2832_r820t_config = { - .i2c_addr = 0x10, - .xtal = 28800000, +static const struct rtl2832_platform_data rtl2832_r820t_platform_data = { + .clk = 28800000, .tuner = TUNER_RTL2832_R820T, }; @@ -678,12 +674,12 @@ static int rtl2832u_fc0012_tuner_callback(struct dvb_usb_device *d, int ret; u8 val; - dev_dbg(&d->udev->dev, "%s: cmd=%d arg=%d\n", __func__, cmd, arg); + dev_dbg(&d->intf->dev, "cmd=%d arg=%d\n", cmd, arg); switch (cmd) { case FC_FE_CALLBACK_VHF_ENABLE: /* set output values */ - ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &val); + ret = rtl28xxu_rd_reg(d, SYS_GPIO_OUT_VAL, &val); if (ret) goto err; @@ -693,7 +689,7 @@ static int rtl2832u_fc0012_tuner_callback(struct dvb_usb_device *d, val |= 0x40; /* set GPIO6 high */ - ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, val); + ret = rtl28xxu_wr_reg(d, SYS_GPIO_OUT_VAL, val); if (ret) goto err; break; @@ -703,7 +699,7 @@ static int rtl2832u_fc0012_tuner_callback(struct dvb_usb_device *d, } return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&d->intf->dev, "failed=%d\n", ret); return ret; } @@ -713,7 +709,7 @@ static int rtl2832u_tua9001_tuner_callback(struct dvb_usb_device *d, int ret; u8 val; - dev_dbg(&d->udev->dev, "%s: cmd=%d arg=%d\n", __func__, cmd, arg); + dev_dbg(&d->intf->dev, "cmd=%d arg=%d\n", cmd, arg); /* * CEN always enabled by hardware wiring @@ -728,7 +724,7 @@ static int rtl2832u_tua9001_tuner_callback(struct dvb_usb_device *d, else val = (0 << 4); - ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, val, 0x10); + ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_VAL, val, 0x10); if (ret) goto err; break; @@ -738,7 +734,7 @@ static int rtl2832u_tua9001_tuner_callback(struct dvb_usb_device *d, else val = (0 << 1); - ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, val, 0x02); + ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_VAL, val, 0x02); if (ret) goto err; break; @@ -746,40 +742,46 @@ static int rtl2832u_tua9001_tuner_callback(struct dvb_usb_device *d, return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&d->intf->dev, "failed=%d\n", ret); return ret; } -static int rtl2832u_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) -{ - struct rtl28xxu_priv *priv = d->priv; - - switch (priv->tuner) { - case TUNER_RTL2832_FC0012: - return rtl2832u_fc0012_tuner_callback(d, cmd, arg); - case TUNER_RTL2832_TUA9001: - return rtl2832u_tua9001_tuner_callback(d, cmd, arg); - default: - break; - } - - return 0; -} - static int rtl2832u_frontend_callback(void *adapter_priv, int component, int cmd, int arg) { - struct i2c_adapter *adap = adapter_priv; - struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct i2c_adapter *adapter = adapter_priv; + struct device *parent = adapter->dev.parent; + struct i2c_adapter *parent_adapter; + struct dvb_usb_device *d; + struct rtl28xxu_dev *dev; + + /* + * All tuners are connected to demod muxed I2C adapter. We have to + * resolve its parent adapter in order to get handle for this driver + * private data. That is a bit hackish solution, GPIO or direct driver + * callback would be better... + */ + if (parent != NULL && parent->type == &i2c_adapter_type) + parent_adapter = to_i2c_adapter(parent); + else + return -EINVAL; + + d = i2c_get_adapdata(parent_adapter); + dev = d->priv; - dev_dbg(&d->udev->dev, "%s: component=%d cmd=%d arg=%d\n", - __func__, component, cmd, arg); + dev_dbg(&d->intf->dev, "component=%d cmd=%d arg=%d\n", + component, cmd, arg); switch (component) { case DVB_FRONTEND_COMPONENT_TUNER: - return rtl2832u_tuner_callback(d, cmd, arg); + switch (dev->tuner) { + case TUNER_RTL2832_FC0012: + return rtl2832u_fc0012_tuner_callback(d, cmd, arg); + case TUNER_RTL2832_TUA9001: + return rtl2832u_tua9001_tuner_callback(d, cmd, arg); + } default: - break; + return -EINVAL; } return 0; @@ -787,57 +789,70 @@ static int rtl2832u_frontend_callback(void *adapter_priv, int component, static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) { - int ret; struct dvb_usb_device *d = adap_to_d(adap); - struct rtl28xxu_priv *priv = d_to_priv(d); - const struct rtl2832_config *rtl2832_config; + struct rtl28xxu_dev *dev = d_to_priv(d); + struct rtl2832_platform_data *pdata = &dev->rtl2832_platform_data; + struct i2c_board_info board_info; + struct i2c_client *client; + int ret; - dev_dbg(&d->udev->dev, "%s:\n", __func__); + dev_dbg(&d->intf->dev, "\n"); - switch (priv->tuner) { + switch (dev->tuner) { case TUNER_RTL2832_FC0012: - rtl2832_config = &rtl28xxu_rtl2832_fc0012_config; + *pdata = rtl2832_fc0012_platform_data; break; case TUNER_RTL2832_FC0013: - rtl2832_config = &rtl28xxu_rtl2832_fc0013_config; + *pdata = rtl2832_fc0013_platform_data; break; case TUNER_RTL2832_FC2580: /* FIXME: do not abuse fc0012 settings */ - rtl2832_config = &rtl28xxu_rtl2832_fc0012_config; + *pdata = rtl2832_fc0012_platform_data; break; case TUNER_RTL2832_TUA9001: - rtl2832_config = &rtl28xxu_rtl2832_tua9001_config; + *pdata = rtl2832_tua9001_platform_data; break; case TUNER_RTL2832_E4000: - rtl2832_config = &rtl28xxu_rtl2832_e4000_config; + *pdata = rtl2832_e4000_platform_data; break; case TUNER_RTL2832_R820T: case TUNER_RTL2832_R828D: - rtl2832_config = &rtl28xxu_rtl2832_r820t_config; + *pdata = rtl2832_r820t_platform_data; break; default: - dev_err(&d->udev->dev, "%s: unknown tuner=%s\n", - KBUILD_MODNAME, priv->tuner_name); + dev_err(&d->intf->dev, "unknown tuner %s\n", dev->tuner_name); ret = -ENODEV; goto err; } /* attach demodulator */ - adap->fe[0] = dvb_attach(rtl2832_attach, rtl2832_config, &d->i2c_adap); - if (!adap->fe[0]) { + memset(&board_info, 0, sizeof(board_info)); + strlcpy(board_info.type, "rtl2832", I2C_NAME_SIZE); + board_info.addr = 0x10; + board_info.platform_data = pdata; + request_module("%s", board_info.type); + client = i2c_new_device(&d->i2c_adap, &board_info); + if (client == NULL || client->dev.driver == NULL) { ret = -ENODEV; goto err; } - /* RTL2832 I2C repeater */ - priv->demod_i2c_adapter = rtl2832_get_i2c_adapter(adap->fe[0]); + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + ret = -ENODEV; + goto err; + } + + adap->fe[0] = pdata->get_dvb_frontend(client); + dev->demod_i2c_adapter = pdata->get_i2c_adapter(client); + + dev->i2c_client_demod = client; /* set fe callback */ adap->fe[0]->callback = rtl2832u_frontend_callback; - if (priv->slave_demod) { + if (dev->slave_demod) { struct i2c_board_info info = {}; - struct i2c_client *client; /* * We continue on reduced mode, without DVB-T2/C, using master @@ -846,28 +861,29 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) ret = 0; /* attach slave demodulator */ - if (priv->slave_demod == SLAVE_DEMOD_MN88472) { + if (dev->slave_demod == SLAVE_DEMOD_MN88472) { struct mn88472_config mn88472_config = {}; mn88472_config.fe = &adap->fe[1]; mn88472_config.i2c_wr_max = 22, strlcpy(info.type, "mn88472", I2C_NAME_SIZE); + mn88472_config.xtal = 20500000; info.addr = 0x18; info.platform_data = &mn88472_config; request_module(info.type); - client = i2c_new_device(priv->demod_i2c_adapter, &info); + client = i2c_new_device(&d->i2c_adap, &info); if (client == NULL || client->dev.driver == NULL) { - priv->slave_demod = SLAVE_DEMOD_NONE; + dev->slave_demod = SLAVE_DEMOD_NONE; goto err_slave_demod_failed; } if (!try_module_get(client->dev.driver->owner)) { i2c_unregister_device(client); - priv->slave_demod = SLAVE_DEMOD_NONE; + dev->slave_demod = SLAVE_DEMOD_NONE; goto err_slave_demod_failed; } - priv->i2c_client_slave_demod = client; + dev->i2c_client_slave_demod = client; } else { struct mn88473_config mn88473_config = {}; @@ -877,29 +893,64 @@ static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap) info.addr = 0x18; info.platform_data = &mn88473_config; request_module(info.type); - client = i2c_new_device(priv->demod_i2c_adapter, &info); + client = i2c_new_device(&d->i2c_adap, &info); if (client == NULL || client->dev.driver == NULL) { - priv->slave_demod = SLAVE_DEMOD_NONE; + dev->slave_demod = SLAVE_DEMOD_NONE; goto err_slave_demod_failed; } if (!try_module_get(client->dev.driver->owner)) { i2c_unregister_device(client); - priv->slave_demod = SLAVE_DEMOD_NONE; + dev->slave_demod = SLAVE_DEMOD_NONE; goto err_slave_demod_failed; } - priv->i2c_client_slave_demod = client; + dev->i2c_client_slave_demod = client; } } return 0; err_slave_demod_failed: err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&d->intf->dev, "failed=%d\n", ret); return ret; } +static int rtl28xxu_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct rtl28xxu_dev *dev = adap_to_priv(adap); + + if (dev->chip_id == CHIP_ID_RTL2831U) + return rtl2831u_frontend_attach(adap); + else + return rtl2832u_frontend_attach(adap); +} + +static int rtl28xxu_frontend_detach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap_to_d(adap); + struct rtl28xxu_dev *dev = d_to_priv(d); + struct i2c_client *client; + + dev_dbg(&d->intf->dev, "\n"); + + /* remove I2C slave demod */ + client = dev->i2c_client_slave_demod; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + + /* remove I2C demod */ + client = dev->i2c_client_demod; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + + return 0; +} + static struct qt1010_config rtl28xxu_qt1010_config = { .i2c_address = 0x62, /* 0xc4 */ }; @@ -930,33 +981,30 @@ static int rtl2831u_tuner_attach(struct dvb_usb_adapter *adap) { int ret; struct dvb_usb_device *d = adap_to_d(adap); - struct rtl28xxu_priv *priv = d_to_priv(d); - struct i2c_adapter *rtl2830_tuner_i2c; + struct rtl28xxu_dev *dev = d_to_priv(d); struct dvb_frontend *fe; - dev_dbg(&d->udev->dev, "%s:\n", __func__); - - /* use rtl2830 driver I2C adapter, for more info see rtl2830 driver */ - rtl2830_tuner_i2c = rtl2830_get_tuner_i2c_adapter(adap->fe[0]); + dev_dbg(&d->intf->dev, "\n"); - switch (priv->tuner) { + switch (dev->tuner) { case TUNER_RTL2830_QT1010: fe = dvb_attach(qt1010_attach, adap->fe[0], - rtl2830_tuner_i2c, &rtl28xxu_qt1010_config); + dev->demod_i2c_adapter, + &rtl28xxu_qt1010_config); break; case TUNER_RTL2830_MT2060: fe = dvb_attach(mt2060_attach, adap->fe[0], - rtl2830_tuner_i2c, &rtl28xxu_mt2060_config, - 1220); + dev->demod_i2c_adapter, + &rtl28xxu_mt2060_config, 1220); break; case TUNER_RTL2830_MXL5005S: fe = dvb_attach(mxl5005s_attach, adap->fe[0], - rtl2830_tuner_i2c, &rtl28xxu_mxl5005s_config); + dev->demod_i2c_adapter, + &rtl28xxu_mxl5005s_config); break; default: fe = NULL; - dev_err(&d->udev->dev, "%s: unknown tuner=%d\n", KBUILD_MODNAME, - priv->tuner); + dev_err(&d->intf->dev, "unknown tuner %d\n", dev->tuner); } if (fe == NULL) { @@ -966,7 +1014,7 @@ static int rtl2831u_tuner_attach(struct dvb_usb_adapter *adap) return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&d->intf->dev, "failed=%d\n", ret); return ret; } @@ -1002,45 +1050,38 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) { int ret; struct dvb_usb_device *d = adap_to_d(adap); - struct rtl28xxu_priv *priv = d_to_priv(d); + struct rtl28xxu_dev *dev = d_to_priv(d); struct dvb_frontend *fe = NULL; struct i2c_board_info info; struct i2c_client *client; + struct v4l2_subdev *subdev = NULL; + struct platform_device *pdev; + struct rtl2832_sdr_platform_data pdata; - dev_dbg(&d->udev->dev, "%s:\n", __func__); + dev_dbg(&d->intf->dev, "\n"); memset(&info, 0, sizeof(struct i2c_board_info)); + memset(&pdata, 0, sizeof(pdata)); - switch (priv->tuner) { + switch (dev->tuner) { case TUNER_RTL2832_FC0012: fe = dvb_attach(fc0012_attach, adap->fe[0], - &d->i2c_adap, &rtl2832u_fc0012_config); + dev->demod_i2c_adapter, &rtl2832u_fc0012_config); /* since fc0012 includs reading the signal strength delegate * that to the tuner driver */ adap->fe[0]->ops.read_signal_strength = adap->fe[0]->ops.tuner_ops.get_rf_strength; - - /* attach SDR */ - dvb_attach_sdr(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap, - &rtl28xxu_rtl2832_fc0012_config, NULL); break; case TUNER_RTL2832_FC0013: fe = dvb_attach(fc0013_attach, adap->fe[0], - &d->i2c_adap, 0xc6>>1, 0, FC_XTAL_28_8_MHZ); + dev->demod_i2c_adapter, 0xc6>>1, 0, FC_XTAL_28_8_MHZ); /* fc0013 also supports signal strength reading */ adap->fe[0]->ops.read_signal_strength = adap->fe[0]->ops.tuner_ops.get_rf_strength; - - /* attach SDR */ - dvb_attach_sdr(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap, - &rtl28xxu_rtl2832_fc0013_config, NULL); break; case TUNER_RTL2832_E4000: { - struct v4l2_subdev *sd; - struct i2c_adapter *i2c_adap_internal = - rtl2832_get_private_i2c_adapter(adap->fe[0]); struct e4000_config e4000_config = { .fe = adap->fe[0], .clock = 28800000, @@ -1051,7 +1092,7 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) info.platform_data = &e4000_config; request_module(info.type); - client = i2c_new_device(priv->demod_i2c_adapter, &info); + client = i2c_new_device(dev->demod_i2c_adapter, &info); if (client == NULL || client->dev.driver == NULL) break; @@ -1060,157 +1101,183 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap) break; } - priv->i2c_client_tuner = client; - sd = i2c_get_clientdata(client); - i2c_set_adapdata(i2c_adap_internal, d); - - /* attach SDR */ - dvb_attach_sdr(rtl2832_sdr_attach, adap->fe[0], - i2c_adap_internal, - &rtl28xxu_rtl2832_e4000_config, sd); + dev->i2c_client_tuner = client; + subdev = i2c_get_clientdata(client); } break; case TUNER_RTL2832_FC2580: - fe = dvb_attach(fc2580_attach, adap->fe[0], &d->i2c_adap, + fe = dvb_attach(fc2580_attach, adap->fe[0], + dev->demod_i2c_adapter, &rtl2832u_fc2580_config); break; case TUNER_RTL2832_TUA9001: /* enable GPIO1 and GPIO4 as output */ - ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x12); + ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_DIR, 0x00, 0x12); if (ret) goto err; - ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x12, 0x12); + ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_EN, 0x12, 0x12); if (ret) goto err; - fe = dvb_attach(tua9001_attach, adap->fe[0], &d->i2c_adap, + fe = dvb_attach(tua9001_attach, adap->fe[0], + dev->demod_i2c_adapter, &rtl2832u_tua9001_config); break; case TUNER_RTL2832_R820T: - fe = dvb_attach(r820t_attach, adap->fe[0], &d->i2c_adap, + fe = dvb_attach(r820t_attach, adap->fe[0], + dev->demod_i2c_adapter, &rtl2832u_r820t_config); /* Use tuner to get the signal strength */ adap->fe[0]->ops.read_signal_strength = adap->fe[0]->ops.tuner_ops.get_rf_strength; - - /* attach SDR */ - dvb_attach_sdr(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap, - &rtl28xxu_rtl2832_r820t_config, NULL); break; case TUNER_RTL2832_R828D: fe = dvb_attach(r820t_attach, adap->fe[0], - priv->demod_i2c_adapter, + dev->demod_i2c_adapter, &rtl2832u_r828d_config); adap->fe[0]->ops.read_signal_strength = adap->fe[0]->ops.tuner_ops.get_rf_strength; if (adap->fe[1]) { fe = dvb_attach(r820t_attach, adap->fe[1], - priv->demod_i2c_adapter, + dev->demod_i2c_adapter, &rtl2832u_r828d_config); adap->fe[1]->ops.read_signal_strength = adap->fe[1]->ops.tuner_ops.get_rf_strength; } - - /* attach SDR */ - dvb_attach_sdr(rtl2832_sdr_attach, adap->fe[0], &d->i2c_adap, - &rtl28xxu_rtl2832_r820t_config, NULL); break; default: - dev_err(&d->udev->dev, "%s: unknown tuner=%d\n", KBUILD_MODNAME, - priv->tuner); + dev_err(&d->intf->dev, "unknown tuner %d\n", dev->tuner); } - - if (fe == NULL && priv->i2c_client_tuner == NULL) { + if (fe == NULL && dev->i2c_client_tuner == NULL) { ret = -ENODEV; goto err; } + /* register SDR */ + switch (dev->tuner) { + case TUNER_RTL2832_FC0012: + case TUNER_RTL2832_FC0013: + case TUNER_RTL2832_E4000: + case TUNER_RTL2832_R820T: + case TUNER_RTL2832_R828D: + pdata.clk = dev->rtl2832_platform_data.clk; + pdata.tuner = dev->tuner; + pdata.i2c_client = dev->i2c_client_demod; + pdata.bulk_read = dev->rtl2832_platform_data.bulk_read; + pdata.bulk_write = dev->rtl2832_platform_data.bulk_write; + pdata.update_bits = dev->rtl2832_platform_data.update_bits; + pdata.dvb_frontend = adap->fe[0]; + pdata.dvb_usb_device = d; + pdata.v4l2_subdev = subdev; + + request_module("%s", "rtl2832_sdr"); + pdev = platform_device_register_data(&d->intf->dev, + "rtl2832_sdr", + PLATFORM_DEVID_AUTO, + &pdata, sizeof(pdata)); + if (pdev == NULL || pdev->dev.driver == NULL) + break; + dev->platform_device_sdr = pdev; + break; + default: + dev_dbg(&d->intf->dev, "no SDR for tuner=%d\n", dev->tuner); + } + return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&d->intf->dev, "failed=%d\n", ret); return ret; } +static int rtl28xxu_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct rtl28xxu_dev *dev = adap_to_priv(adap); + + if (dev->chip_id == CHIP_ID_RTL2831U) + return rtl2831u_tuner_attach(adap); + else + return rtl2832u_tuner_attach(adap); +} + +static int rtl28xxu_tuner_detach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap_to_d(adap); + struct rtl28xxu_dev *dev = d_to_priv(d); + struct i2c_client *client; + struct platform_device *pdev; + + dev_dbg(&d->intf->dev, "\n"); + + /* remove platform SDR */ + pdev = dev->platform_device_sdr; + if (pdev) + platform_device_unregister(pdev); + + /* remove I2C tuner */ + client = dev->i2c_client_tuner; + if (client) { + module_put(client->dev.driver->owner); + i2c_unregister_device(client); + } + + return 0; +} + static int rtl28xxu_init(struct dvb_usb_device *d) { int ret; u8 val; - dev_dbg(&d->udev->dev, "%s:\n", __func__); + dev_dbg(&d->intf->dev, "\n"); /* init USB endpoints */ - ret = rtl28xx_rd_reg(d, USB_SYSCTL_0, &val); + ret = rtl28xxu_rd_reg(d, USB_SYSCTL_0, &val); if (ret) goto err; /* enable DMA and Full Packet Mode*/ val |= 0x09; - ret = rtl28xx_wr_reg(d, USB_SYSCTL_0, val); + ret = rtl28xxu_wr_reg(d, USB_SYSCTL_0, val); if (ret) goto err; /* set EPA maximum packet size to 0x0200 */ - ret = rtl28xx_wr_regs(d, USB_EPA_MAXPKT, "\x00\x02\x00\x00", 4); + ret = rtl28xxu_wr_regs(d, USB_EPA_MAXPKT, "\x00\x02\x00\x00", 4); if (ret) goto err; /* change EPA FIFO length */ - ret = rtl28xx_wr_regs(d, USB_EPA_FIFO_CFG, "\x14\x00\x00\x00", 4); + ret = rtl28xxu_wr_regs(d, USB_EPA_FIFO_CFG, "\x14\x00\x00\x00", 4); if (ret) goto err; return ret; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&d->intf->dev, "failed=%d\n", ret); return ret; } -static void rtl28xxu_exit(struct dvb_usb_device *d) -{ - struct rtl28xxu_priv *priv = d->priv; - struct i2c_client *client; - - dev_dbg(&d->udev->dev, "%s:\n", __func__); - - /* remove I2C tuner */ - client = priv->i2c_client_tuner; - if (client) { - module_put(client->dev.driver->owner); - i2c_unregister_device(client); - } - - /* remove I2C slave demod */ - client = priv->i2c_client_slave_demod; - if (client) { - module_put(client->dev.driver->owner); - i2c_unregister_device(client); - } - - return; -} - static int rtl2831u_power_ctrl(struct dvb_usb_device *d, int onoff) { int ret; u8 gpio, sys0, epa_ctl[2]; - dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff); + dev_dbg(&d->intf->dev, "onoff=%d\n", onoff); /* demod adc */ - ret = rtl28xx_rd_reg(d, SYS_SYS0, &sys0); + ret = rtl28xxu_rd_reg(d, SYS_SYS0, &sys0); if (ret) goto err; /* tuner power, read GPIOs */ - ret = rtl28xx_rd_reg(d, SYS_GPIO_OUT_VAL, &gpio); + ret = rtl28xxu_rd_reg(d, SYS_GPIO_OUT_VAL, &gpio); if (ret) goto err; - dev_dbg(&d->udev->dev, "%s: RD SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, - sys0, gpio); + dev_dbg(&d->intf->dev, "RD SYS0=%02x GPIO_OUT_VAL=%02x\n", sys0, gpio); if (onoff) { gpio |= 0x01; /* GPIO0 = 1 */ @@ -1229,21 +1296,20 @@ static int rtl2831u_power_ctrl(struct dvb_usb_device *d, int onoff) epa_ctl[1] = 0x02; /* set reset */ } - dev_dbg(&d->udev->dev, "%s: WR SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, - sys0, gpio); + dev_dbg(&d->intf->dev, "WR SYS0=%02x GPIO_OUT_VAL=%02x\n", sys0, gpio); /* demod adc */ - ret = rtl28xx_wr_reg(d, SYS_SYS0, sys0); + ret = rtl28xxu_wr_reg(d, SYS_SYS0, sys0); if (ret) goto err; /* tuner power, write GPIOs */ - ret = rtl28xx_wr_reg(d, SYS_GPIO_OUT_VAL, gpio); + ret = rtl28xxu_wr_reg(d, SYS_GPIO_OUT_VAL, gpio); if (ret) goto err; /* streaming EP: stall & reset */ - ret = rtl28xx_wr_regs(d, USB_EPA_CTL, epa_ctl, 2); + ret = rtl28xxu_wr_regs(d, USB_EPA_CTL, epa_ctl, 2); if (ret) goto err; @@ -1252,7 +1318,7 @@ static int rtl2831u_power_ctrl(struct dvb_usb_device *d, int onoff) return ret; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&d->intf->dev, "failed=%d\n", ret); return ret; } @@ -1260,31 +1326,31 @@ static int rtl2832u_power_ctrl(struct dvb_usb_device *d, int onoff) { int ret; - dev_dbg(&d->udev->dev, "%s: onoff=%d\n", __func__, onoff); + dev_dbg(&d->intf->dev, "onoff=%d\n", onoff); if (onoff) { /* GPIO3=1, GPIO4=0 */ - ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x08, 0x18); + ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x08, 0x18); if (ret) goto err; /* suspend? */ - ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL1, 0x00, 0x10); + ret = rtl28xxu_wr_reg_mask(d, SYS_DEMOD_CTL1, 0x00, 0x10); if (ret) goto err; /* enable PLL */ - ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, 0x80, 0x80); + ret = rtl28xxu_wr_reg_mask(d, SYS_DEMOD_CTL, 0x80, 0x80); if (ret) goto err; /* disable reset */ - ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, 0x20, 0x20); + ret = rtl28xxu_wr_reg_mask(d, SYS_DEMOD_CTL, 0x20, 0x20); if (ret) goto err; /* streaming EP: clear stall & reset */ - ret = rtl28xx_wr_regs(d, USB_EPA_CTL, "\x00\x00", 2); + ret = rtl28xxu_wr_regs(d, USB_EPA_CTL, "\x00\x00", 2); if (ret) goto err; @@ -1293,35 +1359,49 @@ static int rtl2832u_power_ctrl(struct dvb_usb_device *d, int onoff) goto err; } else { /* GPIO4=1 */ - ret = rtl28xx_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x10, 0x10); + ret = rtl28xxu_wr_reg_mask(d, SYS_GPIO_OUT_VAL, 0x10, 0x10); if (ret) goto err; /* disable PLL */ - ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, 0x00, 0x80); + ret = rtl28xxu_wr_reg_mask(d, SYS_DEMOD_CTL, 0x00, 0x80); if (ret) goto err; /* streaming EP: set stall & reset */ - ret = rtl28xx_wr_regs(d, USB_EPA_CTL, "\x10\x02", 2); + ret = rtl28xxu_wr_regs(d, USB_EPA_CTL, "\x10\x02", 2); if (ret) goto err; } return ret; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&d->intf->dev, "failed=%d\n", ret); return ret; } -static int rtl2832u_frontend_ctrl(struct dvb_frontend *fe, int onoff) +static int rtl28xxu_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + struct rtl28xxu_dev *dev = d_to_priv(d); + + if (dev->chip_id == CHIP_ID_RTL2831U) + return rtl2831u_power_ctrl(d, onoff); + else + return rtl2832u_power_ctrl(d, onoff); +} + +static int rtl28xxu_frontend_ctrl(struct dvb_frontend *fe, int onoff) { struct dvb_usb_device *d = fe_to_d(fe); - struct dvb_usb_adapter *adap = fe_to_adap(fe); + struct rtl28xxu_dev *dev = fe_to_priv(fe); + struct rtl2832_platform_data *pdata = &dev->rtl2832_platform_data; int ret; u8 val; - dev_dbg(&d->udev->dev, "%s: fe=%d onoff=%d\n", __func__, fe->id, onoff); + dev_dbg(&d->intf->dev, "fe=%d onoff=%d\n", fe->id, onoff); + + if (dev->chip_id == CHIP_ID_RTL2831U) + return 0; /* control internal demod ADC */ if (fe->id == 0 && onoff) @@ -1329,20 +1409,20 @@ static int rtl2832u_frontend_ctrl(struct dvb_frontend *fe, int onoff) else val = 0x00; /* disable ADC */ - ret = rtl28xx_wr_reg_mask(d, SYS_DEMOD_CTL, val, 0x48); + ret = rtl28xxu_wr_reg_mask(d, SYS_DEMOD_CTL, val, 0x48); if (ret) goto err; /* bypass slave demod TS through master demod */ if (fe->id == 1 && onoff) { - ret = rtl2832_enable_external_ts_if(adap->fe[0]); + ret = pdata->enable_slave_ts(dev->i2c_client_demod); if (ret) goto err; } return 0; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&d->intf->dev, "failed=%d\n", ret); return ret; } @@ -1350,7 +1430,7 @@ err: static int rtl2831u_rc_query(struct dvb_usb_device *d) { int ret, i; - struct rtl28xxu_priv *priv = d->priv; + struct rtl28xxu_dev *dev = d->priv; u8 buf[5]; u32 rc_code; struct rtl28xxu_reg_val rc_nec_tab[] = { @@ -1371,17 +1451,17 @@ static int rtl2831u_rc_query(struct dvb_usb_device *d) }; /* init remote controller */ - if (!priv->rc_active) { + if (!dev->rc_active) { for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) { - ret = rtl28xx_wr_reg(d, rc_nec_tab[i].reg, + ret = rtl28xxu_wr_reg(d, rc_nec_tab[i].reg, rc_nec_tab[i].val); if (ret) goto err; } - priv->rc_active = true; + dev->rc_active = true; } - ret = rtl2831_rd_regs(d, SYS_IRRC_RP, buf, 5); + ret = rtl28xxu_rd_regs(d, SYS_IRRC_RP, buf, 5); if (ret) goto err; @@ -1403,19 +1483,19 @@ static int rtl2831u_rc_query(struct dvb_usb_device *d) rc_keydown(d->rc_dev, RC_TYPE_NEC, rc_code, 0); - ret = rtl28xx_wr_reg(d, SYS_IRRC_SR, 1); + ret = rtl28xxu_wr_reg(d, SYS_IRRC_SR, 1); if (ret) goto err; /* repeated intentionally to avoid extra keypress */ - ret = rtl28xx_wr_reg(d, SYS_IRRC_SR, 1); + ret = rtl28xxu_wr_reg(d, SYS_IRRC_SR, 1); if (ret) goto err; } return ret; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&d->intf->dev, "failed=%d\n", ret); return ret; } @@ -1433,7 +1513,7 @@ static int rtl2831u_get_rc_config(struct dvb_usb_device *d, static int rtl2832u_rc_query(struct dvb_usb_device *d) { int ret, i, len; - struct rtl28xxu_priv *priv = d->priv; + struct rtl28xxu_dev *dev = d->priv; struct ir_raw_event ev; u8 buf[128]; static const struct rtl28xxu_reg_val_mask refresh_tab[] = { @@ -1443,7 +1523,7 @@ static int rtl2832u_rc_query(struct dvb_usb_device *d) }; /* init remote controller */ - if (!priv->rc_active) { + if (!dev->rc_active) { static const struct rtl28xxu_reg_val_mask init_tab[] = { {SYS_DEMOD_CTL1, 0x00, 0x04}, {SYS_DEMOD_CTL1, 0x00, 0x08}, @@ -1464,36 +1544,36 @@ static int rtl2832u_rc_query(struct dvb_usb_device *d) }; for (i = 0; i < ARRAY_SIZE(init_tab); i++) { - ret = rtl28xx_wr_reg_mask(d, init_tab[i].reg, + ret = rtl28xxu_wr_reg_mask(d, init_tab[i].reg, init_tab[i].val, init_tab[i].mask); if (ret) goto err; } - priv->rc_active = true; + dev->rc_active = true; } - ret = rtl28xx_rd_reg(d, IR_RX_IF, &buf[0]); + ret = rtl28xxu_rd_reg(d, IR_RX_IF, &buf[0]); if (ret) goto err; if (buf[0] != 0x83) goto exit; - ret = rtl28xx_rd_reg(d, IR_RX_BC, &buf[0]); + ret = rtl28xxu_rd_reg(d, IR_RX_BC, &buf[0]); if (ret) goto err; len = buf[0]; /* read raw code from hw */ - ret = rtl2831_rd_regs(d, IR_RX_BUF, buf, len); + ret = rtl28xxu_rd_regs(d, IR_RX_BUF, buf, len); if (ret) goto err; /* let hw receive new code */ for (i = 0; i < ARRAY_SIZE(refresh_tab); i++) { - ret = rtl28xx_wr_reg_mask(d, refresh_tab[i].reg, + ret = rtl28xxu_wr_reg_mask(d, refresh_tab[i].reg, refresh_tab[i].val, refresh_tab[i].mask); if (ret) goto err; @@ -1514,7 +1594,7 @@ static int rtl2832u_rc_query(struct dvb_usb_device *d) exit: return ret; err: - dev_dbg(&d->udev->dev, "%s: failed=%d\n", __func__, ret); + dev_dbg(&d->intf->dev, "failed=%d\n", ret); return ret; } @@ -1523,7 +1603,7 @@ static int rtl2832u_get_rc_config(struct dvb_usb_device *d, { /* disable IR interrupts in order to avoid SDR sample loss */ if (rtl28xxu_disable_rc) - return rtl28xx_wr_reg(d, IR_RX_IE, 0x00); + return rtl28xxu_wr_reg(d, IR_RX_IE, 0x00); /* load empty to enable rc */ if (!rc->map_name) @@ -1535,52 +1615,80 @@ static int rtl2832u_get_rc_config(struct dvb_usb_device *d, return 0; } + +static int rtl28xxu_get_rc_config(struct dvb_usb_device *d, + struct dvb_usb_rc *rc) +{ + struct rtl28xxu_dev *dev = d_to_priv(d); + + if (dev->chip_id == CHIP_ID_RTL2831U) + return rtl2831u_get_rc_config(d, rc); + else + return rtl2832u_get_rc_config(d, rc); +} #else -#define rtl2831u_get_rc_config NULL -#define rtl2832u_get_rc_config NULL +#define rtl28xxu_get_rc_config NULL #endif -static const struct dvb_usb_device_properties rtl2831u_props = { - .driver_name = KBUILD_MODNAME, - .owner = THIS_MODULE, - .adapter_nr = adapter_nr, - .size_of_priv = sizeof(struct rtl28xxu_priv), +static int rtl28xxu_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + struct rtl28xxu_dev *dev = adap_to_priv(adap); - .power_ctrl = rtl2831u_power_ctrl, - .i2c_algo = &rtl28xxu_i2c_algo, - .read_config = rtl2831u_read_config, - .frontend_attach = rtl2831u_frontend_attach, - .tuner_attach = rtl2831u_tuner_attach, - .init = rtl28xxu_init, - .get_rc_config = rtl2831u_get_rc_config, + if (dev->chip_id == CHIP_ID_RTL2831U) { + struct rtl2830_platform_data *pdata = &dev->rtl2830_platform_data; - .num_adapters = 1, - .adapter = { - { - .stream = DVB_USB_STREAM_BULK(0x81, 6, 8 * 512), - }, - }, -}; + return pdata->pid_filter_ctrl(adap->fe[0], onoff); + } else { + struct rtl2832_platform_data *pdata = &dev->rtl2832_platform_data; -static const struct dvb_usb_device_properties rtl2832u_props = { + return pdata->pid_filter_ctrl(adap->fe[0], onoff); + } +} + +static int rtl28xxu_pid_filter(struct dvb_usb_adapter *adap, int index, + u16 pid, int onoff) +{ + struct rtl28xxu_dev *dev = adap_to_priv(adap); + + if (dev->chip_id == CHIP_ID_RTL2831U) { + struct rtl2830_platform_data *pdata = &dev->rtl2830_platform_data; + + return pdata->pid_filter(adap->fe[0], index, pid, onoff); + } else { + struct rtl2832_platform_data *pdata = &dev->rtl2832_platform_data; + + return pdata->pid_filter(adap->fe[0], index, pid, onoff); + } +} + +static const struct dvb_usb_device_properties rtl28xxu_props = { .driver_name = KBUILD_MODNAME, .owner = THIS_MODULE, .adapter_nr = adapter_nr, - .size_of_priv = sizeof(struct rtl28xxu_priv), + .size_of_priv = sizeof(struct rtl28xxu_dev), - .power_ctrl = rtl2832u_power_ctrl, - .frontend_ctrl = rtl2832u_frontend_ctrl, + .identify_state = rtl28xxu_identify_state, + .power_ctrl = rtl28xxu_power_ctrl, + .frontend_ctrl = rtl28xxu_frontend_ctrl, .i2c_algo = &rtl28xxu_i2c_algo, - .read_config = rtl2832u_read_config, - .frontend_attach = rtl2832u_frontend_attach, - .tuner_attach = rtl2832u_tuner_attach, + .read_config = rtl28xxu_read_config, + .frontend_attach = rtl28xxu_frontend_attach, + .frontend_detach = rtl28xxu_frontend_detach, + .tuner_attach = rtl28xxu_tuner_attach, + .tuner_detach = rtl28xxu_tuner_detach, .init = rtl28xxu_init, - .exit = rtl28xxu_exit, - .get_rc_config = rtl2832u_get_rc_config, + .get_rc_config = rtl28xxu_get_rc_config, .num_adapters = 1, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + + .pid_filter_count = 32, + .pid_filter_ctrl = rtl28xxu_pid_filter_ctrl, + .pid_filter = rtl28xxu_pid_filter, + .stream = DVB_USB_STREAM_BULK(0x81, 6, 8 * 512), }, }, @@ -1589,69 +1697,69 @@ static const struct dvb_usb_device_properties rtl2832u_props = { static const struct usb_device_id rtl28xxu_id_table[] = { /* RTL2831U devices: */ { DVB_USB_DEVICE(USB_VID_REALTEK, USB_PID_REALTEK_RTL2831U, - &rtl2831u_props, "Realtek RTL2831U reference design", NULL) }, + &rtl28xxu_props, "Realtek RTL2831U reference design", NULL) }, { DVB_USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT, - &rtl2831u_props, "Freecom USB2.0 DVB-T", NULL) }, + &rtl28xxu_props, "Freecom USB2.0 DVB-T", NULL) }, { DVB_USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT_2, - &rtl2831u_props, "Freecom USB2.0 DVB-T", NULL) }, + &rtl28xxu_props, "Freecom USB2.0 DVB-T", NULL) }, /* RTL2832U devices: */ { DVB_USB_DEVICE(USB_VID_REALTEK, 0x2832, - &rtl2832u_props, "Realtek RTL2832U reference design", NULL) }, + &rtl28xxu_props, "Realtek RTL2832U reference design", NULL) }, { DVB_USB_DEVICE(USB_VID_REALTEK, 0x2838, - &rtl2832u_props, "Realtek RTL2832U reference design", NULL) }, + &rtl28xxu_props, "Realtek RTL2832U reference design", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_BLACK_REV1, - &rtl2832u_props, "TerraTec Cinergy T Stick Black", RC_MAP_TERRATEC_SLIM) }, + &rtl28xxu_props, "TerraTec Cinergy T Stick Black", RC_MAP_TERRATEC_SLIM) }, { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_DELOCK_USB2_DVBT, - &rtl2832u_props, "G-Tek Electronics Group Lifeview LV5TDLX DVB-T", NULL) }, + &rtl28xxu_props, "G-Tek Electronics Group Lifeview LV5TDLX DVB-T", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK, - &rtl2832u_props, "TerraTec NOXON DAB Stick", NULL) }, + &rtl28xxu_props, "TerraTec NOXON DAB Stick", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK_REV2, - &rtl2832u_props, "TerraTec NOXON DAB Stick (rev 2)", NULL) }, + &rtl28xxu_props, "TerraTec NOXON DAB Stick (rev 2)", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_NOXON_DAB_STICK_REV3, - &rtl2832u_props, "TerraTec NOXON DAB Stick (rev 3)", NULL) }, + &rtl28xxu_props, "TerraTec NOXON DAB Stick (rev 3)", NULL) }, { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_TREKSTOR_TERRES_2_0, - &rtl2832u_props, "Trekstor DVB-T Stick Terres 2.0", NULL) }, + &rtl28xxu_props, "Trekstor DVB-T Stick Terres 2.0", NULL) }, { DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1101, - &rtl2832u_props, "Dexatek DK DVB-T Dongle", NULL) }, + &rtl28xxu_props, "Dexatek DK DVB-T Dongle", NULL) }, { DVB_USB_DEVICE(USB_VID_LEADTEK, 0x6680, - &rtl2832u_props, "DigitalNow Quad DVB-T Receiver", NULL) }, + &rtl28xxu_props, "DigitalNow Quad DVB-T Receiver", NULL) }, { DVB_USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_MINID, - &rtl2832u_props, "Leadtek Winfast DTV Dongle Mini D", NULL) }, + &rtl28xxu_props, "Leadtek Winfast DTV Dongle Mini D", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, 0x00d3, - &rtl2832u_props, "TerraTec Cinergy T Stick RC (Rev. 3)", NULL) }, + &rtl28xxu_props, "TerraTec Cinergy T Stick RC (Rev. 3)", NULL) }, { DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1102, - &rtl2832u_props, "Dexatek DK mini DVB-T Dongle", NULL) }, + &rtl28xxu_props, "Dexatek DK mini DVB-T Dongle", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, 0x00d7, - &rtl2832u_props, "TerraTec Cinergy T Stick+", NULL) }, + &rtl28xxu_props, "TerraTec Cinergy T Stick+", NULL) }, { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd3a8, - &rtl2832u_props, "ASUS My Cinema-U3100Mini Plus V2", NULL) }, + &rtl28xxu_props, "ASUS My Cinema-U3100Mini Plus V2", NULL) }, { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd393, - &rtl2832u_props, "GIGABYTE U7300", NULL) }, + &rtl28xxu_props, "GIGABYTE U7300", NULL) }, { DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1104, - &rtl2832u_props, "MSI DIGIVOX Micro HD", NULL) }, + &rtl28xxu_props, "MSI DIGIVOX Micro HD", NULL) }, { DVB_USB_DEVICE(USB_VID_COMPRO, 0x0620, - &rtl2832u_props, "Compro VideoMate U620F", NULL) }, + &rtl28xxu_props, "Compro VideoMate U620F", NULL) }, { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd394, - &rtl2832u_props, "MaxMedia HU394-T", NULL) }, + &rtl28xxu_props, "MaxMedia HU394-T", NULL) }, { DVB_USB_DEVICE(USB_VID_LEADTEK, 0x6a03, - &rtl2832u_props, "Leadtek WinFast DTV Dongle mini", NULL) }, + &rtl28xxu_props, "Leadtek WinFast DTV Dongle mini", NULL) }, { DVB_USB_DEVICE(USB_VID_GTEK, USB_PID_CPYTO_REDI_PC50A, - &rtl2832u_props, "Crypto ReDi PC 50 A", NULL) }, + &rtl28xxu_props, "Crypto ReDi PC 50 A", NULL) }, { DVB_USB_DEVICE(USB_VID_KYE, 0x707f, - &rtl2832u_props, "Genius TVGo DVB-T03", NULL) }, + &rtl28xxu_props, "Genius TVGo DVB-T03", NULL) }, { DVB_USB_DEVICE(USB_VID_KWORLD_2, 0xd395, - &rtl2832u_props, "Peak DVB-T USB", NULL) }, + &rtl28xxu_props, "Peak DVB-T USB", NULL) }, { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV20_RTL2832U, - &rtl2832u_props, "Sveon STV20", NULL) }, + &rtl28xxu_props, "Sveon STV20", NULL) }, { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV21, - &rtl2832u_props, "Sveon STV21", NULL) }, + &rtl28xxu_props, "Sveon STV21", NULL) }, { DVB_USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV27, - &rtl2832u_props, "Sveon STV27", NULL) }, + &rtl28xxu_props, "Sveon STV27", NULL) }, /* RTL2832P devices: */ { DVB_USB_DEVICE(USB_VID_HANFTEK, 0x0131, - &rtl2832u_props, "Astrometa DVB-T2", NULL) }, + &rtl28xxu_props, "Astrometa DVB-T2", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h index 3e3ea9d64a38..1b5d7ffb685e 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.h +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.h @@ -22,8 +22,26 @@ #ifndef RTL28XXU_H #define RTL28XXU_H +#include <linux/platform_device.h> + #include "dvb_usb.h" +#include "rtl2830.h" +#include "rtl2832.h" +#include "rtl2832_sdr.h" +#include "mn88472.h" +#include "mn88473.h" + +#include "qt1010.h" +#include "mt2060.h" +#include "mxl5005s.h" +#include "fc0012.h" +#include "fc0013.h" +#include "e4000.h" +#include "fc2580.h" +#include "tua9001.h" +#include "r820t.h" + /* * USB commands * (usb_control_msg() index parameter) @@ -50,19 +68,26 @@ #define CMD_I2C_DA_WR 0x0610 -struct rtl28xxu_priv { +struct rtl28xxu_dev { + u8 buf[28]; u8 chip_id; u8 tuner; char *tuner_name; u8 page; /* integrated demod active register page */ struct i2c_adapter *demod_i2c_adapter; bool rc_active; + struct i2c_client *i2c_client_demod; struct i2c_client *i2c_client_tuner; struct i2c_client *i2c_client_slave_demod; + struct platform_device *platform_device_sdr; #define SLAVE_DEMOD_NONE 0 #define SLAVE_DEMOD_MN88472 1 #define SLAVE_DEMOD_MN88473 2 unsigned int slave_demod:2; + union { + struct rtl2830_platform_data rtl2830_platform_data; + struct rtl2832_platform_data rtl2832_platform_data; + }; }; enum rtl28xxu_chip_id { diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index 0f345b1f9014..f327c49d7e09 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -2232,7 +2232,7 @@ static struct dvb_usb_device_properties cxusb_mygica_t230_properties = { { "Mygica T230 DVB-T/T2/C", { NULL }, - { &cxusb_table[22], NULL }, + { &cxusb_table[20], NULL }, }, } }; diff --git a/drivers/media/usb/dvb-usb/m920x.c b/drivers/media/usb/dvb-usb/m920x.c index abf8ab2e02e5..eafc5c82467f 100644 --- a/drivers/media/usb/dvb-usb/m920x.c +++ b/drivers/media/usb/dvb-usb/m920x.c @@ -1269,8 +1269,3 @@ MODULE_AUTHOR("Aapo Tahkola <aet@rasterburn.org>"); MODULE_DESCRIPTION("DVB Driver for ULI M920x"); MODULE_VERSION("0.1"); MODULE_LICENSE("GPL"); - -/* - * Local variables: - * c-basic-offset: 8 - */ diff --git a/drivers/media/usb/em28xx/em28xx-audio.c b/drivers/media/usb/em28xx/em28xx-audio.c index 44ae1e0661e6..49a5f9532bd8 100644 --- a/drivers/media/usb/em28xx/em28xx-audio.c +++ b/drivers/media/usb/em28xx/em28xx-audio.c @@ -820,7 +820,7 @@ static int em28xx_audio_urb_init(struct em28xx *dev) if (urb_size > ep_size * npackets) npackets = DIV_ROUND_UP(urb_size, ep_size); - em28xx_info("Number of URBs: %d, with %d packets and %d size", + em28xx_info("Number of URBs: %d, with %d packets and %d size\n", num_urb, npackets, urb_size); /* Estimate the bytes per period */ @@ -981,7 +981,7 @@ static int em28xx_audio_fini(struct em28xx *dev) return 0; } - em28xx_info("Closing audio extension"); + em28xx_info("Closing audio extension\n"); if (dev->adev.sndcard) { snd_card_disconnect(dev->adev.sndcard); @@ -1005,7 +1005,7 @@ static int em28xx_audio_suspend(struct em28xx *dev) if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR) return 0; - em28xx_info("Suspending audio extension"); + em28xx_info("Suspending audio extension\n"); em28xx_deinit_isoc_audio(dev); atomic_set(&dev->adev.stream_started, 0); return 0; @@ -1019,7 +1019,7 @@ static int em28xx_audio_resume(struct em28xx *dev) if (dev->usb_audio_type != EM28XX_USB_AUDIO_VENDOR) return 0; - em28xx_info("Resuming audio extension"); + em28xx_info("Resuming audio extension\n"); /* Nothing to do other than schedule_work() ?? */ schedule_work(&dev->adev.wq_trigger); return 0; diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 86461a708abe..37456079f490 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -1125,7 +1125,7 @@ int em28xx_suspend_extension(struct em28xx *dev) { const struct em28xx_ops *ops = NULL; - em28xx_info("Suspending extensions"); + em28xx_info("Suspending extensions\n"); mutex_lock(&em28xx_devlist_mutex); list_for_each_entry(ops, &em28xx_extension_devlist, next) { if (ops->suspend) @@ -1139,7 +1139,7 @@ int em28xx_resume_extension(struct em28xx *dev) { const struct em28xx_ops *ops = NULL; - em28xx_info("Resuming extensions"); + em28xx_info("Resuming extensions\n"); mutex_lock(&em28xx_devlist_mutex); list_for_each_entry(ops, &em28xx_extension_devlist, next) { if (ops->resume) diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 9877b699c6bc..aee70d483264 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -1724,7 +1724,7 @@ static int em28xx_dvb_fini(struct em28xx *dev) if (!dev->dvb) return 0; - em28xx_info("Closing DVB extension"); + em28xx_info("Closing DVB extension\n"); dvb = dev->dvb; client = dvb->i2c_client_tuner; @@ -1775,17 +1775,17 @@ static int em28xx_dvb_suspend(struct em28xx *dev) if (!dev->board.has_dvb) return 0; - em28xx_info("Suspending DVB extension"); + em28xx_info("Suspending DVB extension\n"); if (dev->dvb) { struct em28xx_dvb *dvb = dev->dvb; if (dvb->fe[0]) { ret = dvb_frontend_suspend(dvb->fe[0]); - em28xx_info("fe0 suspend %d", ret); + em28xx_info("fe0 suspend %d\n", ret); } if (dvb->fe[1]) { dvb_frontend_suspend(dvb->fe[1]); - em28xx_info("fe1 suspend %d", ret); + em28xx_info("fe1 suspend %d\n", ret); } } @@ -1802,18 +1802,18 @@ static int em28xx_dvb_resume(struct em28xx *dev) if (!dev->board.has_dvb) return 0; - em28xx_info("Resuming DVB extension"); + em28xx_info("Resuming DVB extension\n"); if (dev->dvb) { struct em28xx_dvb *dvb = dev->dvb; if (dvb->fe[0]) { ret = dvb_frontend_resume(dvb->fe[0]); - em28xx_info("fe0 resume %d", ret); + em28xx_info("fe0 resume %d\n", ret); } if (dvb->fe[1]) { ret = dvb_frontend_resume(dvb->fe[1]); - em28xx_info("fe1 resume %d", ret); + em28xx_info("fe1 resume %d\n", ret); } } diff --git a/drivers/media/usb/em28xx/em28xx-input.c b/drivers/media/usb/em28xx/em28xx-input.c index d8dc03aadfbd..4007356d991d 100644 --- a/drivers/media/usb/em28xx/em28xx-input.c +++ b/drivers/media/usb/em28xx/em28xx-input.c @@ -654,8 +654,6 @@ next_button: if (dev->num_button_polling_addresses) { memset(dev->button_polling_last_values, 0, EM28XX_NUM_BUTTON_ADDRESSES_MAX); - INIT_DELAYED_WORK(&dev->buttons_query_work, - em28xx_query_buttons); schedule_delayed_work(&dev->buttons_query_work, msecs_to_jiffies(dev->button_polling_interval)); } @@ -689,6 +687,7 @@ static int em28xx_ir_init(struct em28xx *dev) } kref_get(&dev->ref); + INIT_DELAYED_WORK(&dev->buttons_query_work, em28xx_query_buttons); if (dev->board.buttons) em28xx_init_buttons(dev); @@ -833,7 +832,7 @@ static int em28xx_ir_fini(struct em28xx *dev) return 0; } - em28xx_info("Closing input extension"); + em28xx_info("Closing input extension\n"); em28xx_shutdown_buttons(dev); @@ -862,7 +861,7 @@ static int em28xx_ir_suspend(struct em28xx *dev) if (dev->is_audio_only) return 0; - em28xx_info("Suspending input extension"); + em28xx_info("Suspending input extension\n"); if (ir) cancel_delayed_work_sync(&ir->work); cancel_delayed_work_sync(&dev->buttons_query_work); @@ -879,7 +878,7 @@ static int em28xx_ir_resume(struct em28xx *dev) if (dev->is_audio_only) return 0; - em28xx_info("Resuming input extension"); + em28xx_info("Resuming input extension\n"); /* if suspend calls ir_raw_event_unregister(), the should call ir_raw_event_register() */ if (ir) diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index cf7f58b76292..9ecf65629b3d 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -1958,7 +1958,7 @@ static int em28xx_v4l2_fini(struct em28xx *dev) if (v4l2 == NULL) return 0; - em28xx_info("Closing video extension"); + em28xx_info("Closing video extension\n"); mutex_lock(&dev->lock); @@ -2007,7 +2007,7 @@ static int em28xx_v4l2_suspend(struct em28xx *dev) if (!dev->has_video) return 0; - em28xx_info("Suspending video extension"); + em28xx_info("Suspending video extension\n"); em28xx_stop_urbs(dev); return 0; } @@ -2020,7 +2020,7 @@ static int em28xx_v4l2_resume(struct em28xx *dev) if (!dev->has_video) return 0; - em28xx_info("Resuming video extension"); + em28xx_info("Resuming video extension\n"); /* what do we do here */ return 0; } @@ -2192,7 +2192,6 @@ static struct video_device *vfd = *template; vfd->v4l2_dev = &dev->v4l2->v4l2_dev; - vfd->debug = video_debug; vfd->lock = &dev->lock; if (dev->board.is_webcam) vfd->tvnorms = 0; diff --git a/drivers/media/usb/gspca/Kconfig b/drivers/media/usb/gspca/Kconfig index eed10d782535..60af3b167f3b 100644 --- a/drivers/media/usb/gspca/Kconfig +++ b/drivers/media/usb/gspca/Kconfig @@ -395,6 +395,16 @@ config USB_GSPCA_TOPRO To compile this driver as a module, choose M here: the module will be called gspca_topro. +config USB_GSPCA_TOUPTEK + tristate "Touptek USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the ToupTek UCMOS + / AmScope MU series camera. + + To compile this driver as a module, choose M here: the + module will be called gspca_touptek. + config USB_GSPCA_TV8532 tristate "TV8532 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/drivers/media/usb/gspca/Makefile b/drivers/media/usb/gspca/Makefile index f46975e4c82d..9f5ccecb9c8a 100644 --- a/drivers/media/usb/gspca/Makefile +++ b/drivers/media/usb/gspca/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_USB_GSPCA_STK1135) += gspca_stk1135.o obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o obj-$(CONFIG_USB_GSPCA_TOPRO) += gspca_topro.o +obj-$(CONFIG_USB_GSPCA_TOUPTEK) += gspca_touptek.o obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o obj-$(CONFIG_USB_GSPCA_VICAM) += gspca_vicam.o @@ -86,6 +87,7 @@ gspca_stv0680-objs := stv0680.o gspca_sunplus-objs := sunplus.o gspca_t613-objs := t613.o gspca_topro-objs := topro.o +gspca_touptek-objs := touptek.o gspca_tv8532-objs := tv8532.o gspca_vc032x-objs := vc032x.o gspca_vicam-objs := vicam.o diff --git a/drivers/media/usb/gspca/gspca.c b/drivers/media/usb/gspca/gspca.c index 43d65057a5fe..e54cee856a80 100644 --- a/drivers/media/usb/gspca/gspca.c +++ b/drivers/media/usb/gspca/gspca.c @@ -1562,7 +1562,7 @@ static int vidioc_s_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm) { struct gspca_dev *gspca_dev = video_drvdata(filp); - int n; + unsigned int n; n = parm->parm.capture.readbuffers; if (n == 0 || n >= GSPCA_MAX_FRAMES) diff --git a/drivers/media/usb/gspca/ov534.c b/drivers/media/usb/gspca/ov534.c index 90f0d637cd9d..a9c866d6d82d 100644 --- a/drivers/media/usb/gspca/ov534.c +++ b/drivers/media/usb/gspca/ov534.c @@ -12,6 +12,8 @@ * PS3 Eye camera enhanced by Richard Kaswy http://kaswy.free.fr * PS3 Eye camera - brightness, contrast, awb, agc, aec controls * added by Max Thrun <bear24rw@gmail.com> + * PS3 Eye camera - FPS range extended by Joseph Howse + * <josephhowse@nummist.com> http://nummist.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 @@ -116,7 +118,7 @@ static const struct v4l2_pix_format ov767x_mode[] = { .colorspace = V4L2_COLORSPACE_JPEG}, }; -static const u8 qvga_rates[] = {125, 100, 75, 60, 50, 40, 30}; +static const u8 qvga_rates[] = {187, 150, 137, 125, 100, 75, 60, 50, 37, 30}; static const u8 vga_rates[] = {60, 50, 40, 30, 15}; static const struct framerates ov772x_framerates[] = { @@ -769,12 +771,16 @@ static void set_frame_rate(struct gspca_dev *gspca_dev) {15, 0x03, 0x41, 0x04}, }; static const struct rate_s rate_1[] = { /* 320x240 */ +/* {205, 0x01, 0xc1, 0x02}, * 205 FPS: video is partly corrupt */ + {187, 0x01, 0x81, 0x02}, /* 187 FPS or below: video is valid */ + {150, 0x01, 0xc1, 0x04}, + {137, 0x02, 0xc1, 0x02}, {125, 0x02, 0x81, 0x02}, {100, 0x02, 0xc1, 0x04}, {75, 0x03, 0xc1, 0x04}, {60, 0x04, 0xc1, 0x04}, {50, 0x02, 0x41, 0x04}, - {40, 0x03, 0x41, 0x04}, + {37, 0x03, 0x41, 0x04}, {30, 0x04, 0x41, 0x04}, }; diff --git a/drivers/media/usb/gspca/stv06xx/stv06xx.c b/drivers/media/usb/gspca/stv06xx/stv06xx.c index 49d209bbf9ee..6ac93d8db427 100644 --- a/drivers/media/usb/gspca/stv06xx/stv06xx.c +++ b/drivers/media/usb/gspca/stv06xx/stv06xx.c @@ -505,13 +505,13 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, { int ret = -EINVAL; - if (len == 1 && data[0] == 0x80) { + if (len == 1 && (data[0] == 0x80 || data[0] == 0x10)) { input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); input_sync(gspca_dev->input_dev); ret = 0; } - if (len == 1 && data[0] == 0x88) { + if (len == 1 && (data[0] == 0x88 || data[0] == 0x11)) { input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); input_sync(gspca_dev->input_dev); ret = 0; diff --git a/drivers/media/usb/gspca/touptek.c b/drivers/media/usb/gspca/touptek.c new file mode 100644 index 000000000000..7bac6bc96063 --- /dev/null +++ b/drivers/media/usb/gspca/touptek.c @@ -0,0 +1,731 @@ +/* + * ToupTek UCMOS / AmScope MU series camera driver + * TODO: contrast with ScopeTek / AmScope MDC cameras + * + * Copyright (C) 2012-2014 John McMaster <JohnDMcMaster@gmail.com> + * + * Special thanks to Bushing for helping with the decrypt algorithm and + * Sean O'Sullivan / the Rensselaer Center for Open Source + * Software (RCOS) for helping me learn kernel development + * + * 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 + * 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. + */ + +#include "gspca.h" + +#define MODULE_NAME "touptek" + +MODULE_AUTHOR("John McMaster"); +MODULE_DESCRIPTION("ToupTek UCMOS / Amscope MU microscope camera driver"); +MODULE_LICENSE("GPL"); + +/* + * Exposure reg is linear with exposure time + * Exposure (sec), E (reg) + * 0.000400, 0x0002 + * 0.001000, 0x0005 + * 0.005000, 0x0019 + * 0.020000, 0x0064 + * 0.080000, 0x0190 + * 0.400000, 0x07D0 + * 1.000000, 0x1388 + * 2.000000, 0x2710 + * + * Three gain stages + * 0x1000: master channel enable bit + * 0x007F: low gain bits + * 0x0080: medium gain bit + * 0x0100: high gain bit + * gain = enable * (1 + regH) * (1 + regM) * z * regL + * + * Gain implementation + * Want to do something similar to mt9v011.c's set_balance + * + * Gain does not vary with resolution (checked 640x480 vs 1600x1200) + * + * Constant derivation: + * + * Raw data: + * Gain, GTOP, B, R, GBOT + * 1.00, 0x105C, 0x1068, 0x10C8, 0x105C + * 1.20, 0x106E, 0x107E, 0x10D6, 0x106E + * 1.40, 0x10C0, 0x10CA, 0x10E5, 0x10C0 + * 1.60, 0x10C9, 0x10D4, 0x10F3, 0x10C9 + * 1.80, 0x10D2, 0x10DE, 0x11C1, 0x10D2 + * 2.00, 0x10DC, 0x10E9, 0x11C8, 0x10DC + * 2.20, 0x10E5, 0x10F3, 0x11CF, 0x10E5 + * 2.40, 0x10EE, 0x10FE, 0x11D7, 0x10EE + * 2.60, 0x10F7, 0x11C4, 0x11DE, 0x10F7 + * 2.80, 0x11C0, 0x11CA, 0x11E5, 0x11C0 + * 3.00, 0x11C5, 0x11CF, 0x11ED, 0x11C5 + * + * zR = 0.0069605943152454778 + * about 3/431 = 0.0069605568445475635 + * zB = 0.0095695970695970703 + * about 6/627 = 0.0095693779904306216 + * zG = 0.010889328063241107 + * about 6/551 = 0.010889292196007259 + * about 10 bits for constant + 7 bits for value => at least 17 bit + * intermediate with 32 bit ints should be fine for overflow etc + * Essentially gains are in range 0-0x001FF + * + * However, V4L expects a main gain channel + R and B balance + * To keep things simple for now saturate the values of balance is too high/low + * This isn't really ideal but easy way to fit the Linux model + * + * Converted using gain model turns out to be quite linear: + * Gain, GTOP, B, R, GBOT + * 1.00, 92, 104, 144, 92 + * 1.20, 110, 126, 172, 110 + * 1.40, 128, 148, 202, 128 + * 1.60, 146, 168, 230, 146 + * 1.80, 164, 188, 260, 164 + * 2.00, 184, 210, 288, 184 + * 2.20, 202, 230, 316, 202 + * 2.40, 220, 252, 348, 220 + * 2.60, 238, 272, 376, 238 + * 2.80, 256, 296, 404, 256 + * 3.00, 276, 316, 436, 276 + * + * Maximum gain is 0x7FF * 2 * 2 => 0x1FFC (8188) + * or about 13 effective bits of gain + * The highest the commercial driver goes in my setup 436 + * However, because could *maybe* damage circuits + * limit the gain until have a reason to go higher + * Solution: gain clipped and warning emitted + */ +#define GAIN_MAX 511 + +/* Frame sync is a short read */ +#define BULK_SIZE 0x4000 + +/* MT9E001 reg names to give a rough approximation */ +#define REG_COARSE_INTEGRATION_TIME_ 0x3012 +#define REG_GROUPED_PARAMETER_HOLD_ 0x3022 +#define REG_MODE_SELECT 0x0100 +#define REG_OP_SYS_CLK_DIV 0x030A +#define REG_VT_SYS_CLK_DIV 0x0302 +#define REG_PRE_PLL_CLK_DIV 0x0304 +#define REG_VT_PIX_CLK_DIV 0x0300 +#define REG_OP_PIX_CLK_DIV 0x0308 +#define REG_PLL_MULTIPLIER 0x0306 +#define REG_COARSE_INTEGRATION_TIME_ 0x3012 +#define REG_FRAME_LENGTH_LINES 0x0340 +#define REG_FRAME_LENGTH_LINES_ 0x300A +#define REG_GREEN1_GAIN 0x3056 +#define REG_GREEN2_GAIN 0x305C +#define REG_GROUPED_PARAMETER_HOLD 0x0104 +#define REG_LINE_LENGTH_PCK_ 0x300C +#define REG_MODE_SELECT 0x0100 +#define REG_PLL_MULTIPLIER 0x0306 +#define REG_READ_MODE 0x3040 +#define REG_BLUE_GAIN 0x3058 +#define REG_RED_GAIN 0x305A +#define REG_RESET_REGISTER 0x301A +#define REG_SCALE_M 0x0404 +#define REG_SCALING_MODE 0x0400 +#define REG_SOFTWARE_RESET 0x0103 +#define REG_X_ADDR_END 0x0348 +#define REG_X_ADDR_START 0x0344 +#define REG_X_ADDR_START 0x0344 +#define REG_X_OUTPUT_SIZE 0x034C +#define REG_Y_ADDR_END 0x034A +#define REG_Y_ADDR_START 0x0346 +#define REG_Y_OUTPUT_SIZE 0x034E + + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + /* How many bytes this frame */ + unsigned int this_f; + + /* + Device has separate gains for each Bayer quadrant + V4L supports master gain which is referenced to G1/G2 and supplies + individual balance controls for R/B + */ + struct v4l2_ctrl *blue; + struct v4l2_ctrl *red; +}; + +/* Used to simplify reg write error handling */ +struct cmd { + u16 value; + u16 index; +}; + +static const struct v4l2_pix_format vga_mode[] = { + {800, 600, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600, + .colorspace = V4L2_COLORSPACE_SRGB}, + {1600, 1200, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .bytesperline = 1600, + .sizeimage = 1600 * 1200, + .colorspace = V4L2_COLORSPACE_SRGB}, + {3264, 2448, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .bytesperline = 3264, + .sizeimage = 3264 * 2448, + .colorspace = V4L2_COLORSPACE_SRGB}, +}; + +/* + * As theres no known frame sync, the only way to keep synced is to try hard + * to never miss any packets + */ +#if MAX_NURBS < 4 +#error "Not enough URBs in the gspca table" +#endif + +static int val_reply(struct gspca_dev *gspca_dev, const char *reply, int rc) +{ + if (rc < 0) { + PERR("reply has error %d", rc); + return -EIO; + } + if (rc != 1) { + PERR("Bad reply size %d", rc); + return -EIO; + } + if (reply[0] != 0x08) { + PERR("Bad reply 0x%02X", reply[0]); + return -EIO; + } + return 0; +} + +static void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index) +{ + char buff[1]; + int rc; + + PDEBUG(D_USBO, + "reg_w bReq=0x0B, bReqT=0xC0, wVal=0x%04X, wInd=0x%04X\n", + value, index); + rc = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), + 0x0B, 0xC0, value, index, buff, 1, 500); + PDEBUG(D_USBO, "rc=%d, ret={0x%02X}", rc, buff[0]); + if (rc < 0) { + PERR("Failed reg_w(0x0B, 0xC0, 0x%04X, 0x%04X) w/ rc %d\n", + value, index, rc); + gspca_dev->usb_err = rc; + return; + } + if (val_reply(gspca_dev, buff, rc)) { + PERR("Bad reply to reg_w(0x0B, 0xC0, 0x%04X, 0x%04X\n", + value, index); + gspca_dev->usb_err = -EIO; + } +} + +static void reg_w_buf(struct gspca_dev *gspca_dev, + const struct cmd *p, int l) +{ + do { + reg_w(gspca_dev, p->value, p->index); + p++; + } while (--l > 0); +} + +static void setexposure(struct gspca_dev *gspca_dev, s32 val) +{ + u16 value; + unsigned int w = gspca_dev->pixfmt.width; + + if (w == 800) + value = val * 5; + else if (w == 1600) + value = val * 3; + else if (w == 3264) + value = val * 3 / 2; + else { + PERR("Invalid width %u\n", w); + gspca_dev->usb_err = -EINVAL; + return; + } + PDEBUG(D_STREAM, "exposure: 0x%04X ms\n", value); + /* Wonder if theres a good reason for sending it twice */ + /* probably not but leave it in because...why not */ + reg_w(gspca_dev, value, REG_COARSE_INTEGRATION_TIME_); + reg_w(gspca_dev, value, REG_COARSE_INTEGRATION_TIME_); +} + +static int gainify(int in) +{ + /* + * TODO: check if there are any issues with corner cases + * 0x000 (0):0x07F (127): regL + * 0x080 (128) - 0x0FF (255): regM, regL + * 0x100 (256) - max: regH, regM, regL + */ + if (in <= 0x7F) + return 0x1000 | in; + else if (in <= 0xFF) + return 0x1080 | in / 2; + else + return 0x1180 | in / 4; +} + +static void setggain(struct gspca_dev *gspca_dev, u16 global_gain) +{ + u16 normalized; + + normalized = gainify(global_gain); + PDEBUG(D_STREAM, "gain G1/G2 (0x%04X): 0x%04X (src 0x%04X)\n", + REG_GREEN1_GAIN, + normalized, global_gain); + + reg_w(gspca_dev, normalized, REG_GREEN1_GAIN); + reg_w(gspca_dev, normalized, REG_GREEN2_GAIN); +} + +static void setbgain(struct gspca_dev *gspca_dev, + u16 gain, u16 global_gain) +{ + u16 normalized; + + normalized = global_gain + + ((u32)global_gain) * gain / GAIN_MAX; + if (normalized > GAIN_MAX) { + PDEBUG(D_STREAM, "Truncating blue 0x%04X w/ value 0x%04X\n", + GAIN_MAX, normalized); + normalized = GAIN_MAX; + } + normalized = gainify(normalized); + PDEBUG(D_STREAM, "gain B (0x%04X): 0x%04X w/ source 0x%04X\n", + REG_BLUE_GAIN, normalized, gain); + + reg_w(gspca_dev, normalized, REG_BLUE_GAIN); +} + +static void setrgain(struct gspca_dev *gspca_dev, + u16 gain, u16 global_gain) +{ + u16 normalized; + + normalized = global_gain + + ((u32)global_gain) * gain / GAIN_MAX; + if (normalized > GAIN_MAX) { + PDEBUG(D_STREAM, "Truncating gain 0x%04X w/ value 0x%04X\n", + GAIN_MAX, normalized); + normalized = GAIN_MAX; + } + normalized = gainify(normalized); + PDEBUG(D_STREAM, "gain R (0x%04X): 0x%04X w / source 0x%04X\n", + REG_RED_GAIN, normalized, gain); + + reg_w(gspca_dev, normalized, REG_RED_GAIN); +} + +static void configure_wh(struct gspca_dev *gspca_dev) +{ + unsigned int w = gspca_dev->pixfmt.width; + + PDEBUG(D_STREAM, "configure_wh\n"); + + if (w == 800) { + static const struct cmd reg_init_res[] = { + {0x0060, REG_X_ADDR_START}, + {0x0CD9, REG_X_ADDR_END}, + {0x0036, REG_Y_ADDR_START}, + {0x098F, REG_Y_ADDR_END}, + {0x07C7, REG_READ_MODE}, + }; + + reg_w_buf(gspca_dev, + reg_init_res, ARRAY_SIZE(reg_init_res)); + } else if (w == 1600) { + static const struct cmd reg_init_res[] = { + {0x009C, REG_X_ADDR_START}, + {0x0D19, REG_X_ADDR_END}, + {0x0068, REG_Y_ADDR_START}, + {0x09C5, REG_Y_ADDR_END}, + {0x06C3, REG_READ_MODE}, + }; + + reg_w_buf(gspca_dev, + reg_init_res, ARRAY_SIZE(reg_init_res)); + } else if (w == 3264) { + static const struct cmd reg_init_res[] = { + {0x00E8, REG_X_ADDR_START}, + {0x0DA7, REG_X_ADDR_END}, + {0x009E, REG_Y_ADDR_START}, + {0x0A2D, REG_Y_ADDR_END}, + {0x0241, REG_READ_MODE}, + }; + + reg_w_buf(gspca_dev, + reg_init_res, ARRAY_SIZE(reg_init_res)); + } else { + PERR("bad width %u\n", w); + gspca_dev->usb_err = -EINVAL; + return; + } + + reg_w(gspca_dev, 0x0000, REG_SCALING_MODE); + reg_w(gspca_dev, 0x0010, REG_SCALE_M); + reg_w(gspca_dev, w, REG_X_OUTPUT_SIZE); + reg_w(gspca_dev, gspca_dev->pixfmt.height, REG_Y_OUTPUT_SIZE); + + if (w == 800) { + reg_w(gspca_dev, 0x0384, REG_FRAME_LENGTH_LINES_); + reg_w(gspca_dev, 0x0960, REG_LINE_LENGTH_PCK_); + } else if (w == 1600) { + reg_w(gspca_dev, 0x0640, REG_FRAME_LENGTH_LINES_); + reg_w(gspca_dev, 0x0FA0, REG_LINE_LENGTH_PCK_); + } else if (w == 3264) { + reg_w(gspca_dev, 0x0B4B, REG_FRAME_LENGTH_LINES_); + reg_w(gspca_dev, 0x1F40, REG_LINE_LENGTH_PCK_); + } else { + PERR("bad width %u\n", w); + gspca_dev->usb_err = -EINVAL; + return; + } +} + +/* Packets that were encrypted, no idea if the grouping is significant */ +static void configure_encrypted(struct gspca_dev *gspca_dev) +{ + static const struct cmd reg_init_begin[] = { + {0x0100, REG_SOFTWARE_RESET}, + {0x0000, REG_MODE_SELECT}, + {0x0100, REG_GROUPED_PARAMETER_HOLD}, + {0x0004, REG_VT_PIX_CLK_DIV}, + {0x0001, REG_VT_SYS_CLK_DIV}, + {0x0008, REG_OP_PIX_CLK_DIV}, + {0x0001, REG_OP_SYS_CLK_DIV}, + {0x0004, REG_PRE_PLL_CLK_DIV}, + {0x0040, REG_PLL_MULTIPLIER}, + {0x0000, REG_GROUPED_PARAMETER_HOLD}, + {0x0100, REG_GROUPED_PARAMETER_HOLD}, + }; + static const struct cmd reg_init_end[] = { + {0x0000, REG_GROUPED_PARAMETER_HOLD}, + {0x0301, 0x31AE}, + {0x0805, 0x3064}, + {0x0071, 0x3170}, + {0x10DE, REG_RESET_REGISTER}, + {0x0000, REG_MODE_SELECT}, + {0x0010, REG_PLL_MULTIPLIER}, + {0x0100, REG_MODE_SELECT}, + }; + + PDEBUG(D_STREAM, "Encrypted begin, w = %u\n", gspca_dev->pixfmt.width); + reg_w_buf(gspca_dev, reg_init_begin, ARRAY_SIZE(reg_init_begin)); + configure_wh(gspca_dev); + reg_w_buf(gspca_dev, reg_init_end, ARRAY_SIZE(reg_init_end)); + reg_w(gspca_dev, 0x0100, REG_GROUPED_PARAMETER_HOLD); + reg_w(gspca_dev, 0x0000, REG_GROUPED_PARAMETER_HOLD); + + PDEBUG(D_STREAM, "Encrypted end\n"); +} + +static int configure(struct gspca_dev *gspca_dev) +{ + int rc; + uint8_t buff[4]; + + PDEBUG(D_STREAM, "configure()\n"); + + /* + * First driver sets a sort of encryption key + * A number of futur requests of this type have wValue and wIndex + * encrypted as follows: + * -Compute key = this wValue rotate left by 4 bits + * (decrypt.py rotates right because we are decrypting) + * -Later packets encrypt packets by XOR'ing with key + * XOR encrypt/decrypt is symmetrical + * wValue, and wIndex are encrypted + * bRequest is not and bRequestType is always 0xC0 + * This allows resyncing if key is unknown? + * By setting 0 we XOR with 0 and the shifting and XOR drops out + */ + rc = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), + 0x16, 0xC0, 0x0000, 0x0000, buff, 2, 500); + if (val_reply(gspca_dev, buff, rc)) { + PERR("failed key req"); + return -EIO; + } + + /* + * Next does some sort of 2 packet challenge / response + * evidence suggests its an Atmel I2C crypto part but nobody cares to + * look + * (to make sure its not cloned hardware?) + * Ignore: I want to work with their hardware, not clone it + * 16 bytes out challenge, requestType: 0x40 + * 16 bytes in response, requestType: 0xC0 + */ + + rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), + 0x01, 0x40, 0x0001, 0x000F, NULL, 0, 500); + if (rc < 0) { + PERR("failed to replay packet 176 w/ rc %d\n", rc); + return rc; + } + + rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), + 0x01, 0x40, 0x0000, 0x000F, NULL, 0, 500); + if (rc < 0) { + PERR("failed to replay packet 178 w/ rc %d\n", rc); + return rc; + } + + rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), + 0x01, 0x40, 0x0001, 0x000F, NULL, 0, 500); + if (rc < 0) { + PERR("failed to replay packet 180 w/ rc %d\n", rc); + return rc; + } + + /* + * Serial number? Doesn't seem to be required + * cam1: \xE6\x0D\x00\x00, cam2: \x70\x19\x00\x00 + * rc = usb_control_msg(gspca_dev->dev, + * usb_rcvctrlpipe(gspca_dev->dev, 0), + * 0x20, 0xC0, 0x0000, 0x0000, buff, 4, 500); + */ + + /* Large (EEPROM?) read, skip it since no idea what to do with it */ + gspca_dev->usb_err = 0; + configure_encrypted(gspca_dev); + if (gspca_dev->usb_err) + return gspca_dev->usb_err; + + /* Omitted this by accident, does not work without it */ + rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), + 0x01, 0x40, 0x0003, 0x000F, NULL, 0, 500); + if (rc < 0) { + PERR("failed to replay final packet w/ rc %d\n", rc); + return rc; + } + + PDEBUG(D_STREAM, "Configure complete\n"); + return 0; +} + +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + gspca_dev->cam.cam_mode = vga_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); + + /* Yes we want URBs and we want them now! */ + gspca_dev->cam.no_urb_create = 0; + gspca_dev->cam.bulk_nurbs = 4; + /* Largest size the windows driver uses */ + gspca_dev->cam.bulk_size = BULK_SIZE; + /* Def need to use bulk transfers */ + gspca_dev->cam.bulk = 1; + + return 0; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int rc; + + sd->this_f = 0; + + rc = configure(gspca_dev); + if (rc < 0) { + PERR("Failed configure"); + return rc; + } + /* First two frames have messed up gains + Drop them to avoid special cases in user apps? */ + return 0; +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (len != BULK_SIZE) { + /* can we finish a frame? */ + if (sd->this_f + len == gspca_dev->pixfmt.sizeimage) { + gspca_frame_add(gspca_dev, LAST_PACKET, data, len); + PDEBUG(D_FRAM, "finish frame sz %u/%u w/ len %u\n", + sd->this_f, gspca_dev->pixfmt.sizeimage, len); + /* lost some data, discard the frame */ + } else { + gspca_frame_add(gspca_dev, DISCARD_PACKET, NULL, 0); + PDEBUG(D_FRAM, "abort frame sz %u/%u w/ len %u\n", + sd->this_f, gspca_dev->pixfmt.sizeimage, len); + } + sd->this_f = 0; + } else { + if (sd->this_f == 0) + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); + else + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); + sd->this_f += len; + } +} + +static int sd_init(struct gspca_dev *gspca_dev) +{ + return 0; +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *) gspca_dev; + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + setexposure(gspca_dev, ctrl->val); + break; + case V4L2_CID_GAIN: + /* gspca_dev->gain automatically updated */ + setggain(gspca_dev, gspca_dev->gain->val); + break; + case V4L2_CID_BLUE_BALANCE: + sd->blue->val = ctrl->val; + setbgain(gspca_dev, sd->blue->val, gspca_dev->gain->val); + break; + case V4L2_CID_RED_BALANCE: + sd->red->val = ctrl->val; + setrgain(gspca_dev, sd->red->val, gspca_dev->gain->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 4); + + gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + /* Mostly limited by URB timeouts */ + /* XXX: make dynamic based on frame rate? */ + V4L2_CID_EXPOSURE, 0, 800, 1, 350); + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 511, 1, 128); + sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 0, 1023, 1, 80); + sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, 1023, 1, 295); + + if (hdl->error) { + PERR("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .init_controls = sd_init_controls, + .start = sd_start, + .pkt_scan = sd_pkt_scan, +}; + +/* Table of supported USB devices */ +static const struct usb_device_id device_table[] = { + /* Commented out devices should be related */ + /* AS: AmScope, TT: ToupTek */ + /* { USB_DEVICE(0x0547, 0x6035) }, TT UCMOS00350KPA */ + /* { USB_DEVICE(0x0547, 0x6130) }, TT UCMOS01300KPA */ + /* { USB_DEVICE(0x0547, 0x6200) }, TT UCMOS02000KPA */ + /* { USB_DEVICE(0x0547, 0x6310) }, TT UCMOS03100KPA */ + /* { USB_DEVICE(0x0547, 0x6510) }, TT UCMOS05100KPA */ + /* { USB_DEVICE(0x0547, 0x6800) }, TT UCMOS08000KPA */ + /* { USB_DEVICE(0x0547, 0x6801) }, TT UCMOS08000KPB */ + { USB_DEVICE(0x0547, 0x6801) }, /* TT UCMOS08000KPB, AS MU800 */ + /* { USB_DEVICE(0x0547, 0x6900) }, TT UCMOS09000KPA */ + /* { USB_DEVICE(0x0547, 0x6901) }, TT UCMOS09000KPB */ + /* { USB_DEVICE(0x0547, 0x6010) }, TT UCMOS10000KPA */ + /* { USB_DEVICE(0x0547, 0x6014) }, TT UCMOS14000KPA */ + /* { USB_DEVICE(0x0547, 0x6131) }, TT UCMOS01300KMA */ + /* { USB_DEVICE(0x0547, 0x6511) }, TT UCMOS05100KMA */ + /* { USB_DEVICE(0x0547, 0x8080) }, TT UHCCD00800KPA */ + /* { USB_DEVICE(0x0547, 0x8140) }, TT UHCCD01400KPA */ + /* { USB_DEVICE(0x0547, 0x8141) }, TT EXCCD01400KPA */ + /* { USB_DEVICE(0x0547, 0x8200) }, TT UHCCD02000KPA */ + /* { USB_DEVICE(0x0547, 0x8201) }, TT UHCCD02000KPB */ + /* { USB_DEVICE(0x0547, 0x8310) }, TT UHCCD03100KPA */ + /* { USB_DEVICE(0x0547, 0x8500) }, TT UHCCD05000KPA */ + /* { USB_DEVICE(0x0547, 0x8510) }, TT UHCCD05100KPA */ + /* { USB_DEVICE(0x0547, 0x8600) }, TT UHCCD06000KPA */ + /* { USB_DEVICE(0x0547, 0x8800) }, TT UHCCD08000KPA */ + /* { USB_DEVICE(0x0547, 0x8315) }, TT UHCCD03150KPA */ + /* { USB_DEVICE(0x0547, 0x7800) }, TT UHCCD00800KMA */ + /* { USB_DEVICE(0x0547, 0x7140) }, TT UHCCD01400KMA */ + /* { USB_DEVICE(0x0547, 0x7141) }, TT UHCCD01400KMB */ + /* { USB_DEVICE(0x0547, 0x7200) }, TT UHCCD02000KMA */ + /* { USB_DEVICE(0x0547, 0x7315) }, TT UHCCD03150KMA */ + { } +}; +MODULE_DEVICE_TABLE(usb, device_table); + +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +static int __init sd_mod_init(void) +{ + int ret; + + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/usb/gspca/vc032x.c b/drivers/media/usb/gspca/vc032x.c index c00ac57de510..b4efb2fb36fa 100644 --- a/drivers/media/usb/gspca/vc032x.c +++ b/drivers/media/usb/gspca/vc032x.c @@ -68,12 +68,12 @@ enum sensors { static const struct v4l2_pix_format vc0321_mode[] = { {320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, - .bytesperline = 320, + .bytesperline = 320 * 2, .sizeimage = 320 * 240 * 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1}, {640, 480, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, - .bytesperline = 640, + .bytesperline = 640 * 2, .sizeimage = 640 * 480 * 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, @@ -97,17 +97,17 @@ static const struct v4l2_pix_format vc0323_mode[] = { }; static const struct v4l2_pix_format bi_mode[] = { {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 320, + .bytesperline = 320 * 2, .sizeimage = 320 * 240 * 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 2}, {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 640, + .bytesperline = 640 * 2, .sizeimage = 640 * 480 * 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1}, {1280, 1024, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, - .bytesperline = 1280, + .bytesperline = 1280 * 2, .sizeimage = 1280 * 1024 * 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, diff --git a/drivers/media/usb/pvrusb2/pvrusb2-audio.c b/drivers/media/usb/pvrusb2/pvrusb2-audio.c index cc06d5e4adcc..45276c628482 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-audio.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-audio.c @@ -84,13 +84,3 @@ void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0); } } - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-audio.h b/drivers/media/usb/pvrusb2/pvrusb2-audio.h index e3e63d750891..27cefb5cb170 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-audio.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-audio.h @@ -25,13 +25,3 @@ #include "pvrusb2-hdw-internal.h" void pvr2_msp3400_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); #endif /* __PVRUSB2_AUDIO_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-context.c b/drivers/media/usb/pvrusb2/pvrusb2-context.c index c8761c71c9d2..924fc4c6019a 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-context.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-context.c @@ -418,14 +418,3 @@ struct pvr2_ioread *pvr2_channel_create_mpeg_stream( pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key)); return cp; } - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-context.h b/drivers/media/usb/pvrusb2/pvrusb2-context.h index d657e53bbfa3..1c1d442d9ea3 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-context.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-context.h @@ -83,12 +83,3 @@ int pvr2_context_global_init(void); void pvr2_context_global_done(void); #endif /* __PVRUSB2_CONTEXT_H */ -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c b/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c index 88320900dbd4..f82f0f0f2c04 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.c @@ -82,14 +82,3 @@ void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) sd->ops->audio->s_routing(sd, input, 0, 0); } } - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h b/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h index 53ba548b72a7..86c17bee56f9 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-cs53l32a.h @@ -36,13 +36,3 @@ void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); #endif /* __PVRUSB2_AUDIO_CS53L32A_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-ctrl.c b/drivers/media/usb/pvrusb2/pvrusb2-ctrl.c index 7d5a7139a45a..958db170a048 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-ctrl.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-ctrl.c @@ -596,14 +596,3 @@ int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *cptr, } while(0); LOCK_GIVE(cptr->hdw->big_lock); return ret; } - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-ctrl.h b/drivers/media/usb/pvrusb2/pvrusb2-ctrl.h index 794ff90121c7..c175571868a3 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-ctrl.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-ctrl.h @@ -110,13 +110,3 @@ int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *, unsigned int *len); #endif /* __PVRUSB2_CTRL_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c index c514d0b9ffdc..1a81aa70509b 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -152,15 +152,3 @@ void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) sd->ops->audio->s_routing(sd, (u32)aud_input, 0, 0); } } - - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h index e35c2322a08c..2eed7b7ee25e 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-cx2584x-v4l.h @@ -40,13 +40,3 @@ void pvr2_cx25840_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *sd); #endif /* __PVRUSB2_CX2584X_V4L_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-debug.h b/drivers/media/usb/pvrusb2/pvrusb2-debug.h index be79249f8628..4ef2ebcd97a5 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-debug.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-debug.h @@ -57,13 +57,3 @@ extern int pvrusb2_debug; #endif /* __PVRUSB2_HDW_INTERNAL_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c index 4279ebb811a1..e4022bcb155b 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.c @@ -322,14 +322,3 @@ int pvr2_debugifc_docmd(struct pvr2_hdw *hdw,const char *buf, return 0; } - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-debugifc.h b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.h index 2f8d46761cd0..a8dfc55f136f 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-debugifc.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-debugifc.h @@ -40,13 +40,3 @@ int pvr2_debugifc_docmd(struct pvr2_hdw *, const char *buf_ptr,unsigned int buf_size); #endif /* __PVRUSB2_DEBUGIFC_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-devattr.c b/drivers/media/usb/pvrusb2/pvrusb2-devattr.c index adc501d3c287..06c4c3dabcde 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-devattr.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-devattr.c @@ -564,13 +564,3 @@ MODULE_FIRMWARE(PVR2_FIRMWARE_29xxx); MODULE_FIRMWARE(PVR2_FIRMWARE_24xxx); MODULE_FIRMWARE(PVR2_FIRMWARE_73xxx); MODULE_FIRMWARE(PVR2_FIRMWARE_75xxx); - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-devattr.h b/drivers/media/usb/pvrusb2/pvrusb2-devattr.h index 273c8d4b3853..5aeefb6a991f 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-devattr.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-devattr.h @@ -187,13 +187,3 @@ struct pvr2_device_desc { extern struct usb_device_id pvr2_device_table[]; #endif /* __PVRUSB2_HDW_INTERNAL_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c index 9515f3a68f8f..e1907cd0c3b7 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.c @@ -152,13 +152,3 @@ int pvr2_eeprom_analyze(struct pvr2_hdw *hdw) return 0; } - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-eeprom.h b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.h index cca3216f94cc..f1e33c807f46 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-eeprom.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-eeprom.h @@ -27,13 +27,3 @@ struct pvr2_hdw; int pvr2_eeprom_analyze(struct pvr2_hdw *); #endif /* __PVRUSB2_EEPROM_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-encoder.c b/drivers/media/usb/pvrusb2/pvrusb2-encoder.c index f7702aeeda3f..593b3e9b6bfd 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-encoder.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-encoder.c @@ -538,14 +538,3 @@ int pvr2_encoder_stop(struct pvr2_hdw *hdw) return status; } - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-encoder.h b/drivers/media/usb/pvrusb2/pvrusb2-encoder.h index 232fefbcd1ac..a2bfb48f1ecd 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-encoder.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-encoder.h @@ -30,13 +30,3 @@ int pvr2_encoder_start(struct pvr2_hdw *); int pvr2_encoder_stop(struct pvr2_hdw *); #endif /* __PVRUSB2_ENCODER_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h b/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h index 614755ea2ea3..06a15a68bcfd 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-fx2-cmd.h @@ -60,13 +60,3 @@ #define FX2CMD_ONAIR_DTV_POWER_OFF 0xa3u #endif /* _PVRUSB2_FX2_CMD_H_ */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h index 036952f2a3cb..1f9c02801cee 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw-internal.h @@ -394,13 +394,3 @@ unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *); void pvr2_hdw_status_poll(struct pvr2_hdw *); #endif /* __PVRUSB2_HDW_INTERNAL_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c index 2fd9b5e0e2a9..930593d7028d 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.c @@ -2425,22 +2425,18 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, } if (!hdw) goto fail; - init_timer(&hdw->quiescent_timer); - hdw->quiescent_timer.data = (unsigned long)hdw; - hdw->quiescent_timer.function = pvr2_hdw_quiescent_timeout; + setup_timer(&hdw->quiescent_timer, pvr2_hdw_quiescent_timeout, + (unsigned long)hdw); - init_timer(&hdw->decoder_stabilization_timer); - hdw->decoder_stabilization_timer.data = (unsigned long)hdw; - hdw->decoder_stabilization_timer.function = - pvr2_hdw_decoder_stabilization_timeout; + setup_timer(&hdw->decoder_stabilization_timer, + pvr2_hdw_decoder_stabilization_timeout, + (unsigned long)hdw); - init_timer(&hdw->encoder_wait_timer); - hdw->encoder_wait_timer.data = (unsigned long)hdw; - hdw->encoder_wait_timer.function = pvr2_hdw_encoder_wait_timeout; + setup_timer(&hdw->encoder_wait_timer, pvr2_hdw_encoder_wait_timeout, + (unsigned long)hdw); - init_timer(&hdw->encoder_run_timer); - hdw->encoder_run_timer.data = (unsigned long)hdw; - hdw->encoder_run_timer.function = pvr2_hdw_encoder_run_timeout; + setup_timer(&hdw->encoder_run_timer, pvr2_hdw_encoder_run_timeout, + (unsigned long)hdw); hdw->master_state = PVR2_STATE_DEAD; @@ -3680,10 +3676,8 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw, hdw->ctl_timeout_flag = 0; hdw->ctl_write_pend_flag = 0; hdw->ctl_read_pend_flag = 0; - init_timer(&timer); + setup_timer(&timer, pvr2_ctl_timeout, (unsigned long)hdw); timer.expires = jiffies + timeout; - timer.data = (unsigned long)hdw; - timer.function = pvr2_ctl_timeout; if (write_len) { hdw->cmd_debug_state = 2; @@ -4035,11 +4029,6 @@ int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw) } -int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *hdw) -{ - return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_OFF); -} - int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) { @@ -4301,9 +4290,8 @@ static int state_eval_encoder_config(struct pvr2_hdw *hdw) the encoder. */ if (!hdw->state_encoder_waitok) { hdw->encoder_wait_timer.expires = - jiffies + - (HZ * TIME_MSEC_ENCODER_WAIT - / 1000); + jiffies + msecs_to_jiffies( + TIME_MSEC_ENCODER_WAIT); add_timer(&hdw->encoder_wait_timer); } } @@ -4426,8 +4414,8 @@ static int state_eval_encoder_run(struct pvr2_hdw *hdw) if (pvr2_encoder_start(hdw) < 0) return !0; hdw->state_encoder_run = !0; if (!hdw->state_encoder_runok) { - hdw->encoder_run_timer.expires = - jiffies + (HZ * TIME_MSEC_ENCODER_OK / 1000); + hdw->encoder_run_timer.expires = jiffies + + msecs_to_jiffies(TIME_MSEC_ENCODER_OK); add_timer(&hdw->encoder_run_timer); } } @@ -4518,9 +4506,8 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw) but before we did the pending check. */ if (!hdw->state_decoder_quiescent) { hdw->quiescent_timer.expires = - jiffies + - (HZ * TIME_MSEC_DECODER_WAIT - / 1000); + jiffies + msecs_to_jiffies( + TIME_MSEC_DECODER_WAIT); add_timer(&hdw->quiescent_timer); } } @@ -4544,9 +4531,8 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw) hdw->state_decoder_run = !0; if (hdw->decoder_client_id == PVR2_CLIENT_ID_SAA7115) { hdw->decoder_stabilization_timer.expires = - jiffies + - (HZ * TIME_MSEC_DECODER_STABILIZATION_WAIT / - 1000); + jiffies + msecs_to_jiffies( + TIME_MSEC_DECODER_STABILIZATION_WAIT); add_timer(&hdw->decoder_stabilization_timer); } else { hdw->state_decoder_ready = !0; diff --git a/drivers/media/usb/pvrusb2/pvrusb2-hdw.h b/drivers/media/usb/pvrusb2/pvrusb2-hdw.h index 41847076f51a..a82a00dd7329 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-hdw.h @@ -271,9 +271,6 @@ int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *); /* Execute simple reset command */ int pvr2_hdw_cmd_powerup(struct pvr2_hdw *); -/* suspend */ -int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *); - /* Order decoder to reset */ int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *); @@ -343,13 +340,3 @@ void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw); int pvr2_upload_firmware2(struct pvr2_hdw *hdw); #endif /* __PVRUSB2_HDW_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c index b5e929f1bf82..4baa9d632a4e 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.c @@ -686,13 +686,3 @@ void pvr2_i2c_core_done(struct pvr2_hdw *hdw) hdw->i2c_linked = 0; } } - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h index 6a75769200bd..a10a3e8e9345 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-i2c-core.h @@ -27,14 +27,3 @@ void pvr2_i2c_core_done(struct pvr2_hdw *); #endif /* __PVRUSB2_I2C_ADAPTER_H */ - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-io.c b/drivers/media/usb/pvrusb2/pvrusb2-io.c index 1e354747de3f..0c08f22bdfce 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-io.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-io.c @@ -682,14 +682,3 @@ int pvr2_buffer_get_id(struct pvr2_buffer *bp) { return bp->id; } - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-io.h b/drivers/media/usb/pvrusb2/pvrusb2-io.h index afb7e87c0394..0c47c6a95ab2 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-io.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-io.h @@ -90,13 +90,3 @@ int pvr2_buffer_get_id(struct pvr2_buffer *); int pvr2_buffer_queue(struct pvr2_buffer *); #endif /* __PVRUSB2_IO_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-ioread.c b/drivers/media/usb/pvrusb2/pvrusb2-ioread.c index bba6115c9ae8..cd995b54732e 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-ioread.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-ioread.c @@ -499,14 +499,3 @@ int pvr2_ioread_read(struct pvr2_ioread *cp,void __user *buf,unsigned int cnt) cp,req_cnt,ret); return ret; } - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-ioread.h b/drivers/media/usb/pvrusb2/pvrusb2-ioread.h index 100e0780e1aa..0b1f0fbc3438 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-ioread.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-ioread.h @@ -36,13 +36,3 @@ int pvr2_ioread_read(struct pvr2_ioread *,void __user *buf,unsigned int cnt); int pvr2_ioread_avail(struct pvr2_ioread *); #endif /* __PVRUSB2_IOREAD_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-main.c b/drivers/media/usb/pvrusb2/pvrusb2-main.c index c1d9bb61cd77..86be902a0049 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-main.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-main.c @@ -169,14 +169,3 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); MODULE_VERSION("0.9.1"); - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-std.c b/drivers/media/usb/pvrusb2/pvrusb2-std.c index 453627b07833..9a596a3a4c27 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-std.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-std.c @@ -398,14 +398,3 @@ v4l2_std_id pvr2_std_get_usable(void) { return CSTD_ALL; } - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-std.h b/drivers/media/usb/pvrusb2/pvrusb2-std.h index a35c53d0b320..ed4ec0474429 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-std.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-std.h @@ -47,13 +47,3 @@ struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, v4l2_std_id pvr2_std_get_usable(void); #endif /* __PVRUSB2_STD_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c index 6ef1335b2858..06fe63ced58c 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.c @@ -848,14 +848,3 @@ static ssize_t debugcmd_store(struct device *class_dev, return count; } #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.h b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.h index 6d875bfe7991..6f0579e1e07b 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-sysfs.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-sysfs.h @@ -34,13 +34,3 @@ struct pvr2_sysfs *pvr2_sysfs_create(struct pvr2_context *, struct pvr2_sysfs_class *); #endif /* __PVRUSB2_SYSFS_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-util.h b/drivers/media/usb/pvrusb2/pvrusb2-util.h index 92b75544ee2e..5465bf9cd73e 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-util.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-util.h @@ -50,13 +50,3 @@ #endif /* __PVRUSB2_UTIL_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c index 1b158f1167ed..35e4ea530494 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.c @@ -89,16 +89,6 @@ static int vbi_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1}; module_param_array(vbi_nr, int, NULL, 0444); MODULE_PARM_DESC(vbi_nr, "Offset for device's vbi dev minor"); -static struct v4l2_capability pvr_capability ={ - .driver = "pvrusb2", - .card = "Hauppauge WinTV pvr-usb2", - .bus_info = "usb", - .version = LINUX_VERSION_CODE, - .capabilities = (V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO | - V4L2_CAP_READWRITE), -}; - static struct v4l2_fmtdesc pvr_fmtdesc [] = { { .index = 0, @@ -160,10 +150,22 @@ static int pvr2_querycap(struct file *file, void *priv, struct v4l2_capability * struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability)); + strlcpy(cap->driver, "pvrusb2", sizeof(cap->driver)); strlcpy(cap->bus_info, pvr2_hdw_get_bus_info(hdw), sizeof(cap->bus_info)); strlcpy(cap->card, pvr2_hdw_get_desc(hdw), sizeof(cap->card)); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | + V4L2_CAP_AUDIO | V4L2_CAP_RADIO | + V4L2_CAP_READWRITE | V4L2_CAP_DEVICE_CAPS; + switch (fh->pdi->devbase.vfl_type) { + case VFL_TYPE_GRABBER: + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO; + break; + case VFL_TYPE_RADIO: + cap->device_caps = V4L2_CAP_RADIO; + break; + } + cap->device_caps |= V4L2_CAP_TUNER | V4L2_CAP_READWRITE; return 0; } @@ -1360,13 +1362,3 @@ struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp) pvr2_v4l2_destroy_no_lock(vp); return NULL; } - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.h b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.h index 34c011a7b107..e455c9515841 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-v4l2.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-v4l2.h @@ -27,13 +27,3 @@ struct pvr2_v4l2; struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *); #endif /* __PVRUSB2_V4L2_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c index 2e205c99eb96..139b39740534 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.c @@ -101,14 +101,3 @@ void pvr2_saa7115_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) sd->ops->video->s_routing(sd, input, 0, 0); } } - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h index 3b0bd5db602b..dacf3ec7f9e1 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-video-v4l.h @@ -36,13 +36,3 @@ void pvr2_saa7115_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); #endif /* __PVRUSB2_VIDEO_V4L_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-wm8775.c b/drivers/media/usb/pvrusb2/pvrusb2-wm8775.c index 3ac8d751a5c0..f1df94a2436f 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-wm8775.c +++ b/drivers/media/usb/pvrusb2/pvrusb2-wm8775.c @@ -56,15 +56,3 @@ void pvr2_wm8775_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) sd->ops->audio->s_routing(sd, input, 0, 0); } } - - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2-wm8775.h b/drivers/media/usb/pvrusb2/pvrusb2-wm8775.h index 0577bc7246fb..a4ee12e28d5c 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2-wm8775.h +++ b/drivers/media/usb/pvrusb2/pvrusb2-wm8775.h @@ -40,13 +40,3 @@ void pvr2_wm8775_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *sd); #endif /* __PVRUSB2_WM8775_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pvrusb2/pvrusb2.h b/drivers/media/usb/pvrusb2/pvrusb2.h index 240de9b35661..95f98a87abb3 100644 --- a/drivers/media/usb/pvrusb2/pvrusb2.h +++ b/drivers/media/usb/pvrusb2/pvrusb2.h @@ -30,13 +30,3 @@ #define PVR_NUM 20 #endif /* __PVRUSB2_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/drivers/media/usb/pwc/pwc-if.c b/drivers/media/usb/pwc/pwc-if.c index 15b754da4a2c..702267e208ba 100644 --- a/drivers/media/usb/pwc/pwc-if.c +++ b/drivers/media/usb/pwc/pwc-if.c @@ -508,7 +508,8 @@ static void pwc_isoc_cleanup(struct pwc_device *pdev) } /* Must be called with vb_queue_lock hold */ -static void pwc_cleanup_queued_bufs(struct pwc_device *pdev) +static void pwc_cleanup_queued_bufs(struct pwc_device *pdev, + enum vb2_buffer_state state) { unsigned long flags = 0; @@ -519,7 +520,7 @@ static void pwc_cleanup_queued_bufs(struct pwc_device *pdev) buf = list_entry(pdev->queued_bufs.next, struct pwc_frame_buf, list); list_del(&buf->list); - vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + vb2_buffer_done(&buf->vb, state); } spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags); } @@ -674,7 +675,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) pwc_set_leds(pdev, 0, 0); pwc_camera_power(pdev, 0); /* And cleanup any queued bufs!! */ - pwc_cleanup_queued_bufs(pdev); + pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED); } mutex_unlock(&pdev->v4l2_lock); @@ -692,7 +693,9 @@ static void stop_streaming(struct vb2_queue *vq) pwc_isoc_cleanup(pdev); } - pwc_cleanup_queued_bufs(pdev); + pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_ERROR); + if (pdev->fill_buf) + vb2_buffer_done(&pdev->fill_buf->vb, VB2_BUF_STATE_ERROR); mutex_unlock(&pdev->v4l2_lock); } @@ -1125,7 +1128,6 @@ static void usb_pwc_disconnect(struct usb_interface *intf) if (pdev->vb_queue.streaming) pwc_isoc_cleanup(pdev); pdev->udev = NULL; - pwc_cleanup_queued_bufs(pdev); v4l2_device_disconnect(&pdev->v4l2_dev); video_unregister_device(&pdev->vdev); diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c index de55e96fed15..0f3c34d47ec3 100644 --- a/drivers/media/usb/s2255/s2255drv.c +++ b/drivers/media/usb/s2255/s2255drv.c @@ -2274,9 +2274,7 @@ static int s2255_probe(struct usb_interface *interface, dev_err(&interface->dev, "Could not find bulk-in endpoint\n"); goto errorEP; } - init_timer(&dev->timer); - dev->timer.function = s2255_timer; - dev->timer.data = (unsigned long)dev->fw_data; + setup_timer(&dev->timer, s2255_timer, (unsigned long)dev->fw_data); init_waitqueue_head(&dev->fw_data->wait_fw); for (i = 0; i < MAX_CHANNELS; i++) { struct s2255_vc *vc = &dev->vc[i]; diff --git a/drivers/media/usb/siano/Kconfig b/drivers/media/usb/siano/Kconfig index 5afbd9a4b55c..d37b742d4f7a 100644 --- a/drivers/media/usb/siano/Kconfig +++ b/drivers/media/usb/siano/Kconfig @@ -5,7 +5,9 @@ config SMS_USB_DRV tristate "Siano SMS1xxx based MDTV receiver" depends on DVB_CORE && HAS_DMA + depends on !RC_CORE || RC_CORE select MEDIA_COMMON_OPTIONS + select SMS_SIANO_MDTV ---help--- Choose if you would like to have Siano's support for USB interface diff --git a/drivers/media/usb/stk1160/stk1160-v4l.c b/drivers/media/usb/stk1160/stk1160-v4l.c index a47629108c1b..65a326c5128f 100644 --- a/drivers/media/usb/stk1160/stk1160-v4l.c +++ b/drivers/media/usb/stk1160/stk1160-v4l.c @@ -38,10 +38,6 @@ #include "stk1160.h" #include "stk1160-reg.h" -static unsigned int vidioc_debug; -module_param(vidioc_debug, int, 0644); -MODULE_PARM_DESC(vidioc_debug, "enable debug messages [vidioc]"); - static bool keep_buffers; module_param(keep_buffers, bool, 0644); MODULE_PARM_DESC(keep_buffers, "don't release buffers upon stop streaming"); @@ -659,7 +655,6 @@ int stk1160_video_register(struct stk1160 *dev) /* Initialize video_device with a template structure */ dev->vdev = v4l_template; - dev->vdev.debug = vidioc_debug; dev->vdev.queue = &dev->vb_vidq; /* diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c index 3588dc38db87..e08fa587332f 100644 --- a/drivers/media/usb/stkwebcam/stk-webcam.c +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -1262,7 +1262,6 @@ static int stk_register_video_device(struct stk_camera *dev) dev->vdev = stk_v4l_data; dev->vdev.lock = &dev->lock; - dev->vdev.debug = debug; dev->vdev.v4l2_dev = &dev->v4l2_dev; video_set_drvdata(&dev->vdev, dev); err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); diff --git a/drivers/media/usb/tm6000/tm6000-video.c b/drivers/media/usb/tm6000/tm6000-video.c index 793577fc4633..0f14d3ccc7b4 100644 --- a/drivers/media/usb/tm6000/tm6000-video.c +++ b/drivers/media/usb/tm6000/tm6000-video.c @@ -941,7 +941,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, fmt = format_by_fourcc(f->fmt.pix.pixelformat); if (NULL == fmt) { - dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Fourcc format (0x%08x)" + dprintk(dev, 2, "Fourcc format (0x%08x)" " invalid.\n", f->fmt.pix.pixelformat); return -EINVAL; } @@ -1622,7 +1622,6 @@ static struct video_device *vdev_init(struct tm6000_core *dev, *vfd = *template; vfd->v4l2_dev = &dev->v4l2_dev; vfd->release = video_device_release; - vfd->debug = tm6000_debug; vfd->lock = &dev->lock; snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); diff --git a/drivers/media/usb/usbvision/usbvision-core.c b/drivers/media/usb/usbvision/usbvision-core.c index 302aa07c458f..44b0c28d69b6 100644 --- a/drivers/media/usb/usbvision/usbvision-core.c +++ b/drivers/media/usb/usbvision/usbvision-core.c @@ -2194,9 +2194,8 @@ static void usbvision_power_off_timer(unsigned long data) void usbvision_init_power_off_timer(struct usb_usbvision *usbvision) { - init_timer(&usbvision->power_off_timer); - usbvision->power_off_timer.data = (long)usbvision; - usbvision->power_off_timer.function = usbvision_power_off_timer; + setup_timer(&usbvision->power_off_timer, usbvision_power_off_timer, + (unsigned long)usbvision); } void usbvision_set_power_off_timer(struct usb_usbvision *usbvision) @@ -2502,11 +2501,3 @@ int usbvision_muxsel(struct usb_usbvision *usbvision, int channel) usbvision_set_audio(usbvision, audio[channel]); return 0; } - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/usbvision/usbvision-i2c.c b/drivers/media/usb/usbvision/usbvision-i2c.c index ba262a32bd3a..26dbcb1146af 100644 --- a/drivers/media/usb/usbvision/usbvision-i2c.c +++ b/drivers/media/usb/usbvision/usbvision-i2c.c @@ -445,11 +445,3 @@ static struct i2c_adapter i2c_adap_template = { .owner = THIS_MODULE, .name = "usbvision", }; - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/usbvision/usbvision-video.c b/drivers/media/usb/usbvision/usbvision-video.c index 693d5f409138..cd2fbf11e3b4 100644 --- a/drivers/media/usb/usbvision/usbvision-video.c +++ b/drivers/media/usb/usbvision/usbvision-video.c @@ -1716,11 +1716,3 @@ static void __exit usbvision_exit(void) module_init(usbvision_init); module_exit(usbvision_exit); - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/usbvision/usbvision.h b/drivers/media/usb/usbvision/usbvision.h index a0c73cf1517c..77aeb1ed9a81 100644 --- a/drivers/media/usb/usbvision/usbvision.h +++ b/drivers/media/usb/usbvision/usbvision.h @@ -517,11 +517,3 @@ int usbvision_power_off(struct usb_usbvision *usbvision); int usbvision_power_on(struct usb_usbvision *usbvision); #endif /* __LINUX_USBVISION_H */ - -/* - * Overrides for Emacs so that we follow Linus's tabbing style. - * --------------------------------------------------------------------------- - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/usb/uvc/uvc_driver.c b/drivers/media/usb/uvc/uvc_driver.c index 6a4b0b8cd270..cf27006c29dc 100644 --- a/drivers/media/usb/uvc/uvc_driver.c +++ b/drivers/media/usb/uvc/uvc_driver.c @@ -138,6 +138,11 @@ static struct uvc_format_desc uvc_fmts[] = { .fcc = V4L2_PIX_FMT_RGB565, }, { + .name = "BGR 8:8:8 (BGR3)", + .guid = UVC_GUID_FORMAT_BGR3, + .fcc = V4L2_PIX_FMT_BGR24, + }, + { .name = "H.264", .guid = UVC_GUID_FORMAT_H264, .fcc = V4L2_PIX_FMT_H264, diff --git a/drivers/media/usb/uvc/uvc_queue.c b/drivers/media/usb/uvc/uvc_queue.c index cc960723b926..10c554e7655c 100644 --- a/drivers/media/usb/uvc/uvc_queue.c +++ b/drivers/media/usb/uvc/uvc_queue.c @@ -143,20 +143,6 @@ static void uvc_buffer_finish(struct vb2_buffer *vb) uvc_video_clock_update(stream, &vb->v4l2_buf, buf); } -static void uvc_wait_prepare(struct vb2_queue *vq) -{ - struct uvc_video_queue *queue = vb2_get_drv_priv(vq); - - mutex_unlock(&queue->mutex); -} - -static void uvc_wait_finish(struct vb2_queue *vq) -{ - struct uvc_video_queue *queue = vb2_get_drv_priv(vq); - - mutex_lock(&queue->mutex); -} - static int uvc_start_streaming(struct vb2_queue *vq, unsigned int count) { struct uvc_video_queue *queue = vb2_get_drv_priv(vq); @@ -195,8 +181,8 @@ static struct vb2_ops uvc_queue_qops = { .buf_prepare = uvc_buffer_prepare, .buf_queue = uvc_buffer_queue, .buf_finish = uvc_buffer_finish, - .wait_prepare = uvc_wait_prepare, - .wait_finish = uvc_wait_finish, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, .start_streaming = uvc_start_streaming, .stop_streaming = uvc_stop_streaming, }; @@ -214,6 +200,7 @@ int uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type, queue->queue.mem_ops = &vb2_vmalloc_memops; queue->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC | V4L2_BUF_FLAG_TSTAMP_SRC_SOE; + queue->queue.lock = &queue->mutex; ret = vb2_queue_init(&queue->queue); if (ret) return ret; diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c index 9c5cbcf16529..43e953f73e02 100644 --- a/drivers/media/usb/uvc/uvc_v4l2.c +++ b/drivers/media/usb/uvc/uvc_v4l2.c @@ -13,7 +13,6 @@ #include <linux/compat.h> #include <linux/kernel.h> -#include <linux/version.h> #include <linux/list.h> #include <linux/module.h> #include <linux/slab.h> diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c index 9637e8b86949..20ccc9d315dc 100644 --- a/drivers/media/usb/uvc/uvc_video.c +++ b/drivers/media/usb/uvc/uvc_video.c @@ -1734,13 +1734,13 @@ int uvc_video_resume(struct uvc_streaming *stream, int reset) uvc_video_clock_reset(stream); + if (!uvc_queue_streaming(&stream->queue)) + return 0; + ret = uvc_commit_video(stream, &stream->ctrl); if (ret < 0) return ret; - if (!uvc_queue_streaming(&stream->queue)) - return 0; - return uvc_init_video(stream, GFP_NOIO); } diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h index f0a04b532ede..c63e5b55e143 100644 --- a/drivers/media/usb/uvc/uvcvideo.h +++ b/drivers/media/usb/uvc/uvcvideo.h @@ -109,6 +109,9 @@ #define UVC_GUID_FORMAT_RGBP \ { 'R', 'G', 'B', 'P', 0x00, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_BGR3 \ + { 0x7d, 0xeb, 0x36, 0xe4, 0x4f, 0x52, 0xce, 0x11, \ + 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70} #define UVC_GUID_FORMAT_M420 \ { 'M', '4', '2', '0', 0x00, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c index 5c006277b8b1..ca850316d379 100644 --- a/drivers/media/usb/zr364xx/zr364xx.c +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -1454,8 +1454,6 @@ static int zr364xx_probe(struct usb_interface *intf, cam->vdev.v4l2_dev = &cam->v4l2_dev; cam->vdev.ctrl_handler = &cam->ctrl_handler; video_set_drvdata(&cam->vdev, cam); - if (debug) - cam->vdev.debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; cam->udev = udev; diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 9aa530a8bea9..86bb93fd7db8 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -47,15 +47,15 @@ static ssize_t index_show(struct device *cd, } static DEVICE_ATTR_RO(index); -static ssize_t debug_show(struct device *cd, +static ssize_t dev_debug_show(struct device *cd, struct device_attribute *attr, char *buf) { struct video_device *vdev = to_video_device(cd); - return sprintf(buf, "%i\n", vdev->debug); + return sprintf(buf, "%i\n", vdev->dev_debug); } -static ssize_t debug_store(struct device *cd, struct device_attribute *attr, +static ssize_t dev_debug_store(struct device *cd, struct device_attribute *attr, const char *buf, size_t len) { struct video_device *vdev = to_video_device(cd); @@ -66,10 +66,10 @@ static ssize_t debug_store(struct device *cd, struct device_attribute *attr, if (res) return res; - vdev->debug = value; + vdev->dev_debug = value; return len; } -static DEVICE_ATTR_RW(debug); +static DEVICE_ATTR_RW(dev_debug); static ssize_t name_show(struct device *cd, struct device_attribute *attr, char *buf) @@ -82,7 +82,7 @@ static DEVICE_ATTR_RO(name); static struct attribute *video_device_attrs[] = { &dev_attr_name.attr, - &dev_attr_debug.attr, + &dev_attr_dev_debug.attr, &dev_attr_index.attr, NULL, }; @@ -304,7 +304,8 @@ static ssize_t v4l2_read(struct file *filp, char __user *buf, return -EINVAL; if (video_is_registered(vdev)) ret = vdev->fops->read(filp, buf, sz, off); - if (vdev->debug) + if ((vdev->dev_debug & V4L2_DEV_DEBUG_FOP) && + (vdev->dev_debug & V4L2_DEV_DEBUG_STREAMING)) printk(KERN_DEBUG "%s: read: %zd (%d)\n", video_device_node_name(vdev), sz, ret); return ret; @@ -320,7 +321,8 @@ static ssize_t v4l2_write(struct file *filp, const char __user *buf, return -EINVAL; if (video_is_registered(vdev)) ret = vdev->fops->write(filp, buf, sz, off); - if (vdev->debug) + if ((vdev->dev_debug & V4L2_DEV_DEBUG_FOP) && + (vdev->dev_debug & V4L2_DEV_DEBUG_STREAMING)) printk(KERN_DEBUG "%s: write: %zd (%d)\n", video_device_node_name(vdev), sz, ret); return ret; @@ -335,7 +337,7 @@ static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll) return DEFAULT_POLLMASK; if (video_is_registered(vdev)) res = vdev->fops->poll(filp, poll); - if (vdev->debug > 2) + if (vdev->dev_debug & V4L2_DEV_DEBUG_POLL) printk(KERN_DEBUG "%s: poll: %08x\n", video_device_node_name(vdev), res); return res; @@ -404,7 +406,7 @@ static unsigned long v4l2_get_unmapped_area(struct file *filp, if (!video_is_registered(vdev)) return -ENODEV; ret = vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags); - if (vdev->debug) + if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP) printk(KERN_DEBUG "%s: get_unmapped_area (%d)\n", video_device_node_name(vdev), ret); return ret; @@ -420,7 +422,7 @@ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) return -ENODEV; if (video_is_registered(vdev)) ret = vdev->fops->mmap(filp, vm); - if (vdev->debug) + if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP) printk(KERN_DEBUG "%s: mmap (%d)\n", video_device_node_name(vdev), ret); return ret; @@ -450,7 +452,7 @@ static int v4l2_open(struct inode *inode, struct file *filp) ret = -ENODEV; } - if (vdev->debug) + if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP) printk(KERN_DEBUG "%s: open (%d)\n", video_device_node_name(vdev), ret); /* decrease the refcount in case of an error */ @@ -467,7 +469,7 @@ static int v4l2_release(struct inode *inode, struct file *filp) if (vdev->fops->release) ret = vdev->fops->release(filp); - if (vdev->debug) + if (vdev->dev_debug & V4L2_DEV_DEBUG_FOP) printk(KERN_DEBUG "%s: release\n", video_device_node_name(vdev)); @@ -1033,10 +1035,3 @@ MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org>"); MODULE_DESCRIPTION("Device registrar for Video4Linux drivers v2"); MODULE_LICENSE("GPL"); MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR); - - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index faac2f4e0f3a..b08407225db1 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2339,7 +2339,7 @@ static long __video_do_ioctl(struct file *file, const struct v4l2_ioctl_info *info; void *fh = file->private_data; struct v4l2_fh *vfh = NULL; - int debug = vfd->debug; + int dev_debug = vfd->dev_debug; long ret = -ENOTTY; if (ops == NULL) { @@ -2388,11 +2388,15 @@ static long __video_do_ioctl(struct file *file, } done: - if (debug) { + if (dev_debug & (V4L2_DEV_DEBUG_IOCTL | V4L2_DEV_DEBUG_IOCTL_ARG)) { + if (!(dev_debug & V4L2_DEV_DEBUG_STREAMING) && + (cmd == VIDIOC_QBUF || cmd == VIDIOC_DQBUF)) + return ret; + v4l_printk_ioctl(video_device_node_name(vfd), cmd); if (ret < 0) pr_cont(": error %ld", ret); - if (debug == V4L2_DEBUG_IOCTL) + if (!(dev_debug & V4L2_DEV_DEBUG_IOCTL_ARG)) pr_cont("\n"); else if (_IOC_DIR(cmd) == _IOC_NONE) info->debug(arg, write_only); diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 543631c3557a..19a034e79be4 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -283,10 +283,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (rval) return rval; - rval = v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop); - if (rval != -ENOIOCTLCMD) - return rval; - memset(&sel, 0, sizeof(sel)); sel.which = crop->which; sel.pad = crop->pad; @@ -308,10 +304,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (rval) return rval; - rval = v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop); - if (rval != -ENOIOCTLCMD) - return rval; - memset(&sel, 0, sizeof(sel)); sel.which = crop->which; sel.pad = crop->pad; diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c index 3ff15f1c9d70..f669cedca8bd 100644 --- a/drivers/media/v4l2-core/videobuf-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf-dma-sg.c @@ -145,12 +145,11 @@ struct videobuf_dmabuf *videobuf_to_dma(struct videobuf_buffer *buf) } EXPORT_SYMBOL_GPL(videobuf_to_dma); -void videobuf_dma_init(struct videobuf_dmabuf *dma) +static void videobuf_dma_init(struct videobuf_dmabuf *dma) { memset(dma, 0, sizeof(*dma)); dma->magic = MAGIC_DMABUF; } -EXPORT_SYMBOL_GPL(videobuf_dma_init); static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma, int direction, unsigned long data, unsigned long size) @@ -195,7 +194,7 @@ static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma, return 0; } -int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, +static int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, unsigned long data, unsigned long size) { int ret; @@ -206,9 +205,8 @@ int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, return ret; } -EXPORT_SYMBOL_GPL(videobuf_dma_init_user); -int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, +static int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, int nr_pages) { int i; @@ -267,9 +265,8 @@ out_free_pages: return -ENOMEM; } -EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel); -int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction, +static int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction, dma_addr_t addr, int nr_pages) { dprintk(1, "init overlay [%d pages @ bus 0x%lx]\n", @@ -284,9 +281,8 @@ int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction, return 0; } -EXPORT_SYMBOL_GPL(videobuf_dma_init_overlay); -int videobuf_dma_map(struct device *dev, struct videobuf_dmabuf *dma) +static int videobuf_dma_map(struct device *dev, struct videobuf_dmabuf *dma) { MAGIC_CHECK(dma->magic, MAGIC_DMABUF); BUG_ON(0 == dma->nr_pages); @@ -328,7 +324,6 @@ int videobuf_dma_map(struct device *dev, struct videobuf_dmabuf *dma) return 0; } -EXPORT_SYMBOL_GPL(videobuf_dma_map); int videobuf_dma_unmap(struct device *dev, struct videobuf_dmabuf *dma) { diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index d09a8916e940..bc08a829bc13 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -3146,27 +3146,26 @@ static int vb2_thread(void *data) prequeue--; } else { call_void_qop(q, wait_finish, q); - ret = vb2_internal_dqbuf(q, &fileio->b, 0); + if (!threadio->stop) + ret = vb2_internal_dqbuf(q, &fileio->b, 0); call_void_qop(q, wait_prepare, q); dprintk(5, "file io: vb2_dqbuf result: %d\n", ret); } - if (threadio->stop) - break; - if (ret) + if (ret || threadio->stop) break; try_to_freeze(); vb = q->bufs[fileio->b.index]; if (!(fileio->b.flags & V4L2_BUF_FLAG_ERROR)) - ret = threadio->fnc(vb, threadio->priv); - if (ret) - break; + if (threadio->fnc(vb, threadio->priv)) + break; call_void_qop(q, wait_finish, q); if (set_timestamp) v4l2_get_timestamp(&fileio->b.timestamp); - ret = vb2_internal_qbuf(q, &fileio->b); + if (!threadio->stop) + ret = vb2_internal_qbuf(q, &fileio->b); call_void_qop(q, wait_prepare, q); - if (ret) + if (ret || threadio->stop) break; } @@ -3235,11 +3234,11 @@ int vb2_thread_stop(struct vb2_queue *q) threadio->stop = true; vb2_internal_streamoff(q, q->type); call_void_qop(q, wait_prepare, q); + err = kthread_stop(threadio->thread); q->fileio = NULL; fileio->req.count = 0; vb2_reqbufs(q, &fileio->req); kfree(fileio); - err = kthread_stop(threadio->thread); threadio->thread = NULL; kfree(threadio); q->fileio = NULL; diff --git a/drivers/media/v4l2-core/videobuf2-vmalloc.c b/drivers/media/v4l2-core/videobuf2-vmalloc.c index fba944e50227..bcde88572429 100644 --- a/drivers/media/v4l2-core/videobuf2-vmalloc.c +++ b/drivers/media/v4l2-core/videobuf2-vmalloc.c @@ -95,7 +95,7 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr, if (vb2_get_contig_userptr(vaddr, size, &vma, &physp)) goto fail_pages_array_alloc; buf->vma = vma; - buf->vaddr = ioremap_nocache(physp, size); + buf->vaddr = (__force void *)ioremap_nocache(physp, size); if (!buf->vaddr) goto fail_pages_array_alloc; } else { @@ -155,7 +155,7 @@ static void vb2_vmalloc_put_userptr(void *buf_priv) kfree(buf->pages); } else { vb2_put_vma(buf->vma); - iounmap(buf->vaddr); + iounmap((__force void __iomem *)buf->vaddr); } kfree(buf); } @@ -211,6 +211,7 @@ static int vb2_vmalloc_mmap(void *buf_priv, struct vm_area_struct *vma) return 0; } +#ifdef CONFIG_HAS_DMA /*********************************************/ /* DMABUF ops for exporters */ /*********************************************/ @@ -380,6 +381,8 @@ static struct dma_buf *vb2_vmalloc_get_dmabuf(void *buf_priv, unsigned long flag return dbuf; } +#endif /* CONFIG_HAS_DMA */ + /*********************************************/ /* callbacks for DMABUF buffers */ @@ -437,7 +440,9 @@ const struct vb2_mem_ops vb2_vmalloc_memops = { .put = vb2_vmalloc_put, .get_userptr = vb2_vmalloc_get_userptr, .put_userptr = vb2_vmalloc_put_userptr, +#ifdef CONFIG_HAS_DMA .get_dmabuf = vb2_vmalloc_get_dmabuf, +#endif .map_dmabuf = vb2_vmalloc_map_dmabuf, .unmap_dmabuf = vb2_vmalloc_unmap_dmabuf, .attach_dmabuf = vb2_vmalloc_attach_dmabuf, diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c index 52a0c2f6264f..ae498b53ee40 100644 --- a/drivers/mfd/da9052-core.c +++ b/drivers/mfd/da9052-core.c @@ -554,7 +554,8 @@ int da9052_device_init(struct da9052 *da9052, u8 chip_id) return ret; } - ret = mfd_add_devices(da9052->dev, -1, da9052_subdev_info, + ret = mfd_add_devices(da9052->dev, PLATFORM_DEVID_AUTO, + da9052_subdev_info, ARRAY_SIZE(da9052_subdev_info), NULL, 0, NULL); if (ret) { dev_err(da9052->dev, "mfd_add_devices failed: %d\n", ret); diff --git a/drivers/mfd/rtsx_usb.c b/drivers/mfd/rtsx_usb.c index dbdd0faeb6ce..210d1f85679e 100644 --- a/drivers/mfd/rtsx_usb.c +++ b/drivers/mfd/rtsx_usb.c @@ -681,21 +681,9 @@ static void rtsx_usb_disconnect(struct usb_interface *intf) #ifdef CONFIG_PM static int rtsx_usb_suspend(struct usb_interface *intf, pm_message_t message) { - struct rtsx_ucr *ucr = - (struct rtsx_ucr *)usb_get_intfdata(intf); - dev_dbg(&intf->dev, "%s called with pm message 0x%04x\n", __func__, message.event); - /* - * Call to make sure LED is off during suspend to save more power. - * It is NOT a permanent state and could be turned on anytime later. - * Thus no need to call turn_on when resunming. - */ - mutex_lock(&ucr->dev_mutex); - rtsx_usb_turn_off_led(ucr); - mutex_unlock(&ucr->dev_mutex); - return 0; } diff --git a/drivers/mfd/tps65218.c b/drivers/mfd/tps65218.c index 0d256cb002eb..d6b764349f9d 100644 --- a/drivers/mfd/tps65218.c +++ b/drivers/mfd/tps65218.c @@ -125,10 +125,21 @@ int tps65218_clear_bits(struct tps65218 *tps, unsigned int reg, } EXPORT_SYMBOL_GPL(tps65218_clear_bits); +static const struct regmap_range tps65218_yes_ranges[] = { + regmap_reg_range(TPS65218_REG_INT1, TPS65218_REG_INT2), + regmap_reg_range(TPS65218_REG_STATUS, TPS65218_REG_STATUS), +}; + +static const struct regmap_access_table tps65218_volatile_table = { + .yes_ranges = tps65218_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(tps65218_yes_ranges), +}; + static struct regmap_config tps65218_regmap_config = { .reg_bits = 8, .val_bits = 8, .cache_type = REGCACHE_RBTREE, + .volatile_table = &tps65218_volatile_table, }; static const struct regmap_irq tps65218_irqs[] = { @@ -193,6 +204,7 @@ static struct regmap_irq_chip tps65218_irq_chip = { .num_regs = 2, .mask_base = TPS65218_REG_INT_MASK1, + .status_base = TPS65218_REG_INT1, }; static const struct of_device_id of_tps65218_match_table[] = { diff --git a/drivers/misc/cxl/context.c b/drivers/misc/cxl/context.c index 51fd6b524371..d1b55fe62817 100644 --- a/drivers/misc/cxl/context.c +++ b/drivers/misc/cxl/context.c @@ -100,6 +100,46 @@ int cxl_context_init(struct cxl_context *ctx, struct cxl_afu *afu, bool master, return 0; } +static int cxl_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct cxl_context *ctx = vma->vm_file->private_data; + unsigned long address = (unsigned long)vmf->virtual_address; + u64 area, offset; + + offset = vmf->pgoff << PAGE_SHIFT; + + pr_devel("%s: pe: %i address: 0x%lx offset: 0x%llx\n", + __func__, ctx->pe, address, offset); + + if (ctx->afu->current_mode == CXL_MODE_DEDICATED) { + area = ctx->afu->psn_phys; + if (offset > ctx->afu->adapter->ps_size) + return VM_FAULT_SIGBUS; + } else { + area = ctx->psn_phys; + if (offset > ctx->psn_size) + return VM_FAULT_SIGBUS; + } + + mutex_lock(&ctx->status_mutex); + + if (ctx->status != STARTED) { + mutex_unlock(&ctx->status_mutex); + pr_devel("%s: Context not started, failing problem state access\n", __func__); + return VM_FAULT_SIGBUS; + } + + vm_insert_pfn(vma, address, (area + offset) >> PAGE_SHIFT); + + mutex_unlock(&ctx->status_mutex); + + return VM_FAULT_NOPAGE; +} + +static const struct vm_operations_struct cxl_mmap_vmops = { + .fault = cxl_mmap_fault, +}; + /* * Map a per-context mmio space into the given vma. */ @@ -108,26 +148,25 @@ int cxl_context_iomap(struct cxl_context *ctx, struct vm_area_struct *vma) u64 len = vma->vm_end - vma->vm_start; len = min(len, ctx->psn_size); - if (ctx->afu->current_mode == CXL_MODE_DEDICATED) { - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - return vm_iomap_memory(vma, ctx->afu->psn_phys, ctx->afu->adapter->ps_size); - } + if (ctx->afu->current_mode != CXL_MODE_DEDICATED) { + /* make sure there is a valid per process space for this AFU */ + if ((ctx->master && !ctx->afu->psa) || (!ctx->afu->pp_psa)) { + pr_devel("AFU doesn't support mmio space\n"); + return -EINVAL; + } - /* make sure there is a valid per process space for this AFU */ - if ((ctx->master && !ctx->afu->psa) || (!ctx->afu->pp_psa)) { - pr_devel("AFU doesn't support mmio space\n"); - return -EINVAL; + /* Can't mmap until the AFU is enabled */ + if (!ctx->afu->enabled) + return -EBUSY; } - /* Can't mmap until the AFU is enabled */ - if (!ctx->afu->enabled) - return -EBUSY; - pr_devel("%s: mmio physical: %llx pe: %i master:%i\n", __func__, ctx->psn_phys, ctx->pe , ctx->master); + vma->vm_flags |= VM_IO | VM_PFNMAP; vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - return vm_iomap_memory(vma, ctx->psn_phys, len); + vma->vm_ops = &cxl_mmap_vmops; + return 0; } /* @@ -150,12 +189,6 @@ static void __detach_context(struct cxl_context *ctx) afu_release_irqs(ctx); flush_work(&ctx->fault_work); /* Only needed for dedicated process */ wake_up_all(&ctx->wq); - - /* Release Problem State Area mapping */ - mutex_lock(&ctx->mapping_lock); - if (ctx->mapping) - unmap_mapping_range(ctx->mapping, 0, 0, 1); - mutex_unlock(&ctx->mapping_lock); } /* @@ -184,6 +217,17 @@ void cxl_context_detach_all(struct cxl_afu *afu) * created and torn down after the IDR removed */ __detach_context(ctx); + + /* + * We are force detaching - remove any active PSA mappings so + * userspace cannot interfere with the card if it comes back. + * Easiest way to exercise this is to unbind and rebind the + * driver via sysfs while it is in use. + */ + mutex_lock(&ctx->mapping_lock); + if (ctx->mapping) + unmap_mapping_range(ctx->mapping, 0, 0, 1); + mutex_unlock(&ctx->mapping_lock); } mutex_unlock(&afu->contexts_lock); } diff --git a/drivers/misc/cxl/file.c b/drivers/misc/cxl/file.c index e9f2f10dbb37..b15d8113877c 100644 --- a/drivers/misc/cxl/file.c +++ b/drivers/misc/cxl/file.c @@ -140,18 +140,20 @@ static long afu_ioctl_start_work(struct cxl_context *ctx, pr_devel("%s: pe: %i\n", __func__, ctx->pe); - mutex_lock(&ctx->status_mutex); - if (ctx->status != OPENED) { - rc = -EIO; - goto out; - } - + /* Do this outside the status_mutex to avoid a circular dependency with + * the locking in cxl_mmap_fault() */ if (copy_from_user(&work, uwork, sizeof(struct cxl_ioctl_start_work))) { rc = -EFAULT; goto out; } + mutex_lock(&ctx->status_mutex); + if (ctx->status != OPENED) { + rc = -EIO; + goto out; + } + /* * if any of the reserved fields are set or any of the unused * flags are set it's invalid diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index ff2755062b44..06ff0a2ec960 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -234,6 +234,18 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) struct mei_me_hw *hw = to_me_hw(dev); u32 hcsr = mei_hcsr_read(hw); + /* H_RST may be found lit before reset is started, + * for example if preceding reset flow hasn't completed. + * In that case asserting H_RST will be ignored, therefore + * we need to clean H_RST bit to start a successful reset sequence. + */ + if ((hcsr & H_RST) == H_RST) { + dev_warn(dev->dev, "H_RST is set = 0x%08X", hcsr); + hcsr &= ~H_RST; + mei_me_reg_write(hw, H_CSR, hcsr); + hcsr = mei_hcsr_read(hw); + } + hcsr |= H_RST | H_IG | H_IS; if (intr_enable) diff --git a/drivers/misc/vmw_vmci/vmci_queue_pair.c b/drivers/misc/vmw_vmci/vmci_queue_pair.c index 7aaaf51e1596..35f19a683822 100644 --- a/drivers/misc/vmw_vmci/vmci_queue_pair.c +++ b/drivers/misc/vmw_vmci/vmci_queue_pair.c @@ -370,12 +370,12 @@ static int __qp_memcpy_to_queue(struct vmci_queue *queue, to_copy = size - bytes_copied; if (is_iovec) { - struct iovec *iov = (struct iovec *)src; + struct msghdr *msg = (struct msghdr *)src; int err; /* The iovec will track bytes_copied internally. */ - err = memcpy_fromiovec((u8 *)va + page_offset, - iov, to_copy); + err = memcpy_from_msg((u8 *)va + page_offset, + msg, to_copy); if (err != 0) { if (kernel_if->host) kunmap(kernel_if->u.h.page[page_index]); @@ -580,7 +580,7 @@ static int qp_memcpy_from_queue(void *dest, */ static int qp_memcpy_to_queue_iov(struct vmci_queue *queue, u64 queue_offset, - const void *src, + const void *msg, size_t src_offset, size_t size) { @@ -588,7 +588,7 @@ static int qp_memcpy_to_queue_iov(struct vmci_queue *queue, * We ignore src_offset because src is really a struct iovec * and will * maintain offset internally. */ - return __qp_memcpy_to_queue(queue, queue_offset, src, size, true); + return __qp_memcpy_to_queue(queue, queue_offset, msg, size, true); } /* @@ -3223,13 +3223,13 @@ EXPORT_SYMBOL_GPL(vmci_qpair_peek); * of bytes enqueued or < 0 on error. */ ssize_t vmci_qpair_enquev(struct vmci_qp *qpair, - void *iov, + struct msghdr *msg, size_t iov_size, int buf_type) { ssize_t result; - if (!qpair || !iov) + if (!qpair) return VMCI_ERROR_INVALID_ARGS; qp_lock(qpair); @@ -3238,7 +3238,7 @@ ssize_t vmci_qpair_enquev(struct vmci_qp *qpair, result = qp_enqueue_locked(qpair->produce_q, qpair->consume_q, qpair->produce_q_size, - iov, iov_size, + msg, iov_size, qp_memcpy_to_queue_iov); if (result == VMCI_ERROR_QUEUEPAIR_NOT_READY && diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index e3e56d35f0ee..970314e0aac8 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -247,6 +247,7 @@ static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = { { "INT33BB" , "3" , &sdhci_acpi_slot_int_sd }, { "INT33C6" , NULL, &sdhci_acpi_slot_int_sdio }, { "INT3436" , NULL, &sdhci_acpi_slot_int_sdio }, + { "INT344D" , NULL, &sdhci_acpi_slot_int_sdio }, { "PNP0D40" }, { }, }; @@ -257,6 +258,7 @@ static const struct acpi_device_id sdhci_acpi_ids[] = { { "INT33BB" }, { "INT33C6" }, { "INT3436" }, + { "INT344D" }, { "PNP0D40" }, { }, }; diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 03427755b902..4f38554ce679 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -993,6 +993,31 @@ static const struct pci_device_id pci_ids[] = { .subdevice = PCI_ANY_ID, .driver_data = (kernel_ulong_t)&sdhci_intel_mrfl_mmc, }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_SPT_EMMC, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_SPT_SDIO, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_SPT_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_byt_sd, + }, + { .vendor = PCI_VENDOR_ID_O2, .device = PCI_DEVICE_ID_O2_8120, diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index d57c3d169914..1ec684d06d54 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -21,6 +21,9 @@ #define PCI_DEVICE_ID_INTEL_CLV_EMMC0 0x08e5 #define PCI_DEVICE_ID_INTEL_CLV_EMMC1 0x08e6 #define PCI_DEVICE_ID_INTEL_QRK_SD 0x08A7 +#define PCI_DEVICE_ID_INTEL_SPT_EMMC 0x9d2b +#define PCI_DEVICE_ID_INTEL_SPT_SDIO 0x9d2c +#define PCI_DEVICE_ID_INTEL_SPT_SD 0x9d2d /* * PCI registers diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 45238871192d..ca3424e7ef71 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -300,13 +300,6 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) if (IS_ERR(host)) return PTR_ERR(host); - if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) { - ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info()); - if (ret < 0) - goto err_mbus_win; - } - - pltfm_host = sdhci_priv(host); pltfm_host->priv = pxa; @@ -325,6 +318,12 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) if (!IS_ERR(pxa->clk_core)) clk_prepare_enable(pxa->clk_core); + if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) { + ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info()); + if (ret < 0) + goto err_mbus_win; + } + /* enable 1/8V DDR capable */ host->mmc->caps |= MMC_CAP_1_8V_DDR; @@ -396,11 +395,11 @@ err_add_host: pm_runtime_disable(&pdev->dev); err_of_parse: err_cd_req: +err_mbus_win: clk_disable_unprepare(pxa->clk_io); if (!IS_ERR(pxa->clk_core)) clk_disable_unprepare(pxa->clk_core); err_clk_get: -err_mbus_win: sdhci_pltfm_free(pdev); return ret; } diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index cbb245b58538..f1a488ee432f 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -259,8 +259,6 @@ static void sdhci_reinit(struct sdhci_host *host) del_timer_sync(&host->tuning_timer); host->flags &= ~SDHCI_NEEDS_RETUNING; - host->mmc->max_blk_count = - (host->quirks & SDHCI_QUIRK_NO_MULTIBLOCK) ? 1 : 65535; } sdhci_enable_card_detection(host); } @@ -1273,6 +1271,12 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned char mode, spin_unlock_irq(&host->lock); mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); spin_lock_irq(&host->lock); + + if (mode != MMC_POWER_OFF) + sdhci_writeb(host, SDHCI_POWER_ON, SDHCI_POWER_CONTROL); + else + sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); + return; } @@ -1353,6 +1357,8 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) sdhci_runtime_pm_get(host); + present = mmc_gpio_get_cd(host->mmc); + spin_lock_irqsave(&host->lock, flags); WARN_ON(host->mrq != NULL); @@ -1381,7 +1387,6 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) * zero: cd-gpio is used, and card is removed * one: cd-gpio is used, and card is present */ - present = mmc_gpio_get_cd(host->mmc); if (present < 0) { /* If polling, assume that the card is always present. */ if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) @@ -1880,6 +1885,18 @@ static int sdhci_card_busy(struct mmc_host *mmc) return !(present_state & SDHCI_DATA_LVL_MASK); } +static int sdhci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct sdhci_host *host = mmc_priv(mmc); + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + host->flags |= SDHCI_HS400_TUNING; + spin_unlock_irqrestore(&host->lock, flags); + + return 0; +} + static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) { struct sdhci_host *host = mmc_priv(mmc); @@ -1887,10 +1904,18 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) int tuning_loop_counter = MAX_TUNING_LOOP; int err = 0; unsigned long flags; + unsigned int tuning_count = 0; + bool hs400_tuning; sdhci_runtime_pm_get(host); spin_lock_irqsave(&host->lock, flags); + hs400_tuning = host->flags & SDHCI_HS400_TUNING; + host->flags &= ~SDHCI_HS400_TUNING; + + if (host->tuning_mode == SDHCI_TUNING_MODE_1) + tuning_count = host->tuning_count; + /* * The Host Controller needs tuning only in case of SDR104 mode * and for SDR50 mode when Use Tuning for SDR50 is set in the @@ -1899,8 +1924,20 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) * tuning function has to be executed. */ switch (host->timing) { + /* HS400 tuning is done in HS200 mode */ case MMC_TIMING_MMC_HS400: + err = -EINVAL; + goto out_unlock; + case MMC_TIMING_MMC_HS200: + /* + * Periodic re-tuning for HS400 is not expected to be needed, so + * disable it here. + */ + if (hs400_tuning) + tuning_count = 0; + break; + case MMC_TIMING_UHS_SDR104: break; @@ -1911,9 +1948,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) /* FALLTHROUGH */ default: - spin_unlock_irqrestore(&host->lock, flags); - sdhci_runtime_pm_put(host); - return 0; + goto out_unlock; } if (host->ops->platform_execute_tuning) { @@ -2037,24 +2072,11 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) } out: - /* - * If this is the very first time we are here, we start the retuning - * timer. Since only during the first time, SDHCI_NEEDS_RETUNING - * flag won't be set, we check this condition before actually starting - * the timer. - */ - if (!(host->flags & SDHCI_NEEDS_RETUNING) && host->tuning_count && - (host->tuning_mode == SDHCI_TUNING_MODE_1)) { + host->flags &= ~SDHCI_NEEDS_RETUNING; + + if (tuning_count) { host->flags |= SDHCI_USING_RETUNING_TIMER; - mod_timer(&host->tuning_timer, jiffies + - host->tuning_count * HZ); - /* Tuning mode 1 limits the maximum data length to 4MB */ - mmc->max_blk_count = (4 * 1024 * 1024) / mmc->max_blk_size; - } else if (host->flags & SDHCI_USING_RETUNING_TIMER) { - host->flags &= ~SDHCI_NEEDS_RETUNING; - /* Reload the new initial value for timer */ - mod_timer(&host->tuning_timer, jiffies + - host->tuning_count * HZ); + mod_timer(&host->tuning_timer, jiffies + tuning_count * HZ); } /* @@ -2070,6 +2092,7 @@ out: sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); +out_unlock: spin_unlock_irqrestore(&host->lock, flags); sdhci_runtime_pm_put(host); @@ -2110,15 +2133,18 @@ static void sdhci_card_event(struct mmc_host *mmc) { struct sdhci_host *host = mmc_priv(mmc); unsigned long flags; + int present; /* First check if client has provided their own card event */ if (host->ops->card_event) host->ops->card_event(host); + present = sdhci_do_get_cd(host); + spin_lock_irqsave(&host->lock, flags); /* Check host->mrq first in case we are runtime suspended */ - if (host->mrq && !sdhci_do_get_cd(host)) { + if (host->mrq && !present) { pr_err("%s: Card removed during transfer!\n", mmc_hostname(host->mmc)); pr_err("%s: Resetting controller.\n", @@ -2142,6 +2168,7 @@ static const struct mmc_host_ops sdhci_ops = { .hw_reset = sdhci_hw_reset, .enable_sdio_irq = sdhci_enable_sdio_irq, .start_signal_voltage_switch = sdhci_start_signal_voltage_switch, + .prepare_hs400_tuning = sdhci_prepare_hs400_tuning, .execute_tuning = sdhci_execute_tuning, .card_event = sdhci_card_event, .card_busy = sdhci_card_busy, @@ -3260,8 +3287,9 @@ int sdhci_add_host(struct sdhci_host *host) mmc->max_segs = SDHCI_MAX_SEGS; /* - * Maximum number of sectors in one transfer. Limited by DMA boundary - * size (512KiB). + * Maximum number of sectors in one transfer. Limited by SDMA boundary + * size (512KiB). Note some tuning modes impose a 4MiB limit, but this + * is less anyway. */ mmc->max_req_size = 524288; diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index d6607ee9c855..84673ebcf428 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -197,6 +197,7 @@ config NETCONSOLE_DYNAMIC config NETPOLL def_bool NETCONSOLE + select SRCU config NET_POLL_CONTROLLER def_bool NETPOLL diff --git a/drivers/net/arcnet/com20020-pci.c b/drivers/net/arcnet/com20020-pci.c index 6c99ff0b0bdd..945f532078e9 100644 --- a/drivers/net/arcnet/com20020-pci.c +++ b/drivers/net/arcnet/com20020-pci.c @@ -78,6 +78,9 @@ static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *i priv = devm_kzalloc(&pdev->dev, sizeof(struct com20020_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + ci = (struct com20020_pci_card_info *)id->driver_data; priv->ci = ci; diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 8baa87df1738..cfc4a9c1000a 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -467,11 +467,14 @@ static void __record_pdu(struct lacpdu *lacpdu, struct port *port) /* set the partner sync. to on if the partner is sync, * and the port is matched */ - if ((port->sm_vars & AD_PORT_MATCHED) - && (lacpdu->actor_state & AD_STATE_SYNCHRONIZATION)) + if ((port->sm_vars & AD_PORT_MATCHED) && + (lacpdu->actor_state & AD_STATE_SYNCHRONIZATION)) { partner->port_state |= AD_STATE_SYNCHRONIZATION; - else + pr_debug("%s partner sync=1\n", port->slave->dev->name); + } else { partner->port_state &= ~AD_STATE_SYNCHRONIZATION; + pr_debug("%s partner sync=0\n", port->slave->dev->name); + } } } @@ -726,6 +729,8 @@ static inline void __update_lacpdu_from_port(struct port *port) lacpdu->actor_port_priority = htons(port->actor_port_priority); lacpdu->actor_port = htons(port->actor_port_number); lacpdu->actor_state = port->actor_oper_port_state; + pr_debug("update lacpdu: %s, actor port state %x\n", + port->slave->dev->name, port->actor_oper_port_state); /* lacpdu->reserved_3_1 initialized * lacpdu->tlv_type_partner_info initialized @@ -898,7 +903,9 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) if ((port->sm_vars & AD_PORT_SELECTED) && (port->partner_oper.port_state & AD_STATE_SYNCHRONIZATION) && !__check_agg_selection_timer(port)) { - port->sm_mux_state = AD_MUX_COLLECTING_DISTRIBUTING; + if (port->aggregator->is_active) + port->sm_mux_state = + AD_MUX_COLLECTING_DISTRIBUTING; } else if (!(port->sm_vars & AD_PORT_SELECTED) || (port->sm_vars & AD_PORT_STANDBY)) { /* if UNSELECTED or STANDBY */ @@ -910,12 +917,16 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) */ __set_agg_ports_ready(port->aggregator, __agg_ports_are_ready(port->aggregator)); port->sm_mux_state = AD_MUX_DETACHED; + } else if (port->aggregator->is_active) { + port->actor_oper_port_state |= + AD_STATE_SYNCHRONIZATION; } break; case AD_MUX_COLLECTING_DISTRIBUTING: if (!(port->sm_vars & AD_PORT_SELECTED) || (port->sm_vars & AD_PORT_STANDBY) || - !(port->partner_oper.port_state & AD_STATE_SYNCHRONIZATION)) { + !(port->partner_oper.port_state & AD_STATE_SYNCHRONIZATION) || + !(port->actor_oper_port_state & AD_STATE_SYNCHRONIZATION)) { port->sm_mux_state = AD_MUX_ATTACHED; } else { /* if port state hasn't changed make @@ -937,8 +948,10 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) /* check if the state machine was changed */ if (port->sm_mux_state != last_state) { - pr_debug("Mux Machine: Port=%d, Last State=%d, Curr State=%d\n", - port->actor_port_number, last_state, + pr_debug("Mux Machine: Port=%d (%s), Last State=%d, Curr State=%d\n", + port->actor_port_number, + port->slave->dev->name, + last_state, port->sm_mux_state); switch (port->sm_mux_state) { case AD_MUX_DETACHED: @@ -953,7 +966,12 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) port->sm_mux_timer_counter = __ad_timer_to_ticks(AD_WAIT_WHILE_TIMER, 0); break; case AD_MUX_ATTACHED: - port->actor_oper_port_state |= AD_STATE_SYNCHRONIZATION; + if (port->aggregator->is_active) + port->actor_oper_port_state |= + AD_STATE_SYNCHRONIZATION; + else + port->actor_oper_port_state &= + ~AD_STATE_SYNCHRONIZATION; port->actor_oper_port_state &= ~AD_STATE_COLLECTING; port->actor_oper_port_state &= ~AD_STATE_DISTRIBUTING; ad_disable_collecting_distributing(port, @@ -963,6 +981,7 @@ static void ad_mux_machine(struct port *port, bool *update_slave_arr) case AD_MUX_COLLECTING_DISTRIBUTING: port->actor_oper_port_state |= AD_STATE_COLLECTING; port->actor_oper_port_state |= AD_STATE_DISTRIBUTING; + port->actor_oper_port_state |= AD_STATE_SYNCHRONIZATION; ad_enable_collecting_distributing(port, update_slave_arr); port->ntt = true; @@ -1044,8 +1063,10 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) /* check if the State machine was changed or new lacpdu arrived */ if ((port->sm_rx_state != last_state) || (lacpdu)) { - pr_debug("Rx Machine: Port=%d, Last State=%d, Curr State=%d\n", - port->actor_port_number, last_state, + pr_debug("Rx Machine: Port=%d (%s), Last State=%d, Curr State=%d\n", + port->actor_port_number, + port->slave->dev->name, + last_state, port->sm_rx_state); switch (port->sm_rx_state) { case AD_RX_INITIALIZE: @@ -1394,6 +1415,9 @@ static void ad_port_selection_logic(struct port *port, bool *update_slave_arr) aggregator = __get_first_agg(port); ad_agg_selection_logic(aggregator, update_slave_arr); + + if (!port->aggregator->is_active) + port->actor_oper_port_state &= ~AD_STATE_SYNCHRONIZATION; } /* Decide if "agg" is a better choice for the new active aggregator that @@ -2195,8 +2219,10 @@ static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, switch (lacpdu->subtype) { case AD_TYPE_LACPDU: ret = RX_HANDLER_CONSUMED; - netdev_dbg(slave->bond->dev, "Received LACPDU on port %d\n", - port->actor_port_number); + netdev_dbg(slave->bond->dev, + "Received LACPDU on port %d slave %s\n", + port->actor_port_number, + slave->dev->name); /* Protect against concurrent state machines */ spin_lock(&slave->bond->mode_lock); ad_rx_machine(lacpdu, port); @@ -2288,7 +2314,10 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave) port->actor_admin_port_key &= ~AD_DUPLEX_KEY_MASKS; port->actor_oper_port_key = port->actor_admin_port_key |= __get_duplex(port); - netdev_dbg(slave->bond->dev, "Port %d changed duplex\n", port->actor_port_number); + netdev_dbg(slave->bond->dev, "Port %d slave %s changed duplex\n", + port->actor_port_number, slave->dev->name); + if (port->actor_oper_port_key & AD_DUPLEX_KEY_MASKS) + port->sm_vars |= AD_PORT_LACP_ENABLED; /* there is no need to reselect a new aggregator, just signal the * state machines to reinitialize */ diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 0dceba1a2ba1..b979c265fc51 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -77,6 +77,7 @@ #include <net/pkt_sched.h> #include <linux/rculist.h> #include <net/flow_keys.h> +#include <net/switchdev.h> #include <net/bonding.h> #include <net/bond_3ad.h> #include <net/bond_alb.h> @@ -334,7 +335,7 @@ static int bond_vlan_rx_kill_vid(struct net_device *bond_dev, * * Returns zero if carrier state does not change, nonzero if it does. */ -static int bond_set_carrier(struct bonding *bond) +int bond_set_carrier(struct bonding *bond) { struct list_head *iter; struct slave *slave; @@ -789,7 +790,7 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active) } new_active->delay = 0; - new_active->link = BOND_LINK_UP; + bond_set_slave_link_state(new_active, BOND_LINK_UP); if (BOND_MODE(bond) == BOND_MODE_8023AD) bond_3ad_handle_link_change(new_active, BOND_LINK_UP); @@ -979,7 +980,11 @@ static netdev_features_t bond_fix_features(struct net_device *dev, netdev_features_t mask; struct slave *slave; - mask = features; + /* If any slave has the offload feature flag set, + * set the offload flag on the bond. + */ + mask = features | NETIF_F_HW_SWITCH_OFFLOAD; + features &= ~NETIF_F_ONE_FOR_ALL; features |= NETIF_F_ALL_FOR_ALL; @@ -998,7 +1003,7 @@ static netdev_features_t bond_fix_features(struct net_device *dev, NETIF_F_HIGHDMA | NETIF_F_LRO) #define BOND_ENC_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | NETIF_F_RXCSUM |\ - NETIF_F_TSO | NETIF_F_GSO_UDP_TUNNEL) + NETIF_F_TSO) static void bond_compute_features(struct bonding *bond) { @@ -1034,7 +1039,7 @@ static void bond_compute_features(struct bonding *bond) done: bond_dev->vlan_features = vlan_features; - bond_dev->hw_enc_features = enc_features; + bond_dev->hw_enc_features = enc_features | NETIF_F_GSO_ENCAP_ALL; bond_dev->hard_header_len = max_hard_header_len; bond_dev->gso_max_segs = gso_max_segs; netif_set_gso_max_size(bond_dev, gso_max_size); @@ -1176,6 +1181,56 @@ static void bond_free_slave(struct slave *slave) kfree(slave); } +static void bond_fill_ifbond(struct bonding *bond, struct ifbond *info) +{ + info->bond_mode = BOND_MODE(bond); + info->miimon = bond->params.miimon; + info->num_slaves = bond->slave_cnt; +} + +static void bond_fill_ifslave(struct slave *slave, struct ifslave *info) +{ + strcpy(info->slave_name, slave->dev->name); + info->link = slave->link; + info->state = bond_slave_state(slave); + info->link_failure_count = slave->link_failure_count; +} + +static void bond_netdev_notify(struct net_device *dev, + struct netdev_bonding_info *info) +{ + rtnl_lock(); + netdev_bonding_info_change(dev, info); + rtnl_unlock(); +} + +static void bond_netdev_notify_work(struct work_struct *_work) +{ + struct netdev_notify_work *w = + container_of(_work, struct netdev_notify_work, work.work); + + bond_netdev_notify(w->dev, &w->bonding_info); + dev_put(w->dev); + kfree(w); +} + +void bond_queue_slave_event(struct slave *slave) +{ + struct bonding *bond = slave->bond; + struct netdev_notify_work *nnw = kzalloc(sizeof(*nnw), GFP_ATOMIC); + + if (!nnw) + return; + + dev_hold(slave->dev); + nnw->dev = slave->dev; + bond_fill_ifslave(slave, &nnw->bonding_info.slave); + bond_fill_ifbond(bond, &nnw->bonding_info.master); + INIT_DELAYED_WORK(&nnw->work, bond_netdev_notify_work); + + queue_delayed_work(slave->bond->wq, &nnw->work, 0); +} + /* enslave device <slave> to bond device <master> */ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) { @@ -1439,19 +1494,22 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) if (bond->params.miimon) { if (bond_check_dev_link(bond, slave_dev, 0) == BMSR_LSTATUS) { if (bond->params.updelay) { - new_slave->link = BOND_LINK_BACK; + bond_set_slave_link_state(new_slave, + BOND_LINK_BACK); new_slave->delay = bond->params.updelay; } else { - new_slave->link = BOND_LINK_UP; + bond_set_slave_link_state(new_slave, + BOND_LINK_UP); } } else { - new_slave->link = BOND_LINK_DOWN; + bond_set_slave_link_state(new_slave, BOND_LINK_DOWN); } } else if (bond->params.arp_interval) { - new_slave->link = (netif_carrier_ok(slave_dev) ? - BOND_LINK_UP : BOND_LINK_DOWN); + bond_set_slave_link_state(new_slave, + (netif_carrier_ok(slave_dev) ? + BOND_LINK_UP : BOND_LINK_DOWN)); } else { - new_slave->link = BOND_LINK_UP; + bond_set_slave_link_state(new_slave, BOND_LINK_UP); } if (new_slave->link != BOND_LINK_DOWN) @@ -1567,6 +1625,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) new_slave->link != BOND_LINK_DOWN ? "an up" : "a down"); /* enslave is successful */ + bond_queue_slave_event(new_slave); return 0; /* Undo stages on error */ @@ -1816,11 +1875,7 @@ static int bond_release_and_destroy(struct net_device *bond_dev, static int bond_info_query(struct net_device *bond_dev, struct ifbond *info) { struct bonding *bond = netdev_priv(bond_dev); - - info->bond_mode = BOND_MODE(bond); - info->miimon = bond->params.miimon; - info->num_slaves = bond->slave_cnt; - + bond_fill_ifbond(bond, info); return 0; } @@ -1834,10 +1889,7 @@ static int bond_slave_info_query(struct net_device *bond_dev, struct ifslave *in bond_for_each_slave(bond, slave, iter) { if (i++ == (int)info->slave_id) { res = 0; - strcpy(info->slave_name, slave->dev->name); - info->link = slave->link; - info->state = bond_slave_state(slave); - info->link_failure_count = slave->link_failure_count; + bond_fill_ifslave(slave, info); break; } } @@ -1867,7 +1919,7 @@ static int bond_miimon_inspect(struct bonding *bond) if (link_state) continue; - slave->link = BOND_LINK_FAIL; + bond_set_slave_link_state(slave, BOND_LINK_FAIL); slave->delay = bond->params.downdelay; if (slave->delay) { netdev_info(bond->dev, "link status down for %sinterface %s, disabling it in %d ms\n", @@ -1882,7 +1934,7 @@ static int bond_miimon_inspect(struct bonding *bond) case BOND_LINK_FAIL: if (link_state) { /* recovered before downdelay expired */ - slave->link = BOND_LINK_UP; + bond_set_slave_link_state(slave, BOND_LINK_UP); slave->last_link_up = jiffies; netdev_info(bond->dev, "link status up again after %d ms for interface %s\n", (bond->params.downdelay - slave->delay) * @@ -1904,7 +1956,7 @@ static int bond_miimon_inspect(struct bonding *bond) if (!link_state) continue; - slave->link = BOND_LINK_BACK; + bond_set_slave_link_state(slave, BOND_LINK_BACK); slave->delay = bond->params.updelay; if (slave->delay) { @@ -1917,7 +1969,8 @@ static int bond_miimon_inspect(struct bonding *bond) /*FALLTHRU*/ case BOND_LINK_BACK: if (!link_state) { - slave->link = BOND_LINK_DOWN; + bond_set_slave_link_state(slave, + BOND_LINK_DOWN); netdev_info(bond->dev, "link status down again after %d ms for interface %s\n", (bond->params.updelay - slave->delay) * bond->params.miimon, @@ -1955,7 +2008,7 @@ static void bond_miimon_commit(struct bonding *bond) continue; case BOND_LINK_UP: - slave->link = BOND_LINK_UP; + bond_set_slave_link_state(slave, BOND_LINK_UP); slave->last_link_up = jiffies; primary = rtnl_dereference(bond->primary_slave); @@ -1995,7 +2048,7 @@ static void bond_miimon_commit(struct bonding *bond) if (slave->link_failure_count < UINT_MAX) slave->link_failure_count++; - slave->link = BOND_LINK_DOWN; + bond_set_slave_link_state(slave, BOND_LINK_DOWN); if (BOND_MODE(bond) == BOND_MODE_ACTIVEBACKUP || BOND_MODE(bond) == BOND_MODE_8023AD) @@ -2578,7 +2631,7 @@ static void bond_ab_arp_commit(struct bonding *bond) struct slave *current_arp_slave; current_arp_slave = rtnl_dereference(bond->current_arp_slave); - slave->link = BOND_LINK_UP; + bond_set_slave_link_state(slave, BOND_LINK_UP); if (current_arp_slave) { bond_set_slave_inactive_flags( current_arp_slave, @@ -2601,7 +2654,7 @@ static void bond_ab_arp_commit(struct bonding *bond) if (slave->link_failure_count < UINT_MAX) slave->link_failure_count++; - slave->link = BOND_LINK_DOWN; + bond_set_slave_link_state(slave, BOND_LINK_DOWN); bond_set_slave_inactive_flags(slave, BOND_SLAVE_NOTIFY_NOW); @@ -2680,7 +2733,7 @@ static bool bond_ab_arp_probe(struct bonding *bond) * up when it is actually down */ if (!bond_slave_is_up(slave) && slave->link == BOND_LINK_UP) { - slave->link = BOND_LINK_DOWN; + bond_set_slave_link_state(slave, BOND_LINK_DOWN); if (slave->link_failure_count < UINT_MAX) slave->link_failure_count++; @@ -2700,7 +2753,7 @@ static bool bond_ab_arp_probe(struct bonding *bond) if (!new_slave) goto check_state; - new_slave->link = BOND_LINK_BACK; + bond_set_slave_link_state(new_slave, BOND_LINK_BACK); bond_set_slave_active_flags(new_slave, BOND_SLAVE_NOTIFY_LATER); bond_arp_send_all(bond, new_slave); new_slave->last_link_up = jiffies; @@ -3066,7 +3119,7 @@ static int bond_open(struct net_device *bond_dev) slave != rcu_access_pointer(bond->curr_active_slave)) { bond_set_slave_inactive_flags(slave, BOND_SLAVE_NOTIFY_NOW); - } else { + } else if (BOND_MODE(bond) != BOND_MODE_8023AD) { bond_set_slave_active_flags(slave, BOND_SLAVE_NOTIFY_NOW); } @@ -3734,7 +3787,7 @@ out: * usable slave array is formed in the control path. The xmit function * just calculates hash and sends the packet out. */ -int bond_3ad_xor_xmit(struct sk_buff *skb, struct net_device *dev) +static int bond_3ad_xor_xmit(struct sk_buff *skb, struct net_device *dev) { struct bonding *bond = netdev_priv(dev); struct slave *slave; @@ -3952,6 +4005,8 @@ static const struct net_device_ops bond_netdev_ops = { .ndo_add_slave = bond_enslave, .ndo_del_slave = bond_release, .ndo_fix_features = bond_fix_features, + .ndo_bridge_setlink = ndo_dflt_netdev_switch_port_bridge_setlink, + .ndo_bridge_dellink = ndo_dflt_netdev_switch_port_bridge_dellink, }; static const struct device_type bond_type = { @@ -4010,7 +4065,7 @@ void bond_setup(struct net_device *bond_dev) NETIF_F_HW_VLAN_CTAG_FILTER; bond_dev->hw_features &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_HW_CSUM); - bond_dev->hw_features |= NETIF_F_GSO_UDP_TUNNEL; + bond_dev->hw_features |= NETIF_F_GSO_ENCAP_ALL; bond_dev->features |= bond_dev->hw_features; } diff --git a/drivers/net/bonding/bond_options.c b/drivers/net/bonding/bond_options.c index 1a61cc9b3402..4df28943d222 100644 --- a/drivers/net/bonding/bond_options.c +++ b/drivers/net/bonding/bond_options.c @@ -186,7 +186,7 @@ static const struct bond_opt_value bond_tlb_dynamic_lb_tbl[] = { { NULL, -1, 0} }; -static const struct bond_option bond_opts[] = { +static const struct bond_option bond_opts[BOND_OPT_LAST] = { [BOND_OPT_MODE] = { .id = BOND_OPT_MODE, .name = "mode", @@ -379,8 +379,7 @@ static const struct bond_option bond_opts[] = { .values = bond_tlb_dynamic_lb_tbl, .flags = BOND_OPTFLAG_IFDOWN, .set = bond_option_tlb_dynamic_lb_set, - }, - { } + } }; /* Searches for an option by name */ @@ -1182,6 +1181,7 @@ static int bond_option_min_links_set(struct bonding *bond, netdev_info(bond->dev, "Setting min links value to %llu\n", newval->value); bond->params.min_links = newval->value; + bond_set_carrier(bond); return 0; } diff --git a/drivers/net/caif/caif_hsi.c b/drivers/net/caif/caif_hsi.c index 5e40a8b68cbe..b3b922adc0e4 100644 --- a/drivers/net/caif/caif_hsi.c +++ b/drivers/net/caif/caif_hsi.c @@ -1415,7 +1415,6 @@ static int caif_hsi_newlink(struct net *src_net, struct net_device *dev, cfhsi = netdev_priv(dev); cfhsi_netlink_parms(data, cfhsi); - dev_net_set(cfhsi->ndev, src_net); get_ops = symbol_get(cfhsi_get_ops); if (!get_ops) { diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c index d0c2463b573f..eeb4b8b6b335 100644 --- a/drivers/net/can/at91_can.c +++ b/drivers/net/can/at91_can.c @@ -138,7 +138,6 @@ struct at91_devtype_data { struct at91_priv { struct can_priv can; /* must be the first member! */ - struct net_device *dev; struct napi_struct napi; void __iomem *reg_base; @@ -1350,7 +1349,6 @@ static int at91_can_probe(struct platform_device *pdev) priv->can.do_get_berr_counter = at91_get_berr_counter; priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY; - priv->dev = dev; priv->reg_base = addr; priv->devtype_data = *devtype_data; priv->clk = clk; diff --git a/drivers/net/can/bfin_can.c b/drivers/net/can/bfin_can.c index 417d50998e31..e7a6363e736b 100644 --- a/drivers/net/can/bfin_can.c +++ b/drivers/net/can/bfin_can.c @@ -352,6 +352,7 @@ static int bfin_can_err(struct net_device *dev, u16 isrc, u16 status) netdev_dbg(dev, "bus-off mode interrupt\n"); state = CAN_STATE_BUS_OFF; cf->can_id |= CAN_ERR_BUSOFF; + priv->can.can_stats.bus_off++; can_bus_off(dev); } diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c index f94a9fa60488..041525d2595c 100644 --- a/drivers/net/can/c_can/c_can.c +++ b/drivers/net/can/c_can/c_can.c @@ -615,6 +615,9 @@ static void c_can_stop(struct net_device *dev) c_can_irq_control(priv, false); + /* put ctrl to init on stop to end ongoing transmission */ + priv->write_reg(priv, C_CAN_CTRL_REG, CONTROL_INIT); + /* deactivate pins */ pinctrl_pm_select_sleep_state(dev->dev.parent); priv->can.state = CAN_STATE_STOPPED; @@ -866,7 +869,7 @@ static int c_can_handle_state_change(struct net_device *dev, case C_CAN_BUS_OFF: /* bus-off state */ priv->can.state = CAN_STATE_BUS_OFF; - can_bus_off(dev); + priv->can.can_stats.bus_off++; break; default: break; diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c index f363972cd77d..e36d10520e24 100644 --- a/drivers/net/can/c_can/c_can_platform.c +++ b/drivers/net/can/c_can/c_can_platform.c @@ -103,27 +103,34 @@ static void c_can_hw_raminit_syscon(const struct c_can_priv *priv, bool enable) mask = 1 << raminit->bits.start | 1 << raminit->bits.done; regmap_read(raminit->syscon, raminit->reg, &ctrl); - /* We clear the done and start bit first. The start bit is + /* We clear the start bit first. The start bit is * looking at the 0 -> transition, but is not self clearing; - * And we clear the init done bit as well. * NOTE: DONE must be written with 1 to clear it. + * We can't clear the DONE bit here using regmap_update_bits() + * as it will bypass the write if initial condition is START:0 DONE:1 + * e.g. on DRA7 which needs START pulse. */ - ctrl &= ~(1 << raminit->bits.start); - ctrl |= 1 << raminit->bits.done; - regmap_write(raminit->syscon, raminit->reg, ctrl); + ctrl &= ~mask; /* START = 0, DONE = 0 */ + regmap_update_bits(raminit->syscon, raminit->reg, mask, ctrl); - ctrl &= ~(1 << raminit->bits.done); - c_can_hw_raminit_wait_syscon(priv, mask, ctrl); + /* check if START bit is 0. Ignore DONE bit for now + * as it can be either 0 or 1. + */ + c_can_hw_raminit_wait_syscon(priv, 1 << raminit->bits.start, ctrl); if (enable) { - /* Set start bit and wait for the done bit. */ + /* Clear DONE bit & set START bit. */ ctrl |= 1 << raminit->bits.start; - regmap_write(raminit->syscon, raminit->reg, ctrl); - + /* DONE must be written with 1 to clear it */ + ctrl |= 1 << raminit->bits.done; + regmap_update_bits(raminit->syscon, raminit->reg, mask, ctrl); + /* prevent further clearing of DONE bit */ + ctrl &= ~(1 << raminit->bits.done); /* clear START bit if start pulse is needed */ if (raminit->needs_pulse) { ctrl &= ~(1 << raminit->bits.start); - regmap_write(raminit->syscon, raminit->reg, ctrl); + regmap_update_bits(raminit->syscon, raminit->reg, + mask, ctrl); } ctrl |= 1 << raminit->bits.done; diff --git a/drivers/net/can/cc770/cc770.c b/drivers/net/can/cc770/cc770.c index c486fe510f37..c11d44984036 100644 --- a/drivers/net/can/cc770/cc770.c +++ b/drivers/net/can/cc770/cc770.c @@ -535,6 +535,7 @@ static int cc770_err(struct net_device *dev, u8 status) cc770_write_reg(priv, control, CTRL_INI); cf->can_id |= CAN_ERR_BUSOFF; priv->can.state = CAN_STATE_BUS_OFF; + priv->can.can_stats.bus_off++; can_bus_off(dev); } else if (status & STAT_WARN) { cf->can_id |= CAN_ERR_CRTL; diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index 3ec8f6f25e5f..3c82e02e3dae 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -289,9 +289,11 @@ static void can_update_state_error_stats(struct net_device *dev, priv->can_stats.error_passive++; break; case CAN_STATE_BUS_OFF: + priv->can_stats.bus_off++; + break; default: break; - }; + } } static int can_tx_state_to_frame(struct net_device *dev, enum can_state state) @@ -544,7 +546,6 @@ void can_bus_off(struct net_device *dev) netdev_dbg(dev, "bus-off\n"); netif_carrier_off(dev); - priv->can_stats.bus_off++; if (priv->restart_ms) mod_timer(&priv->restart_timer, @@ -807,10 +808,14 @@ static int can_changelink(struct net_device *dev, if (dev->flags & IFF_UP) return -EBUSY; cm = nla_data(data[IFLA_CAN_CTRLMODE]); - if (cm->flags & ~priv->ctrlmode_supported) + + /* check whether changed bits are allowed to be modified */ + if (cm->mask & ~priv->ctrlmode_supported) return -EOPNOTSUPP; + + /* clear bits to be modified and copy the flag values */ priv->ctrlmode &= ~cm->mask; - priv->ctrlmode |= cm->flags; + priv->ctrlmode |= (cm->flags & cm->mask); /* CAN_CTRLMODE_FD can only be set when driver supports FD */ if (priv->ctrlmode & CAN_CTRLMODE_FD) diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index b1d583ba9674..80c46ad4cee4 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -247,7 +247,6 @@ struct flexcan_devtype_data { struct flexcan_priv { struct can_priv can; - struct net_device *dev; struct napi_struct napi; void __iomem *base; @@ -1220,7 +1219,6 @@ static int flexcan_probe(struct platform_device *pdev) CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_BERR_REPORTING; priv->base = base; - priv->dev = dev; priv->clk_ipg = clk_ipg; priv->clk_per = clk_per; priv->pdata = dev_get_platdata(&pdev->dev); diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c index 1b118394907f..4dd183a3643a 100644 --- a/drivers/net/can/janz-ican3.c +++ b/drivers/net/can/janz-ican3.c @@ -1008,6 +1008,7 @@ static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg) if (status & SR_BS) { state = CAN_STATE_BUS_OFF; cf->can_id |= CAN_ERR_BUSOFF; + mod->can.can_stats.bus_off++; can_bus_off(dev); } else if (status & SR_ES) { if (rxerr >= 128 || txerr >= 128) @@ -1678,8 +1679,7 @@ static int ican3_get_berr_counter(const struct net_device *ndev, if (ret) return ret; - ret = wait_for_completion_timeout(&mod->buserror_comp, HZ); - if (ret == 0) { + if (!wait_for_completion_timeout(&mod->buserror_comp, HZ)) { netdev_info(mod->ndev, "%s timed out\n", __func__); return -ETIMEDOUT; } @@ -1704,8 +1704,7 @@ static ssize_t ican3_sysfs_show_term(struct device *dev, if (ret) return ret; - ret = wait_for_completion_timeout(&mod->termination_comp, HZ); - if (ret == 0) { + if (!wait_for_completion_timeout(&mod->termination_comp, HZ)) { netdev_info(mod->ndev, "%s timed out\n", __func__); return -ETIMEDOUT; } diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c index d7bc462aafdc..2e04b3aeeb37 100644 --- a/drivers/net/can/m_can/m_can.c +++ b/drivers/net/can/m_can/m_can.c @@ -589,6 +589,7 @@ static int m_can_handle_state_change(struct net_device *dev, /* bus-off state */ priv->can.state = CAN_STATE_BUS_OFF; m_can_disable_all_interrupts(priv); + priv->can.can_stats.bus_off++; can_bus_off(dev); break; default: @@ -955,6 +956,11 @@ static struct net_device *alloc_m_can_dev(void) priv->can.data_bittiming_const = &m_can_data_bittiming_const; priv->can.do_set_mode = m_can_set_mode; priv->can.do_get_berr_counter = m_can_get_berr_counter; + + /* CAN_CTRLMODE_FD_NON_ISO is fixed with M_CAN IP v3.0.1 */ + priv->can.ctrlmode = CAN_CTRLMODE_FD_NON_ISO; + + /* CAN_CTRLMODE_FD_NON_ISO can not be changed with M_CAN IP v3.0.1 */ priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_BERR_REPORTING | diff --git a/drivers/net/can/pch_can.c b/drivers/net/can/pch_can.c index a67eb01f3028..e187ca783da0 100644 --- a/drivers/net/can/pch_can.c +++ b/drivers/net/can/pch_can.c @@ -505,6 +505,7 @@ static void pch_can_error(struct net_device *ndev, u32 status) pch_can_set_rx_all(priv, 0); state = CAN_STATE_BUS_OFF; cf->can_id |= CAN_ERR_BUSOFF; + priv->can.can_stats.bus_off++; can_bus_off(ndev); } diff --git a/drivers/net/can/rcar_can.c b/drivers/net/can/rcar_can.c index 91cd48ca0efc..7deb80dcbe8c 100644 --- a/drivers/net/can/rcar_can.c +++ b/drivers/net/can/rcar_can.c @@ -331,6 +331,7 @@ static void rcar_can_error(struct net_device *ndev) priv->can.state = CAN_STATE_BUS_OFF; /* Clear interrupt condition */ writeb(~RCAR_CAN_EIFR_BOEIF, &priv->regs->eifr); + priv->can.can_stats.bus_off++; can_bus_off(ndev); if (skb) cf->can_id |= CAN_ERR_BUSOFF; diff --git a/drivers/net/can/softing/softing_main.c b/drivers/net/can/softing/softing_main.c index 2bf98d862302..7621f91a8a20 100644 --- a/drivers/net/can/softing/softing_main.c +++ b/drivers/net/can/softing/softing_main.c @@ -261,6 +261,7 @@ static int softing_handle_1(struct softing *card) ++priv->can.can_stats.error_passive; else if (can_state == CAN_STATE_BUS_OFF) { /* this calls can_close_cleanup() */ + ++priv->can.can_stats.bus_off; can_bus_off(netdev); netif_stop_queue(netdev); } diff --git a/drivers/net/can/spi/mcp251x.c b/drivers/net/can/spi/mcp251x.c index c66d699640a9..bf63fee4e743 100644 --- a/drivers/net/can/spi/mcp251x.c +++ b/drivers/net/can/spi/mcp251x.c @@ -905,6 +905,7 @@ static irqreturn_t mcp251x_can_ist(int irq, void *dev_id) if (priv->can.state == CAN_STATE_BUS_OFF) { if (priv->can.restart_ms == 0) { priv->force_quit = 1; + priv->can.can_stats.bus_off++; can_bus_off(net); mcp251x_hw_sleep(spi); break; diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index 9a07eafe554b..e95a9e1a889f 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -715,6 +715,7 @@ static int ti_hecc_error(struct net_device *ndev, int int_status, hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_CCR); /* Disable all interrupts in bus-off to avoid int hog */ hecc_write(priv, HECC_CANGIM, 0); + ++priv->can.can_stats.bus_off; can_bus_off(ndev); } diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig index a77db919363c..bcb272f6c68a 100644 --- a/drivers/net/can/usb/Kconfig +++ b/drivers/net/can/usb/Kconfig @@ -25,7 +25,7 @@ config CAN_KVASER_USB tristate "Kvaser CAN/USB interface" ---help--- This driver adds support for Kvaser CAN/USB devices like Kvaser - Leaf Light. + Leaf Light and Kvaser USBcan II. The driver provides support for the following devices: - Kvaser Leaf Light @@ -46,6 +46,12 @@ config CAN_KVASER_USB - Kvaser USBcan R - Kvaser Leaf Light v2 - Kvaser Mini PCI Express HS + - Kvaser USBcan II HS/HS + - Kvaser USBcan II HS/LS + - Kvaser USBcan Rugged ("USBcan Rev B") + - Kvaser Memorator HS/HS + - Kvaser Memorator HS/LS + - Scania VCI2 (if you have the Kvaser logo on top) If unsure, say N. @@ -53,10 +59,18 @@ config CAN_KVASER_USB module will be called kvaser_usb. config CAN_PEAK_USB - tristate "PEAK PCAN-USB/USB Pro interfaces" + tristate "PEAK PCAN-USB/USB Pro interfaces for CAN 2.0b/CAN-FD" ---help--- - This driver supports the PCAN-USB and PCAN-USB Pro adapters - from PEAK-System Technik (http://www.peak-system.com). + This driver supports the PEAK-System Technik USB adapters that enable + access to the CAN bus, with repect to the CAN 2.0b and/or CAN-FD + standards, that is: + + PCAN-USB single CAN 2.0b channel USB adapter + PCAN-USB Pro dual CAN 2.0b channels USB adapter + PCAN-USB FD single CAN-FD channel USB adapter + PCAN-USB Pro FD dual CAN-FD channels USB adapter + + (see also http://www.peak-system.com). config CAN_8DEV_USB tristate "8 devices USB2CAN interface" diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c index 29d3f0938eb8..9376f5e5b94e 100644 --- a/drivers/net/can/usb/ems_usb.c +++ b/drivers/net/can/usb/ems_usb.c @@ -347,6 +347,7 @@ static void ems_usb_rx_err(struct ems_usb *dev, struct ems_cpc_msg *msg) dev->can.state = CAN_STATE_BUS_OFF; cf->can_id |= CAN_ERR_BUSOFF; + dev->can.can_stats.bus_off++; can_bus_off(dev->netdev); } else if (state & SJA1000_SR_ES) { dev->can.state = CAN_STATE_ERROR_WARNING; diff --git a/drivers/net/can/usb/esd_usb2.c b/drivers/net/can/usb/esd_usb2.c index c063a54ab8dd..bacca0bd89c1 100644 --- a/drivers/net/can/usb/esd_usb2.c +++ b/drivers/net/can/usb/esd_usb2.c @@ -250,6 +250,7 @@ static void esd_usb2_rx_event(struct esd_usb2_net_priv *priv, case ESD_BUSSTATE_BUSOFF: priv->can.state = CAN_STATE_BUS_OFF; cf->can_id |= CAN_ERR_BUSOFF; + priv->can.can_stats.bus_off++; can_bus_off(priv->netdev); break; case ESD_BUSSTATE_WARN: diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c index 541fb7a05625..2928f7003041 100644 --- a/drivers/net/can/usb/kvaser_usb.c +++ b/drivers/net/can/usb/kvaser_usb.c @@ -6,10 +6,12 @@ * Parts of this driver are based on the following: * - Kvaser linux leaf driver (version 4.78) * - CAN driver for esd CAN-USB/2 + * - Kvaser linux usbcanII driver (version 5.3) * * Copyright (C) 2002-2006 KVASER AB, Sweden. All rights reserved. * Copyright (C) 2010 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh * Copyright (C) 2012 Olivier Sobrie <olivier@sobrie.be> + * Copyright (C) 2015 Valeo S.A. */ #include <linux/completion.h> @@ -30,8 +32,9 @@ #define RX_BUFFER_SIZE 3072 #define CAN_USB_CLOCK 8000000 #define MAX_NET_DEVICES 3 +#define MAX_USBCAN_NET_DEVICES 2 -/* Kvaser USB devices */ +/* Kvaser Leaf USB devices */ #define KVASER_VENDOR_ID 0x0bfd #define USB_LEAF_DEVEL_PRODUCT_ID 10 #define USB_LEAF_LITE_PRODUCT_ID 11 @@ -56,6 +59,24 @@ #define USB_LEAF_LITE_V2_PRODUCT_ID 288 #define USB_MINI_PCIE_HS_PRODUCT_ID 289 +static inline bool kvaser_is_leaf(const struct usb_device_id *id) +{ + return id->idProduct >= USB_LEAF_DEVEL_PRODUCT_ID && + id->idProduct <= USB_MINI_PCIE_HS_PRODUCT_ID; +} + +/* Kvaser USBCan-II devices */ +#define USB_USBCAN_REVB_PRODUCT_ID 2 +#define USB_VCI2_PRODUCT_ID 3 +#define USB_USBCAN2_PRODUCT_ID 4 +#define USB_MEMORATOR_PRODUCT_ID 5 + +static inline bool kvaser_is_usbcan(const struct usb_device_id *id) +{ + return id->idProduct >= USB_USBCAN_REVB_PRODUCT_ID && + id->idProduct <= USB_MEMORATOR_PRODUCT_ID; +} + /* USB devices features */ #define KVASER_HAS_SILENT_MODE BIT(0) #define KVASER_HAS_TXRX_ERRORS BIT(1) @@ -73,7 +94,7 @@ #define MSG_FLAG_TX_ACK BIT(6) #define MSG_FLAG_TX_REQUEST BIT(7) -/* Can states */ +/* Can states (M16C CxSTRH register) */ #define M16C_STATE_BUS_RESET BIT(0) #define M16C_STATE_BUS_ERROR BIT(4) #define M16C_STATE_BUS_PASSIVE BIT(5) @@ -98,7 +119,11 @@ #define CMD_START_CHIP_REPLY 27 #define CMD_STOP_CHIP 28 #define CMD_STOP_CHIP_REPLY 29 -#define CMD_GET_CARD_INFO2 32 + +#define CMD_LEAF_GET_CARD_INFO2 32 +#define CMD_USBCAN_RESET_CLOCK 32 +#define CMD_USBCAN_CLOCK_OVERFLOW_EVENT 33 + #define CMD_GET_CARD_INFO 34 #define CMD_GET_CARD_INFO_REPLY 35 #define CMD_GET_SOFTWARE_INFO 38 @@ -108,8 +133,9 @@ #define CMD_RESET_ERROR_COUNTER 49 #define CMD_TX_ACKNOWLEDGE 50 #define CMD_CAN_ERROR_EVENT 51 -#define CMD_USB_THROTTLE 77 -#define CMD_LOG_MESSAGE 106 + +#define CMD_LEAF_USB_THROTTLE 77 +#define CMD_LEAF_LOG_MESSAGE 106 /* error factors */ #define M16C_EF_ACKE BIT(0) @@ -121,6 +147,14 @@ #define M16C_EF_RCVE BIT(6) #define M16C_EF_TRE BIT(7) +/* Only Leaf-based devices can report M16C error factors, + * thus define our own error status flags for USBCANII + */ +#define USBCAN_ERROR_STATE_NONE 0 +#define USBCAN_ERROR_STATE_TX_ERROR BIT(0) +#define USBCAN_ERROR_STATE_RX_ERROR BIT(1) +#define USBCAN_ERROR_STATE_BUSERROR BIT(2) + /* bittiming parameters */ #define KVASER_USB_TSEG1_MIN 1 #define KVASER_USB_TSEG1_MAX 16 @@ -137,9 +171,18 @@ #define KVASER_CTRL_MODE_SELFRECEPTION 3 #define KVASER_CTRL_MODE_OFF 4 -/* log message */ +/* Extended CAN identifier flag */ #define KVASER_EXTENDED_FRAME BIT(31) +/* Kvaser USB CAN dongles are divided into two major families: + * - Leaf: Based on Renesas M32C, running firmware labeled as 'filo' + * - UsbcanII: Based on Renesas M16C, running firmware labeled as 'helios' + */ +enum kvaser_usb_family { + KVASER_LEAF, + KVASER_USBCAN, +}; + struct kvaser_msg_simple { u8 tid; u8 channel; @@ -148,30 +191,55 @@ struct kvaser_msg_simple { struct kvaser_msg_cardinfo { u8 tid; u8 nchannels; - __le32 serial_number; - __le32 padding; + union { + struct { + __le32 serial_number; + __le32 padding; + } __packed leaf0; + struct { + __le32 serial_number_low; + __le32 serial_number_high; + } __packed usbcan0; + } __packed; __le32 clock_resolution; __le32 mfgdate; u8 ean[8]; u8 hw_revision; - u8 usb_hs_mode; - __le16 padding2; + union { + struct { + u8 usb_hs_mode; + } __packed leaf1; + struct { + u8 padding; + } __packed usbcan1; + } __packed; + __le16 padding; } __packed; struct kvaser_msg_cardinfo2 { u8 tid; - u8 channel; + u8 reserved; u8 pcb_id[24]; __le32 oem_unlock_code; } __packed; -struct kvaser_msg_softinfo { +struct leaf_msg_softinfo { u8 tid; - u8 channel; + u8 padding0; __le32 sw_options; __le32 fw_version; __le16 max_outstanding_tx; - __le16 padding[9]; + __le16 padding1[9]; +} __packed; + +struct usbcan_msg_softinfo { + u8 tid; + u8 fw_name[5]; + __le16 max_outstanding_tx; + u8 padding[6]; + __le32 fw_version; + __le16 checksum; + __le16 sw_options; } __packed; struct kvaser_msg_busparams { @@ -188,36 +256,86 @@ struct kvaser_msg_tx_can { u8 channel; u8 tid; u8 msg[14]; - u8 padding; - u8 flags; + union { + struct { + u8 padding; + u8 flags; + } __packed leaf; + struct { + u8 flags; + u8 padding; + } __packed usbcan; + } __packed; } __packed; -struct kvaser_msg_rx_can { +struct kvaser_msg_rx_can_header { u8 channel; u8 flag; +} __packed; + +struct leaf_msg_rx_can { + u8 channel; + u8 flag; + __le16 time[3]; u8 msg[14]; } __packed; -struct kvaser_msg_chip_state_event { +struct usbcan_msg_rx_can { + u8 channel; + u8 flag; + + u8 msg[14]; + __le16 time; +} __packed; + +struct leaf_msg_chip_state_event { u8 tid; u8 channel; + __le16 time[3]; u8 tx_errors_count; u8 rx_errors_count; + u8 status; u8 padding[3]; } __packed; -struct kvaser_msg_tx_acknowledge { +struct usbcan_msg_chip_state_event { + u8 tid; + u8 channel; + + u8 tx_errors_count; + u8 rx_errors_count; + __le16 time; + + u8 status; + u8 padding[3]; +} __packed; + +struct kvaser_msg_tx_acknowledge_header { u8 channel; u8 tid; +} __packed; + +struct leaf_msg_tx_acknowledge { + u8 channel; + u8 tid; + __le16 time[3]; u8 flags; u8 time_offset; } __packed; -struct kvaser_msg_error_event { +struct usbcan_msg_tx_acknowledge { + u8 channel; + u8 tid; + + __le16 time; + __le16 padding; +} __packed; + +struct leaf_msg_error_event { u8 tid; u8 flags; __le16 time[3]; @@ -229,6 +347,18 @@ struct kvaser_msg_error_event { u8 error_factor; } __packed; +struct usbcan_msg_error_event { + u8 tid; + u8 padding; + u8 tx_errors_count_ch0; + u8 rx_errors_count_ch0; + u8 tx_errors_count_ch1; + u8 rx_errors_count_ch1; + u8 status_ch0; + u8 status_ch1; + __le16 time; +} __packed; + struct kvaser_msg_ctrl_mode { u8 tid; u8 channel; @@ -243,7 +373,7 @@ struct kvaser_msg_flush_queue { u8 padding[3]; } __packed; -struct kvaser_msg_log_message { +struct leaf_msg_log_message { u8 channel; u8 flags; __le16 time[3]; @@ -260,19 +390,57 @@ struct kvaser_msg { struct kvaser_msg_simple simple; struct kvaser_msg_cardinfo cardinfo; struct kvaser_msg_cardinfo2 cardinfo2; - struct kvaser_msg_softinfo softinfo; struct kvaser_msg_busparams busparams; + + struct kvaser_msg_rx_can_header rx_can_header; + struct kvaser_msg_tx_acknowledge_header tx_acknowledge_header; + + union { + struct leaf_msg_softinfo softinfo; + struct leaf_msg_rx_can rx_can; + struct leaf_msg_chip_state_event chip_state_event; + struct leaf_msg_tx_acknowledge tx_acknowledge; + struct leaf_msg_error_event error_event; + struct leaf_msg_log_message log_message; + } __packed leaf; + + union { + struct usbcan_msg_softinfo softinfo; + struct usbcan_msg_rx_can rx_can; + struct usbcan_msg_chip_state_event chip_state_event; + struct usbcan_msg_tx_acknowledge tx_acknowledge; + struct usbcan_msg_error_event error_event; + } __packed usbcan; + struct kvaser_msg_tx_can tx_can; - struct kvaser_msg_rx_can rx_can; - struct kvaser_msg_chip_state_event chip_state_event; - struct kvaser_msg_tx_acknowledge tx_acknowledge; - struct kvaser_msg_error_event error_event; struct kvaser_msg_ctrl_mode ctrl_mode; struct kvaser_msg_flush_queue flush_queue; - struct kvaser_msg_log_message log_message; } u; } __packed; +/* Summary of a kvaser error event, for a unified Leaf/Usbcan error + * handling. Some discrepancies between the two families exist: + * + * - USBCAN firmware does not report M16C "error factors" + * - USBCAN controllers has difficulties reporting if the raised error + * event is for ch0 or ch1. They leave such arbitration to the OS + * driver by letting it compare error counters with previous values + * and decide the error event's channel. Thus for USBCAN, the channel + * field is only advisory. + */ +struct kvaser_usb_error_summary { + u8 channel, status, txerr, rxerr; + union { + struct { + u8 error_factor; + } leaf; + struct { + u8 other_ch_status; + u8 error_state; + } usbcan; + }; +}; + struct kvaser_usb_tx_urb_context { struct kvaser_usb_net_priv *priv; u32 echo_index; @@ -288,6 +456,7 @@ struct kvaser_usb { u32 fw_version; unsigned int nchannels; + enum kvaser_usb_family family; bool rxinitdone; void *rxbuf[MAX_RX_URBS]; @@ -311,6 +480,7 @@ struct kvaser_usb_net_priv { }; static const struct usb_device_id kvaser_usb_table[] = { + /* Leaf family IDs */ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) }, { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) }, { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID), @@ -360,6 +530,17 @@ static const struct usb_device_id kvaser_usb_table[] = { .driver_info = KVASER_HAS_TXRX_ERRORS }, { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_V2_PRODUCT_ID) }, { USB_DEVICE(KVASER_VENDOR_ID, USB_MINI_PCIE_HS_PRODUCT_ID) }, + + /* USBCANII family IDs */ + { USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN2_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_USBCAN_REVB_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMORATOR_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_VCI2_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS }, + { } }; MODULE_DEVICE_TABLE(usb, kvaser_usb_table); @@ -463,7 +644,14 @@ static int kvaser_usb_get_software_info(struct kvaser_usb *dev) if (err) return err; - dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version); + switch (dev->family) { + case KVASER_LEAF: + dev->fw_version = le32_to_cpu(msg.u.leaf.softinfo.fw_version); + break; + case KVASER_USBCAN: + dev->fw_version = le32_to_cpu(msg.u.usbcan.softinfo.fw_version); + break; + } return 0; } @@ -482,7 +670,9 @@ static int kvaser_usb_get_card_info(struct kvaser_usb *dev) return err; dev->nchannels = msg.u.cardinfo.nchannels; - if (dev->nchannels > MAX_NET_DEVICES) + if ((dev->nchannels > MAX_NET_DEVICES) || + (dev->family == KVASER_USBCAN && + dev->nchannels > MAX_USBCAN_NET_DEVICES)) return -EINVAL; return 0; @@ -496,8 +686,10 @@ static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev, struct kvaser_usb_net_priv *priv; struct sk_buff *skb; struct can_frame *cf; - u8 channel = msg->u.tx_acknowledge.channel; - u8 tid = msg->u.tx_acknowledge.tid; + u8 channel, tid; + + channel = msg->u.tx_acknowledge_header.channel; + tid = msg->u.tx_acknowledge_header.tid; if (channel >= dev->nchannels) { dev_err(dev->udev->dev.parent, @@ -520,10 +712,10 @@ static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev, skb = alloc_can_err_skb(priv->netdev, &cf); if (skb) { cf->can_id |= CAN_ERR_RESTARTED; - netif_rx(skb); stats->rx_packets++; stats->rx_bytes += cf->can_dlc; + netif_rx(skb); } else { netdev_err(priv->netdev, "No memory left for err_skb\n"); @@ -587,7 +779,7 @@ static int kvaser_usb_simple_msg_async(struct kvaser_usb_net_priv *priv, usb_sndbulkpipe(dev->udev, dev->bulk_out->bEndpointAddress), buf, msg->len, - kvaser_usb_simple_msg_callback, priv); + kvaser_usb_simple_msg_callback, netdev); usb_anchor_urb(urb, &priv->tx_submitted); err = usb_submit_urb(urb, GFP_ATOMIC); @@ -615,165 +807,280 @@ static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv) priv->tx_contexts[i].echo_index = MAX_TX_URBS; } -static void kvaser_usb_rx_error(const struct kvaser_usb *dev, - const struct kvaser_msg *msg) +static void kvaser_usb_rx_error_update_can_state(struct kvaser_usb_net_priv *priv, + const struct kvaser_usb_error_summary *es, + struct can_frame *cf) { - struct can_frame *cf; - struct sk_buff *skb; - struct net_device_stats *stats; - struct kvaser_usb_net_priv *priv; - unsigned int new_state; - u8 channel, status, txerr, rxerr, error_factor; + struct kvaser_usb *dev = priv->dev; + struct net_device_stats *stats = &priv->netdev->stats; + enum can_state cur_state, new_state, tx_state, rx_state; - switch (msg->id) { - case CMD_CAN_ERROR_EVENT: - channel = msg->u.error_event.channel; - status = msg->u.error_event.status; - txerr = msg->u.error_event.tx_errors_count; - rxerr = msg->u.error_event.rx_errors_count; - error_factor = msg->u.error_event.error_factor; - break; - case CMD_LOG_MESSAGE: - channel = msg->u.log_message.channel; - status = msg->u.log_message.data[0]; - txerr = msg->u.log_message.data[2]; - rxerr = msg->u.log_message.data[3]; - error_factor = msg->u.log_message.data[1]; + netdev_dbg(priv->netdev, "Error status: 0x%02x\n", es->status); + + new_state = cur_state = priv->can.state; + + if (es->status & (M16C_STATE_BUS_OFF | M16C_STATE_BUS_RESET)) + new_state = CAN_STATE_BUS_OFF; + else if (es->status & M16C_STATE_BUS_PASSIVE) + new_state = CAN_STATE_ERROR_PASSIVE; + else if (es->status & M16C_STATE_BUS_ERROR) { + /* Guard against spurious error events after a busoff */ + if (cur_state < CAN_STATE_BUS_OFF) { + if ((es->txerr >= 128) || (es->rxerr >= 128)) + new_state = CAN_STATE_ERROR_PASSIVE; + else if ((es->txerr >= 96) || (es->rxerr >= 96)) + new_state = CAN_STATE_ERROR_WARNING; + else if (cur_state > CAN_STATE_ERROR_ACTIVE) + new_state = CAN_STATE_ERROR_ACTIVE; + } + } + + if (!es->status) + new_state = CAN_STATE_ERROR_ACTIVE; + + if (new_state != cur_state) { + tx_state = (es->txerr >= es->rxerr) ? new_state : 0; + rx_state = (es->txerr <= es->rxerr) ? new_state : 0; + + can_change_state(priv->netdev, cf, tx_state, rx_state); + } + + if (priv->can.restart_ms && + (cur_state >= CAN_STATE_BUS_OFF) && + (new_state < CAN_STATE_BUS_OFF)) { + priv->can.can_stats.restarts++; + } + + switch (dev->family) { + case KVASER_LEAF: + if (es->leaf.error_factor) { + priv->can.can_stats.bus_error++; + stats->rx_errors++; + } break; - case CMD_CHIP_STATE_EVENT: - channel = msg->u.chip_state_event.channel; - status = msg->u.chip_state_event.status; - txerr = msg->u.chip_state_event.tx_errors_count; - rxerr = msg->u.chip_state_event.rx_errors_count; - error_factor = 0; + case KVASER_USBCAN: + if (es->usbcan.error_state & USBCAN_ERROR_STATE_TX_ERROR) + stats->tx_errors++; + if (es->usbcan.error_state & USBCAN_ERROR_STATE_RX_ERROR) + stats->rx_errors++; + if (es->usbcan.error_state & USBCAN_ERROR_STATE_BUSERROR) { + priv->can.can_stats.bus_error++; + } break; - default: - dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n", - msg->id); - return; } - if (channel >= dev->nchannels) { + priv->bec.txerr = es->txerr; + priv->bec.rxerr = es->rxerr; +} + +static void kvaser_usb_rx_error(const struct kvaser_usb *dev, + const struct kvaser_usb_error_summary *es) +{ + struct can_frame *cf, tmp_cf = { .can_id = CAN_ERR_FLAG, .can_dlc = CAN_ERR_DLC }; + struct sk_buff *skb; + struct net_device_stats *stats; + struct kvaser_usb_net_priv *priv; + enum can_state old_state, new_state; + + if (es->channel >= dev->nchannels) { dev_err(dev->udev->dev.parent, - "Invalid channel number (%d)\n", channel); + "Invalid channel number (%d)\n", es->channel); return; } - priv = dev->nets[channel]; + priv = dev->nets[es->channel]; stats = &priv->netdev->stats; - if (status & M16C_STATE_BUS_RESET) { - kvaser_usb_unlink_tx_urbs(priv); - return; - } + /* Update all of the can interface's state and error counters before + * trying any memory allocation that can actually fail with -ENOMEM. + * + * We send a temporary stack-allocated error can frame to + * can_change_state() for the very same reason. + * + * TODO: Split can_change_state() responsibility between updating the + * can interface's state and counters, and the setting up of can error + * frame ID and data to userspace. Remove stack allocation afterwards. + */ + old_state = priv->can.state; + kvaser_usb_rx_error_update_can_state(priv, es, &tmp_cf); + new_state = priv->can.state; skb = alloc_can_err_skb(priv->netdev, &cf); if (!skb) { stats->rx_dropped++; return; } - - new_state = priv->can.state; - - netdev_dbg(priv->netdev, "Error status: 0x%02x\n", status); - - if (status & M16C_STATE_BUS_OFF) { - cf->can_id |= CAN_ERR_BUSOFF; - - priv->can.can_stats.bus_off++; - if (!priv->can.restart_ms) - kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP); - - netif_carrier_off(priv->netdev); - - new_state = CAN_STATE_BUS_OFF; - } else if (status & M16C_STATE_BUS_PASSIVE) { - if (priv->can.state != CAN_STATE_ERROR_PASSIVE) { - cf->can_id |= CAN_ERR_CRTL; - - if (txerr || rxerr) - cf->data[1] = (txerr > rxerr) - ? CAN_ERR_CRTL_TX_PASSIVE - : CAN_ERR_CRTL_RX_PASSIVE; - else - cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE | - CAN_ERR_CRTL_RX_PASSIVE; - - priv->can.can_stats.error_passive++; + memcpy(cf, &tmp_cf, sizeof(*cf)); + + if (new_state != old_state) { + if (es->status & + (M16C_STATE_BUS_OFF | M16C_STATE_BUS_RESET)) { + if (!priv->can.restart_ms) + kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP); + netif_carrier_off(priv->netdev); } - new_state = CAN_STATE_ERROR_PASSIVE; + if (priv->can.restart_ms && + (old_state >= CAN_STATE_BUS_OFF) && + (new_state < CAN_STATE_BUS_OFF)) { + cf->can_id |= CAN_ERR_RESTARTED; + netif_carrier_on(priv->netdev); + } } - if (status == M16C_STATE_BUS_ERROR) { - if ((priv->can.state < CAN_STATE_ERROR_WARNING) && - ((txerr >= 96) || (rxerr >= 96))) { - cf->can_id |= CAN_ERR_CRTL; - cf->data[1] = (txerr > rxerr) - ? CAN_ERR_CRTL_TX_WARNING - : CAN_ERR_CRTL_RX_WARNING; - - priv->can.can_stats.error_warning++; - new_state = CAN_STATE_ERROR_WARNING; - } else if (priv->can.state > CAN_STATE_ERROR_ACTIVE) { - cf->can_id |= CAN_ERR_PROT; - cf->data[2] = CAN_ERR_PROT_ACTIVE; - - new_state = CAN_STATE_ERROR_ACTIVE; + switch (dev->family) { + case KVASER_LEAF: + if (es->leaf.error_factor) { + cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; + + if (es->leaf.error_factor & M16C_EF_ACKE) + cf->data[3] |= (CAN_ERR_PROT_LOC_ACK); + if (es->leaf.error_factor & M16C_EF_CRCE) + cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ | + CAN_ERR_PROT_LOC_CRC_DEL); + if (es->leaf.error_factor & M16C_EF_FORME) + cf->data[2] |= CAN_ERR_PROT_FORM; + if (es->leaf.error_factor & M16C_EF_STFE) + cf->data[2] |= CAN_ERR_PROT_STUFF; + if (es->leaf.error_factor & M16C_EF_BITE0) + cf->data[2] |= CAN_ERR_PROT_BIT0; + if (es->leaf.error_factor & M16C_EF_BITE1) + cf->data[2] |= CAN_ERR_PROT_BIT1; + if (es->leaf.error_factor & M16C_EF_TRE) + cf->data[2] |= CAN_ERR_PROT_TX; + } + break; + case KVASER_USBCAN: + if (es->usbcan.error_state & USBCAN_ERROR_STATE_BUSERROR) { + cf->can_id |= CAN_ERR_BUSERROR; } + break; } - if (!status) { - cf->can_id |= CAN_ERR_PROT; - cf->data[2] = CAN_ERR_PROT_ACTIVE; + cf->data[6] = es->txerr; + cf->data[7] = es->rxerr; - new_state = CAN_STATE_ERROR_ACTIVE; - } + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); +} - if (priv->can.restart_ms && - (priv->can.state >= CAN_STATE_BUS_OFF) && - (new_state < CAN_STATE_BUS_OFF)) { - cf->can_id |= CAN_ERR_RESTARTED; - netif_carrier_on(priv->netdev); +/* For USBCAN, report error to userspace iff the channels's errors counter + * has changed, or we're the only channel seeing a bus error state. + */ +static void kvaser_usbcan_conditionally_rx_error(const struct kvaser_usb *dev, + struct kvaser_usb_error_summary *es) +{ + struct kvaser_usb_net_priv *priv; + int channel; + bool report_error; - priv->can.can_stats.restarts++; + channel = es->channel; + if (channel >= dev->nchannels) { + dev_err(dev->udev->dev.parent, + "Invalid channel number (%d)\n", channel); + return; } - if (error_factor) { - priv->can.can_stats.bus_error++; - stats->rx_errors++; + priv = dev->nets[channel]; + report_error = false; - cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; - - if (error_factor & M16C_EF_ACKE) - cf->data[3] |= (CAN_ERR_PROT_LOC_ACK); - if (error_factor & M16C_EF_CRCE) - cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ | - CAN_ERR_PROT_LOC_CRC_DEL); - if (error_factor & M16C_EF_FORME) - cf->data[2] |= CAN_ERR_PROT_FORM; - if (error_factor & M16C_EF_STFE) - cf->data[2] |= CAN_ERR_PROT_STUFF; - if (error_factor & M16C_EF_BITE0) - cf->data[2] |= CAN_ERR_PROT_BIT0; - if (error_factor & M16C_EF_BITE1) - cf->data[2] |= CAN_ERR_PROT_BIT1; - if (error_factor & M16C_EF_TRE) - cf->data[2] |= CAN_ERR_PROT_TX; + if (es->txerr != priv->bec.txerr) { + es->usbcan.error_state |= USBCAN_ERROR_STATE_TX_ERROR; + report_error = true; + } + if (es->rxerr != priv->bec.rxerr) { + es->usbcan.error_state |= USBCAN_ERROR_STATE_RX_ERROR; + report_error = true; + } + if ((es->status & M16C_STATE_BUS_ERROR) && + !(es->usbcan.other_ch_status & M16C_STATE_BUS_ERROR)) { + es->usbcan.error_state |= USBCAN_ERROR_STATE_BUSERROR; + report_error = true; } - cf->data[6] = txerr; - cf->data[7] = rxerr; + if (report_error) + kvaser_usb_rx_error(dev, es); +} - priv->bec.txerr = txerr; - priv->bec.rxerr = rxerr; +static void kvaser_usbcan_rx_error(const struct kvaser_usb *dev, + const struct kvaser_msg *msg) +{ + struct kvaser_usb_error_summary es = { }; - priv->can.state = new_state; + switch (msg->id) { + /* Sometimes errors are sent as unsolicited chip state events */ + case CMD_CHIP_STATE_EVENT: + es.channel = msg->u.usbcan.chip_state_event.channel; + es.status = msg->u.usbcan.chip_state_event.status; + es.txerr = msg->u.usbcan.chip_state_event.tx_errors_count; + es.rxerr = msg->u.usbcan.chip_state_event.rx_errors_count; + kvaser_usbcan_conditionally_rx_error(dev, &es); + break; - netif_rx(skb); + case CMD_CAN_ERROR_EVENT: + es.channel = 0; + es.status = msg->u.usbcan.error_event.status_ch0; + es.txerr = msg->u.usbcan.error_event.tx_errors_count_ch0; + es.rxerr = msg->u.usbcan.error_event.rx_errors_count_ch0; + es.usbcan.other_ch_status = + msg->u.usbcan.error_event.status_ch1; + kvaser_usbcan_conditionally_rx_error(dev, &es); + + /* The USBCAN firmware supports up to 2 channels. + * Now that ch0 was checked, check if ch1 has any errors. + */ + if (dev->nchannels == MAX_USBCAN_NET_DEVICES) { + es.channel = 1; + es.status = msg->u.usbcan.error_event.status_ch1; + es.txerr = msg->u.usbcan.error_event.tx_errors_count_ch1; + es.rxerr = msg->u.usbcan.error_event.rx_errors_count_ch1; + es.usbcan.other_ch_status = + msg->u.usbcan.error_event.status_ch0; + kvaser_usbcan_conditionally_rx_error(dev, &es); + } + break; - stats->rx_packets++; - stats->rx_bytes += cf->can_dlc; + default: + dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n", + msg->id); + } +} + +static void kvaser_leaf_rx_error(const struct kvaser_usb *dev, + const struct kvaser_msg *msg) +{ + struct kvaser_usb_error_summary es = { }; + + switch (msg->id) { + case CMD_CAN_ERROR_EVENT: + es.channel = msg->u.leaf.error_event.channel; + es.status = msg->u.leaf.error_event.status; + es.txerr = msg->u.leaf.error_event.tx_errors_count; + es.rxerr = msg->u.leaf.error_event.rx_errors_count; + es.leaf.error_factor = msg->u.leaf.error_event.error_factor; + break; + case CMD_LEAF_LOG_MESSAGE: + es.channel = msg->u.leaf.log_message.channel; + es.status = msg->u.leaf.log_message.data[0]; + es.txerr = msg->u.leaf.log_message.data[2]; + es.rxerr = msg->u.leaf.log_message.data[3]; + es.leaf.error_factor = msg->u.leaf.log_message.data[1]; + break; + case CMD_CHIP_STATE_EVENT: + es.channel = msg->u.leaf.chip_state_event.channel; + es.status = msg->u.leaf.chip_state_event.status; + es.txerr = msg->u.leaf.chip_state_event.tx_errors_count; + es.rxerr = msg->u.leaf.chip_state_event.rx_errors_count; + es.leaf.error_factor = 0; + break; + default: + dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n", + msg->id); + return; + } + + kvaser_usb_rx_error(dev, &es); } static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv, @@ -783,16 +1090,19 @@ static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv, struct sk_buff *skb; struct net_device_stats *stats = &priv->netdev->stats; - if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME | + if (msg->u.rx_can_header.flag & (MSG_FLAG_ERROR_FRAME | MSG_FLAG_NERR)) { netdev_err(priv->netdev, "Unknow error (flags: 0x%02x)\n", - msg->u.rx_can.flag); + msg->u.rx_can_header.flag); stats->rx_errors++; return; } - if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) { + if (msg->u.rx_can_header.flag & MSG_FLAG_OVERRUN) { + stats->rx_over_errors++; + stats->rx_errors++; + skb = alloc_can_err_skb(priv->netdev, &cf); if (!skb) { stats->rx_dropped++; @@ -802,13 +1112,9 @@ static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv, cf->can_id |= CAN_ERR_CRTL; cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; - stats->rx_over_errors++; - stats->rx_errors++; - - netif_rx(skb); - stats->rx_packets++; stats->rx_bytes += cf->can_dlc; + netif_rx(skb); } } @@ -819,7 +1125,8 @@ static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev, struct can_frame *cf; struct sk_buff *skb; struct net_device_stats *stats; - u8 channel = msg->u.rx_can.channel; + u8 channel = msg->u.rx_can_header.channel; + const u8 *rx_msg = NULL; /* GCC */ if (channel >= dev->nchannels) { dev_err(dev->udev->dev.parent, @@ -830,67 +1137,74 @@ static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev, priv = dev->nets[channel]; stats = &priv->netdev->stats; - if ((msg->u.rx_can.flag & MSG_FLAG_ERROR_FRAME) && - (msg->id == CMD_LOG_MESSAGE)) { - kvaser_usb_rx_error(dev, msg); + if ((msg->u.rx_can_header.flag & MSG_FLAG_ERROR_FRAME) && + (dev->family == KVASER_LEAF && msg->id == CMD_LEAF_LOG_MESSAGE)) { + kvaser_leaf_rx_error(dev, msg); return; - } else if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME | - MSG_FLAG_NERR | - MSG_FLAG_OVERRUN)) { + } else if (msg->u.rx_can_header.flag & (MSG_FLAG_ERROR_FRAME | + MSG_FLAG_NERR | + MSG_FLAG_OVERRUN)) { kvaser_usb_rx_can_err(priv, msg); return; - } else if (msg->u.rx_can.flag & ~MSG_FLAG_REMOTE_FRAME) { + } else if (msg->u.rx_can_header.flag & ~MSG_FLAG_REMOTE_FRAME) { netdev_warn(priv->netdev, "Unhandled frame (flags: 0x%02x)", - msg->u.rx_can.flag); + msg->u.rx_can_header.flag); return; } + switch (dev->family) { + case KVASER_LEAF: + rx_msg = msg->u.leaf.rx_can.msg; + break; + case KVASER_USBCAN: + rx_msg = msg->u.usbcan.rx_can.msg; + break; + } + skb = alloc_can_skb(priv->netdev, &cf); if (!skb) { stats->tx_dropped++; return; } - if (msg->id == CMD_LOG_MESSAGE) { - cf->can_id = le32_to_cpu(msg->u.log_message.id); + if (dev->family == KVASER_LEAF && msg->id == CMD_LEAF_LOG_MESSAGE) { + cf->can_id = le32_to_cpu(msg->u.leaf.log_message.id); if (cf->can_id & KVASER_EXTENDED_FRAME) cf->can_id &= CAN_EFF_MASK | CAN_EFF_FLAG; else cf->can_id &= CAN_SFF_MASK; - cf->can_dlc = get_can_dlc(msg->u.log_message.dlc); + cf->can_dlc = get_can_dlc(msg->u.leaf.log_message.dlc); - if (msg->u.log_message.flags & MSG_FLAG_REMOTE_FRAME) + if (msg->u.leaf.log_message.flags & MSG_FLAG_REMOTE_FRAME) cf->can_id |= CAN_RTR_FLAG; else - memcpy(cf->data, &msg->u.log_message.data, + memcpy(cf->data, &msg->u.leaf.log_message.data, cf->can_dlc); } else { - cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) | - (msg->u.rx_can.msg[1] & 0x3f); + cf->can_id = ((rx_msg[0] & 0x1f) << 6) | (rx_msg[1] & 0x3f); if (msg->id == CMD_RX_EXT_MESSAGE) { cf->can_id <<= 18; - cf->can_id |= ((msg->u.rx_can.msg[2] & 0x0f) << 14) | - ((msg->u.rx_can.msg[3] & 0xff) << 6) | - (msg->u.rx_can.msg[4] & 0x3f); + cf->can_id |= ((rx_msg[2] & 0x0f) << 14) | + ((rx_msg[3] & 0xff) << 6) | + (rx_msg[4] & 0x3f); cf->can_id |= CAN_EFF_FLAG; } - cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]); + cf->can_dlc = get_can_dlc(rx_msg[5]); - if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME) + if (msg->u.rx_can_header.flag & MSG_FLAG_REMOTE_FRAME) cf->can_id |= CAN_RTR_FLAG; else - memcpy(cf->data, &msg->u.rx_can.msg[6], + memcpy(cf->data, &rx_msg[6], cf->can_dlc); } - netif_rx(skb); - stats->rx_packets++; stats->rx_bytes += cf->can_dlc; + netif_rx(skb); } static void kvaser_usb_start_chip_reply(const struct kvaser_usb *dev, @@ -947,21 +1261,35 @@ static void kvaser_usb_handle_message(const struct kvaser_usb *dev, case CMD_RX_STD_MESSAGE: case CMD_RX_EXT_MESSAGE: - case CMD_LOG_MESSAGE: + kvaser_usb_rx_can_msg(dev, msg); + break; + + case CMD_LEAF_LOG_MESSAGE: + if (dev->family != KVASER_LEAF) + goto warn; kvaser_usb_rx_can_msg(dev, msg); break; case CMD_CHIP_STATE_EVENT: case CMD_CAN_ERROR_EVENT: - kvaser_usb_rx_error(dev, msg); + if (dev->family == KVASER_LEAF) + kvaser_leaf_rx_error(dev, msg); + else + kvaser_usbcan_rx_error(dev, msg); break; case CMD_TX_ACKNOWLEDGE: kvaser_usb_tx_acknowledge(dev, msg); break; + /* Ignored messages */ + case CMD_USBCAN_CLOCK_OVERFLOW_EVENT: + if (dev->family != KVASER_USBCAN) + goto warn; + break; + default: - dev_warn(dev->udev->dev.parent, +warn: dev_warn(dev->udev->dev.parent, "Unhandled message (%d)\n", msg->id); break; } @@ -1181,7 +1509,7 @@ static void kvaser_usb_unlink_all_urbs(struct kvaser_usb *dev) dev->rxbuf[i], dev->rxbuf_dma[i]); - for (i = 0; i < MAX_NET_DEVICES; i++) { + for (i = 0; i < dev->nchannels; i++) { struct kvaser_usb_net_priv *priv = dev->nets[i]; if (priv) @@ -1246,6 +1574,9 @@ static int kvaser_usb_close(struct net_device *netdev) if (err) netdev_warn(netdev, "Cannot stop device, error %d\n", err); + /* reset tx contexts */ + kvaser_usb_unlink_tx_urbs(priv); + priv->can.state = CAN_STATE_STOPPED; close_candev(priv->netdev); @@ -1286,6 +1617,7 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, struct kvaser_msg *msg; int i, err; int ret = NETDEV_TX_OK; + u8 *msg_tx_can_flags = NULL; /* GCC */ if (can_dropped_invalid_skb(netdev, skb)) return NETDEV_TX_OK; @@ -1294,20 +1626,32 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, if (!urb) { netdev_err(netdev, "No memory left for URBs\n"); stats->tx_dropped++; - goto nourbmem; + dev_kfree_skb(skb); + return NETDEV_TX_OK; } buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC); if (!buf) { stats->tx_dropped++; + dev_kfree_skb(skb); goto nobufmem; } msg = buf; msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can); - msg->u.tx_can.flags = 0; msg->u.tx_can.channel = priv->channel; + switch (dev->family) { + case KVASER_LEAF: + msg_tx_can_flags = &msg->u.tx_can.leaf.flags; + break; + case KVASER_USBCAN: + msg_tx_can_flags = &msg->u.tx_can.usbcan.flags; + break; + } + + *msg_tx_can_flags = 0; + if (cf->can_id & CAN_EFF_FLAG) { msg->id = CMD_TX_EXT_MESSAGE; msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f; @@ -1325,7 +1669,7 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc); if (cf->can_id & CAN_RTR_FLAG) - msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME; + *msg_tx_can_flags |= MSG_FLAG_REMOTE_FRAME; for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) { if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) { @@ -1334,6 +1678,7 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, } } + /* This should never happen; it implies a flow control bug */ if (!context) { netdev_warn(netdev, "cannot find free context\n"); ret = NETDEV_TX_BUSY; @@ -1364,9 +1709,6 @@ static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, if (unlikely(err)) { can_free_echo_skb(netdev, context->echo_index); - skb = NULL; /* set to NULL to avoid double free in - * dev_kfree_skb(skb) */ - atomic_dec(&priv->active_tx_urbs); usb_unanchor_urb(urb); @@ -1388,8 +1730,6 @@ releasebuf: kfree(buf); nobufmem: usb_free_urb(urb); -nourbmem: - dev_kfree_skb(skb); return ret; } @@ -1502,6 +1842,10 @@ static int kvaser_usb_init_one(struct usb_interface *intf, struct kvaser_usb_net_priv *priv; int i, err; + err = kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, channel); + if (err) + return err; + netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS); if (!netdev) { dev_err(&intf->dev, "Cannot alloc candev\n"); @@ -1588,12 +1932,23 @@ static int kvaser_usb_probe(struct usb_interface *intf, { struct kvaser_usb *dev; int err = -ENOMEM; - int i; + int i, retry = 3; dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; + if (kvaser_is_leaf(id)) { + dev->family = KVASER_LEAF; + } else if (kvaser_is_usbcan(id)) { + dev->family = KVASER_USBCAN; + } else { + dev_err(&intf->dev, + "Product ID (%d) does not belong to any known Kvaser USB family", + id->idProduct); + return -ENODEV; + } + err = kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out); if (err) { dev_err(&intf->dev, "Cannot get usb endpoint(s)"); @@ -1606,10 +1961,15 @@ static int kvaser_usb_probe(struct usb_interface *intf, usb_set_intfdata(intf, dev); - for (i = 0; i < MAX_NET_DEVICES; i++) - kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, i); + /* On some x86 laptops, plugging a Kvaser device again after + * an unplug makes the firmware always ignore the very first + * command. For such a case, provide some room for retries + * instead of completely exiting the driver. + */ + do { + err = kvaser_usb_get_software_info(dev); + } while (--retry && err == -ETIMEDOUT); - err = kvaser_usb_get_software_info(dev); if (err) { dev_err(&intf->dev, "Cannot get software infos, error %d\n", err); diff --git a/drivers/net/can/usb/peak_usb/Makefile b/drivers/net/can/usb/peak_usb/Makefile index 1aefbc88d643..1839e9ca62e7 100644 --- a/drivers/net/can/usb/peak_usb/Makefile +++ b/drivers/net/can/usb/peak_usb/Makefile @@ -1,2 +1,2 @@ obj-$(CONFIG_CAN_PEAK_USB) += peak_usb.o -peak_usb-y = pcan_usb_core.o pcan_usb.o pcan_usb_pro.o +peak_usb-y = pcan_usb_core.o pcan_usb.o pcan_usb_pro.o pcan_usb_fd.o diff --git a/drivers/net/can/usb/peak_usb/pcan_ucan.h b/drivers/net/can/usb/peak_usb/pcan_ucan.h new file mode 100644 index 000000000000..1ba7c25002e1 --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_ucan.h @@ -0,0 +1,222 @@ +/* + * CAN driver for PEAK System micro-CAN based adapters + * + * Copyright (C) 2003-2011 PEAK System-Technik GmbH + * Copyright (C) 2011-2013 Stephane Grosjean <s.grosjean@peak-system.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; version 2 of the License. + * + * 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. + */ +#ifndef PUCAN_H +#define PUCAN_H + +/* uCAN commands opcodes list (low-order 10 bits) */ +#define PUCAN_CMD_NOP 0x000 +#define PUCAN_CMD_RESET_MODE 0x001 +#define PUCAN_CMD_NORMAL_MODE 0x002 +#define PUCAN_CMD_LISTEN_ONLY_MODE 0x003 +#define PUCAN_CMD_TIMING_SLOW 0x004 +#define PUCAN_CMD_TIMING_FAST 0x005 +#define PUCAN_CMD_FILTER_STD 0x008 +#define PUCAN_CMD_TX_ABORT 0x009 +#define PUCAN_CMD_WR_ERR_CNT 0x00a +#define PUCAN_CMD_RX_FRAME_ENABLE 0x00b +#define PUCAN_CMD_RX_FRAME_DISABLE 0x00c +#define PUCAN_CMD_END_OF_COLLECTION 0x3ff + +/* uCAN received messages list */ +#define PUCAN_MSG_CAN_RX 0x0001 +#define PUCAN_MSG_ERROR 0x0002 +#define PUCAN_MSG_STATUS 0x0003 +#define PUCAN_MSG_BUSLOAD 0x0004 +#define PUCAN_MSG_CAN_TX 0x1000 + +/* uCAN command common header */ +struct __packed pucan_command { + __le16 opcode_channel; + u16 args[3]; +}; + +/* uCAN TIMING_SLOW command fields */ +#define PUCAN_TSLOW_SJW_T(s, t) (((s) & 0xf) | ((!!(t)) << 7)) +#define PUCAN_TSLOW_TSEG2(t) ((t) & 0xf) +#define PUCAN_TSLOW_TSEG1(t) ((t) & 0x3f) +#define PUCAN_TSLOW_BRP(b) ((b) & 0x3ff) + +struct __packed pucan_timing_slow { + __le16 opcode_channel; + + u8 ewl; /* Error Warning limit */ + u8 sjw_t; /* Sync Jump Width + Triple sampling */ + u8 tseg2; /* Timing SEGment 2 */ + u8 tseg1; /* Timing SEGment 1 */ + + __le16 brp; /* BaudRate Prescaler */ +}; + +/* uCAN TIMING_FAST command fields */ +#define PUCAN_TFAST_SJW(s) ((s) & 0x3) +#define PUCAN_TFAST_TSEG2(t) ((t) & 0x7) +#define PUCAN_TFAST_TSEG1(t) ((t) & 0xf) +#define PUCAN_TFAST_BRP(b) ((b) & 0x3ff) + +struct __packed pucan_timing_fast { + __le16 opcode_channel; + + u8 unused; + u8 sjw; /* Sync Jump Width */ + u8 tseg2; /* Timing SEGment 2 */ + u8 tseg1; /* Timing SEGment 1 */ + + __le16 brp; /* BaudRate Prescaler */ +}; + +/* uCAN FILTER_STD command fields */ +#define PUCAN_FLTSTD_ROW_IDX_BITS 6 + +struct __packed pucan_filter_std { + __le16 opcode_channel; + + __le16 idx; + __le32 mask; /* CAN-ID bitmask in idx range */ +}; + +/* uCAN WR_ERR_CNT command fields */ +#define PUCAN_WRERRCNT_TE 0x4000 /* Tx error cntr write Enable */ +#define PUCAN_WRERRCNT_RE 0x8000 /* Rx error cntr write Enable */ + +struct __packed pucan_wr_err_cnt { + __le16 opcode_channel; + + __le16 sel_mask; + u8 tx_counter; /* Tx error counter new value */ + u8 rx_counter; /* Rx error counter new value */ + + u16 unused; +}; + +/* uCAN RX_FRAME_ENABLE command fields */ +#define PUCAN_FLTEXT_ERROR 0x0001 +#define PUCAN_FLTEXT_BUSLOAD 0x0002 + +struct __packed pucan_filter_ext { + __le16 opcode_channel; + + __le16 ext_mask; + u32 unused; +}; + +/* uCAN received messages global format */ +struct __packed pucan_msg { + __le16 size; + __le16 type; + __le32 ts_low; + __le32 ts_high; +}; + +/* uCAN flags for CAN/CANFD messages */ +#define PUCAN_MSG_SELF_RECEIVE 0x80 +#define PUCAN_MSG_ERROR_STATE_IND 0x40 /* error state indicator */ +#define PUCAN_MSG_BITRATE_SWITCH 0x20 /* bitrate switch */ +#define PUCAN_MSG_EXT_DATA_LEN 0x10 /* extended data length */ +#define PUCAN_MSG_SINGLE_SHOT 0x08 +#define PUCAN_MSG_LOOPED_BACK 0x04 +#define PUCAN_MSG_EXT_ID 0x02 +#define PUCAN_MSG_RTR 0x01 + +struct __packed pucan_rx_msg { + __le16 size; + __le16 type; + __le32 ts_low; + __le32 ts_high; + __le32 tag_low; + __le32 tag_high; + u8 channel_dlc; + u8 client; + __le16 flags; + __le32 can_id; + u8 d[0]; +}; + +/* uCAN error types */ +#define PUCAN_ERMSG_BIT_ERROR 0 +#define PUCAN_ERMSG_FORM_ERROR 1 +#define PUCAN_ERMSG_STUFF_ERROR 2 +#define PUCAN_ERMSG_OTHER_ERROR 3 +#define PUCAN_ERMSG_ERR_CNT_DEC 4 + +struct __packed pucan_error_msg { + __le16 size; + __le16 type; + __le32 ts_low; + __le32 ts_high; + u8 channel_type_d; + u8 code_g; + u8 tx_err_cnt; + u8 rx_err_cnt; +}; + +#define PUCAN_BUS_PASSIVE 0x20 +#define PUCAN_BUS_WARNING 0x40 +#define PUCAN_BUS_BUSOFF 0x80 + +struct __packed pucan_status_msg { + __le16 size; + __le16 type; + __le32 ts_low; + __le32 ts_high; + u8 channel_p_w_b; + u8 unused[3]; +}; + +/* uCAN transmitted message format */ +#define PUCAN_MSG_CHANNEL_DLC(c, d) (((c) & 0xf) | ((d) << 4)) + +struct __packed pucan_tx_msg { + __le16 size; + __le16 type; + __le32 tag_low; + __le32 tag_high; + u8 channel_dlc; + u8 client; + __le16 flags; + __le32 can_id; + u8 d[0]; +}; + +/* build the cmd opcode_channel field with respect to the correct endianness */ +static inline __le16 pucan_cmd_opcode_channel(struct peak_usb_device *dev, + int opcode) +{ + return cpu_to_le16(((dev->ctrl_idx) << 12) | ((opcode) & 0x3ff)); +} + +/* return the channel number part from any received message channel_dlc field */ +static inline int pucan_msg_get_channel(struct pucan_rx_msg *rm) +{ + return rm->channel_dlc & 0xf; +} + +/* return the dlc value from any received message channel_dlc field */ +static inline int pucan_msg_get_dlc(struct pucan_rx_msg *rm) +{ + return rm->channel_dlc >> 4; +} + +static inline int pucan_ermsg_get_channel(struct pucan_error_msg *em) +{ + return em->channel_type_d & 0x0f; +} + +static inline int pucan_stmsg_get_channel(struct pucan_status_msg *sm) +{ + return sm->channel_p_w_b & 0x0f; +} + +#endif diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c index 4e1659d07979..72427f21edff 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb.c @@ -488,6 +488,7 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n, switch (new_state) { case CAN_STATE_BUS_OFF: cf->can_id |= CAN_ERR_BUSOFF; + mc->pdev->dev.can.can_stats.bus_off++; can_bus_off(mc->netdev); break; @@ -854,10 +855,11 @@ static int pcan_usb_probe(struct usb_interface *intf) /* * describe the PCAN-USB adapter */ -struct peak_usb_adapter pcan_usb = { +const struct peak_usb_adapter pcan_usb = { .name = "PCAN-USB", .device_id = PCAN_USB_PRODUCT_ID, .ctrl_count = 1, + .ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY, .clock = { .freq = PCAN_USB_CRYSTAL_HZ / 2 , }, diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c index c62f48a1161d..7921cff93a63 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c @@ -37,16 +37,19 @@ MODULE_LICENSE("GPL v2"); static struct usb_device_id peak_usb_table[] = { {USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USB_PRODUCT_ID)}, {USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBPRO_PRODUCT_ID)}, + {USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBFD_PRODUCT_ID)}, + {USB_DEVICE(PCAN_USB_VENDOR_ID, PCAN_USBPROFD_PRODUCT_ID)}, {} /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, peak_usb_table); /* List of supported PCAN-USB adapters (NULL terminated list) */ -static struct peak_usb_adapter *peak_usb_adapters_list[] = { +static const struct peak_usb_adapter *const peak_usb_adapters_list[] = { &pcan_usb, &pcan_usb_pro, - NULL, + &pcan_usb_fd, + &pcan_usb_pro_fd, }; /* @@ -65,7 +68,7 @@ void pcan_dump_mem(char *prompt, void *p, int l) * initialize a time_ref object with usb adapter own settings */ void peak_usb_init_time_ref(struct peak_time_ref *time_ref, - struct peak_usb_adapter *adapter) + const struct peak_usb_adapter *adapter) { if (time_ref) { memset(time_ref, 0, sizeof(struct peak_time_ref)); @@ -165,6 +168,21 @@ void peak_usb_get_ts_tv(struct peak_time_ref *time_ref, u32 ts, } /* + * post received skb after having set any hw timestamp + */ +int peak_usb_netif_rx(struct sk_buff *skb, + struct peak_time_ref *time_ref, u32 ts_low, u32 ts_high) +{ + struct skb_shared_hwtstamps *hwts = skb_hwtstamps(skb); + struct timeval tv; + + peak_usb_get_ts_tv(time_ref, ts_low, &tv); + hwts->hwtstamp = timeval_to_ktime(tv); + + return netif_rx(skb); +} + +/* * callback for bulk Rx urb */ static void peak_usb_read_bulk_callback(struct urb *urb) @@ -253,7 +271,7 @@ static void peak_usb_write_bulk_callback(struct urb *urb) case 0: /* transmission complete */ netdev->stats.tx_packets++; - netdev->stats.tx_bytes += context->dlc; + netdev->stats.tx_bytes += context->data_len; /* prevent tx timeout */ netdev->trans_start = jiffies; @@ -289,7 +307,7 @@ static netdev_tx_t peak_usb_ndo_start_xmit(struct sk_buff *skb, struct peak_usb_device *dev = netdev_priv(netdev); struct peak_tx_urb_context *context = NULL; struct net_device_stats *stats = &netdev->stats; - struct can_frame *cf = (struct can_frame *)skb->data; + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; struct urb *urb; u8 *obuf; int i, err; @@ -322,7 +340,9 @@ static netdev_tx_t peak_usb_ndo_start_xmit(struct sk_buff *skb, } context->echo_index = i; - context->dlc = cf->can_dlc; + + /* Note: this works with CANFD frames too */ + context->data_len = cfd->len; usb_anchor_urb(urb, &dev->tx_submitted); @@ -679,19 +699,43 @@ static int peak_usb_set_mode(struct net_device *netdev, enum can_mode mode) } /* - * candev callback used to set device bitrate. + * candev callback used to set device nominal/arbitration bitrate. */ static int peak_usb_set_bittiming(struct net_device *netdev) { struct peak_usb_device *dev = netdev_priv(netdev); - struct can_bittiming *bt = &dev->can.bittiming; + const struct peak_usb_adapter *pa = dev->adapter; - if (dev->adapter->dev_set_bittiming) { - int err = dev->adapter->dev_set_bittiming(dev, bt); + if (pa->dev_set_bittiming) { + struct can_bittiming *bt = &dev->can.bittiming; + int err = pa->dev_set_bittiming(dev, bt); if (err) netdev_info(netdev, "couldn't set bitrate (err %d)\n", - err); + err); + return err; + } + + return 0; +} + +/* + * candev callback used to set device data bitrate. + */ +static int peak_usb_set_data_bittiming(struct net_device *netdev) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + const struct peak_usb_adapter *pa = dev->adapter; + + if (pa->dev_set_data_bittiming) { + struct can_bittiming *bt = &dev->can.data_bittiming; + int err = pa->dev_set_data_bittiming(dev, bt); + + if (err) + netdev_info(netdev, + "couldn't set data bitrate (err %d)\n", + err); + return err; } @@ -709,7 +753,7 @@ static const struct net_device_ops peak_usb_netdev_ops = { * create one device which is attached to CAN controller #ctrl_idx of the * usb adapter. */ -static int peak_usb_create_dev(struct peak_usb_adapter *peak_usb_adapter, +static int peak_usb_create_dev(const struct peak_usb_adapter *peak_usb_adapter, struct usb_interface *intf, int ctrl_idx) { struct usb_device *usb_dev = interface_to_usbdev(intf); @@ -750,9 +794,11 @@ static int peak_usb_create_dev(struct peak_usb_adapter *peak_usb_adapter, dev->can.clock = peak_usb_adapter->clock; dev->can.bittiming_const = &peak_usb_adapter->bittiming_const; dev->can.do_set_bittiming = peak_usb_set_bittiming; + dev->can.data_bittiming_const = &peak_usb_adapter->data_bittiming_const; + dev->can.do_set_data_bittiming = peak_usb_set_data_bittiming; dev->can.do_set_mode = peak_usb_set_mode; - dev->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | - CAN_CTRLMODE_LISTENONLY; + dev->can.do_get_berr_counter = peak_usb_adapter->do_get_berr_counter; + dev->can.ctrlmode_supported = peak_usb_adapter->ctrlmode_supported; netdev->netdev_ops = &peak_usb_netdev_ops; @@ -857,17 +903,18 @@ static int peak_usb_probe(struct usb_interface *intf, { struct usb_device *usb_dev = interface_to_usbdev(intf); const u16 usb_id_product = le16_to_cpu(usb_dev->descriptor.idProduct); - struct peak_usb_adapter *peak_usb_adapter, **pp; + const struct peak_usb_adapter *peak_usb_adapter = NULL; int i, err = -ENOMEM; usb_dev = interface_to_usbdev(intf); /* get corresponding PCAN-USB adapter */ - for (pp = peak_usb_adapters_list; *pp; pp++) - if ((*pp)->device_id == usb_id_product) + for (i = 0; i < ARRAY_SIZE(peak_usb_adapters_list); i++) + if (peak_usb_adapters_list[i]->device_id == usb_id_product) { + peak_usb_adapter = peak_usb_adapters_list[i]; break; + } - peak_usb_adapter = *pp; if (!peak_usb_adapter) { /* should never come except device_id bad usage in this file */ pr_err("%s: didn't find device id. 0x%x in devices list\n", diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.h b/drivers/net/can/usb/peak_usb/pcan_usb_core.h index 073b47ff8eee..9e624f05ad4d 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.h +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h @@ -25,6 +25,8 @@ /* supported device ids. */ #define PCAN_USB_PRODUCT_ID 0x000c #define PCAN_USBPRO_PRODUCT_ID 0x000d +#define PCAN_USBPROFD_PRODUCT_ID 0x0011 +#define PCAN_USBFD_PRODUCT_ID 0x0012 #define PCAN_USB_DRIVER_NAME "peak_usb" @@ -44,8 +46,10 @@ struct peak_usb_device; struct peak_usb_adapter { char *name; u32 device_id; + u32 ctrlmode_supported; struct can_clock clock; const struct can_bittiming_const bittiming_const; + const struct can_bittiming_const data_bittiming_const; unsigned int ctrl_count; int (*intf_probe)(struct usb_interface *intf); @@ -57,6 +61,8 @@ struct peak_usb_adapter { int (*dev_close)(struct peak_usb_device *dev); int (*dev_set_bittiming)(struct peak_usb_device *dev, struct can_bittiming *bt); + int (*dev_set_data_bittiming)(struct peak_usb_device *dev, + struct can_bittiming *bt); int (*dev_set_bus)(struct peak_usb_device *dev, u8 onoff); int (*dev_get_device_id)(struct peak_usb_device *dev, u32 *device_id); int (*dev_decode_buf)(struct peak_usb_device *dev, struct urb *urb); @@ -66,6 +72,8 @@ struct peak_usb_adapter { int (*dev_stop)(struct peak_usb_device *dev); int (*dev_restart_async)(struct peak_usb_device *dev, struct urb *urb, u8 *buf); + int (*do_get_berr_counter)(const struct net_device *netdev, + struct can_berr_counter *bec); u8 ep_msg_in; u8 ep_msg_out[PCAN_USB_MAX_CHANNEL]; u8 ts_used_bits; @@ -78,21 +86,23 @@ struct peak_usb_adapter { int sizeof_dev_private; }; -extern struct peak_usb_adapter pcan_usb; -extern struct peak_usb_adapter pcan_usb_pro; +extern const struct peak_usb_adapter pcan_usb; +extern const struct peak_usb_adapter pcan_usb_pro; +extern const struct peak_usb_adapter pcan_usb_fd; +extern const struct peak_usb_adapter pcan_usb_pro_fd; struct peak_time_ref { struct timeval tv_host_0, tv_host; u32 ts_dev_1, ts_dev_2; u64 ts_total; u32 tick_count; - struct peak_usb_adapter *adapter; + const struct peak_usb_adapter *adapter; }; struct peak_tx_urb_context { struct peak_usb_device *dev; u32 echo_index; - u8 dlc; + u8 data_len; struct urb *urb; }; @@ -102,7 +112,7 @@ struct peak_tx_urb_context { /* PEAK-System USB device */ struct peak_usb_device { struct can_priv can; - struct peak_usb_adapter *adapter; + const struct peak_usb_adapter *adapter; unsigned int ctrl_idx; u32 state; @@ -134,12 +144,14 @@ void pcan_dump_mem(char *prompt, void *p, int l); /* common timestamp management */ void peak_usb_init_time_ref(struct peak_time_ref *time_ref, - struct peak_usb_adapter *adapter); + const struct peak_usb_adapter *adapter); void peak_usb_update_ts_now(struct peak_time_ref *time_ref, u32 ts_now); void peak_usb_set_ts_now(struct peak_time_ref *time_ref, u32 ts_now); void peak_usb_get_ts_tv(struct peak_time_ref *time_ref, u32 ts, struct timeval *tv); - +int peak_usb_netif_rx(struct sk_buff *skb, + struct peak_time_ref *time_ref, u32 ts_low, u32 ts_high); void peak_usb_async_complete(struct urb *urb); void peak_usb_restart_complete(struct peak_usb_device *dev); + #endif diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c new file mode 100644 index 000000000000..962c3f027383 --- /dev/null +++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c @@ -0,0 +1,1095 @@ +/* + * CAN driver for PEAK System PCAN-USB FD / PCAN-USB Pro FD adapter + * + * Copyright (C) 2013-2014 Stephane Grosjean <s.grosjean@peak-system.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; version 2 of the License. + * + * 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/netdevice.h> +#include <linux/usb.h> +#include <linux/module.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +#include "pcan_usb_core.h" +#include "pcan_usb_pro.h" +#include "pcan_ucan.h" + +MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB FD adapter"); +MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB Pro FD adapter"); + +#define PCAN_USBPROFD_CHANNEL_COUNT 2 +#define PCAN_USBFD_CHANNEL_COUNT 1 + +/* PCAN-USB Pro FD adapter internal clock (Hz) */ +#define PCAN_UFD_CRYSTAL_HZ 80000000 + +#define PCAN_UFD_CMD_BUFFER_SIZE 512 +#define PCAN_UFD_LOSPD_PKT_SIZE 64 + +/* PCAN-USB Pro FD command timeout (ms.) */ +#define PCAN_UFD_CMD_TIMEOUT_MS 1000 + +/* PCAN-USB Pro FD rx/tx buffers size */ +#define PCAN_UFD_RX_BUFFER_SIZE 2048 +#define PCAN_UFD_TX_BUFFER_SIZE 512 + +/* read some versions info from the hw devcie */ +struct __packed pcan_ufd_fw_info { + __le16 size_of; /* sizeof this */ + __le16 type; /* type of this structure */ + u8 hw_type; /* Type of hardware (HW_TYPE_xxx) */ + u8 bl_version[3]; /* Bootloader version */ + u8 hw_version; /* Hardware version (PCB) */ + u8 fw_version[3]; /* Firmware version */ + __le32 dev_id[2]; /* "device id" per CAN */ + __le32 ser_no; /* S/N */ + __le32 flags; /* special functions */ +}; + +/* handle device specific info used by the netdevices */ +struct pcan_usb_fd_if { + struct peak_usb_device *dev[PCAN_USB_MAX_CHANNEL]; + struct pcan_ufd_fw_info fw_info; + struct peak_time_ref time_ref; + int cm_ignore_count; + int dev_opened_count; +}; + +/* device information */ +struct pcan_usb_fd_device { + struct peak_usb_device dev; + struct can_berr_counter bec; + struct pcan_usb_fd_if *usb_if; + u8 *cmd_buffer_addr; +}; + +/* Extended USB commands (non uCAN commands) */ + +/* Clock Modes command */ +#define PCAN_UFD_CMD_CLK_SET 0x80 + +#define PCAN_UFD_CLK_80MHZ 0x0 +#define PCAN_UFD_CLK_60MHZ 0x1 +#define PCAN_UFD_CLK_40MHZ 0x2 +#define PCAN_UFD_CLK_30MHZ 0x3 +#define PCAN_UFD_CLK_24MHZ 0x4 +#define PCAN_UFD_CLK_20MHZ 0x5 +#define PCAN_UFD_CLK_DEF PCAN_UFD_CLK_80MHZ + +struct __packed pcan_ufd_clock { + __le16 opcode_channel; + + u8 mode; + u8 unused[5]; +}; + +/* LED control command */ +#define PCAN_UFD_CMD_LED_SET 0x86 + +#define PCAN_UFD_LED_DEV 0x00 +#define PCAN_UFD_LED_FAST 0x01 +#define PCAN_UFD_LED_SLOW 0x02 +#define PCAN_UFD_LED_ON 0x03 +#define PCAN_UFD_LED_OFF 0x04 +#define PCAN_UFD_LED_DEF PCAN_UFD_LED_DEV + +struct __packed pcan_ufd_led { + __le16 opcode_channel; + + u8 mode; + u8 unused[5]; +}; + +/* Extended usage of uCAN commands CMD_RX_FRAME_xxxABLE for PCAN-USB Pro FD */ +#define PCAN_UFD_FLTEXT_CALIBRATION 0x8000 + +struct __packed pcan_ufd_filter_ext { + __le16 opcode_channel; + + __le16 ext_mask; + u16 unused; + __le16 usb_mask; +}; + +/* Extended usage of uCAN messages for PCAN-USB Pro FD */ +#define PCAN_UFD_MSG_CALIBRATION 0x100 + +struct __packed pcan_ufd_ts_msg { + __le16 size; + __le16 type; + __le32 ts_low; + __le32 ts_high; + __le16 usb_frame_index; + u16 unused; +}; + +#define PCAN_UFD_MSG_OVERRUN 0x101 + +#define PCAN_UFD_OVMSG_CHANNEL(o) ((o)->channel & 0xf) + +struct __packed pcan_ufd_ovr_msg { + __le16 size; + __le16 type; + __le32 ts_low; + __le32 ts_high; + u8 channel; + u8 unused[3]; +}; + +static inline int pufd_omsg_get_channel(struct pcan_ufd_ovr_msg *om) +{ + return om->channel & 0xf; +} + +/* Clock mode frequency values */ +static const u32 pcan_usb_fd_clk_freq[6] = { + [PCAN_UFD_CLK_80MHZ] = 80000000, + [PCAN_UFD_CLK_60MHZ] = 60000000, + [PCAN_UFD_CLK_40MHZ] = 40000000, + [PCAN_UFD_CLK_30MHZ] = 30000000, + [PCAN_UFD_CLK_24MHZ] = 24000000, + [PCAN_UFD_CLK_20MHZ] = 20000000 +}; + +/* return a device USB interface */ +static inline +struct pcan_usb_fd_if *pcan_usb_fd_dev_if(struct peak_usb_device *dev) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + return pdev->usb_if; +} + +/* return a device USB commands buffer */ +static inline void *pcan_usb_fd_cmd_buffer(struct peak_usb_device *dev) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + return pdev->cmd_buffer_addr; +} + +/* send PCAN-USB Pro FD commands synchronously */ +static int pcan_usb_fd_send_cmd(struct peak_usb_device *dev, void *cmd_tail) +{ + void *cmd_head = pcan_usb_fd_cmd_buffer(dev); + int err; + u8 *packet_ptr; + int i, n = 1, packet_len; + ptrdiff_t cmd_len; + + /* usb device unregistered? */ + if (!(dev->state & PCAN_USB_STATE_CONNECTED)) + return 0; + + /* if a packet is not filled completely by commands, the command list + * is terminated with an "end of collection" record. + */ + cmd_len = cmd_tail - cmd_head; + if (cmd_len <= (PCAN_UFD_CMD_BUFFER_SIZE - sizeof(u64))) { + memset(cmd_tail, 0xff, sizeof(u64)); + cmd_len += sizeof(u64); + } + + packet_ptr = cmd_head; + + /* firmware is not able to re-assemble 512 bytes buffer in full-speed */ + if ((dev->udev->speed != USB_SPEED_HIGH) && + (cmd_len > PCAN_UFD_LOSPD_PKT_SIZE)) { + packet_len = PCAN_UFD_LOSPD_PKT_SIZE; + n += cmd_len / packet_len; + } else { + packet_len = cmd_len; + } + + for (i = 0; i < n; i++) { + err = usb_bulk_msg(dev->udev, + usb_sndbulkpipe(dev->udev, + PCAN_USBPRO_EP_CMDOUT), + packet_ptr, packet_len, + NULL, PCAN_UFD_CMD_TIMEOUT_MS); + if (err) { + netdev_err(dev->netdev, + "sending command failure: %d\n", err); + break; + } + + packet_ptr += packet_len; + } + + return err; +} + +/* build the commands list in the given buffer, to enter operational mode */ +static int pcan_usb_fd_build_restart_cmd(struct peak_usb_device *dev, u8 *buf) +{ + struct pucan_wr_err_cnt *prc; + struct pucan_command *cmd; + u8 *pc = buf; + + /* 1st, reset error counters: */ + prc = (struct pucan_wr_err_cnt *)pc; + prc->opcode_channel = pucan_cmd_opcode_channel(dev, + PUCAN_CMD_WR_ERR_CNT); + + /* select both counters */ + prc->sel_mask = cpu_to_le16(PUCAN_WRERRCNT_TE|PUCAN_WRERRCNT_RE); + + /* and reset their values */ + prc->tx_counter = 0; + prc->rx_counter = 0; + + /* moves the pointer forward */ + pc += sizeof(struct pucan_wr_err_cnt); + + /* next, go back to operational mode */ + cmd = (struct pucan_command *)pc; + cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + (dev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) ? + PUCAN_CMD_LISTEN_ONLY_MODE : + PUCAN_CMD_NORMAL_MODE); + pc += sizeof(struct pucan_command); + + return pc - buf; +} + +/* set CAN bus on/off */ +static int pcan_usb_fd_set_bus(struct peak_usb_device *dev, u8 onoff) +{ + u8 *pc = pcan_usb_fd_cmd_buffer(dev); + int l; + + if (onoff) { + /* build the cmds list to enter operational mode */ + l = pcan_usb_fd_build_restart_cmd(dev, pc); + } else { + struct pucan_command *cmd = (struct pucan_command *)pc; + + /* build cmd to go back to reset mode */ + cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + PUCAN_CMD_RESET_MODE); + l = sizeof(struct pucan_command); + } + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, pc + l); +} + +/* set filtering masks: + * + * idx in range [0..63] selects a row #idx, all rows otherwise + * mask in range [0..0xffffffff] defines up to 32 CANIDs in the row(s) + * + * Each bit of this 64 x 32 bits array defines a CANID value: + * + * bit[i,j] = 1 implies that CANID=(i x 32)+j will be received, while + * bit[i,j] = 0 implies that CANID=(i x 32)+j will be discarded. + */ +static int pcan_usb_fd_set_filter_std(struct peak_usb_device *dev, int idx, + u32 mask) +{ + struct pucan_filter_std *cmd = pcan_usb_fd_cmd_buffer(dev); + int i, n; + + /* select all rows when idx is out of range [0..63] */ + if ((idx < 0) || (idx >= (1 << PUCAN_FLTSTD_ROW_IDX_BITS))) { + n = 1 << PUCAN_FLTSTD_ROW_IDX_BITS; + idx = 0; + + /* select the row (and only the row) otherwise */ + } else { + n = idx + 1; + } + + for (i = idx; i < n; i++, cmd++) { + cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + PUCAN_CMD_FILTER_STD); + cmd->idx = cpu_to_le16(i); + cmd->mask = cpu_to_le32(mask); + } + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, cmd); +} + +/* set/unset notifications filter: + * + * onoff sets(1)/unset(0) notifications + * mask each bit defines a kind of notification to set/unset + */ +static int pcan_usb_fd_set_filter_ext(struct peak_usb_device *dev, + bool onoff, u16 ext_mask, u16 usb_mask) +{ + struct pcan_ufd_filter_ext *cmd = pcan_usb_fd_cmd_buffer(dev); + + cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + (onoff) ? PUCAN_CMD_RX_FRAME_ENABLE : + PUCAN_CMD_RX_FRAME_DISABLE); + + cmd->ext_mask = cpu_to_le16(ext_mask); + cmd->usb_mask = cpu_to_le16(usb_mask); + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, ++cmd); +} + +/* setup LED control */ +static int pcan_usb_fd_set_can_led(struct peak_usb_device *dev, u8 led_mode) +{ + struct pcan_ufd_led *cmd = pcan_usb_fd_cmd_buffer(dev); + + cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + PCAN_UFD_CMD_LED_SET); + cmd->mode = led_mode; + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, ++cmd); +} + +/* set CAN clock domain */ +static int pcan_usb_fd_set_clock_domain(struct peak_usb_device *dev, + u8 clk_mode) +{ + struct pcan_ufd_clock *cmd = pcan_usb_fd_cmd_buffer(dev); + + cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + PCAN_UFD_CMD_CLK_SET); + cmd->mode = clk_mode; + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, ++cmd); +} + +/* set bittiming for CAN and CAN-FD header */ +static int pcan_usb_fd_set_bittiming_slow(struct peak_usb_device *dev, + struct can_bittiming *bt) +{ + struct pucan_timing_slow *cmd = pcan_usb_fd_cmd_buffer(dev); + + cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + PUCAN_CMD_TIMING_SLOW); + cmd->sjw_t = PUCAN_TSLOW_SJW_T(bt->sjw - 1, + dev->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES); + + cmd->tseg2 = PUCAN_TSLOW_TSEG2(bt->phase_seg2 - 1); + cmd->tseg1 = PUCAN_TSLOW_TSEG1(bt->prop_seg + bt->phase_seg1 - 1); + cmd->brp = cpu_to_le16(PUCAN_TSLOW_BRP(bt->brp - 1)); + + cmd->ewl = 96; /* default */ + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, ++cmd); +} + +/* set CAN-FD bittiming for data */ +static int pcan_usb_fd_set_bittiming_fast(struct peak_usb_device *dev, + struct can_bittiming *bt) +{ + struct pucan_timing_fast *cmd = pcan_usb_fd_cmd_buffer(dev); + + cmd->opcode_channel = pucan_cmd_opcode_channel(dev, + PUCAN_CMD_TIMING_FAST); + cmd->sjw = PUCAN_TFAST_SJW(bt->sjw - 1); + cmd->tseg2 = PUCAN_TFAST_TSEG2(bt->phase_seg2 - 1); + cmd->tseg1 = PUCAN_TFAST_TSEG1(bt->prop_seg + bt->phase_seg1 - 1); + cmd->brp = cpu_to_le16(PUCAN_TFAST_BRP(bt->brp - 1)); + + /* send the command */ + return pcan_usb_fd_send_cmd(dev, ++cmd); +} + +/* handle restart but in asynchronously way + * (uses PCAN-USB Pro code to complete asynchronous request) + */ +static int pcan_usb_fd_restart_async(struct peak_usb_device *dev, + struct urb *urb, u8 *buf) +{ + u8 *pc = buf; + + /* build the entire cmds list in the provided buffer, to go back into + * operational mode. + */ + pc += pcan_usb_fd_build_restart_cmd(dev, pc); + + /* add EOC */ + memset(pc, 0xff, sizeof(struct pucan_command)); + pc += sizeof(struct pucan_command); + + /* complete the URB */ + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, PCAN_USBPRO_EP_CMDOUT), + buf, pc - buf, + pcan_usb_pro_restart_complete, dev); + + /* and submit it. */ + return usb_submit_urb(urb, GFP_ATOMIC); +} + +static int pcan_usb_fd_drv_loaded(struct peak_usb_device *dev, bool loaded) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + + pdev->cmd_buffer_addr[0] = 0; + pdev->cmd_buffer_addr[1] = !!loaded; + + return pcan_usb_pro_send_req(dev, + PCAN_USBPRO_REQ_FCT, + PCAN_USBPRO_FCT_DRVLD, + pdev->cmd_buffer_addr, + PCAN_USBPRO_FCT_DRVLD_REQ_LEN); +} + +static int pcan_usb_fd_decode_canmsg(struct pcan_usb_fd_if *usb_if, + struct pucan_msg *rx_msg) +{ + struct pucan_rx_msg *rm = (struct pucan_rx_msg *)rx_msg; + struct peak_usb_device *dev = usb_if->dev[pucan_msg_get_channel(rm)]; + struct net_device *netdev = dev->netdev; + struct canfd_frame *cfd; + struct sk_buff *skb; + const u16 rx_msg_flags = le16_to_cpu(rm->flags); + + if (rx_msg_flags & PUCAN_MSG_EXT_DATA_LEN) { + /* CANFD frame case */ + skb = alloc_canfd_skb(netdev, &cfd); + if (!skb) + return -ENOMEM; + + if (rx_msg_flags & PUCAN_MSG_BITRATE_SWITCH) + cfd->flags |= CANFD_BRS; + + if (rx_msg_flags & PUCAN_MSG_ERROR_STATE_IND) + cfd->flags |= CANFD_ESI; + + cfd->len = can_dlc2len(get_canfd_dlc(pucan_msg_get_dlc(rm))); + } else { + /* CAN 2.0 frame case */ + skb = alloc_can_skb(netdev, (struct can_frame **)&cfd); + if (!skb) + return -ENOMEM; + + cfd->len = get_can_dlc(pucan_msg_get_dlc(rm)); + } + + cfd->can_id = le32_to_cpu(rm->can_id); + + if (rx_msg_flags & PUCAN_MSG_EXT_ID) + cfd->can_id |= CAN_EFF_FLAG; + + if (rx_msg_flags & PUCAN_MSG_RTR) + cfd->can_id |= CAN_RTR_FLAG; + else + memcpy(cfd->data, rm->d, cfd->len); + + peak_usb_netif_rx(skb, &usb_if->time_ref, + le32_to_cpu(rm->ts_low), le32_to_cpu(rm->ts_high)); + + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += cfd->len; + + return 0; +} + +/* handle uCAN status message */ +static int pcan_usb_fd_decode_status(struct pcan_usb_fd_if *usb_if, + struct pucan_msg *rx_msg) +{ + struct pucan_status_msg *sm = (struct pucan_status_msg *)rx_msg; + struct peak_usb_device *dev = usb_if->dev[pucan_stmsg_get_channel(sm)]; + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + enum can_state new_state = CAN_STATE_ERROR_ACTIVE; + enum can_state rx_state, tx_state; + struct net_device *netdev = dev->netdev; + struct can_frame *cf; + struct sk_buff *skb; + + /* nothing should be sent while in BUS_OFF state */ + if (dev->can.state == CAN_STATE_BUS_OFF) + return 0; + + if (sm->channel_p_w_b & PUCAN_BUS_BUSOFF) { + new_state = CAN_STATE_BUS_OFF; + } else if (sm->channel_p_w_b & PUCAN_BUS_PASSIVE) { + new_state = CAN_STATE_ERROR_PASSIVE; + } else if (sm->channel_p_w_b & PUCAN_BUS_WARNING) { + new_state = CAN_STATE_ERROR_WARNING; + } else { + /* no error bit (so, no error skb, back to active state) */ + dev->can.state = CAN_STATE_ERROR_ACTIVE; + pdev->bec.txerr = 0; + pdev->bec.rxerr = 0; + return 0; + } + + /* state hasn't changed */ + if (new_state == dev->can.state) + return 0; + + /* handle bus state change */ + tx_state = (pdev->bec.txerr >= pdev->bec.rxerr) ? new_state : 0; + rx_state = (pdev->bec.txerr <= pdev->bec.rxerr) ? new_state : 0; + + /* allocate an skb to store the error frame */ + skb = alloc_can_err_skb(netdev, &cf); + if (skb) + can_change_state(netdev, cf, tx_state, rx_state); + + /* things must be done even in case of OOM */ + if (new_state == CAN_STATE_BUS_OFF) + can_bus_off(netdev); + + if (!skb) + return -ENOMEM; + + peak_usb_netif_rx(skb, &usb_if->time_ref, + le32_to_cpu(sm->ts_low), le32_to_cpu(sm->ts_high)); + + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += cf->can_dlc; + + return 0; +} + +/* handle uCAN error message */ +static int pcan_usb_fd_decode_error(struct pcan_usb_fd_if *usb_if, + struct pucan_msg *rx_msg) +{ + struct pucan_error_msg *er = (struct pucan_error_msg *)rx_msg; + struct peak_usb_device *dev = usb_if->dev[pucan_ermsg_get_channel(er)]; + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + + /* keep a trace of tx and rx error counters for later use */ + pdev->bec.txerr = er->tx_err_cnt; + pdev->bec.rxerr = er->rx_err_cnt; + + return 0; +} + +/* handle uCAN overrun message */ +static int pcan_usb_fd_decode_overrun(struct pcan_usb_fd_if *usb_if, + struct pucan_msg *rx_msg) +{ + struct pcan_ufd_ovr_msg *ov = (struct pcan_ufd_ovr_msg *)rx_msg; + struct peak_usb_device *dev = usb_if->dev[pufd_omsg_get_channel(ov)]; + struct net_device *netdev = dev->netdev; + struct can_frame *cf; + struct sk_buff *skb; + + /* allocate an skb to store the error frame */ + skb = alloc_can_err_skb(netdev, &cf); + if (!skb) + return -ENOMEM; + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + + peak_usb_netif_rx(skb, &usb_if->time_ref, + le32_to_cpu(ov->ts_low), le32_to_cpu(ov->ts_high)); + + netdev->stats.rx_over_errors++; + netdev->stats.rx_errors++; + + return 0; +} + +/* handle USB calibration message */ +static void pcan_usb_fd_decode_ts(struct pcan_usb_fd_if *usb_if, + struct pucan_msg *rx_msg) +{ + struct pcan_ufd_ts_msg *ts = (struct pcan_ufd_ts_msg *)rx_msg; + + /* should wait until clock is stabilized */ + if (usb_if->cm_ignore_count > 0) + usb_if->cm_ignore_count--; + else + peak_usb_set_ts_now(&usb_if->time_ref, le32_to_cpu(ts->ts_low)); +} + +/* callback for bulk IN urb */ +static int pcan_usb_fd_decode_buf(struct peak_usb_device *dev, struct urb *urb) +{ + struct pcan_usb_fd_if *usb_if = pcan_usb_fd_dev_if(dev); + struct net_device *netdev = dev->netdev; + struct pucan_msg *rx_msg; + u8 *msg_ptr, *msg_end; + int err = 0; + + /* loop reading all the records from the incoming message */ + msg_ptr = urb->transfer_buffer; + msg_end = urb->transfer_buffer + urb->actual_length; + for (; msg_ptr < msg_end;) { + u16 rx_msg_type, rx_msg_size; + + rx_msg = (struct pucan_msg *)msg_ptr; + if (!rx_msg->size) { + /* null packet found: end of list */ + break; + } + + rx_msg_size = le16_to_cpu(rx_msg->size); + rx_msg_type = le16_to_cpu(rx_msg->type); + + /* check if the record goes out of current packet */ + if (msg_ptr + rx_msg_size > msg_end) { + netdev_err(netdev, + "got frag rec: should inc usb rx buf sze\n"); + err = -EBADMSG; + break; + } + + switch (rx_msg_type) { + case PUCAN_MSG_CAN_RX: + err = pcan_usb_fd_decode_canmsg(usb_if, rx_msg); + if (err < 0) + goto fail; + break; + + case PCAN_UFD_MSG_CALIBRATION: + pcan_usb_fd_decode_ts(usb_if, rx_msg); + break; + + case PUCAN_MSG_ERROR: + err = pcan_usb_fd_decode_error(usb_if, rx_msg); + if (err < 0) + goto fail; + break; + + case PUCAN_MSG_STATUS: + err = pcan_usb_fd_decode_status(usb_if, rx_msg); + if (err < 0) + goto fail; + break; + + case PCAN_UFD_MSG_OVERRUN: + err = pcan_usb_fd_decode_overrun(usb_if, rx_msg); + if (err < 0) + goto fail; + break; + + default: + netdev_err(netdev, + "unhandled msg type 0x%02x (%d): ignored\n", + rx_msg_type, rx_msg_type); + break; + } + + msg_ptr += rx_msg_size; + } + +fail: + if (err) + pcan_dump_mem("received msg", + urb->transfer_buffer, urb->actual_length); + return err; +} + +/* CAN/CANFD frames encoding callback */ +static int pcan_usb_fd_encode_msg(struct peak_usb_device *dev, + struct sk_buff *skb, u8 *obuf, size_t *size) +{ + struct pucan_tx_msg *tx_msg = (struct pucan_tx_msg *)obuf; + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; + u16 tx_msg_size, tx_msg_flags; + u8 can_dlc; + + tx_msg_size = ALIGN(sizeof(struct pucan_tx_msg) + cfd->len, 4); + tx_msg->size = cpu_to_le16(tx_msg_size); + tx_msg->type = cpu_to_le16(PUCAN_MSG_CAN_TX); + + tx_msg_flags = 0; + if (cfd->can_id & CAN_EFF_FLAG) { + tx_msg_flags |= PUCAN_MSG_EXT_ID; + tx_msg->can_id = cpu_to_le32(cfd->can_id & CAN_EFF_MASK); + } else { + tx_msg->can_id = cpu_to_le32(cfd->can_id & CAN_SFF_MASK); + } + + if (can_is_canfd_skb(skb)) { + /* considering a CANFD frame */ + can_dlc = can_len2dlc(cfd->len); + + tx_msg_flags |= PUCAN_MSG_EXT_DATA_LEN; + + if (cfd->flags & CANFD_BRS) + tx_msg_flags |= PUCAN_MSG_BITRATE_SWITCH; + + if (cfd->flags & CANFD_ESI) + tx_msg_flags |= PUCAN_MSG_ERROR_STATE_IND; + } else { + /* CAND 2.0 frames */ + can_dlc = cfd->len; + + if (cfd->can_id & CAN_RTR_FLAG) + tx_msg_flags |= PUCAN_MSG_RTR; + } + + tx_msg->flags = cpu_to_le16(tx_msg_flags); + tx_msg->channel_dlc = PUCAN_MSG_CHANNEL_DLC(dev->ctrl_idx, can_dlc); + memcpy(tx_msg->d, cfd->data, cfd->len); + + /* add null size message to tag the end (messages are 32-bits aligned) + */ + tx_msg = (struct pucan_tx_msg *)(obuf + tx_msg_size); + + tx_msg->size = 0; + + /* set the whole size of the USB packet to send */ + *size = tx_msg_size + sizeof(u32); + + return 0; +} + +/* start the interface (last chance before set bus on) */ +static int pcan_usb_fd_start(struct peak_usb_device *dev) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + int err; + + /* set filter mode: all acceptance */ + err = pcan_usb_fd_set_filter_std(dev, -1, 0xffffffff); + if (err) + return err; + + /* opening first device: */ + if (pdev->usb_if->dev_opened_count == 0) { + /* reset time_ref */ + peak_usb_init_time_ref(&pdev->usb_if->time_ref, + &pcan_usb_pro_fd); + + /* enable USB calibration messages */ + err = pcan_usb_fd_set_filter_ext(dev, 1, + PUCAN_FLTEXT_ERROR, + PCAN_UFD_FLTEXT_CALIBRATION); + } + + pdev->usb_if->dev_opened_count++; + + /* reset cached error counters */ + pdev->bec.txerr = 0; + pdev->bec.rxerr = 0; + + return err; +} + +/* socket callback used to copy berr counters values receieved through USB */ +static int pcan_usb_fd_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + struct peak_usb_device *dev = netdev_priv(netdev); + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + + *bec = pdev->bec; + + /* must return 0 */ + return 0; +} + +/* stop interface (last chance before set bus off) */ +static int pcan_usb_fd_stop(struct peak_usb_device *dev) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + + /* turn off special msgs for that interface if no other dev opened */ + if (pdev->usb_if->dev_opened_count == 1) + pcan_usb_fd_set_filter_ext(dev, 0, + PUCAN_FLTEXT_ERROR, + PCAN_UFD_FLTEXT_CALIBRATION); + pdev->usb_if->dev_opened_count--; + + return 0; +} + +/* called when probing, to initialize a device object */ +static int pcan_usb_fd_init(struct peak_usb_device *dev) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + int i, err = -ENOMEM; + + /* do this for 1st channel only */ + if (!dev->prev_siblings) { + /* allocate netdevices common structure attached to first one */ + pdev->usb_if = kzalloc(sizeof(*pdev->usb_if), GFP_KERNEL); + if (!pdev->usb_if) + goto err_out; + + /* allocate command buffer once for all for the interface */ + pdev->cmd_buffer_addr = kmalloc(PCAN_UFD_CMD_BUFFER_SIZE, + GFP_KERNEL); + if (!pdev->cmd_buffer_addr) + goto err_out_1; + + /* number of ts msgs to ignore before taking one into account */ + pdev->usb_if->cm_ignore_count = 5; + + err = pcan_usb_pro_send_req(dev, PCAN_USBPRO_REQ_INFO, + PCAN_USBPRO_INFO_FW, + &pdev->usb_if->fw_info, + sizeof(pdev->usb_if->fw_info)); + if (err) { + dev_err(dev->netdev->dev.parent, + "unable to read %s firmware info (err %d)\n", + dev->adapter->name, err); + goto err_out_2; + } + + /* explicit use of dev_xxx() instead of netdev_xxx() here: + * information displayed are related to the device itself, not + * to the canx (channel) device. + */ + dev_info(dev->netdev->dev.parent, + "PEAK-System %s v%u fw v%u.%u.%u (%u channels)\n", + dev->adapter->name, pdev->usb_if->fw_info.hw_version, + pdev->usb_if->fw_info.fw_version[0], + pdev->usb_if->fw_info.fw_version[1], + pdev->usb_if->fw_info.fw_version[2], + dev->adapter->ctrl_count); + + /* the currently supported hw is non-ISO */ + dev->can.ctrlmode = CAN_CTRLMODE_FD_NON_ISO; + + /* tell the hardware the can driver is running */ + err = pcan_usb_fd_drv_loaded(dev, 1); + if (err) { + dev_err(dev->netdev->dev.parent, + "unable to tell %s driver is loaded (err %d)\n", + dev->adapter->name, err); + goto err_out_2; + } + } else { + /* otherwise, simply copy previous sibling's values */ + struct pcan_usb_fd_device *ppdev = + container_of(dev->prev_siblings, + struct pcan_usb_fd_device, dev); + + pdev->usb_if = ppdev->usb_if; + pdev->cmd_buffer_addr = ppdev->cmd_buffer_addr; + } + + pdev->usb_if->dev[dev->ctrl_idx] = dev; + dev->device_number = + le32_to_cpu(pdev->usb_if->fw_info.dev_id[dev->ctrl_idx]); + + /* set clock domain */ + for (i = 0; i < ARRAY_SIZE(pcan_usb_fd_clk_freq); i++) + if (dev->adapter->clock.freq == pcan_usb_fd_clk_freq[i]) + break; + + if (i >= ARRAY_SIZE(pcan_usb_fd_clk_freq)) { + dev_warn(dev->netdev->dev.parent, + "incompatible clock frequencies\n"); + err = -EINVAL; + goto err_out_2; + } + + pcan_usb_fd_set_clock_domain(dev, i); + + /* set LED in default state (end of init phase) */ + pcan_usb_fd_set_can_led(dev, PCAN_UFD_LED_DEF); + + return 0; + +err_out_2: + kfree(pdev->cmd_buffer_addr); +err_out_1: + kfree(pdev->usb_if); +err_out: + return err; +} + +/* called when driver module is being unloaded */ +static void pcan_usb_fd_exit(struct peak_usb_device *dev) +{ + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + + /* when rmmod called before unplug and if down, should reset things + * before leaving + */ + if (dev->can.state != CAN_STATE_STOPPED) { + /* set bus off on the corresponding channel */ + pcan_usb_fd_set_bus(dev, 0); + } + + /* switch off corresponding CAN LEDs */ + pcan_usb_fd_set_can_led(dev, PCAN_UFD_LED_OFF); + + /* if channel #0 (only) */ + if (dev->ctrl_idx == 0) { + /* turn off calibration message if any device were opened */ + if (pdev->usb_if->dev_opened_count > 0) + pcan_usb_fd_set_filter_ext(dev, 0, + PUCAN_FLTEXT_ERROR, + PCAN_UFD_FLTEXT_CALIBRATION); + + /* tell USB adapter that the driver is being unloaded */ + pcan_usb_fd_drv_loaded(dev, 0); + } +} + +/* called when the USB adapter is unplugged */ +static void pcan_usb_fd_free(struct peak_usb_device *dev) +{ + /* last device: can free shared objects now */ + if (!dev->prev_siblings && !dev->next_siblings) { + struct pcan_usb_fd_device *pdev = + container_of(dev, struct pcan_usb_fd_device, dev); + + /* free commands buffer */ + kfree(pdev->cmd_buffer_addr); + + /* free usb interface object */ + kfree(pdev->usb_if); + } +} + +/* describes the PCAN-USB FD adapter */ +const struct peak_usb_adapter pcan_usb_fd = { + .name = "PCAN-USB FD", + .device_id = PCAN_USBFD_PRODUCT_ID, + .ctrl_count = PCAN_USBFD_CHANNEL_COUNT, + .ctrlmode_supported = CAN_CTRLMODE_FD | + CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY, + .clock = { + .freq = PCAN_UFD_CRYSTAL_HZ, + }, + .bittiming_const = { + .name = "pcan_usb_fd", + .tseg1_min = 1, + .tseg1_max = 64, + .tseg2_min = 1, + .tseg2_max = 16, + .sjw_max = 16, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, + }, + .data_bittiming_const = { + .name = "pcan_usb_fd", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, + }, + + /* size of device private data */ + .sizeof_dev_private = sizeof(struct pcan_usb_fd_device), + + /* timestamps usage */ + .ts_used_bits = 32, + .ts_period = 1000000, /* calibration period in ts. */ + .us_per_ts_scale = 1, /* us = (ts * scale) >> shift */ + .us_per_ts_shift = 0, + + /* give here messages in/out endpoints */ + .ep_msg_in = PCAN_USBPRO_EP_MSGIN, + .ep_msg_out = {PCAN_USBPRO_EP_MSGOUT_0}, + + /* size of rx/tx usb buffers */ + .rx_buffer_size = PCAN_UFD_RX_BUFFER_SIZE, + .tx_buffer_size = PCAN_UFD_TX_BUFFER_SIZE, + + /* device callbacks */ + .intf_probe = pcan_usb_pro_probe, /* same as PCAN-USB Pro */ + .dev_init = pcan_usb_fd_init, + + .dev_exit = pcan_usb_fd_exit, + .dev_free = pcan_usb_fd_free, + .dev_set_bus = pcan_usb_fd_set_bus, + .dev_set_bittiming = pcan_usb_fd_set_bittiming_slow, + .dev_set_data_bittiming = pcan_usb_fd_set_bittiming_fast, + .dev_decode_buf = pcan_usb_fd_decode_buf, + .dev_start = pcan_usb_fd_start, + .dev_stop = pcan_usb_fd_stop, + .dev_restart_async = pcan_usb_fd_restart_async, + .dev_encode_msg = pcan_usb_fd_encode_msg, + + .do_get_berr_counter = pcan_usb_fd_get_berr_counter, +}; + +/* describes the PCAN-USB Pro FD adapter */ +const struct peak_usb_adapter pcan_usb_pro_fd = { + .name = "PCAN-USB Pro FD", + .device_id = PCAN_USBPROFD_PRODUCT_ID, + .ctrl_count = PCAN_USBPROFD_CHANNEL_COUNT, + .ctrlmode_supported = CAN_CTRLMODE_FD | + CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY, + .clock = { + .freq = PCAN_UFD_CRYSTAL_HZ, + }, + .bittiming_const = { + .name = "pcan_usb_pro_fd", + .tseg1_min = 1, + .tseg1_max = 64, + .tseg2_min = 1, + .tseg2_max = 16, + .sjw_max = 16, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, + }, + .data_bittiming_const = { + .name = "pcan_usb_pro_fd", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, + }, + + /* size of device private data */ + .sizeof_dev_private = sizeof(struct pcan_usb_fd_device), + + /* timestamps usage */ + .ts_used_bits = 32, + .ts_period = 1000000, /* calibration period in ts. */ + .us_per_ts_scale = 1, /* us = (ts * scale) >> shift */ + .us_per_ts_shift = 0, + + /* give here messages in/out endpoints */ + .ep_msg_in = PCAN_USBPRO_EP_MSGIN, + .ep_msg_out = {PCAN_USBPRO_EP_MSGOUT_0, PCAN_USBPRO_EP_MSGOUT_1}, + + /* size of rx/tx usb buffers */ + .rx_buffer_size = PCAN_UFD_RX_BUFFER_SIZE, + .tx_buffer_size = PCAN_UFD_TX_BUFFER_SIZE, + + /* device callbacks */ + .intf_probe = pcan_usb_pro_probe, /* same as PCAN-USB Pro */ + .dev_init = pcan_usb_fd_init, + + .dev_exit = pcan_usb_fd_exit, + .dev_free = pcan_usb_fd_free, + .dev_set_bus = pcan_usb_fd_set_bus, + .dev_set_bittiming = pcan_usb_fd_set_bittiming_slow, + .dev_set_data_bittiming = pcan_usb_fd_set_bittiming_fast, + .dev_decode_buf = pcan_usb_fd_decode_buf, + .dev_start = pcan_usb_fd_start, + .dev_stop = pcan_usb_fd_stop, + .dev_restart_async = pcan_usb_fd_restart_async, + .dev_encode_msg = pcan_usb_fd_encode_msg, + + .do_get_berr_counter = pcan_usb_fd_get_berr_counter, +}; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c index 4cfa3b8605b1..dec51717635e 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c @@ -27,14 +27,6 @@ MODULE_SUPPORTED_DEVICE("PEAK-System PCAN-USB Pro adapter"); -/* PCAN-USB Pro Endpoints */ -#define PCAN_USBPRO_EP_CMDOUT 1 -#define PCAN_USBPRO_EP_CMDIN (PCAN_USBPRO_EP_CMDOUT | USB_DIR_IN) -#define PCAN_USBPRO_EP_MSGOUT_0 2 -#define PCAN_USBPRO_EP_MSGIN (PCAN_USBPRO_EP_MSGOUT_0 | USB_DIR_IN) -#define PCAN_USBPRO_EP_MSGOUT_1 3 -#define PCAN_USBPRO_EP_UNUSED (PCAN_USBPRO_EP_MSGOUT_1 | USB_DIR_IN) - #define PCAN_USBPRO_CHANNEL_COUNT 2 /* PCAN-USB Pro adapter internal clock (MHz) */ @@ -322,8 +314,8 @@ static int pcan_usb_pro_wait_rsp(struct peak_usb_device *dev, return (i >= PCAN_USBPRO_RSP_SUBMIT_MAX) ? -ERANGE : err; } -static int pcan_usb_pro_send_req(struct peak_usb_device *dev, int req_id, - int req_value, void *req_addr, int req_size) +int pcan_usb_pro_send_req(struct peak_usb_device *dev, int req_id, + int req_value, void *req_addr, int req_size) { int err; u8 req_type; @@ -475,7 +467,7 @@ static int pcan_usb_pro_set_bittiming(struct peak_usb_device *dev, return pcan_usb_pro_set_bitrate(dev, ccbt); } -static void pcan_usb_pro_restart_complete(struct urb *urb) +void pcan_usb_pro_restart_complete(struct urb *urb) { /* can delete usb resources */ peak_usb_async_complete(urb); @@ -634,6 +626,7 @@ static int pcan_usb_pro_handle_error(struct pcan_usb_pro_interface *usb_if, switch (new_state) { case CAN_STATE_BUS_OFF: can_frame->can_id |= CAN_ERR_BUSOFF; + dev->can.can_stats.bus_off++; can_bus_off(netdev); break; @@ -977,7 +970,7 @@ static void pcan_usb_pro_free(struct peak_usb_device *dev) /* * probe function for new PCAN-USB Pro usb interface */ -static int pcan_usb_pro_probe(struct usb_interface *intf) +int pcan_usb_pro_probe(struct usb_interface *intf) { struct usb_host_interface *if_desc; int i; @@ -1011,10 +1004,11 @@ static int pcan_usb_pro_probe(struct usb_interface *intf) /* * describe the PCAN-USB Pro adapter */ -struct peak_usb_adapter pcan_usb_pro = { +const struct peak_usb_adapter pcan_usb_pro = { .name = "PCAN-USB Pro", .device_id = PCAN_USBPRO_PRODUCT_ID, .ctrl_count = PCAN_USBPRO_CHANNEL_COUNT, + .ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | CAN_CTRLMODE_LISTENONLY, .clock = { .freq = PCAN_USBPRO_CRYSTAL_HZ, }, diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.h b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h index 837cee267132..a62f7ab8980f 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.h +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.h @@ -27,6 +27,14 @@ #define PCAN_USBPRO_INFO_BL 0 #define PCAN_USBPRO_INFO_FW 1 +/* PCAN-USB Pro (FD) Endpoints */ +#define PCAN_USBPRO_EP_CMDOUT 1 +#define PCAN_USBPRO_EP_CMDIN (PCAN_USBPRO_EP_CMDOUT | USB_DIR_IN) +#define PCAN_USBPRO_EP_MSGOUT_0 2 +#define PCAN_USBPRO_EP_MSGIN (PCAN_USBPRO_EP_MSGOUT_0 | USB_DIR_IN) +#define PCAN_USBPRO_EP_MSGOUT_1 3 +#define PCAN_USBPRO_EP_UNUSED (PCAN_USBPRO_EP_MSGOUT_1 | USB_DIR_IN) + /* Vendor Request value for XXX_FCT */ #define PCAN_USBPRO_FCT_DRVLD 5 /* tell device driver is loaded */ #define PCAN_USBPRO_FCT_DRVLD_REQ_LEN 16 @@ -176,4 +184,9 @@ union pcan_usb_pro_rec { struct pcan_usb_pro_txmsg tx_msg; }; +int pcan_usb_pro_probe(struct usb_interface *intf); +int pcan_usb_pro_send_req(struct peak_usb_device *dev, int req_id, + int req_value, void *req_addr, int req_size); +void pcan_usb_pro_restart_complete(struct urb *urb); + #endif diff --git a/drivers/net/can/usb/usb_8dev.c b/drivers/net/can/usb/usb_8dev.c index ef674ecb82f8..dd52c7a4c80d 100644 --- a/drivers/net/can/usb/usb_8dev.c +++ b/drivers/net/can/usb/usb_8dev.c @@ -377,6 +377,7 @@ static void usb_8dev_rx_err_msg(struct usb_8dev_priv *priv, case USB_8DEV_STATUSMSG_BUSOFF: priv->can.state = CAN_STATE_BUS_OFF; cf->can_id |= CAN_ERR_BUSOFF; + priv->can.can_stats.bus_off++; can_bus_off(priv->netdev); break; case USB_8DEV_STATUSMSG_OVERRUN: diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index feb29c4526f7..4daffb284931 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -233,6 +233,35 @@ static void bcm_sf2_eee_enable_set(struct dsa_switch *ds, int port, bool enable) core_writel(priv, reg, CORE_EEE_EN_CTRL); } +static void bcm_sf2_gphy_enable_set(struct dsa_switch *ds, bool enable) +{ + struct bcm_sf2_priv *priv = ds_to_priv(ds); + u32 reg; + + reg = reg_readl(priv, REG_SPHY_CNTRL); + if (enable) { + reg |= PHY_RESET; + reg &= ~(EXT_PWR_DOWN | IDDQ_BIAS | CK25_DIS); + reg_writel(priv, reg, REG_SPHY_CNTRL); + udelay(21); + reg = reg_readl(priv, REG_SPHY_CNTRL); + reg &= ~PHY_RESET; + } else { + reg |= EXT_PWR_DOWN | IDDQ_BIAS | PHY_RESET; + reg_writel(priv, reg, REG_SPHY_CNTRL); + mdelay(1); + reg |= CK25_DIS; + } + reg_writel(priv, reg, REG_SPHY_CNTRL); + + /* Use PHY-driven LED signaling */ + if (!enable) { + reg = reg_readl(priv, REG_LED_CNTRL(0)); + reg |= SPDLNK_SRC_SEL; + reg_writel(priv, reg, REG_LED_CNTRL(0)); + } +} + static int bcm_sf2_port_setup(struct dsa_switch *ds, int port, struct phy_device *phy) { @@ -248,6 +277,24 @@ static int bcm_sf2_port_setup(struct dsa_switch *ds, int port, /* Clear the Rx and Tx disable bits and set to no spanning tree */ core_writel(priv, 0, CORE_G_PCTL_PORT(port)); + /* Re-enable the GPHY and re-apply workarounds */ + if (port == 0 && priv->hw_params.num_gphy == 1) { + bcm_sf2_gphy_enable_set(ds, true); + if (phy) { + /* if phy_stop() has been called before, phy + * will be in halted state, and phy_start() + * will call resume. + * + * the resume path does not configure back + * autoneg settings, and since we hard reset + * the phy manually here, we need to reset the + * state machine also. + */ + phy->state = PHY_READY; + phy_init_hw(phy); + } + } + /* Enable port 7 interrupts to get notified */ if (port == 7) intrl2_1_mask_clear(priv, P_IRQ_MASK(P7_IRQ_OFF)); @@ -281,6 +328,9 @@ static void bcm_sf2_port_disable(struct dsa_switch *ds, int port, intrl2_1_writel(priv, P_IRQ_MASK(P7_IRQ_OFF), INTRL2_CPU_CLEAR); } + if (port == 0 && priv->hw_params.num_gphy == 1) + bcm_sf2_gphy_enable_set(ds, false); + if (dsa_is_cpu_port(ds, port)) off = CORE_IMP_CTL; else @@ -400,6 +450,16 @@ static int bcm_sf2_sw_rst(struct bcm_sf2_priv *priv) return 0; } +static void bcm_sf2_intr_disable(struct bcm_sf2_priv *priv) +{ + intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET); + intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); + intrl2_0_writel(priv, 0, INTRL2_CPU_MASK_CLEAR); + intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET); + intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); + intrl2_1_writel(priv, 0, INTRL2_CPU_MASK_CLEAR); +} + static int bcm_sf2_sw_setup(struct dsa_switch *ds) { const char *reg_names[BCM_SF2_REGS_NUM] = BCM_SF2_REGS_NAME; @@ -440,12 +500,7 @@ static int bcm_sf2_sw_setup(struct dsa_switch *ds) } /* Disable all interrupts and request them */ - intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET); - intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); - intrl2_0_writel(priv, 0, INTRL2_CPU_MASK_CLEAR); - intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET); - intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); - intrl2_1_writel(priv, 0, INTRL2_CPU_MASK_CLEAR); + bcm_sf2_intr_disable(priv); ret = request_irq(priv->irq0, bcm_sf2_switch_0_isr, 0, "switch_0", priv); @@ -747,12 +802,7 @@ static int bcm_sf2_sw_suspend(struct dsa_switch *ds) struct bcm_sf2_priv *priv = ds_to_priv(ds); unsigned int port; - intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET); - intrl2_0_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); - intrl2_0_writel(priv, 0, INTRL2_CPU_MASK_CLEAR); - intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_MASK_SET); - intrl2_1_writel(priv, 0xffffffff, INTRL2_CPU_CLEAR); - intrl2_1_writel(priv, 0, INTRL2_CPU_MASK_CLEAR); + bcm_sf2_intr_disable(priv); /* Disable all ports physically present including the IMP * port, the other ones have already been disabled during @@ -771,7 +821,6 @@ static int bcm_sf2_sw_resume(struct dsa_switch *ds) { struct bcm_sf2_priv *priv = ds_to_priv(ds); unsigned int port; - u32 reg; int ret; ret = bcm_sf2_sw_rst(priv); @@ -780,17 +829,8 @@ static int bcm_sf2_sw_resume(struct dsa_switch *ds) return ret; } - /* Reinitialize the single GPHY */ - if (priv->hw_params.num_gphy == 1) { - reg = reg_readl(priv, REG_SPHY_CNTRL); - reg |= PHY_RESET; - reg &= ~(EXT_PWR_DOWN | IDDQ_BIAS); - reg_writel(priv, reg, REG_SPHY_CNTRL); - udelay(21); - reg = reg_readl(priv, REG_SPHY_CNTRL); - reg &= ~PHY_RESET; - reg_writel(priv, reg, REG_SPHY_CNTRL); - } + if (priv->hw_params.num_gphy == 1) + bcm_sf2_gphy_enable_set(ds, true); for (port = 0; port < DSA_MAX_PORTS; port++) { if ((1 << port) & ds->phys_port_mask) diff --git a/drivers/net/dsa/bcm_sf2_regs.h b/drivers/net/dsa/bcm_sf2_regs.h index 1bb49cb699ab..cabdfa5e217a 100644 --- a/drivers/net/dsa/bcm_sf2_regs.h +++ b/drivers/net/dsa/bcm_sf2_regs.h @@ -61,6 +61,10 @@ #define LPI_COUNT_SHIFT 9 #define LPI_COUNT_MASK 0x3F +#define REG_LED_CNTRL_BASE 0x90 +#define REG_LED_CNTRL(x) (REG_LED_CNTRL_BASE + (x) * 4) +#define SPDLNK_SRC_SEL (1 << 24) + /* Register set relative to 'INTRL2_0' and 'INTRL2_1' */ #define INTRL2_CPU_STATUS 0x00 #define INTRL2_CPU_SET 0x04 diff --git a/drivers/net/dsa/mv88e6131.c b/drivers/net/dsa/mv88e6131.c index 1230f52aa70e..2540ef0142af 100644 --- a/drivers/net/dsa/mv88e6131.c +++ b/drivers/net/dsa/mv88e6131.c @@ -139,7 +139,8 @@ static int mv88e6131_setup_global(struct dsa_switch *ds) int nexthop; nexthop = 0x1f; - if (i != ds->index && i < ds->dst->pd->nr_chips) + if (ds->pd->rtable && + i != ds->index && i < ds->dst->pd->nr_chips) nexthop = ds->pd->rtable[i] & 0x1f; REG_WRITE(REG_GLOBAL2, 0x06, 0x8000 | (i << 8) | nexthop); diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c index 258d9ef5ef25..e13adc7b3dda 100644 --- a/drivers/net/dsa/mv88e6352.c +++ b/drivers/net/dsa/mv88e6352.c @@ -22,17 +22,14 @@ #include <net/dsa.h> #include "mv88e6xxx.h" -static int mv88e6352_wait(struct dsa_switch *ds, int reg, u16 mask) +static int mv88e6352_wait(struct dsa_switch *ds, int reg, int offset, u16 mask) { unsigned long timeout = jiffies + HZ / 10; while (time_before(jiffies, timeout)) { int ret; - ret = REG_READ(REG_GLOBAL2, reg); - if (ret < 0) - return ret; - + ret = REG_READ(reg, offset); if (!(ret & mask)) return 0; @@ -43,17 +40,17 @@ static int mv88e6352_wait(struct dsa_switch *ds, int reg, u16 mask) static inline int mv88e6352_phy_wait(struct dsa_switch *ds) { - return mv88e6352_wait(ds, 0x18, 0x8000); + return mv88e6352_wait(ds, REG_GLOBAL2, 0x18, 0x8000); } static inline int mv88e6352_eeprom_load_wait(struct dsa_switch *ds) { - return mv88e6352_wait(ds, 0x14, 0x0800); + return mv88e6352_wait(ds, REG_GLOBAL2, 0x14, 0x0800); } static inline int mv88e6352_eeprom_busy_wait(struct dsa_switch *ds) { - return mv88e6352_wait(ds, 0x14, 0x8000); + return mv88e6352_wait(ds, REG_GLOBAL2, 0x14, 0x8000); } static int __mv88e6352_phy_read(struct dsa_switch *ds, int addr, int regnum) diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index cd6807c6b4ed..3e7e31a6abb7 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c @@ -85,6 +85,12 @@ int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg) ret = __mv88e6xxx_reg_read(bus, ds->pd->sw_addr, addr, reg); mutex_unlock(&ps->smi_mutex); + if (ret < 0) + return ret; + + dev_dbg(ds->master_dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n", + addr, reg, ret); + return ret; } @@ -128,6 +134,9 @@ int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val) if (bus == NULL) return -EINVAL; + dev_dbg(ds->master_dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n", + addr, reg, val); + mutex_lock(&ps->smi_mutex); ret = __mv88e6xxx_reg_write(bus, ds->pd->sw_addr, addr, reg, val); mutex_unlock(&ps->smi_mutex); diff --git a/drivers/net/ethernet/3com/typhoon.c b/drivers/net/ethernet/3com/typhoon.c index dede43f4ce09..8f8418d2ac4a 100644 --- a/drivers/net/ethernet/3com/typhoon.c +++ b/drivers/net/ethernet/3com/typhoon.c @@ -769,11 +769,11 @@ typhoon_start_tx(struct sk_buff *skb, struct net_device *dev) first_txd->processFlags |= TYPHOON_TX_PF_IP_CHKSUM; } - if(vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { first_txd->processFlags |= TYPHOON_TX_PF_INSERT_VLAN | TYPHOON_TX_PF_VLAN_PRIORITY; first_txd->processFlags |= - cpu_to_le32(htons(vlan_tx_tag_get(skb)) << + cpu_to_le32(htons(skb_vlan_tag_get(skb)) << TYPHOON_TX_PF_VLAN_TAG_SHIFT); } diff --git a/drivers/net/ethernet/allwinner/sun4i-emac.c b/drivers/net/ethernet/allwinner/sun4i-emac.c index 1fcd5568a352..f3470d96837a 100644 --- a/drivers/net/ethernet/allwinner/sun4i-emac.c +++ b/drivers/net/ethernet/allwinner/sun4i-emac.c @@ -850,8 +850,10 @@ static int emac_probe(struct platform_device *pdev) } db->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(db->clk)) + if (IS_ERR(db->clk)) { + ret = PTR_ERR(db->clk); goto out; + } clk_prepare_enable(db->clk); diff --git a/drivers/net/ethernet/alteon/acenic.c b/drivers/net/ethernet/alteon/acenic.c index b68074803de3..b90a26b13fdf 100644 --- a/drivers/net/ethernet/alteon/acenic.c +++ b/drivers/net/ethernet/alteon/acenic.c @@ -2429,9 +2429,9 @@ restart: flagsize = (skb->len << 16) | (BD_FLG_END); if (skb->ip_summed == CHECKSUM_PARTIAL) flagsize |= BD_FLG_TCP_UDP_SUM; - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { flagsize |= BD_FLG_VLAN_TAG; - vlan_tag = vlan_tx_tag_get(skb); + vlan_tag = skb_vlan_tag_get(skb); } desc = ap->tx_ring + idx; idx = (idx + 1) % ACE_TX_RING_ENTRIES(ap); @@ -2450,9 +2450,9 @@ restart: flagsize = (skb_headlen(skb) << 16); if (skb->ip_summed == CHECKSUM_PARTIAL) flagsize |= BD_FLG_TCP_UDP_SUM; - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { flagsize |= BD_FLG_VLAN_TAG; - vlan_tag = vlan_tx_tag_get(skb); + vlan_tag = skb_vlan_tag_get(skb); } ace_load_tx_bd(ap, ap->tx_ring + idx, mapping, flagsize, vlan_tag); diff --git a/drivers/net/ethernet/altera/altera_tse_main.c b/drivers/net/ethernet/altera/altera_tse_main.c index 3498760dc22a..760c72c6e2ac 100644 --- a/drivers/net/ethernet/altera/altera_tse_main.c +++ b/drivers/net/ethernet/altera/altera_tse_main.c @@ -1170,10 +1170,6 @@ tx_request_irq_error: init_error: free_skbufs(dev); alloc_skbuf_error: - if (priv->phydev) { - phy_disconnect(priv->phydev); - priv->phydev = NULL; - } phy_error: return ret; } @@ -1186,12 +1182,9 @@ static int tse_shutdown(struct net_device *dev) int ret; unsigned long int flags; - /* Stop and disconnect the PHY */ - if (priv->phydev) { + /* Stop the PHY */ + if (priv->phydev) phy_stop(priv->phydev); - phy_disconnect(priv->phydev); - priv->phydev = NULL; - } netif_stop_queue(dev); napi_disable(&priv->napi); @@ -1525,6 +1518,10 @@ err_free_netdev: static int altera_tse_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); + struct altera_tse_private *priv = netdev_priv(ndev); + + if (priv->phydev) + phy_disconnect(priv->phydev); platform_set_drvdata(pdev, NULL); altera_tse_mdio_destroy(ndev); diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig index 7a5e4aa5415e..c638c85f3954 100644 --- a/drivers/net/ethernet/amd/Kconfig +++ b/drivers/net/ethernet/amd/Kconfig @@ -45,7 +45,7 @@ config AMD8111_ETH config LANCE tristate "AMD LANCE and PCnet (AT1500 and NE2100) support" - depends on ISA && ISA_DMA_API + depends on ISA && ISA_DMA_API && !ARM ---help--- If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from @@ -142,7 +142,7 @@ config PCMCIA_NMCLAN config NI65 tristate "NI6510 support" - depends on ISA && ISA_DMA_API + depends on ISA && ISA_DMA_API && !ARM ---help--- If you have a network (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available from @@ -179,7 +179,7 @@ config SUNLANCE config AMD_XGBE tristate "AMD 10GbE Ethernet driver" - depends on OF_NET && HAS_IOMEM + depends on (OF_NET || ACPI) && HAS_IOMEM select PHYLIB select AMD_XGBE_PHY select BITREVERSE diff --git a/drivers/net/ethernet/amd/amd8111e.c b/drivers/net/ethernet/amd/amd8111e.c index 841e6558db68..4c2ae2221780 100644 --- a/drivers/net/ethernet/amd/amd8111e.c +++ b/drivers/net/ethernet/amd/amd8111e.c @@ -1299,11 +1299,11 @@ static netdev_tx_t amd8111e_start_xmit(struct sk_buff *skb, lp->tx_ring[tx_index].tx_flags = 0; #if AMD8111E_VLAN_TAG_USED - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { lp->tx_ring[tx_index].tag_ctrl_cmd |= cpu_to_le16(TCC_VLAN_INSERT); lp->tx_ring[tx_index].tag_ctrl_info = - cpu_to_le16(vlan_tx_tag_get(skb)); + cpu_to_le16(skb_vlan_tag_get(skb)); } #endif diff --git a/drivers/net/ethernet/amd/atarilance.c b/drivers/net/ethernet/amd/atarilance.c index e07ce5ff2d48..b10964e8cb54 100644 --- a/drivers/net/ethernet/amd/atarilance.c +++ b/drivers/net/ethernet/amd/atarilance.c @@ -553,8 +553,8 @@ static unsigned long __init lance_probe1( struct net_device *dev, if (lp->cardtype == PAM_CARD || memaddr == (unsigned short *)0xffe00000) { /* PAMs card and Riebl on ST use level 5 autovector */ - if (request_irq(IRQ_AUTO_5, lance_interrupt, IRQ_TYPE_PRIO, - "PAM,Riebl-ST Ethernet", dev)) { + if (request_irq(IRQ_AUTO_5, lance_interrupt, 0, + "PAM,Riebl-ST Ethernet", dev)) { printk( "Lance: request for irq %d failed\n", IRQ_AUTO_5 ); return 0; } @@ -567,8 +567,8 @@ static unsigned long __init lance_probe1( struct net_device *dev, printk( "Lance: request for VME interrupt failed\n" ); return 0; } - if (request_irq(irq, lance_interrupt, IRQ_TYPE_PRIO, - "Riebl-VME Ethernet", dev)) { + if (request_irq(irq, lance_interrupt, 0, "Riebl-VME Ethernet", + dev)) { printk( "Lance: request for irq %u failed\n", irq ); return 0; } diff --git a/drivers/net/ethernet/amd/nmclan_cs.c b/drivers/net/ethernet/amd/nmclan_cs.c index 5b22764ba88d..27245efe9f50 100644 --- a/drivers/net/ethernet/amd/nmclan_cs.c +++ b/drivers/net/ethernet/amd/nmclan_cs.c @@ -952,6 +952,8 @@ static irqreturn_t mace_interrupt(int irq, void *dev_id) do { /* WARNING: MACE_IR is a READ/CLEAR port! */ status = inb(ioaddr + AM2150_MACE_BASE + MACE_IR); + if (!(status & ~MACE_IMR_DEFAULT) && IntrCnt == MACE_MAX_IR_ITERATIONS) + return IRQ_NONE; pr_debug("mace_interrupt: irq 0x%X status 0x%X.\n", irq, status); diff --git a/drivers/net/ethernet/amd/pcnet32.c b/drivers/net/ethernet/amd/pcnet32.c index e2e3aaf501a2..11d6e6561df1 100644 --- a/drivers/net/ethernet/amd/pcnet32.c +++ b/drivers/net/ethernet/amd/pcnet32.c @@ -2806,7 +2806,7 @@ static void pcnet32_check_media(struct net_device *dev, int verbose) /* * Check for loss of link and link establishment. - * Can not use mii_check_media because it does nothing if mode is forced. + * Could possibly be changed to use mii_check_media instead. */ static void pcnet32_watchdog(struct net_device *dev) diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index 75b08c63d39f..29a09271b64a 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -767,16 +767,17 @@ #define MTL_Q_RQOMR 0x40 #define MTL_Q_RQMPOCR 0x44 #define MTL_Q_RQDR 0x4c +#define MTL_Q_RQFCR 0x50 #define MTL_Q_IER 0x70 #define MTL_Q_ISR 0x74 /* MTL queue register entry bit positions and sizes */ +#define MTL_Q_RQFCR_RFA_INDEX 1 +#define MTL_Q_RQFCR_RFA_WIDTH 6 +#define MTL_Q_RQFCR_RFD_INDEX 17 +#define MTL_Q_RQFCR_RFD_WIDTH 6 #define MTL_Q_RQOMR_EHFC_INDEX 7 #define MTL_Q_RQOMR_EHFC_WIDTH 1 -#define MTL_Q_RQOMR_RFA_INDEX 8 -#define MTL_Q_RQOMR_RFA_WIDTH 3 -#define MTL_Q_RQOMR_RFD_INDEX 13 -#define MTL_Q_RQOMR_RFD_WIDTH 3 #define MTL_Q_RQOMR_RQS_INDEX 16 #define MTL_Q_RQOMR_RQS_WIDTH 9 #define MTL_Q_RQOMR_RSF_INDEX 5 diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c index 76479d04b903..2c063b60db4b 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c @@ -328,7 +328,7 @@ void xgbe_debugfs_init(struct xgbe_prv_data *pdata) buf = kasprintf(GFP_KERNEL, "amd-xgbe-%s", pdata->netdev->name); pdata->xgbe_debugfs = debugfs_create_dir(buf, NULL); - if (pdata->xgbe_debugfs == NULL) { + if (!pdata->xgbe_debugfs) { netdev_err(pdata->netdev, "debugfs_create_dir failed\n"); return; } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-desc.c b/drivers/net/ethernet/amd/xgbe/xgbe-desc.c index a50891f52197..d81fc6bd4759 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-desc.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-desc.c @@ -422,7 +422,6 @@ static void xgbe_wrapper_rx_descriptor_init(struct xgbe_prv_data *pdata) ring->cur = 0; ring->dirty = 0; - memset(&ring->rx, 0, sizeof(ring->rx)); hw_if->rx_desc_init(channel); } @@ -621,35 +620,6 @@ err_out: return 0; } -static void xgbe_realloc_rx_buffer(struct xgbe_channel *channel) -{ - struct xgbe_prv_data *pdata = channel->pdata; - struct xgbe_hw_if *hw_if = &pdata->hw_if; - struct xgbe_ring *ring = channel->rx_ring; - struct xgbe_ring_data *rdata; - int i; - - DBGPR("-->xgbe_realloc_rx_buffer: rx_ring->rx.realloc_index = %u\n", - ring->rx.realloc_index); - - for (i = 0; i < ring->dirty; i++) { - rdata = XGBE_GET_DESC_DATA(ring, ring->rx.realloc_index); - - /* Reset rdata values */ - xgbe_unmap_rdata(pdata, rdata); - - if (xgbe_map_rx_buffer(pdata, ring, rdata)) - break; - - hw_if->rx_desc_reset(rdata); - - ring->rx.realloc_index++; - } - ring->dirty = 0; - - DBGPR("<--xgbe_realloc_rx_buffer\n"); -} - void xgbe_init_function_ptrs_desc(struct xgbe_desc_if *desc_if) { DBGPR("-->xgbe_init_function_ptrs_desc\n"); @@ -657,7 +627,7 @@ void xgbe_init_function_ptrs_desc(struct xgbe_desc_if *desc_if) desc_if->alloc_ring_resources = xgbe_alloc_ring_resources; desc_if->free_ring_resources = xgbe_free_ring_resources; desc_if->map_tx_skb = xgbe_map_tx_skb; - desc_if->realloc_rx_buffer = xgbe_realloc_rx_buffer; + desc_if->map_rx_buffer = xgbe_map_rx_buffer; desc_if->unmap_rdata = xgbe_unmap_rdata; desc_if->wrapper_tx_desc_init = xgbe_wrapper_tx_descriptor_init; desc_if->wrapper_rx_desc_init = xgbe_wrapper_rx_descriptor_init; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index 53f5f66ec2ee..400757b49872 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -115,6 +115,7 @@ */ #include <linux/phy.h> +#include <linux/mdio.h> #include <linux/clk.h> #include <linux/bitrev.h> #include <linux/crc32.h> @@ -130,7 +131,7 @@ static unsigned int xgbe_usec_to_riwt(struct xgbe_prv_data *pdata, DBGPR("-->xgbe_usec_to_riwt\n"); - rate = clk_get_rate(pdata->sysclk); + rate = pdata->sysclk_rate; /* * Convert the input usec value to the watchdog timer value. Each @@ -153,7 +154,7 @@ static unsigned int xgbe_riwt_to_usec(struct xgbe_prv_data *pdata, DBGPR("-->xgbe_riwt_to_usec\n"); - rate = clk_get_rate(pdata->sysclk); + rate = pdata->sysclk_rate; /* * Convert the input watchdog timer value to the usec value. Each @@ -673,6 +674,9 @@ static void xgbe_enable_mac_interrupts(struct xgbe_prv_data *pdata) static int xgbe_set_gmii_speed(struct xgbe_prv_data *pdata) { + if (XGMAC_IOREAD_BITS(pdata, MAC_TCR, SS) == 0x3) + return 0; + XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0x3); return 0; @@ -680,6 +684,9 @@ static int xgbe_set_gmii_speed(struct xgbe_prv_data *pdata) static int xgbe_set_gmii_2500_speed(struct xgbe_prv_data *pdata) { + if (XGMAC_IOREAD_BITS(pdata, MAC_TCR, SS) == 0x2) + return 0; + XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0x2); return 0; @@ -687,6 +694,9 @@ static int xgbe_set_gmii_2500_speed(struct xgbe_prv_data *pdata) static int xgbe_set_xgmii_speed(struct xgbe_prv_data *pdata) { + if (XGMAC_IOREAD_BITS(pdata, MAC_TCR, SS) == 0) + return 0; + XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0); return 0; @@ -881,6 +891,23 @@ static void xgbe_write_mmd_regs(struct xgbe_prv_data *pdata, int prtad, else mmd_address = (pdata->mdio_mmd << 16) | (mmd_reg & 0xffff); + /* If the PCS is changing modes, match the MAC speed to it */ + if (((mmd_address >> 16) == MDIO_MMD_PCS) && + ((mmd_address & 0xffff) == MDIO_CTRL2)) { + struct phy_device *phydev = pdata->phydev; + + if (mmd_data & MDIO_PCS_CTRL2_TYPE) { + /* KX mode */ + if (phydev->supported & SUPPORTED_1000baseKX_Full) + xgbe_set_gmii_speed(pdata); + else + xgbe_set_gmii_2500_speed(pdata); + } else { + /* KR mode */ + xgbe_set_xgmii_speed(pdata); + } + } + /* The PCS registers are accessed using mmio. The underlying APB3 * management interface uses indirect addressing to access the MMD * register sets. This requires accessing of the PCS register in two @@ -1359,6 +1386,7 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) unsigned int tso_context, vlan_context; unsigned int tx_set_ic; int start_index = ring->cur; + int cur_index = ring->cur; int i; DBGPR("-->xgbe_dev_xmit\n"); @@ -1401,7 +1429,7 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) else tx_set_ic = 0; - rdata = XGBE_GET_DESC_DATA(ring, ring->cur); + rdata = XGBE_GET_DESC_DATA(ring, cur_index); rdesc = rdata->rdesc; /* Create a context descriptor if this is a TSO packet */ @@ -1444,8 +1472,8 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) ring->tx.cur_vlan_ctag = packet->vlan_ctag; } - ring->cur++; - rdata = XGBE_GET_DESC_DATA(ring, ring->cur); + cur_index++; + rdata = XGBE_GET_DESC_DATA(ring, cur_index); rdesc = rdata->rdesc; } @@ -1473,7 +1501,7 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, CTXT, 0); /* Set OWN bit if not the first descriptor */ - if (ring->cur != start_index) + if (cur_index != start_index) XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, OWN, 1); if (tso) { @@ -1497,9 +1525,9 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) packet->length); } - for (i = ring->cur - start_index + 1; i < packet->rdesc_count; i++) { - ring->cur++; - rdata = XGBE_GET_DESC_DATA(ring, ring->cur); + for (i = cur_index - start_index + 1; i < packet->rdesc_count; i++) { + cur_index++; + rdata = XGBE_GET_DESC_DATA(ring, cur_index); rdesc = rdata->rdesc; /* Update buffer address */ @@ -1551,7 +1579,7 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) /* Make sure ownership is written to the descriptor */ wmb(); - ring->cur++; + ring->cur = cur_index + 1; if (!packet->skb->xmit_more || netif_xmit_stopped(netdev_get_tx_queue(pdata->netdev, channel->queue_index))) @@ -2079,10 +2107,10 @@ static void xgbe_config_flow_control_threshold(struct xgbe_prv_data *pdata) for (i = 0; i < pdata->rx_q_count; i++) { /* Activate flow control when less than 4k left in fifo */ - XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, RFA, 2); + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQFCR, RFA, 2); /* De-activate flow control when more than 6k left in fifo */ - XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQOMR, RFD, 4); + XGMAC_MTL_IOWRITE_BITS(pdata, i, MTL_Q_RQFCR, RFD, 4); } } @@ -2107,6 +2135,23 @@ static void xgbe_config_jumbo_enable(struct xgbe_prv_data *pdata) XGMAC_IOWRITE_BITS(pdata, MAC_RCR, JE, val); } +static void xgbe_config_mac_speed(struct xgbe_prv_data *pdata) +{ + switch (pdata->phy_speed) { + case SPEED_10000: + xgbe_set_xgmii_speed(pdata); + break; + + case SPEED_2500: + xgbe_set_gmii_2500_speed(pdata); + break; + + case SPEED_1000: + xgbe_set_gmii_speed(pdata); + break; + } +} + static void xgbe_config_checksum_offload(struct xgbe_prv_data *pdata) { if (pdata->netdev->features & NETIF_F_RXCSUM) @@ -2757,6 +2802,7 @@ static int xgbe_init(struct xgbe_prv_data *pdata) xgbe_config_mac_address(pdata); xgbe_config_jumbo_enable(pdata); xgbe_config_flow_control(pdata); + xgbe_config_mac_speed(pdata); xgbe_config_checksum_offload(pdata); xgbe_config_vlan_support(pdata); xgbe_config_mmc(pdata); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 7bb5f07dbeef..b93d4404d975 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -225,6 +225,11 @@ static inline unsigned int xgbe_tx_avail_desc(struct xgbe_ring *ring) return (ring->rdesc_count - (ring->cur - ring->dirty)); } +static inline unsigned int xgbe_rx_dirty_desc(struct xgbe_ring *ring) +{ + return (ring->cur - ring->dirty); +} + static int xgbe_maybe_stop_tx_queue(struct xgbe_channel *channel, struct xgbe_ring *ring, unsigned int count) { @@ -337,12 +342,13 @@ static irqreturn_t xgbe_isr(int irq, void *data) dma_ch_isr = XGMAC_DMA_IOREAD(channel, DMA_CH_SR); DBGPR(" DMA_CH%u_ISR = %08x\n", i, dma_ch_isr); - /* If we get a TI or RI interrupt that means per channel DMA - * interrupts are not enabled, so we use the private data napi - * structure, not the per channel napi structure + /* The TI or RI interrupt bits may still be set even if using + * per channel DMA interrupts. Check to be sure those are not + * enabled before using the private data napi structure. */ - if (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, TI) || - XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, RI)) { + if (!pdata->per_channel_irq && + (XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, TI) || + XGMAC_GET_BITS(dma_ch_isr, DMA_CH_SR, RI))) { if (napi_schedule_prep(&pdata->napi)) { /* Disable Tx and Rx interrupts */ xgbe_disable_rx_tx_ints(pdata); @@ -410,17 +416,13 @@ static enum hrtimer_restart xgbe_tx_timer(struct hrtimer *timer) struct xgbe_channel *channel = container_of(timer, struct xgbe_channel, tx_timer); - struct xgbe_ring *ring = channel->tx_ring; struct xgbe_prv_data *pdata = channel->pdata; struct napi_struct *napi; - unsigned long flags; DBGPR("-->xgbe_tx_timer\n"); napi = (pdata->per_channel_irq) ? &channel->napi : &pdata->napi; - spin_lock_irqsave(&ring->lock, flags); - if (napi_schedule_prep(napi)) { /* Disable Tx and Rx interrupts */ if (pdata->per_channel_irq) @@ -434,8 +436,6 @@ static enum hrtimer_restart xgbe_tx_timer(struct hrtimer *timer) channel->tx_timer_active = 0; - spin_unlock_irqrestore(&ring->lock, flags); - DBGPR("<--xgbe_tx_timer\n"); return HRTIMER_NORESTART; @@ -523,6 +523,7 @@ void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata) hw_feat->sph = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, SPHEN); hw_feat->tso = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, TSOEN); hw_feat->dma_debug = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, DBGMEMA); + hw_feat->rss = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, RSSEN); hw_feat->tc_cnt = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, NUMTC); hw_feat->hash_table_size = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, HASHTBLSZ); @@ -552,13 +553,14 @@ void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata) break; } - /* The Queue and Channel counts are zero based so increment them + /* The Queue, Channel and TC counts are zero based so increment them * to get the actual number */ hw_feat->rx_q_cnt++; hw_feat->tx_q_cnt++; hw_feat->rx_ch_cnt++; hw_feat->tx_ch_cnt++; + hw_feat->tc_cnt++; DBGPR("<--xgbe_get_all_hw_features\n"); } @@ -692,7 +694,7 @@ static void xgbe_adjust_link(struct net_device *netdev) struct phy_device *phydev = pdata->phydev; int new_state = 0; - if (phydev == NULL) + if (!phydev) return; if (phydev->link) { @@ -927,7 +929,7 @@ static void xgbe_stop(struct xgbe_prv_data *pdata) DBGPR("<--xgbe_stop\n"); } -static void xgbe_restart_dev(struct xgbe_prv_data *pdata, unsigned int reset) +static void xgbe_restart_dev(struct xgbe_prv_data *pdata) { struct xgbe_channel *channel; struct xgbe_hw_if *hw_if = &pdata->hw_if; @@ -950,9 +952,8 @@ static void xgbe_restart_dev(struct xgbe_prv_data *pdata, unsigned int reset) xgbe_free_tx_data(pdata); xgbe_free_rx_data(pdata); - /* Issue software reset to device if requested */ - if (reset) - hw_if->exit(pdata); + /* Issue software reset to device */ + hw_if->exit(pdata); xgbe_start(pdata); @@ -967,7 +968,7 @@ static void xgbe_restart(struct work_struct *work) rtnl_lock(); - xgbe_restart_dev(pdata, 1); + xgbe_restart_dev(pdata); rtnl_unlock(); } @@ -1165,8 +1166,8 @@ static void xgbe_prep_tx_tstamp(struct xgbe_prv_data *pdata, static void xgbe_prep_vlan(struct sk_buff *skb, struct xgbe_packet_data *packet) { - if (vlan_tx_tag_present(skb)) - packet->vlan_ctag = vlan_tx_tag_get(skb); + if (skb_vlan_tag_present(skb)) + packet->vlan_ctag = skb_vlan_tag_get(skb); } static int xgbe_prep_tso(struct sk_buff *skb, struct xgbe_packet_data *packet) @@ -1247,9 +1248,9 @@ static void xgbe_packet_info(struct xgbe_prv_data *pdata, XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, CSUM_ENABLE, 1); - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { /* VLAN requires an extra descriptor if tag is different */ - if (vlan_tx_tag_get(skb) != ring->tx.cur_vlan_ctag) + if (skb_vlan_tag_get(skb) != ring->tx.cur_vlan_ctag) /* We can share with the TSO context descriptor */ if (!context_desc) { context_desc = 1; @@ -1446,7 +1447,6 @@ static int xgbe_xmit(struct sk_buff *skb, struct net_device *netdev) struct xgbe_ring *ring; struct xgbe_packet_data *packet; struct netdev_queue *txq; - unsigned long flags; int ret; DBGPR("-->xgbe_xmit: skb->len = %d\n", skb->len); @@ -1458,8 +1458,6 @@ static int xgbe_xmit(struct sk_buff *skb, struct net_device *netdev) ret = NETDEV_TX_OK; - spin_lock_irqsave(&ring->lock, flags); - if (skb->len == 0) { netdev_err(netdev, "empty skb received from stack\n"); dev_kfree_skb_any(skb); @@ -1506,10 +1504,6 @@ static int xgbe_xmit(struct sk_buff *skb, struct net_device *netdev) ret = NETDEV_TX_OK; tx_netdev_return: - spin_unlock_irqrestore(&ring->lock, flags); - - DBGPR("<--xgbe_xmit\n"); - return ret; } @@ -1587,7 +1581,7 @@ static int xgbe_change_mtu(struct net_device *netdev, int mtu) pdata->rx_buf_size = ret; netdev->mtu = mtu; - xgbe_restart_dev(pdata, 0); + xgbe_restart_dev(pdata); DBGPR("<--xgbe_change_mtu\n"); @@ -1776,15 +1770,28 @@ struct net_device_ops *xgbe_get_netdev_ops(void) static void xgbe_rx_refresh(struct xgbe_channel *channel) { struct xgbe_prv_data *pdata = channel->pdata; + struct xgbe_hw_if *hw_if = &pdata->hw_if; struct xgbe_desc_if *desc_if = &pdata->desc_if; struct xgbe_ring *ring = channel->rx_ring; struct xgbe_ring_data *rdata; - desc_if->realloc_rx_buffer(channel); + while (ring->dirty != ring->cur) { + rdata = XGBE_GET_DESC_DATA(ring, ring->dirty); + + /* Reset rdata values */ + desc_if->unmap_rdata(pdata, rdata); + + if (desc_if->map_rx_buffer(pdata, ring, rdata)) + break; + + hw_if->rx_desc_reset(rdata); + + ring->dirty++; + } /* Update the Rx Tail Pointer Register with address of * the last cleaned entry */ - rdata = XGBE_GET_DESC_DATA(ring, ring->rx.realloc_index - 1); + rdata = XGBE_GET_DESC_DATA(ring, ring->dirty - 1); XGMAC_DMA_IOWRITE(channel, DMA_CH_RDTR_LO, lower_32_bits(rdata->rdesc_dma)); } @@ -1824,7 +1831,6 @@ static int xgbe_tx_poll(struct xgbe_channel *channel) struct xgbe_ring_desc *rdesc; struct net_device *netdev = pdata->netdev; struct netdev_queue *txq; - unsigned long flags; int processed = 0; unsigned int tx_packets = 0, tx_bytes = 0; @@ -1836,8 +1842,6 @@ static int xgbe_tx_poll(struct xgbe_channel *channel) txq = netdev_get_tx_queue(netdev, channel->queue_index); - spin_lock_irqsave(&ring->lock, flags); - while ((processed < XGBE_TX_DESC_MAX_PROC) && (ring->dirty != ring->cur)) { rdata = XGBE_GET_DESC_DATA(ring, ring->dirty); @@ -1868,7 +1872,7 @@ static int xgbe_tx_poll(struct xgbe_channel *channel) } if (!processed) - goto unlock; + return 0; netdev_tx_completed_queue(txq, tx_packets, tx_bytes); @@ -1880,9 +1884,6 @@ static int xgbe_tx_poll(struct xgbe_channel *channel) DBGPR("<--xgbe_tx_poll: processed=%d\n", processed); -unlock: - spin_unlock_irqrestore(&ring->lock, flags); - return processed; } @@ -1934,7 +1935,7 @@ static int xgbe_rx_poll(struct xgbe_channel *channel, int budget) read_again: rdata = XGBE_GET_DESC_DATA(ring, ring->cur); - if (ring->dirty > (XGBE_RX_DESC_CNT >> 3)) + if (xgbe_rx_dirty_desc(ring) > (XGBE_RX_DESC_CNT >> 3)) xgbe_rx_refresh(channel); if (hw_if->dev_read(channel)) @@ -1942,7 +1943,6 @@ read_again: received++; ring->cur++; - ring->dirty++; incomplete = XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index dbd3850b8b0a..32dd65137051 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -123,7 +123,10 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/of_net.h> +#include <linux/of_address.h> #include <linux/clk.h> +#include <linux/property.h> +#include <linux/acpi.h> #include "xgbe.h" #include "xgbe-common.h" @@ -148,6 +151,7 @@ static void xgbe_default_config(struct xgbe_prv_data *pdata) pdata->pause_autoneg = 1; pdata->tx_pause = 1; pdata->rx_pause = 1; + pdata->phy_speed = SPEED_UNKNOWN; pdata->power_down = 0; pdata->default_autoneg = AUTONEG_ENABLE; pdata->default_speed = SPEED_10000; @@ -161,6 +165,96 @@ static void xgbe_init_all_fptrs(struct xgbe_prv_data *pdata) xgbe_init_function_ptrs_desc(&pdata->desc_if); } +#ifdef CONFIG_ACPI +static int xgbe_acpi_support(struct xgbe_prv_data *pdata) +{ + struct acpi_device *adev = pdata->adev; + struct device *dev = pdata->dev; + u32 property; + acpi_handle handle; + acpi_status status; + unsigned long long data; + int cca; + int ret; + + /* Obtain the system clock setting */ + ret = device_property_read_u32(dev, XGBE_ACPI_DMA_FREQ, &property); + if (ret) { + dev_err(dev, "unable to obtain %s property\n", + XGBE_ACPI_DMA_FREQ); + return ret; + } + pdata->sysclk_rate = property; + + /* Obtain the PTP clock setting */ + ret = device_property_read_u32(dev, XGBE_ACPI_PTP_FREQ, &property); + if (ret) { + dev_err(dev, "unable to obtain %s property\n", + XGBE_ACPI_PTP_FREQ); + return ret; + } + pdata->ptpclk_rate = property; + + /* Retrieve the device cache coherency value */ + handle = adev->handle; + do { + status = acpi_evaluate_integer(handle, "_CCA", NULL, &data); + if (!ACPI_FAILURE(status)) { + cca = data; + break; + } + + status = acpi_get_parent(handle, &handle); + } while (!ACPI_FAILURE(status)); + + if (ACPI_FAILURE(status)) { + dev_err(dev, "error obtaining acpi coherency value\n"); + return -EINVAL; + } + pdata->coherent = !!cca; + + return 0; +} +#else /* CONFIG_ACPI */ +static int xgbe_acpi_support(struct xgbe_prv_data *pdata) +{ + return -EINVAL; +} +#endif /* CONFIG_ACPI */ + +#ifdef CONFIG_OF +static int xgbe_of_support(struct xgbe_prv_data *pdata) +{ + struct device *dev = pdata->dev; + + /* Obtain the system clock setting */ + pdata->sysclk = devm_clk_get(dev, XGBE_DMA_CLOCK); + if (IS_ERR(pdata->sysclk)) { + dev_err(dev, "dma devm_clk_get failed\n"); + return PTR_ERR(pdata->sysclk); + } + pdata->sysclk_rate = clk_get_rate(pdata->sysclk); + + /* Obtain the PTP clock setting */ + pdata->ptpclk = devm_clk_get(dev, XGBE_PTP_CLOCK); + if (IS_ERR(pdata->ptpclk)) { + dev_err(dev, "ptp devm_clk_get failed\n"); + return PTR_ERR(pdata->ptpclk); + } + pdata->ptpclk_rate = clk_get_rate(pdata->ptpclk); + + /* Retrieve the device cache coherency value */ + pdata->coherent = of_dma_is_coherent(dev->of_node); + + return 0; +} +#else /* CONFIG_OF */ +static int xgbe_of_support(struct xgbe_prv_data *pdata) +{ + return -EINVAL; +} +#endif /*CONFIG_OF */ + static int xgbe_probe(struct platform_device *pdev) { struct xgbe_prv_data *pdata; @@ -169,7 +263,7 @@ static int xgbe_probe(struct platform_device *pdev) struct net_device *netdev; struct device *dev = &pdev->dev; struct resource *res; - const u8 *mac_addr; + const char *phy_mode; unsigned int i; int ret; @@ -186,6 +280,7 @@ static int xgbe_probe(struct platform_device *pdev) pdata = netdev_priv(netdev); pdata->netdev = netdev; pdata->pdev = pdev; + pdata->adev = ACPI_COMPANION(dev); pdata->dev = dev; platform_set_drvdata(pdev, netdev); @@ -194,6 +289,9 @@ static int xgbe_probe(struct platform_device *pdev) mutex_init(&pdata->rss_mutex); spin_lock_init(&pdata->tstamp_lock); + /* Check if we should use ACPI or DT */ + pdata->use_acpi = (!pdata->adev || acpi_disabled) ? 0 : 1; + /* Set and validate the number of descriptors for a ring */ BUILD_BUG_ON_NOT_POWER_OF_2(XGBE_TX_DESC_CNT); pdata->tx_desc_count = XGBE_TX_DESC_CNT; @@ -212,22 +310,6 @@ static int xgbe_probe(struct platform_device *pdev) goto err_io; } - /* Obtain the system clock setting */ - pdata->sysclk = devm_clk_get(dev, XGBE_DMA_CLOCK); - if (IS_ERR(pdata->sysclk)) { - dev_err(dev, "dma devm_clk_get failed\n"); - ret = PTR_ERR(pdata->sysclk); - goto err_io; - } - - /* Obtain the PTP clock setting */ - pdata->ptpclk = devm_clk_get(dev, XGBE_PTP_CLOCK); - if (IS_ERR(pdata->ptpclk)) { - dev_err(dev, "ptp devm_clk_get failed\n"); - ret = PTR_ERR(pdata->ptpclk); - goto err_io; - } - /* Obtain the mmio areas for the device */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); pdata->xgmac_regs = devm_ioremap_resource(dev, res); @@ -247,16 +329,42 @@ static int xgbe_probe(struct platform_device *pdev) } DBGPR(" xpcs_regs = %p\n", pdata->xpcs_regs); - /* Set the DMA mask */ - if (!dev->dma_mask) - dev->dma_mask = &dev->coherent_dma_mask; - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40)); - if (ret) { - dev_err(dev, "dma_set_mask_and_coherent failed\n"); + /* Retrieve the MAC address */ + ret = device_property_read_u8_array(dev, XGBE_MAC_ADDR_PROPERTY, + pdata->mac_addr, + sizeof(pdata->mac_addr)); + if (ret || !is_valid_ether_addr(pdata->mac_addr)) { + dev_err(dev, "invalid %s property\n", XGBE_MAC_ADDR_PROPERTY); + if (!ret) + ret = -EINVAL; goto err_io; } - if (of_property_read_bool(dev->of_node, "dma-coherent")) { + /* Retrieve the PHY mode - it must be "xgmii" */ + ret = device_property_read_string(dev, XGBE_PHY_MODE_PROPERTY, + &phy_mode); + if (ret || strcmp(phy_mode, phy_modes(PHY_INTERFACE_MODE_XGMII))) { + dev_err(dev, "invalid %s property\n", XGBE_PHY_MODE_PROPERTY); + if (!ret) + ret = -EINVAL; + goto err_io; + } + pdata->phy_mode = PHY_INTERFACE_MODE_XGMII; + + /* Check for per channel interrupt support */ + if (device_property_present(dev, XGBE_DMA_IRQS_PROPERTY)) + pdata->per_channel_irq = 1; + + /* Obtain device settings unique to ACPI/OF */ + if (pdata->use_acpi) + ret = xgbe_acpi_support(pdata); + else + ret = xgbe_of_support(pdata); + if (ret) + goto err_io; + + /* Set the DMA coherency values */ + if (pdata->coherent) { pdata->axdomain = XGBE_DMA_OS_AXDOMAIN; pdata->arcache = XGBE_DMA_OS_ARCACHE; pdata->awcache = XGBE_DMA_OS_AWCACHE; @@ -266,10 +374,16 @@ static int xgbe_probe(struct platform_device *pdev) pdata->awcache = XGBE_DMA_SYS_AWCACHE; } - /* Check for per channel interrupt support */ - if (of_property_read_bool(dev->of_node, XGBE_DMA_IRQS)) - pdata->per_channel_irq = 1; + /* Set the DMA mask */ + if (!dev->dma_mask) + dev->dma_mask = &dev->coherent_dma_mask; + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40)); + if (ret) { + dev_err(dev, "dma_set_mask_and_coherent failed\n"); + goto err_io; + } + /* Get the device interrupt */ ret = platform_get_irq(pdev, 0); if (ret < 0) { dev_err(dev, "platform_get_irq 0 failed\n"); @@ -279,6 +393,7 @@ static int xgbe_probe(struct platform_device *pdev) netdev->irq = pdata->dev_irq; netdev->base_addr = (unsigned long)pdata->xgmac_regs; + memcpy(netdev->dev_addr, pdata->mac_addr, netdev->addr_len); /* Set all the function pointers */ xgbe_init_all_fptrs(pdata); @@ -291,23 +406,6 @@ static int xgbe_probe(struct platform_device *pdev) /* Populate the hardware features */ xgbe_get_all_hw_features(pdata); - /* Retrieve the MAC address */ - mac_addr = of_get_mac_address(dev->of_node); - if (!mac_addr) { - dev_err(dev, "invalid mac address for this device\n"); - ret = -EINVAL; - goto err_io; - } - memcpy(netdev->dev_addr, mac_addr, netdev->addr_len); - - /* Retrieve the PHY mode - it must be "xgmii" */ - pdata->phy_mode = of_get_phy_mode(dev->of_node); - if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII) { - dev_err(dev, "invalid phy-mode specified for this device\n"); - ret = -EINVAL; - goto err_io; - } - /* Set default configuration data */ xgbe_default_config(pdata); @@ -491,18 +589,35 @@ static int xgbe_resume(struct device *dev) } #endif /* CONFIG_PM */ +#ifdef CONFIG_ACPI +static const struct acpi_device_id xgbe_acpi_match[] = { + { "AMDI8001", 0 }, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, xgbe_acpi_match); +#endif + +#ifdef CONFIG_OF static const struct of_device_id xgbe_of_match[] = { { .compatible = "amd,xgbe-seattle-v1a", }, {}, }; MODULE_DEVICE_TABLE(of, xgbe_of_match); +#endif + static SIMPLE_DEV_PM_OPS(xgbe_pm_ops, xgbe_suspend, xgbe_resume); static struct platform_driver xgbe_driver = { .driver = { .name = "amd-xgbe", +#ifdef CONFIG_ACPI + .acpi_match_table = xgbe_acpi_match, +#endif +#ifdef CONFIG_OF .of_match_table = xgbe_of_match, +#endif .pm = &xgbe_pm_ops, }, .probe = xgbe_probe, diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 363b210560f3..59e267f3f1b7 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -205,25 +205,16 @@ void xgbe_dump_phy_registers(struct xgbe_prv_data *pdata) int xgbe_mdio_register(struct xgbe_prv_data *pdata) { - struct device_node *phy_node; struct mii_bus *mii; struct phy_device *phydev; int ret = 0; DBGPR("-->xgbe_mdio_register\n"); - /* Retrieve the phy-handle */ - phy_node = of_parse_phandle(pdata->dev->of_node, "phy-handle", 0); - if (!phy_node) { - dev_err(pdata->dev, "unable to parse phy-handle\n"); - return -EINVAL; - } - mii = mdiobus_alloc(); - if (mii == NULL) { + if (!mii) { dev_err(pdata->dev, "mdiobus_alloc failed\n"); - ret = -ENOMEM; - goto err_node_get; + return -ENOMEM; } /* Register on the MDIO bus (don't probe any PHYs) */ @@ -252,18 +243,19 @@ int xgbe_mdio_register(struct xgbe_prv_data *pdata) request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phydev->c45_ids.device_ids[MDIO_MMD_PCS])); - of_node_get(phy_node); - phydev->dev.of_node = phy_node; ret = phy_device_register(phydev); if (ret) { dev_err(pdata->dev, "phy_device_register failed\n"); - of_node_put(phy_node); + goto err_phy_device; + } + if (!phydev->dev.driver) { + dev_err(pdata->dev, "phy driver probe failed\n"); + ret = -EIO; goto err_phy_device; } /* Add a reference to the PHY driver so it can't be unloaded */ - pdata->phy_module = phydev->dev.driver ? - phydev->dev.driver->owner : NULL; + pdata->phy_module = phydev->dev.driver->owner; if (!try_module_get(pdata->phy_module)) { dev_err(pdata->dev, "try_module_get failed\n"); ret = -EIO; @@ -283,8 +275,6 @@ int xgbe_mdio_register(struct xgbe_prv_data *pdata) pdata->phydev = phydev; - of_node_put(phy_node); - DBGPHY_REGS(pdata); DBGPR("<--xgbe_mdio_register\n"); @@ -300,9 +290,6 @@ err_mdiobus_register: err_mdiobus_alloc: mdiobus_free(mii); -err_node_get: - of_node_put(phy_node); - return ret; } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c b/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c index a1bf9d1cdae1..f326178ef376 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ptp.c @@ -171,15 +171,9 @@ static int xgbe_adjtime(struct ptp_clock_info *info, s64 delta) struct xgbe_prv_data, ptp_clock_info); unsigned long flags; - u64 nsec; spin_lock_irqsave(&pdata->tstamp_lock, flags); - - nsec = timecounter_read(&pdata->tstamp_tc); - - nsec += delta; - timecounter_init(&pdata->tstamp_tc, &pdata->tstamp_cc, nsec); - + timecounter_adjtime(&pdata->tstamp_tc, delta); spin_unlock_irqrestore(&pdata->tstamp_lock, flags); return 0; @@ -239,7 +233,7 @@ void xgbe_ptp_register(struct xgbe_prv_data *pdata) snprintf(info->name, sizeof(info->name), "%s", netdev_name(pdata->netdev)); info->owner = THIS_MODULE; - info->max_adj = clk_get_rate(pdata->ptpclk); + info->max_adj = pdata->ptpclk_rate; info->adjfreq = xgbe_adjfreq; info->adjtime = xgbe_adjtime; info->gettime = xgbe_gettime; @@ -260,7 +254,7 @@ void xgbe_ptp_register(struct xgbe_prv_data *pdata) */ dividend = 50000000; dividend <<= 32; - pdata->tstamp_addend = div_u64(dividend, clk_get_rate(pdata->ptpclk)); + pdata->tstamp_addend = div_u64(dividend, pdata->ptpclk_rate); /* Setup the timecounter */ cc->read = xgbe_cc_read; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index f9ec762ac3f0..13e8f95c077c 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -124,7 +124,7 @@ #include <linux/if_vlan.h> #include <linux/bitops.h> #include <linux/ptp_clock_kernel.h> -#include <linux/clocksource.h> +#include <linux/timecounter.h> #include <linux/net_tstamp.h> #include <net/dcbnl.h> @@ -182,10 +182,18 @@ #define XGBE_PHY_NAME "amd_xgbe_phy" #define XGBE_PRTAD 0 +/* Common property names */ +#define XGBE_MAC_ADDR_PROPERTY "mac-address" +#define XGBE_PHY_MODE_PROPERTY "phy-mode" +#define XGBE_DMA_IRQS_PROPERTY "amd,per-channel-interrupt" + /* Device-tree clock names */ #define XGBE_DMA_CLOCK "dma_clk" #define XGBE_PTP_CLOCK "ptp_clk" -#define XGBE_DMA_IRQS "amd,per-channel-interrupt" + +/* ACPI property names */ +#define XGBE_ACPI_DMA_FREQ "amd,dma-freq" +#define XGBE_ACPI_PTP_FREQ "amd,ptp-freq" /* Timestamp support - values based on 50MHz PTP clock * 50MHz => 20 nsec @@ -361,8 +369,7 @@ struct xgbe_ring { * cur - Tx: index of descriptor to be used for current transfer * Rx: index of descriptor to check for packet availability * dirty - Tx: index of descriptor to check for transfer complete - * Rx: count of descriptors in which a packet has been received - * (used with skb_realloc_index to refresh the ring) + * Rx: index of descriptor to check for buffer reallocation */ unsigned int cur; unsigned int dirty; @@ -377,11 +384,6 @@ struct xgbe_ring { unsigned short cur_mss; unsigned short cur_vlan_ctag; } tx; - - struct { - unsigned int realloc_index; - unsigned int realloc_threshold; - } rx; }; } ____cacheline_aligned; @@ -596,7 +598,8 @@ struct xgbe_desc_if { int (*alloc_ring_resources)(struct xgbe_prv_data *); void (*free_ring_resources)(struct xgbe_prv_data *); int (*map_tx_skb)(struct xgbe_channel *, struct sk_buff *); - void (*realloc_rx_buffer)(struct xgbe_channel *); + int (*map_rx_buffer)(struct xgbe_prv_data *, struct xgbe_ring *, + struct xgbe_ring_data *); void (*unmap_rdata)(struct xgbe_prv_data *, struct xgbe_ring_data *); void (*wrapper_tx_desc_init)(struct xgbe_prv_data *); void (*wrapper_rx_desc_init)(struct xgbe_prv_data *); @@ -650,8 +653,12 @@ struct xgbe_hw_features { struct xgbe_prv_data { struct net_device *netdev; struct platform_device *pdev; + struct acpi_device *adev; struct device *dev; + /* ACPI or DT flag */ + unsigned int use_acpi; + /* XGMAC/XPCS related mmio registers */ void __iomem *xgmac_regs; /* XGMAC CSRs */ void __iomem *xpcs_regs; /* XPCS MMD registers */ @@ -672,6 +679,7 @@ struct xgbe_prv_data { struct xgbe_desc_if desc_if; /* AXI DMA settings */ + unsigned int coherent; unsigned int axdomain; unsigned int arcache; unsigned int awcache; @@ -739,6 +747,7 @@ struct xgbe_prv_data { unsigned int phy_rx_pause; /* Netdev related settings */ + unsigned char mac_addr[ETH_ALEN]; netdev_features_t netdev_features; struct napi_struct napi; struct xgbe_mmc_stats mmc_stats; @@ -748,7 +757,9 @@ struct xgbe_prv_data { /* Device clocks */ struct clk *sysclk; + unsigned long sysclk_rate; struct clk *ptpclk; + unsigned long ptpclk_rate; /* Timestamp support */ spinlock_t tstamp_lock; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index 7ba83ffb08ac..869d97fcf781 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -593,10 +593,12 @@ static int xgene_enet_reset(struct xgene_enet_pdata *pdata) if (!xgene_ring_mgr_init(pdata)) return -ENODEV; - clk_prepare_enable(pdata->clk); - clk_disable_unprepare(pdata->clk); - clk_prepare_enable(pdata->clk); - xgene_enet_ecc_init(pdata); + if (!efi_enabled(EFI_BOOT)) { + clk_prepare_enable(pdata->clk); + clk_disable_unprepare(pdata->clk); + clk_prepare_enable(pdata->clk); + xgene_enet_ecc_init(pdata); + } xgene_enet_config_ring_if_assoc(pdata); /* Enable auto-incr for scanning */ @@ -663,15 +665,20 @@ static int xgene_enet_phy_connect(struct net_device *ndev) struct phy_device *phy_dev; struct device *dev = &pdata->pdev->dev; - phy_np = of_parse_phandle(dev->of_node, "phy-handle", 0); - if (!phy_np) { - netdev_dbg(ndev, "No phy-handle found\n"); - return -ENODEV; + if (dev->of_node) { + phy_np = of_parse_phandle(dev->of_node, "phy-handle", 0); + if (!phy_np) { + netdev_dbg(ndev, "No phy-handle found in DT\n"); + return -ENODEV; + } + pdata->phy_dev = of_phy_find_device(phy_np); } - phy_dev = of_phy_connect(ndev, phy_np, &xgene_enet_adjust_link, - 0, pdata->phy_mode); - if (!phy_dev) { + 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; } @@ -681,32 +688,71 @@ static int xgene_enet_phy_connect(struct net_device *ndev) ~SUPPORTED_100baseT_Half & ~SUPPORTED_1000baseT_Half; phy_dev->advertising = phy_dev->supported; - pdata->phy_dev = phy_dev; return 0; } -int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata) +static int xgene_mdiobus_register(struct xgene_enet_pdata *pdata, + struct mii_bus *mdio) { - struct net_device *ndev = pdata->ndev; struct device *dev = &pdata->pdev->dev; + struct net_device *ndev = pdata->ndev; + struct phy_device *phy; struct device_node *child_np; struct device_node *mdio_np = NULL; - struct mii_bus *mdio_bus; int ret; + u32 phy_id; + + if (dev->of_node) { + for_each_child_of_node(dev->of_node, child_np) { + if (of_device_is_compatible(child_np, + "apm,xgene-mdio")) { + mdio_np = child_np; + break; + } + } - for_each_child_of_node(dev->of_node, child_np) { - if (of_device_is_compatible(child_np, "apm,xgene-mdio")) { - mdio_np = child_np; - break; + if (!mdio_np) { + netdev_dbg(ndev, "No mdio node in the dts\n"); + return -ENXIO; } - } - if (!mdio_np) { - netdev_dbg(ndev, "No mdio node in the dts\n"); - return -ENXIO; + return of_mdiobus_register(mdio, mdio_np); } + /* Mask out all PHYs from auto probing. */ + mdio->phy_mask = ~0; + + /* Register the MDIO bus */ + ret = mdiobus_register(mdio); + if (ret) + return ret; + + ret = device_property_read_u32(dev, "phy-channel", &phy_id); + if (ret) + ret = device_property_read_u32(dev, "phy-addr", &phy_id); + if (ret) + return -EINVAL; + + phy = get_phy_device(mdio, phy_id, true); + if (!phy || IS_ERR(phy)) + return -EIO; + + ret = phy_device_register(phy); + if (ret) + phy_device_free(phy); + else + pdata->phy_dev = phy; + + return ret; +} + +int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata) +{ + struct net_device *ndev = pdata->ndev; + struct mii_bus *mdio_bus; + int ret; + mdio_bus = mdiobus_alloc(); if (!mdio_bus) return -ENOMEM; @@ -720,7 +766,7 @@ int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata) mdio_bus->priv = pdata; mdio_bus->parent = &ndev->dev; - ret = of_mdiobus_register(mdio_bus, mdio_np); + ret = xgene_mdiobus_register(pdata, mdio_bus); if (ret) { netdev_err(ndev, "Failed to register MDIO bus\n"); mdiobus_free(mdio_bus); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index 83a50280bb70..44b15373d6b3 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -24,6 +24,10 @@ #include "xgene_enet_sgmac.h" #include "xgene_enet_xgmac.h" +#define RES_ENET_CSR 0 +#define RES_RING_CSR 1 +#define RES_RING_CMD 2 + static void xgene_enet_init_bufpool(struct xgene_enet_desc_ring *buf_pool) { struct xgene_enet_raw_desc16 *raw_desc; @@ -369,6 +373,8 @@ static int xgene_enet_process_ring(struct xgene_enet_desc_ring *ring, if (unlikely(xgene_enet_is_desc_slot_empty(raw_desc))) break; + /* read fpqnum field after dataaddr field */ + dma_rmb(); if (is_rx_desc(raw_desc)) ret = xgene_enet_rx_frame(ring, raw_desc); else @@ -746,6 +752,41 @@ static const struct net_device_ops xgene_ndev_ops = { .ndo_set_mac_address = xgene_enet_set_mac_address, }; +static int xgene_get_mac_address(struct device *dev, + unsigned char *addr) +{ + int ret; + + ret = device_property_read_u8_array(dev, "local-mac-address", addr, 6); + if (ret) + ret = device_property_read_u8_array(dev, "mac-address", + addr, 6); + if (ret) + return -ENODEV; + + return ETH_ALEN; +} + +static int xgene_get_phy_mode(struct device *dev) +{ + int i, ret; + char *modestr; + + ret = device_property_read_string(dev, "phy-connection-type", + (const char **)&modestr); + if (ret) + ret = device_property_read_string(dev, "phy-mode", + (const char **)&modestr); + if (ret) + return -ENODEV; + + for (i = 0; i < PHY_INTERFACE_MODE_MAX; i++) { + if (!strcasecmp(modestr, phy_modes(i))) + return i; + } + return -ENODEV; +} + static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) { struct platform_device *pdev; @@ -753,32 +794,45 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) struct device *dev; struct resource *res; void __iomem *base_addr; - const char *mac; int ret; pdev = pdata->pdev; dev = &pdev->dev; ndev = pdata->ndev; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "enet_csr"); - pdata->base_addr = devm_ioremap_resource(dev, res); - if (IS_ERR(pdata->base_addr)) { + res = platform_get_resource(pdev, IORESOURCE_MEM, RES_ENET_CSR); + if (!res) { + dev_err(dev, "Resource enet_csr not defined\n"); + return -ENODEV; + } + pdata->base_addr = devm_ioremap(dev, res->start, resource_size(res)); + if (!pdata->base_addr) { dev_err(dev, "Unable to retrieve ENET Port CSR region\n"); - return PTR_ERR(pdata->base_addr); + return -ENOMEM; } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ring_csr"); - pdata->ring_csr_addr = devm_ioremap_resource(dev, res); - if (IS_ERR(pdata->ring_csr_addr)) { + res = platform_get_resource(pdev, IORESOURCE_MEM, RES_RING_CSR); + if (!res) { + dev_err(dev, "Resource ring_csr not defined\n"); + return -ENODEV; + } + pdata->ring_csr_addr = devm_ioremap(dev, res->start, + resource_size(res)); + if (!pdata->ring_csr_addr) { dev_err(dev, "Unable to retrieve ENET Ring CSR region\n"); - return PTR_ERR(pdata->ring_csr_addr); + return -ENOMEM; } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ring_cmd"); - pdata->ring_cmd_addr = devm_ioremap_resource(dev, res); - if (IS_ERR(pdata->ring_cmd_addr)) { + res = platform_get_resource(pdev, IORESOURCE_MEM, RES_RING_CMD); + if (!res) { + dev_err(dev, "Resource ring_cmd not defined\n"); + return -ENODEV; + } + pdata->ring_cmd_addr = devm_ioremap(dev, res->start, + resource_size(res)); + if (!pdata->ring_cmd_addr) { dev_err(dev, "Unable to retrieve ENET Ring command region\n"); - return PTR_ERR(pdata->ring_cmd_addr); + return -ENOMEM; } ret = platform_get_irq(pdev, 0); @@ -789,14 +843,12 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) } pdata->rx_irq = ret; - mac = of_get_mac_address(dev->of_node); - if (mac) - memcpy(ndev->dev_addr, mac, ndev->addr_len); - else + if (xgene_get_mac_address(dev, ndev->dev_addr) != ETH_ALEN) eth_hw_addr_random(ndev); + memcpy(ndev->perm_addr, ndev->dev_addr, ndev->addr_len); - pdata->phy_mode = of_get_phy_mode(pdev->dev.of_node); + pdata->phy_mode = xgene_get_phy_mode(dev); if (pdata->phy_mode < 0) { dev_err(dev, "Unable to get phy-connection-type\n"); return pdata->phy_mode; @@ -809,11 +861,9 @@ static int xgene_enet_get_resources(struct xgene_enet_pdata *pdata) } pdata->clk = devm_clk_get(&pdev->dev, NULL); - ret = IS_ERR(pdata->clk); if (IS_ERR(pdata->clk)) { - dev_err(&pdev->dev, "can't get clock\n"); - ret = PTR_ERR(pdata->clk); - return ret; + /* Firmware may have set up the clock already. */ + pdata->clk = NULL; } base_addr = pdata->base_addr; @@ -924,7 +974,7 @@ static int xgene_enet_probe(struct platform_device *pdev) goto err; } - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(64)); if (ret) { netdev_err(ndev, "No usable DMA configuration\n"); goto err; @@ -972,17 +1022,26 @@ static int xgene_enet_remove(struct platform_device *pdev) return 0; } -static struct of_device_id xgene_enet_match[] = { +#ifdef CONFIG_ACPI +static const struct acpi_device_id xgene_enet_acpi_match[] = { + { "APMC0D05", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, xgene_enet_acpi_match); +#endif + +static struct of_device_id xgene_enet_of_match[] = { {.compatible = "apm,xgene-enet",}, {}, }; -MODULE_DEVICE_TABLE(of, xgene_enet_match); +MODULE_DEVICE_TABLE(of, xgene_enet_of_match); static struct platform_driver xgene_enet_driver = { .driver = { .name = "xgene-enet", - .of_match_table = xgene_enet_match, + .of_match_table = of_match_ptr(xgene_enet_of_match), + .acpi_match_table = ACPI_PTR(xgene_enet_acpi_match), }, .probe = xgene_enet_probe, .remove = xgene_enet_remove, diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h index f9958fae6ffd..c2d465c3db66 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.h +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.h @@ -22,7 +22,10 @@ #ifndef __XGENE_ENET_MAIN_H__ #define __XGENE_ENET_MAIN_H__ +#include <linux/acpi.h> #include <linux/clk.h> +#include <linux/efi.h> +#include <linux/io.h> #include <linux/of_platform.h> #include <linux/of_net.h> #include <linux/of_mdio.h> diff --git a/drivers/net/ethernet/atheros/alx/main.c b/drivers/net/ethernet/atheros/alx/main.c index e398eda07298..c8af3ce3ea38 100644 --- a/drivers/net/ethernet/atheros/alx/main.c +++ b/drivers/net/ethernet/atheros/alx/main.c @@ -184,15 +184,16 @@ static void alx_schedule_reset(struct alx_priv *alx) schedule_work(&alx->reset_wk); } -static bool alx_clean_rx_irq(struct alx_priv *alx, int budget) +static int alx_clean_rx_irq(struct alx_priv *alx, int budget) { struct alx_rx_queue *rxq = &alx->rxq; struct alx_rrd *rrd; struct alx_buffer *rxb; struct sk_buff *skb; u16 length, rfd_cleaned = 0; + int work = 0; - while (budget > 0) { + while (work < budget) { rrd = &rxq->rrd[rxq->rrd_read_idx]; if (!(rrd->word3 & cpu_to_le32(1 << RRD_UPDATED_SHIFT))) break; @@ -203,7 +204,7 @@ static bool alx_clean_rx_irq(struct alx_priv *alx, int budget) ALX_GET_FIELD(le32_to_cpu(rrd->word0), RRD_NOR) != 1) { alx_schedule_reset(alx); - return 0; + return work; } rxb = &rxq->bufs[rxq->read_idx]; @@ -243,7 +244,7 @@ static bool alx_clean_rx_irq(struct alx_priv *alx, int budget) } napi_gro_receive(&alx->napi, skb); - budget--; + work++; next_pkt: if (++rxq->read_idx == alx->rx_ringsz) @@ -258,21 +259,22 @@ next_pkt: if (rfd_cleaned) alx_refill_rx_ring(alx, GFP_ATOMIC); - return budget > 0; + return work; } static int alx_poll(struct napi_struct *napi, int budget) { struct alx_priv *alx = container_of(napi, struct alx_priv, napi); struct alx_hw *hw = &alx->hw; - bool complete = true; unsigned long flags; + bool tx_complete; + int work; - complete = alx_clean_tx_irq(alx) && - alx_clean_rx_irq(alx, budget); + tx_complete = alx_clean_tx_irq(alx); + work = alx_clean_rx_irq(alx, budget); - if (!complete) - return 1; + if (!tx_complete || work == budget) + return budget; napi_complete(&alx->napi); @@ -284,7 +286,7 @@ static int alx_poll(struct napi_struct *napi, int budget) alx_post_write(hw); - return 0; + return work; } static irqreturn_t alx_intr_handle(struct alx_priv *alx, u32 intr) diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c index c9946c6c119e..587f63e87588 100644 --- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c +++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c @@ -2235,8 +2235,8 @@ static netdev_tx_t atl1c_xmit_frame(struct sk_buff *skb, return NETDEV_TX_OK; } - if (unlikely(vlan_tx_tag_present(skb))) { - u16 vlan = vlan_tx_tag_get(skb); + if (unlikely(skb_vlan_tag_present(skb))) { + u16 vlan = skb_vlan_tag_get(skb); __le16 tag; vlan = cpu_to_le16(vlan); diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c index 2326579f9454..59a03a193e83 100644 --- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c +++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c @@ -1892,8 +1892,8 @@ static netdev_tx_t atl1e_xmit_frame(struct sk_buff *skb, tpd = atl1e_get_tpd(adapter); - if (vlan_tx_tag_present(skb)) { - u16 vlan_tag = vlan_tx_tag_get(skb); + if (skb_vlan_tag_present(skb)) { + u16 vlan_tag = skb_vlan_tag_get(skb); u16 atl1e_vlan_tag; tpd->word3 |= 1 << TPD_INS_VL_TAG_SHIFT; @@ -2373,9 +2373,8 @@ static int atl1e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netif_napi_add(netdev, &adapter->napi, atl1e_clean, 64); - init_timer(&adapter->phy_config_timer); - adapter->phy_config_timer.function = atl1e_phy_config; - adapter->phy_config_timer.data = (unsigned long) adapter; + setup_timer(&adapter->phy_config_timer, atl1e_phy_config, + (unsigned long)adapter); /* get user settings */ atl1e_check_options(adapter); diff --git a/drivers/net/ethernet/atheros/atlx/atl1.c b/drivers/net/ethernet/atheros/atlx/atl1.c index 2c8f398aeda9..eca1d113fee1 100644 --- a/drivers/net/ethernet/atheros/atlx/atl1.c +++ b/drivers/net/ethernet/atheros/atlx/atl1.c @@ -2415,8 +2415,8 @@ static netdev_tx_t atl1_xmit_frame(struct sk_buff *skb, (u16) atomic_read(&tpd_ring->next_to_use)); memset(ptpd, 0, sizeof(struct tx_packet_desc)); - if (vlan_tx_tag_present(skb)) { - vlan_tag = vlan_tx_tag_get(skb); + if (skb_vlan_tag_present(skb)) { + vlan_tag = skb_vlan_tag_get(skb); vlan_tag = (vlan_tag << 4) | (vlan_tag >> 13) | ((vlan_tag >> 9) & 0x8); ptpd->word3 |= 1 << TPD_INS_VL_TAG_SHIFT; diff --git a/drivers/net/ethernet/atheros/atlx/atl2.c b/drivers/net/ethernet/atheros/atlx/atl2.c index 84a09e8ddd9c..46a535318c7a 100644 --- a/drivers/net/ethernet/atheros/atlx/atl2.c +++ b/drivers/net/ethernet/atheros/atlx/atl2.c @@ -887,8 +887,8 @@ static netdev_tx_t atl2_xmit_frame(struct sk_buff *skb, offset = ((u32)(skb->len-copy_len + 3) & ~3); } #ifdef NETIF_F_HW_VLAN_CTAG_TX - if (vlan_tx_tag_present(skb)) { - u16 vlan_tag = vlan_tx_tag_get(skb); + if (skb_vlan_tag_present(skb)) { + u16 vlan_tag = skb_vlan_tag_get(skb); vlan_tag = (vlan_tag << 4) | (vlan_tag >> 13) | ((vlan_tag >> 9) & 0x8); @@ -1436,13 +1436,11 @@ static int atl2_probe(struct pci_dev *pdev, const struct pci_device_id *ent) atl2_check_options(adapter); - init_timer(&adapter->watchdog_timer); - adapter->watchdog_timer.function = atl2_watchdog; - adapter->watchdog_timer.data = (unsigned long) adapter; + setup_timer(&adapter->watchdog_timer, atl2_watchdog, + (unsigned long)adapter); - init_timer(&adapter->phy_config_timer); - adapter->phy_config_timer.function = atl2_phy_config; - adapter->phy_config_timer.data = (unsigned long) adapter; + setup_timer(&adapter->phy_config_timer, atl2_phy_config, + (unsigned long)adapter); INIT_WORK(&adapter->reset_task, atl2_reset_task); INIT_WORK(&adapter->link_chg_task, atl2_link_chg_task); diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 05c6af6c418f..3007d95fbb9f 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -1167,10 +1167,10 @@ static int bgmac_poll(struct napi_struct *napi, int weight) bgmac->int_status = 0; } - if (handled < weight) + if (handled < weight) { napi_complete(napi); - - bgmac_chip_intrs_on(bgmac); + bgmac_chip_intrs_on(bgmac); + } return handled; } @@ -1515,6 +1515,8 @@ static int bgmac_probe(struct bcma_device *core) if (core->bus->sprom.boardflags_lo & BGMAC_BFL_ENETADM) bgmac_warn(bgmac, "Support for ADMtek ethernet switch not implemented\n"); + netif_napi_add(net_dev, &bgmac->napi, bgmac_poll, BGMAC_WEIGHT); + err = bgmac_mii_register(bgmac); if (err) { bgmac_err(bgmac, "Cannot register MDIO\n"); @@ -1529,8 +1531,6 @@ static int bgmac_probe(struct bcma_device *core) netif_carrier_off(net_dev); - netif_napi_add(net_dev, &bgmac->napi, bgmac_poll, BGMAC_WEIGHT); - return 0; err_mii_unregister: @@ -1549,9 +1549,9 @@ static void bgmac_remove(struct bcma_device *core) { struct bgmac *bgmac = bcma_get_drvdata(core); - netif_napi_del(&bgmac->napi); unregister_netdev(bgmac->net_dev); bgmac_mii_unregister(bgmac); + netif_napi_del(&bgmac->napi); bgmac_dma_free(bgmac); bcma_set_drvdata(core, NULL); free_netdev(bgmac->net_dev); diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index 823d01c5684c..02bf0b86995b 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -6597,9 +6597,9 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev) vlan_tag_flags |= TX_BD_FLAGS_TCP_UDP_CKSUM; } - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { vlan_tag_flags |= - (TX_BD_FLAGS_VLAN_TAG | (vlan_tx_tag_get(skb) << 16)); + (TX_BD_FLAGS_VLAN_TAG | (skb_vlan_tag_get(skb) << 16)); } if ((mss = skb_shinfo(skb)->gso_size)) { diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index c3a6072134f5..756053c028be 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -22,7 +22,7 @@ #include <linux/ptp_clock_kernel.h> #include <linux/net_tstamp.h> -#include <linux/clocksource.h> +#include <linux/timecounter.h> /* compilation time flags */ @@ -1138,12 +1138,8 @@ struct bnx2x_port { u32 link_config[LINK_CONFIG_SIZE]; u32 supported[LINK_CONFIG_SIZE]; -/* link settings - missing defines */ -#define SUPPORTED_2500baseX_Full (1 << 15) u32 advertising[LINK_CONFIG_SIZE]; -/* link settings - missing defines */ -#define ADVERTISED_2500baseX_Full (1 << 15) u32 phy_addr; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 1d1147c93d59..0a9faa134a9a 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -3175,7 +3175,7 @@ static int bnx2x_poll(struct napi_struct *napi, int budget) } #endif if (!bnx2x_fp_lock_napi(fp)) - return work_done; + return budget; for_each_cos_in_tx_queue(fp, cos) if (bnx2x_tx_queue_has_work(fp->txdata_ptr[cos])) @@ -3865,9 +3865,9 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) "sending pkt %u @%p next_idx %u bd %u @%p\n", pkt_prod, tx_buf, txdata->tx_pkt_prod, bd_prod, tx_start_bd); - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { tx_start_bd->vlan_or_ethertype = - cpu_to_le16(vlan_tx_tag_get(skb)); + cpu_to_le16(skb_vlan_tag_get(skb)); tx_start_bd->bd_flags.as_bitfield |= (X_ETH_OUTBAND_VLAN << ETH_TX_BD_FLAGS_VLAN_MODE_SHIFT); } else { diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 72eef9fc883e..7155e1d2c208 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -9169,7 +9169,7 @@ static void bnx2x_disable_ptp(struct bnx2x *bp) } /* Called during unload, to stop PTP-related stuff */ -void bnx2x_stop_ptp(struct bnx2x *bp) +static void bnx2x_stop_ptp(struct bnx2x *bp) { /* Cancel PTP work queue. Should be done after the Tx queues are * drained to prevent additional scheduling. @@ -13267,14 +13267,10 @@ static int bnx2x_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) static int bnx2x_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) { struct bnx2x *bp = container_of(ptp, struct bnx2x, ptp_clock_info); - u64 now; DP(BNX2X_MSG_PTP, "PTP adjtime called, delta = %llx\n", delta); - now = timecounter_read(&bp->timecounter); - now += delta; - /* Re-init the timecounter */ - timecounter_init(&bp->timecounter, &bp->cyclecounter, now); + timecounter_adjtime(&bp->timecounter, delta); return 0; } @@ -13322,7 +13318,7 @@ static int bnx2x_ptp_enable(struct ptp_clock_info *ptp, return -ENOTSUPP; } -void bnx2x_register_phc(struct bnx2x *bp) +static void bnx2x_register_phc(struct bnx2x *bp) { /* Fill the ptp_clock_info struct and register PTP clock*/ bp->ptp_clock_info.owner = THIS_MODULE; @@ -14614,7 +14610,7 @@ static void bnx2x_init_cyclecounter(struct bnx2x *bp) { memset(&bp->cyclecounter, 0, sizeof(bp->cyclecounter)); bp->cyclecounter.read = bnx2x_cyclecounter_read; - bp->cyclecounter.mask = CLOCKSOURCE_MASK(64); + bp->cyclecounter.mask = CYCLECOUNTER_MASK(64); bp->cyclecounter.shift = 1; bp->cyclecounter.mult = 1; } @@ -14639,7 +14635,7 @@ static int bnx2x_send_reset_timesync_ramrod(struct bnx2x *bp) return bnx2x_func_state_change(bp, &func_params); } -int bnx2x_enable_ptp_packets(struct bnx2x *bp) +static int bnx2x_enable_ptp_packets(struct bnx2x *bp) { struct bnx2x_queue_state_params q_params; int rc, i; diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 553dcd8a9df2..615a6dbde047 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -7413,6 +7413,8 @@ static inline void tg3_netif_start(struct tg3 *tp) } static void tg3_irq_quiesce(struct tg3 *tp) + __releases(tp->lock) + __acquires(tp->lock) { int i; @@ -7421,8 +7423,12 @@ static void tg3_irq_quiesce(struct tg3 *tp) tp->irq_sync = 1; smp_mb(); + spin_unlock_bh(&tp->lock); + for (i = 0; i < tp->irq_cnt; i++) synchronize_irq(tp->napi[i].irq_vec); + + spin_lock_bh(&tp->lock); } /* Fully shutdown all tg3 driver activity elsewhere in the system. @@ -8002,9 +8008,9 @@ static netdev_tx_t tg3_start_xmit(struct sk_buff *skb, struct net_device *dev) !mss && skb->len > VLAN_ETH_FRAME_LEN) base_flags |= TXD_FLAG_JMB_PKT; - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { base_flags |= TXD_FLAG_VLAN; - vlan = vlan_tx_tag_get(skb); + vlan = skb_vlan_tag_get(skb); } if ((unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) && @@ -9018,6 +9024,8 @@ static void tg3_restore_clk(struct tg3 *tp) /* tp->lock is held. */ static int tg3_chip_reset(struct tg3 *tp) + __releases(tp->lock) + __acquires(tp->lock) { u32 val; void (*write_op)(struct tg3 *, u32, u32); @@ -9073,9 +9081,13 @@ static int tg3_chip_reset(struct tg3 *tp) } smp_mb(); + tg3_full_unlock(tp); + for (i = 0; i < tp->irq_cnt; i++) synchronize_irq(tp->napi[i].irq_vec); + tg3_full_lock(tp, 0); + if (tg3_asic_rev(tp) == ASIC_REV_57780) { val = tr32(TG3_PCIE_LNKCTL) & ~TG3_PCIE_LNKCTL_L1_PLL_PD_EN; tw32(TG3_PCIE_LNKCTL, val | TG3_PCIE_LNKCTL_L1_PLL_PD_DIS); @@ -10903,11 +10915,13 @@ static void tg3_timer(unsigned long __opaque) { struct tg3 *tp = (struct tg3 *) __opaque; - if (tp->irq_sync || tg3_flag(tp, RESET_TASK_PENDING)) - goto restart_timer; - spin_lock(&tp->lock); + if (tp->irq_sync || tg3_flag(tp, RESET_TASK_PENDING)) { + spin_unlock(&tp->lock); + goto restart_timer; + } + if (tg3_asic_rev(tp) == ASIC_REV_5717 || tg3_flag(tp, 57765_CLASS)) tg3_chk_missed_msi(tp); @@ -11101,11 +11115,13 @@ static void tg3_reset_task(struct work_struct *work) struct tg3 *tp = container_of(work, struct tg3, reset_task); int err; + rtnl_lock(); tg3_full_lock(tp, 0); if (!netif_running(tp->dev)) { tg3_flag_clear(tp, RESET_TASK_PENDING); tg3_full_unlock(tp); + rtnl_unlock(); return; } @@ -11138,6 +11154,7 @@ out: tg3_phy_start(tp); tg3_flag_clear(tp, RESET_TASK_PENDING); + rtnl_unlock(); } static int tg3_request_irq(struct tg3 *tp, int irq_num) @@ -11556,11 +11573,7 @@ static int tg3_start(struct tg3 *tp, bool reset_phy, bool test_irq, tg3_flag_set(tp, INIT_COMPLETE); tg3_enable_ints(tp); - if (init) - tg3_ptp_init(tp); - else - tg3_ptp_resume(tp); - + tg3_ptp_resume(tp); tg3_full_unlock(tp); @@ -11681,13 +11694,6 @@ static int tg3_open(struct net_device *dev) pci_set_power_state(tp->pdev, PCI_D3hot); } - if (tg3_flag(tp, PTP_CAPABLE)) { - tp->ptp_clock = ptp_clock_register(&tp->ptp_info, - &tp->pdev->dev); - if (IS_ERR(tp->ptp_clock)) - tp->ptp_clock = NULL; - } - return err; } @@ -11701,8 +11707,6 @@ static int tg3_close(struct net_device *dev) return -EAGAIN; } - tg3_ptp_fini(tp); - tg3_stop(tp); /* Clear stats across close / open calls */ @@ -17880,6 +17884,14 @@ static int tg3_init_one(struct pci_dev *pdev, goto err_out_apeunmap; } + if (tg3_flag(tp, PTP_CAPABLE)) { + tg3_ptp_init(tp); + tp->ptp_clock = ptp_clock_register(&tp->ptp_info, + &tp->pdev->dev); + if (IS_ERR(tp->ptp_clock)) + tp->ptp_clock = NULL; + } + netdev_info(dev, "Tigon3 [partno(%s) rev %04x] (%s) MAC address %pM\n", tp->board_part_number, tg3_chip_rev_id(tp), @@ -17955,6 +17967,8 @@ static void tg3_remove_one(struct pci_dev *pdev) if (dev) { struct tg3 *tp = netdev_priv(dev); + tg3_ptp_fini(tp); + release_firmware(tp->fw); tg3_reset_task_cancel(tp); diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c index 323721838cf9..7714d7790089 100644 --- a/drivers/net/ethernet/brocade/bna/bnad.c +++ b/drivers/net/ethernet/brocade/bna/bnad.c @@ -2824,8 +2824,8 @@ bnad_txq_wi_prepare(struct bnad *bnad, struct bna_tcb *tcb, u32 gso_size; u16 vlan_tag = 0; - if (vlan_tx_tag_present(skb)) { - vlan_tag = (u16)vlan_tx_tag_get(skb); + if (skb_vlan_tag_present(skb)) { + vlan_tag = (u16)skb_vlan_tag_get(skb); flags |= (BNA_TXQ_WI_CF_INS_PRIO | BNA_TXQ_WI_CF_INS_VLAN); } if (test_bit(BNAD_RF_CEE_RUNNING, &bnad->run_flags)) { diff --git a/drivers/net/ethernet/cadence/at91_ether.c b/drivers/net/ethernet/cadence/at91_ether.c index 55eb7f2af2b4..7ef55f5fa664 100644 --- a/drivers/net/ethernet/cadence/at91_ether.c +++ b/drivers/net/ethernet/cadence/at91_ether.c @@ -340,7 +340,7 @@ static int __init at91ether_probe(struct platform_device *pdev) res = PTR_ERR(lp->pclk); goto err_free_dev; } - clk_enable(lp->pclk); + clk_prepare_enable(lp->pclk); lp->hclk = ERR_PTR(-ENOENT); lp->tx_clk = ERR_PTR(-ENOENT); @@ -406,7 +406,7 @@ static int __init at91ether_probe(struct platform_device *pdev) err_out_unregister_netdev: unregister_netdev(dev); err_disable_clock: - clk_disable(lp->pclk); + clk_disable_unprepare(lp->pclk); err_free_dev: free_netdev(dev); return res; @@ -424,7 +424,7 @@ static int at91ether_remove(struct platform_device *pdev) kfree(lp->mii_bus->irq); mdiobus_free(lp->mii_bus); unregister_netdev(dev); - clk_disable(lp->pclk); + clk_disable_unprepare(lp->pclk); free_netdev(dev); return 0; @@ -440,7 +440,7 @@ static int at91ether_suspend(struct platform_device *pdev, pm_message_t mesg) netif_stop_queue(net_dev); netif_device_detach(net_dev); - clk_disable(lp->pclk); + clk_disable_unprepare(lp->pclk); } return 0; } @@ -451,7 +451,7 @@ static int at91ether_resume(struct platform_device *pdev) struct macb *lp = netdev_priv(net_dev); if (netif_running(net_dev)) { - clk_enable(lp->pclk); + clk_prepare_enable(lp->pclk); netif_device_attach(net_dev); netif_start_queue(net_dev); diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c index 3767271c7667..ad76b8e35a00 100644 --- a/drivers/net/ethernet/cadence/macb.c +++ b/drivers/net/ethernet/cadence/macb.c @@ -1691,7 +1691,7 @@ static int hash_get_index(__u8 *addr) for (j = 0; j < 6; j++) { for (i = 0, bitval = 0; i < 8; i++) - bitval ^= hash_bit_value(i*6 + j, addr); + bitval ^= hash_bit_value(i * 6 + j, addr); hash_index |= (bitval << j); } @@ -1827,12 +1827,23 @@ static int macb_close(struct net_device *dev) static void gem_update_stats(struct macb *bp) { - u32 __iomem *reg = bp->regs + GEM_OTX; + int i; u32 *p = &bp->hw_stats.gem.tx_octets_31_0; - u32 *end = &bp->hw_stats.gem.rx_udp_checksum_errors + 1; - for (; p < end; p++, reg++) - *p += __raw_readl(reg); + for (i = 0; i < GEM_STATS_LEN; ++i, ++p) { + u32 offset = gem_statistics[i].offset; + u64 val = __raw_readl(bp->regs + offset); + + bp->ethtool_stats[i] += val; + *p += val; + + if (offset == GEM_OCTTXL || offset == GEM_OCTRXL) { + /* Add GEM_OCTTXH, GEM_OCTRXH */ + val = __raw_readl(bp->regs + offset + 4); + bp->ethtool_stats[i] += ((u64)val) << 32; + *(++p) += val; + } + } } static struct net_device_stats *gem_get_stats(struct macb *bp) @@ -1873,6 +1884,39 @@ static struct net_device_stats *gem_get_stats(struct macb *bp) return nstat; } +static void gem_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct macb *bp; + + bp = netdev_priv(dev); + gem_update_stats(bp); + memcpy(data, &bp->ethtool_stats, sizeof(u64) * GEM_STATS_LEN); +} + +static int gem_get_sset_count(struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return GEM_STATS_LEN; + default: + return -EOPNOTSUPP; + } +} + +static void gem_get_ethtool_strings(struct net_device *dev, u32 sset, u8 *p) +{ + int i; + + switch (sset) { + case ETH_SS_STATS: + for (i = 0; i < GEM_STATS_LEN; i++, p += ETH_GSTRING_LEN) + memcpy(p, gem_statistics[i].stat_string, + ETH_GSTRING_LEN); + break; + } +} + struct net_device_stats *macb_get_stats(struct net_device *dev) { struct macb *bp = netdev_priv(dev); @@ -1991,6 +2035,18 @@ const struct ethtool_ops macb_ethtool_ops = { }; EXPORT_SYMBOL_GPL(macb_ethtool_ops); +static const struct ethtool_ops gem_ethtool_ops = { + .get_settings = macb_get_settings, + .set_settings = macb_set_settings, + .get_regs_len = macb_get_regs_len, + .get_regs = macb_get_regs, + .get_link = ethtool_op_get_link, + .get_ts_info = ethtool_op_get_ts_info, + .get_ethtool_stats = gem_get_ethtool_stats, + .get_strings = gem_get_ethtool_strings, + .get_sset_count = gem_get_sset_count, +}; + int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct macb *bp = netdev_priv(dev); @@ -2148,7 +2204,7 @@ static void macb_probe_queues(void __iomem *mem, (*num_queues)++; } -static int __init macb_probe(struct platform_device *pdev) +static int macb_probe(struct platform_device *pdev) { struct macb_platform_data *pdata; struct resource *regs; @@ -2278,7 +2334,6 @@ static int __init macb_probe(struct platform_device *pdev) dev->netdev_ops = &macb_netdev_ops; netif_napi_add(dev, &bp->napi, macb_poll, 64); - dev->ethtool_ops = &macb_ethtool_ops; dev->base_addr = regs->start; @@ -2292,12 +2347,14 @@ static int __init macb_probe(struct platform_device *pdev) bp->macbgem_ops.mog_free_rx_buffers = gem_free_rx_buffers; bp->macbgem_ops.mog_init_rings = gem_init_rings; bp->macbgem_ops.mog_rx = gem_rx; + dev->ethtool_ops = &gem_ethtool_ops; } else { bp->max_tx_length = MACB_MAX_TX_LEN; bp->macbgem_ops.mog_alloc_rx_buffers = macb_alloc_rx_buffers; bp->macbgem_ops.mog_free_rx_buffers = macb_free_rx_buffers; bp->macbgem_ops.mog_init_rings = macb_init_rings; bp->macbgem_ops.mog_rx = macb_rx; + dev->ethtool_ops = &macb_ethtool_ops; } /* Set features */ @@ -2386,7 +2443,7 @@ err_out: return err; } -static int __exit macb_remove(struct platform_device *pdev) +static int macb_remove(struct platform_device *pdev) { struct net_device *dev; struct macb *bp; @@ -2411,8 +2468,7 @@ static int __exit macb_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int macb_suspend(struct device *dev) +static int __maybe_unused macb_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct net_device *netdev = platform_get_drvdata(pdev); @@ -2429,7 +2485,7 @@ static int macb_suspend(struct device *dev) return 0; } -static int macb_resume(struct device *dev) +static int __maybe_unused macb_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct net_device *netdev = platform_get_drvdata(pdev); @@ -2444,12 +2500,12 @@ static int macb_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(macb_pm_ops, macb_suspend, macb_resume); static struct platform_driver macb_driver = { - .remove = __exit_p(macb_remove), + .probe = macb_probe, + .remove = macb_remove, .driver = { .name = "macb", .of_match_table = of_match_ptr(macb_dt_ids), @@ -2457,7 +2513,7 @@ static struct platform_driver macb_driver = { }, }; -module_platform_driver_probe(macb_driver, macb_probe); +module_platform_driver(macb_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Cadence MACB/GEM Ethernet driver"); diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index 084191b6fad2..31dc080f2437 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -15,263 +15,309 @@ #define MACB_MAX_QUEUES 8 /* MACB register offsets */ -#define MACB_NCR 0x0000 -#define MACB_NCFGR 0x0004 -#define MACB_NSR 0x0008 -#define MACB_TAR 0x000c /* AT91RM9200 only */ -#define MACB_TCR 0x0010 /* AT91RM9200 only */ -#define MACB_TSR 0x0014 -#define MACB_RBQP 0x0018 -#define MACB_TBQP 0x001c -#define MACB_RSR 0x0020 -#define MACB_ISR 0x0024 -#define MACB_IER 0x0028 -#define MACB_IDR 0x002c -#define MACB_IMR 0x0030 -#define MACB_MAN 0x0034 -#define MACB_PTR 0x0038 -#define MACB_PFR 0x003c -#define MACB_FTO 0x0040 -#define MACB_SCF 0x0044 -#define MACB_MCF 0x0048 -#define MACB_FRO 0x004c -#define MACB_FCSE 0x0050 -#define MACB_ALE 0x0054 -#define MACB_DTF 0x0058 -#define MACB_LCOL 0x005c -#define MACB_EXCOL 0x0060 -#define MACB_TUND 0x0064 -#define MACB_CSE 0x0068 -#define MACB_RRE 0x006c -#define MACB_ROVR 0x0070 -#define MACB_RSE 0x0074 -#define MACB_ELE 0x0078 -#define MACB_RJA 0x007c -#define MACB_USF 0x0080 -#define MACB_STE 0x0084 -#define MACB_RLE 0x0088 -#define MACB_TPF 0x008c -#define MACB_HRB 0x0090 -#define MACB_HRT 0x0094 -#define MACB_SA1B 0x0098 -#define MACB_SA1T 0x009c -#define MACB_SA2B 0x00a0 -#define MACB_SA2T 0x00a4 -#define MACB_SA3B 0x00a8 -#define MACB_SA3T 0x00ac -#define MACB_SA4B 0x00b0 -#define MACB_SA4T 0x00b4 -#define MACB_TID 0x00b8 -#define MACB_TPQ 0x00bc -#define MACB_USRIO 0x00c0 -#define MACB_WOL 0x00c4 -#define MACB_MID 0x00fc +#define MACB_NCR 0x0000 /* Network Control */ +#define MACB_NCFGR 0x0004 /* Network Config */ +#define MACB_NSR 0x0008 /* Network Status */ +#define MACB_TAR 0x000c /* AT91RM9200 only */ +#define MACB_TCR 0x0010 /* AT91RM9200 only */ +#define MACB_TSR 0x0014 /* Transmit Status */ +#define MACB_RBQP 0x0018 /* RX Q Base Address */ +#define MACB_TBQP 0x001c /* TX Q Base Address */ +#define MACB_RSR 0x0020 /* Receive Status */ +#define MACB_ISR 0x0024 /* Interrupt Status */ +#define MACB_IER 0x0028 /* Interrupt Enable */ +#define MACB_IDR 0x002c /* Interrupt Disable */ +#define MACB_IMR 0x0030 /* Interrupt Mask */ +#define MACB_MAN 0x0034 /* PHY Maintenance */ +#define MACB_PTR 0x0038 +#define MACB_PFR 0x003c +#define MACB_FTO 0x0040 +#define MACB_SCF 0x0044 +#define MACB_MCF 0x0048 +#define MACB_FRO 0x004c +#define MACB_FCSE 0x0050 +#define MACB_ALE 0x0054 +#define MACB_DTF 0x0058 +#define MACB_LCOL 0x005c +#define MACB_EXCOL 0x0060 +#define MACB_TUND 0x0064 +#define MACB_CSE 0x0068 +#define MACB_RRE 0x006c +#define MACB_ROVR 0x0070 +#define MACB_RSE 0x0074 +#define MACB_ELE 0x0078 +#define MACB_RJA 0x007c +#define MACB_USF 0x0080 +#define MACB_STE 0x0084 +#define MACB_RLE 0x0088 +#define MACB_TPF 0x008c +#define MACB_HRB 0x0090 +#define MACB_HRT 0x0094 +#define MACB_SA1B 0x0098 +#define MACB_SA1T 0x009c +#define MACB_SA2B 0x00a0 +#define MACB_SA2T 0x00a4 +#define MACB_SA3B 0x00a8 +#define MACB_SA3T 0x00ac +#define MACB_SA4B 0x00b0 +#define MACB_SA4T 0x00b4 +#define MACB_TID 0x00b8 +#define MACB_TPQ 0x00bc +#define MACB_USRIO 0x00c0 +#define MACB_WOL 0x00c4 +#define MACB_MID 0x00fc /* GEM register offsets. */ -#define GEM_NCFGR 0x0004 -#define GEM_USRIO 0x000c -#define GEM_DMACFG 0x0010 -#define GEM_HRB 0x0080 -#define GEM_HRT 0x0084 -#define GEM_SA1B 0x0088 -#define GEM_SA1T 0x008C -#define GEM_SA2B 0x0090 -#define GEM_SA2T 0x0094 -#define GEM_SA3B 0x0098 -#define GEM_SA3T 0x009C -#define GEM_SA4B 0x00A0 -#define GEM_SA4T 0x00A4 -#define GEM_OTX 0x0100 -#define GEM_DCFG1 0x0280 -#define GEM_DCFG2 0x0284 -#define GEM_DCFG3 0x0288 -#define GEM_DCFG4 0x028c -#define GEM_DCFG5 0x0290 -#define GEM_DCFG6 0x0294 -#define GEM_DCFG7 0x0298 - -#define GEM_ISR(hw_q) (0x0400 + ((hw_q) << 2)) -#define GEM_TBQP(hw_q) (0x0440 + ((hw_q) << 2)) -#define GEM_RBQP(hw_q) (0x0480 + ((hw_q) << 2)) -#define GEM_IER(hw_q) (0x0600 + ((hw_q) << 2)) -#define GEM_IDR(hw_q) (0x0620 + ((hw_q) << 2)) -#define GEM_IMR(hw_q) (0x0640 + ((hw_q) << 2)) +#define GEM_NCFGR 0x0004 /* Network Config */ +#define GEM_USRIO 0x000c /* User IO */ +#define GEM_DMACFG 0x0010 /* DMA Configuration */ +#define GEM_HRB 0x0080 /* Hash Bottom */ +#define GEM_HRT 0x0084 /* Hash Top */ +#define GEM_SA1B 0x0088 /* Specific1 Bottom */ +#define GEM_SA1T 0x008C /* Specific1 Top */ +#define GEM_SA2B 0x0090 /* Specific2 Bottom */ +#define GEM_SA2T 0x0094 /* Specific2 Top */ +#define GEM_SA3B 0x0098 /* Specific3 Bottom */ +#define GEM_SA3T 0x009C /* Specific3 Top */ +#define GEM_SA4B 0x00A0 /* Specific4 Bottom */ +#define GEM_SA4T 0x00A4 /* Specific4 Top */ +#define GEM_OTX 0x0100 /* Octets transmitted */ +#define GEM_OCTTXL 0x0100 /* Octets transmitted [31:0] */ +#define GEM_OCTTXH 0x0104 /* Octets transmitted [47:32] */ +#define GEM_TXCNT 0x0108 /* Frames Transmitted counter */ +#define GEM_TXBCCNT 0x010c /* Broadcast Frames counter */ +#define GEM_TXMCCNT 0x0110 /* Multicast Frames counter */ +#define GEM_TXPAUSECNT 0x0114 /* Pause Frames Transmitted Counter */ +#define GEM_TX64CNT 0x0118 /* 64 byte Frames TX counter */ +#define GEM_TX65CNT 0x011c /* 65-127 byte Frames TX counter */ +#define GEM_TX128CNT 0x0120 /* 128-255 byte Frames TX counter */ +#define GEM_TX256CNT 0x0124 /* 256-511 byte Frames TX counter */ +#define GEM_TX512CNT 0x0128 /* 512-1023 byte Frames TX counter */ +#define GEM_TX1024CNT 0x012c /* 1024-1518 byte Frames TX counter */ +#define GEM_TX1519CNT 0x0130 /* 1519+ byte Frames TX counter */ +#define GEM_TXURUNCNT 0x0134 /* TX under run error counter */ +#define GEM_SNGLCOLLCNT 0x0138 /* Single Collision Frame Counter */ +#define GEM_MULTICOLLCNT 0x013c /* Multiple Collision Frame Counter */ +#define GEM_EXCESSCOLLCNT 0x0140 /* Excessive Collision Frame Counter */ +#define GEM_LATECOLLCNT 0x0144 /* Late Collision Frame Counter */ +#define GEM_TXDEFERCNT 0x0148 /* Deferred Transmission Frame Counter */ +#define GEM_TXCSENSECNT 0x014c /* Carrier Sense Error Counter */ +#define GEM_ORX 0x0150 /* Octets received */ +#define GEM_OCTRXL 0x0150 /* Octets received [31:0] */ +#define GEM_OCTRXH 0x0154 /* Octets received [47:32] */ +#define GEM_RXCNT 0x0158 /* Frames Received Counter */ +#define GEM_RXBROADCNT 0x015c /* Broadcast Frames Received Counter */ +#define GEM_RXMULTICNT 0x0160 /* Multicast Frames Received Counter */ +#define GEM_RXPAUSECNT 0x0164 /* Pause Frames Received Counter */ +#define GEM_RX64CNT 0x0168 /* 64 byte Frames RX Counter */ +#define GEM_RX65CNT 0x016c /* 65-127 byte Frames RX Counter */ +#define GEM_RX128CNT 0x0170 /* 128-255 byte Frames RX Counter */ +#define GEM_RX256CNT 0x0174 /* 256-511 byte Frames RX Counter */ +#define GEM_RX512CNT 0x0178 /* 512-1023 byte Frames RX Counter */ +#define GEM_RX1024CNT 0x017c /* 1024-1518 byte Frames RX Counter */ +#define GEM_RX1519CNT 0x0180 /* 1519+ byte Frames RX Counter */ +#define GEM_RXUNDRCNT 0x0184 /* Undersize Frames Received Counter */ +#define GEM_RXOVRCNT 0x0188 /* Oversize Frames Received Counter */ +#define GEM_RXJABCNT 0x018c /* Jabbers Received Counter */ +#define GEM_RXFCSCNT 0x0190 /* Frame Check Sequence Error Counter */ +#define GEM_RXLENGTHCNT 0x0194 /* Length Field Error Counter */ +#define GEM_RXSYMBCNT 0x0198 /* Symbol Error Counter */ +#define GEM_RXALIGNCNT 0x019c /* Alignment Error Counter */ +#define GEM_RXRESERRCNT 0x01a0 /* Receive Resource Error Counter */ +#define GEM_RXORCNT 0x01a4 /* Receive Overrun Counter */ +#define GEM_RXIPCCNT 0x01a8 /* IP header Checksum Error Counter */ +#define GEM_RXTCPCCNT 0x01ac /* TCP Checksum Error Counter */ +#define GEM_RXUDPCCNT 0x01b0 /* UDP Checksum Error Counter */ +#define GEM_DCFG1 0x0280 /* Design Config 1 */ +#define GEM_DCFG2 0x0284 /* Design Config 2 */ +#define GEM_DCFG3 0x0288 /* Design Config 3 */ +#define GEM_DCFG4 0x028c /* Design Config 4 */ +#define GEM_DCFG5 0x0290 /* Design Config 5 */ +#define GEM_DCFG6 0x0294 /* Design Config 6 */ +#define GEM_DCFG7 0x0298 /* Design Config 7 */ + +#define GEM_ISR(hw_q) (0x0400 + ((hw_q) << 2)) +#define GEM_TBQP(hw_q) (0x0440 + ((hw_q) << 2)) +#define GEM_RBQP(hw_q) (0x0480 + ((hw_q) << 2)) +#define GEM_IER(hw_q) (0x0600 + ((hw_q) << 2)) +#define GEM_IDR(hw_q) (0x0620 + ((hw_q) << 2)) +#define GEM_IMR(hw_q) (0x0640 + ((hw_q) << 2)) /* Bitfields in NCR */ -#define MACB_LB_OFFSET 0 -#define MACB_LB_SIZE 1 -#define MACB_LLB_OFFSET 1 -#define MACB_LLB_SIZE 1 -#define MACB_RE_OFFSET 2 -#define MACB_RE_SIZE 1 -#define MACB_TE_OFFSET 3 -#define MACB_TE_SIZE 1 -#define MACB_MPE_OFFSET 4 -#define MACB_MPE_SIZE 1 -#define MACB_CLRSTAT_OFFSET 5 -#define MACB_CLRSTAT_SIZE 1 -#define MACB_INCSTAT_OFFSET 6 -#define MACB_INCSTAT_SIZE 1 -#define MACB_WESTAT_OFFSET 7 -#define MACB_WESTAT_SIZE 1 -#define MACB_BP_OFFSET 8 -#define MACB_BP_SIZE 1 -#define MACB_TSTART_OFFSET 9 -#define MACB_TSTART_SIZE 1 -#define MACB_THALT_OFFSET 10 -#define MACB_THALT_SIZE 1 -#define MACB_NCR_TPF_OFFSET 11 -#define MACB_NCR_TPF_SIZE 1 -#define MACB_TZQ_OFFSET 12 -#define MACB_TZQ_SIZE 1 +#define MACB_LB_OFFSET 0 /* reserved */ +#define MACB_LB_SIZE 1 +#define MACB_LLB_OFFSET 1 /* Loop back local */ +#define MACB_LLB_SIZE 1 +#define MACB_RE_OFFSET 2 /* Receive enable */ +#define MACB_RE_SIZE 1 +#define MACB_TE_OFFSET 3 /* Transmit enable */ +#define MACB_TE_SIZE 1 +#define MACB_MPE_OFFSET 4 /* Management port enable */ +#define MACB_MPE_SIZE 1 +#define MACB_CLRSTAT_OFFSET 5 /* Clear stats regs */ +#define MACB_CLRSTAT_SIZE 1 +#define MACB_INCSTAT_OFFSET 6 /* Incremental stats regs */ +#define MACB_INCSTAT_SIZE 1 +#define MACB_WESTAT_OFFSET 7 /* Write enable stats regs */ +#define MACB_WESTAT_SIZE 1 +#define MACB_BP_OFFSET 8 /* Back pressure */ +#define MACB_BP_SIZE 1 +#define MACB_TSTART_OFFSET 9 /* Start transmission */ +#define MACB_TSTART_SIZE 1 +#define MACB_THALT_OFFSET 10 /* Transmit halt */ +#define MACB_THALT_SIZE 1 +#define MACB_NCR_TPF_OFFSET 11 /* Transmit pause frame */ +#define MACB_NCR_TPF_SIZE 1 +#define MACB_TZQ_OFFSET 12 /* Transmit zero quantum pause frame */ +#define MACB_TZQ_SIZE 1 /* Bitfields in NCFGR */ -#define MACB_SPD_OFFSET 0 -#define MACB_SPD_SIZE 1 -#define MACB_FD_OFFSET 1 -#define MACB_FD_SIZE 1 -#define MACB_BIT_RATE_OFFSET 2 -#define MACB_BIT_RATE_SIZE 1 -#define MACB_JFRAME_OFFSET 3 -#define MACB_JFRAME_SIZE 1 -#define MACB_CAF_OFFSET 4 -#define MACB_CAF_SIZE 1 -#define MACB_NBC_OFFSET 5 -#define MACB_NBC_SIZE 1 -#define MACB_NCFGR_MTI_OFFSET 6 -#define MACB_NCFGR_MTI_SIZE 1 -#define MACB_UNI_OFFSET 7 -#define MACB_UNI_SIZE 1 -#define MACB_BIG_OFFSET 8 -#define MACB_BIG_SIZE 1 -#define MACB_EAE_OFFSET 9 -#define MACB_EAE_SIZE 1 -#define MACB_CLK_OFFSET 10 -#define MACB_CLK_SIZE 2 -#define MACB_RTY_OFFSET 12 -#define MACB_RTY_SIZE 1 -#define MACB_PAE_OFFSET 13 -#define MACB_PAE_SIZE 1 -#define MACB_RM9200_RMII_OFFSET 13 /* AT91RM9200 only */ -#define MACB_RM9200_RMII_SIZE 1 /* AT91RM9200 only */ -#define MACB_RBOF_OFFSET 14 -#define MACB_RBOF_SIZE 2 -#define MACB_RLCE_OFFSET 16 -#define MACB_RLCE_SIZE 1 -#define MACB_DRFCS_OFFSET 17 -#define MACB_DRFCS_SIZE 1 -#define MACB_EFRHD_OFFSET 18 -#define MACB_EFRHD_SIZE 1 -#define MACB_IRXFCS_OFFSET 19 -#define MACB_IRXFCS_SIZE 1 +#define MACB_SPD_OFFSET 0 /* Speed */ +#define MACB_SPD_SIZE 1 +#define MACB_FD_OFFSET 1 /* Full duplex */ +#define MACB_FD_SIZE 1 +#define MACB_BIT_RATE_OFFSET 2 /* Discard non-VLAN frames */ +#define MACB_BIT_RATE_SIZE 1 +#define MACB_JFRAME_OFFSET 3 /* reserved */ +#define MACB_JFRAME_SIZE 1 +#define MACB_CAF_OFFSET 4 /* Copy all frames */ +#define MACB_CAF_SIZE 1 +#define MACB_NBC_OFFSET 5 /* No broadcast */ +#define MACB_NBC_SIZE 1 +#define MACB_NCFGR_MTI_OFFSET 6 /* Multicast hash enable */ +#define MACB_NCFGR_MTI_SIZE 1 +#define MACB_UNI_OFFSET 7 /* Unicast hash enable */ +#define MACB_UNI_SIZE 1 +#define MACB_BIG_OFFSET 8 /* Receive 1536 byte frames */ +#define MACB_BIG_SIZE 1 +#define MACB_EAE_OFFSET 9 /* External address match enable */ +#define MACB_EAE_SIZE 1 +#define MACB_CLK_OFFSET 10 +#define MACB_CLK_SIZE 2 +#define MACB_RTY_OFFSET 12 /* Retry test */ +#define MACB_RTY_SIZE 1 +#define MACB_PAE_OFFSET 13 /* Pause enable */ +#define MACB_PAE_SIZE 1 +#define MACB_RM9200_RMII_OFFSET 13 /* AT91RM9200 only */ +#define MACB_RM9200_RMII_SIZE 1 /* AT91RM9200 only */ +#define MACB_RBOF_OFFSET 14 /* Receive buffer offset */ +#define MACB_RBOF_SIZE 2 +#define MACB_RLCE_OFFSET 16 /* Length field error frame discard */ +#define MACB_RLCE_SIZE 1 +#define MACB_DRFCS_OFFSET 17 /* FCS remove */ +#define MACB_DRFCS_SIZE 1 +#define MACB_EFRHD_OFFSET 18 +#define MACB_EFRHD_SIZE 1 +#define MACB_IRXFCS_OFFSET 19 +#define MACB_IRXFCS_SIZE 1 /* GEM specific NCFGR bitfields. */ -#define GEM_GBE_OFFSET 10 -#define GEM_GBE_SIZE 1 -#define GEM_CLK_OFFSET 18 -#define GEM_CLK_SIZE 3 -#define GEM_DBW_OFFSET 21 -#define GEM_DBW_SIZE 2 -#define GEM_RXCOEN_OFFSET 24 -#define GEM_RXCOEN_SIZE 1 +#define GEM_GBE_OFFSET 10 /* Gigabit mode enable */ +#define GEM_GBE_SIZE 1 +#define GEM_CLK_OFFSET 18 /* MDC clock division */ +#define GEM_CLK_SIZE 3 +#define GEM_DBW_OFFSET 21 /* Data bus width */ +#define GEM_DBW_SIZE 2 +#define GEM_RXCOEN_OFFSET 24 +#define GEM_RXCOEN_SIZE 1 /* Constants for data bus width. */ -#define GEM_DBW32 0 -#define GEM_DBW64 1 -#define GEM_DBW128 2 +#define GEM_DBW32 0 /* 32 bit AMBA AHB data bus width */ +#define GEM_DBW64 1 /* 64 bit AMBA AHB data bus width */ +#define GEM_DBW128 2 /* 128 bit AMBA AHB data bus width */ /* Bitfields in DMACFG. */ -#define GEM_FBLDO_OFFSET 0 -#define GEM_FBLDO_SIZE 5 -#define GEM_ENDIA_OFFSET 7 -#define GEM_ENDIA_SIZE 1 -#define GEM_RXBMS_OFFSET 8 -#define GEM_RXBMS_SIZE 2 -#define GEM_TXPBMS_OFFSET 10 -#define GEM_TXPBMS_SIZE 1 -#define GEM_TXCOEN_OFFSET 11 -#define GEM_TXCOEN_SIZE 1 -#define GEM_RXBS_OFFSET 16 -#define GEM_RXBS_SIZE 8 -#define GEM_DDRP_OFFSET 24 -#define GEM_DDRP_SIZE 1 +#define GEM_FBLDO_OFFSET 0 /* fixed burst length for DMA */ +#define GEM_FBLDO_SIZE 5 +#define GEM_ENDIA_OFFSET 7 /* endian swap mode for packet data access */ +#define GEM_ENDIA_SIZE 1 +#define GEM_RXBMS_OFFSET 8 /* RX packet buffer memory size select */ +#define GEM_RXBMS_SIZE 2 +#define GEM_TXPBMS_OFFSET 10 /* TX packet buffer memory size select */ +#define GEM_TXPBMS_SIZE 1 +#define GEM_TXCOEN_OFFSET 11 /* TX IP/TCP/UDP checksum gen offload */ +#define GEM_TXCOEN_SIZE 1 +#define GEM_RXBS_OFFSET 16 /* DMA receive buffer size */ +#define GEM_RXBS_SIZE 8 +#define GEM_DDRP_OFFSET 24 /* disc_when_no_ahb */ +#define GEM_DDRP_SIZE 1 /* Bitfields in NSR */ -#define MACB_NSR_LINK_OFFSET 0 -#define MACB_NSR_LINK_SIZE 1 -#define MACB_MDIO_OFFSET 1 -#define MACB_MDIO_SIZE 1 -#define MACB_IDLE_OFFSET 2 -#define MACB_IDLE_SIZE 1 +#define MACB_NSR_LINK_OFFSET 0 /* pcs_link_state */ +#define MACB_NSR_LINK_SIZE 1 +#define MACB_MDIO_OFFSET 1 /* status of the mdio_in pin */ +#define MACB_MDIO_SIZE 1 +#define MACB_IDLE_OFFSET 2 /* The PHY management logic is idle */ +#define MACB_IDLE_SIZE 1 /* Bitfields in TSR */ -#define MACB_UBR_OFFSET 0 -#define MACB_UBR_SIZE 1 -#define MACB_COL_OFFSET 1 -#define MACB_COL_SIZE 1 -#define MACB_TSR_RLE_OFFSET 2 -#define MACB_TSR_RLE_SIZE 1 -#define MACB_TGO_OFFSET 3 -#define MACB_TGO_SIZE 1 -#define MACB_BEX_OFFSET 4 -#define MACB_BEX_SIZE 1 -#define MACB_RM9200_BNQ_OFFSET 4 /* AT91RM9200 only */ -#define MACB_RM9200_BNQ_SIZE 1 /* AT91RM9200 only */ -#define MACB_COMP_OFFSET 5 -#define MACB_COMP_SIZE 1 -#define MACB_UND_OFFSET 6 -#define MACB_UND_SIZE 1 +#define MACB_UBR_OFFSET 0 /* Used bit read */ +#define MACB_UBR_SIZE 1 +#define MACB_COL_OFFSET 1 /* Collision occurred */ +#define MACB_COL_SIZE 1 +#define MACB_TSR_RLE_OFFSET 2 /* Retry limit exceeded */ +#define MACB_TSR_RLE_SIZE 1 +#define MACB_TGO_OFFSET 3 /* Transmit go */ +#define MACB_TGO_SIZE 1 +#define MACB_BEX_OFFSET 4 /* TX frame corruption due to AHB error */ +#define MACB_BEX_SIZE 1 +#define MACB_RM9200_BNQ_OFFSET 4 /* AT91RM9200 only */ +#define MACB_RM9200_BNQ_SIZE 1 /* AT91RM9200 only */ +#define MACB_COMP_OFFSET 5 /* Trnasmit complete */ +#define MACB_COMP_SIZE 1 +#define MACB_UND_OFFSET 6 /* Trnasmit under run */ +#define MACB_UND_SIZE 1 /* Bitfields in RSR */ -#define MACB_BNA_OFFSET 0 -#define MACB_BNA_SIZE 1 -#define MACB_REC_OFFSET 1 -#define MACB_REC_SIZE 1 -#define MACB_OVR_OFFSET 2 -#define MACB_OVR_SIZE 1 +#define MACB_BNA_OFFSET 0 /* Buffer not available */ +#define MACB_BNA_SIZE 1 +#define MACB_REC_OFFSET 1 /* Frame received */ +#define MACB_REC_SIZE 1 +#define MACB_OVR_OFFSET 2 /* Receive overrun */ +#define MACB_OVR_SIZE 1 /* Bitfields in ISR/IER/IDR/IMR */ -#define MACB_MFD_OFFSET 0 -#define MACB_MFD_SIZE 1 -#define MACB_RCOMP_OFFSET 1 -#define MACB_RCOMP_SIZE 1 -#define MACB_RXUBR_OFFSET 2 -#define MACB_RXUBR_SIZE 1 -#define MACB_TXUBR_OFFSET 3 -#define MACB_TXUBR_SIZE 1 -#define MACB_ISR_TUND_OFFSET 4 -#define MACB_ISR_TUND_SIZE 1 -#define MACB_ISR_RLE_OFFSET 5 -#define MACB_ISR_RLE_SIZE 1 -#define MACB_TXERR_OFFSET 6 -#define MACB_TXERR_SIZE 1 -#define MACB_TCOMP_OFFSET 7 -#define MACB_TCOMP_SIZE 1 -#define MACB_ISR_LINK_OFFSET 9 -#define MACB_ISR_LINK_SIZE 1 -#define MACB_ISR_ROVR_OFFSET 10 -#define MACB_ISR_ROVR_SIZE 1 -#define MACB_HRESP_OFFSET 11 -#define MACB_HRESP_SIZE 1 -#define MACB_PFR_OFFSET 12 -#define MACB_PFR_SIZE 1 -#define MACB_PTZ_OFFSET 13 -#define MACB_PTZ_SIZE 1 +#define MACB_MFD_OFFSET 0 /* Management frame sent */ +#define MACB_MFD_SIZE 1 +#define MACB_RCOMP_OFFSET 1 /* Receive complete */ +#define MACB_RCOMP_SIZE 1 +#define MACB_RXUBR_OFFSET 2 /* RX used bit read */ +#define MACB_RXUBR_SIZE 1 +#define MACB_TXUBR_OFFSET 3 /* TX used bit read */ +#define MACB_TXUBR_SIZE 1 +#define MACB_ISR_TUND_OFFSET 4 /* Enable TX buffer under run interrupt */ +#define MACB_ISR_TUND_SIZE 1 +#define MACB_ISR_RLE_OFFSET 5 /* EN retry exceeded/late coll interrupt */ +#define MACB_ISR_RLE_SIZE 1 +#define MACB_TXERR_OFFSET 6 /* EN TX frame corrupt from error interrupt */ +#define MACB_TXERR_SIZE 1 +#define MACB_TCOMP_OFFSET 7 /* Enable transmit complete interrupt */ +#define MACB_TCOMP_SIZE 1 +#define MACB_ISR_LINK_OFFSET 9 /* Enable link change interrupt */ +#define MACB_ISR_LINK_SIZE 1 +#define MACB_ISR_ROVR_OFFSET 10 /* Enable receive overrun interrupt */ +#define MACB_ISR_ROVR_SIZE 1 +#define MACB_HRESP_OFFSET 11 /* Enable hrsep not OK interrupt */ +#define MACB_HRESP_SIZE 1 +#define MACB_PFR_OFFSET 12 /* Enable pause frame w/ quantum interrupt */ +#define MACB_PFR_SIZE 1 +#define MACB_PTZ_OFFSET 13 /* Enable pause time zero interrupt */ +#define MACB_PTZ_SIZE 1 /* Bitfields in MAN */ -#define MACB_DATA_OFFSET 0 -#define MACB_DATA_SIZE 16 -#define MACB_CODE_OFFSET 16 -#define MACB_CODE_SIZE 2 -#define MACB_REGA_OFFSET 18 -#define MACB_REGA_SIZE 5 -#define MACB_PHYA_OFFSET 23 -#define MACB_PHYA_SIZE 5 -#define MACB_RW_OFFSET 28 -#define MACB_RW_SIZE 2 -#define MACB_SOF_OFFSET 30 -#define MACB_SOF_SIZE 2 +#define MACB_DATA_OFFSET 0 /* data */ +#define MACB_DATA_SIZE 16 +#define MACB_CODE_OFFSET 16 /* Must be written to 10 */ +#define MACB_CODE_SIZE 2 +#define MACB_REGA_OFFSET 18 /* Register address */ +#define MACB_REGA_SIZE 5 +#define MACB_PHYA_OFFSET 23 /* PHY address */ +#define MACB_PHYA_SIZE 5 +#define MACB_RW_OFFSET 28 /* Operation. 10 is read. 01 is write. */ +#define MACB_RW_SIZE 2 +#define MACB_SOF_OFFSET 30 /* Must be written to 1 for Clause 22 */ +#define MACB_SOF_SIZE 2 /* Bitfields in USRIO (AVR32) */ #define MACB_MII_OFFSET 0 @@ -286,7 +332,7 @@ /* Bitfields in USRIO (AT91) */ #define MACB_RMII_OFFSET 0 #define MACB_RMII_SIZE 1 -#define GEM_RGMII_OFFSET 0 /* GEM gigabit mode */ +#define GEM_RGMII_OFFSET 0 /* GEM gigabit mode */ #define GEM_RGMII_SIZE 1 #define MACB_CLKEN_OFFSET 1 #define MACB_CLKEN_SIZE 1 @@ -389,8 +435,7 @@ #define queue_writel(queue, reg, value) \ __raw_writel((value), (queue)->bp->regs + (queue)->reg) -/* - * Conditional GEM/MACB macros. These perform the operation to the correct +/* Conditional GEM/MACB macros. These perform the operation to the correct * register dependent on whether the device is a GEM or a MACB. For registers * and bitfields that are common across both devices, use macb_{read,write}l * to avoid the cost of the conditional. @@ -413,8 +458,7 @@ __v; \ }) -/** - * struct macb_dma_desc - Hardware DMA descriptor +/* struct macb_dma_desc - Hardware DMA descriptor * @addr: DMA address of data buffer * @ctrl: Control and status bits */ @@ -503,8 +547,7 @@ struct macb_dma_desc { /* limit RX checksum offload to TCP and UDP packets */ #define GEM_RX_CSUM_CHECKED_MASK 2 -/** - * struct macb_tx_skb - data about an skb which is being transmitted +/* struct macb_tx_skb - data about an skb which is being transmitted * @skb: skb currently being transmitted, only set for the last buffer * of the frame * @mapping: DMA address of the skb's fragment buffer @@ -519,8 +562,7 @@ struct macb_tx_skb { bool mapped_as_page; }; -/* - * Hardware-collected statistics. Used when updating the network +/* Hardware-collected statistics. Used when updating the network * device stats by a periodic timer. */ struct macb_stats { @@ -595,6 +637,107 @@ struct gem_stats { u32 rx_udp_checksum_errors; }; +/* Describes the name and offset of an individual statistic register, as + * returned by `ethtool -S`. Also describes which net_device_stats statistics + * this register should contribute to. + */ +struct gem_statistic { + char stat_string[ETH_GSTRING_LEN]; + int offset; + u32 stat_bits; +}; + +/* Bitfield defs for net_device_stat statistics */ +#define GEM_NDS_RXERR_OFFSET 0 +#define GEM_NDS_RXLENERR_OFFSET 1 +#define GEM_NDS_RXOVERERR_OFFSET 2 +#define GEM_NDS_RXCRCERR_OFFSET 3 +#define GEM_NDS_RXFRAMEERR_OFFSET 4 +#define GEM_NDS_RXFIFOERR_OFFSET 5 +#define GEM_NDS_TXERR_OFFSET 6 +#define GEM_NDS_TXABORTEDERR_OFFSET 7 +#define GEM_NDS_TXCARRIERERR_OFFSET 8 +#define GEM_NDS_TXFIFOERR_OFFSET 9 +#define GEM_NDS_COLLISIONS_OFFSET 10 + +#define GEM_STAT_TITLE(name, title) GEM_STAT_TITLE_BITS(name, title, 0) +#define GEM_STAT_TITLE_BITS(name, title, bits) { \ + .stat_string = title, \ + .offset = GEM_##name, \ + .stat_bits = bits \ +} + +/* list of gem statistic registers. The names MUST match the + * corresponding GEM_* definitions. + */ +static const struct gem_statistic gem_statistics[] = { + GEM_STAT_TITLE(OCTTXL, "tx_octets"), /* OCTTXH combined with OCTTXL */ + GEM_STAT_TITLE(TXCNT, "tx_frames"), + GEM_STAT_TITLE(TXBCCNT, "tx_broadcast_frames"), + GEM_STAT_TITLE(TXMCCNT, "tx_multicast_frames"), + GEM_STAT_TITLE(TXPAUSECNT, "tx_pause_frames"), + GEM_STAT_TITLE(TX64CNT, "tx_64_byte_frames"), + GEM_STAT_TITLE(TX65CNT, "tx_65_127_byte_frames"), + GEM_STAT_TITLE(TX128CNT, "tx_128_255_byte_frames"), + GEM_STAT_TITLE(TX256CNT, "tx_256_511_byte_frames"), + GEM_STAT_TITLE(TX512CNT, "tx_512_1023_byte_frames"), + GEM_STAT_TITLE(TX1024CNT, "tx_1024_1518_byte_frames"), + GEM_STAT_TITLE(TX1519CNT, "tx_greater_than_1518_byte_frames"), + GEM_STAT_TITLE_BITS(TXURUNCNT, "tx_underrun", + GEM_BIT(NDS_TXERR)|GEM_BIT(NDS_TXFIFOERR)), + GEM_STAT_TITLE_BITS(SNGLCOLLCNT, "tx_single_collision_frames", + GEM_BIT(NDS_TXERR)|GEM_BIT(NDS_COLLISIONS)), + GEM_STAT_TITLE_BITS(MULTICOLLCNT, "tx_multiple_collision_frames", + GEM_BIT(NDS_TXERR)|GEM_BIT(NDS_COLLISIONS)), + GEM_STAT_TITLE_BITS(EXCESSCOLLCNT, "tx_excessive_collisions", + GEM_BIT(NDS_TXERR)| + GEM_BIT(NDS_TXABORTEDERR)| + GEM_BIT(NDS_COLLISIONS)), + GEM_STAT_TITLE_BITS(LATECOLLCNT, "tx_late_collisions", + GEM_BIT(NDS_TXERR)|GEM_BIT(NDS_COLLISIONS)), + GEM_STAT_TITLE(TXDEFERCNT, "tx_deferred_frames"), + GEM_STAT_TITLE_BITS(TXCSENSECNT, "tx_carrier_sense_errors", + GEM_BIT(NDS_TXERR)|GEM_BIT(NDS_COLLISIONS)), + GEM_STAT_TITLE(OCTRXL, "rx_octets"), /* OCTRXH combined with OCTRXL */ + GEM_STAT_TITLE(RXCNT, "rx_frames"), + GEM_STAT_TITLE(RXBROADCNT, "rx_broadcast_frames"), + GEM_STAT_TITLE(RXMULTICNT, "rx_multicast_frames"), + GEM_STAT_TITLE(RXPAUSECNT, "rx_pause_frames"), + GEM_STAT_TITLE(RX64CNT, "rx_64_byte_frames"), + GEM_STAT_TITLE(RX65CNT, "rx_65_127_byte_frames"), + GEM_STAT_TITLE(RX128CNT, "rx_128_255_byte_frames"), + GEM_STAT_TITLE(RX256CNT, "rx_256_511_byte_frames"), + GEM_STAT_TITLE(RX512CNT, "rx_512_1023_byte_frames"), + GEM_STAT_TITLE(RX1024CNT, "rx_1024_1518_byte_frames"), + GEM_STAT_TITLE(RX1519CNT, "rx_greater_than_1518_byte_frames"), + GEM_STAT_TITLE_BITS(RXUNDRCNT, "rx_undersized_frames", + GEM_BIT(NDS_RXERR)|GEM_BIT(NDS_RXLENERR)), + GEM_STAT_TITLE_BITS(RXOVRCNT, "rx_oversize_frames", + GEM_BIT(NDS_RXERR)|GEM_BIT(NDS_RXLENERR)), + GEM_STAT_TITLE_BITS(RXJABCNT, "rx_jabbers", + GEM_BIT(NDS_RXERR)|GEM_BIT(NDS_RXLENERR)), + GEM_STAT_TITLE_BITS(RXFCSCNT, "rx_frame_check_sequence_errors", + GEM_BIT(NDS_RXERR)|GEM_BIT(NDS_RXCRCERR)), + GEM_STAT_TITLE_BITS(RXLENGTHCNT, "rx_length_field_frame_errors", + GEM_BIT(NDS_RXERR)), + GEM_STAT_TITLE_BITS(RXSYMBCNT, "rx_symbol_errors", + GEM_BIT(NDS_RXERR)|GEM_BIT(NDS_RXFRAMEERR)), + GEM_STAT_TITLE_BITS(RXALIGNCNT, "rx_alignment_errors", + GEM_BIT(NDS_RXERR)|GEM_BIT(NDS_RXOVERERR)), + GEM_STAT_TITLE_BITS(RXRESERRCNT, "rx_resource_errors", + GEM_BIT(NDS_RXERR)|GEM_BIT(NDS_RXOVERERR)), + GEM_STAT_TITLE_BITS(RXORCNT, "rx_overruns", + GEM_BIT(NDS_RXERR)|GEM_BIT(NDS_RXFIFOERR)), + GEM_STAT_TITLE_BITS(RXIPCCNT, "rx_ip_header_checksum_errors", + GEM_BIT(NDS_RXERR)), + GEM_STAT_TITLE_BITS(RXTCPCCNT, "rx_tcp_checksum_errors", + GEM_BIT(NDS_RXERR)), + GEM_STAT_TITLE_BITS(RXUDPCCNT, "rx_udp_checksum_errors", + GEM_BIT(NDS_RXERR)), +}; + +#define GEM_STATS_LEN ARRAY_SIZE(gem_statistics) + struct macb; struct macb_or_gem_ops { @@ -673,6 +816,8 @@ struct macb { dma_addr_t skb_physaddr; /* phys addr from pci_map_single */ int skb_length; /* saved skb length for pci_unmap_single */ unsigned int max_tx_length; + + u64 ethtool_stats[GEM_STATS_LEN]; }; extern const struct ethtool_ops macb_ethtool_ops; diff --git a/drivers/net/ethernet/chelsio/cxgb/sge.c b/drivers/net/ethernet/chelsio/cxgb/sge.c index babe2a915b00..526ea74e82d9 100644 --- a/drivers/net/ethernet/chelsio/cxgb/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb/sge.c @@ -1860,9 +1860,9 @@ netdev_tx_t t1_start_xmit(struct sk_buff *skb, struct net_device *dev) } cpl->iff = dev->if_port; - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { cpl->vlan_valid = 1; - cpl->vlan = htons(vlan_tx_tag_get(skb)); + cpl->vlan = htons(skb_vlan_tag_get(skb)); st->vlan_insert++; } else cpl->vlan_valid = 0; diff --git a/drivers/net/ethernet/chelsio/cxgb3/mc5.c b/drivers/net/ethernet/chelsio/cxgb3/mc5.c index e13b7fe9d082..338301b11518 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/mc5.c +++ b/drivers/net/ethernet/chelsio/cxgb3/mc5.c @@ -97,14 +97,6 @@ static int mc5_cmd_write(struct adapter *adapter, u32 cmd) F_DBGIRSPVALID, 1, MAX_WRITE_ATTEMPTS, 1); } -static inline void dbgi_wr_addr3(struct adapter *adapter, u32 v1, u32 v2, - u32 v3) -{ - t3_write_reg(adapter, A_MC5_DB_DBGI_REQ_ADDR0, v1); - t3_write_reg(adapter, A_MC5_DB_DBGI_REQ_ADDR1, v2); - t3_write_reg(adapter, A_MC5_DB_DBGI_REQ_ADDR2, v3); -} - static inline void dbgi_wr_data3(struct adapter *adapter, u32 v1, u32 v2, u32 v3) { @@ -113,14 +105,6 @@ static inline void dbgi_wr_data3(struct adapter *adapter, u32 v1, u32 v2, t3_write_reg(adapter, A_MC5_DB_DBGI_REQ_DATA2, v3); } -static inline void dbgi_rd_rsp3(struct adapter *adapter, u32 *v1, u32 *v2, - u32 *v3) -{ - *v1 = t3_read_reg(adapter, A_MC5_DB_DBGI_RSP_DATA0); - *v2 = t3_read_reg(adapter, A_MC5_DB_DBGI_RSP_DATA1); - *v3 = t3_read_reg(adapter, A_MC5_DB_DBGI_RSP_DATA2); -} - /* * Write data to the TCAM register at address (0, 0, addr_lo) using the TCAM * command cmd. The data to be written must have been set up by the caller. diff --git a/drivers/net/ethernet/chelsio/cxgb3/sge.c b/drivers/net/ethernet/chelsio/cxgb3/sge.c index 3dfcf600fcc6..d6aa602f168d 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb3/sge.c @@ -1148,8 +1148,8 @@ static void write_tx_pkt_wr(struct adapter *adap, struct sk_buff *skb, cpl->len = htonl(skb->len); cntrl = V_TXPKT_INTF(pi->port_id); - if (vlan_tx_tag_present(skb)) - cntrl |= F_TXPKT_VLAN_VLD | V_TXPKT_VLAN(vlan_tx_tag_get(skb)); + if (skb_vlan_tag_present(skb)) + cntrl |= F_TXPKT_VLAN_VLD | V_TXPKT_VLAN(skb_vlan_tag_get(skb)); tso_info = V_LSO_MSS(skb_shinfo(skb)->gso_size); if (tso_info) { @@ -1282,7 +1282,7 @@ netdev_tx_t t3_eth_xmit(struct sk_buff *skb, struct net_device *dev) qs->port_stats[SGE_PSTAT_TX_CSUM]++; if (skb_shinfo(skb)->gso_size) qs->port_stats[SGE_PSTAT_TSO]++; - if (vlan_tx_tag_present(skb)) + if (skb_vlan_tag_present(skb)) qs->port_stats[SGE_PSTAT_VLANINS]++; /* diff --git a/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c b/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c index c74a898fcd4f..184a8d545ac4 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c @@ -727,9 +727,9 @@ static int get_vpd_params(struct adapter *adapter, struct vpd_params *p) p->xauicfg[1] = simple_strtoul(vpd.xaui1cfg_data, NULL, 16); } - for (i = 0; i < 6; i++) - p->eth_base[i] = hex_to_bin(vpd.na_data[2 * i]) * 16 + - hex_to_bin(vpd.na_data[2 * i + 1]); + ret = hex2bin(p->eth_base, vpd.na_data, 6); + if (ret < 0) + return -EINVAL; return 0; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/Makefile b/drivers/net/ethernet/chelsio/cxgb4/Makefile index b85280775997..ae50cd72358c 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/Makefile +++ b/drivers/net/ethernet/chelsio/cxgb4/Makefile @@ -4,6 +4,6 @@ obj-$(CONFIG_CHELSIO_T4) += cxgb4.o -cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o +cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o clip_tbl.o cxgb4-$(CONFIG_CHELSIO_T4_DCB) += cxgb4_dcb.o cxgb4-$(CONFIG_DEBUG_FS) += cxgb4_debugfs.o diff --git a/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c b/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c new file mode 100644 index 000000000000..9062a8434246 --- /dev/null +++ b/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.c @@ -0,0 +1,317 @@ +/* + * This file is part of the Chelsio T4 Ethernet driver for Linux. + * Copyright (C) 2003-2014 Chelsio Communications. All rights reserved. + * + * Written by Deepak (deepak.s@chelsio.com) + * + * 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 LICENSE file included in this + * release for licensing terms and conditions. + */ + +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/jhash.h> +#include <linux/if_vlan.h> +#include <net/addrconf.h> +#include "cxgb4.h" +#include "clip_tbl.h" + +static inline unsigned int ipv4_clip_hash(struct clip_tbl *c, const u32 *key) +{ + unsigned int clipt_size_half = c->clipt_size / 2; + + return jhash_1word(*key, 0) % clipt_size_half; +} + +static inline unsigned int ipv6_clip_hash(struct clip_tbl *d, const u32 *key) +{ + unsigned int clipt_size_half = d->clipt_size / 2; + u32 xor = key[0] ^ key[1] ^ key[2] ^ key[3]; + + return clipt_size_half + + (jhash_1word(xor, 0) % clipt_size_half); +} + +static unsigned int clip_addr_hash(struct clip_tbl *ctbl, const u32 *addr, + int addr_len) +{ + return addr_len == 4 ? ipv4_clip_hash(ctbl, addr) : + ipv6_clip_hash(ctbl, addr); +} + +static int clip6_get_mbox(const struct net_device *dev, + const struct in6_addr *lip) +{ + struct adapter *adap = netdev2adap(dev); + struct fw_clip_cmd c; + + memset(&c, 0, sizeof(c)); + c.op_to_write = htonl(FW_CMD_OP_V(FW_CLIP_CMD) | + FW_CMD_REQUEST_F | FW_CMD_WRITE_F); + c.alloc_to_len16 = htonl(FW_CLIP_CMD_ALLOC_F | FW_LEN16(c)); + *(__be64 *)&c.ip_hi = *(__be64 *)(lip->s6_addr); + *(__be64 *)&c.ip_lo = *(__be64 *)(lip->s6_addr + 8); + return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false); +} + +static int clip6_release_mbox(const struct net_device *dev, + const struct in6_addr *lip) +{ + struct adapter *adap = netdev2adap(dev); + struct fw_clip_cmd c; + + memset(&c, 0, sizeof(c)); + c.op_to_write = htonl(FW_CMD_OP_V(FW_CLIP_CMD) | + FW_CMD_REQUEST_F | FW_CMD_READ_F); + c.alloc_to_len16 = htonl(FW_CLIP_CMD_FREE_F | FW_LEN16(c)); + *(__be64 *)&c.ip_hi = *(__be64 *)(lip->s6_addr); + *(__be64 *)&c.ip_lo = *(__be64 *)(lip->s6_addr + 8); + return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false); +} + +int cxgb4_clip_get(const struct net_device *dev, const u32 *lip, u8 v6) +{ + struct adapter *adap = netdev2adap(dev); + struct clip_tbl *ctbl = adap->clipt; + struct clip_entry *ce, *cte; + u32 *addr = (u32 *)lip; + int hash; + int addr_len; + int ret = 0; + + if (!ctbl) + return 0; + + if (v6) + addr_len = 16; + else + addr_len = 4; + + hash = clip_addr_hash(ctbl, addr, addr_len); + + read_lock_bh(&ctbl->lock); + list_for_each_entry(cte, &ctbl->hash_list[hash], list) { + if (addr_len == cte->addr_len && + memcmp(lip, cte->addr, cte->addr_len) == 0) { + ce = cte; + read_unlock_bh(&ctbl->lock); + goto found; + } + } + read_unlock_bh(&ctbl->lock); + + write_lock_bh(&ctbl->lock); + if (!list_empty(&ctbl->ce_free_head)) { + ce = list_first_entry(&ctbl->ce_free_head, + struct clip_entry, list); + list_del(&ce->list); + INIT_LIST_HEAD(&ce->list); + spin_lock_init(&ce->lock); + atomic_set(&ce->refcnt, 0); + atomic_dec(&ctbl->nfree); + ce->addr_len = addr_len; + memcpy(ce->addr, lip, addr_len); + list_add_tail(&ce->list, &ctbl->hash_list[hash]); + if (v6) { + ret = clip6_get_mbox(dev, (const struct in6_addr *)lip); + if (ret) { + write_unlock_bh(&ctbl->lock); + return ret; + } + } + } else { + write_unlock_bh(&ctbl->lock); + return -ENOMEM; + } + write_unlock_bh(&ctbl->lock); +found: + atomic_inc(&ce->refcnt); + + return 0; +} +EXPORT_SYMBOL(cxgb4_clip_get); + +void cxgb4_clip_release(const struct net_device *dev, const u32 *lip, u8 v6) +{ + struct adapter *adap = netdev2adap(dev); + struct clip_tbl *ctbl = adap->clipt; + struct clip_entry *ce, *cte; + u32 *addr = (u32 *)lip; + int hash; + int addr_len; + + if (v6) + addr_len = 16; + else + addr_len = 4; + + hash = clip_addr_hash(ctbl, addr, addr_len); + + read_lock_bh(&ctbl->lock); + list_for_each_entry(cte, &ctbl->hash_list[hash], list) { + if (addr_len == cte->addr_len && + memcmp(lip, cte->addr, cte->addr_len) == 0) { + ce = cte; + read_unlock_bh(&ctbl->lock); + goto found; + } + } + read_unlock_bh(&ctbl->lock); + + return; +found: + write_lock_bh(&ctbl->lock); + spin_lock_bh(&ce->lock); + if (atomic_dec_and_test(&ce->refcnt)) { + list_del(&ce->list); + INIT_LIST_HEAD(&ce->list); + list_add_tail(&ce->list, &ctbl->ce_free_head); + atomic_inc(&ctbl->nfree); + if (v6) + clip6_release_mbox(dev, (const struct in6_addr *)lip); + } + spin_unlock_bh(&ce->lock); + write_unlock_bh(&ctbl->lock); +} +EXPORT_SYMBOL(cxgb4_clip_release); + +/* Retrieves IPv6 addresses from a root device (bond, vlan) associated with + * a physical device. + * The physical device reference is needed to send the actul CLIP command. + */ +static int cxgb4_update_dev_clip(struct net_device *root_dev, + struct net_device *dev) +{ + struct inet6_dev *idev = NULL; + struct inet6_ifaddr *ifa; + int ret = 0; + + idev = __in6_dev_get(root_dev); + if (!idev) + return ret; + + read_lock_bh(&idev->lock); + list_for_each_entry(ifa, &idev->addr_list, if_list) { + ret = cxgb4_clip_get(dev, (const u32 *)ifa->addr.s6_addr, 1); + if (ret < 0) + break; + } + read_unlock_bh(&idev->lock); + + return ret; +} + +int cxgb4_update_root_dev_clip(struct net_device *dev) +{ + struct net_device *root_dev = NULL; + int i, ret = 0; + + /* First populate the real net device's IPv6 addresses */ + ret = cxgb4_update_dev_clip(dev, dev); + if (ret) + return ret; + + /* Parse all bond and vlan devices layered on top of the physical dev */ + root_dev = netdev_master_upper_dev_get_rcu(dev); + if (root_dev) { + ret = cxgb4_update_dev_clip(root_dev, dev); + if (ret) + return ret; + } + + for (i = 0; i < VLAN_N_VID; i++) { + root_dev = __vlan_find_dev_deep_rcu(dev, htons(ETH_P_8021Q), i); + if (!root_dev) + continue; + + ret = cxgb4_update_dev_clip(root_dev, dev); + if (ret) + break; + } + + return ret; +} +EXPORT_SYMBOL(cxgb4_update_root_dev_clip); + +int clip_tbl_show(struct seq_file *seq, void *v) +{ + struct adapter *adapter = seq->private; + struct clip_tbl *ctbl = adapter->clipt; + struct clip_entry *ce; + char ip[60]; + int i; + + read_lock_bh(&ctbl->lock); + + seq_puts(seq, "IP Address Users\n"); + for (i = 0 ; i < ctbl->clipt_size; ++i) { + list_for_each_entry(ce, &ctbl->hash_list[i], list) { + ip[0] = '\0'; + if (ce->addr_len == 16) + sprintf(ip, "%pI6c", ce->addr); + else + sprintf(ip, "%pI4c", ce->addr); + seq_printf(seq, "%-25s %u\n", ip, + atomic_read(&ce->refcnt)); + } + } + seq_printf(seq, "Free clip entries : %d\n", atomic_read(&ctbl->nfree)); + + read_unlock_bh(&ctbl->lock); + + return 0; +} + +struct clip_tbl *t4_init_clip_tbl(unsigned int clipt_start, + unsigned int clipt_end) +{ + struct clip_entry *cl_list; + struct clip_tbl *ctbl; + unsigned int clipt_size; + int i; + + if (clipt_start >= clipt_end) + return NULL; + clipt_size = clipt_end - clipt_start + 1; + if (clipt_size < CLIPT_MIN_HASH_BUCKETS) + return NULL; + + ctbl = t4_alloc_mem(sizeof(*ctbl) + + clipt_size*sizeof(struct list_head)); + if (!ctbl) + return NULL; + + ctbl->clipt_start = clipt_start; + ctbl->clipt_size = clipt_size; + INIT_LIST_HEAD(&ctbl->ce_free_head); + + atomic_set(&ctbl->nfree, clipt_size); + rwlock_init(&ctbl->lock); + + for (i = 0; i < ctbl->clipt_size; ++i) + INIT_LIST_HEAD(&ctbl->hash_list[i]); + + cl_list = t4_alloc_mem(clipt_size*sizeof(struct clip_entry)); + ctbl->cl_list = (void *)cl_list; + + for (i = 0; i < clipt_size; i++) { + INIT_LIST_HEAD(&cl_list[i].list); + list_add_tail(&cl_list[i].list, &ctbl->ce_free_head); + } + + return ctbl; +} + +void t4_cleanup_clip_tbl(struct adapter *adap) +{ + struct clip_tbl *ctbl = adap->clipt; + + if (ctbl) { + if (ctbl->cl_list) + t4_free_mem(ctbl->cl_list); + t4_free_mem(ctbl); + } +} +EXPORT_SYMBOL(t4_cleanup_clip_tbl); diff --git a/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.h b/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.h new file mode 100644 index 000000000000..2eaba0161cf8 --- /dev/null +++ b/drivers/net/ethernet/chelsio/cxgb4/clip_tbl.h @@ -0,0 +1,41 @@ +/* + * This file is part of the Chelsio T4 Ethernet driver for Linux. + * Copyright (C) 2003-2014 Chelsio Communications. All rights reserved. + * + * Written by Deepak (deepak.s@chelsio.com) + * + * 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 LICENSE file included in this + * release for licensing terms and conditions. + */ + +struct clip_entry { + spinlock_t lock; /* Hold while modifying clip reference */ + atomic_t refcnt; + struct list_head list; + u32 addr[4]; + int addr_len; +}; + +struct clip_tbl { + unsigned int clipt_start; + unsigned int clipt_size; + rwlock_t lock; + atomic_t nfree; + struct list_head ce_free_head; + void *cl_list; + struct list_head hash_list[0]; +}; + +enum { + CLIPT_MIN_HASH_BUCKETS = 2, +}; + +struct clip_tbl *t4_init_clip_tbl(unsigned int clipt_start, + unsigned int clipt_end); +int cxgb4_clip_get(const struct net_device *dev, const u32 *lip, u8 v6); +void cxgb4_clip_release(const struct net_device *dev, const u32 *lip, u8 v6); +int clip_tbl_show(struct seq_file *seq, void *v); +int cxgb4_update_root_dev_clip(struct net_device *dev); +void t4_cleanup_clip_tbl(struct adapter *adap); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index 5ab5c3133acd..d6cda17efe6e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -49,16 +49,6 @@ #include <asm/io.h> #include "cxgb4_uld.h" -#define T4FW_VERSION_MAJOR 0x01 -#define T4FW_VERSION_MINOR 0x0C -#define T4FW_VERSION_MICRO 0x19 -#define T4FW_VERSION_BUILD 0x00 - -#define T5FW_VERSION_MAJOR 0x01 -#define T5FW_VERSION_MINOR 0x0C -#define T5FW_VERSION_MICRO 0x19 -#define T5FW_VERSION_BUILD 0x00 - #define CH_WARN(adap, fmt, ...) dev_warn(adap->pdev_dev, fmt, ## __VA_ARGS__) enum { @@ -231,6 +221,7 @@ struct sge_params { struct tp_params { unsigned int ntxchan; /* # of Tx channels */ unsigned int tre; /* log2 of core clocks per TP tick */ + unsigned int la_mask; /* what events are recorded by TP LA */ unsigned short tx_modq_map; /* TX modulation scheduler queue to */ /* channel map */ @@ -290,11 +281,21 @@ enum chip_type { T5_LAST_REV = T5_A1, }; +struct devlog_params { + u32 memtype; /* which memory (EDC0, EDC1, MC) */ + u32 start; /* start of log in firmware memory */ + u32 size; /* size of log */ +}; + struct adapter_params { struct sge_params sge; struct tp_params tp; struct vpd_params vpd; struct pci_params pci; + struct devlog_params devlog; + enum pcie_memwin drv_memwin; + + unsigned int cim_la_size; unsigned int sf_size; /* serial flash size in bytes */ unsigned int sf_nsec; /* # of flash sectors */ @@ -476,6 +477,22 @@ struct sge_rspq { /* state for an SGE response queue */ struct adapter *adap; struct net_device *netdev; /* associated net device */ rspq_handler_t handler; +#ifdef CONFIG_NET_RX_BUSY_POLL +#define CXGB_POLL_STATE_IDLE 0 +#define CXGB_POLL_STATE_NAPI BIT(0) /* NAPI owns this poll */ +#define CXGB_POLL_STATE_POLL BIT(1) /* poll owns this poll */ +#define CXGB_POLL_STATE_NAPI_YIELD BIT(2) /* NAPI yielded this poll */ +#define CXGB_POLL_STATE_POLL_YIELD BIT(3) /* poll yielded this poll */ +#define CXGB_POLL_YIELD (CXGB_POLL_STATE_NAPI_YIELD | \ + CXGB_POLL_STATE_POLL_YIELD) +#define CXGB_POLL_LOCKED (CXGB_POLL_STATE_NAPI | \ + CXGB_POLL_STATE_POLL) +#define CXGB_POLL_USER_PEND (CXGB_POLL_STATE_POLL | \ + CXGB_POLL_STATE_POLL_YIELD) + unsigned int bpoll_state; + spinlock_t bpoll_lock; /* lock for busy poll */ +#endif /* CONFIG_NET_RX_BUSY_POLL */ + }; struct sge_eth_stats { /* Ethernet queue statistics */ @@ -658,6 +675,9 @@ struct adapter { unsigned int l2t_start; unsigned int l2t_end; struct l2t_data *l2t; + unsigned int clipt_start; + unsigned int clipt_end; + struct clip_tbl *clipt; void *uld_handle[CXGB4_ULD_MAX]; struct list_head list_node; struct list_head rcu_node; @@ -877,6 +897,102 @@ static inline struct adapter *netdev2adap(const struct net_device *dev) return netdev2pinfo(dev)->adapter; } +#ifdef CONFIG_NET_RX_BUSY_POLL +static inline void cxgb_busy_poll_init_lock(struct sge_rspq *q) +{ + spin_lock_init(&q->bpoll_lock); + q->bpoll_state = CXGB_POLL_STATE_IDLE; +} + +static inline bool cxgb_poll_lock_napi(struct sge_rspq *q) +{ + bool rc = true; + + spin_lock(&q->bpoll_lock); + if (q->bpoll_state & CXGB_POLL_LOCKED) { + q->bpoll_state |= CXGB_POLL_STATE_NAPI_YIELD; + rc = false; + } else { + q->bpoll_state = CXGB_POLL_STATE_NAPI; + } + spin_unlock(&q->bpoll_lock); + return rc; +} + +static inline bool cxgb_poll_unlock_napi(struct sge_rspq *q) +{ + bool rc = false; + + spin_lock(&q->bpoll_lock); + if (q->bpoll_state & CXGB_POLL_STATE_POLL_YIELD) + rc = true; + q->bpoll_state = CXGB_POLL_STATE_IDLE; + spin_unlock(&q->bpoll_lock); + return rc; +} + +static inline bool cxgb_poll_lock_poll(struct sge_rspq *q) +{ + bool rc = true; + + spin_lock_bh(&q->bpoll_lock); + if (q->bpoll_state & CXGB_POLL_LOCKED) { + q->bpoll_state |= CXGB_POLL_STATE_POLL_YIELD; + rc = false; + } else { + q->bpoll_state |= CXGB_POLL_STATE_POLL; + } + spin_unlock_bh(&q->bpoll_lock); + return rc; +} + +static inline bool cxgb_poll_unlock_poll(struct sge_rspq *q) +{ + bool rc = false; + + spin_lock_bh(&q->bpoll_lock); + if (q->bpoll_state & CXGB_POLL_STATE_POLL_YIELD) + rc = true; + q->bpoll_state = CXGB_POLL_STATE_IDLE; + spin_unlock_bh(&q->bpoll_lock); + return rc; +} + +static inline bool cxgb_poll_busy_polling(struct sge_rspq *q) +{ + return q->bpoll_state & CXGB_POLL_USER_PEND; +} +#else +static inline void cxgb_busy_poll_init_lock(struct sge_rspq *q) +{ +} + +static inline bool cxgb_poll_lock_napi(struct sge_rspq *q) +{ + return true; +} + +static inline bool cxgb_poll_unlock_napi(struct sge_rspq *q) +{ + return false; +} + +static inline bool cxgb_poll_lock_poll(struct sge_rspq *q) +{ + return false; +} + +static inline bool cxgb_poll_unlock_poll(struct sge_rspq *q) +{ + return false; +} + +static inline bool cxgb_poll_busy_polling(struct sge_rspq *q) +{ + return false; +} +#endif /* CONFIG_NET_RX_BUSY_POLL */ + void t4_os_portmod_changed(const struct adapter *adap, int port_id); void t4_os_link_changed(struct adapter *adap, int port_id, int link_stat); @@ -905,6 +1021,7 @@ irqreturn_t t4_sge_intr_msix(int irq, void *cookie); int t4_sge_init(struct adapter *adap); void t4_sge_start(struct adapter *adap); void t4_sge_stop(struct adapter *adap); +int cxgb_busy_poll(struct napi_struct *napi); extern int dbfifo_int_thresh; #define for_each_port(adapter, iter) \ @@ -995,12 +1112,16 @@ static inline int t4_memory_write(struct adapter *adap, int mtype, u32 addr, int t4_seeprom_wp(struct adapter *adapter, bool enable); int get_vpd_params(struct adapter *adapter, struct vpd_params *p); +int t4_read_flash(struct adapter *adapter, unsigned int addr, + unsigned int nwords, u32 *data, int byte_oriented); int t4_load_fw(struct adapter *adapter, const u8 *fw_data, unsigned int size); +int t4_fwcache(struct adapter *adap, enum fw_params_param_dev_fwcache op); int t4_fw_upgrade(struct adapter *adap, unsigned int mbox, const u8 *fw_data, unsigned int size, int force); unsigned int t4_flash_cfg_addr(struct adapter *adapter); int t4_get_fw_version(struct adapter *adapter, u32 *vers); int t4_get_tp_version(struct adapter *adapter, u32 *vers); +int t4_get_exprom_version(struct adapter *adapter, u32 *vers); int t4_prep_fw(struct adapter *adap, struct fw_info *fw_info, const u8 *fw_data, unsigned int fw_size, struct fw_hdr *card_fw, enum dev_state state, int *reset); @@ -1013,6 +1134,8 @@ int cxgb4_t4_bar2_sge_qregs(struct adapter *adapter, u64 *pbar2_qoffset, unsigned int *pbar2_qid); +unsigned int qtimer_val(const struct adapter *adap, + const struct sge_rspq *q); int t4_init_sge_params(struct adapter *adapter); int t4_init_tp_params(struct adapter *adap); int t4_filter_field_shift(const struct adapter *adap, int filter_sel); @@ -1022,20 +1145,46 @@ int t4_config_rss_range(struct adapter *adapter, int mbox, unsigned int viid, int start, int n, const u16 *rspq, unsigned int nrspq); int t4_config_glbl_rss(struct adapter *adapter, int mbox, unsigned int mode, unsigned int flags); +int t4_read_rss(struct adapter *adapter, u16 *entries); +void t4_read_rss_key(struct adapter *adapter, u32 *key); +void t4_write_rss_key(struct adapter *adap, const u32 *key, int idx); +void t4_read_rss_pf_config(struct adapter *adapter, unsigned int index, + u32 *valp); +void t4_read_rss_vf_config(struct adapter *adapter, unsigned int index, + u32 *vfl, u32 *vfh); +u32 t4_read_rss_pf_map(struct adapter *adapter); +u32 t4_read_rss_pf_mask(struct adapter *adapter); + int t4_mc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, u64 *parity); int t4_edc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, u64 *parity); +void t4_pmtx_get_stats(struct adapter *adap, u32 cnt[], u64 cycles[]); +void t4_pmrx_get_stats(struct adapter *adap, u32 cnt[], u64 cycles[]); +int t4_read_cim_ibq(struct adapter *adap, unsigned int qid, u32 *data, + size_t n); +int t4_read_cim_obq(struct adapter *adap, unsigned int qid, u32 *data, + size_t n); +int t4_cim_read(struct adapter *adap, unsigned int addr, unsigned int n, + unsigned int *valp); +int t4_cim_write(struct adapter *adap, unsigned int addr, unsigned int n, + const unsigned int *valp); +int t4_cim_read_la(struct adapter *adap, u32 *la_buf, unsigned int *wrptr); +void t4_read_cimq_cfg(struct adapter *adap, u16 *base, u16 *size, u16 *thres); const char *t4_get_port_type_description(enum fw_port_type port_type); void t4_get_port_stats(struct adapter *adap, int idx, struct port_stats *p); void t4_read_mtu_tbl(struct adapter *adap, u16 *mtus, u8 *mtu_log); +void t4_read_cong_tbl(struct adapter *adap, u16 incr[NMTUS][NCCTRL_WIN]); void t4_tp_wr_bits_indirect(struct adapter *adap, unsigned int addr, unsigned int mask, unsigned int val); +void t4_tp_read_la(struct adapter *adap, u64 *la_buf, unsigned int *wrptr); void t4_tp_get_tcp_stats(struct adapter *adap, struct tp_tcp_stats *v4, struct tp_tcp_stats *v6); void t4_load_mtus(struct adapter *adap, const unsigned short *mtus, const unsigned short *alpha, const unsigned short *beta); +void t4_ulprx_read_la(struct adapter *adap, u32 *la_buf); + void t4_mk_filtdelwr(unsigned int ftid, struct fw_filter_wr *wr, int qid); void t4_wol_magic_enable(struct adapter *adap, unsigned int port, diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c index a35d1ec6950e..6074680bc985 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.c @@ -22,7 +22,7 @@ /* DCBx version control */ -char *dcb_ver_array[] = { +static const char * const dcb_ver_array[] = { "Unknown", "DCBx-CIN", "DCBx-CEE 1.01", @@ -428,7 +428,10 @@ static void cxgb4_getpgtccfg(struct net_device *dev, int tc, } *pgid = (be32_to_cpu(pcmd.u.dcb.pgid.pgid) >> (tc * 4)) & 0xf; - INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); + if (local) + INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); + else + INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE; err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); if (err != FW_PORT_DCB_CFG_SUCCESS) { @@ -900,6 +903,88 @@ cxgb4_ieee_negotiation_complete(struct net_device *dev, (dcb->supported & DCB_CAP_DCBX_VER_IEEE)); } +static int cxgb4_ieee_read_ets(struct net_device *dev, struct ieee_ets *ets, + int local) +{ + struct port_info *pi = netdev2pinfo(dev); + struct port_dcb_info *dcb = &pi->dcb; + struct adapter *adap = pi->adapter; + uint32_t tc_info; + struct fw_port_cmd pcmd; + int i, bwg, err; + + if (!(dcb->msgs & (CXGB4_DCB_FW_PGID | CXGB4_DCB_FW_PGRATE))) + return 0; + + ets->ets_cap = dcb->pg_num_tcs_supported; + + if (local) { + ets->willing = 1; + INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); + } else { + INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); + } + + pcmd.u.dcb.pgid.type = FW_PORT_DCB_TYPE_PGID; + err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); + if (err != FW_PORT_DCB_CFG_SUCCESS) { + dev_err(adap->pdev_dev, "DCB read PGID failed with %d\n", -err); + return err; + } + + tc_info = be32_to_cpu(pcmd.u.dcb.pgid.pgid); + + if (local) + INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); + else + INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); + + pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE; + err = t4_wr_mbox(adap, adap->mbox, &pcmd, sizeof(pcmd), &pcmd); + if (err != FW_PORT_DCB_CFG_SUCCESS) { + dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n", + -err); + return err; + } + + for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { + bwg = (tc_info >> ((7 - i) * 4)) & 0xF; + ets->prio_tc[i] = bwg; + ets->tc_tx_bw[i] = pcmd.u.dcb.pgrate.pgrate[i]; + ets->tc_rx_bw[i] = ets->tc_tx_bw[i]; + ets->tc_tsa[i] = pcmd.u.dcb.pgrate.tsa[i]; + } + + return 0; +} + +static int cxgb4_ieee_get_ets(struct net_device *dev, struct ieee_ets *ets) +{ + return cxgb4_ieee_read_ets(dev, ets, 1); +} + +/* We reuse this for peer PFC as well, as we can't have it enabled one way */ +static int cxgb4_ieee_get_pfc(struct net_device *dev, struct ieee_pfc *pfc) +{ + struct port_info *pi = netdev2pinfo(dev); + struct port_dcb_info *dcb = &pi->dcb; + + memset(pfc, 0, sizeof(struct ieee_pfc)); + + if (!(dcb->msgs & CXGB4_DCB_FW_PFC)) + return 0; + + pfc->pfc_cap = dcb->pfc_num_tcs_supported; + pfc->pfc_en = bitswap_1(dcb->pfcen); + + return 0; +} + +static int cxgb4_ieee_peer_ets(struct net_device *dev, struct ieee_ets *ets) +{ + return cxgb4_ieee_read_ets(dev, ets, 0); +} + /* Fill in the Application User Priority Map associated with the * specified Application. * Priority for IEEE dcb_app is an integer, with 0 being a valid value @@ -1106,14 +1191,23 @@ static int cxgb4_cee_peer_getpfc(struct net_device *dev, struct cee_pfc *pfc) struct port_info *pi = netdev2pinfo(dev); cxgb4_getnumtcs(dev, DCB_NUMTCS_ATTR_PFC, &(pfc->tcs_supported)); - pfc->pfc_en = pi->dcb.pfcen; + + /* Firmware sends this to us in a formwat that is a bit flipped version + * of spec, correct it before we send it to host. This is taken care of + * by bit shifting in other uses of pfcen + */ + pfc->pfc_en = bitswap_1(pi->dcb.pfcen); return 0; } const struct dcbnl_rtnl_ops cxgb4_dcb_ops = { + .ieee_getets = cxgb4_ieee_get_ets, + .ieee_getpfc = cxgb4_ieee_get_pfc, .ieee_getapp = cxgb4_ieee_getapp, .ieee_setapp = cxgb4_ieee_setapp, + .ieee_peer_getets = cxgb4_ieee_peer_ets, + .ieee_peer_getpfc = cxgb4_ieee_get_pfc, /* CEE std */ .getstate = cxgb4_getstate, diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.h index 31ce425616c9..ccf24d3dc982 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_dcb.h @@ -136,6 +136,17 @@ void cxgb4_dcb_handle_fw_update(struct adapter *, const struct fw_port_cmd *); void cxgb4_dcb_set_caps(struct adapter *, const struct fw_port_cmd *); extern const struct dcbnl_rtnl_ops cxgb4_dcb_ops; +static inline __u8 bitswap_1(unsigned char val) +{ + return ((val & 0x80) >> 7) | + ((val & 0x40) >> 5) | + ((val & 0x20) >> 3) | + ((val & 0x10) >> 1) | + ((val & 0x08) << 1) | + ((val & 0x04) << 3) | + ((val & 0x02) << 5) | + ((val & 0x01) << 7); +} #define CXGB4_DCB_ENABLED true #else /* !CONFIG_CHELSIO_T4_DCB */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index c98a350d857e..d221f6b28fcd 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -36,13 +36,1867 @@ #include <linux/debugfs.h> #include <linux/string_helpers.h> #include <linux/sort.h> +#include <linux/ctype.h> #include "cxgb4.h" #include "t4_regs.h" +#include "t4_values.h" #include "t4fw_api.h" #include "cxgb4_debugfs.h" +#include "clip_tbl.h" #include "l2t.h" +/* generic seq_file support for showing a table of size rows x width. */ +static void *seq_tab_get_idx(struct seq_tab *tb, loff_t pos) +{ + pos -= tb->skip_first; + return pos >= tb->rows ? NULL : &tb->data[pos * tb->width]; +} + +static void *seq_tab_start(struct seq_file *seq, loff_t *pos) +{ + struct seq_tab *tb = seq->private; + + if (tb->skip_first && *pos == 0) + return SEQ_START_TOKEN; + + return seq_tab_get_idx(tb, *pos); +} + +static void *seq_tab_next(struct seq_file *seq, void *v, loff_t *pos) +{ + v = seq_tab_get_idx(seq->private, *pos + 1); + if (v) + ++*pos; + return v; +} + +static void seq_tab_stop(struct seq_file *seq, void *v) +{ +} + +static int seq_tab_show(struct seq_file *seq, void *v) +{ + const struct seq_tab *tb = seq->private; + + return tb->show(seq, v, ((char *)v - tb->data) / tb->width); +} + +static const struct seq_operations seq_tab_ops = { + .start = seq_tab_start, + .next = seq_tab_next, + .stop = seq_tab_stop, + .show = seq_tab_show +}; + +struct seq_tab *seq_open_tab(struct file *f, unsigned int rows, + unsigned int width, unsigned int have_header, + int (*show)(struct seq_file *seq, void *v, int i)) +{ + struct seq_tab *p; + + p = __seq_open_private(f, &seq_tab_ops, sizeof(*p) + rows * width); + if (p) { + p->show = show; + p->rows = rows; + p->width = width; + p->skip_first = have_header != 0; + } + return p; +} + +/* Trim the size of a seq_tab to the supplied number of rows. The operation is + * irreversible. + */ +static int seq_tab_trim(struct seq_tab *p, unsigned int new_rows) +{ + if (new_rows > p->rows) + return -EINVAL; + p->rows = new_rows; + return 0; +} + +static int cim_la_show(struct seq_file *seq, void *v, int idx) +{ + if (v == SEQ_START_TOKEN) + seq_puts(seq, "Status Data PC LS0Stat LS0Addr " + " LS0Data\n"); + else { + const u32 *p = v; + + seq_printf(seq, + " %02x %x%07x %x%07x %08x %08x %08x%08x%08x%08x\n", + (p[0] >> 4) & 0xff, p[0] & 0xf, p[1] >> 4, + p[1] & 0xf, p[2] >> 4, p[2] & 0xf, p[3], p[4], p[5], + p[6], p[7]); + } + return 0; +} + +static int cim_la_show_3in1(struct seq_file *seq, void *v, int idx) +{ + if (v == SEQ_START_TOKEN) { + seq_puts(seq, "Status Data PC\n"); + } else { + const u32 *p = v; + + seq_printf(seq, " %02x %08x %08x\n", p[5] & 0xff, p[6], + p[7]); + seq_printf(seq, " %02x %02x%06x %02x%06x\n", + (p[3] >> 8) & 0xff, p[3] & 0xff, p[4] >> 8, + p[4] & 0xff, p[5] >> 8); + seq_printf(seq, " %02x %x%07x %x%07x\n", (p[0] >> 4) & 0xff, + p[0] & 0xf, p[1] >> 4, p[1] & 0xf, p[2] >> 4); + } + return 0; +} + +static int cim_la_open(struct inode *inode, struct file *file) +{ + int ret; + unsigned int cfg; + struct seq_tab *p; + struct adapter *adap = inode->i_private; + + ret = t4_cim_read(adap, UP_UP_DBG_LA_CFG_A, 1, &cfg); + if (ret) + return ret; + + p = seq_open_tab(file, adap->params.cim_la_size / 8, 8 * sizeof(u32), 1, + cfg & UPDBGLACAPTPCONLY_F ? + cim_la_show_3in1 : cim_la_show); + if (!p) + return -ENOMEM; + + ret = t4_cim_read_la(adap, (u32 *)p->data, NULL); + if (ret) + seq_release_private(inode, file); + return ret; +} + +static const struct file_operations cim_la_fops = { + .owner = THIS_MODULE, + .open = cim_la_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private +}; + +static int cim_qcfg_show(struct seq_file *seq, void *v) +{ + static const char * const qname[] = { + "TP0", "TP1", "ULP", "SGE0", "SGE1", "NC-SI", + "ULP0", "ULP1", "ULP2", "ULP3", "SGE", "NC-SI", + "SGE0-RX", "SGE1-RX" + }; + + int i; + struct adapter *adap = seq->private; + u16 base[CIM_NUM_IBQ + CIM_NUM_OBQ_T5]; + u16 size[CIM_NUM_IBQ + CIM_NUM_OBQ_T5]; + u32 stat[(4 * (CIM_NUM_IBQ + CIM_NUM_OBQ_T5))]; + u16 thres[CIM_NUM_IBQ]; + u32 obq_wr_t4[2 * CIM_NUM_OBQ], *wr; + u32 obq_wr_t5[2 * CIM_NUM_OBQ_T5]; + u32 *p = stat; + int cim_num_obq = is_t4(adap->params.chip) ? + CIM_NUM_OBQ : CIM_NUM_OBQ_T5; + + i = t4_cim_read(adap, is_t4(adap->params.chip) ? UP_IBQ_0_RDADDR_A : + UP_IBQ_0_SHADOW_RDADDR_A, + ARRAY_SIZE(stat), stat); + if (!i) { + if (is_t4(adap->params.chip)) { + i = t4_cim_read(adap, UP_OBQ_0_REALADDR_A, + ARRAY_SIZE(obq_wr_t4), obq_wr_t4); + wr = obq_wr_t4; + } else { + i = t4_cim_read(adap, UP_OBQ_0_SHADOW_REALADDR_A, + ARRAY_SIZE(obq_wr_t5), obq_wr_t5); + wr = obq_wr_t5; + } + } + if (i) + return i; + + t4_read_cimq_cfg(adap, base, size, thres); + + seq_printf(seq, + " Queue Base Size Thres RdPtr WrPtr SOP EOP Avail\n"); + for (i = 0; i < CIM_NUM_IBQ; i++, p += 4) + seq_printf(seq, "%7s %5x %5u %5u %6x %4x %4u %4u %5u\n", + qname[i], base[i], size[i], thres[i], + IBQRDADDR_G(p[0]), IBQWRADDR_G(p[1]), + QUESOPCNT_G(p[3]), QUEEOPCNT_G(p[3]), + QUEREMFLITS_G(p[2]) * 16); + for ( ; i < CIM_NUM_IBQ + cim_num_obq; i++, p += 4, wr += 2) + seq_printf(seq, "%7s %5x %5u %12x %4x %4u %4u %5u\n", + qname[i], base[i], size[i], + QUERDADDR_G(p[0]) & 0x3fff, wr[0] - base[i], + QUESOPCNT_G(p[3]), QUEEOPCNT_G(p[3]), + QUEREMFLITS_G(p[2]) * 16); + return 0; +} + +static int cim_qcfg_open(struct inode *inode, struct file *file) +{ + return single_open(file, cim_qcfg_show, inode->i_private); +} + +static const struct file_operations cim_qcfg_fops = { + .owner = THIS_MODULE, + .open = cim_qcfg_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int cimq_show(struct seq_file *seq, void *v, int idx) +{ + const u32 *p = v; + + seq_printf(seq, "%#06x: %08x %08x %08x %08x\n", idx * 16, p[0], p[1], + p[2], p[3]); + return 0; +} + +static int cim_ibq_open(struct inode *inode, struct file *file) +{ + int ret; + struct seq_tab *p; + unsigned int qid = (uintptr_t)inode->i_private & 7; + struct adapter *adap = inode->i_private - qid; + + p = seq_open_tab(file, CIM_IBQ_SIZE, 4 * sizeof(u32), 0, cimq_show); + if (!p) + return -ENOMEM; + + ret = t4_read_cim_ibq(adap, qid, (u32 *)p->data, CIM_IBQ_SIZE * 4); + if (ret < 0) + seq_release_private(inode, file); + else + ret = 0; + return ret; +} + +static const struct file_operations cim_ibq_fops = { + .owner = THIS_MODULE, + .open = cim_ibq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private +}; + +static int cim_obq_open(struct inode *inode, struct file *file) +{ + int ret; + struct seq_tab *p; + unsigned int qid = (uintptr_t)inode->i_private & 7; + struct adapter *adap = inode->i_private - qid; + + p = seq_open_tab(file, 6 * CIM_OBQ_SIZE, 4 * sizeof(u32), 0, cimq_show); + if (!p) + return -ENOMEM; + + ret = t4_read_cim_obq(adap, qid, (u32 *)p->data, 6 * CIM_OBQ_SIZE * 4); + if (ret < 0) { + seq_release_private(inode, file); + } else { + seq_tab_trim(p, ret / 4); + ret = 0; + } + return ret; +} + +static const struct file_operations cim_obq_fops = { + .owner = THIS_MODULE, + .open = cim_obq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private +}; + +struct field_desc { + const char *name; + unsigned int start; + unsigned int width; +}; + +static void field_desc_show(struct seq_file *seq, u64 v, + const struct field_desc *p) +{ + char buf[32]; + int line_size = 0; + + while (p->name) { + u64 mask = (1ULL << p->width) - 1; + int len = scnprintf(buf, sizeof(buf), "%s: %llu", p->name, + ((unsigned long long)v >> p->start) & mask); + + if (line_size + len >= 79) { + line_size = 8; + seq_puts(seq, "\n "); + } + seq_printf(seq, "%s ", buf); + line_size += len + 1; + p++; + } + seq_putc(seq, '\n'); +} + +static struct field_desc tp_la0[] = { + { "RcfOpCodeOut", 60, 4 }, + { "State", 56, 4 }, + { "WcfState", 52, 4 }, + { "RcfOpcSrcOut", 50, 2 }, + { "CRxError", 49, 1 }, + { "ERxError", 48, 1 }, + { "SanityFailed", 47, 1 }, + { "SpuriousMsg", 46, 1 }, + { "FlushInputMsg", 45, 1 }, + { "FlushInputCpl", 44, 1 }, + { "RssUpBit", 43, 1 }, + { "RssFilterHit", 42, 1 }, + { "Tid", 32, 10 }, + { "InitTcb", 31, 1 }, + { "LineNumber", 24, 7 }, + { "Emsg", 23, 1 }, + { "EdataOut", 22, 1 }, + { "Cmsg", 21, 1 }, + { "CdataOut", 20, 1 }, + { "EreadPdu", 19, 1 }, + { "CreadPdu", 18, 1 }, + { "TunnelPkt", 17, 1 }, + { "RcfPeerFin", 16, 1 }, + { "RcfReasonOut", 12, 4 }, + { "TxCchannel", 10, 2 }, + { "RcfTxChannel", 8, 2 }, + { "RxEchannel", 6, 2 }, + { "RcfRxChannel", 5, 1 }, + { "RcfDataOutSrdy", 4, 1 }, + { "RxDvld", 3, 1 }, + { "RxOoDvld", 2, 1 }, + { "RxCongestion", 1, 1 }, + { "TxCongestion", 0, 1 }, + { NULL } +}; + +static int tp_la_show(struct seq_file *seq, void *v, int idx) +{ + const u64 *p = v; + + field_desc_show(seq, *p, tp_la0); + return 0; +} + +static int tp_la_show2(struct seq_file *seq, void *v, int idx) +{ + const u64 *p = v; + + if (idx) + seq_putc(seq, '\n'); + field_desc_show(seq, p[0], tp_la0); + if (idx < (TPLA_SIZE / 2 - 1) || p[1] != ~0ULL) + field_desc_show(seq, p[1], tp_la0); + return 0; +} + +static int tp_la_show3(struct seq_file *seq, void *v, int idx) +{ + static struct field_desc tp_la1[] = { + { "CplCmdIn", 56, 8 }, + { "CplCmdOut", 48, 8 }, + { "ESynOut", 47, 1 }, + { "EAckOut", 46, 1 }, + { "EFinOut", 45, 1 }, + { "ERstOut", 44, 1 }, + { "SynIn", 43, 1 }, + { "AckIn", 42, 1 }, + { "FinIn", 41, 1 }, + { "RstIn", 40, 1 }, + { "DataIn", 39, 1 }, + { "DataInVld", 38, 1 }, + { "PadIn", 37, 1 }, + { "RxBufEmpty", 36, 1 }, + { "RxDdp", 35, 1 }, + { "RxFbCongestion", 34, 1 }, + { "TxFbCongestion", 33, 1 }, + { "TxPktSumSrdy", 32, 1 }, + { "RcfUlpType", 28, 4 }, + { "Eread", 27, 1 }, + { "Ebypass", 26, 1 }, + { "Esave", 25, 1 }, + { "Static0", 24, 1 }, + { "Cread", 23, 1 }, + { "Cbypass", 22, 1 }, + { "Csave", 21, 1 }, + { "CPktOut", 20, 1 }, + { "RxPagePoolFull", 18, 2 }, + { "RxLpbkPkt", 17, 1 }, + { "TxLpbkPkt", 16, 1 }, + { "RxVfValid", 15, 1 }, + { "SynLearned", 14, 1 }, + { "SetDelEntry", 13, 1 }, + { "SetInvEntry", 12, 1 }, + { "CpcmdDvld", 11, 1 }, + { "CpcmdSave", 10, 1 }, + { "RxPstructsFull", 8, 2 }, + { "EpcmdDvld", 7, 1 }, + { "EpcmdFlush", 6, 1 }, + { "EpcmdTrimPrefix", 5, 1 }, + { "EpcmdTrimPostfix", 4, 1 }, + { "ERssIp4Pkt", 3, 1 }, + { "ERssIp6Pkt", 2, 1 }, + { "ERssTcpUdpPkt", 1, 1 }, + { "ERssFceFipPkt", 0, 1 }, + { NULL } + }; + static struct field_desc tp_la2[] = { + { "CplCmdIn", 56, 8 }, + { "MpsVfVld", 55, 1 }, + { "MpsPf", 52, 3 }, + { "MpsVf", 44, 8 }, + { "SynIn", 43, 1 }, + { "AckIn", 42, 1 }, + { "FinIn", 41, 1 }, + { "RstIn", 40, 1 }, + { "DataIn", 39, 1 }, + { "DataInVld", 38, 1 }, + { "PadIn", 37, 1 }, + { "RxBufEmpty", 36, 1 }, + { "RxDdp", 35, 1 }, + { "RxFbCongestion", 34, 1 }, + { "TxFbCongestion", 33, 1 }, + { "TxPktSumSrdy", 32, 1 }, + { "RcfUlpType", 28, 4 }, + { "Eread", 27, 1 }, + { "Ebypass", 26, 1 }, + { "Esave", 25, 1 }, + { "Static0", 24, 1 }, + { "Cread", 23, 1 }, + { "Cbypass", 22, 1 }, + { "Csave", 21, 1 }, + { "CPktOut", 20, 1 }, + { "RxPagePoolFull", 18, 2 }, + { "RxLpbkPkt", 17, 1 }, + { "TxLpbkPkt", 16, 1 }, + { "RxVfValid", 15, 1 }, + { "SynLearned", 14, 1 }, + { "SetDelEntry", 13, 1 }, + { "SetInvEntry", 12, 1 }, + { "CpcmdDvld", 11, 1 }, + { "CpcmdSave", 10, 1 }, + { "RxPstructsFull", 8, 2 }, + { "EpcmdDvld", 7, 1 }, + { "EpcmdFlush", 6, 1 }, + { "EpcmdTrimPrefix", 5, 1 }, + { "EpcmdTrimPostfix", 4, 1 }, + { "ERssIp4Pkt", 3, 1 }, + { "ERssIp6Pkt", 2, 1 }, + { "ERssTcpUdpPkt", 1, 1 }, + { "ERssFceFipPkt", 0, 1 }, + { NULL } + }; + const u64 *p = v; + + if (idx) + seq_putc(seq, '\n'); + field_desc_show(seq, p[0], tp_la0); + if (idx < (TPLA_SIZE / 2 - 1) || p[1] != ~0ULL) + field_desc_show(seq, p[1], (p[0] & BIT(17)) ? tp_la2 : tp_la1); + return 0; +} + +static int tp_la_open(struct inode *inode, struct file *file) +{ + struct seq_tab *p; + struct adapter *adap = inode->i_private; + + switch (DBGLAMODE_G(t4_read_reg(adap, TP_DBG_LA_CONFIG_A))) { + case 2: + p = seq_open_tab(file, TPLA_SIZE / 2, 2 * sizeof(u64), 0, + tp_la_show2); + break; + case 3: + p = seq_open_tab(file, TPLA_SIZE / 2, 2 * sizeof(u64), 0, + tp_la_show3); + break; + default: + p = seq_open_tab(file, TPLA_SIZE, sizeof(u64), 0, tp_la_show); + } + if (!p) + return -ENOMEM; + + t4_tp_read_la(adap, (u64 *)p->data, NULL); + return 0; +} + +static ssize_t tp_la_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + int err; + char s[32]; + unsigned long val; + size_t size = min(sizeof(s) - 1, count); + struct adapter *adap = FILE_DATA(file)->i_private; + + if (copy_from_user(s, buf, size)) + return -EFAULT; + s[size] = '\0'; + err = kstrtoul(s, 0, &val); + if (err) + return err; + if (val > 0xffff) + return -EINVAL; + adap->params.tp.la_mask = val << 16; + t4_set_reg_field(adap, TP_DBG_LA_CONFIG_A, 0xffff0000U, + adap->params.tp.la_mask); + return count; +} + +static const struct file_operations tp_la_fops = { + .owner = THIS_MODULE, + .open = tp_la_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, + .write = tp_la_write +}; + +static int ulprx_la_show(struct seq_file *seq, void *v, int idx) +{ + const u32 *p = v; + + if (v == SEQ_START_TOKEN) + seq_puts(seq, " Pcmd Type Message" + " Data\n"); + else + seq_printf(seq, "%08x%08x %4x %08x %08x%08x%08x%08x\n", + p[1], p[0], p[2], p[3], p[7], p[6], p[5], p[4]); + return 0; +} + +static int ulprx_la_open(struct inode *inode, struct file *file) +{ + struct seq_tab *p; + struct adapter *adap = inode->i_private; + + p = seq_open_tab(file, ULPRX_LA_SIZE, 8 * sizeof(u32), 1, + ulprx_la_show); + if (!p) + return -ENOMEM; + + t4_ulprx_read_la(adap, (u32 *)p->data); + return 0; +} + +static const struct file_operations ulprx_la_fops = { + .owner = THIS_MODULE, + .open = ulprx_la_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private +}; + +/* Show the PM memory stats. These stats include: + * + * TX: + * Read: memory read operation + * Write Bypass: cut-through + * Bypass + mem: cut-through and save copy + * + * RX: + * Read: memory read + * Write Bypass: cut-through + * Flush: payload trim or drop + */ +static int pm_stats_show(struct seq_file *seq, void *v) +{ + static const char * const tx_pm_stats[] = { + "Read:", "Write bypass:", "Write mem:", "Bypass + mem:" + }; + static const char * const rx_pm_stats[] = { + "Read:", "Write bypass:", "Write mem:", "Flush:" + }; + + int i; + u32 tx_cnt[PM_NSTATS], rx_cnt[PM_NSTATS]; + u64 tx_cyc[PM_NSTATS], rx_cyc[PM_NSTATS]; + struct adapter *adap = seq->private; + + t4_pmtx_get_stats(adap, tx_cnt, tx_cyc); + t4_pmrx_get_stats(adap, rx_cnt, rx_cyc); + + seq_printf(seq, "%13s %10s %20s\n", " ", "Tx pcmds", "Tx bytes"); + for (i = 0; i < PM_NSTATS - 1; i++) + seq_printf(seq, "%-13s %10u %20llu\n", + tx_pm_stats[i], tx_cnt[i], tx_cyc[i]); + + seq_printf(seq, "%13s %10s %20s\n", " ", "Rx pcmds", "Rx bytes"); + for (i = 0; i < PM_NSTATS - 1; i++) + seq_printf(seq, "%-13s %10u %20llu\n", + rx_pm_stats[i], rx_cnt[i], rx_cyc[i]); + return 0; +} + +static int pm_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, pm_stats_show, inode->i_private); +} + +static ssize_t pm_stats_clear(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct adapter *adap = FILE_DATA(file)->i_private; + + t4_write_reg(adap, PM_RX_STAT_CONFIG_A, 0); + t4_write_reg(adap, PM_TX_STAT_CONFIG_A, 0); + return count; +} + +static const struct file_operations pm_stats_debugfs_fops = { + .owner = THIS_MODULE, + .open = pm_stats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = pm_stats_clear +}; + +static int cctrl_tbl_show(struct seq_file *seq, void *v) +{ + static const char * const dec_fac[] = { + "0.5", "0.5625", "0.625", "0.6875", "0.75", "0.8125", "0.875", + "0.9375" }; + + int i; + u16 incr[NMTUS][NCCTRL_WIN]; + struct adapter *adap = seq->private; + + t4_read_cong_tbl(adap, incr); + + for (i = 0; i < NCCTRL_WIN; ++i) { + seq_printf(seq, "%2d: %4u %4u %4u %4u %4u %4u %4u %4u\n", i, + incr[0][i], incr[1][i], incr[2][i], incr[3][i], + incr[4][i], incr[5][i], incr[6][i], incr[7][i]); + seq_printf(seq, "%8u %4u %4u %4u %4u %4u %4u %4u %5u %s\n", + incr[8][i], incr[9][i], incr[10][i], incr[11][i], + incr[12][i], incr[13][i], incr[14][i], incr[15][i], + adap->params.a_wnd[i], + dec_fac[adap->params.b_wnd[i]]); + } + return 0; +} + +DEFINE_SIMPLE_DEBUGFS_FILE(cctrl_tbl); + +/* Format a value in a unit that differs from the value's native unit by the + * given factor. + */ +static char *unit_conv(char *buf, size_t len, unsigned int val, + unsigned int factor) +{ + unsigned int rem = val % factor; + + if (rem == 0) { + snprintf(buf, len, "%u", val / factor); + } else { + while (rem % 10 == 0) + rem /= 10; + snprintf(buf, len, "%u.%u", val / factor, rem); + } + return buf; +} + +static int clk_show(struct seq_file *seq, void *v) +{ + char buf[32]; + struct adapter *adap = seq->private; + unsigned int cclk_ps = 1000000000 / adap->params.vpd.cclk; /* in ps */ + u32 res = t4_read_reg(adap, TP_TIMER_RESOLUTION_A); + unsigned int tre = TIMERRESOLUTION_G(res); + unsigned int dack_re = DELAYEDACKRESOLUTION_G(res); + unsigned long long tp_tick_us = (cclk_ps << tre) / 1000000; /* in us */ + + seq_printf(seq, "Core clock period: %s ns\n", + unit_conv(buf, sizeof(buf), cclk_ps, 1000)); + seq_printf(seq, "TP timer tick: %s us\n", + unit_conv(buf, sizeof(buf), (cclk_ps << tre), 1000000)); + seq_printf(seq, "TCP timestamp tick: %s us\n", + unit_conv(buf, sizeof(buf), + (cclk_ps << TIMESTAMPRESOLUTION_G(res)), 1000000)); + seq_printf(seq, "DACK tick: %s us\n", + unit_conv(buf, sizeof(buf), (cclk_ps << dack_re), 1000000)); + seq_printf(seq, "DACK timer: %u us\n", + ((cclk_ps << dack_re) / 1000000) * + t4_read_reg(adap, TP_DACK_TIMER_A)); + seq_printf(seq, "Retransmit min: %llu us\n", + tp_tick_us * t4_read_reg(adap, TP_RXT_MIN_A)); + seq_printf(seq, "Retransmit max: %llu us\n", + tp_tick_us * t4_read_reg(adap, TP_RXT_MAX_A)); + seq_printf(seq, "Persist timer min: %llu us\n", + tp_tick_us * t4_read_reg(adap, TP_PERS_MIN_A)); + seq_printf(seq, "Persist timer max: %llu us\n", + tp_tick_us * t4_read_reg(adap, TP_PERS_MAX_A)); + seq_printf(seq, "Keepalive idle timer: %llu us\n", + tp_tick_us * t4_read_reg(adap, TP_KEEP_IDLE_A)); + seq_printf(seq, "Keepalive interval: %llu us\n", + tp_tick_us * t4_read_reg(adap, TP_KEEP_INTVL_A)); + seq_printf(seq, "Initial SRTT: %llu us\n", + tp_tick_us * INITSRTT_G(t4_read_reg(adap, TP_INIT_SRTT_A))); + seq_printf(seq, "FINWAIT2 timer: %llu us\n", + tp_tick_us * t4_read_reg(adap, TP_FINWAIT2_TIMER_A)); + + return 0; +} + +DEFINE_SIMPLE_DEBUGFS_FILE(clk); + +/* Firmware Device Log dump. */ +static const char * const devlog_level_strings[] = { + [FW_DEVLOG_LEVEL_EMERG] = "EMERG", + [FW_DEVLOG_LEVEL_CRIT] = "CRIT", + [FW_DEVLOG_LEVEL_ERR] = "ERR", + [FW_DEVLOG_LEVEL_NOTICE] = "NOTICE", + [FW_DEVLOG_LEVEL_INFO] = "INFO", + [FW_DEVLOG_LEVEL_DEBUG] = "DEBUG" +}; + +static const char * const devlog_facility_strings[] = { + [FW_DEVLOG_FACILITY_CORE] = "CORE", + [FW_DEVLOG_FACILITY_SCHED] = "SCHED", + [FW_DEVLOG_FACILITY_TIMER] = "TIMER", + [FW_DEVLOG_FACILITY_RES] = "RES", + [FW_DEVLOG_FACILITY_HW] = "HW", + [FW_DEVLOG_FACILITY_FLR] = "FLR", + [FW_DEVLOG_FACILITY_DMAQ] = "DMAQ", + [FW_DEVLOG_FACILITY_PHY] = "PHY", + [FW_DEVLOG_FACILITY_MAC] = "MAC", + [FW_DEVLOG_FACILITY_PORT] = "PORT", + [FW_DEVLOG_FACILITY_VI] = "VI", + [FW_DEVLOG_FACILITY_FILTER] = "FILTER", + [FW_DEVLOG_FACILITY_ACL] = "ACL", + [FW_DEVLOG_FACILITY_TM] = "TM", + [FW_DEVLOG_FACILITY_QFC] = "QFC", + [FW_DEVLOG_FACILITY_DCB] = "DCB", + [FW_DEVLOG_FACILITY_ETH] = "ETH", + [FW_DEVLOG_FACILITY_OFLD] = "OFLD", + [FW_DEVLOG_FACILITY_RI] = "RI", + [FW_DEVLOG_FACILITY_ISCSI] = "ISCSI", + [FW_DEVLOG_FACILITY_FCOE] = "FCOE", + [FW_DEVLOG_FACILITY_FOISCSI] = "FOISCSI", + [FW_DEVLOG_FACILITY_FOFCOE] = "FOFCOE" +}; + +/* Information gathered by Device Log Open routine for the display routine. + */ +struct devlog_info { + unsigned int nentries; /* number of entries in log[] */ + unsigned int first; /* first [temporal] entry in log[] */ + struct fw_devlog_e log[0]; /* Firmware Device Log */ +}; + +/* Dump a Firmaware Device Log entry. + */ +static int devlog_show(struct seq_file *seq, void *v) +{ + if (v == SEQ_START_TOKEN) + seq_printf(seq, "%10s %15s %8s %8s %s\n", + "Seq#", "Tstamp", "Level", "Facility", "Message"); + else { + struct devlog_info *dinfo = seq->private; + int fidx = (uintptr_t)v - 2; + unsigned long index; + struct fw_devlog_e *e; + + /* Get a pointer to the log entry to display. Skip unused log + * entries. + */ + index = dinfo->first + fidx; + if (index >= dinfo->nentries) + index -= dinfo->nentries; + e = &dinfo->log[index]; + if (e->timestamp == 0) + return 0; + + /* Print the message. This depends on the firmware using + * exactly the same formating strings as the kernel so we may + * eventually have to put a format interpreter in here ... + */ + seq_printf(seq, "%10d %15llu %8s %8s ", + e->seqno, e->timestamp, + (e->level < ARRAY_SIZE(devlog_level_strings) + ? devlog_level_strings[e->level] + : "UNKNOWN"), + (e->facility < ARRAY_SIZE(devlog_facility_strings) + ? devlog_facility_strings[e->facility] + : "UNKNOWN")); + seq_printf(seq, e->fmt, e->params[0], e->params[1], + e->params[2], e->params[3], e->params[4], + e->params[5], e->params[6], e->params[7]); + } + return 0; +} + +/* Sequential File Operations for Device Log. + */ +static inline void *devlog_get_idx(struct devlog_info *dinfo, loff_t pos) +{ + if (pos > dinfo->nentries) + return NULL; + + return (void *)(uintptr_t)(pos + 1); +} + +static void *devlog_start(struct seq_file *seq, loff_t *pos) +{ + struct devlog_info *dinfo = seq->private; + + return (*pos + ? devlog_get_idx(dinfo, *pos) + : SEQ_START_TOKEN); +} + +static void *devlog_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct devlog_info *dinfo = seq->private; + + (*pos)++; + return devlog_get_idx(dinfo, *pos); +} + +static void devlog_stop(struct seq_file *seq, void *v) +{ +} + +static const struct seq_operations devlog_seq_ops = { + .start = devlog_start, + .next = devlog_next, + .stop = devlog_stop, + .show = devlog_show +}; + +/* Set up for reading the firmware's device log. We read the entire log here + * and then display it incrementally in devlog_show(). + */ +static int devlog_open(struct inode *inode, struct file *file) +{ + struct adapter *adap = inode->i_private; + struct devlog_params *dparams = &adap->params.devlog; + struct devlog_info *dinfo; + unsigned int index; + u32 fseqno; + int ret; + + /* If we don't know where the log is we can't do anything. + */ + if (dparams->start == 0) + return -ENXIO; + + /* Allocate the space to read in the firmware's device log and set up + * for the iterated call to our display function. + */ + dinfo = __seq_open_private(file, &devlog_seq_ops, + sizeof(*dinfo) + dparams->size); + if (!dinfo) + return -ENOMEM; + + /* Record the basic log buffer information and read in the raw log. + */ + dinfo->nentries = (dparams->size / sizeof(struct fw_devlog_e)); + dinfo->first = 0; + spin_lock(&adap->win0_lock); + ret = t4_memory_rw(adap, adap->params.drv_memwin, dparams->memtype, + dparams->start, dparams->size, (__be32 *)dinfo->log, + T4_MEMORY_READ); + spin_unlock(&adap->win0_lock); + if (ret) { + seq_release_private(inode, file); + return ret; + } + + /* Translate log multi-byte integral elements into host native format + * and determine where the first entry in the log is. + */ + for (fseqno = ~((u32)0), index = 0; index < dinfo->nentries; index++) { + struct fw_devlog_e *e = &dinfo->log[index]; + int i; + __u32 seqno; + + if (e->timestamp == 0) + continue; + + e->timestamp = (__force __be64)be64_to_cpu(e->timestamp); + seqno = be32_to_cpu(e->seqno); + for (i = 0; i < 8; i++) + e->params[i] = + (__force __be32)be32_to_cpu(e->params[i]); + + if (seqno < fseqno) { + fseqno = seqno; + dinfo->first = index; + } + } + return 0; +} + +static const struct file_operations devlog_fops = { + .owner = THIS_MODULE, + .open = devlog_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private +}; + +static int mbox_show(struct seq_file *seq, void *v) +{ + static const char * const owner[] = { "none", "FW", "driver", + "unknown" }; + + 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)); + seq_printf(seq, "mailbox owned by %s\n\n", owner[i]); + + for (i = 0; i < MBOX_LEN; i += 8) + seq_printf(seq, "%016llx\n", + (unsigned long long)readq(addr + i)); + return 0; +} + +static int mbox_open(struct inode *inode, struct file *file) +{ + return single_open(file, mbox_show, inode->i_private); +} + +static ssize_t mbox_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + int i; + char c = '\n', s[256]; + unsigned long long data[8]; + const struct inode *ino; + unsigned int mbox; + struct adapter *adap; + void __iomem *addr; + void __iomem *ctrl; + + if (count > sizeof(s) - 1 || !count) + return -EINVAL; + if (copy_from_user(s, buf, count)) + return -EFAULT; + s[count] = '\0'; + + if (sscanf(s, "%llx %llx %llx %llx %llx %llx %llx %llx%c", &data[0], + &data[1], &data[2], &data[3], &data[4], &data[5], &data[6], + &data[7], &c) < 8 || c != '\n') + return -EINVAL; + + ino = FILE_DATA(file); + mbox = (uintptr_t)ino->i_private & 7; + adap = ino->i_private - mbox; + addr = adap->regs + PF_REG(mbox, CIM_PF_MAILBOX_DATA_A); + ctrl = addr + MBOX_LEN; + + if (MBOWNER_G(readl(ctrl)) != X_MBOWNER_PL) + return -EBUSY; + + for (i = 0; i < 8; i++) + writeq(data[i], addr + 8 * i); + + writel(MBMSGVALID_F | MBOWNER_V(X_MBOWNER_FW), ctrl); + return count; +} + +static const struct file_operations mbox_debugfs_fops = { + .owner = THIS_MODULE, + .open = mbox_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = mbox_write +}; + +static ssize_t flash_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + loff_t pos = *ppos; + loff_t avail = FILE_DATA(file)->i_size; + struct adapter *adap = file->private_data; + + if (pos < 0) + return -EINVAL; + if (pos >= avail) + return 0; + if (count > avail - pos) + count = avail - pos; + + while (count) { + size_t len; + int ret, ofst; + u8 data[256]; + + ofst = pos & 3; + len = min(count + ofst, sizeof(data)); + ret = t4_read_flash(adap, pos - ofst, (len + 3) / 4, + (u32 *)data, 1); + if (ret) + return ret; + + len -= ofst; + if (copy_to_user(buf, data + ofst, len)) + return -EFAULT; + + buf += len; + pos += len; + count -= len; + } + count = pos - *ppos; + *ppos = pos; + return count; +} + +static const struct file_operations flash_debugfs_fops = { + .owner = THIS_MODULE, + .open = mem_open, + .read = flash_read, +}; + +static inline void tcamxy2valmask(u64 x, u64 y, u8 *addr, u64 *mask) +{ + *mask = x | y; + y = (__force u64)cpu_to_be64(y); + memcpy(addr, (char *)&y + 2, ETH_ALEN); +} + +static int mps_tcam_show(struct seq_file *seq, void *v) +{ + if (v == SEQ_START_TOKEN) + seq_puts(seq, "Idx Ethernet address Mask Vld Ports PF" + " VF Replication " + "P0 P1 P2 P3 ML\n"); + else { + u64 mask; + u8 addr[ETH_ALEN]; + struct adapter *adap = seq->private; + unsigned int idx = (uintptr_t)v - 2; + u64 tcamy = t4_read_reg64(adap, MPS_CLS_TCAM_Y_L(idx)); + u64 tcamx = t4_read_reg64(adap, MPS_CLS_TCAM_X_L(idx)); + u32 cls_lo = t4_read_reg(adap, MPS_CLS_SRAM_L(idx)); + u32 cls_hi = t4_read_reg(adap, MPS_CLS_SRAM_H(idx)); + u32 rplc[4] = {0, 0, 0, 0}; + + if (tcamx & tcamy) { + seq_printf(seq, "%3u -\n", idx); + goto out; + } + + if (cls_lo & REPLICATE_F) { + struct fw_ldst_cmd ldst_cmd; + int ret; + + memset(&ldst_cmd, 0, sizeof(ldst_cmd)); + ldst_cmd.op_to_addrspace = + htonl(FW_CMD_OP_V(FW_LDST_CMD) | + FW_CMD_REQUEST_F | + FW_CMD_READ_F | + FW_LDST_CMD_ADDRSPACE_V( + FW_LDST_ADDRSPC_MPS)); + ldst_cmd.cycles_to_len16 = htonl(FW_LEN16(ldst_cmd)); + ldst_cmd.u.mps.fid_ctl = + htons(FW_LDST_CMD_FID_V(FW_LDST_MPS_RPLC) | + FW_LDST_CMD_CTL_V(idx)); + ret = t4_wr_mbox(adap, adap->mbox, &ldst_cmd, + sizeof(ldst_cmd), &ldst_cmd); + if (ret) + dev_warn(adap->pdev_dev, "Can't read MPS " + "replication map for idx %d: %d\n", + idx, -ret); + else { + rplc[0] = ntohl(ldst_cmd.u.mps.rplc31_0); + rplc[1] = ntohl(ldst_cmd.u.mps.rplc63_32); + rplc[2] = ntohl(ldst_cmd.u.mps.rplc95_64); + rplc[3] = ntohl(ldst_cmd.u.mps.rplc127_96); + } + } + + tcamxy2valmask(tcamx, tcamy, addr, &mask); + seq_printf(seq, "%3u %02x:%02x:%02x:%02x:%02x:%02x %012llx" + "%3c %#x%4u%4d", + idx, addr[0], addr[1], addr[2], addr[3], addr[4], + addr[5], (unsigned long long)mask, + (cls_lo & SRAM_VLD_F) ? 'Y' : 'N', PORTMAP_G(cls_hi), + PF_G(cls_lo), + (cls_lo & VF_VALID_F) ? VF_G(cls_lo) : -1); + if (cls_lo & REPLICATE_F) + seq_printf(seq, " %08x %08x %08x %08x", + rplc[3], rplc[2], rplc[1], rplc[0]); + else + seq_printf(seq, "%36c", ' '); + seq_printf(seq, "%4u%3u%3u%3u %#x\n", + SRAM_PRIO0_G(cls_lo), SRAM_PRIO1_G(cls_lo), + SRAM_PRIO2_G(cls_lo), SRAM_PRIO3_G(cls_lo), + (cls_lo >> MULTILISTEN0_S) & 0xf); + } +out: return 0; +} + +static inline void *mps_tcam_get_idx(struct seq_file *seq, loff_t pos) +{ + struct adapter *adap = seq->private; + int max_mac_addr = is_t4(adap->params.chip) ? + NUM_MPS_CLS_SRAM_L_INSTANCES : + NUM_MPS_T5_CLS_SRAM_L_INSTANCES; + return ((pos <= max_mac_addr) ? (void *)(uintptr_t)(pos + 1) : NULL); +} + +static void *mps_tcam_start(struct seq_file *seq, loff_t *pos) +{ + return *pos ? mps_tcam_get_idx(seq, *pos) : SEQ_START_TOKEN; +} + +static void *mps_tcam_next(struct seq_file *seq, void *v, loff_t *pos) +{ + ++*pos; + return mps_tcam_get_idx(seq, *pos); +} + +static void mps_tcam_stop(struct seq_file *seq, void *v) +{ +} + +static const struct seq_operations mps_tcam_seq_ops = { + .start = mps_tcam_start, + .next = mps_tcam_next, + .stop = mps_tcam_stop, + .show = mps_tcam_show +}; + +static int mps_tcam_open(struct inode *inode, struct file *file) +{ + int res = seq_open(file, &mps_tcam_seq_ops); + + if (!res) { + struct seq_file *seq = file->private_data; + + seq->private = inode->i_private; + } + return res; +} + +static const struct file_operations mps_tcam_debugfs_fops = { + .owner = THIS_MODULE, + .open = mps_tcam_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +/* Display various sensor information. + */ +static int sensors_show(struct seq_file *seq, void *v) +{ + struct adapter *adap = seq->private; + u32 param[7], val[7]; + int ret; + + /* Note that if the sensors haven't been initialized and turned on + * we'll get values of 0, so treat those as "<unknown>" ... + */ + param[0] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) | + FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_DIAG) | + FW_PARAMS_PARAM_Y_V(FW_PARAM_DEV_DIAG_TMP)); + param[1] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) | + FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_DIAG) | + FW_PARAMS_PARAM_Y_V(FW_PARAM_DEV_DIAG_VDD)); + ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 2, + param, val); + + if (ret < 0 || val[0] == 0) + seq_puts(seq, "Temperature: <unknown>\n"); + else + seq_printf(seq, "Temperature: %dC\n", val[0]); + + if (ret < 0 || val[1] == 0) + seq_puts(seq, "Core VDD: <unknown>\n"); + else + seq_printf(seq, "Core VDD: %dmV\n", val[1]); + + return 0; +} + +DEFINE_SIMPLE_DEBUGFS_FILE(sensors); + +#if IS_ENABLED(CONFIG_IPV6) +static int clip_tbl_open(struct inode *inode, struct file *file) +{ + return single_open(file, clip_tbl_show, inode->i_private); +} + +static const struct file_operations clip_tbl_debugfs_fops = { + .owner = THIS_MODULE, + .open = clip_tbl_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release +}; +#endif + +/*RSS Table. + */ + +static int rss_show(struct seq_file *seq, void *v, int idx) +{ + u16 *entry = v; + + seq_printf(seq, "%4d: %4u %4u %4u %4u %4u %4u %4u %4u\n", + idx * 8, entry[0], entry[1], entry[2], entry[3], entry[4], + entry[5], entry[6], entry[7]); + return 0; +} + +static int rss_open(struct inode *inode, struct file *file) +{ + int ret; + struct seq_tab *p; + struct adapter *adap = inode->i_private; + + p = seq_open_tab(file, RSS_NENTRIES / 8, 8 * sizeof(u16), 0, rss_show); + if (!p) + return -ENOMEM; + + ret = t4_read_rss(adap, (u16 *)p->data); + if (ret) + seq_release_private(inode, file); + + return ret; +} + +static const struct file_operations rss_debugfs_fops = { + .owner = THIS_MODULE, + .open = rss_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private +}; + +/* RSS Configuration. + */ + +/* Small utility function to return the strings "yes" or "no" if the supplied + * argument is non-zero. + */ +static const char *yesno(int x) +{ + static const char *yes = "yes"; + static const char *no = "no"; + + return x ? yes : no; +} + +static int rss_config_show(struct seq_file *seq, void *v) +{ + struct adapter *adapter = seq->private; + static const char * const keymode[] = { + "global", + "global and per-VF scramble", + "per-PF and per-VF scramble", + "per-VF and per-VF scramble", + }; + u32 rssconf; + + rssconf = t4_read_reg(adapter, TP_RSS_CONFIG_A); + seq_printf(seq, "TP_RSS_CONFIG: %#x\n", rssconf); + seq_printf(seq, " Tnl4TupEnIpv6: %3s\n", yesno(rssconf & + TNL4TUPENIPV6_F)); + seq_printf(seq, " Tnl2TupEnIpv6: %3s\n", yesno(rssconf & + TNL2TUPENIPV6_F)); + seq_printf(seq, " Tnl4TupEnIpv4: %3s\n", yesno(rssconf & + TNL4TUPENIPV4_F)); + seq_printf(seq, " Tnl2TupEnIpv4: %3s\n", yesno(rssconf & + TNL2TUPENIPV4_F)); + seq_printf(seq, " TnlTcpSel: %3s\n", yesno(rssconf & TNLTCPSEL_F)); + seq_printf(seq, " TnlIp6Sel: %3s\n", yesno(rssconf & TNLIP6SEL_F)); + seq_printf(seq, " TnlVrtSel: %3s\n", yesno(rssconf & TNLVRTSEL_F)); + seq_printf(seq, " TnlMapEn: %3s\n", yesno(rssconf & TNLMAPEN_F)); + seq_printf(seq, " OfdHashSave: %3s\n", yesno(rssconf & + OFDHASHSAVE_F)); + seq_printf(seq, " OfdVrtSel: %3s\n", yesno(rssconf & OFDVRTSEL_F)); + seq_printf(seq, " OfdMapEn: %3s\n", yesno(rssconf & OFDMAPEN_F)); + seq_printf(seq, " OfdLkpEn: %3s\n", yesno(rssconf & OFDLKPEN_F)); + seq_printf(seq, " Syn4TupEnIpv6: %3s\n", yesno(rssconf & + SYN4TUPENIPV6_F)); + seq_printf(seq, " Syn2TupEnIpv6: %3s\n", yesno(rssconf & + SYN2TUPENIPV6_F)); + seq_printf(seq, " Syn4TupEnIpv4: %3s\n", yesno(rssconf & + SYN4TUPENIPV4_F)); + seq_printf(seq, " Syn2TupEnIpv4: %3s\n", yesno(rssconf & + SYN2TUPENIPV4_F)); + seq_printf(seq, " Syn4TupEnIpv6: %3s\n", yesno(rssconf & + SYN4TUPENIPV6_F)); + seq_printf(seq, " SynIp6Sel: %3s\n", yesno(rssconf & SYNIP6SEL_F)); + seq_printf(seq, " SynVrt6Sel: %3s\n", yesno(rssconf & SYNVRTSEL_F)); + seq_printf(seq, " SynMapEn: %3s\n", yesno(rssconf & SYNMAPEN_F)); + seq_printf(seq, " SynLkpEn: %3s\n", yesno(rssconf & SYNLKPEN_F)); + seq_printf(seq, " ChnEn: %3s\n", yesno(rssconf & + CHANNELENABLE_F)); + seq_printf(seq, " PrtEn: %3s\n", yesno(rssconf & + PORTENABLE_F)); + seq_printf(seq, " TnlAllLkp: %3s\n", yesno(rssconf & + TNLALLLOOKUP_F)); + seq_printf(seq, " VrtEn: %3s\n", yesno(rssconf & + VIRTENABLE_F)); + seq_printf(seq, " CngEn: %3s\n", yesno(rssconf & + CONGESTIONENABLE_F)); + seq_printf(seq, " HashToeplitz: %3s\n", yesno(rssconf & + HASHTOEPLITZ_F)); + seq_printf(seq, " Udp4En: %3s\n", yesno(rssconf & UDPENABLE_F)); + seq_printf(seq, " Disable: %3s\n", yesno(rssconf & DISABLE_F)); + + seq_puts(seq, "\n"); + + rssconf = t4_read_reg(adapter, TP_RSS_CONFIG_TNL_A); + seq_printf(seq, "TP_RSS_CONFIG_TNL: %#x\n", rssconf); + seq_printf(seq, " MaskSize: %3d\n", MASKSIZE_G(rssconf)); + seq_printf(seq, " MaskFilter: %3d\n", MASKFILTER_G(rssconf)); + if (CHELSIO_CHIP_VERSION(adapter->params.chip) > CHELSIO_T5) { + seq_printf(seq, " HashAll: %3s\n", + yesno(rssconf & HASHALL_F)); + seq_printf(seq, " HashEth: %3s\n", + yesno(rssconf & HASHETH_F)); + } + seq_printf(seq, " UseWireCh: %3s\n", yesno(rssconf & USEWIRECH_F)); + + seq_puts(seq, "\n"); + + rssconf = t4_read_reg(adapter, TP_RSS_CONFIG_OFD_A); + seq_printf(seq, "TP_RSS_CONFIG_OFD: %#x\n", rssconf); + seq_printf(seq, " MaskSize: %3d\n", MASKSIZE_G(rssconf)); + seq_printf(seq, " RRCplMapEn: %3s\n", yesno(rssconf & + RRCPLMAPEN_F)); + seq_printf(seq, " RRCplQueWidth: %3d\n", RRCPLQUEWIDTH_G(rssconf)); + + seq_puts(seq, "\n"); + + rssconf = t4_read_reg(adapter, TP_RSS_CONFIG_SYN_A); + seq_printf(seq, "TP_RSS_CONFIG_SYN: %#x\n", rssconf); + seq_printf(seq, " MaskSize: %3d\n", MASKSIZE_G(rssconf)); + seq_printf(seq, " UseWireCh: %3s\n", yesno(rssconf & USEWIRECH_F)); + + seq_puts(seq, "\n"); + + rssconf = t4_read_reg(adapter, TP_RSS_CONFIG_VRT_A); + seq_printf(seq, "TP_RSS_CONFIG_VRT: %#x\n", rssconf); + if (CHELSIO_CHIP_VERSION(adapter->params.chip) > CHELSIO_T5) { + seq_printf(seq, " KeyWrAddrX: %3d\n", + KEYWRADDRX_G(rssconf)); + seq_printf(seq, " KeyExtend: %3s\n", + yesno(rssconf & KEYEXTEND_F)); + } + seq_printf(seq, " VfRdRg: %3s\n", yesno(rssconf & VFRDRG_F)); + seq_printf(seq, " VfRdEn: %3s\n", yesno(rssconf & VFRDEN_F)); + seq_printf(seq, " VfPerrEn: %3s\n", yesno(rssconf & VFPERREN_F)); + seq_printf(seq, " KeyPerrEn: %3s\n", yesno(rssconf & KEYPERREN_F)); + seq_printf(seq, " DisVfVlan: %3s\n", yesno(rssconf & + DISABLEVLAN_F)); + seq_printf(seq, " EnUpSwt: %3s\n", yesno(rssconf & ENABLEUP0_F)); + seq_printf(seq, " HashDelay: %3d\n", HASHDELAY_G(rssconf)); + if (CHELSIO_CHIP_VERSION(adapter->params.chip) <= CHELSIO_T5) + seq_printf(seq, " VfWrAddr: %3d\n", VFWRADDR_G(rssconf)); + seq_printf(seq, " KeyMode: %s\n", keymode[KEYMODE_G(rssconf)]); + seq_printf(seq, " VfWrEn: %3s\n", yesno(rssconf & VFWREN_F)); + seq_printf(seq, " KeyWrEn: %3s\n", yesno(rssconf & KEYWREN_F)); + seq_printf(seq, " KeyWrAddr: %3d\n", KEYWRADDR_G(rssconf)); + + seq_puts(seq, "\n"); + + rssconf = t4_read_reg(adapter, TP_RSS_CONFIG_CNG_A); + seq_printf(seq, "TP_RSS_CONFIG_CNG: %#x\n", rssconf); + seq_printf(seq, " ChnCount3: %3s\n", yesno(rssconf & CHNCOUNT3_F)); + seq_printf(seq, " ChnCount2: %3s\n", yesno(rssconf & CHNCOUNT2_F)); + seq_printf(seq, " ChnCount1: %3s\n", yesno(rssconf & CHNCOUNT1_F)); + seq_printf(seq, " ChnCount0: %3s\n", yesno(rssconf & CHNCOUNT0_F)); + seq_printf(seq, " ChnUndFlow3: %3s\n", yesno(rssconf & + CHNUNDFLOW3_F)); + seq_printf(seq, " ChnUndFlow2: %3s\n", yesno(rssconf & + CHNUNDFLOW2_F)); + seq_printf(seq, " ChnUndFlow1: %3s\n", yesno(rssconf & + CHNUNDFLOW1_F)); + seq_printf(seq, " ChnUndFlow0: %3s\n", yesno(rssconf & + CHNUNDFLOW0_F)); + seq_printf(seq, " RstChn3: %3s\n", yesno(rssconf & RSTCHN3_F)); + seq_printf(seq, " RstChn2: %3s\n", yesno(rssconf & RSTCHN2_F)); + seq_printf(seq, " RstChn1: %3s\n", yesno(rssconf & RSTCHN1_F)); + seq_printf(seq, " RstChn0: %3s\n", yesno(rssconf & RSTCHN0_F)); + seq_printf(seq, " UpdVld: %3s\n", yesno(rssconf & UPDVLD_F)); + seq_printf(seq, " Xoff: %3s\n", yesno(rssconf & XOFF_F)); + seq_printf(seq, " UpdChn3: %3s\n", yesno(rssconf & UPDCHN3_F)); + seq_printf(seq, " UpdChn2: %3s\n", yesno(rssconf & UPDCHN2_F)); + seq_printf(seq, " UpdChn1: %3s\n", yesno(rssconf & UPDCHN1_F)); + seq_printf(seq, " UpdChn0: %3s\n", yesno(rssconf & UPDCHN0_F)); + seq_printf(seq, " Queue: %3d\n", QUEUE_G(rssconf)); + + return 0; +} + +DEFINE_SIMPLE_DEBUGFS_FILE(rss_config); + +/* RSS Secret Key. + */ + +static int rss_key_show(struct seq_file *seq, void *v) +{ + u32 key[10]; + + t4_read_rss_key(seq->private, key); + seq_printf(seq, "%08x%08x%08x%08x%08x%08x%08x%08x%08x%08x\n", + key[9], key[8], key[7], key[6], key[5], key[4], key[3], + key[2], key[1], key[0]); + return 0; +} + +static int rss_key_open(struct inode *inode, struct file *file) +{ + return single_open(file, rss_key_show, inode->i_private); +} + +static ssize_t rss_key_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + int i, j; + u32 key[10]; + char s[100], *p; + struct adapter *adap = FILE_DATA(file)->i_private; + + if (count > sizeof(s) - 1) + return -EINVAL; + if (copy_from_user(s, buf, count)) + return -EFAULT; + for (i = count; i > 0 && isspace(s[i - 1]); i--) + ; + s[i] = '\0'; + + for (p = s, i = 9; i >= 0; i--) { + key[i] = 0; + for (j = 0; j < 8; j++, p++) { + if (!isxdigit(*p)) + return -EINVAL; + key[i] = (key[i] << 4) | hex2val(*p); + } + } + + t4_write_rss_key(adap, key, -1); + return count; +} + +static const struct file_operations rss_key_debugfs_fops = { + .owner = THIS_MODULE, + .open = rss_key_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = rss_key_write +}; + +/* PF RSS Configuration. + */ + +struct rss_pf_conf { + u32 rss_pf_map; + u32 rss_pf_mask; + u32 rss_pf_config; +}; + +static int rss_pf_config_show(struct seq_file *seq, void *v, int idx) +{ + struct rss_pf_conf *pfconf; + + if (v == SEQ_START_TOKEN) { + /* use the 0th entry to dump the PF Map Index Size */ + pfconf = seq->private + offsetof(struct seq_tab, data); + seq_printf(seq, "PF Map Index Size = %d\n\n", + LKPIDXSIZE_G(pfconf->rss_pf_map)); + + seq_puts(seq, " RSS PF VF Hash Tuple Enable Default\n"); + seq_puts(seq, " Enable IPF Mask Mask IPv6 IPv4 UDP Queue\n"); + seq_puts(seq, " PF Map Chn Prt Map Size Size Four Two Four Two Four Ch1 Ch0\n"); + } else { + #define G_PFnLKPIDX(map, n) \ + (((map) >> PF1LKPIDX_S*(n)) & PF0LKPIDX_M) + #define G_PFnMSKSIZE(mask, n) \ + (((mask) >> PF1MSKSIZE_S*(n)) & PF1MSKSIZE_M) + + pfconf = v; + seq_printf(seq, "%3d %3s %3s %3s %3d %3d %3d %3s %3s %3s %3s %3s %3d %3d\n", + idx, + yesno(pfconf->rss_pf_config & MAPENABLE_F), + yesno(pfconf->rss_pf_config & CHNENABLE_F), + yesno(pfconf->rss_pf_config & PRTENABLE_F), + G_PFnLKPIDX(pfconf->rss_pf_map, idx), + G_PFnMSKSIZE(pfconf->rss_pf_mask, idx), + IVFWIDTH_G(pfconf->rss_pf_config), + yesno(pfconf->rss_pf_config & IP6FOURTUPEN_F), + yesno(pfconf->rss_pf_config & IP6TWOTUPEN_F), + yesno(pfconf->rss_pf_config & IP4FOURTUPEN_F), + yesno(pfconf->rss_pf_config & IP4TWOTUPEN_F), + yesno(pfconf->rss_pf_config & UDPFOURTUPEN_F), + CH1DEFAULTQUEUE_G(pfconf->rss_pf_config), + CH0DEFAULTQUEUE_G(pfconf->rss_pf_config)); + + #undef G_PFnLKPIDX + #undef G_PFnMSKSIZE + } + return 0; +} + +static int rss_pf_config_open(struct inode *inode, struct file *file) +{ + struct adapter *adapter = inode->i_private; + struct seq_tab *p; + u32 rss_pf_map, rss_pf_mask; + struct rss_pf_conf *pfconf; + int pf; + + p = seq_open_tab(file, 8, sizeof(*pfconf), 1, rss_pf_config_show); + if (!p) + return -ENOMEM; + + pfconf = (struct rss_pf_conf *)p->data; + rss_pf_map = t4_read_rss_pf_map(adapter); + rss_pf_mask = t4_read_rss_pf_mask(adapter); + for (pf = 0; pf < 8; pf++) { + pfconf[pf].rss_pf_map = rss_pf_map; + pfconf[pf].rss_pf_mask = rss_pf_mask; + t4_read_rss_pf_config(adapter, pf, &pfconf[pf].rss_pf_config); + } + return 0; +} + +static const struct file_operations rss_pf_config_debugfs_fops = { + .owner = THIS_MODULE, + .open = rss_pf_config_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private +}; + +/* VF RSS Configuration. + */ + +struct rss_vf_conf { + u32 rss_vf_vfl; + u32 rss_vf_vfh; +}; + +static int rss_vf_config_show(struct seq_file *seq, void *v, int idx) +{ + if (v == SEQ_START_TOKEN) { + seq_puts(seq, " RSS Hash Tuple Enable\n"); + seq_puts(seq, " Enable IVF Dis Enb IPv6 IPv4 UDP Def Secret Key\n"); + seq_puts(seq, " VF Chn Prt Map VLAN uP Four Two Four Two Four Que Idx Hash\n"); + } else { + struct rss_vf_conf *vfconf = v; + + seq_printf(seq, "%3d %3s %3s %3d %3s %3s %3s %3s %3s %3s %3s %4d %3d %#10x\n", + idx, + yesno(vfconf->rss_vf_vfh & VFCHNEN_F), + yesno(vfconf->rss_vf_vfh & VFPRTEN_F), + VFLKPIDX_G(vfconf->rss_vf_vfh), + yesno(vfconf->rss_vf_vfh & VFVLNEX_F), + yesno(vfconf->rss_vf_vfh & VFUPEN_F), + yesno(vfconf->rss_vf_vfh & VFIP4FOURTUPEN_F), + yesno(vfconf->rss_vf_vfh & VFIP6TWOTUPEN_F), + yesno(vfconf->rss_vf_vfh & VFIP4FOURTUPEN_F), + yesno(vfconf->rss_vf_vfh & VFIP4TWOTUPEN_F), + yesno(vfconf->rss_vf_vfh & ENABLEUDPHASH_F), + DEFAULTQUEUE_G(vfconf->rss_vf_vfh), + KEYINDEX_G(vfconf->rss_vf_vfh), + vfconf->rss_vf_vfl); + } + return 0; +} + +static int rss_vf_config_open(struct inode *inode, struct file *file) +{ + struct adapter *adapter = inode->i_private; + struct seq_tab *p; + struct rss_vf_conf *vfconf; + int vf; + + p = seq_open_tab(file, 128, sizeof(*vfconf), 1, rss_vf_config_show); + if (!p) + return -ENOMEM; + + vfconf = (struct rss_vf_conf *)p->data; + for (vf = 0; vf < 128; vf++) { + t4_read_rss_vf_config(adapter, vf, &vfconf[vf].rss_vf_vfl, + &vfconf[vf].rss_vf_vfh); + } + return 0; +} + +static const struct file_operations rss_vf_config_debugfs_fops = { + .owner = THIS_MODULE, + .open = rss_vf_config_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private +}; + +/** + * ethqset2pinfo - return port_info of an Ethernet Queue Set + * @adap: the adapter + * @qset: Ethernet Queue Set + */ +static inline struct port_info *ethqset2pinfo(struct adapter *adap, int qset) +{ + int pidx; + + for_each_port(adap, pidx) { + struct port_info *pi = adap2pinfo(adap, pidx); + + if (qset >= pi->first_qset && + qset < pi->first_qset + pi->nqsets) + return pi; + } + + /* should never happen! */ + BUG_ON(1); + return NULL; +} + +static int sge_qinfo_show(struct seq_file *seq, void *v) +{ + struct adapter *adap = seq->private; + int eth_entries = DIV_ROUND_UP(adap->sge.ethqsets, 4); + int toe_entries = DIV_ROUND_UP(adap->sge.ofldqsets, 4); + int rdma_entries = DIV_ROUND_UP(adap->sge.rdmaqs, 4); + int ciq_entries = DIV_ROUND_UP(adap->sge.rdmaciqs, 4); + int ctrl_entries = DIV_ROUND_UP(MAX_CTRL_QUEUES, 4); + int i, r = (uintptr_t)v - 1; + int toe_idx = r - eth_entries; + int rdma_idx = toe_idx - toe_entries; + int ciq_idx = rdma_idx - rdma_entries; + int ctrl_idx = ciq_idx - ciq_entries; + int fq_idx = ctrl_idx - ctrl_entries; + + if (r) + seq_putc(seq, '\n'); + +#define S3(fmt_spec, s, v) \ +do { \ + seq_printf(seq, "%-12s", s); \ + for (i = 0; i < n; ++i) \ + seq_printf(seq, " %16" fmt_spec, v); \ + seq_putc(seq, '\n'); \ +} while (0) +#define S(s, v) S3("s", s, v) +#define T(s, v) S3("u", s, tx[i].v) +#define R(s, v) S3("u", s, rx[i].v) + + if (r < eth_entries) { + int base_qset = r * 4; + const struct sge_eth_rxq *rx = &adap->sge.ethrxq[base_qset]; + const struct sge_eth_txq *tx = &adap->sge.ethtxq[base_qset]; + int n = min(4, adap->sge.ethqsets - 4 * r); + + S("QType:", "Ethernet"); + S("Interface:", + rx[i].rspq.netdev ? rx[i].rspq.netdev->name : "N/A"); + T("TxQ ID:", q.cntxt_id); + T("TxQ size:", q.size); + T("TxQ inuse:", q.in_use); + T("TxQ CIDX:", q.cidx); + T("TxQ PIDX:", q.pidx); +#ifdef CONFIG_CHELSIO_T4_DCB + T("DCB Prio:", dcb_prio); + S3("u", "DCB PGID:", + (ethqset2pinfo(adap, base_qset + i)->dcb.pgid >> + 4*(7-tx[i].dcb_prio)) & 0xf); + S3("u", "DCB PFC:", + (ethqset2pinfo(adap, base_qset + i)->dcb.pfcen >> + 1*(7-tx[i].dcb_prio)) & 0x1); +#endif + R("RspQ ID:", rspq.abs_id); + R("RspQ size:", rspq.size); + R("RspQE size:", rspq.iqe_len); + R("RspQ CIDX:", rspq.cidx); + R("RspQ Gen:", rspq.gen); + S3("u", "Intr delay:", qtimer_val(adap, &rx[i].rspq)); + S3("u", "Intr pktcnt:", + adap->sge.counter_val[rx[i].rspq.pktcnt_idx]); + R("FL ID:", fl.cntxt_id); + R("FL size:", fl.size - 8); + R("FL pend:", fl.pend_cred); + R("FL avail:", fl.avail); + R("FL PIDX:", fl.pidx); + R("FL CIDX:", fl.cidx); + } else if (toe_idx < toe_entries) { + const struct sge_ofld_rxq *rx = &adap->sge.ofldrxq[toe_idx * 4]; + const struct sge_ofld_txq *tx = &adap->sge.ofldtxq[toe_idx * 4]; + int n = min(4, adap->sge.ofldqsets - 4 * toe_idx); + + S("QType:", "TOE"); + T("TxQ ID:", q.cntxt_id); + T("TxQ size:", q.size); + T("TxQ inuse:", q.in_use); + T("TxQ CIDX:", q.cidx); + T("TxQ PIDX:", q.pidx); + R("RspQ ID:", rspq.abs_id); + R("RspQ size:", rspq.size); + R("RspQE size:", rspq.iqe_len); + R("RspQ CIDX:", rspq.cidx); + R("RspQ Gen:", rspq.gen); + S3("u", "Intr delay:", qtimer_val(adap, &rx[i].rspq)); + S3("u", "Intr pktcnt:", + adap->sge.counter_val[rx[i].rspq.pktcnt_idx]); + R("FL ID:", fl.cntxt_id); + R("FL size:", fl.size - 8); + R("FL pend:", fl.pend_cred); + R("FL avail:", fl.avail); + R("FL PIDX:", fl.pidx); + R("FL CIDX:", fl.cidx); + } else if (rdma_idx < rdma_entries) { + const struct sge_ofld_rxq *rx = + &adap->sge.rdmarxq[rdma_idx * 4]; + int n = min(4, adap->sge.rdmaqs - 4 * rdma_idx); + + S("QType:", "RDMA-CPL"); + R("RspQ ID:", rspq.abs_id); + R("RspQ size:", rspq.size); + R("RspQE size:", rspq.iqe_len); + R("RspQ CIDX:", rspq.cidx); + R("RspQ Gen:", rspq.gen); + S3("u", "Intr delay:", qtimer_val(adap, &rx[i].rspq)); + S3("u", "Intr pktcnt:", + adap->sge.counter_val[rx[i].rspq.pktcnt_idx]); + R("FL ID:", fl.cntxt_id); + R("FL size:", fl.size - 8); + R("FL pend:", fl.pend_cred); + R("FL avail:", fl.avail); + R("FL PIDX:", fl.pidx); + R("FL CIDX:", fl.cidx); + } else if (ciq_idx < ciq_entries) { + const struct sge_ofld_rxq *rx = &adap->sge.rdmaciq[ciq_idx * 4]; + int n = min(4, adap->sge.rdmaciqs - 4 * ciq_idx); + + S("QType:", "RDMA-CIQ"); + R("RspQ ID:", rspq.abs_id); + R("RspQ size:", rspq.size); + R("RspQE size:", rspq.iqe_len); + R("RspQ CIDX:", rspq.cidx); + R("RspQ Gen:", rspq.gen); + S3("u", "Intr delay:", qtimer_val(adap, &rx[i].rspq)); + S3("u", "Intr pktcnt:", + adap->sge.counter_val[rx[i].rspq.pktcnt_idx]); + } else if (ctrl_idx < ctrl_entries) { + const struct sge_ctrl_txq *tx = &adap->sge.ctrlq[ctrl_idx * 4]; + int n = min(4, adap->params.nports - 4 * ctrl_idx); + + S("QType:", "Control"); + T("TxQ ID:", q.cntxt_id); + T("TxQ size:", q.size); + T("TxQ inuse:", q.in_use); + T("TxQ CIDX:", q.cidx); + T("TxQ PIDX:", q.pidx); + } else if (fq_idx == 0) { + const struct sge_rspq *evtq = &adap->sge.fw_evtq; + + seq_printf(seq, "%-12s %16s\n", "QType:", "FW event queue"); + seq_printf(seq, "%-12s %16u\n", "RspQ ID:", evtq->abs_id); + seq_printf(seq, "%-12s %16u\n", "RspQ size:", evtq->size); + seq_printf(seq, "%-12s %16u\n", "RspQE size:", evtq->iqe_len); + seq_printf(seq, "%-12s %16u\n", "RspQ CIDX:", evtq->cidx); + seq_printf(seq, "%-12s %16u\n", "RspQ Gen:", evtq->gen); + seq_printf(seq, "%-12s %16u\n", "Intr delay:", + qtimer_val(adap, evtq)); + seq_printf(seq, "%-12s %16u\n", "Intr pktcnt:", + adap->sge.counter_val[evtq->pktcnt_idx]); + } +#undef R +#undef T +#undef S +#undef S3 +return 0; +} + +static int sge_queue_entries(const struct adapter *adap) +{ + return DIV_ROUND_UP(adap->sge.ethqsets, 4) + + DIV_ROUND_UP(adap->sge.ofldqsets, 4) + + DIV_ROUND_UP(adap->sge.rdmaqs, 4) + + DIV_ROUND_UP(adap->sge.rdmaciqs, 4) + + DIV_ROUND_UP(MAX_CTRL_QUEUES, 4) + 1; +} + +static void *sge_queue_start(struct seq_file *seq, loff_t *pos) +{ + int entries = sge_queue_entries(seq->private); + + return *pos < entries ? (void *)((uintptr_t)*pos + 1) : NULL; +} + +static void sge_queue_stop(struct seq_file *seq, void *v) +{ +} + +static void *sge_queue_next(struct seq_file *seq, void *v, loff_t *pos) +{ + int entries = sge_queue_entries(seq->private); + + ++*pos; + return *pos < entries ? (void *)((uintptr_t)*pos + 1) : NULL; +} + +static const struct seq_operations sge_qinfo_seq_ops = { + .start = sge_queue_start, + .next = sge_queue_next, + .stop = sge_queue_stop, + .show = sge_qinfo_show +}; + +static int sge_qinfo_open(struct inode *inode, struct file *file) +{ + int res = seq_open(file, &sge_qinfo_seq_ops); + + if (!res) { + struct seq_file *seq = file->private_data; + + seq->private = inode->i_private; + } + return res; +} + +static const struct file_operations sge_qinfo_debugfs_fops = { + .owner = THIS_MODULE, + .open = sge_qinfo_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +int mem_open(struct inode *inode, struct file *file) +{ + unsigned int mem; + struct adapter *adap; + + file->private_data = inode->i_private; + + mem = (uintptr_t)file->private_data & 0x3; + adap = file->private_data - mem; + + (void)t4_fwcache(adap, FW_PARAM_DEV_FWCACHE_FLUSH); + + return 0; +} + static ssize_t mem_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { @@ -80,7 +1934,6 @@ static ssize_t mem_read(struct file *file, char __user *buf, size_t count, *ppos = pos + count; return count; } - static const struct file_operations mem_debugfs_fops = { .owner = THIS_MODULE, .open = simple_open, @@ -88,6 +1941,12 @@ static const struct file_operations mem_debugfs_fops = { .llseek = default_llseek, }; +static void set_debugfs_file_size(struct dentry *de, loff_t size) +{ + if (!IS_ERR(de) && de->d_inode) + de->d_inode->i_size = size; +} + static void add_debugfs_mem(struct adapter *adap, const char *name, unsigned int idx, unsigned int size_mb) { @@ -119,14 +1978,65 @@ int t4_setup_debugfs(struct adapter *adap) { int i; u32 size; + struct dentry *de; static struct t4_debugfs_entry t4_debugfs_files[] = { + { "cim_la", &cim_la_fops, S_IRUSR, 0 }, + { "cim_qcfg", &cim_qcfg_fops, S_IRUSR, 0 }, + { "clk", &clk_debugfs_fops, S_IRUSR, 0 }, + { "devlog", &devlog_fops, S_IRUSR, 0 }, + { "mbox0", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 0 }, + { "mbox1", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 1 }, + { "mbox2", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 2 }, + { "mbox3", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 3 }, + { "mbox4", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 4 }, + { "mbox5", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 5 }, + { "mbox6", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 6 }, + { "mbox7", &mbox_debugfs_fops, S_IRUSR | S_IWUSR, 7 }, { "l2t", &t4_l2t_fops, S_IRUSR, 0}, + { "mps_tcam", &mps_tcam_debugfs_fops, S_IRUSR, 0 }, + { "rss", &rss_debugfs_fops, S_IRUSR, 0 }, + { "rss_config", &rss_config_debugfs_fops, S_IRUSR, 0 }, + { "rss_key", &rss_key_debugfs_fops, S_IRUSR, 0 }, + { "rss_pf_config", &rss_pf_config_debugfs_fops, S_IRUSR, 0 }, + { "rss_vf_config", &rss_vf_config_debugfs_fops, S_IRUSR, 0 }, + { "sge_qinfo", &sge_qinfo_debugfs_fops, S_IRUSR, 0 }, + { "ibq_tp0", &cim_ibq_fops, S_IRUSR, 0 }, + { "ibq_tp1", &cim_ibq_fops, S_IRUSR, 1 }, + { "ibq_ulp", &cim_ibq_fops, S_IRUSR, 2 }, + { "ibq_sge0", &cim_ibq_fops, S_IRUSR, 3 }, + { "ibq_sge1", &cim_ibq_fops, S_IRUSR, 4 }, + { "ibq_ncsi", &cim_ibq_fops, S_IRUSR, 5 }, + { "obq_ulp0", &cim_obq_fops, S_IRUSR, 0 }, + { "obq_ulp1", &cim_obq_fops, S_IRUSR, 1 }, + { "obq_ulp2", &cim_obq_fops, S_IRUSR, 2 }, + { "obq_ulp3", &cim_obq_fops, S_IRUSR, 3 }, + { "obq_sge", &cim_obq_fops, S_IRUSR, 4 }, + { "obq_ncsi", &cim_obq_fops, S_IRUSR, 5 }, + { "tp_la", &tp_la_fops, S_IRUSR, 0 }, + { "ulprx_la", &ulprx_la_fops, S_IRUSR, 0 }, + { "sensors", &sensors_debugfs_fops, S_IRUSR, 0 }, + { "pm_stats", &pm_stats_debugfs_fops, S_IRUSR, 0 }, + { "cctrl", &cctrl_tbl_debugfs_fops, S_IRUSR, 0 }, +#if IS_ENABLED(CONFIG_IPV6) + { "clip_tbl", &clip_tbl_debugfs_fops, S_IRUSR, 0 }, +#endif + }; + + /* Debug FS nodes common to all T5 and later adapters. + */ + static struct t4_debugfs_entry t5_debugfs_files[] = { + { "obq_sge_rx_q0", &cim_obq_fops, S_IRUSR, 6 }, + { "obq_sge_rx_q1", &cim_obq_fops, S_IRUSR, 7 }, }; add_debugfs_files(adap, t4_debugfs_files, ARRAY_SIZE(t4_debugfs_files)); + if (!is_t4(adap->params.chip)) + add_debugfs_files(adap, + t5_debugfs_files, + ARRAY_SIZE(t5_debugfs_files)); i = t4_read_reg(adap, MA_TARGET_MEM_ENABLE_A); if (i & EDRAM0_ENABLE_F) { @@ -154,5 +2064,10 @@ int t4_setup_debugfs(struct adapter *adap) EXT_MEM1_SIZE_G(size)); } } + + de = debugfs_create_file("flash", S_IRUSR, adap->debugfs_root, adap, + &flash_debugfs_fops); + set_debugfs_file_size(de, adap->params.sf_size); + return 0; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.h index a3d8867efd3d..b63cfee2d963 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.h @@ -37,6 +37,21 @@ #include <linux/export.h> +#define FILE_DATA(_file) ((_file)->f_path.dentry->d_inode) + +#define DEFINE_SIMPLE_DEBUGFS_FILE(name) \ +static int name##_open(struct inode *inode, struct file *file) \ +{ \ + return single_open(file, name##_show, inode->i_private); \ +} \ +static const struct file_operations name##_debugfs_fops = { \ + .owner = THIS_MODULE, \ + .open = name##_open, \ + .read = seq_read, \ + .llseek = seq_lseek, \ + .release = single_release \ +} + struct t4_debugfs_entry { const char *name; const struct file_operations *ops; @@ -44,9 +59,27 @@ struct t4_debugfs_entry { unsigned char data; }; +struct seq_tab { + int (*show)(struct seq_file *seq, void *v, int idx); + unsigned int rows; /* # of entries */ + unsigned char width; /* size in bytes of each entry */ + unsigned char skip_first; /* whether the first line is a header */ + char data[0]; /* the table data */ +}; + +static inline unsigned int hex2val(char c) +{ + return isdigit(c) ? c - '0' : tolower(c) - 'a' + 10; +} + +struct seq_tab *seq_open_tab(struct file *f, unsigned int rows, + unsigned int width, unsigned int have_header, + int (*show)(struct seq_file *seq, void *v, int i)); + int t4_setup_debugfs(struct adapter *adap); void add_debugfs_files(struct adapter *adap, struct t4_debugfs_entry *files, unsigned int nfiles); +int mem_open(struct inode *inode, struct file *file); #endif diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index ccf3436024bc..a22cf932ca35 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -62,14 +62,18 @@ #include <net/netevent.h> #include <net/addrconf.h> #include <net/bonding.h> +#include <net/addrconf.h> #include <asm/uaccess.h> #include "cxgb4.h" #include "t4_regs.h" +#include "t4_values.h" #include "t4_msg.h" #include "t4fw_api.h" +#include "t4fw_version.h" #include "cxgb4_dcb.h" #include "cxgb4_debugfs.h" +#include "clip_tbl.h" #include "l2t.h" #ifdef DRV_VERSION @@ -78,99 +82,6 @@ #define DRV_VERSION "2.0.0-ko" #define DRV_DESC "Chelsio T4/T5 Network Driver" -/* - * Max interrupt hold-off timer value in us. Queues fall back to this value - * under extreme memory pressure so it's largish to give the system time to - * recover. - */ -#define MAX_SGE_TIMERVAL 200U - -enum { - /* - * Physical Function provisioning constants. - */ - PFRES_NVI = 4, /* # of Virtual Interfaces */ - PFRES_NETHCTRL = 128, /* # of EQs used for ETH or CTRL Qs */ - PFRES_NIQFLINT = 128, /* # of ingress Qs/w Free List(s)/intr - */ - PFRES_NEQ = 256, /* # of egress queues */ - PFRES_NIQ = 0, /* # of ingress queues */ - PFRES_TC = 0, /* PCI-E traffic class */ - PFRES_NEXACTF = 128, /* # of exact MPS filters */ - - PFRES_R_CAPS = FW_CMD_CAP_PF, - PFRES_WX_CAPS = FW_CMD_CAP_PF, - -#ifdef CONFIG_PCI_IOV - /* - * Virtual Function provisioning constants. We need two extra Ingress - * Queues with Interrupt capability to serve as the VF's Firmware - * Event Queue and Forwarded Interrupt Queue (when using MSI mode) -- - * neither will have Free Lists associated with them). For each - * Ethernet/Control Egress Queue and for each Free List, we need an - * Egress Context. - */ - VFRES_NPORTS = 1, /* # of "ports" per VF */ - VFRES_NQSETS = 2, /* # of "Queue Sets" per VF */ - - VFRES_NVI = VFRES_NPORTS, /* # of Virtual Interfaces */ - VFRES_NETHCTRL = VFRES_NQSETS, /* # of EQs used for ETH or CTRL Qs */ - VFRES_NIQFLINT = VFRES_NQSETS+2,/* # of ingress Qs/w Free List(s)/intr */ - VFRES_NEQ = VFRES_NQSETS*2, /* # of egress queues */ - VFRES_NIQ = 0, /* # of non-fl/int ingress queues */ - VFRES_TC = 0, /* PCI-E traffic class */ - VFRES_NEXACTF = 16, /* # of exact MPS filters */ - - VFRES_R_CAPS = FW_CMD_CAP_DMAQ|FW_CMD_CAP_VF|FW_CMD_CAP_PORT, - VFRES_WX_CAPS = FW_CMD_CAP_DMAQ|FW_CMD_CAP_VF, -#endif -}; - -/* - * Provide a Port Access Rights Mask for the specified PF/VF. This is very - * static and likely not to be useful in the long run. We really need to - * implement some form of persistent configuration which the firmware - * controls. - */ -static unsigned int pfvfres_pmask(struct adapter *adapter, - unsigned int pf, unsigned int vf) -{ - unsigned int portn, portvec; - - /* - * Give PF's access to all of the ports. - */ - if (vf == 0) - return FW_PFVF_CMD_PMASK_M; - - /* - * For VFs, we'll assign them access to the ports based purely on the - * PF. We assign active ports in order, wrapping around if there are - * fewer active ports than PFs: e.g. active port[pf % nports]. - * Unfortunately the adapter's port_info structs haven't been - * initialized yet so we have to compute this. - */ - if (adapter->params.nports == 0) - return 0; - - portn = pf % adapter->params.nports; - portvec = adapter->params.portvec; - for (;;) { - /* - * Isolate the lowest set bit in the port vector. If we're at - * the port number that we want, return that as the pmask. - * otherwise mask that bit out of the port vector and - * decrement our port number ... - */ - unsigned int pmask = portvec ^ (portvec & (portvec-1)); - if (portn == 0) - return pmask; - portn--; - portvec &= ~pmask; - } - /*NOTREACHED*/ -} - enum { MAX_TXQ_ENTRIES = 16384, MAX_CTRL_TXQ_ENTRIES = 1024, @@ -263,7 +174,8 @@ MODULE_PARM_DESC(force_init, "Forcibly become Master PF and initialize adapter") static uint force_old_init; module_param(force_old_init, uint, 0644); -MODULE_PARM_DESC(force_old_init, "Force old initialization sequence"); +MODULE_PARM_DESC(force_old_init, "Force old initialization sequence, deprecated" + " parameter"); static int dflt_msg_enable = DFLT_MSG_ENABLE; @@ -292,13 +204,14 @@ static unsigned int intr_holdoff[SGE_NTIMERS - 1] = { 5, 10, 20, 50, 100 }; module_param_array(intr_holdoff, uint, NULL, 0644); MODULE_PARM_DESC(intr_holdoff, "values for queue interrupt hold-off timers " - "0..4 in microseconds"); + "0..4 in microseconds, deprecated parameter"); static unsigned int intr_cnt[SGE_NCOUNTERS - 1] = { 4, 8, 16 }; module_param_array(intr_cnt, uint, NULL, 0644); MODULE_PARM_DESC(intr_cnt, - "thresholds 1..3 for queue interrupt packet counters"); + "thresholds 1..3 for queue interrupt packet counters, " + "deprecated parameter"); /* * Normally we tell the chip to deliver Ingress Packets into our DMA buffers @@ -318,7 +231,8 @@ static bool vf_acls; #ifdef CONFIG_PCI_IOV module_param(vf_acls, bool, 0644); -MODULE_PARM_DESC(vf_acls, "if set enable virtualization L2 ACL enforcement"); +MODULE_PARM_DESC(vf_acls, "if set enable virtualization L2 ACL enforcement, " + "deprecated parameter"); /* Configure the number of PCI-E Virtual Function which are to be instantiated * on SR-IOV Capable Physical Functions. @@ -340,32 +254,11 @@ module_param(select_queue, int, 0644); MODULE_PARM_DESC(select_queue, "Select between kernel provided method of selecting or driver method of selecting TX queue. Default is kernel method."); -/* - * The filter TCAM has a fixed portion and a variable portion. The fixed - * portion can match on source/destination IP IPv4/IPv6 addresses and TCP/UDP - * ports. The variable portion is 36 bits which can include things like Exact - * Match MAC Index (9 bits), Ether Type (16 bits), IP Protocol (8 bits), - * [Inner] VLAN Tag (17 bits), etc. which, if all were somehow selected, would - * far exceed the 36-bit budget for this "compressed" header portion of the - * filter. Thus, we have a scarce resource which must be carefully managed. - * - * By default we set this up to mostly match the set of filter matching - * capabilities of T3 but with accommodations for some of T4's more - * interesting features: - * - * { IP Fragment (1), MPS Match Type (3), IP Protocol (8), - * [Inner] VLAN (17), Port (3), FCoE (1) } - */ -enum { - TP_VLAN_PRI_MAP_DEFAULT = HW_TPL_FR_MT_PR_IV_P_FC, - TP_VLAN_PRI_MAP_FIRST = FCOE_SHIFT, - TP_VLAN_PRI_MAP_LAST = FRAGMENTATION_SHIFT, -}; - -static unsigned int tp_vlan_pri_map = TP_VLAN_PRI_MAP_DEFAULT; +static unsigned int tp_vlan_pri_map = HW_TPL_FR_MT_PR_IV_P_FC; module_param(tp_vlan_pri_map, uint, 0644); -MODULE_PARM_DESC(tp_vlan_pri_map, "global compressed filter configuration"); +MODULE_PARM_DESC(tp_vlan_pri_map, "global compressed filter configuration, " + "deprecated parameter"); static struct dentry *cxgb4_debugfs_root; @@ -671,7 +564,7 @@ static void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl) if (idx >= adap->tids.ftid_base && nidx < (adap->tids.nftids + adap->tids.nsftids)) { idx = nidx; - ret = GET_TCB_COOKIE(rpl->cookie); + ret = TCB_COOKIE_G(rpl->cookie); f = &adap->tids.ftid_tab[idx]; if (ret == FW_FILTER_WR_FLT_DELETED) { @@ -723,7 +616,7 @@ static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp, if (likely(opcode == CPL_SGE_EGR_UPDATE)) { const struct cpl_sge_egr_update *p = (void *)rsp; - unsigned int qid = EGR_QID(ntohl(p->opcode_qid)); + unsigned int qid = EGR_QID_G(ntohl(p->opcode_qid)); struct sge_txq *txq; txq = q->adap->sge.egr_map[qid - q->adap->sge.egr_start]; @@ -833,11 +726,11 @@ static void disable_msi(struct adapter *adapter) static irqreturn_t t4_nondata_intr(int irq, void *cookie) { struct adapter *adap = cookie; + u32 v = t4_read_reg(adap, MYPF_REG(PL_PF_INT_CAUSE_A)); - u32 v = t4_read_reg(adap, MYPF_REG(PL_PF_INT_CAUSE)); - if (v & PFSW) { + if (v & PFSW_F) { adap->swintr = 1; - t4_write_reg(adap, MYPF_REG(PL_PF_INT_CAUSE), v); + t4_write_reg(adap, MYPF_REG(PL_PF_INT_CAUSE_A), v); } t4_slow_intr_handler(adap); return IRQ_HANDLED; @@ -1030,8 +923,14 @@ static void quiesce_rx(struct adapter *adap) for (i = 0; i < ARRAY_SIZE(adap->sge.ingr_map); i++) { struct sge_rspq *q = adap->sge.ingr_map[i]; - if (q && q->handler) + if (q && q->handler) { napi_disable(&q->napi); + local_bh_disable(); + while (!cxgb_poll_lock_napi(q)) + mdelay(1); + local_bh_enable(); + } + } } @@ -1047,12 +946,14 @@ static void enable_rx(struct adapter *adap) if (!q) continue; - if (q->handler) + if (q->handler) { + cxgb_busy_poll_init_lock(q); napi_enable(&q->napi); + } /* 0-increment GTS to start the timer and enable interrupts */ - t4_write_reg(adap, MYPF_REG(SGE_PF_GTS), - SEINTARM(q->intr_params) | - INGRESSQID(q->cntxt_id)); + t4_write_reg(adap, MYPF_REG(SGE_PF_GTS_A), + SEINTARM_V(q->intr_params) | + INGRESSQID_V(q->cntxt_id)); } } @@ -1176,10 +1077,10 @@ freeout: t4_free_sge_resources(adap); } t4_write_reg(adap, is_t4(adap->params.chip) ? - MPS_TRC_RSS_CONTROL : - MPS_T5_TRC_RSS_CONTROL, - RSSCONTROL(netdev2pinfo(adap->port[0])->tx_chan) | - QUEUENUMBER(s->ethrxq[0].rspq.abs_id)); + MPS_TRC_RSS_CONTROL_A : + MPS_T5_TRC_RSS_CONTROL_A, + RSSCONTROL_V(netdev2pinfo(adap->port[0])->tx_chan) | + QUEUENUMBER_V(s->ethrxq[0].rspq.abs_id)); return 0; } @@ -1518,6 +1419,7 @@ static int get_eeprom_len(struct net_device *dev) static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct adapter *adapter = netdev2adap(dev); + u32 exprom_vers; strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); strlcpy(info->version, DRV_VERSION, sizeof(info->version)); @@ -1535,6 +1437,14 @@ static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) FW_HDR_FW_VER_MINOR_G(adapter->params.tp_vers), FW_HDR_FW_VER_MICRO_G(adapter->params.tp_vers), FW_HDR_FW_VER_BUILD_G(adapter->params.tp_vers)); + + if (!t4_get_exprom_version(adapter, &exprom_vers)) + snprintf(info->erom_version, sizeof(info->erom_version), + "%u.%u.%u.%u", + FW_HDR_FW_VER_MAJOR_G(exprom_vers), + FW_HDR_FW_VER_MINOR_G(exprom_vers), + FW_HDR_FW_VER_MICRO_G(exprom_vers), + FW_HDR_FW_VER_BUILD_G(exprom_vers)); } static void get_strings(struct net_device *dev, u32 stringset, u8 *data) @@ -1589,9 +1499,9 @@ static void get_stats(struct net_device *dev, struct ethtool_stats *stats, collect_sge_port_stats(adapter, pi, (struct queue_port_stats *)data); data += sizeof(struct queue_port_stats) / sizeof(u64); if (!is_t4(adapter->params.chip)) { - t4_write_reg(adapter, SGE_STAT_CFG, STATSOURCE_T5(7)); - val1 = t4_read_reg(adapter, SGE_STAT_TOTAL); - val2 = t4_read_reg(adapter, SGE_STAT_MATCH); + t4_write_reg(adapter, SGE_STAT_CFG_A, STATSOURCE_T5_V(7)); + val1 = t4_read_reg(adapter, SGE_STAT_TOTAL_A); + val2 = t4_read_reg(adapter, SGE_STAT_MATCH_A); *data = val1 - val2; data++; *data = val2; @@ -2608,8 +2518,8 @@ static int closest_thres(const struct sge *s, int thres) /* * Return a queue's interrupt hold-off time in us. 0 means no timer. */ -static unsigned int qtimer_val(const struct adapter *adap, - const struct sge_rspq *q) +unsigned int qtimer_val(const struct adapter *adap, + const struct sge_rspq *q) { unsigned int idx = q->intr_params >> 1; @@ -3346,40 +3256,6 @@ static int tid_init(struct tid_info *t) return 0; } -int cxgb4_clip_get(const struct net_device *dev, - const struct in6_addr *lip) -{ - struct adapter *adap; - struct fw_clip_cmd c; - - adap = netdev2adap(dev); - memset(&c, 0, sizeof(c)); - c.op_to_write = htonl(FW_CMD_OP_V(FW_CLIP_CMD) | - FW_CMD_REQUEST_F | FW_CMD_WRITE_F); - c.alloc_to_len16 = htonl(FW_CLIP_CMD_ALLOC_F | FW_LEN16(c)); - c.ip_hi = *(__be64 *)(lip->s6_addr); - c.ip_lo = *(__be64 *)(lip->s6_addr + 8); - return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false); -} -EXPORT_SYMBOL(cxgb4_clip_get); - -int cxgb4_clip_release(const struct net_device *dev, - const struct in6_addr *lip) -{ - struct adapter *adap; - struct fw_clip_cmd c; - - adap = netdev2adap(dev); - memset(&c, 0, sizeof(c)); - c.op_to_write = htonl(FW_CMD_OP_V(FW_CLIP_CMD) | - FW_CMD_REQUEST_F | FW_CMD_READ_F); - c.alloc_to_len16 = htonl(FW_CLIP_CMD_FREE_F | FW_LEN16(c)); - c.ip_hi = *(__be64 *)(lip->s6_addr); - c.ip_lo = *(__be64 *)(lip->s6_addr + 8); - return t4_wr_mbox_meat(adap, adap->mbox, &c, sizeof(c), &c, false); -} -EXPORT_SYMBOL(cxgb4_clip_release); - /** * cxgb4_create_server - create an IP server * @dev: the device @@ -3415,8 +3291,8 @@ int cxgb4_create_server(const struct net_device *dev, unsigned int stid, req->peer_ip = htonl(0); chan = rxq_to_chan(&adap->sge, queue); req->opt0 = cpu_to_be64(TX_CHAN_V(chan)); - req->opt1 = cpu_to_be64(CONN_POLICY_ASK | - SYN_RSS_ENABLE | SYN_RSS_QUEUE(queue)); + req->opt1 = cpu_to_be64(CONN_POLICY_V(CPL_CONN_POLICY_ASK) | + SYN_RSS_ENABLE_F | SYN_RSS_QUEUE_V(queue)); ret = t4_mgmt_tx(adap, skb); return net_xmit_eval(ret); } @@ -3458,8 +3334,8 @@ int cxgb4_create_server6(const struct net_device *dev, unsigned int stid, req->peer_ip_lo = cpu_to_be64(0); chan = rxq_to_chan(&adap->sge, queue); req->opt0 = cpu_to_be64(TX_CHAN_V(chan)); - req->opt1 = cpu_to_be64(CONN_POLICY_ASK | - SYN_RSS_ENABLE | SYN_RSS_QUEUE(queue)); + req->opt1 = cpu_to_be64(CONN_POLICY_V(CPL_CONN_POLICY_ASK) | + SYN_RSS_ENABLE_F | SYN_RSS_QUEUE_V(queue)); ret = t4_mgmt_tx(adap, skb); return net_xmit_eval(ret); } @@ -3482,8 +3358,8 @@ int cxgb4_remove_server(const struct net_device *dev, unsigned int stid, req = (struct cpl_close_listsvr_req *)__skb_put(skb, sizeof(*req)); INIT_TP_WR(req, 0); OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_CLOSE_LISTSRV_REQ, stid)); - req->reply_ctrl = htons(NO_REPLY(0) | (ipv6 ? LISTSVR_IPV6(1) : - LISTSVR_IPV6(0)) | QUEUENO(queue)); + req->reply_ctrl = htons(NO_REPLY_V(0) | (ipv6 ? LISTSVR_IPV6_V(1) : + LISTSVR_IPV6_V(0)) | QUEUENO_V(queue)); ret = t4_mgmt_tx(adap, skb); return net_xmit_eval(ret); } @@ -3600,14 +3476,14 @@ unsigned int cxgb4_dbfifo_count(const struct net_device *dev, int lpfifo) struct adapter *adap = netdev2adap(dev); u32 v1, v2, lp_count, hp_count; - v1 = t4_read_reg(adap, A_SGE_DBFIFO_STATUS); - v2 = t4_read_reg(adap, SGE_DBFIFO_STATUS2); + v1 = t4_read_reg(adap, SGE_DBFIFO_STATUS_A); + v2 = t4_read_reg(adap, SGE_DBFIFO_STATUS2_A); if (is_t4(adap->params.chip)) { - lp_count = G_LP_COUNT(v1); - hp_count = G_HP_COUNT(v1); + lp_count = LP_COUNT_G(v1); + hp_count = HP_COUNT_G(v1); } else { - lp_count = G_LP_COUNT_T5(v1); - hp_count = G_HP_COUNT_T5(v2); + lp_count = LP_COUNT_T5_G(v1); + hp_count = HP_COUNT_T5_G(v2); } return lpfifo ? lp_count : hp_count; } @@ -3653,10 +3529,10 @@ void cxgb4_iscsi_init(struct net_device *dev, unsigned int tag_mask, { struct adapter *adap = netdev2adap(dev); - t4_write_reg(adap, ULP_RX_ISCSI_TAGMASK, tag_mask); - t4_write_reg(adap, ULP_RX_ISCSI_PSZ, HPZ0(pgsz_order[0]) | - HPZ1(pgsz_order[1]) | HPZ2(pgsz_order[2]) | - HPZ3(pgsz_order[3])); + t4_write_reg(adap, ULP_RX_ISCSI_TAGMASK_A, tag_mask); + t4_write_reg(adap, ULP_RX_ISCSI_PSZ_A, HPZ0_V(pgsz_order[0]) | + HPZ1_V(pgsz_order[1]) | HPZ2_V(pgsz_order[2]) | + HPZ3_V(pgsz_order[3])); } EXPORT_SYMBOL(cxgb4_iscsi_init); @@ -3666,14 +3542,14 @@ int cxgb4_flush_eq_cache(struct net_device *dev) int ret; ret = t4_fwaddrspace_write(adap, adap->mbox, - 0xe1000000 + A_SGE_CTXT_CMD, 0x20000000); + 0xe1000000 + SGE_CTXT_CMD_A, 0x20000000); return ret; } EXPORT_SYMBOL(cxgb4_flush_eq_cache); static int read_eq_indices(struct adapter *adap, u16 qid, u16 *pidx, u16 *cidx) { - u32 addr = t4_read_reg(adap, A_SGE_DBQ_CTXT_BADDR) + 24 * qid + 8; + u32 addr = t4_read_reg(adap, SGE_DBQ_CTXT_BADDR_A) + 24 * qid + 8; __be64 indices; int ret; @@ -3702,14 +3578,20 @@ int cxgb4_sync_txq_pidx(struct net_device *dev, u16 qid, u16 pidx, if (pidx != hw_pidx) { u16 delta; + u32 val; if (pidx >= hw_pidx) delta = pidx - hw_pidx; else delta = size - hw_pidx + pidx; + + if (is_t4(adap->params.chip)) + val = PIDX_V(delta); + else + val = PIDX_T5_V(delta); wmb(); - t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL), - QID(qid) | PIDX(delta)); + t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL_A), + QID_V(qid) | val); } out: return ret; @@ -3721,8 +3603,8 @@ void cxgb4_disable_db_coalescing(struct net_device *dev) struct adapter *adap; adap = netdev2adap(dev); - t4_set_reg_field(adap, A_SGE_DOORBELL_CONTROL, F_NOCOALESCE, - F_NOCOALESCE); + t4_set_reg_field(adap, SGE_DOORBELL_CONTROL_A, NOCOALESCE_F, + NOCOALESCE_F); } EXPORT_SYMBOL(cxgb4_disable_db_coalescing); @@ -3731,7 +3613,7 @@ void cxgb4_enable_db_coalescing(struct net_device *dev) struct adapter *adap; adap = netdev2adap(dev); - t4_set_reg_field(adap, A_SGE_DOORBELL_CONTROL, F_NOCOALESCE, 0); + t4_set_reg_field(adap, SGE_DOORBELL_CONTROL_A, NOCOALESCE_F, 0); } EXPORT_SYMBOL(cxgb4_enable_db_coalescing); @@ -3809,8 +3691,8 @@ u64 cxgb4_read_sge_timestamp(struct net_device *dev) struct adapter *adap; adap = netdev2adap(dev); - lo = t4_read_reg(adap, SGE_TIMESTAMP_LO); - hi = GET_TSVAL(t4_read_reg(adap, SGE_TIMESTAMP_HI)); + lo = t4_read_reg(adap, SGE_TIMESTAMP_LO_A); + hi = TSVAL_G(t4_read_reg(adap, SGE_TIMESTAMP_HI_A)); return ((u64)hi << 32) | (u64)lo; } @@ -3870,14 +3752,14 @@ static void drain_db_fifo(struct adapter *adap, int usecs) u32 v1, v2, lp_count, hp_count; do { - v1 = t4_read_reg(adap, A_SGE_DBFIFO_STATUS); - v2 = t4_read_reg(adap, SGE_DBFIFO_STATUS2); + v1 = t4_read_reg(adap, SGE_DBFIFO_STATUS_A); + v2 = t4_read_reg(adap, SGE_DBFIFO_STATUS2_A); if (is_t4(adap->params.chip)) { - lp_count = G_LP_COUNT(v1); - hp_count = G_HP_COUNT(v1); + lp_count = LP_COUNT_G(v1); + hp_count = HP_COUNT_G(v1); } else { - lp_count = G_LP_COUNT_T5(v1); - hp_count = G_HP_COUNT_T5(v2); + lp_count = LP_COUNT_T5_G(v1); + hp_count = HP_COUNT_T5_G(v2); } if (lp_count == 0 && hp_count == 0) @@ -3904,8 +3786,8 @@ static void enable_txq_db(struct adapter *adap, struct sge_txq *q) * are committed before we tell HW about them. */ wmb(); - t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL), - QID(q->cntxt_id) | PIDX(q->db_pidx_inc)); + t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL_A), + QID_V(q->cntxt_id) | PIDX_V(q->db_pidx_inc)); q->db_pidx_inc = 0; } q->db_disabled = 0; @@ -3952,9 +3834,9 @@ static void process_db_full(struct work_struct *work) drain_db_fifo(adap, dbfifo_drain_delay); enable_dbs(adap); notify_rdma_uld(adap, CXGB4_CONTROL_DB_EMPTY); - t4_set_reg_field(adap, SGE_INT_ENABLE3, - DBFIFO_HP_INT | DBFIFO_LP_INT, - DBFIFO_HP_INT | DBFIFO_LP_INT); + t4_set_reg_field(adap, SGE_INT_ENABLE3_A, + DBFIFO_HP_INT_F | DBFIFO_LP_INT_F, + DBFIFO_HP_INT_F | DBFIFO_LP_INT_F); } static void sync_txq_pidx(struct adapter *adap, struct sge_txq *q) @@ -3968,14 +3850,20 @@ static void sync_txq_pidx(struct adapter *adap, struct sge_txq *q) goto out; if (q->db_pidx != hw_pidx) { u16 delta; + u32 val; if (q->db_pidx >= hw_pidx) delta = q->db_pidx - hw_pidx; else delta = q->size - hw_pidx + q->db_pidx; + + if (is_t4(adap->params.chip)) + val = PIDX_V(delta); + else + val = PIDX_T5_V(delta); wmb(); - t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL), - QID(q->cntxt_id) | PIDX(delta)); + t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL_A), + QID_V(q->cntxt_id) | val); } out: q->db_disabled = 0; @@ -4024,14 +3912,14 @@ static void process_db_drop(struct work_struct *work) dev_err(adap->pdev_dev, "doorbell drop recovery: " "qid=%d, pidx_inc=%d\n", qid, pidx_inc); else - writel(PIDX_T5(pidx_inc) | QID(bar2_qid), + writel(PIDX_T5_V(pidx_inc) | QID_V(bar2_qid), adap->bar2 + bar2_qoffset + SGE_UDB_KDOORBELL); /* Re-enable BAR2 WC */ t4_set_reg_field(adap, 0x10b0, 1<<15, 1<<15); } - t4_set_reg_field(adap, A_SGE_DOORBELL_CONTROL, F_DROPPED_DB, 0); + t4_set_reg_field(adap, SGE_DOORBELL_CONTROL_A, DROPPED_DB_F, 0); } void t4_db_full(struct adapter *adap) @@ -4039,8 +3927,8 @@ void t4_db_full(struct adapter *adap) if (is_t4(adap->params.chip)) { disable_dbs(adap); notify_rdma_uld(adap, CXGB4_CONTROL_DB_FULL); - t4_set_reg_field(adap, SGE_INT_ENABLE3, - DBFIFO_HP_INT | DBFIFO_LP_INT, 0); + t4_set_reg_field(adap, SGE_INT_ENABLE3_A, + DBFIFO_HP_INT_F | DBFIFO_LP_INT_F, 0); queue_work(adap->workq, &adap->db_full_task); } } @@ -4081,7 +3969,7 @@ static void uld_attach(struct adapter *adap, unsigned int uld) lli.nports = adap->params.nports; lli.wr_cred = adap->params.ofldq_wr_cred; lli.adapter_type = adap->params.chip; - lli.iscsi_iolen = MAXRXDATA_GET(t4_read_reg(adap, TP_PARA_REG2)); + lli.iscsi_iolen = MAXRXDATA_G(t4_read_reg(adap, TP_PARA_REG2_A)); lli.cclk_ps = 1000000000 / adap->params.vpd.cclk; lli.udb_density = 1 << adap->params.sge.eq_qpp; lli.ucq_density = 1 << adap->params.sge.iq_qpp; @@ -4089,8 +3977,8 @@ static void uld_attach(struct adapter *adap, unsigned int uld) /* MODQ_REQ_MAP sets queues 0-3 to chan 0-3 */ for (i = 0; i < NCHAN; i++) lli.tx_modq[i] = i; - lli.gts_reg = adap->regs + MYPF_REG(SGE_PF_GTS); - lli.db_reg = adap->regs + MYPF_REG(SGE_PF_KDOORBELL); + lli.gts_reg = adap->regs + MYPF_REG(SGE_PF_GTS_A); + lli.db_reg = adap->regs + MYPF_REG(SGE_PF_KDOORBELL_A); lli.fw_vers = adap->params.fw_vers; lli.dbfifo_int_thresh = dbfifo_int_thresh; lli.sge_ingpadboundary = adap->sge.fl_align; @@ -4220,148 +4108,61 @@ int cxgb4_unregister_uld(enum cxgb4_uld type) } EXPORT_SYMBOL(cxgb4_unregister_uld); -/* Check if netdev on which event is occured belongs to us or not. Return - * success (true) if it belongs otherwise failure (false). - * Called with rcu_read_lock() held. - */ #if IS_ENABLED(CONFIG_IPV6) -static bool cxgb4_netdev(const struct net_device *netdev) +static int cxgb4_inet6addr_handler(struct notifier_block *this, + unsigned long event, void *data) { + struct inet6_ifaddr *ifa = data; + struct net_device *event_dev = ifa->idev->dev; + const struct device *parent = NULL; +#if IS_ENABLED(CONFIG_BONDING) struct adapter *adap; - int i; - - list_for_each_entry_rcu(adap, &adap_rcu_list, rcu_node) - for (i = 0; i < MAX_NPORTS; i++) - if (adap->port[i] == netdev) - return true; - return false; -} +#endif + if (event_dev->priv_flags & IFF_802_1Q_VLAN) + event_dev = vlan_dev_real_dev(event_dev); +#if IS_ENABLED(CONFIG_BONDING) + if (event_dev->flags & IFF_MASTER) { + list_for_each_entry(adap, &adapter_list, list_node) { + switch (event) { + case NETDEV_UP: + cxgb4_clip_get(adap->port[0], + (const u32 *)ifa, 1); + break; + case NETDEV_DOWN: + cxgb4_clip_release(adap->port[0], + (const u32 *)ifa, 1); + break; + default: + break; + } + } + return NOTIFY_OK; + } +#endif -static int clip_add(struct net_device *event_dev, struct inet6_ifaddr *ifa, - unsigned long event) -{ - int ret = NOTIFY_DONE; + if (event_dev) + parent = event_dev->dev.parent; - rcu_read_lock(); - if (cxgb4_netdev(event_dev)) { + if (parent && parent->driver == &cxgb4_driver.driver) { switch (event) { case NETDEV_UP: - ret = cxgb4_clip_get(event_dev, &ifa->addr); - if (ret < 0) { - rcu_read_unlock(); - return ret; - } - ret = NOTIFY_OK; + cxgb4_clip_get(event_dev, (const u32 *)ifa, 1); break; case NETDEV_DOWN: - cxgb4_clip_release(event_dev, &ifa->addr); - ret = NOTIFY_OK; + cxgb4_clip_release(event_dev, (const u32 *)ifa, 1); break; default: break; } } - rcu_read_unlock(); - return ret; -} - -static int cxgb4_inet6addr_handler(struct notifier_block *this, - unsigned long event, void *data) -{ - struct inet6_ifaddr *ifa = data; - struct net_device *event_dev; - int ret = NOTIFY_DONE; - struct bonding *bond = netdev_priv(ifa->idev->dev); - struct list_head *iter; - struct slave *slave; - struct pci_dev *first_pdev = NULL; - - if (ifa->idev->dev->priv_flags & IFF_802_1Q_VLAN) { - event_dev = vlan_dev_real_dev(ifa->idev->dev); - ret = clip_add(event_dev, ifa, event); - } else if (ifa->idev->dev->flags & IFF_MASTER) { - /* It is possible that two different adapters are bonded in one - * bond. We need to find such different adapters and add clip - * in all of them only once. - */ - bond_for_each_slave(bond, slave, iter) { - if (!first_pdev) { - ret = clip_add(slave->dev, ifa, event); - /* If clip_add is success then only initialize - * first_pdev since it means it is our device - */ - if (ret == NOTIFY_OK) - first_pdev = to_pci_dev( - slave->dev->dev.parent); - } else if (first_pdev != - to_pci_dev(slave->dev->dev.parent)) - ret = clip_add(slave->dev, ifa, event); - } - } else - ret = clip_add(ifa->idev->dev, ifa, event); - - return ret; + return NOTIFY_OK; } +static bool inet6addr_registered; static struct notifier_block cxgb4_inet6addr_notifier = { .notifier_call = cxgb4_inet6addr_handler }; -/* Retrieves IPv6 addresses from a root device (bond, vlan) associated with - * a physical device. - * The physical device reference is needed to send the actul CLIP command. - */ -static int update_dev_clip(struct net_device *root_dev, struct net_device *dev) -{ - struct inet6_dev *idev = NULL; - struct inet6_ifaddr *ifa; - int ret = 0; - - idev = __in6_dev_get(root_dev); - if (!idev) - return ret; - - read_lock_bh(&idev->lock); - list_for_each_entry(ifa, &idev->addr_list, if_list) { - ret = cxgb4_clip_get(dev, &ifa->addr); - if (ret < 0) - break; - } - read_unlock_bh(&idev->lock); - - return ret; -} - -static int update_root_dev_clip(struct net_device *dev) -{ - struct net_device *root_dev = NULL; - int i, ret = 0; - - /* First populate the real net device's IPv6 addresses */ - ret = update_dev_clip(dev, dev); - if (ret) - return ret; - - /* Parse all bond and vlan devices layered on top of the physical dev */ - root_dev = netdev_master_upper_dev_get_rcu(dev); - if (root_dev) { - ret = update_dev_clip(root_dev, dev); - if (ret) - return ret; - } - - for (i = 0; i < VLAN_N_VID; i++) { - root_dev = __vlan_find_dev_deep_rcu(dev, htons(ETH_P_8021Q), i); - if (!root_dev) - continue; - - ret = update_dev_clip(root_dev, dev); - if (ret) - break; - } - return ret; -} - static void update_clip(const struct adapter *adap) { int i; @@ -4375,7 +4176,7 @@ static void update_clip(const struct adapter *adap) ret = 0; if (dev) - ret = update_root_dev_clip(dev); + ret = cxgb4_update_root_dev_clip(dev); if (ret < 0) break; @@ -4567,13 +4368,13 @@ int cxgb4_create_server_filter(const struct net_device *dev, unsigned int stid, f->fs.val.lip[i] = val[i]; f->fs.mask.lip[i] = ~0; } - if (adap->params.tp.vlan_pri_map & F_PORT) { + if (adap->params.tp.vlan_pri_map & PORT_F) { f->fs.val.iport = port; f->fs.mask.iport = mask; } } - if (adap->params.tp.vlan_pri_map & F_PROTOCOL) { + if (adap->params.tp.vlan_pri_map & PROTOCOL_F) { f->fs.val.proto = IPPROTO_TCP; f->fs.mask.proto = ~0; } @@ -4779,11 +4580,15 @@ static const struct net_device_ops cxgb4_netdev_ops = { #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = cxgb_netpoll, #endif +#ifdef CONFIG_NET_RX_BUSY_POLL + .ndo_busy_poll = cxgb_busy_poll, +#endif + }; void t4_fatal_err(struct adapter *adap) { - t4_set_reg_field(adap, SGE_CONTROL, GLOBALENABLE, 0); + t4_set_reg_field(adap, SGE_CONTROL_A, GLOBALENABLE_F, 0); t4_intr_disable(adap); dev_alert(adap->pdev_dev, "encountered fatal error, adapter stopped\n"); } @@ -4858,16 +4663,16 @@ static void setup_memwin(struct adapter *adap) mem_win2_base = MEMWIN2_BASE_T5; mem_win2_aperture = MEMWIN2_APERTURE_T5; } - t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 0), - mem_win0_base | BIR(0) | - WINDOW(ilog2(MEMWIN0_APERTURE) - 10)); - t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 1), - mem_win1_base | BIR(0) | - WINDOW(ilog2(MEMWIN1_APERTURE) - 10)); - t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 2), - mem_win2_base | BIR(0) | - WINDOW(ilog2(mem_win2_aperture) - 10)); - t4_read_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 2)); + t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, 0), + mem_win0_base | BIR_V(0) | + WINDOW_V(ilog2(MEMWIN0_APERTURE) - 10)); + t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, 1), + mem_win1_base | BIR_V(0) | + WINDOW_V(ilog2(MEMWIN1_APERTURE) - 10)); + t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, 2), + mem_win2_base | BIR_V(0) | + WINDOW_V(ilog2(mem_win2_aperture) - 10)); + t4_read_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, 2)); } static void setup_memwin_rdma(struct adapter *adap) @@ -4881,13 +4686,13 @@ static void setup_memwin_rdma(struct adapter *adap) start += OCQ_WIN_OFFSET(adap->pdev, &adap->vres); sz_kb = roundup_pow_of_two(adap->vres.ocq.size) >> 10; t4_write_reg(adap, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 3), - start | BIR(1) | WINDOW(ilog2(sz_kb))); + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, 3), + start | BIR_V(1) | WINDOW_V(ilog2(sz_kb))); t4_write_reg(adap, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, 3), + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, 3), adap->vres.ocq.start); t4_read_reg(adap, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, 3)); + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, 3)); } } @@ -4936,38 +4741,38 @@ static int adap_init1(struct adapter *adap, struct fw_caps_config_cmd *c) t4_sge_init(adap); /* tweak some settings */ - t4_write_reg(adap, TP_SHIFT_CNT, 0x64f8849); - t4_write_reg(adap, ULP_RX_TDDP_PSZ, HPZ0(PAGE_SHIFT - 12)); - t4_write_reg(adap, TP_PIO_ADDR, TP_INGRESS_CONFIG); - v = t4_read_reg(adap, TP_PIO_DATA); - t4_write_reg(adap, TP_PIO_DATA, v & ~CSUM_HAS_PSEUDO_HDR); + t4_write_reg(adap, TP_SHIFT_CNT_A, 0x64f8849); + t4_write_reg(adap, ULP_RX_TDDP_PSZ_A, HPZ0_V(PAGE_SHIFT - 12)); + t4_write_reg(adap, TP_PIO_ADDR_A, TP_INGRESS_CONFIG_A); + v = t4_read_reg(adap, TP_PIO_DATA_A); + t4_write_reg(adap, TP_PIO_DATA_A, v & ~CSUM_HAS_PSEUDO_HDR_F); /* first 4 Tx modulation queues point to consecutive Tx channels */ adap->params.tp.tx_modq_map = 0xE4; - t4_write_reg(adap, A_TP_TX_MOD_QUEUE_REQ_MAP, - V_TX_MOD_QUEUE_REQ_MAP(adap->params.tp.tx_modq_map)); + t4_write_reg(adap, TP_TX_MOD_QUEUE_REQ_MAP_A, + TX_MOD_QUEUE_REQ_MAP_V(adap->params.tp.tx_modq_map)); /* associate each Tx modulation queue with consecutive Tx channels */ v = 0x84218421; - t4_write_indirect(adap, TP_PIO_ADDR, TP_PIO_DATA, - &v, 1, A_TP_TX_SCHED_HDR); - t4_write_indirect(adap, TP_PIO_ADDR, TP_PIO_DATA, - &v, 1, A_TP_TX_SCHED_FIFO); - t4_write_indirect(adap, TP_PIO_ADDR, TP_PIO_DATA, - &v, 1, A_TP_TX_SCHED_PCMD); + t4_write_indirect(adap, TP_PIO_ADDR_A, TP_PIO_DATA_A, + &v, 1, TP_TX_SCHED_HDR_A); + t4_write_indirect(adap, TP_PIO_ADDR_A, TP_PIO_DATA_A, + &v, 1, TP_TX_SCHED_FIFO_A); + t4_write_indirect(adap, TP_PIO_ADDR_A, TP_PIO_DATA_A, + &v, 1, TP_TX_SCHED_PCMD_A); #define T4_TX_MODQ_10G_WEIGHT_DEFAULT 16 /* in KB units */ if (is_offload(adap)) { - t4_write_reg(adap, A_TP_TX_MOD_QUEUE_WEIGHT0, - V_TX_MODQ_WEIGHT0(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | - V_TX_MODQ_WEIGHT1(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | - V_TX_MODQ_WEIGHT2(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | - V_TX_MODQ_WEIGHT3(T4_TX_MODQ_10G_WEIGHT_DEFAULT)); - t4_write_reg(adap, A_TP_TX_MOD_CHANNEL_WEIGHT, - V_TX_MODQ_WEIGHT0(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | - V_TX_MODQ_WEIGHT1(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | - V_TX_MODQ_WEIGHT2(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | - V_TX_MODQ_WEIGHT3(T4_TX_MODQ_10G_WEIGHT_DEFAULT)); + t4_write_reg(adap, TP_TX_MOD_QUEUE_WEIGHT0_A, + TX_MODQ_WEIGHT0_V(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | + TX_MODQ_WEIGHT1_V(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | + TX_MODQ_WEIGHT2_V(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | + TX_MODQ_WEIGHT3_V(T4_TX_MODQ_10G_WEIGHT_DEFAULT)); + t4_write_reg(adap, TP_TX_MOD_CHANNEL_WEIGHT_A, + TX_MODQ_WEIGHT0_V(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | + TX_MODQ_WEIGHT1_V(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | + TX_MODQ_WEIGHT2_V(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | + TX_MODQ_WEIGHT3_V(T4_TX_MODQ_10G_WEIGHT_DEFAULT)); } /* get basic stuff going */ @@ -5013,16 +4818,16 @@ static int adap_init0_tweaks(struct adapter *adapter) rx_dma_offset); rx_dma_offset = 2; } - t4_set_reg_field(adapter, SGE_CONTROL, - PKTSHIFT_MASK, - PKTSHIFT(rx_dma_offset)); + t4_set_reg_field(adapter, SGE_CONTROL_A, + PKTSHIFT_V(PKTSHIFT_M), + PKTSHIFT_V(rx_dma_offset)); /* * Don't include the "IP Pseudo Header" in CPL_RX_PKT checksums: Linux * adds the pseudo header itself. */ - t4_tp_wr_bits_indirect(adapter, TP_INGRESS_CONFIG, - CSUM_HAS_PSEUDO_HDR, 0); + t4_tp_wr_bits_indirect(adapter, TP_INGRESS_CONFIG_A, + CSUM_HAS_PSEUDO_HDR_F, 0); return 0; } @@ -5046,7 +4851,7 @@ static int adap_init0_config(struct adapter *adapter, int reset) */ if (reset) { ret = t4_fw_reset(adapter, adapter->mbox, - PIORSTMODE | PIORST); + PIORSTMODE_F | PIORST_F); if (ret < 0) goto bye; } @@ -5212,12 +5017,9 @@ static int adap_init0_config(struct adapter *adapter, int reset) if (ret < 0) goto bye; - /* - * Return successfully and note that we're operating with parameters - * not supplied by the driver, rather than from hard-wired - * initialization constants burried in the driver. + /* Emit Firmware Configuration File information and return + * successfully. */ - adapter->flags |= USING_SOFT_PARAMS; dev_info(adapter->pdev_dev, "Successfully configured using Firmware "\ "Configuration File \"%s\", version %#x, computed checksum %#x\n", config_name, finiver, cfcsum); @@ -5235,249 +5037,6 @@ bye: return ret; } -/* - * Attempt to initialize the adapter via hard-coded, driver supplied - * parameters ... - */ -static int adap_init0_no_config(struct adapter *adapter, int reset) -{ - struct sge *s = &adapter->sge; - struct fw_caps_config_cmd caps_cmd; - u32 v; - int i, ret; - - /* - * Reset device if necessary - */ - if (reset) { - ret = t4_fw_reset(adapter, adapter->mbox, - PIORSTMODE | PIORST); - if (ret < 0) - goto bye; - } - - /* - * Get device capabilities and select which we'll be using. - */ - memset(&caps_cmd, 0, sizeof(caps_cmd)); - caps_cmd.op_to_write = htonl(FW_CMD_OP_V(FW_CAPS_CONFIG_CMD) | - FW_CMD_REQUEST_F | FW_CMD_READ_F); - caps_cmd.cfvalid_to_len16 = htonl(FW_LEN16(caps_cmd)); - ret = t4_wr_mbox(adapter, adapter->mbox, &caps_cmd, sizeof(caps_cmd), - &caps_cmd); - if (ret < 0) - goto bye; - - if (caps_cmd.niccaps & htons(FW_CAPS_CONFIG_NIC_VM)) { - if (!vf_acls) - caps_cmd.niccaps ^= htons(FW_CAPS_CONFIG_NIC_VM); - else - caps_cmd.niccaps = htons(FW_CAPS_CONFIG_NIC_VM); - } else if (vf_acls) { - dev_err(adapter->pdev_dev, "virtualization ACLs not supported"); - goto bye; - } - caps_cmd.op_to_write = htonl(FW_CMD_OP_V(FW_CAPS_CONFIG_CMD) | - FW_CMD_REQUEST_F | FW_CMD_WRITE_F); - ret = t4_wr_mbox(adapter, adapter->mbox, &caps_cmd, sizeof(caps_cmd), - NULL); - if (ret < 0) - goto bye; - - /* - * Tweak configuration based on system architecture, module - * parameters, etc. - */ - ret = adap_init0_tweaks(adapter); - if (ret < 0) - goto bye; - - /* - * Select RSS Global Mode we want to use. We use "Basic Virtual" - * mode which maps each Virtual Interface to its own section of - * the RSS Table and we turn on all map and hash enables ... - */ - adapter->flags |= RSS_TNLALLLOOKUP; - ret = t4_config_glbl_rss(adapter, adapter->mbox, - FW_RSS_GLB_CONFIG_CMD_MODE_BASICVIRTUAL, - FW_RSS_GLB_CONFIG_CMD_TNLMAPEN_F | - FW_RSS_GLB_CONFIG_CMD_HASHTOEPLITZ_F | - ((adapter->flags & RSS_TNLALLLOOKUP) ? - FW_RSS_GLB_CONFIG_CMD_TNLALLLKP_F : 0)); - if (ret < 0) - goto bye; - - /* - * Set up our own fundamental resource provisioning ... - */ - ret = t4_cfg_pfvf(adapter, adapter->mbox, adapter->fn, 0, - PFRES_NEQ, PFRES_NETHCTRL, - PFRES_NIQFLINT, PFRES_NIQ, - PFRES_TC, PFRES_NVI, - FW_PFVF_CMD_CMASK_M, - pfvfres_pmask(adapter, adapter->fn, 0), - PFRES_NEXACTF, - PFRES_R_CAPS, PFRES_WX_CAPS); - if (ret < 0) - goto bye; - - /* - * Perform low level SGE initialization. We need to do this before we - * send the firmware the INITIALIZE command because that will cause - * any other PF Drivers which are waiting for the Master - * Initialization to proceed forward. - */ - for (i = 0; i < SGE_NTIMERS - 1; i++) - s->timer_val[i] = min(intr_holdoff[i], MAX_SGE_TIMERVAL); - s->timer_val[SGE_NTIMERS - 1] = MAX_SGE_TIMERVAL; - s->counter_val[0] = 1; - for (i = 1; i < SGE_NCOUNTERS; i++) - s->counter_val[i] = min(intr_cnt[i - 1], - THRESHOLD_0_GET(THRESHOLD_0_MASK)); - t4_sge_init(adapter); - -#ifdef CONFIG_PCI_IOV - /* - * Provision resource limits for Virtual Functions. We currently - * grant them all the same static resource limits except for the Port - * Access Rights Mask which we're assigning based on the PF. All of - * the static provisioning stuff for both the PF and VF really needs - * to be managed in a persistent manner for each device which the - * firmware controls. - */ - { - int pf, vf; - - for (pf = 0; pf < ARRAY_SIZE(num_vf); pf++) { - if (num_vf[pf] <= 0) - continue; - - /* VF numbering starts at 1! */ - for (vf = 1; vf <= num_vf[pf]; vf++) { - ret = t4_cfg_pfvf(adapter, adapter->mbox, - pf, vf, - VFRES_NEQ, VFRES_NETHCTRL, - VFRES_NIQFLINT, VFRES_NIQ, - VFRES_TC, VFRES_NVI, - FW_PFVF_CMD_CMASK_M, - pfvfres_pmask( - adapter, pf, vf), - VFRES_NEXACTF, - VFRES_R_CAPS, VFRES_WX_CAPS); - if (ret < 0) - dev_warn(adapter->pdev_dev, - "failed to "\ - "provision pf/vf=%d/%d; " - "err=%d\n", pf, vf, ret); - } - } - } -#endif - - /* - * Set up the default filter mode. Later we'll want to implement this - * via a firmware command, etc. ... This needs to be done before the - * firmare initialization command ... If the selected set of fields - * isn't equal to the default value, we'll need to make sure that the - * field selections will fit in the 36-bit budget. - */ - if (tp_vlan_pri_map != TP_VLAN_PRI_MAP_DEFAULT) { - int j, bits = 0; - - for (j = TP_VLAN_PRI_MAP_FIRST; j <= TP_VLAN_PRI_MAP_LAST; j++) - switch (tp_vlan_pri_map & (1 << j)) { - case 0: - /* compressed filter field not enabled */ - break; - case FCOE_MASK: - bits += 1; - break; - case PORT_MASK: - bits += 3; - break; - case VNIC_ID_MASK: - bits += 17; - break; - case VLAN_MASK: - bits += 17; - break; - case TOS_MASK: - bits += 8; - break; - case PROTOCOL_MASK: - bits += 8; - break; - case ETHERTYPE_MASK: - bits += 16; - break; - case MACMATCH_MASK: - bits += 9; - break; - case MPSHITTYPE_MASK: - bits += 3; - break; - case FRAGMENTATION_MASK: - bits += 1; - break; - } - - if (bits > 36) { - dev_err(adapter->pdev_dev, - "tp_vlan_pri_map=%#x needs %d bits > 36;"\ - " using %#x\n", tp_vlan_pri_map, bits, - TP_VLAN_PRI_MAP_DEFAULT); - tp_vlan_pri_map = TP_VLAN_PRI_MAP_DEFAULT; - } - } - v = tp_vlan_pri_map; - t4_write_indirect(adapter, TP_PIO_ADDR, TP_PIO_DATA, - &v, 1, TP_VLAN_PRI_MAP); - - /* - * We need Five Tuple Lookup mode to be set in TP_GLOBAL_CONFIG order - * to support any of the compressed filter fields above. Newer - * versions of the firmware do this automatically but it doesn't hurt - * to set it here. Meanwhile, we do _not_ need to set Lookup Every - * Packet in TP_INGRESS_CONFIG to support matching non-TCP packets - * since the firmware automatically turns this on and off when we have - * a non-zero number of filters active (since it does have a - * performance impact). - */ - if (tp_vlan_pri_map) - t4_set_reg_field(adapter, TP_GLOBAL_CONFIG, - FIVETUPLELOOKUP_MASK, - FIVETUPLELOOKUP_MASK); - - /* - * Tweak some settings. - */ - t4_write_reg(adapter, TP_SHIFT_CNT, SYNSHIFTMAX(6) | - RXTSHIFTMAXR1(4) | RXTSHIFTMAXR2(15) | - PERSHIFTBACKOFFMAX(8) | PERSHIFTMAX(8) | - KEEPALIVEMAXR1(4) | KEEPALIVEMAXR2(9)); - - /* - * Get basic stuff going by issuing the Firmware Initialize command. - * Note that this _must_ be after all PFVF commands ... - */ - ret = t4_fw_initialize(adapter, adapter->mbox); - if (ret < 0) - goto bye; - - /* - * Return successfully! - */ - dev_info(adapter->pdev_dev, "Successfully configured using built-in "\ - "driver parameters\n"); - return 0; - - /* - * Something bad happened. Return the error ... - */ -bye: - return ret; -} - static struct fw_info fw_info_array[] = { { .chip = CHELSIO_T4, @@ -5529,6 +5088,8 @@ static int adap_init0(struct adapter *adap) enum dev_state state; u32 params[7], val[7]; struct fw_caps_config_cmd caps_cmd; + struct fw_devlog_cmd devlog_cmd; + u32 devlog_meminfo; int reset = 1; /* Contact FW, advertising Master capability */ @@ -5590,8 +5151,7 @@ static int adap_init0(struct adapter *adap) state, &reset); /* Cleaning up */ - if (fw != NULL) - release_firmware(fw); + release_firmware(fw); t4_free_mem(card_fw); if (ret < 0) @@ -5609,6 +5169,30 @@ static int adap_init0(struct adapter *adap) if (ret < 0) goto bye; + /* Read firmware device log parameters. We really need to find a way + * to get these parameters initialized with some default values (which + * are likely to be correct) for the case where we either don't + * attache to the firmware or it's crashed when we probe the adapter. + * That way we'll still be able to perform early firmware startup + * debugging ... If the request to get the Firmware's Device Log + * parameters fails, we'll live so we don't make that a fatal error. + */ + memset(&devlog_cmd, 0, sizeof(devlog_cmd)); + devlog_cmd.op_to_write = htonl(FW_CMD_OP_V(FW_DEVLOG_CMD) | + FW_CMD_REQUEST_F | FW_CMD_READ_F); + devlog_cmd.retval_len16 = htonl(FW_LEN16(devlog_cmd)); + ret = t4_wr_mbox(adap, adap->mbox, &devlog_cmd, sizeof(devlog_cmd), + &devlog_cmd); + if (ret == 0) { + devlog_meminfo = + ntohl(devlog_cmd.memtype_devlog_memaddr16_devlog); + adap->params.devlog.memtype = + FW_DEVLOG_CMD_MEMTYPE_DEVLOG_G(devlog_meminfo); + adap->params.devlog.start = + FW_DEVLOG_CMD_MEMADDR16_DEVLOG_G(devlog_meminfo) << 4; + adap->params.devlog.size = ntohl(devlog_cmd.memsize_devlog); + } + /* * Find out what ports are available to us. Note that we need to do * this before calling adap_init0_no_config() since it needs nports @@ -5624,88 +5208,58 @@ static int adap_init0(struct adapter *adap) adap->params.nports = hweight32(port_vec); adap->params.portvec = port_vec; - /* - * If the firmware is initialized already (and we're not forcing a - * master initialization), note that we're living with existing - * adapter parameters. Otherwise, it's time to try initializing the - * adapter ... + /* If the firmware is initialized already, emit a simply note to that + * effect. Otherwise, it's time to try initializing the adapter. */ if (state == DEV_STATE_INIT) { dev_info(adap->pdev_dev, "Coming up as %s: "\ "Adapter already initialized\n", adap->flags & MASTER_PF ? "MASTER" : "SLAVE"); - adap->flags |= USING_SOFT_PARAMS; } else { dev_info(adap->pdev_dev, "Coming up as MASTER: "\ "Initializing adapter\n"); - /* - * If the firmware doesn't support Configuration - * Files warn user and exit, + + /* Find out whether we're dealing with a version of the + * firmware which has configuration file support. */ - if (ret < 0) - dev_warn(adap->pdev_dev, "Firmware doesn't support " - "configuration file.\n"); - if (force_old_init) - ret = adap_init0_no_config(adap, reset); - else { - /* - * Find out whether we're dealing with a version of - * the firmware which has configuration file support. - */ - params[0] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) | - FW_PARAMS_PARAM_X_V( - FW_PARAMS_PARAM_DEV_CF)); - ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 1, - params, val); - - /* - * If the firmware doesn't support Configuration - * Files, use the old Driver-based, hard-wired - * initialization. Otherwise, try using the - * Configuration File support and fall back to the - * Driver-based initialization if there's no - * Configuration File found. - */ - if (ret < 0) - ret = adap_init0_no_config(adap, reset); - else { - /* - * The firmware provides us with a memory - * buffer where we can load a Configuration - * File from the host if we want to override - * the Configuration File in flash. - */ + params[0] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) | + FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_CF)); + ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 1, + params, val); - ret = adap_init0_config(adap, reset); - if (ret == -ENOENT) { - dev_info(adap->pdev_dev, - "No Configuration File present " - "on adapter. Using hard-wired " - "configuration parameters.\n"); - ret = adap_init0_no_config(adap, reset); - } - } + /* If the firmware doesn't support Configuration Files, + * return an error. + */ + if (ret < 0) { + dev_err(adap->pdev_dev, "firmware doesn't support " + "Firmware Configuration Files\n"); + goto bye; + } + + /* The firmware provides us with a memory buffer where we can + * load a Configuration File from the host if we want to + * override the Configuration File in flash. + */ + ret = adap_init0_config(adap, reset); + if (ret == -ENOENT) { + dev_err(adap->pdev_dev, "no Configuration File " + "present on adapter.\n"); + goto bye; } if (ret < 0) { - dev_err(adap->pdev_dev, - "could not initialize adapter, error %d\n", - -ret); + dev_err(adap->pdev_dev, "could not initialize " + "adapter, error %d\n", -ret); goto bye; } } - /* - * If we're living with non-hard-coded parameters (either from a - * Firmware Configuration File or values programmed by a different PF - * Driver), give the SGE code a chance to pull in anything that it - * needs ... Note that this must be called after we retrieve our VPD - * parameters in order to know how to convert core ticks to seconds. + /* Give the SGE code a chance to pull in anything that it needs ... + * Note that this must be called after we retrieve our VPD parameters + * in order to know how to convert core ticks to seconds, etc. */ - if (adap->flags & USING_SOFT_PARAMS) { - ret = t4_sge_init(adap); - if (ret < 0) - goto bye; - } + ret = t4_sge_init(adap); + if (ret < 0) + goto bye; if (is_bypass_device(adap->pdev->device)) adap->params.bypass = 1; @@ -5739,6 +5293,14 @@ static int adap_init0(struct adapter *adap) adap->tids.nftids = val[4] - val[3] + 1; adap->sge.ingr_start = val[5]; + params[0] = FW_PARAM_PFVF(CLIP_START); + params[1] = FW_PARAM_PFVF(CLIP_END); + ret = t4_query_params(adap, adap->mbox, adap->fn, 0, 2, params, val); + if (ret < 0) + goto bye; + adap->clipt_start = val[0]; + adap->clipt_end = val[1]; + /* query params related to active filter region */ params[0] = FW_PARAM_PFVF(ACTIVE_FILTER_START); params[1] = FW_PARAM_PFVF(ACTIVE_FILTER_END); @@ -6401,7 +5963,7 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_unmap_bar0; /* We control everything through one PF */ - func = SOURCEPF_GET(readl(regs + PL_WHOAMI)); + func = SOURCEPF_G(readl(regs + PL_WHOAMI_A)); if (func != ent->driver_data) { iounmap(regs); pci_disable_device(pdev); @@ -6467,9 +6029,11 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (!is_t4(adapter->params.chip)) { - s_qpp = QUEUESPERPAGEPF1 * adapter->fn; - qpp = 1 << QUEUESPERPAGEPF0_GET(t4_read_reg(adapter, - SGE_EGRESS_QUEUES_PER_PAGE_PF) >> s_qpp); + s_qpp = (QUEUESPERPAGEPF0_S + + (QUEUESPERPAGEPF1_S - QUEUESPERPAGEPF0_S) * + adapter->fn); + qpp = 1 << QUEUESPERPAGEPF0_G(t4_read_reg(adapter, + SGE_EGRESS_QUEUES_PER_PAGE_PF_A) >> s_qpp); num_seg = PAGE_SIZE / SEGMENT_SIZE; /* Each segment size is 128B. Write coalescing is enabled only @@ -6557,6 +6121,18 @@ static int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) adapter->params.offload = 0; } +#if IS_ENABLED(CONFIG_IPV6) + adapter->clipt = t4_init_clip_tbl(adapter->clipt_start, + adapter->clipt_end); + if (!adapter->clipt) { + /* We tolerate a lack of clip_table, giving up + * some functionality + */ + dev_warn(&pdev->dev, + "could not allocate Clip table, continuing\n"); + adapter->params.offload = 0; + } +#endif if (is_offload(adapter) && tid_init(&adapter->tids) < 0) { dev_warn(&pdev->dev, "could not allocate TID table, " "continuing\n"); @@ -6682,6 +6258,9 @@ static void remove_one(struct pci_dev *pdev) cxgb_down(adapter); free_some_resources(adapter); +#if IS_ENABLED(CONFIG_IPV6) + t4_cleanup_clip_tbl(adapter); +#endif iounmap(adapter->regs); if (!is_t4(adapter->params.chip)) iounmap(adapter->bar2); @@ -6720,7 +6299,10 @@ static int __init cxgb4_init_module(void) debugfs_remove(cxgb4_debugfs_root); #if IS_ENABLED(CONFIG_IPV6) - register_inet6addr_notifier(&cxgb4_inet6addr_notifier); + if (!inet6addr_registered) { + register_inet6addr_notifier(&cxgb4_inet6addr_notifier); + inet6addr_registered = true; + } #endif return ret; @@ -6729,7 +6311,10 @@ static int __init cxgb4_init_module(void) static void __exit cxgb4_cleanup_module(void) { #if IS_ENABLED(CONFIG_IPV6) - unregister_inet6addr_notifier(&cxgb4_inet6addr_notifier); + if (inet6addr_registered) { + unregister_inet6addr_notifier(&cxgb4_inet6addr_notifier); + inet6addr_registered = false; + } #endif pci_unregister_driver(&cxgb4_driver); debugfs_remove(cxgb4_debugfs_root); /* NULL ok */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h index 152b4c4c7809..78ab4d406ce2 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h @@ -173,9 +173,6 @@ int cxgb4_create_server_filter(const struct net_device *dev, unsigned int stid, unsigned char port, unsigned char mask); int cxgb4_remove_server_filter(const struct net_device *dev, unsigned int stid, unsigned int queue, bool ipv6); -int cxgb4_clip_get(const struct net_device *dev, const struct in6_addr *lip); -int cxgb4_clip_release(const struct net_device *dev, - const struct in6_addr *lip); static inline void set_wr_txq(struct sk_buff *skb, int prio, int queue) { diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.c b/drivers/net/ethernet/chelsio/cxgb4/l2t.c index a047baa9fd04..252efc29321f 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/l2t.c +++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.c @@ -46,6 +46,7 @@ #include "t4_msg.h" #include "t4fw_api.h" #include "t4_regs.h" +#include "t4_values.h" #define VLAN_NONE 0xfff @@ -150,8 +151,8 @@ static int write_l2e(struct adapter *adap, struct l2t_entry *e, int sync) OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_L2T_WRITE_REQ, e->idx | (sync ? F_SYNC_WR : 0) | - TID_QID(adap->sge.fw_evtq.abs_id))); - req->params = htons(L2T_W_PORT(e->lport) | L2T_W_NOREPLY(!sync)); + TID_QID_V(adap->sge.fw_evtq.abs_id))); + req->params = htons(L2T_W_PORT_V(e->lport) | L2T_W_NOREPLY_V(!sync)); req->l2t_idx = htons(e->idx); req->vlan = htons(e->vlan); if (e->neigh && !(e->neigh->dev->flags & IFF_LOOPBACK)) @@ -425,7 +426,7 @@ u64 cxgb4_select_ntuple(struct net_device *dev, * in the Compressed Filter Tuple. */ if (tp->vlan_shift >= 0 && l2t->vlan != VLAN_NONE) - ntuple |= (u64)(F_FT_VLAN_VLD | l2t->vlan) << tp->vlan_shift; + ntuple |= (u64)(FT_VLAN_VLD_F | l2t->vlan) << tp->vlan_shift; if (tp->port_shift >= 0) ntuple |= (u64)l2t->lport << tp->port_shift; @@ -439,9 +440,9 @@ u64 cxgb4_select_ntuple(struct net_device *dev, u32 pf = FW_VIID_PFN_G(viid); u32 vld = FW_VIID_VIVLD_G(viid); - ntuple |= (u64)(V_FT_VNID_ID_VF(vf) | - V_FT_VNID_ID_PF(pf) | - V_FT_VNID_ID_VLD(vld)) << tp->vnic_shift; + ntuple |= (u64)(FT_VNID_ID_VF_V(vf) | + FT_VNID_ID_PF_V(pf) | + FT_VNID_ID_VLD_V(vld)) << tp->vnic_shift; } return ntuple; diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c b/drivers/net/ethernet/chelsio/cxgb4/sge.c index ebf935a1e352..b4b9f6048fe7 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c @@ -43,8 +43,12 @@ #include <linux/export.h> #include <net/ipv6.h> #include <net/tcp.h> +#ifdef CONFIG_NET_RX_BUSY_POLL +#include <net/busy_poll.h> +#endif /* CONFIG_NET_RX_BUSY_POLL */ #include "cxgb4.h" #include "t4_regs.h" +#include "t4_values.h" #include "t4_msg.h" #include "t4fw_api.h" @@ -521,10 +525,12 @@ static inline void ring_fl_db(struct adapter *adap, struct sge_fl *q) { u32 val; if (q->pend_cred >= 8) { - val = PIDX(q->pend_cred / 8); - if (!is_t4(adap->params.chip)) - val |= DBTYPE(1); - val |= DBPRIO(1); + if (is_t4(adap->params.chip)) + val = PIDX_V(q->pend_cred / 8); + else + val = PIDX_T5_V(q->pend_cred / 8) | + DBTYPE_F; + val |= DBPRIO_F; wmb(); /* If we don't have access to the new User Doorbell (T5+), use @@ -532,10 +538,10 @@ static inline void ring_fl_db(struct adapter *adap, struct sge_fl *q) * mechanism. */ if (unlikely(q->bar2_addr == NULL)) { - t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL), - val | QID(q->cntxt_id)); + t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL_A), + val | QID_V(q->cntxt_id)); } else { - writel(val | QID(q->bar2_qid), + writel(val | QID_V(q->bar2_qid), q->bar2_addr + SGE_UDB_KDOORBELL); /* This Write memory Barrier will force the write to @@ -818,7 +824,8 @@ static void write_sgl(const struct sk_buff *skb, struct sge_txq *q, sgl->addr0 = cpu_to_be64(addr[1]); } - sgl->cmd_nsge = htonl(ULPTX_CMD_V(ULP_TX_SC_DSGL) | ULPTX_NSGE(nfrags)); + sgl->cmd_nsge = htonl(ULPTX_CMD_V(ULP_TX_SC_DSGL) | + ULPTX_NSGE_V(nfrags)); if (likely(--nfrags == 0)) return; /* @@ -884,7 +891,7 @@ static inline void ring_tx_db(struct adapter *adap, struct sge_txq *q, int n) * doorbell mechanism; otherwise use the new BAR2 mechanism. */ if (unlikely(q->bar2_addr == NULL)) { - u32 val = PIDX(n); + u32 val = PIDX_V(n); unsigned long flags; /* For T4 we need to participate in the Doorbell Recovery @@ -892,14 +899,14 @@ static inline void ring_tx_db(struct adapter *adap, struct sge_txq *q, int n) */ spin_lock_irqsave(&q->db_lock, flags); if (!q->db_disabled) - t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL), - QID(q->cntxt_id) | val); + t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL_A), + QID_V(q->cntxt_id) | val); else q->db_pidx_inc += n; q->db_pidx = q->pidx; spin_unlock_irqrestore(&q->db_lock, flags); } else { - u32 val = PIDX_T5(n); + u32 val = PIDX_T5_V(n); /* T4 and later chips share the same PIDX field offset within * the doorbell, but T5 and later shrank the field in order to @@ -907,7 +914,7 @@ static inline void ring_tx_db(struct adapter *adap, struct sge_txq *q, int n) * large in the first place (14 bits) so we just use the T5 * and later limits and warn if a Queue ID is too large. */ - WARN_ON(val & DBPRIO(1)); + WARN_ON(val & DBPRIO_F); /* If we're only writing a single TX Descriptor and we can use * Inferred QID registers, we can use the Write Combining @@ -923,7 +930,7 @@ static inline void ring_tx_db(struct adapter *adap, struct sge_txq *q, int n) (q->bar2_addr + SGE_UDB_WCDOORBELL), wr); } else { - writel(val | QID(q->bar2_qid), + writel(val | QID_V(q->bar2_qid), q->bar2_addr + SGE_UDB_KDOORBELL); } @@ -1150,9 +1157,9 @@ out_free: dev_kfree_skb_any(skb); cntrl = TXPKT_L4CSUM_DIS | TXPKT_IPCSUM_DIS; } - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { q->vlan_ins++; - cntrl |= TXPKT_VLAN_VLD | TXPKT_VLAN(vlan_tx_tag_get(skb)); + cntrl |= TXPKT_VLAN_VLD | TXPKT_VLAN(skb_vlan_tag_get(skb)); } cpl->ctrl0 = htonl(TXPKT_OPCODE(CPL_TX_PKT_XT) | @@ -1716,6 +1723,7 @@ static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl, skb->truesize += skb->data_len; skb->ip_summed = CHECKSUM_UNNECESSARY; skb_record_rx_queue(skb, rxq->rspq.idx); + skb_mark_napi_id(skb, &rxq->rspq.napi); if (rxq->rspq.netdev->features & NETIF_F_RXHASH) skb_set_hash(skb, (__force u32)pkt->rsshdr.hash_val, PKT_HASH_TYPE_L3); @@ -1758,7 +1766,8 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, pkt = (const struct cpl_rx_pkt *)rsp; csum_ok = pkt->csum_calc && !pkt->err_vec && (q->netdev->features & NETIF_F_RXCSUM); - if ((pkt->l2info & htonl(RXF_TCP)) && + if ((pkt->l2info & htonl(RXF_TCP_F)) && + !(cxgb_poll_busy_polling(q)) && (q->netdev->features & NETIF_F_GRO) && csum_ok && !pkt->ip_frag) { do_gro(rxq, si, pkt); return 0; @@ -1780,11 +1789,11 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, rxq->stats.pkts++; - if (csum_ok && (pkt->l2info & htonl(RXF_UDP | RXF_TCP))) { + if (csum_ok && (pkt->l2info & htonl(RXF_UDP_F | RXF_TCP_F))) { if (!pkt->ip_frag) { skb->ip_summed = CHECKSUM_UNNECESSARY; rxq->stats.rx_cso++; - } else if (pkt->l2info & htonl(RXF_IP)) { + } else if (pkt->l2info & htonl(RXF_IP_F)) { __sum16 c = (__force __sum16)pkt->csum; skb->csum = csum_unfold(c); skb->ip_summed = CHECKSUM_COMPLETE; @@ -1797,6 +1806,7 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(pkt->vlan)); rxq->stats.vlan_ex++; } + skb_mark_napi_id(skb, &q->napi); netif_receive_skb(skb); return 0; } @@ -1959,6 +1969,38 @@ static int process_responses(struct sge_rspq *q, int budget) return budget - budget_left; } +#ifdef CONFIG_NET_RX_BUSY_POLL +int cxgb_busy_poll(struct napi_struct *napi) +{ + struct sge_rspq *q = container_of(napi, struct sge_rspq, napi); + unsigned int params, work_done; + u32 val; + + if (!cxgb_poll_lock_poll(q)) + return LL_FLUSH_BUSY; + + work_done = process_responses(q, 4); + params = QINTR_TIMER_IDX(TIMERREG_COUNTER0_X) | QINTR_CNT_EN; + q->next_intr_params = params; + val = CIDXINC_V(work_done) | SEINTARM_V(params); + + /* If we don't have access to the new User GTS (T5+), use the old + * doorbell mechanism; otherwise use the new BAR2 mechanism. + */ + if (unlikely(!q->bar2_addr)) + t4_write_reg(q->adap, MYPF_REG(SGE_PF_GTS_A), + val | INGRESSQID_V((u32)q->cntxt_id)); + else { + writel(val | INGRESSQID_V(q->bar2_qid), + q->bar2_addr + SGE_UDB_GTS); + wmb(); + } + + cxgb_poll_unlock_poll(q); + return work_done; +} +#endif /* CONFIG_NET_RX_BUSY_POLL */ + /** * napi_rx_handler - the NAPI handler for Rx processing * @napi: the napi instance @@ -1974,9 +2016,13 @@ static int napi_rx_handler(struct napi_struct *napi, int budget) { unsigned int params; struct sge_rspq *q = container_of(napi, struct sge_rspq, napi); - int work_done = process_responses(q, budget); + int work_done; u32 val; + if (!cxgb_poll_lock_napi(q)) + return budget; + + work_done = process_responses(q, budget); if (likely(work_done < budget)) { int timer_index; @@ -2001,19 +2047,20 @@ static int napi_rx_handler(struct napi_struct *napi, int budget) } else params = QINTR_TIMER_IDX(7); - val = CIDXINC(work_done) | SEINTARM(params); + val = CIDXINC_V(work_done) | SEINTARM_V(params); /* If we don't have access to the new User GTS (T5+), use the old * doorbell mechanism; otherwise use the new BAR2 mechanism. */ if (unlikely(q->bar2_addr == NULL)) { - t4_write_reg(q->adap, MYPF_REG(SGE_PF_GTS), - val | INGRESSQID((u32)q->cntxt_id)); + t4_write_reg(q->adap, MYPF_REG(SGE_PF_GTS_A), + val | INGRESSQID_V((u32)q->cntxt_id)); } else { - writel(val | INGRESSQID(q->bar2_qid), + writel(val | INGRESSQID_V(q->bar2_qid), q->bar2_addr + SGE_UDB_GTS); wmb(); } + cxgb_poll_unlock_napi(q); return work_done; } @@ -2056,16 +2103,16 @@ static unsigned int process_intrq(struct adapter *adap) rspq_next(q); } - val = CIDXINC(credits) | SEINTARM(q->intr_params); + val = CIDXINC_V(credits) | SEINTARM_V(q->intr_params); /* If we don't have access to the new User GTS (T5+), use the old * doorbell mechanism; otherwise use the new BAR2 mechanism. */ if (unlikely(q->bar2_addr == NULL)) { - t4_write_reg(adap, MYPF_REG(SGE_PF_GTS), - val | INGRESSQID(q->cntxt_id)); + t4_write_reg(adap, MYPF_REG(SGE_PF_GTS_A), + val | INGRESSQID_V(q->cntxt_id)); } else { - writel(val | INGRESSQID(q->bar2_qid), + writel(val | INGRESSQID_V(q->bar2_qid), q->bar2_addr + SGE_UDB_GTS); wmb(); } @@ -2095,7 +2142,7 @@ static irqreturn_t t4_intr_intx(int irq, void *cookie) { struct adapter *adap = cookie; - t4_write_reg(adap, MYPF_REG(PCIE_PF_CLI), 0); + t4_write_reg(adap, MYPF_REG(PCIE_PF_CLI_A), 0); if (t4_slow_intr_handler(adap) | process_intrq(adap)) return IRQ_HANDLED; return IRQ_NONE; /* probably shared interrupt */ @@ -2142,9 +2189,9 @@ static void sge_rx_timer_cb(unsigned long data) } } - t4_write_reg(adap, SGE_DEBUG_INDEX, 13); - idma_same_state_cnt[0] = t4_read_reg(adap, SGE_DEBUG_DATA_HIGH); - idma_same_state_cnt[1] = t4_read_reg(adap, SGE_DEBUG_DATA_LOW); + t4_write_reg(adap, SGE_DEBUG_INDEX_A, 13); + idma_same_state_cnt[0] = t4_read_reg(adap, SGE_DEBUG_DATA_HIGH_A); + idma_same_state_cnt[1] = t4_read_reg(adap, SGE_DEBUG_DATA_LOW_A); for (i = 0; i < 2; i++) { u32 debug0, debug11; @@ -2188,12 +2235,12 @@ static void sge_rx_timer_cb(unsigned long data) /* Read and save the SGE IDMA State and Queue ID information. * We do this every time in case it changes across time ... */ - t4_write_reg(adap, SGE_DEBUG_INDEX, 0); - debug0 = t4_read_reg(adap, SGE_DEBUG_DATA_LOW); + t4_write_reg(adap, SGE_DEBUG_INDEX_A, 0); + debug0 = t4_read_reg(adap, SGE_DEBUG_DATA_LOW_A); s->idma_state[i] = (debug0 >> (i * 9)) & 0x3f; - t4_write_reg(adap, SGE_DEBUG_INDEX, 11); - debug11 = t4_read_reg(adap, SGE_DEBUG_DATA_LOW); + t4_write_reg(adap, SGE_DEBUG_INDEX_A, 11); + debug11 = t4_read_reg(adap, SGE_DEBUG_DATA_LOW_A); s->idma_qid[i] = (debug11 >> (i * 16)) & 0xffff; CH_WARN(adap, "SGE idma%u, queue%u, maybe stuck state%u %dsecs (debug0=%#x, debug11=%#x)\n", @@ -2337,6 +2384,7 @@ int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq, goto err; netif_napi_add(dev, &iq->napi, napi_rx_handler, 64); + napi_hash_add(&iq->napi); iq->cur_desc = iq->desc; iq->cidx = 0; iq->gen = 1; @@ -2594,6 +2642,7 @@ static void free_rspq_fl(struct adapter *adap, struct sge_rspq *rq, rq->cntxt_id, fl_id, 0xffff); dma_free_coherent(adap->pdev_dev, (rq->size + 1) * rq->iqe_len, rq->desc, rq->phys_addr); + napi_hash_del(&rq->napi); netif_napi_del(&rq->napi); rq->netdev = NULL; rq->cntxt_id = rq->abs_id = 0; @@ -2738,24 +2787,11 @@ void t4_sge_stop(struct adapter *adap) } /** - * t4_sge_init - initialize SGE + * t4_sge_init_soft - grab core SGE values needed by SGE code * @adap: the adapter * - * Performs SGE initialization needed every time after a chip reset. - * We do not initialize any of the queues here, instead the driver - * top-level must request them individually. - * - * Called in two different modes: - * - * 1. Perform actual hardware initialization and record hard-coded - * parameters which were used. This gets used when we're the - * Master PF and the Firmware Configuration File support didn't - * work for some reason. - * - * 2. We're not the Master PF or initialization was performed with - * a Firmware Configuration File. In this case we need to grab - * any of the SGE operating parameters that we need to have in - * order to do our job and make sure we can live with them ... + * We need to grab the SGE operating parameters that we need to have + * in order to do our job and make sure we can live with them. */ static int t4_sge_init_soft(struct adapter *adap) @@ -2770,8 +2806,8 @@ static int t4_sge_init_soft(struct adapter *adap) * process_responses() and that only packet data is going to the * Free Lists. */ - if ((t4_read_reg(adap, SGE_CONTROL) & RXPKTCPLMODE_MASK) != - RXPKTCPLMODE(X_RXPKTCPLMODE_SPLIT)) { + if ((t4_read_reg(adap, SGE_CONTROL_A) & RXPKTCPLMODE_F) != + RXPKTCPLMODE_V(RXPKTCPLMODE_SPLIT_X)) { dev_err(adap->pdev_dev, "bad SGE CPL MODE\n"); return -EINVAL; } @@ -2785,7 +2821,7 @@ static int t4_sge_init_soft(struct adapter *adap) * XXX meet our needs! */ #define READ_FL_BUF(x) \ - t4_read_reg(adap, SGE_FL_BUFFER_SIZE0+(x)*sizeof(u32)) + t4_read_reg(adap, SGE_FL_BUFFER_SIZE0_A+(x)*sizeof(u32)) fl_small_pg = READ_FL_BUF(RX_SMALL_PG_BUF); fl_large_pg = READ_FL_BUF(RX_LARGE_PG_BUF); @@ -2823,99 +2859,38 @@ static int t4_sge_init_soft(struct adapter *adap) * Retrieve our RX interrupt holdoff timer values and counter * threshold values from the SGE parameters. */ - timer_value_0_and_1 = t4_read_reg(adap, SGE_TIMER_VALUE_0_AND_1); - timer_value_2_and_3 = t4_read_reg(adap, SGE_TIMER_VALUE_2_AND_3); - timer_value_4_and_5 = t4_read_reg(adap, SGE_TIMER_VALUE_4_AND_5); + timer_value_0_and_1 = t4_read_reg(adap, SGE_TIMER_VALUE_0_AND_1_A); + timer_value_2_and_3 = t4_read_reg(adap, SGE_TIMER_VALUE_2_AND_3_A); + timer_value_4_and_5 = t4_read_reg(adap, SGE_TIMER_VALUE_4_AND_5_A); s->timer_val[0] = core_ticks_to_us(adap, - TIMERVALUE0_GET(timer_value_0_and_1)); + TIMERVALUE0_G(timer_value_0_and_1)); s->timer_val[1] = core_ticks_to_us(adap, - TIMERVALUE1_GET(timer_value_0_and_1)); + TIMERVALUE1_G(timer_value_0_and_1)); s->timer_val[2] = core_ticks_to_us(adap, - TIMERVALUE2_GET(timer_value_2_and_3)); + TIMERVALUE2_G(timer_value_2_and_3)); s->timer_val[3] = core_ticks_to_us(adap, - TIMERVALUE3_GET(timer_value_2_and_3)); + TIMERVALUE3_G(timer_value_2_and_3)); s->timer_val[4] = core_ticks_to_us(adap, - TIMERVALUE4_GET(timer_value_4_and_5)); + TIMERVALUE4_G(timer_value_4_and_5)); s->timer_val[5] = core_ticks_to_us(adap, - TIMERVALUE5_GET(timer_value_4_and_5)); + TIMERVALUE5_G(timer_value_4_and_5)); - ingress_rx_threshold = t4_read_reg(adap, SGE_INGRESS_RX_THRESHOLD); - s->counter_val[0] = THRESHOLD_0_GET(ingress_rx_threshold); - s->counter_val[1] = THRESHOLD_1_GET(ingress_rx_threshold); - s->counter_val[2] = THRESHOLD_2_GET(ingress_rx_threshold); - s->counter_val[3] = THRESHOLD_3_GET(ingress_rx_threshold); - - return 0; -} - -static int t4_sge_init_hard(struct adapter *adap) -{ - struct sge *s = &adap->sge; - - /* - * Set up our basic SGE mode to deliver CPL messages to our Ingress - * Queue and Packet Date to the Free List. - */ - t4_set_reg_field(adap, SGE_CONTROL, RXPKTCPLMODE_MASK, - RXPKTCPLMODE_MASK); - - /* - * Set up to drop DOORBELL writes when the DOORBELL FIFO overflows - * and generate an interrupt when this occurs so we can recover. - */ - if (is_t4(adap->params.chip)) { - t4_set_reg_field(adap, A_SGE_DBFIFO_STATUS, - V_HP_INT_THRESH(M_HP_INT_THRESH) | - V_LP_INT_THRESH(M_LP_INT_THRESH), - V_HP_INT_THRESH(dbfifo_int_thresh) | - V_LP_INT_THRESH(dbfifo_int_thresh)); - } else { - t4_set_reg_field(adap, A_SGE_DBFIFO_STATUS, - V_LP_INT_THRESH_T5(M_LP_INT_THRESH_T5), - V_LP_INT_THRESH_T5(dbfifo_int_thresh)); - t4_set_reg_field(adap, SGE_DBFIFO_STATUS2, - V_HP_INT_THRESH_T5(M_HP_INT_THRESH_T5), - V_HP_INT_THRESH_T5(dbfifo_int_thresh)); - } - t4_set_reg_field(adap, A_SGE_DOORBELL_CONTROL, F_ENABLE_DROP, - F_ENABLE_DROP); - - /* - * SGE_FL_BUFFER_SIZE0 (RX_SMALL_PG_BUF) is set up by - * t4_fixup_host_params(). - */ - s->fl_pg_order = FL_PG_ORDER; - if (s->fl_pg_order) - t4_write_reg(adap, - SGE_FL_BUFFER_SIZE0+RX_LARGE_PG_BUF*sizeof(u32), - PAGE_SIZE << FL_PG_ORDER); - t4_write_reg(adap, SGE_FL_BUFFER_SIZE0+RX_SMALL_MTU_BUF*sizeof(u32), - FL_MTU_SMALL_BUFSIZE(adap)); - t4_write_reg(adap, SGE_FL_BUFFER_SIZE0+RX_LARGE_MTU_BUF*sizeof(u32), - FL_MTU_LARGE_BUFSIZE(adap)); - - /* - * Note that the SGE Ingress Packet Count Interrupt Threshold and - * Timer Holdoff values must be supplied by our caller. - */ - t4_write_reg(adap, SGE_INGRESS_RX_THRESHOLD, - THRESHOLD_0(s->counter_val[0]) | - THRESHOLD_1(s->counter_val[1]) | - THRESHOLD_2(s->counter_val[2]) | - THRESHOLD_3(s->counter_val[3])); - t4_write_reg(adap, SGE_TIMER_VALUE_0_AND_1, - TIMERVALUE0(us_to_core_ticks(adap, s->timer_val[0])) | - TIMERVALUE1(us_to_core_ticks(adap, s->timer_val[1]))); - t4_write_reg(adap, SGE_TIMER_VALUE_2_AND_3, - TIMERVALUE2(us_to_core_ticks(adap, s->timer_val[2])) | - TIMERVALUE3(us_to_core_ticks(adap, s->timer_val[3]))); - t4_write_reg(adap, SGE_TIMER_VALUE_4_AND_5, - TIMERVALUE4(us_to_core_ticks(adap, s->timer_val[4])) | - TIMERVALUE5(us_to_core_ticks(adap, s->timer_val[5]))); + ingress_rx_threshold = t4_read_reg(adap, SGE_INGRESS_RX_THRESHOLD_A); + s->counter_val[0] = THRESHOLD_0_G(ingress_rx_threshold); + s->counter_val[1] = THRESHOLD_1_G(ingress_rx_threshold); + s->counter_val[2] = THRESHOLD_2_G(ingress_rx_threshold); + s->counter_val[3] = THRESHOLD_3_G(ingress_rx_threshold); return 0; } +/** + * t4_sge_init - initialize SGE + * @adap: the adapter + * + * Perform low-level SGE code initialization needed every time after a + * chip reset. + */ int t4_sge_init(struct adapter *adap) { struct sge *s = &adap->sge; @@ -2927,9 +2902,9 @@ int t4_sge_init(struct adapter *adap) * Ingress Padding Boundary and Egress Status Page Size are set up by * t4_fixup_host_params(). */ - sge_control = t4_read_reg(adap, SGE_CONTROL); - s->pktshift = PKTSHIFT_GET(sge_control); - s->stat_len = (sge_control & EGRSTATUSPAGESIZE_MASK) ? 128 : 64; + sge_control = t4_read_reg(adap, SGE_CONTROL_A); + s->pktshift = PKTSHIFT_G(sge_control); + s->stat_len = (sge_control & EGRSTATUSPAGESIZE_F) ? 128 : 64; /* T4 uses a single control field to specify both the PCIe Padding and * Packing Boundary. T5 introduced the ability to specify these @@ -2937,8 +2912,8 @@ int t4_sge_init(struct adapter *adap) * within Packed Buffer Mode is the maximum of these two * specifications. */ - ingpadboundary = 1 << (INGPADBOUNDARY_GET(sge_control) + - X_INGPADBOUNDARY_SHIFT); + ingpadboundary = 1 << (INGPADBOUNDARY_G(sge_control) + + INGPADBOUNDARY_SHIFT_X); if (is_t4(adap->params.chip)) { s->fl_align = ingpadboundary; } else { @@ -2956,10 +2931,7 @@ int t4_sge_init(struct adapter *adap) s->fl_align = max(ingpadboundary, ingpackboundary); } - if (adap->flags & USING_SOFT_PARAMS) - ret = t4_sge_init_soft(adap); - else - ret = t4_sge_init_hard(adap); + ret = t4_sge_init_soft(adap); if (ret < 0) return ret; @@ -2975,11 +2947,11 @@ int t4_sge_init(struct adapter *adap) * buffers and a new field which only applies to Packed Mode Free List * buffers. */ - sge_conm_ctrl = t4_read_reg(adap, SGE_CONM_CTRL); + sge_conm_ctrl = t4_read_reg(adap, SGE_CONM_CTRL_A); if (is_t4(adap->params.chip)) - egress_threshold = EGRTHRESHOLD_GET(sge_conm_ctrl); + egress_threshold = EGRTHRESHOLD_G(sge_conm_ctrl); else - egress_threshold = EGRTHRESHOLDPACKING_GET(sge_conm_ctrl); + egress_threshold = EGRTHRESHOLDPACKING_G(sge_conm_ctrl); s->fl_starve_thres = 2*egress_threshold + 1; setup_timer(&s->rx_timer, sge_rx_timer_cb, (unsigned long)adap); diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index c132d9030729..4d643b65265e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -35,6 +35,7 @@ #include <linux/delay.h> #include "cxgb4.h" #include "t4_regs.h" +#include "t4_values.h" #include "t4fw_api.h" /** @@ -149,20 +150,20 @@ void t4_write_indirect(struct adapter *adap, unsigned int addr_reg, */ void t4_hw_pci_read_cfg4(struct adapter *adap, int reg, u32 *val) { - u32 req = ENABLE | FUNCTION(adap->fn) | reg; + u32 req = ENABLE_F | FUNCTION_V(adap->fn) | REGISTER_V(reg); if (is_t4(adap->params.chip)) - req |= F_LOCALCFG; + req |= LOCALCFG_F; - t4_write_reg(adap, PCIE_CFG_SPACE_REQ, req); - *val = t4_read_reg(adap, PCIE_CFG_SPACE_DATA); + t4_write_reg(adap, PCIE_CFG_SPACE_REQ_A, req); + *val = t4_read_reg(adap, PCIE_CFG_SPACE_DATA_A); /* Reset ENABLE to 0 so reads of PCIE_CFG_SPACE_DATA won't cause a * Configuration Space read. (None of the other fields matter when * ENABLE is 0 so a simple register write is easier than a * read-modify-write via t4_set_reg_field().) */ - t4_write_reg(adap, PCIE_CFG_SPACE_REQ, 0); + t4_write_reg(adap, PCIE_CFG_SPACE_REQ_A, 0); } /* @@ -187,8 +188,8 @@ static void t4_report_fw_error(struct adapter *adap) }; u32 pcie_fw; - pcie_fw = t4_read_reg(adap, MA_PCIE_FW); - if (pcie_fw & PCIE_FW_ERR) + pcie_fw = t4_read_reg(adap, PCIE_FW_A); + if (pcie_fw & PCIE_FW_ERR_F) dev_err(adap->pdev_dev, "Firmware reports adapter error: %s\n", reason[PCIE_FW_EVAL_G(pcie_fw)]); } @@ -264,8 +265,8 @@ int t4_wr_mbox_meat(struct adapter *adap, int mbox, const void *cmd, int size, u64 res; int i, ms, delay_idx; const __be64 *p = cmd; - u32 data_reg = PF_REG(mbox, CIM_PF_MAILBOX_DATA); - u32 ctl_reg = PF_REG(mbox, CIM_PF_MAILBOX_CTRL); + u32 data_reg = PF_REG(mbox, CIM_PF_MAILBOX_DATA_A); + u32 ctl_reg = PF_REG(mbox, CIM_PF_MAILBOX_CTRL_A); if ((size & 15) || size > MBOX_LEN) return -EINVAL; @@ -277,9 +278,9 @@ int t4_wr_mbox_meat(struct adapter *adap, int mbox, const void *cmd, int size, if (adap->pdev->error_state != pci_channel_io_normal) return -EIO; - v = MBOWNER_GET(t4_read_reg(adap, ctl_reg)); + v = MBOWNER_G(t4_read_reg(adap, ctl_reg)); for (i = 0; v == MBOX_OWNER_NONE && i < 3; i++) - v = MBOWNER_GET(t4_read_reg(adap, ctl_reg)); + v = MBOWNER_G(t4_read_reg(adap, ctl_reg)); if (v != MBOX_OWNER_DRV) return v ? -EBUSY : -ETIMEDOUT; @@ -287,7 +288,7 @@ int t4_wr_mbox_meat(struct adapter *adap, int mbox, const void *cmd, int size, for (i = 0; i < size; i += 8) t4_write_reg64(adap, data_reg + i, be64_to_cpu(*p++)); - t4_write_reg(adap, ctl_reg, MBMSGVALID | MBOWNER(MBOX_OWNER_FW)); + t4_write_reg(adap, ctl_reg, MBMSGVALID_F | MBOWNER_V(MBOX_OWNER_FW)); t4_read_reg(adap, ctl_reg); /* flush write */ delay_idx = 0; @@ -303,8 +304,8 @@ int t4_wr_mbox_meat(struct adapter *adap, int mbox, const void *cmd, int size, mdelay(ms); v = t4_read_reg(adap, ctl_reg); - if (MBOWNER_GET(v) == MBOX_OWNER_DRV) { - if (!(v & MBMSGVALID)) { + if (MBOWNER_G(v) == MBOX_OWNER_DRV) { + if (!(v & MBMSGVALID_F)) { t4_write_reg(adap, ctl_reg, 0); continue; } @@ -350,27 +351,27 @@ int t4_mc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, u64 *ecc) u32 mc_bist_status_rdata, mc_bist_data_pattern; if (is_t4(adap->params.chip)) { - mc_bist_cmd = MC_BIST_CMD; - mc_bist_cmd_addr = MC_BIST_CMD_ADDR; - mc_bist_cmd_len = MC_BIST_CMD_LEN; - mc_bist_status_rdata = MC_BIST_STATUS_RDATA; - mc_bist_data_pattern = MC_BIST_DATA_PATTERN; + mc_bist_cmd = MC_BIST_CMD_A; + mc_bist_cmd_addr = MC_BIST_CMD_ADDR_A; + mc_bist_cmd_len = MC_BIST_CMD_LEN_A; + mc_bist_status_rdata = MC_BIST_STATUS_RDATA_A; + mc_bist_data_pattern = MC_BIST_DATA_PATTERN_A; } else { - mc_bist_cmd = MC_REG(MC_P_BIST_CMD, idx); - mc_bist_cmd_addr = MC_REG(MC_P_BIST_CMD_ADDR, idx); - mc_bist_cmd_len = MC_REG(MC_P_BIST_CMD_LEN, idx); - mc_bist_status_rdata = MC_REG(MC_P_BIST_STATUS_RDATA, idx); - mc_bist_data_pattern = MC_REG(MC_P_BIST_DATA_PATTERN, idx); + mc_bist_cmd = MC_REG(MC_P_BIST_CMD_A, idx); + mc_bist_cmd_addr = MC_REG(MC_P_BIST_CMD_ADDR_A, idx); + mc_bist_cmd_len = MC_REG(MC_P_BIST_CMD_LEN_A, idx); + mc_bist_status_rdata = MC_REG(MC_P_BIST_STATUS_RDATA_A, idx); + mc_bist_data_pattern = MC_REG(MC_P_BIST_DATA_PATTERN_A, idx); } - if (t4_read_reg(adap, mc_bist_cmd) & START_BIST) + if (t4_read_reg(adap, mc_bist_cmd) & START_BIST_F) return -EBUSY; t4_write_reg(adap, mc_bist_cmd_addr, addr & ~0x3fU); t4_write_reg(adap, mc_bist_cmd_len, 64); t4_write_reg(adap, mc_bist_data_pattern, 0xc); - t4_write_reg(adap, mc_bist_cmd, BIST_OPCODE(1) | START_BIST | - BIST_CMD_GAP(1)); - i = t4_wait_op_done(adap, mc_bist_cmd, START_BIST, 0, 10, 1); + t4_write_reg(adap, mc_bist_cmd, BIST_OPCODE_V(1) | START_BIST_F | + BIST_CMD_GAP_V(1)); + i = t4_wait_op_done(adap, mc_bist_cmd, START_BIST_F, 0, 10, 1); if (i) return i; @@ -403,31 +404,31 @@ int t4_edc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, u64 *ecc) u32 edc_bist_cmd_data_pattern, edc_bist_status_rdata; if (is_t4(adap->params.chip)) { - edc_bist_cmd = EDC_REG(EDC_BIST_CMD, idx); - edc_bist_cmd_addr = EDC_REG(EDC_BIST_CMD_ADDR, idx); - edc_bist_cmd_len = EDC_REG(EDC_BIST_CMD_LEN, idx); - edc_bist_cmd_data_pattern = EDC_REG(EDC_BIST_DATA_PATTERN, - idx); - edc_bist_status_rdata = EDC_REG(EDC_BIST_STATUS_RDATA, + edc_bist_cmd = EDC_REG(EDC_BIST_CMD_A, idx); + edc_bist_cmd_addr = EDC_REG(EDC_BIST_CMD_ADDR_A, idx); + edc_bist_cmd_len = EDC_REG(EDC_BIST_CMD_LEN_A, idx); + edc_bist_cmd_data_pattern = EDC_REG(EDC_BIST_DATA_PATTERN_A, idx); + edc_bist_status_rdata = EDC_REG(EDC_BIST_STATUS_RDATA_A, + idx); } else { - edc_bist_cmd = EDC_REG_T5(EDC_H_BIST_CMD, idx); - edc_bist_cmd_addr = EDC_REG_T5(EDC_H_BIST_CMD_ADDR, idx); - edc_bist_cmd_len = EDC_REG_T5(EDC_H_BIST_CMD_LEN, idx); + edc_bist_cmd = EDC_REG_T5(EDC_H_BIST_CMD_A, idx); + edc_bist_cmd_addr = EDC_REG_T5(EDC_H_BIST_CMD_ADDR_A, idx); + edc_bist_cmd_len = EDC_REG_T5(EDC_H_BIST_CMD_LEN_A, idx); edc_bist_cmd_data_pattern = - EDC_REG_T5(EDC_H_BIST_DATA_PATTERN, idx); + EDC_REG_T5(EDC_H_BIST_DATA_PATTERN_A, idx); edc_bist_status_rdata = - EDC_REG_T5(EDC_H_BIST_STATUS_RDATA, idx); + EDC_REG_T5(EDC_H_BIST_STATUS_RDATA_A, idx); } - if (t4_read_reg(adap, edc_bist_cmd) & START_BIST) + if (t4_read_reg(adap, edc_bist_cmd) & START_BIST_F) return -EBUSY; t4_write_reg(adap, edc_bist_cmd_addr, addr & ~0x3fU); t4_write_reg(adap, edc_bist_cmd_len, 64); t4_write_reg(adap, edc_bist_cmd_data_pattern, 0xc); t4_write_reg(adap, edc_bist_cmd, - BIST_OPCODE(1) | BIST_CMD_GAP(1) | START_BIST); - i = t4_wait_op_done(adap, edc_bist_cmd, START_BIST, 0, 10, 1); + BIST_OPCODE_V(1) | BIST_CMD_GAP_V(1) | START_BIST_F); + i = t4_wait_op_done(adap, edc_bist_cmd, START_BIST_F, 0, 10, 1); if (i) return i; @@ -505,13 +506,13 @@ int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr, * the address is relative to BAR0. */ mem_reg = t4_read_reg(adap, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, win)); - mem_aperture = 1 << (GET_WINDOW(mem_reg) + 10); - mem_base = GET_PCIEOFST(mem_reg) << 10; + mem_aperture = 1 << (WINDOW_G(mem_reg) + WINDOW_SHIFT_X); + mem_base = PCIEOFST_G(mem_reg) << PCIEOFST_SHIFT_X; if (is_t4(adap->params.chip)) mem_base -= adap->t4_bar0; - win_pf = is_t4(adap->params.chip) ? 0 : V_PFNUM(adap->fn); + win_pf = is_t4(adap->params.chip) ? 0 : PFNUM_V(adap->fn); /* Calculate our initial PCI-E Memory Window Position and Offset into * that Window. @@ -524,10 +525,10 @@ int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr, * attempt to use the new value.) */ t4_write_reg(adap, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, win), + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, win), pos | win_pf); t4_read_reg(adap, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, win)); + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, win)); /* Transfer data to/from the adapter as long as there's an integral * number of 32-bit transfers to complete. @@ -552,11 +553,11 @@ int t4_memory_rw(struct adapter *adap, int win, int mtype, u32 addr, pos += mem_aperture; offset = 0; t4_write_reg(adap, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, - win), pos | win_pf); + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, + win), pos | win_pf); t4_read_reg(adap, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, - win)); + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, + win)); } } @@ -760,14 +761,13 @@ static int sf1_read(struct adapter *adapter, unsigned int byte_cnt, int cont, if (!byte_cnt || byte_cnt > 4) return -EINVAL; - if (t4_read_reg(adapter, SF_OP) & SF_BUSY) + if (t4_read_reg(adapter, SF_OP_A) & SF_BUSY_F) return -EBUSY; - cont = cont ? SF_CONT : 0; - lock = lock ? SF_LOCK : 0; - t4_write_reg(adapter, SF_OP, lock | cont | BYTECNT(byte_cnt - 1)); - ret = t4_wait_op_done(adapter, SF_OP, SF_BUSY, 0, SF_ATTEMPTS, 5); + t4_write_reg(adapter, SF_OP_A, SF_LOCK_V(lock) | + SF_CONT_V(cont) | BYTECNT_V(byte_cnt - 1)); + ret = t4_wait_op_done(adapter, SF_OP_A, SF_BUSY_F, 0, SF_ATTEMPTS, 5); if (!ret) - *valp = t4_read_reg(adapter, SF_DATA); + *valp = t4_read_reg(adapter, SF_DATA_A); return ret; } @@ -788,14 +788,12 @@ static int sf1_write(struct adapter *adapter, unsigned int byte_cnt, int cont, { if (!byte_cnt || byte_cnt > 4) return -EINVAL; - if (t4_read_reg(adapter, SF_OP) & SF_BUSY) + if (t4_read_reg(adapter, SF_OP_A) & SF_BUSY_F) return -EBUSY; - cont = cont ? SF_CONT : 0; - lock = lock ? SF_LOCK : 0; - t4_write_reg(adapter, SF_DATA, val); - t4_write_reg(adapter, SF_OP, lock | - cont | BYTECNT(byte_cnt - 1) | OP_WR); - return t4_wait_op_done(adapter, SF_OP, SF_BUSY, 0, SF_ATTEMPTS, 5); + t4_write_reg(adapter, SF_DATA_A, val); + t4_write_reg(adapter, SF_OP_A, SF_LOCK_V(lock) | + SF_CONT_V(cont) | BYTECNT_V(byte_cnt - 1) | OP_V(1)); + return t4_wait_op_done(adapter, SF_OP_A, SF_BUSY_F, 0, SF_ATTEMPTS, 5); } /** @@ -837,8 +835,8 @@ static int flash_wait_op(struct adapter *adapter, int attempts, int delay) * (i.e., big-endian), otherwise as 32-bit words in the platform's * natural endianess. */ -static int t4_read_flash(struct adapter *adapter, unsigned int addr, - unsigned int nwords, u32 *data, int byte_oriented) +int t4_read_flash(struct adapter *adapter, unsigned int addr, + unsigned int nwords, u32 *data, int byte_oriented) { int ret; @@ -854,7 +852,7 @@ static int t4_read_flash(struct adapter *adapter, unsigned int addr, for ( ; nwords; nwords--, data++) { ret = sf1_read(adapter, 4, nwords > 1, nwords == 1, data); if (nwords == 1) - t4_write_reg(adapter, SF_OP, 0); /* unlock SF */ + t4_write_reg(adapter, SF_OP_A, 0); /* unlock SF */ if (ret) return ret; if (byte_oriented) @@ -902,7 +900,7 @@ static int t4_write_flash(struct adapter *adapter, unsigned int addr, if (ret) goto unlock; - t4_write_reg(adapter, SF_OP, 0); /* unlock SF */ + t4_write_reg(adapter, SF_OP_A, 0); /* unlock SF */ /* Read the page to verify the write succeeded */ ret = t4_read_flash(adapter, addr & ~0xff, ARRAY_SIZE(buf), buf, 1); @@ -918,7 +916,7 @@ static int t4_write_flash(struct adapter *adapter, unsigned int addr, return 0; unlock: - t4_write_reg(adapter, SF_OP, 0); /* unlock SF */ + t4_write_reg(adapter, SF_OP_A, 0); /* unlock SF */ return ret; } @@ -950,6 +948,43 @@ int t4_get_tp_version(struct adapter *adapter, u32 *vers) 1, vers, 0); } +/** + * t4_get_exprom_version - return the Expansion ROM version (if any) + * @adapter: the adapter + * @vers: where to place the version + * + * Reads the Expansion ROM header from FLASH and returns the version + * number (if present) through the @vers return value pointer. We return + * this in the Firmware Version Format since it's convenient. Return + * 0 on success, -ENOENT if no Expansion ROM is present. + */ +int t4_get_exprom_version(struct adapter *adap, u32 *vers) +{ + struct exprom_header { + unsigned char hdr_arr[16]; /* must start with 0x55aa */ + unsigned char hdr_ver[4]; /* Expansion ROM version */ + } *hdr; + u32 exprom_header_buf[DIV_ROUND_UP(sizeof(struct exprom_header), + sizeof(u32))]; + int ret; + + ret = t4_read_flash(adap, FLASH_EXP_ROM_START, + ARRAY_SIZE(exprom_header_buf), exprom_header_buf, + 0); + if (ret) + return ret; + + hdr = (struct exprom_header *)exprom_header_buf; + if (hdr->hdr_arr[0] != 0x55 || hdr->hdr_arr[1] != 0xaa) + return -ENOENT; + + *vers = (FW_HDR_FW_VER_MAJOR_V(hdr->hdr_ver[0]) | + FW_HDR_FW_VER_MINOR_V(hdr->hdr_ver[1]) | + FW_HDR_FW_VER_MICRO_V(hdr->hdr_ver[2]) | + FW_HDR_FW_VER_BUILD_V(hdr->hdr_ver[3])); + return 0; +} + /* Is the given firmware API compatible with the one the driver was compiled * with? */ @@ -1113,7 +1148,7 @@ static int t4_flash_erase_sectors(struct adapter *adapter, int start, int end) } start++; } - t4_write_reg(adapter, SF_OP, 0); /* unlock SF */ + t4_write_reg(adapter, SF_OP_A, 0); /* unlock SF */ return ret; } @@ -1241,6 +1276,45 @@ out: return ret; } +/** + * t4_fwcache - firmware cache operation + * @adap: the adapter + * @op : the operation (flush or flush and invalidate) + */ +int t4_fwcache(struct adapter *adap, enum fw_params_param_dev_fwcache op) +{ + struct fw_params_cmd c; + + memset(&c, 0, sizeof(c)); + c.op_to_vfn = + cpu_to_be32(FW_CMD_OP_V(FW_PARAMS_CMD) | + FW_CMD_REQUEST_F | FW_CMD_WRITE_F | + FW_PARAMS_CMD_PFN_V(adap->fn) | + FW_PARAMS_CMD_VFN_V(0)); + c.retval_len16 = cpu_to_be32(FW_LEN16(c)); + c.param[0].mnem = + cpu_to_be32(FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) | + FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_FWCACHE)); + c.param[0].val = (__force __be32)op; + + return t4_wr_mbox(adap, adap->mbox, &c, sizeof(c), NULL); +} + +void t4_ulprx_read_la(struct adapter *adap, u32 *la_buf) +{ + unsigned int i, j; + + for (i = 0; i < 8; i++) { + u32 *p = la_buf + i; + + t4_write_reg(adap, ULP_RX_LA_CTL_A, i); + j = t4_read_reg(adap, ULP_RX_LA_WRPTR_A); + t4_write_reg(adap, ULP_RX_LA_RDPTR_A, j); + for (j = 0; j < ULPRX_LA_SIZE; j++, p += 8) + *p = t4_read_reg(adap, ULP_RX_LA_RDDATA_A); + } +} + #define ADVERT_MASK (FW_PORT_CAP_SPEED_100M | FW_PORT_CAP_SPEED_1G |\ FW_PORT_CAP_SPEED_10G | FW_PORT_CAP_SPEED_40G | \ FW_PORT_CAP_ANEG) @@ -1365,95 +1439,97 @@ static int t4_handle_intr_status(struct adapter *adapter, unsigned int reg, static void pcie_intr_handler(struct adapter *adapter) { static const struct intr_info sysbus_intr_info[] = { - { RNPP, "RXNP array parity error", -1, 1 }, - { RPCP, "RXPC array parity error", -1, 1 }, - { RCIP, "RXCIF array parity error", -1, 1 }, - { RCCP, "Rx completions control array parity error", -1, 1 }, - { RFTP, "RXFT array parity error", -1, 1 }, + { RNPP_F, "RXNP array parity error", -1, 1 }, + { RPCP_F, "RXPC array parity error", -1, 1 }, + { RCIP_F, "RXCIF array parity error", -1, 1 }, + { RCCP_F, "Rx completions control array parity error", -1, 1 }, + { RFTP_F, "RXFT array parity error", -1, 1 }, { 0 } }; static const struct intr_info pcie_port_intr_info[] = { - { TPCP, "TXPC array parity error", -1, 1 }, - { TNPP, "TXNP array parity error", -1, 1 }, - { TFTP, "TXFT array parity error", -1, 1 }, - { TCAP, "TXCA array parity error", -1, 1 }, - { TCIP, "TXCIF array parity error", -1, 1 }, - { RCAP, "RXCA array parity error", -1, 1 }, - { OTDD, "outbound request TLP discarded", -1, 1 }, - { RDPE, "Rx data parity error", -1, 1 }, - { TDUE, "Tx uncorrectable data error", -1, 1 }, + { TPCP_F, "TXPC array parity error", -1, 1 }, + { TNPP_F, "TXNP array parity error", -1, 1 }, + { TFTP_F, "TXFT array parity error", -1, 1 }, + { TCAP_F, "TXCA array parity error", -1, 1 }, + { TCIP_F, "TXCIF array parity error", -1, 1 }, + { RCAP_F, "RXCA array parity error", -1, 1 }, + { OTDD_F, "outbound request TLP discarded", -1, 1 }, + { RDPE_F, "Rx data parity error", -1, 1 }, + { TDUE_F, "Tx uncorrectable data error", -1, 1 }, { 0 } }; static const struct intr_info pcie_intr_info[] = { - { MSIADDRLPERR, "MSI AddrL parity error", -1, 1 }, - { MSIADDRHPERR, "MSI AddrH parity error", -1, 1 }, - { MSIDATAPERR, "MSI data parity error", -1, 1 }, - { MSIXADDRLPERR, "MSI-X AddrL parity error", -1, 1 }, - { MSIXADDRHPERR, "MSI-X AddrH parity error", -1, 1 }, - { MSIXDATAPERR, "MSI-X data parity error", -1, 1 }, - { MSIXDIPERR, "MSI-X DI parity error", -1, 1 }, - { PIOCPLPERR, "PCI PIO completion FIFO parity error", -1, 1 }, - { PIOREQPERR, "PCI PIO request FIFO parity error", -1, 1 }, - { TARTAGPERR, "PCI PCI target tag FIFO parity error", -1, 1 }, - { CCNTPERR, "PCI CMD channel count parity error", -1, 1 }, - { CREQPERR, "PCI CMD channel request parity error", -1, 1 }, - { CRSPPERR, "PCI CMD channel response parity error", -1, 1 }, - { DCNTPERR, "PCI DMA channel count parity error", -1, 1 }, - { DREQPERR, "PCI DMA channel request parity error", -1, 1 }, - { DRSPPERR, "PCI DMA channel response parity error", -1, 1 }, - { HCNTPERR, "PCI HMA channel count parity error", -1, 1 }, - { HREQPERR, "PCI HMA channel request parity error", -1, 1 }, - { HRSPPERR, "PCI HMA channel response parity error", -1, 1 }, - { CFGSNPPERR, "PCI config snoop FIFO parity error", -1, 1 }, - { FIDPERR, "PCI FID parity error", -1, 1 }, - { INTXCLRPERR, "PCI INTx clear parity error", -1, 1 }, - { MATAGPERR, "PCI MA tag parity error", -1, 1 }, - { PIOTAGPERR, "PCI PIO tag parity error", -1, 1 }, - { RXCPLPERR, "PCI Rx completion parity error", -1, 1 }, - { RXWRPERR, "PCI Rx write parity error", -1, 1 }, - { RPLPERR, "PCI replay buffer parity error", -1, 1 }, - { PCIESINT, "PCI core secondary fault", -1, 1 }, - { PCIEPINT, "PCI core primary fault", -1, 1 }, - { UNXSPLCPLERR, "PCI unexpected split completion error", -1, 0 }, + { MSIADDRLPERR_F, "MSI AddrL parity error", -1, 1 }, + { MSIADDRHPERR_F, "MSI AddrH parity error", -1, 1 }, + { MSIDATAPERR_F, "MSI data parity error", -1, 1 }, + { MSIXADDRLPERR_F, "MSI-X AddrL parity error", -1, 1 }, + { MSIXADDRHPERR_F, "MSI-X AddrH parity error", -1, 1 }, + { MSIXDATAPERR_F, "MSI-X data parity error", -1, 1 }, + { MSIXDIPERR_F, "MSI-X DI parity error", -1, 1 }, + { PIOCPLPERR_F, "PCI PIO completion FIFO parity error", -1, 1 }, + { PIOREQPERR_F, "PCI PIO request FIFO parity error", -1, 1 }, + { TARTAGPERR_F, "PCI PCI target tag FIFO parity error", -1, 1 }, + { CCNTPERR_F, "PCI CMD channel count parity error", -1, 1 }, + { CREQPERR_F, "PCI CMD channel request parity error", -1, 1 }, + { CRSPPERR_F, "PCI CMD channel response parity error", -1, 1 }, + { DCNTPERR_F, "PCI DMA channel count parity error", -1, 1 }, + { DREQPERR_F, "PCI DMA channel request parity error", -1, 1 }, + { DRSPPERR_F, "PCI DMA channel response parity error", -1, 1 }, + { HCNTPERR_F, "PCI HMA channel count parity error", -1, 1 }, + { HREQPERR_F, "PCI HMA channel request parity error", -1, 1 }, + { HRSPPERR_F, "PCI HMA channel response parity error", -1, 1 }, + { CFGSNPPERR_F, "PCI config snoop FIFO parity error", -1, 1 }, + { FIDPERR_F, "PCI FID parity error", -1, 1 }, + { INTXCLRPERR_F, "PCI INTx clear parity error", -1, 1 }, + { MATAGPERR_F, "PCI MA tag parity error", -1, 1 }, + { PIOTAGPERR_F, "PCI PIO tag parity error", -1, 1 }, + { RXCPLPERR_F, "PCI Rx completion parity error", -1, 1 }, + { RXWRPERR_F, "PCI Rx write parity error", -1, 1 }, + { RPLPERR_F, "PCI replay buffer parity error", -1, 1 }, + { PCIESINT_F, "PCI core secondary fault", -1, 1 }, + { PCIEPINT_F, "PCI core primary fault", -1, 1 }, + { UNXSPLCPLERR_F, "PCI unexpected split completion error", + -1, 0 }, { 0 } }; static struct intr_info t5_pcie_intr_info[] = { - { MSTGRPPERR, "Master Response Read Queue parity error", + { MSTGRPPERR_F, "Master Response Read Queue parity error", -1, 1 }, - { MSTTIMEOUTPERR, "Master Timeout FIFO parity error", -1, 1 }, - { MSIXSTIPERR, "MSI-X STI SRAM parity error", -1, 1 }, - { MSIXADDRLPERR, "MSI-X AddrL parity error", -1, 1 }, - { MSIXADDRHPERR, "MSI-X AddrH parity error", -1, 1 }, - { MSIXDATAPERR, "MSI-X data parity error", -1, 1 }, - { MSIXDIPERR, "MSI-X DI parity error", -1, 1 }, - { PIOCPLGRPPERR, "PCI PIO completion Group FIFO parity error", + { MSTTIMEOUTPERR_F, "Master Timeout FIFO parity error", -1, 1 }, + { MSIXSTIPERR_F, "MSI-X STI SRAM parity error", -1, 1 }, + { MSIXADDRLPERR_F, "MSI-X AddrL parity error", -1, 1 }, + { MSIXADDRHPERR_F, "MSI-X AddrH parity error", -1, 1 }, + { MSIXDATAPERR_F, "MSI-X data parity error", -1, 1 }, + { MSIXDIPERR_F, "MSI-X DI parity error", -1, 1 }, + { PIOCPLGRPPERR_F, "PCI PIO completion Group FIFO parity error", -1, 1 }, - { PIOREQGRPPERR, "PCI PIO request Group FIFO parity error", + { PIOREQGRPPERR_F, "PCI PIO request Group FIFO parity error", -1, 1 }, - { TARTAGPERR, "PCI PCI target tag FIFO parity error", -1, 1 }, - { MSTTAGQPERR, "PCI master tag queue parity error", -1, 1 }, - { CREQPERR, "PCI CMD channel request parity error", -1, 1 }, - { CRSPPERR, "PCI CMD channel response parity error", -1, 1 }, - { DREQWRPERR, "PCI DMA channel write request parity error", + { TARTAGPERR_F, "PCI PCI target tag FIFO parity error", -1, 1 }, + { MSTTAGQPERR_F, "PCI master tag queue parity error", -1, 1 }, + { CREQPERR_F, "PCI CMD channel request parity error", -1, 1 }, + { CRSPPERR_F, "PCI CMD channel response parity error", -1, 1 }, + { DREQWRPERR_F, "PCI DMA channel write request parity error", -1, 1 }, - { DREQPERR, "PCI DMA channel request parity error", -1, 1 }, - { DRSPPERR, "PCI DMA channel response parity error", -1, 1 }, - { HREQWRPERR, "PCI HMA channel count parity error", -1, 1 }, - { HREQPERR, "PCI HMA channel request parity error", -1, 1 }, - { HRSPPERR, "PCI HMA channel response parity error", -1, 1 }, - { CFGSNPPERR, "PCI config snoop FIFO parity error", -1, 1 }, - { FIDPERR, "PCI FID parity error", -1, 1 }, - { VFIDPERR, "PCI INTx clear parity error", -1, 1 }, - { MAGRPPERR, "PCI MA group FIFO parity error", -1, 1 }, - { PIOTAGPERR, "PCI PIO tag parity error", -1, 1 }, - { IPRXHDRGRPPERR, "PCI IP Rx header group parity error", + { DREQPERR_F, "PCI DMA channel request parity error", -1, 1 }, + { DRSPPERR_F, "PCI DMA channel response parity error", -1, 1 }, + { HREQWRPERR_F, "PCI HMA channel count parity error", -1, 1 }, + { HREQPERR_F, "PCI HMA channel request parity error", -1, 1 }, + { HRSPPERR_F, "PCI HMA channel response parity error", -1, 1 }, + { CFGSNPPERR_F, "PCI config snoop FIFO parity error", -1, 1 }, + { FIDPERR_F, "PCI FID parity error", -1, 1 }, + { VFIDPERR_F, "PCI INTx clear parity error", -1, 1 }, + { MAGRPPERR_F, "PCI MA group FIFO parity error", -1, 1 }, + { PIOTAGPERR_F, "PCI PIO tag parity error", -1, 1 }, + { IPRXHDRGRPPERR_F, "PCI IP Rx header group parity error", -1, 1 }, - { IPRXDATAGRPPERR, "PCI IP Rx data group parity error", -1, 1 }, - { RPLPERR, "PCI IP replay buffer parity error", -1, 1 }, - { IPSOTPERR, "PCI IP SOT buffer parity error", -1, 1 }, - { TRGT1GRPPERR, "PCI TRGT1 group FIFOs parity error", -1, 1 }, - { READRSPERR, "Outbound read error", -1, 0 }, + { IPRXDATAGRPPERR_F, "PCI IP Rx data group parity error", + -1, 1 }, + { RPLPERR_F, "PCI IP replay buffer parity error", -1, 1 }, + { IPSOTPERR_F, "PCI IP SOT buffer parity error", -1, 1 }, + { TRGT1GRPPERR_F, "PCI TRGT1 group FIFOs parity error", -1, 1 }, + { READRSPERR_F, "Outbound read error", -1, 0 }, { 0 } }; @@ -1461,15 +1537,15 @@ static void pcie_intr_handler(struct adapter *adapter) if (is_t4(adapter->params.chip)) fat = t4_handle_intr_status(adapter, - PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS, - sysbus_intr_info) + + PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS_A, + sysbus_intr_info) + t4_handle_intr_status(adapter, - PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS, - pcie_port_intr_info) + - t4_handle_intr_status(adapter, PCIE_INT_CAUSE, + PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS_A, + pcie_port_intr_info) + + t4_handle_intr_status(adapter, PCIE_INT_CAUSE_A, pcie_intr_info); else - fat = t4_handle_intr_status(adapter, PCIE_INT_CAUSE, + fat = t4_handle_intr_status(adapter, PCIE_INT_CAUSE_A, t5_pcie_intr_info); if (fat) @@ -1483,11 +1559,11 @@ static void tp_intr_handler(struct adapter *adapter) { static const struct intr_info tp_intr_info[] = { { 0x3fffffff, "TP parity error", -1, 1 }, - { FLMTXFLSTEMPTY, "TP out of Tx pages", -1, 1 }, + { FLMTXFLSTEMPTY_F, "TP out of Tx pages", -1, 1 }, { 0 } }; - if (t4_handle_intr_status(adapter, TP_INT_CAUSE, tp_intr_info)) + if (t4_handle_intr_status(adapter, TP_INT_CAUSE_A, tp_intr_info)) t4_fatal_err(adapter); } @@ -1499,102 +1575,107 @@ static void sge_intr_handler(struct adapter *adapter) u64 v; static const struct intr_info sge_intr_info[] = { - { ERR_CPL_EXCEED_IQE_SIZE, + { ERR_CPL_EXCEED_IQE_SIZE_F, "SGE received CPL exceeding IQE size", -1, 1 }, - { ERR_INVALID_CIDX_INC, + { ERR_INVALID_CIDX_INC_F, "SGE GTS CIDX increment too large", -1, 0 }, - { ERR_CPL_OPCODE_0, "SGE received 0-length CPL", -1, 0 }, - { DBFIFO_LP_INT, NULL, -1, 0, t4_db_full }, - { DBFIFO_HP_INT, NULL, -1, 0, t4_db_full }, - { ERR_DROPPED_DB, NULL, -1, 0, t4_db_dropped }, - { ERR_DATA_CPL_ON_HIGH_QID1 | ERR_DATA_CPL_ON_HIGH_QID0, + { ERR_CPL_OPCODE_0_F, "SGE received 0-length CPL", -1, 0 }, + { DBFIFO_LP_INT_F, NULL, -1, 0, t4_db_full }, + { DBFIFO_HP_INT_F, NULL, -1, 0, t4_db_full }, + { ERR_DROPPED_DB_F, NULL, -1, 0, t4_db_dropped }, + { ERR_DATA_CPL_ON_HIGH_QID1_F | ERR_DATA_CPL_ON_HIGH_QID0_F, "SGE IQID > 1023 received CPL for FL", -1, 0 }, - { ERR_BAD_DB_PIDX3, "SGE DBP 3 pidx increment too large", -1, + { ERR_BAD_DB_PIDX3_F, "SGE DBP 3 pidx increment too large", -1, 0 }, - { ERR_BAD_DB_PIDX2, "SGE DBP 2 pidx increment too large", -1, + { ERR_BAD_DB_PIDX2_F, "SGE DBP 2 pidx increment too large", -1, 0 }, - { ERR_BAD_DB_PIDX1, "SGE DBP 1 pidx increment too large", -1, + { ERR_BAD_DB_PIDX1_F, "SGE DBP 1 pidx increment too large", -1, 0 }, - { ERR_BAD_DB_PIDX0, "SGE DBP 0 pidx increment too large", -1, + { ERR_BAD_DB_PIDX0_F, "SGE DBP 0 pidx increment too large", -1, 0 }, - { ERR_ING_CTXT_PRIO, + { ERR_ING_CTXT_PRIO_F, "SGE too many priority ingress contexts", -1, 0 }, - { ERR_EGR_CTXT_PRIO, + { ERR_EGR_CTXT_PRIO_F, "SGE too many priority egress contexts", -1, 0 }, - { INGRESS_SIZE_ERR, "SGE illegal ingress QID", -1, 0 }, - { EGRESS_SIZE_ERR, "SGE illegal egress QID", -1, 0 }, + { INGRESS_SIZE_ERR_F, "SGE illegal ingress QID", -1, 0 }, + { EGRESS_SIZE_ERR_F, "SGE illegal egress QID", -1, 0 }, { 0 } }; - v = (u64)t4_read_reg(adapter, SGE_INT_CAUSE1) | - ((u64)t4_read_reg(adapter, SGE_INT_CAUSE2) << 32); + v = (u64)t4_read_reg(adapter, SGE_INT_CAUSE1_A) | + ((u64)t4_read_reg(adapter, SGE_INT_CAUSE2_A) << 32); if (v) { dev_alert(adapter->pdev_dev, "SGE parity error (%#llx)\n", (unsigned long long)v); - t4_write_reg(adapter, SGE_INT_CAUSE1, v); - t4_write_reg(adapter, SGE_INT_CAUSE2, v >> 32); + t4_write_reg(adapter, SGE_INT_CAUSE1_A, v); + t4_write_reg(adapter, SGE_INT_CAUSE2_A, v >> 32); } - if (t4_handle_intr_status(adapter, SGE_INT_CAUSE3, sge_intr_info) || + if (t4_handle_intr_status(adapter, SGE_INT_CAUSE3_A, sge_intr_info) || v != 0) t4_fatal_err(adapter); } +#define CIM_OBQ_INTR (OBQULP0PARERR_F | OBQULP1PARERR_F | OBQULP2PARERR_F |\ + OBQULP3PARERR_F | OBQSGEPARERR_F | OBQNCSIPARERR_F) +#define CIM_IBQ_INTR (IBQTP0PARERR_F | IBQTP1PARERR_F | IBQULPPARERR_F |\ + IBQSGEHIPARERR_F | IBQSGELOPARERR_F | IBQNCSIPARERR_F) + /* * CIM interrupt handler. */ static void cim_intr_handler(struct adapter *adapter) { static const struct intr_info cim_intr_info[] = { - { PREFDROPINT, "CIM control register prefetch drop", -1, 1 }, - { OBQPARERR, "CIM OBQ parity error", -1, 1 }, - { IBQPARERR, "CIM IBQ parity error", -1, 1 }, - { MBUPPARERR, "CIM mailbox uP parity error", -1, 1 }, - { MBHOSTPARERR, "CIM mailbox host parity error", -1, 1 }, - { TIEQINPARERRINT, "CIM TIEQ outgoing parity error", -1, 1 }, - { TIEQOUTPARERRINT, "CIM TIEQ incoming parity error", -1, 1 }, + { PREFDROPINT_F, "CIM control register prefetch drop", -1, 1 }, + { CIM_OBQ_INTR, "CIM OBQ parity error", -1, 1 }, + { CIM_IBQ_INTR, "CIM IBQ parity error", -1, 1 }, + { MBUPPARERR_F, "CIM mailbox uP parity error", -1, 1 }, + { MBHOSTPARERR_F, "CIM mailbox host parity error", -1, 1 }, + { TIEQINPARERRINT_F, "CIM TIEQ outgoing parity error", -1, 1 }, + { TIEQOUTPARERRINT_F, "CIM TIEQ incoming parity error", -1, 1 }, { 0 } }; static const struct intr_info cim_upintr_info[] = { - { RSVDSPACEINT, "CIM reserved space access", -1, 1 }, - { ILLTRANSINT, "CIM illegal transaction", -1, 1 }, - { ILLWRINT, "CIM illegal write", -1, 1 }, - { ILLRDINT, "CIM illegal read", -1, 1 }, - { ILLRDBEINT, "CIM illegal read BE", -1, 1 }, - { ILLWRBEINT, "CIM illegal write BE", -1, 1 }, - { SGLRDBOOTINT, "CIM single read from boot space", -1, 1 }, - { SGLWRBOOTINT, "CIM single write to boot space", -1, 1 }, - { BLKWRBOOTINT, "CIM block write to boot space", -1, 1 }, - { SGLRDFLASHINT, "CIM single read from flash space", -1, 1 }, - { SGLWRFLASHINT, "CIM single write to flash space", -1, 1 }, - { BLKWRFLASHINT, "CIM block write to flash space", -1, 1 }, - { SGLRDEEPROMINT, "CIM single EEPROM read", -1, 1 }, - { SGLWREEPROMINT, "CIM single EEPROM write", -1, 1 }, - { BLKRDEEPROMINT, "CIM block EEPROM read", -1, 1 }, - { BLKWREEPROMINT, "CIM block EEPROM write", -1, 1 }, - { SGLRDCTLINT , "CIM single read from CTL space", -1, 1 }, - { SGLWRCTLINT , "CIM single write to CTL space", -1, 1 }, - { BLKRDCTLINT , "CIM block read from CTL space", -1, 1 }, - { BLKWRCTLINT , "CIM block write to CTL space", -1, 1 }, - { SGLRDPLINT , "CIM single read from PL space", -1, 1 }, - { SGLWRPLINT , "CIM single write to PL space", -1, 1 }, - { BLKRDPLINT , "CIM block read from PL space", -1, 1 }, - { BLKWRPLINT , "CIM block write to PL space", -1, 1 }, - { REQOVRLOOKUPINT , "CIM request FIFO overwrite", -1, 1 }, - { RSPOVRLOOKUPINT , "CIM response FIFO overwrite", -1, 1 }, - { TIMEOUTINT , "CIM PIF timeout", -1, 1 }, - { TIMEOUTMAINT , "CIM PIF MA timeout", -1, 1 }, + { RSVDSPACEINT_F, "CIM reserved space access", -1, 1 }, + { ILLTRANSINT_F, "CIM illegal transaction", -1, 1 }, + { ILLWRINT_F, "CIM illegal write", -1, 1 }, + { ILLRDINT_F, "CIM illegal read", -1, 1 }, + { ILLRDBEINT_F, "CIM illegal read BE", -1, 1 }, + { ILLWRBEINT_F, "CIM illegal write BE", -1, 1 }, + { SGLRDBOOTINT_F, "CIM single read from boot space", -1, 1 }, + { SGLWRBOOTINT_F, "CIM single write to boot space", -1, 1 }, + { BLKWRBOOTINT_F, "CIM block write to boot space", -1, 1 }, + { SGLRDFLASHINT_F, "CIM single read from flash space", -1, 1 }, + { SGLWRFLASHINT_F, "CIM single write to flash space", -1, 1 }, + { BLKWRFLASHINT_F, "CIM block write to flash space", -1, 1 }, + { SGLRDEEPROMINT_F, "CIM single EEPROM read", -1, 1 }, + { SGLWREEPROMINT_F, "CIM single EEPROM write", -1, 1 }, + { BLKRDEEPROMINT_F, "CIM block EEPROM read", -1, 1 }, + { BLKWREEPROMINT_F, "CIM block EEPROM write", -1, 1 }, + { SGLRDCTLINT_F, "CIM single read from CTL space", -1, 1 }, + { SGLWRCTLINT_F, "CIM single write to CTL space", -1, 1 }, + { BLKRDCTLINT_F, "CIM block read from CTL space", -1, 1 }, + { BLKWRCTLINT_F, "CIM block write to CTL space", -1, 1 }, + { SGLRDPLINT_F, "CIM single read from PL space", -1, 1 }, + { SGLWRPLINT_F, "CIM single write to PL space", -1, 1 }, + { BLKRDPLINT_F, "CIM block read from PL space", -1, 1 }, + { BLKWRPLINT_F, "CIM block write to PL space", -1, 1 }, + { REQOVRLOOKUPINT_F, "CIM request FIFO overwrite", -1, 1 }, + { RSPOVRLOOKUPINT_F, "CIM response FIFO overwrite", -1, 1 }, + { TIMEOUTINT_F, "CIM PIF timeout", -1, 1 }, + { TIMEOUTMAINT_F, "CIM PIF MA timeout", -1, 1 }, { 0 } }; int fat; - if (t4_read_reg(adapter, MA_PCIE_FW) & PCIE_FW_ERR) + if (t4_read_reg(adapter, PCIE_FW_A) & PCIE_FW_ERR_F) t4_report_fw_error(adapter); - fat = t4_handle_intr_status(adapter, CIM_HOST_INT_CAUSE, + fat = t4_handle_intr_status(adapter, CIM_HOST_INT_CAUSE_A, cim_intr_info) + - t4_handle_intr_status(adapter, CIM_HOST_UPACC_INT_CAUSE, + t4_handle_intr_status(adapter, CIM_HOST_UPACC_INT_CAUSE_A, cim_upintr_info); if (fat) t4_fatal_err(adapter); @@ -1611,7 +1692,7 @@ static void ulprx_intr_handler(struct adapter *adapter) { 0 } }; - if (t4_handle_intr_status(adapter, ULP_RX_INT_CAUSE, ulprx_intr_info)) + if (t4_handle_intr_status(adapter, ULP_RX_INT_CAUSE_A, ulprx_intr_info)) t4_fatal_err(adapter); } @@ -1621,19 +1702,19 @@ static void ulprx_intr_handler(struct adapter *adapter) static void ulptx_intr_handler(struct adapter *adapter) { static const struct intr_info ulptx_intr_info[] = { - { PBL_BOUND_ERR_CH3, "ULPTX channel 3 PBL out of bounds", -1, + { PBL_BOUND_ERR_CH3_F, "ULPTX channel 3 PBL out of bounds", -1, 0 }, - { PBL_BOUND_ERR_CH2, "ULPTX channel 2 PBL out of bounds", -1, + { PBL_BOUND_ERR_CH2_F, "ULPTX channel 2 PBL out of bounds", -1, 0 }, - { PBL_BOUND_ERR_CH1, "ULPTX channel 1 PBL out of bounds", -1, + { PBL_BOUND_ERR_CH1_F, "ULPTX channel 1 PBL out of bounds", -1, 0 }, - { PBL_BOUND_ERR_CH0, "ULPTX channel 0 PBL out of bounds", -1, + { PBL_BOUND_ERR_CH0_F, "ULPTX channel 0 PBL out of bounds", -1, 0 }, { 0xfffffff, "ULPTX parity error", -1, 1 }, { 0 } }; - if (t4_handle_intr_status(adapter, ULP_TX_INT_CAUSE, ulptx_intr_info)) + if (t4_handle_intr_status(adapter, ULP_TX_INT_CAUSE_A, ulptx_intr_info)) t4_fatal_err(adapter); } @@ -1643,19 +1724,20 @@ static void ulptx_intr_handler(struct adapter *adapter) static void pmtx_intr_handler(struct adapter *adapter) { static const struct intr_info pmtx_intr_info[] = { - { PCMD_LEN_OVFL0, "PMTX channel 0 pcmd too large", -1, 1 }, - { PCMD_LEN_OVFL1, "PMTX channel 1 pcmd too large", -1, 1 }, - { PCMD_LEN_OVFL2, "PMTX channel 2 pcmd too large", -1, 1 }, - { ZERO_C_CMD_ERROR, "PMTX 0-length pcmd", -1, 1 }, - { PMTX_FRAMING_ERROR, "PMTX framing error", -1, 1 }, - { OESPI_PAR_ERROR, "PMTX oespi parity error", -1, 1 }, - { DB_OPTIONS_PAR_ERROR, "PMTX db_options parity error", -1, 1 }, - { ICSPI_PAR_ERROR, "PMTX icspi parity error", -1, 1 }, - { C_PCMD_PAR_ERROR, "PMTX c_pcmd parity error", -1, 1}, + { PCMD_LEN_OVFL0_F, "PMTX channel 0 pcmd too large", -1, 1 }, + { PCMD_LEN_OVFL1_F, "PMTX channel 1 pcmd too large", -1, 1 }, + { PCMD_LEN_OVFL2_F, "PMTX channel 2 pcmd too large", -1, 1 }, + { ZERO_C_CMD_ERROR_F, "PMTX 0-length pcmd", -1, 1 }, + { PMTX_FRAMING_ERROR_F, "PMTX framing error", -1, 1 }, + { OESPI_PAR_ERROR_F, "PMTX oespi parity error", -1, 1 }, + { DB_OPTIONS_PAR_ERROR_F, "PMTX db_options parity error", + -1, 1 }, + { ICSPI_PAR_ERROR_F, "PMTX icspi parity error", -1, 1 }, + { PMTX_C_PCMD_PAR_ERROR_F, "PMTX c_pcmd parity error", -1, 1}, { 0 } }; - if (t4_handle_intr_status(adapter, PM_TX_INT_CAUSE, pmtx_intr_info)) + if (t4_handle_intr_status(adapter, PM_TX_INT_CAUSE_A, pmtx_intr_info)) t4_fatal_err(adapter); } @@ -1665,16 +1747,17 @@ static void pmtx_intr_handler(struct adapter *adapter) static void pmrx_intr_handler(struct adapter *adapter) { static const struct intr_info pmrx_intr_info[] = { - { ZERO_E_CMD_ERROR, "PMRX 0-length pcmd", -1, 1 }, - { PMRX_FRAMING_ERROR, "PMRX framing error", -1, 1 }, - { OCSPI_PAR_ERROR, "PMRX ocspi parity error", -1, 1 }, - { DB_OPTIONS_PAR_ERROR, "PMRX db_options parity error", -1, 1 }, - { IESPI_PAR_ERROR, "PMRX iespi parity error", -1, 1 }, - { E_PCMD_PAR_ERROR, "PMRX e_pcmd parity error", -1, 1}, + { ZERO_E_CMD_ERROR_F, "PMRX 0-length pcmd", -1, 1 }, + { PMRX_FRAMING_ERROR_F, "PMRX framing error", -1, 1 }, + { OCSPI_PAR_ERROR_F, "PMRX ocspi parity error", -1, 1 }, + { DB_OPTIONS_PAR_ERROR_F, "PMRX db_options parity error", + -1, 1 }, + { IESPI_PAR_ERROR_F, "PMRX iespi parity error", -1, 1 }, + { PMRX_E_PCMD_PAR_ERROR_F, "PMRX e_pcmd parity error", -1, 1}, { 0 } }; - if (t4_handle_intr_status(adapter, PM_RX_INT_CAUSE, pmrx_intr_info)) + if (t4_handle_intr_status(adapter, PM_RX_INT_CAUSE_A, pmrx_intr_info)) t4_fatal_err(adapter); } @@ -1684,16 +1767,16 @@ static void pmrx_intr_handler(struct adapter *adapter) static void cplsw_intr_handler(struct adapter *adapter) { static const struct intr_info cplsw_intr_info[] = { - { CIM_OP_MAP_PERR, "CPLSW CIM op_map parity error", -1, 1 }, - { CIM_OVFL_ERROR, "CPLSW CIM overflow", -1, 1 }, - { TP_FRAMING_ERROR, "CPLSW TP framing error", -1, 1 }, - { SGE_FRAMING_ERROR, "CPLSW SGE framing error", -1, 1 }, - { CIM_FRAMING_ERROR, "CPLSW CIM framing error", -1, 1 }, - { ZERO_SWITCH_ERROR, "CPLSW no-switch error", -1, 1 }, + { CIM_OP_MAP_PERR_F, "CPLSW CIM op_map parity error", -1, 1 }, + { CIM_OVFL_ERROR_F, "CPLSW CIM overflow", -1, 1 }, + { TP_FRAMING_ERROR_F, "CPLSW TP framing error", -1, 1 }, + { SGE_FRAMING_ERROR_F, "CPLSW SGE framing error", -1, 1 }, + { CIM_FRAMING_ERROR_F, "CPLSW CIM framing error", -1, 1 }, + { ZERO_SWITCH_ERROR_F, "CPLSW no-switch error", -1, 1 }, { 0 } }; - if (t4_handle_intr_status(adapter, CPL_INTR_CAUSE, cplsw_intr_info)) + if (t4_handle_intr_status(adapter, CPL_INTR_CAUSE_A, cplsw_intr_info)) t4_fatal_err(adapter); } @@ -1703,15 +1786,15 @@ static void cplsw_intr_handler(struct adapter *adapter) static void le_intr_handler(struct adapter *adap) { static const struct intr_info le_intr_info[] = { - { LIPMISS, "LE LIP miss", -1, 0 }, - { LIP0, "LE 0 LIP error", -1, 0 }, - { PARITYERR, "LE parity error", -1, 1 }, - { UNKNOWNCMD, "LE unknown command", -1, 1 }, - { REQQPARERR, "LE request queue parity error", -1, 1 }, + { LIPMISS_F, "LE LIP miss", -1, 0 }, + { LIP0_F, "LE 0 LIP error", -1, 0 }, + { PARITYERR_F, "LE parity error", -1, 1 }, + { UNKNOWNCMD_F, "LE unknown command", -1, 1 }, + { REQQPARERR_F, "LE request queue parity error", -1, 1 }, { 0 } }; - if (t4_handle_intr_status(adap, LE_DB_INT_CAUSE, le_intr_info)) + if (t4_handle_intr_status(adap, LE_DB_INT_CAUSE_A, le_intr_info)) t4_fatal_err(adap); } @@ -1725,19 +1808,22 @@ static void mps_intr_handler(struct adapter *adapter) { 0 } }; static const struct intr_info mps_tx_intr_info[] = { - { TPFIFO, "MPS Tx TP FIFO parity error", -1, 1 }, - { NCSIFIFO, "MPS Tx NC-SI FIFO parity error", -1, 1 }, - { TXDATAFIFO, "MPS Tx data FIFO parity error", -1, 1 }, - { TXDESCFIFO, "MPS Tx desc FIFO parity error", -1, 1 }, - { BUBBLE, "MPS Tx underflow", -1, 1 }, - { SECNTERR, "MPS Tx SOP/EOP error", -1, 1 }, - { FRMERR, "MPS Tx framing error", -1, 1 }, + { TPFIFO_V(TPFIFO_M), "MPS Tx TP FIFO parity error", -1, 1 }, + { NCSIFIFO_F, "MPS Tx NC-SI FIFO parity error", -1, 1 }, + { TXDATAFIFO_V(TXDATAFIFO_M), "MPS Tx data FIFO parity error", + -1, 1 }, + { TXDESCFIFO_V(TXDESCFIFO_M), "MPS Tx desc FIFO parity error", + -1, 1 }, + { BUBBLE_F, "MPS Tx underflow", -1, 1 }, + { SECNTERR_F, "MPS Tx SOP/EOP error", -1, 1 }, + { FRMERR_F, "MPS Tx framing error", -1, 1 }, { 0 } }; static const struct intr_info mps_trc_intr_info[] = { - { FILTMEM, "MPS TRC filter parity error", -1, 1 }, - { PKTFIFO, "MPS TRC packet FIFO parity error", -1, 1 }, - { MISCPERR, "MPS TRC misc parity error", -1, 1 }, + { FILTMEM_V(FILTMEM_M), "MPS TRC filter parity error", -1, 1 }, + { PKTFIFO_V(PKTFIFO_M), "MPS TRC packet FIFO parity error", + -1, 1 }, + { MISCPERR_F, "MPS TRC misc parity error", -1, 1 }, { 0 } }; static const struct intr_info mps_stat_sram_intr_info[] = { @@ -1753,37 +1839,37 @@ static void mps_intr_handler(struct adapter *adapter) { 0 } }; static const struct intr_info mps_cls_intr_info[] = { - { MATCHSRAM, "MPS match SRAM parity error", -1, 1 }, - { MATCHTCAM, "MPS match TCAM parity error", -1, 1 }, - { HASHSRAM, "MPS hash SRAM parity error", -1, 1 }, + { MATCHSRAM_F, "MPS match SRAM parity error", -1, 1 }, + { MATCHTCAM_F, "MPS match TCAM parity error", -1, 1 }, + { HASHSRAM_F, "MPS hash SRAM parity error", -1, 1 }, { 0 } }; int fat; - fat = t4_handle_intr_status(adapter, MPS_RX_PERR_INT_CAUSE, + fat = t4_handle_intr_status(adapter, MPS_RX_PERR_INT_CAUSE_A, mps_rx_intr_info) + - t4_handle_intr_status(adapter, MPS_TX_INT_CAUSE, + t4_handle_intr_status(adapter, MPS_TX_INT_CAUSE_A, mps_tx_intr_info) + - t4_handle_intr_status(adapter, MPS_TRC_INT_CAUSE, + t4_handle_intr_status(adapter, MPS_TRC_INT_CAUSE_A, mps_trc_intr_info) + - t4_handle_intr_status(adapter, MPS_STAT_PERR_INT_CAUSE_SRAM, + t4_handle_intr_status(adapter, MPS_STAT_PERR_INT_CAUSE_SRAM_A, mps_stat_sram_intr_info) + - t4_handle_intr_status(adapter, MPS_STAT_PERR_INT_CAUSE_TX_FIFO, + t4_handle_intr_status(adapter, MPS_STAT_PERR_INT_CAUSE_TX_FIFO_A, mps_stat_tx_intr_info) + - t4_handle_intr_status(adapter, MPS_STAT_PERR_INT_CAUSE_RX_FIFO, + t4_handle_intr_status(adapter, MPS_STAT_PERR_INT_CAUSE_RX_FIFO_A, mps_stat_rx_intr_info) + - t4_handle_intr_status(adapter, MPS_CLS_INT_CAUSE, + t4_handle_intr_status(adapter, MPS_CLS_INT_CAUSE_A, mps_cls_intr_info); - t4_write_reg(adapter, MPS_INT_CAUSE, CLSINT | TRCINT | - RXINT | TXINT | STATINT); - t4_read_reg(adapter, MPS_INT_CAUSE); /* flush */ + t4_write_reg(adapter, MPS_INT_CAUSE_A, 0); + t4_read_reg(adapter, MPS_INT_CAUSE_A); /* flush */ if (fat) t4_fatal_err(adapter); } -#define MEM_INT_MASK (PERR_INT_CAUSE | ECC_CE_INT_CAUSE | ECC_UE_INT_CAUSE) +#define MEM_INT_MASK (PERR_INT_CAUSE_F | ECC_CE_INT_CAUSE_F | \ + ECC_UE_INT_CAUSE_F) /* * EDC/MC interrupt handler. @@ -1795,40 +1881,40 @@ static void mem_intr_handler(struct adapter *adapter, int idx) unsigned int addr, cnt_addr, v; if (idx <= MEM_EDC1) { - addr = EDC_REG(EDC_INT_CAUSE, idx); - cnt_addr = EDC_REG(EDC_ECC_STATUS, idx); + addr = EDC_REG(EDC_INT_CAUSE_A, idx); + cnt_addr = EDC_REG(EDC_ECC_STATUS_A, idx); } else if (idx == MEM_MC) { if (is_t4(adapter->params.chip)) { - addr = MC_INT_CAUSE; - cnt_addr = MC_ECC_STATUS; + addr = MC_INT_CAUSE_A; + cnt_addr = MC_ECC_STATUS_A; } else { - addr = MC_P_INT_CAUSE; - cnt_addr = MC_P_ECC_STATUS; + addr = MC_P_INT_CAUSE_A; + cnt_addr = MC_P_ECC_STATUS_A; } } else { - addr = MC_REG(MC_P_INT_CAUSE, 1); - cnt_addr = MC_REG(MC_P_ECC_STATUS, 1); + addr = MC_REG(MC_P_INT_CAUSE_A, 1); + cnt_addr = MC_REG(MC_P_ECC_STATUS_A, 1); } v = t4_read_reg(adapter, addr) & MEM_INT_MASK; - if (v & PERR_INT_CAUSE) + if (v & PERR_INT_CAUSE_F) dev_alert(adapter->pdev_dev, "%s FIFO parity error\n", name[idx]); - if (v & ECC_CE_INT_CAUSE) { - u32 cnt = ECC_CECNT_GET(t4_read_reg(adapter, cnt_addr)); + if (v & ECC_CE_INT_CAUSE_F) { + u32 cnt = ECC_CECNT_G(t4_read_reg(adapter, cnt_addr)); - t4_write_reg(adapter, cnt_addr, ECC_CECNT_MASK); + t4_write_reg(adapter, cnt_addr, ECC_CECNT_V(ECC_CECNT_M)); if (printk_ratelimit()) dev_warn(adapter->pdev_dev, "%u %s correctable ECC data error%s\n", cnt, name[idx], cnt > 1 ? "s" : ""); } - if (v & ECC_UE_INT_CAUSE) + if (v & ECC_UE_INT_CAUSE_F) dev_alert(adapter->pdev_dev, "%s uncorrectable ECC data error\n", name[idx]); t4_write_reg(adapter, addr, v); - if (v & (PERR_INT_CAUSE | ECC_UE_INT_CAUSE)) + if (v & (PERR_INT_CAUSE_F | ECC_UE_INT_CAUSE_F)) t4_fatal_err(adapter); } @@ -1837,26 +1923,26 @@ static void mem_intr_handler(struct adapter *adapter, int idx) */ static void ma_intr_handler(struct adapter *adap) { - u32 v, status = t4_read_reg(adap, MA_INT_CAUSE); + u32 v, status = t4_read_reg(adap, MA_INT_CAUSE_A); - if (status & MEM_PERR_INT_CAUSE) { + if (status & MEM_PERR_INT_CAUSE_F) { dev_alert(adap->pdev_dev, "MA parity error, parity status %#x\n", - t4_read_reg(adap, MA_PARITY_ERROR_STATUS)); + t4_read_reg(adap, MA_PARITY_ERROR_STATUS1_A)); if (is_t5(adap->params.chip)) dev_alert(adap->pdev_dev, "MA parity error, parity status %#x\n", t4_read_reg(adap, - MA_PARITY_ERROR_STATUS2)); + MA_PARITY_ERROR_STATUS2_A)); } - if (status & MEM_WRAP_INT_CAUSE) { - v = t4_read_reg(adap, MA_INT_WRAP_STATUS); + if (status & MEM_WRAP_INT_CAUSE_F) { + v = t4_read_reg(adap, MA_INT_WRAP_STATUS_A); dev_alert(adap->pdev_dev, "MA address wrap-around error by " "client %u to address %#x\n", - MEM_WRAP_CLIENT_NUM_GET(v), - MEM_WRAP_ADDRESS_GET(v) << 4); + MEM_WRAP_CLIENT_NUM_G(v), + MEM_WRAP_ADDRESS_G(v) << 4); } - t4_write_reg(adap, MA_INT_CAUSE, status); + t4_write_reg(adap, MA_INT_CAUSE_A, status); t4_fatal_err(adap); } @@ -1866,13 +1952,13 @@ static void ma_intr_handler(struct adapter *adap) static void smb_intr_handler(struct adapter *adap) { static const struct intr_info smb_intr_info[] = { - { MSTTXFIFOPARINT, "SMB master Tx FIFO parity error", -1, 1 }, - { MSTRXFIFOPARINT, "SMB master Rx FIFO parity error", -1, 1 }, - { SLVFIFOPARINT, "SMB slave FIFO parity error", -1, 1 }, + { MSTTXFIFOPARINT_F, "SMB master Tx FIFO parity error", -1, 1 }, + { MSTRXFIFOPARINT_F, "SMB master Rx FIFO parity error", -1, 1 }, + { SLVFIFOPARINT_F, "SMB slave FIFO parity error", -1, 1 }, { 0 } }; - if (t4_handle_intr_status(adap, SMB_INT_CAUSE, smb_intr_info)) + if (t4_handle_intr_status(adap, SMB_INT_CAUSE_A, smb_intr_info)) t4_fatal_err(adap); } @@ -1882,14 +1968,14 @@ static void smb_intr_handler(struct adapter *adap) static void ncsi_intr_handler(struct adapter *adap) { static const struct intr_info ncsi_intr_info[] = { - { CIM_DM_PRTY_ERR, "NC-SI CIM parity error", -1, 1 }, - { MPS_DM_PRTY_ERR, "NC-SI MPS parity error", -1, 1 }, - { TXFIFO_PRTY_ERR, "NC-SI Tx FIFO parity error", -1, 1 }, - { RXFIFO_PRTY_ERR, "NC-SI Rx FIFO parity error", -1, 1 }, + { CIM_DM_PRTY_ERR_F, "NC-SI CIM parity error", -1, 1 }, + { MPS_DM_PRTY_ERR_F, "NC-SI MPS parity error", -1, 1 }, + { TXFIFO_PRTY_ERR_F, "NC-SI Tx FIFO parity error", -1, 1 }, + { RXFIFO_PRTY_ERR_F, "NC-SI Rx FIFO parity error", -1, 1 }, { 0 } }; - if (t4_handle_intr_status(adap, NCSI_INT_CAUSE, ncsi_intr_info)) + if (t4_handle_intr_status(adap, NCSI_INT_CAUSE_A, ncsi_intr_info)) t4_fatal_err(adap); } @@ -1901,23 +1987,23 @@ static void xgmac_intr_handler(struct adapter *adap, int port) u32 v, int_cause_reg; if (is_t4(adap->params.chip)) - int_cause_reg = PORT_REG(port, XGMAC_PORT_INT_CAUSE); + int_cause_reg = PORT_REG(port, XGMAC_PORT_INT_CAUSE_A); else - int_cause_reg = T5_PORT_REG(port, MAC_PORT_INT_CAUSE); + int_cause_reg = T5_PORT_REG(port, MAC_PORT_INT_CAUSE_A); v = t4_read_reg(adap, int_cause_reg); - v &= TXFIFO_PRTY_ERR | RXFIFO_PRTY_ERR; + v &= TXFIFO_PRTY_ERR_F | RXFIFO_PRTY_ERR_F; if (!v) return; - if (v & TXFIFO_PRTY_ERR) + if (v & TXFIFO_PRTY_ERR_F) dev_alert(adap->pdev_dev, "XGMAC %d Tx FIFO parity error\n", port); - if (v & RXFIFO_PRTY_ERR) + if (v & RXFIFO_PRTY_ERR_F) dev_alert(adap->pdev_dev, "XGMAC %d Rx FIFO parity error\n", port); - t4_write_reg(adap, PORT_REG(port, XGMAC_PORT_INT_CAUSE), v); + t4_write_reg(adap, PORT_REG(port, XGMAC_PORT_INT_CAUSE_A), v); t4_fatal_err(adap); } @@ -1927,19 +2013,19 @@ static void xgmac_intr_handler(struct adapter *adap, int port) static void pl_intr_handler(struct adapter *adap) { static const struct intr_info pl_intr_info[] = { - { FATALPERR, "T4 fatal parity error", -1, 1 }, - { PERRVFID, "PL VFID_MAP parity error", -1, 1 }, + { FATALPERR_F, "T4 fatal parity error", -1, 1 }, + { PERRVFID_F, "PL VFID_MAP parity error", -1, 1 }, { 0 } }; - if (t4_handle_intr_status(adap, PL_PL_INT_CAUSE, pl_intr_info)) + if (t4_handle_intr_status(adap, PL_PL_INT_CAUSE_A, pl_intr_info)) t4_fatal_err(adap); } -#define PF_INTR_MASK (PFSW) -#define GLBL_INTR_MASK (CIM | MPS | PL | PCIE | MC | EDC0 | \ - EDC1 | LE | TP | MA | PM_TX | PM_RX | ULP_RX | \ - CPL_SWITCH | SGE | ULP_TX) +#define PF_INTR_MASK (PFSW_F) +#define GLBL_INTR_MASK (CIM_F | MPS_F | PL_F | PCIE_F | MC_F | EDC0_F | \ + EDC1_F | LE_F | TP_F | MA_F | PM_TX_F | PM_RX_F | ULP_RX_F | \ + CPL_SWITCH_F | SGE_F | ULP_TX_F) /** * t4_slow_intr_handler - control path interrupt handler @@ -1951,60 +2037,60 @@ static void pl_intr_handler(struct adapter *adap) */ int t4_slow_intr_handler(struct adapter *adapter) { - u32 cause = t4_read_reg(adapter, PL_INT_CAUSE); + u32 cause = t4_read_reg(adapter, PL_INT_CAUSE_A); if (!(cause & GLBL_INTR_MASK)) return 0; - if (cause & CIM) + if (cause & CIM_F) cim_intr_handler(adapter); - if (cause & MPS) + if (cause & MPS_F) mps_intr_handler(adapter); - if (cause & NCSI) + if (cause & NCSI_F) ncsi_intr_handler(adapter); - if (cause & PL) + if (cause & PL_F) pl_intr_handler(adapter); - if (cause & SMB) + if (cause & SMB_F) smb_intr_handler(adapter); - if (cause & XGMAC0) + if (cause & XGMAC0_F) xgmac_intr_handler(adapter, 0); - if (cause & XGMAC1) + if (cause & XGMAC1_F) xgmac_intr_handler(adapter, 1); - if (cause & XGMAC_KR0) + if (cause & XGMAC_KR0_F) xgmac_intr_handler(adapter, 2); - if (cause & XGMAC_KR1) + if (cause & XGMAC_KR1_F) xgmac_intr_handler(adapter, 3); - if (cause & PCIE) + if (cause & PCIE_F) pcie_intr_handler(adapter); - if (cause & MC) + if (cause & MC_F) mem_intr_handler(adapter, MEM_MC); - if (!is_t4(adapter->params.chip) && (cause & MC1)) + if (!is_t4(adapter->params.chip) && (cause & MC1_S)) mem_intr_handler(adapter, MEM_MC1); - if (cause & EDC0) + if (cause & EDC0_F) mem_intr_handler(adapter, MEM_EDC0); - if (cause & EDC1) + if (cause & EDC1_F) mem_intr_handler(adapter, MEM_EDC1); - if (cause & LE) + if (cause & LE_F) le_intr_handler(adapter); - if (cause & TP) + if (cause & TP_F) tp_intr_handler(adapter); - if (cause & MA) + if (cause & MA_F) ma_intr_handler(adapter); - if (cause & PM_TX) + if (cause & PM_TX_F) pmtx_intr_handler(adapter); - if (cause & PM_RX) + if (cause & PM_RX_F) pmrx_intr_handler(adapter); - if (cause & ULP_RX) + if (cause & ULP_RX_F) ulprx_intr_handler(adapter); - if (cause & CPL_SWITCH) + if (cause & CPL_SWITCH_F) cplsw_intr_handler(adapter); - if (cause & SGE) + if (cause & SGE_F) sge_intr_handler(adapter); - if (cause & ULP_TX) + if (cause & ULP_TX_F) ulptx_intr_handler(adapter); /* Clear the interrupts just processed for which we are the master. */ - t4_write_reg(adapter, PL_INT_CAUSE, cause & GLBL_INTR_MASK); - (void) t4_read_reg(adapter, PL_INT_CAUSE); /* flush */ + t4_write_reg(adapter, PL_INT_CAUSE_A, cause & GLBL_INTR_MASK); + (void)t4_read_reg(adapter, PL_INT_CAUSE_A); /* flush */ return 1; } @@ -2023,19 +2109,19 @@ int t4_slow_intr_handler(struct adapter *adapter) */ void t4_intr_enable(struct adapter *adapter) { - u32 pf = SOURCEPF_GET(t4_read_reg(adapter, PL_WHOAMI)); - - t4_write_reg(adapter, SGE_INT_ENABLE3, ERR_CPL_EXCEED_IQE_SIZE | - ERR_INVALID_CIDX_INC | ERR_CPL_OPCODE_0 | - ERR_DROPPED_DB | ERR_DATA_CPL_ON_HIGH_QID1 | - ERR_DATA_CPL_ON_HIGH_QID0 | ERR_BAD_DB_PIDX3 | - ERR_BAD_DB_PIDX2 | ERR_BAD_DB_PIDX1 | - ERR_BAD_DB_PIDX0 | ERR_ING_CTXT_PRIO | - ERR_EGR_CTXT_PRIO | INGRESS_SIZE_ERR | - DBFIFO_HP_INT | DBFIFO_LP_INT | - EGRESS_SIZE_ERR); - t4_write_reg(adapter, MYPF_REG(PL_PF_INT_ENABLE), PF_INTR_MASK); - t4_set_reg_field(adapter, PL_INT_MAP0, 0, 1 << pf); + u32 pf = SOURCEPF_G(t4_read_reg(adapter, PL_WHOAMI_A)); + + t4_write_reg(adapter, SGE_INT_ENABLE3_A, ERR_CPL_EXCEED_IQE_SIZE_F | + ERR_INVALID_CIDX_INC_F | ERR_CPL_OPCODE_0_F | + ERR_DROPPED_DB_F | ERR_DATA_CPL_ON_HIGH_QID1_F | + ERR_DATA_CPL_ON_HIGH_QID0_F | ERR_BAD_DB_PIDX3_F | + ERR_BAD_DB_PIDX2_F | ERR_BAD_DB_PIDX1_F | + ERR_BAD_DB_PIDX0_F | ERR_ING_CTXT_PRIO_F | + ERR_EGR_CTXT_PRIO_F | INGRESS_SIZE_ERR_F | + DBFIFO_HP_INT_F | DBFIFO_LP_INT_F | + EGRESS_SIZE_ERR_F); + t4_write_reg(adapter, MYPF_REG(PL_PF_INT_ENABLE_A), PF_INTR_MASK); + t4_set_reg_field(adapter, PL_INT_MAP0_A, 0, 1 << pf); } /** @@ -2048,10 +2134,10 @@ void t4_intr_enable(struct adapter *adapter) */ void t4_intr_disable(struct adapter *adapter) { - u32 pf = SOURCEPF_GET(t4_read_reg(adapter, PL_WHOAMI)); + u32 pf = SOURCEPF_G(t4_read_reg(adapter, PL_WHOAMI_A)); - t4_write_reg(adapter, MYPF_REG(PL_PF_INT_ENABLE), 0); - t4_set_reg_field(adapter, PL_INT_MAP0, 1 << pf, 0); + t4_write_reg(adapter, MYPF_REG(PL_PF_INT_ENABLE_A), 0); + t4_set_reg_field(adapter, PL_INT_MAP0_A, 1 << pf, 0); } /** @@ -2166,6 +2252,147 @@ int t4_config_glbl_rss(struct adapter *adapter, int mbox, unsigned int mode, return t4_wr_mbox(adapter, mbox, &c, sizeof(c), NULL); } +/* Read an RSS table row */ +static int rd_rss_row(struct adapter *adap, int row, u32 *val) +{ + t4_write_reg(adap, TP_RSS_LKP_TABLE_A, 0xfff00000 | row); + return t4_wait_op_done_val(adap, TP_RSS_LKP_TABLE_A, LKPTBLROWVLD_F, 1, + 5, 0, val); +} + +/** + * t4_read_rss - read the contents of the RSS mapping table + * @adapter: the adapter + * @map: holds the contents of the RSS mapping table + * + * Reads the contents of the RSS hash->queue mapping table. + */ +int t4_read_rss(struct adapter *adapter, u16 *map) +{ + u32 val; + int i, ret; + + for (i = 0; i < RSS_NENTRIES / 2; ++i) { + ret = rd_rss_row(adapter, i, &val); + if (ret) + return ret; + *map++ = LKPTBLQUEUE0_G(val); + *map++ = LKPTBLQUEUE1_G(val); + } + return 0; +} + +/** + * t4_read_rss_key - read the global RSS key + * @adap: the adapter + * @key: 10-entry array holding the 320-bit RSS key + * + * Reads the global 320-bit RSS key. + */ +void t4_read_rss_key(struct adapter *adap, u32 *key) +{ + t4_read_indirect(adap, TP_PIO_ADDR_A, TP_PIO_DATA_A, key, 10, + TP_RSS_SECRET_KEY0_A); +} + +/** + * t4_write_rss_key - program one of the RSS keys + * @adap: the adapter + * @key: 10-entry array holding the 320-bit RSS key + * @idx: which RSS key to write + * + * Writes one of the RSS keys with the given 320-bit value. If @idx is + * 0..15 the corresponding entry in the RSS key table is written, + * otherwise the global RSS key is written. + */ +void t4_write_rss_key(struct adapter *adap, const u32 *key, int idx) +{ + t4_write_indirect(adap, TP_PIO_ADDR_A, TP_PIO_DATA_A, key, 10, + TP_RSS_SECRET_KEY0_A); + if (idx >= 0 && idx < 16) + t4_write_reg(adap, TP_RSS_CONFIG_VRT_A, + KEYWRADDR_V(idx) | KEYWREN_F); +} + +/** + * t4_read_rss_pf_config - read PF RSS Configuration Table + * @adapter: the adapter + * @index: the entry in the PF RSS table to read + * @valp: where to store the returned value + * + * Reads the PF RSS Configuration Table at the specified index and returns + * the value found there. + */ +void t4_read_rss_pf_config(struct adapter *adapter, unsigned int index, + u32 *valp) +{ + t4_read_indirect(adapter, TP_PIO_ADDR_A, TP_PIO_DATA_A, + valp, 1, TP_RSS_PF0_CONFIG_A + index); +} + +/** + * t4_read_rss_vf_config - read VF RSS Configuration Table + * @adapter: the adapter + * @index: the entry in the VF RSS table to read + * @vfl: where to store the returned VFL + * @vfh: where to store the returned VFH + * + * Reads the VF RSS Configuration Table at the specified index and returns + * the (VFL, VFH) values found there. + */ +void t4_read_rss_vf_config(struct adapter *adapter, unsigned int index, + u32 *vfl, u32 *vfh) +{ + u32 vrt, mask, data; + + mask = VFWRADDR_V(VFWRADDR_M); + data = VFWRADDR_V(index); + + /* Request that the index'th VF Table values be read into VFL/VFH. + */ + vrt = t4_read_reg(adapter, TP_RSS_CONFIG_VRT_A); + vrt &= ~(VFRDRG_F | VFWREN_F | KEYWREN_F | mask); + vrt |= data | VFRDEN_F; + t4_write_reg(adapter, TP_RSS_CONFIG_VRT_A, vrt); + + /* Grab the VFL/VFH values ... + */ + t4_read_indirect(adapter, TP_PIO_ADDR_A, TP_PIO_DATA_A, + vfl, 1, TP_RSS_VFL_CONFIG_A); + t4_read_indirect(adapter, TP_PIO_ADDR_A, TP_PIO_DATA_A, + vfh, 1, TP_RSS_VFH_CONFIG_A); +} + +/** + * t4_read_rss_pf_map - read PF RSS Map + * @adapter: the adapter + * + * Reads the PF RSS Map register and returns its value. + */ +u32 t4_read_rss_pf_map(struct adapter *adapter) +{ + u32 pfmap; + + t4_read_indirect(adapter, TP_PIO_ADDR_A, TP_PIO_DATA_A, + &pfmap, 1, TP_RSS_PF_MAP_A); + return pfmap; +} + +/** + * t4_read_rss_pf_mask - read PF RSS Mask + * @adapter: the adapter + * + * Reads the PF RSS Mask register and returns its value. + */ +u32 t4_read_rss_pf_mask(struct adapter *adapter) +{ + u32 pfmask; + + t4_read_indirect(adapter, TP_PIO_ADDR_A, TP_PIO_DATA_A, + &pfmask, 1, TP_RSS_PF_MSK_A); + return pfmask; +} + /** * t4_tp_get_tcp_stats - read TP's TCP MIB counters * @adap: the adapter @@ -2178,23 +2405,23 @@ int t4_config_glbl_rss(struct adapter *adapter, int mbox, unsigned int mode, void t4_tp_get_tcp_stats(struct adapter *adap, struct tp_tcp_stats *v4, struct tp_tcp_stats *v6) { - u32 val[TP_MIB_TCP_RXT_SEG_LO - TP_MIB_TCP_OUT_RST + 1]; + u32 val[TP_MIB_TCP_RXT_SEG_LO_A - TP_MIB_TCP_OUT_RST_A + 1]; -#define STAT_IDX(x) ((TP_MIB_TCP_##x) - TP_MIB_TCP_OUT_RST) +#define STAT_IDX(x) ((TP_MIB_TCP_##x##_A) - TP_MIB_TCP_OUT_RST_A) #define STAT(x) val[STAT_IDX(x)] #define STAT64(x) (((u64)STAT(x##_HI) << 32) | STAT(x##_LO)) if (v4) { - t4_read_indirect(adap, TP_MIB_INDEX, TP_MIB_DATA, val, - ARRAY_SIZE(val), TP_MIB_TCP_OUT_RST); + t4_read_indirect(adap, TP_MIB_INDEX_A, TP_MIB_DATA_A, val, + ARRAY_SIZE(val), TP_MIB_TCP_OUT_RST_A); v4->tcpOutRsts = STAT(OUT_RST); v4->tcpInSegs = STAT64(IN_SEG); v4->tcpOutSegs = STAT64(OUT_SEG); v4->tcpRetransSegs = STAT64(RXT_SEG); } if (v6) { - t4_read_indirect(adap, TP_MIB_INDEX, TP_MIB_DATA, val, - ARRAY_SIZE(val), TP_MIB_TCP_V6OUT_RST); + t4_read_indirect(adap, TP_MIB_INDEX_A, TP_MIB_DATA_A, val, + ARRAY_SIZE(val), TP_MIB_TCP_V6OUT_RST_A); v6->tcpOutRsts = STAT(OUT_RST); v6->tcpInSegs = STAT64(IN_SEG); v6->tcpOutSegs = STAT64(OUT_SEG); @@ -2219,16 +2446,37 @@ void t4_read_mtu_tbl(struct adapter *adap, u16 *mtus, u8 *mtu_log) int i; for (i = 0; i < NMTUS; ++i) { - t4_write_reg(adap, TP_MTU_TABLE, - MTUINDEX(0xff) | MTUVALUE(i)); - v = t4_read_reg(adap, TP_MTU_TABLE); - mtus[i] = MTUVALUE_GET(v); + t4_write_reg(adap, TP_MTU_TABLE_A, + MTUINDEX_V(0xff) | MTUVALUE_V(i)); + v = t4_read_reg(adap, TP_MTU_TABLE_A); + mtus[i] = MTUVALUE_G(v); if (mtu_log) - mtu_log[i] = MTUWIDTH_GET(v); + mtu_log[i] = MTUWIDTH_G(v); } } /** + * t4_read_cong_tbl - reads the congestion control table + * @adap: the adapter + * @incr: where to store the alpha values + * + * Reads the additive increments programmed into the HW congestion + * control table. + */ +void t4_read_cong_tbl(struct adapter *adap, u16 incr[NMTUS][NCCTRL_WIN]) +{ + unsigned int mtu, w; + + for (mtu = 0; mtu < NMTUS; ++mtu) + for (w = 0; w < NCCTRL_WIN; ++w) { + t4_write_reg(adap, TP_CCTRL_TABLE_A, + ROWINDEX_V(0xffff) | (mtu << 5) | w); + incr[mtu][w] = (u16)t4_read_reg(adap, + TP_CCTRL_TABLE_A) & 0x1fff; + } +} + +/** * t4_tp_wr_bits_indirect - set/clear bits in an indirect TP register * @adap: the adapter * @addr: the indirect TP register address @@ -2240,9 +2488,9 @@ void t4_read_mtu_tbl(struct adapter *adap, u16 *mtus, u8 *mtu_log) void t4_tp_wr_bits_indirect(struct adapter *adap, unsigned int addr, unsigned int mask, unsigned int val) { - t4_write_reg(adap, TP_PIO_ADDR, addr); - val |= t4_read_reg(adap, TP_PIO_DATA) & ~mask; - t4_write_reg(adap, TP_PIO_DATA, val); + t4_write_reg(adap, TP_PIO_ADDR_A, addr); + val |= t4_read_reg(adap, TP_PIO_DATA_A) & ~mask; + t4_write_reg(adap, TP_PIO_DATA_A, val); } /** @@ -2321,8 +2569,8 @@ void t4_load_mtus(struct adapter *adap, const unsigned short *mtus, if (!(mtu & ((1 << log2) >> 2))) /* round */ log2--; - t4_write_reg(adap, TP_MTU_TABLE, MTUINDEX(i) | - MTUWIDTH(log2) | MTUVALUE(mtu)); + t4_write_reg(adap, TP_MTU_TABLE_A, MTUINDEX_V(i) | + MTUWIDTH_V(log2) | MTUVALUE_V(mtu)); for (w = 0; w < NCCTRL_WIN; ++w) { unsigned int inc; @@ -2330,13 +2578,67 @@ void t4_load_mtus(struct adapter *adap, const unsigned short *mtus, inc = max(((mtu - 40) * alpha[w]) / avg_pkts[w], CC_MIN_INCR); - t4_write_reg(adap, TP_CCTRL_TABLE, (i << 21) | + t4_write_reg(adap, TP_CCTRL_TABLE_A, (i << 21) | (w << 16) | (beta[w] << 13) | inc); } } } /** + * t4_pmtx_get_stats - returns the HW stats from PMTX + * @adap: the adapter + * @cnt: where to store the count statistics + * @cycles: where to store the cycle statistics + * + * Returns performance statistics from PMTX. + */ +void t4_pmtx_get_stats(struct adapter *adap, u32 cnt[], u64 cycles[]) +{ + int i; + u32 data[2]; + + for (i = 0; i < PM_NSTATS; i++) { + t4_write_reg(adap, PM_TX_STAT_CONFIG_A, i + 1); + cnt[i] = t4_read_reg(adap, PM_TX_STAT_COUNT_A); + if (is_t4(adap->params.chip)) { + cycles[i] = t4_read_reg64(adap, PM_TX_STAT_LSB_A); + } else { + t4_read_indirect(adap, PM_TX_DBG_CTRL_A, + PM_TX_DBG_DATA_A, data, 2, + PM_TX_DBG_STAT_MSB_A); + cycles[i] = (((u64)data[0] << 32) | data[1]); + } + } +} + +/** + * t4_pmrx_get_stats - returns the HW stats from PMRX + * @adap: the adapter + * @cnt: where to store the count statistics + * @cycles: where to store the cycle statistics + * + * Returns performance statistics from PMRX. + */ +void t4_pmrx_get_stats(struct adapter *adap, u32 cnt[], u64 cycles[]) +{ + int i; + u32 data[2]; + + for (i = 0; i < PM_NSTATS; i++) { + t4_write_reg(adap, PM_RX_STAT_CONFIG_A, i + 1); + cnt[i] = t4_read_reg(adap, PM_RX_STAT_COUNT_A); + if (is_t4(adap->params.chip)) { + cycles[i] = t4_read_reg64(adap, PM_RX_STAT_LSB_A); + } else { + t4_read_indirect(adap, PM_RX_DBG_CTRL_A, + PM_RX_DBG_DATA_A, data, 2, + PM_RX_DBG_STAT_MSB_A); + cycles[i] = (((u64)data[0] << 32) | data[1]); + } + } +} + +/** * get_mps_bg_map - return the buffer groups associated with a port * @adap: the adapter * @idx: the port index @@ -2347,7 +2649,7 @@ void t4_load_mtus(struct adapter *adap, const unsigned short *mtus, */ static unsigned int get_mps_bg_map(struct adapter *adap, int idx) { - u32 n = NUMPORTS_GET(t4_read_reg(adap, MPS_CMN_CTL)); + u32 n = NUMPORTS_G(t4_read_reg(adap, MPS_CMN_CTL_A)); if (n == 0) return idx == 0 ? 0xf : 0; @@ -2485,11 +2787,11 @@ void t4_wol_magic_enable(struct adapter *adap, unsigned int port, if (is_t4(adap->params.chip)) { mag_id_reg_l = PORT_REG(port, XGMAC_PORT_MAGIC_MACID_LO); mag_id_reg_h = PORT_REG(port, XGMAC_PORT_MAGIC_MACID_HI); - port_cfg_reg = PORT_REG(port, XGMAC_PORT_CFG2); + port_cfg_reg = PORT_REG(port, XGMAC_PORT_CFG2_A); } else { mag_id_reg_l = T5_PORT_REG(port, MAC_PORT_MAGIC_MACID_LO); mag_id_reg_h = T5_PORT_REG(port, MAC_PORT_MAGIC_MACID_HI); - port_cfg_reg = T5_PORT_REG(port, MAC_PORT_CFG2); + port_cfg_reg = T5_PORT_REG(port, MAC_PORT_CFG2_A); } if (addr) { @@ -2499,8 +2801,8 @@ void t4_wol_magic_enable(struct adapter *adap, unsigned int port, t4_write_reg(adap, mag_id_reg_h, (addr[0] << 8) | addr[1]); } - t4_set_reg_field(adap, port_cfg_reg, MAGICEN, - addr ? MAGICEN : 0); + t4_set_reg_field(adap, port_cfg_reg, MAGICEN_F, + addr ? MAGICEN_F : 0); } /** @@ -2525,20 +2827,21 @@ int t4_wol_pat_enable(struct adapter *adap, unsigned int port, unsigned int map, u32 port_cfg_reg; if (is_t4(adap->params.chip)) - port_cfg_reg = PORT_REG(port, XGMAC_PORT_CFG2); + port_cfg_reg = PORT_REG(port, XGMAC_PORT_CFG2_A); else - port_cfg_reg = T5_PORT_REG(port, MAC_PORT_CFG2); + port_cfg_reg = T5_PORT_REG(port, MAC_PORT_CFG2_A); if (!enable) { - t4_set_reg_field(adap, port_cfg_reg, PATEN, 0); + t4_set_reg_field(adap, port_cfg_reg, PATEN_F, 0); return 0; } if (map > 0xff) return -EINVAL; #define EPIO_REG(name) \ - (is_t4(adap->params.chip) ? PORT_REG(port, XGMAC_PORT_EPIO_##name) : \ - T5_PORT_REG(port, MAC_PORT_EPIO_##name)) + (is_t4(adap->params.chip) ? \ + PORT_REG(port, XGMAC_PORT_EPIO_##name##_A) : \ + T5_PORT_REG(port, MAC_PORT_EPIO_##name##_A)) t4_write_reg(adap, EPIO_REG(DATA1), mask0 >> 32); t4_write_reg(adap, EPIO_REG(DATA2), mask1); @@ -2550,21 +2853,21 @@ int t4_wol_pat_enable(struct adapter *adap, unsigned int port, unsigned int map, /* write byte masks */ t4_write_reg(adap, EPIO_REG(DATA0), mask0); - t4_write_reg(adap, EPIO_REG(OP), ADDRESS(i) | EPIOWR); + t4_write_reg(adap, EPIO_REG(OP), ADDRESS_V(i) | EPIOWR_F); t4_read_reg(adap, EPIO_REG(OP)); /* flush */ - if (t4_read_reg(adap, EPIO_REG(OP)) & SF_BUSY) + if (t4_read_reg(adap, EPIO_REG(OP)) & SF_BUSY_F) return -ETIMEDOUT; /* write CRC */ t4_write_reg(adap, EPIO_REG(DATA0), crc); - t4_write_reg(adap, EPIO_REG(OP), ADDRESS(i + 32) | EPIOWR); + t4_write_reg(adap, EPIO_REG(OP), ADDRESS_V(i + 32) | EPIOWR_F); t4_read_reg(adap, EPIO_REG(OP)); /* flush */ - if (t4_read_reg(adap, EPIO_REG(OP)) & SF_BUSY) + if (t4_read_reg(adap, EPIO_REG(OP)) & SF_BUSY_F) return -ETIMEDOUT; } #undef EPIO_REG - t4_set_reg_field(adap, PORT_REG(port, XGMAC_PORT_CFG2), 0, PATEN); + t4_set_reg_field(adap, PORT_REG(port, XGMAC_PORT_CFG2_A), 0, PATEN_F); return 0; } @@ -2749,9 +3052,9 @@ void t4_sge_decode_idma_state(struct adapter *adapter, int state) "IDMA_FL_SEND_COMPLETION_TO_IMSG", }; static const u32 sge_regs[] = { - SGE_DEBUG_DATA_LOW_INDEX_2, - SGE_DEBUG_DATA_LOW_INDEX_3, - SGE_DEBUG_DATA_HIGH_INDEX_10, + SGE_DEBUG_DATA_LOW_INDEX_2_A, + SGE_DEBUG_DATA_LOW_INDEX_3_A, + SGE_DEBUG_DATA_HIGH_INDEX_10_A, }; const char **sge_idma_decode; int sge_idma_decode_nstates; @@ -2818,7 +3121,7 @@ retry: if (ret < 0) { if ((ret == -EBUSY || ret == -ETIMEDOUT) && retries-- > 0) goto retry; - if (t4_read_reg(adap, MA_PCIE_FW) & PCIE_FW_ERR) + if (t4_read_reg(adap, PCIE_FW_A) & PCIE_FW_ERR_F) t4_report_fw_error(adap); return ret; } @@ -2868,8 +3171,8 @@ retry: * timeout ... and then retry if we haven't exhausted * our retries ... */ - pcie_fw = t4_read_reg(adap, MA_PCIE_FW); - if (!(pcie_fw & (PCIE_FW_ERR|PCIE_FW_INIT))) { + pcie_fw = t4_read_reg(adap, PCIE_FW_A); + if (!(pcie_fw & (PCIE_FW_ERR_F|PCIE_FW_INIT_F))) { if (waiting <= 0) { if (retries-- > 0) goto retry; @@ -2884,9 +3187,9 @@ retry: * report errors preferentially. */ if (state) { - if (pcie_fw & PCIE_FW_ERR) + if (pcie_fw & PCIE_FW_ERR_F) *state = DEV_STATE_ERR; - else if (pcie_fw & PCIE_FW_INIT) + else if (pcie_fw & PCIE_FW_INIT_F) *state = DEV_STATE_INIT; } @@ -2896,7 +3199,7 @@ retry: * for our caller. */ if (master_mbox == PCIE_FW_MASTER_M && - (pcie_fw & PCIE_FW_MASTER_VLD)) + (pcie_fw & PCIE_FW_MASTER_VLD_F)) master_mbox = PCIE_FW_MASTER_G(pcie_fw); break; } @@ -2985,7 +3288,7 @@ static int t4_fw_halt(struct adapter *adap, unsigned int mbox, int force) memset(&c, 0, sizeof(c)); INIT_CMD(c, RESET, WRITE); - c.val = htonl(PIORST | PIORSTMODE); + c.val = htonl(PIORST_F | PIORSTMODE_F); c.halt_pkd = htonl(FW_RESET_CMD_HALT_F); ret = t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); } @@ -3004,8 +3307,8 @@ static int t4_fw_halt(struct adapter *adap, unsigned int mbox, int force) * rather than a RESET ... if it's new enough to understand that ... */ if (ret == 0 || force) { - t4_set_reg_field(adap, CIM_BOOT_CFG, UPCRST, UPCRST); - t4_set_reg_field(adap, PCIE_FW, PCIE_FW_HALT_F, + t4_set_reg_field(adap, CIM_BOOT_CFG_A, UPCRST_F, UPCRST_F); + t4_set_reg_field(adap, PCIE_FW_A, PCIE_FW_HALT_F, PCIE_FW_HALT_F); } @@ -3045,7 +3348,7 @@ static int t4_fw_restart(struct adapter *adap, unsigned int mbox, int reset) * doing it automatically, we need to clear the PCIE_FW.HALT * bit. */ - t4_set_reg_field(adap, PCIE_FW, PCIE_FW_HALT_F, 0); + t4_set_reg_field(adap, PCIE_FW_A, PCIE_FW_HALT_F, 0); /* * If we've been given a valid mailbox, first try to get the @@ -3055,21 +3358,21 @@ static int t4_fw_restart(struct adapter *adap, unsigned int mbox, int reset) * hitting the chip with a hammer. */ if (mbox <= PCIE_FW_MASTER_M) { - t4_set_reg_field(adap, CIM_BOOT_CFG, UPCRST, 0); + t4_set_reg_field(adap, CIM_BOOT_CFG_A, UPCRST_F, 0); msleep(100); if (t4_fw_reset(adap, mbox, - PIORST | PIORSTMODE) == 0) + PIORST_F | PIORSTMODE_F) == 0) return 0; } - t4_write_reg(adap, PL_RST, PIORST | PIORSTMODE); + t4_write_reg(adap, PL_RST_A, PIORST_F | PIORSTMODE_F); msleep(2000); } else { int ms; - t4_set_reg_field(adap, CIM_BOOT_CFG, UPCRST, 0); + t4_set_reg_field(adap, CIM_BOOT_CFG_A, UPCRST_F, 0); for (ms = 0; ms < FW_CMD_MAX_TIMEOUT; ) { - if (!(t4_read_reg(adap, PCIE_FW) & PCIE_FW_HALT_F)) + if (!(t4_read_reg(adap, PCIE_FW_A) & PCIE_FW_HALT_F)) return 0; msleep(100); ms += 100; @@ -3148,22 +3451,23 @@ int t4_fixup_host_params(struct adapter *adap, unsigned int page_size, unsigned int fl_align = cache_line_size < 32 ? 32 : cache_line_size; unsigned int fl_align_log = fls(fl_align) - 1; - t4_write_reg(adap, SGE_HOST_PAGE_SIZE, - HOSTPAGESIZEPF0(sge_hps) | - HOSTPAGESIZEPF1(sge_hps) | - HOSTPAGESIZEPF2(sge_hps) | - HOSTPAGESIZEPF3(sge_hps) | - HOSTPAGESIZEPF4(sge_hps) | - HOSTPAGESIZEPF5(sge_hps) | - HOSTPAGESIZEPF6(sge_hps) | - HOSTPAGESIZEPF7(sge_hps)); + t4_write_reg(adap, SGE_HOST_PAGE_SIZE_A, + HOSTPAGESIZEPF0_V(sge_hps) | + HOSTPAGESIZEPF1_V(sge_hps) | + HOSTPAGESIZEPF2_V(sge_hps) | + HOSTPAGESIZEPF3_V(sge_hps) | + HOSTPAGESIZEPF4_V(sge_hps) | + HOSTPAGESIZEPF5_V(sge_hps) | + HOSTPAGESIZEPF6_V(sge_hps) | + HOSTPAGESIZEPF7_V(sge_hps)); if (is_t4(adap->params.chip)) { - t4_set_reg_field(adap, SGE_CONTROL, - INGPADBOUNDARY_MASK | - EGRSTATUSPAGESIZE_MASK, - INGPADBOUNDARY(fl_align_log - 5) | - EGRSTATUSPAGESIZE(stat_len != 64)); + t4_set_reg_field(adap, SGE_CONTROL_A, + INGPADBOUNDARY_V(INGPADBOUNDARY_M) | + EGRSTATUSPAGESIZE_F, + INGPADBOUNDARY_V(fl_align_log - + INGPADBOUNDARY_SHIFT_X) | + EGRSTATUSPAGESIZE_V(stat_len != 64)); } else { /* T5 introduced the separation of the Free List Padding and * Packing Boundaries. Thus, we can select a smaller Padding @@ -3193,15 +3497,15 @@ int t4_fixup_host_params(struct adapter *adap, unsigned int page_size, fl_align = 64; fl_align_log = 6; } - t4_set_reg_field(adap, SGE_CONTROL, - INGPADBOUNDARY_MASK | - EGRSTATUSPAGESIZE_MASK, - INGPADBOUNDARY(INGPCIEBOUNDARY_32B_X) | - EGRSTATUSPAGESIZE(stat_len != 64)); + t4_set_reg_field(adap, SGE_CONTROL_A, + INGPADBOUNDARY_V(INGPADBOUNDARY_M) | + EGRSTATUSPAGESIZE_F, + INGPADBOUNDARY_V(INGPCIEBOUNDARY_32B_X) | + EGRSTATUSPAGESIZE_V(stat_len != 64)); t4_set_reg_field(adap, SGE_CONTROL2_A, INGPACKBOUNDARY_V(INGPACKBOUNDARY_M), INGPACKBOUNDARY_V(fl_align_log - - INGPACKBOUNDARY_SHIFT_X)); + INGPACKBOUNDARY_SHIFT_X)); } /* * Adjust various SGE Free List Host Buffer Sizes. @@ -3224,15 +3528,15 @@ int t4_fixup_host_params(struct adapter *adap, unsigned int page_size, * Default Firmware Configuration File but we need to adjust it for * this host's cache line size. */ - t4_write_reg(adap, SGE_FL_BUFFER_SIZE0, page_size); - t4_write_reg(adap, SGE_FL_BUFFER_SIZE2, - (t4_read_reg(adap, SGE_FL_BUFFER_SIZE2) + fl_align-1) + t4_write_reg(adap, SGE_FL_BUFFER_SIZE0_A, page_size); + t4_write_reg(adap, SGE_FL_BUFFER_SIZE2_A, + (t4_read_reg(adap, SGE_FL_BUFFER_SIZE2_A) + fl_align-1) & ~(fl_align-1)); - t4_write_reg(adap, SGE_FL_BUFFER_SIZE3, - (t4_read_reg(adap, SGE_FL_BUFFER_SIZE3) + fl_align-1) + t4_write_reg(adap, SGE_FL_BUFFER_SIZE3_A, + (t4_read_reg(adap, SGE_FL_BUFFER_SIZE3_A) + fl_align-1) & ~(fl_align-1)); - t4_write_reg(adap, ULP_RX_TDDP_PSZ, HPZ0(page_shift - 12)); + t4_write_reg(adap, ULP_RX_TDDP_PSZ_A, HPZ0_V(page_shift - 12)); return 0; } @@ -3917,12 +4221,12 @@ int t4_wait_dev_ready(void __iomem *regs) { u32 whoami; - whoami = readl(regs + PL_WHOAMI); + whoami = readl(regs + PL_WHOAMI_A); if (whoami != 0xffffffff && whoami != CIM_PF_NOACCESS) return 0; msleep(500); - whoami = readl(regs + PL_WHOAMI); + whoami = readl(regs + PL_WHOAMI_A); return (whoami != 0xffffffff && whoami != CIM_PF_NOACCESS ? 0 : -EIO); } @@ -3946,7 +4250,7 @@ static int get_flash_params(struct adapter *adap) ret = sf1_write(adap, 1, 1, 0, SF_RD_ID); if (!ret) ret = sf1_read(adap, 3, 0, 1, &info); - t4_write_reg(adap, SF_OP, 0); /* unlock SF */ + t4_write_reg(adap, SF_OP_A, 0); /* unlock SF */ if (ret) return ret; @@ -3969,7 +4273,7 @@ static int get_flash_params(struct adapter *adap) return -EINVAL; adap->params.sf_size = 1 << info; adap->params.sf_fw_start = - t4_read_reg(adap, CIM_BOOT_CFG) & BOOTADDR_MASK; + t4_read_reg(adap, CIM_BOOT_CFG_A) & BOOTADDR_M; if (adap->params.sf_size < FLASH_MIN_SIZE) dev_warn(adap->pdev_dev, "WARNING!!! FLASH size %#x < %#x!!!\n", @@ -3993,7 +4297,7 @@ int t4_prep_adapter(struct adapter *adapter) u32 pl_rev; get_pci_mode(adapter, &adapter->params.pci); - pl_rev = G_REV(t4_read_reg(adapter, PL_REV)); + pl_rev = REV_G(t4_read_reg(adapter, PL_REV_A)); ret = get_flash_params(adapter); if (ret < 0) { @@ -4019,6 +4323,7 @@ int t4_prep_adapter(struct adapter *adapter) return -EINVAL; } + adapter->params.cim_la_size = CIMLA_SIZE; init_cong_ctrl(adapter->params.a_wnd, adapter->params.b_wnd); /* @@ -4133,7 +4438,7 @@ int t4_init_sge_params(struct adapter *adapter) /* Extract the SGE Page Size for our PF. */ - hps = t4_read_reg(adapter, SGE_HOST_PAGE_SIZE); + hps = t4_read_reg(adapter, SGE_HOST_PAGE_SIZE_A); s_hps = (HOSTPAGESIZEPF0_S + (HOSTPAGESIZEPF1_S - HOSTPAGESIZEPF0_S) * adapter->fn); sge_params->hps = ((hps >> s_hps) & HOSTPAGESIZEPF0_M); @@ -4142,10 +4447,10 @@ int t4_init_sge_params(struct adapter *adapter) */ s_qpp = (QUEUESPERPAGEPF0_S + (QUEUESPERPAGEPF1_S - QUEUESPERPAGEPF0_S) * adapter->fn); - qpp = t4_read_reg(adapter, SGE_EGRESS_QUEUES_PER_PAGE_PF); - sge_params->eq_qpp = ((qpp >> s_qpp) & QUEUESPERPAGEPF0_MASK); - qpp = t4_read_reg(adapter, SGE_INGRESS_QUEUES_PER_PAGE_PF); - sge_params->iq_qpp = ((qpp >> s_qpp) & QUEUESPERPAGEPF0_MASK); + qpp = t4_read_reg(adapter, SGE_EGRESS_QUEUES_PER_PAGE_PF_A); + sge_params->eq_qpp = ((qpp >> s_qpp) & QUEUESPERPAGEPF0_M); + qpp = t4_read_reg(adapter, SGE_INGRESS_QUEUES_PER_PAGE_PF_A); + sge_params->iq_qpp = ((qpp >> s_qpp) & QUEUESPERPAGEPF0_M); return 0; } @@ -4161,9 +4466,9 @@ int t4_init_tp_params(struct adapter *adap) int chan; u32 v; - v = t4_read_reg(adap, TP_TIMER_RESOLUTION); - adap->params.tp.tre = TIMERRESOLUTION_GET(v); - adap->params.tp.dack_re = DELAYEDACKRESOLUTION_GET(v); + v = t4_read_reg(adap, TP_TIMER_RESOLUTION_A); + adap->params.tp.tre = TIMERRESOLUTION_G(v); + adap->params.tp.dack_re = DELAYEDACKRESOLUTION_G(v); /* MODQ_REQ_MAP defaults to setting queues 0-3 to chan 0-3 */ for (chan = 0; chan < NCHAN; chan++) @@ -4172,27 +4477,27 @@ int t4_init_tp_params(struct adapter *adap) /* Cache the adapter's Compressed Filter Mode and global Incress * Configuration. */ - t4_read_indirect(adap, TP_PIO_ADDR, TP_PIO_DATA, + t4_read_indirect(adap, TP_PIO_ADDR_A, TP_PIO_DATA_A, &adap->params.tp.vlan_pri_map, 1, - TP_VLAN_PRI_MAP); - t4_read_indirect(adap, TP_PIO_ADDR, TP_PIO_DATA, + TP_VLAN_PRI_MAP_A); + t4_read_indirect(adap, TP_PIO_ADDR_A, TP_PIO_DATA_A, &adap->params.tp.ingress_config, 1, - TP_INGRESS_CONFIG); + TP_INGRESS_CONFIG_A); /* Now that we have TP_VLAN_PRI_MAP cached, we can calculate the field * shift positions of several elements of the Compressed Filter Tuple * for this adapter which we need frequently ... */ - adap->params.tp.vlan_shift = t4_filter_field_shift(adap, F_VLAN); - adap->params.tp.vnic_shift = t4_filter_field_shift(adap, F_VNIC_ID); - adap->params.tp.port_shift = t4_filter_field_shift(adap, F_PORT); + adap->params.tp.vlan_shift = t4_filter_field_shift(adap, VLAN_F); + adap->params.tp.vnic_shift = t4_filter_field_shift(adap, VNIC_ID_F); + adap->params.tp.port_shift = t4_filter_field_shift(adap, PORT_F); adap->params.tp.protocol_shift = t4_filter_field_shift(adap, - F_PROTOCOL); + PROTOCOL_F); /* If TP_INGRESS_CONFIG.VNID == 0, then TP_VLAN_PRI_MAP.VNIC_ID * represents the presense of an Outer VLAN instead of a VNIC ID. */ - if ((adap->params.tp.ingress_config & F_VNIC) == 0) + if ((adap->params.tp.ingress_config & VNIC_F) == 0) adap->params.tp.vnic_shift = -1; return 0; @@ -4218,35 +4523,35 @@ int t4_filter_field_shift(const struct adapter *adap, int filter_sel) for (sel = 1, field_shift = 0; sel < filter_sel; sel <<= 1) { switch (filter_mode & sel) { - case F_FCOE: - field_shift += W_FT_FCOE; + case FCOE_F: + field_shift += FT_FCOE_W; break; - case F_PORT: - field_shift += W_FT_PORT; + case PORT_F: + field_shift += FT_PORT_W; break; - case F_VNIC_ID: - field_shift += W_FT_VNIC_ID; + case VNIC_ID_F: + field_shift += FT_VNIC_ID_W; break; - case F_VLAN: - field_shift += W_FT_VLAN; + case VLAN_F: + field_shift += FT_VLAN_W; break; - case F_TOS: - field_shift += W_FT_TOS; + case TOS_F: + field_shift += FT_TOS_W; break; - case F_PROTOCOL: - field_shift += W_FT_PROTOCOL; + case PROTOCOL_F: + field_shift += FT_PROTOCOL_W; break; - case F_ETHERTYPE: - field_shift += W_FT_ETHERTYPE; + case ETHERTYPE_F: + field_shift += FT_ETHERTYPE_W; break; - case F_MACMATCH: - field_shift += W_FT_MACMATCH; + case MACMATCH_F: + field_shift += FT_MACMATCH_W; break; - case F_MPSHITTYPE: - field_shift += W_FT_MPSHITTYPE; + case MPSHITTYPE_F: + field_shift += FT_MPSHITTYPE_W; break; - case F_FRAGMENTATION: - field_shift += W_FT_FRAGMENTATION; + case FRAGMENTATION_F: + field_shift += FT_FRAGMENTATION_W; break; } } @@ -4311,3 +4616,289 @@ int t4_port_init(struct adapter *adap, int mbox, int pf, int vf) } return 0; } + +/** + * t4_read_cimq_cfg - read CIM queue configuration + * @adap: the adapter + * @base: holds the queue base addresses in bytes + * @size: holds the queue sizes in bytes + * @thres: holds the queue full thresholds in bytes + * + * Returns the current configuration of the CIM queues, starting with + * the IBQs, then the OBQs. + */ +void t4_read_cimq_cfg(struct adapter *adap, u16 *base, u16 *size, u16 *thres) +{ + unsigned int i, v; + int cim_num_obq = is_t4(adap->params.chip) ? + CIM_NUM_OBQ : CIM_NUM_OBQ_T5; + + for (i = 0; i < CIM_NUM_IBQ; i++) { + t4_write_reg(adap, CIM_QUEUE_CONFIG_REF_A, IBQSELECT_F | + QUENUMSELECT_V(i)); + v = t4_read_reg(adap, CIM_QUEUE_CONFIG_CTRL_A); + /* value is in 256-byte units */ + *base++ = CIMQBASE_G(v) * 256; + *size++ = CIMQSIZE_G(v) * 256; + *thres++ = QUEFULLTHRSH_G(v) * 8; /* 8-byte unit */ + } + for (i = 0; i < cim_num_obq; i++) { + t4_write_reg(adap, CIM_QUEUE_CONFIG_REF_A, OBQSELECT_F | + QUENUMSELECT_V(i)); + v = t4_read_reg(adap, CIM_QUEUE_CONFIG_CTRL_A); + /* value is in 256-byte units */ + *base++ = CIMQBASE_G(v) * 256; + *size++ = CIMQSIZE_G(v) * 256; + } +} + +/** + * t4_read_cim_ibq - read the contents of a CIM inbound queue + * @adap: the adapter + * @qid: the queue index + * @data: where to store the queue contents + * @n: capacity of @data in 32-bit words + * + * Reads the contents of the selected CIM queue starting at address 0 up + * to the capacity of @data. @n must be a multiple of 4. Returns < 0 on + * error and the number of 32-bit words actually read on success. + */ +int t4_read_cim_ibq(struct adapter *adap, unsigned int qid, u32 *data, size_t n) +{ + int i, err, attempts; + unsigned int addr; + const unsigned int nwords = CIM_IBQ_SIZE * 4; + + if (qid > 5 || (n & 3)) + return -EINVAL; + + addr = qid * nwords; + if (n > nwords) + n = nwords; + + /* It might take 3-10ms before the IBQ debug read access is allowed. + * Wait for 1 Sec with a delay of 1 usec. + */ + attempts = 1000000; + + for (i = 0; i < n; i++, addr++) { + t4_write_reg(adap, CIM_IBQ_DBG_CFG_A, IBQDBGADDR_V(addr) | + IBQDBGEN_F); + err = t4_wait_op_done(adap, CIM_IBQ_DBG_CFG_A, IBQDBGBUSY_F, 0, + attempts, 1); + if (err) + return err; + *data++ = t4_read_reg(adap, CIM_IBQ_DBG_DATA_A); + } + t4_write_reg(adap, CIM_IBQ_DBG_CFG_A, 0); + return i; +} + +/** + * t4_read_cim_obq - read the contents of a CIM outbound queue + * @adap: the adapter + * @qid: the queue index + * @data: where to store the queue contents + * @n: capacity of @data in 32-bit words + * + * Reads the contents of the selected CIM queue starting at address 0 up + * to the capacity of @data. @n must be a multiple of 4. Returns < 0 on + * error and the number of 32-bit words actually read on success. + */ +int t4_read_cim_obq(struct adapter *adap, unsigned int qid, u32 *data, size_t n) +{ + int i, err; + unsigned int addr, v, nwords; + int cim_num_obq = is_t4(adap->params.chip) ? + CIM_NUM_OBQ : CIM_NUM_OBQ_T5; + + if ((qid > (cim_num_obq - 1)) || (n & 3)) + return -EINVAL; + + t4_write_reg(adap, CIM_QUEUE_CONFIG_REF_A, OBQSELECT_F | + QUENUMSELECT_V(qid)); + v = t4_read_reg(adap, CIM_QUEUE_CONFIG_CTRL_A); + + addr = CIMQBASE_G(v) * 64; /* muliple of 256 -> muliple of 4 */ + nwords = CIMQSIZE_G(v) * 64; /* same */ + if (n > nwords) + n = nwords; + + for (i = 0; i < n; i++, addr++) { + t4_write_reg(adap, CIM_OBQ_DBG_CFG_A, OBQDBGADDR_V(addr) | + OBQDBGEN_F); + err = t4_wait_op_done(adap, CIM_OBQ_DBG_CFG_A, OBQDBGBUSY_F, 0, + 2, 1); + if (err) + return err; + *data++ = t4_read_reg(adap, CIM_OBQ_DBG_DATA_A); + } + t4_write_reg(adap, CIM_OBQ_DBG_CFG_A, 0); + return i; +} + +/** + * t4_cim_read - read a block from CIM internal address space + * @adap: the adapter + * @addr: the start address within the CIM address space + * @n: number of words to read + * @valp: where to store the result + * + * Reads a block of 4-byte words from the CIM intenal address space. + */ +int t4_cim_read(struct adapter *adap, unsigned int addr, unsigned int n, + unsigned int *valp) +{ + int ret = 0; + + if (t4_read_reg(adap, CIM_HOST_ACC_CTRL_A) & HOSTBUSY_F) + return -EBUSY; + + for ( ; !ret && n--; addr += 4) { + t4_write_reg(adap, CIM_HOST_ACC_CTRL_A, addr); + ret = t4_wait_op_done(adap, CIM_HOST_ACC_CTRL_A, HOSTBUSY_F, + 0, 5, 2); + if (!ret) + *valp++ = t4_read_reg(adap, CIM_HOST_ACC_DATA_A); + } + return ret; +} + +/** + * t4_cim_write - write a block into CIM internal address space + * @adap: the adapter + * @addr: the start address within the CIM address space + * @n: number of words to write + * @valp: set of values to write + * + * Writes a block of 4-byte words into the CIM intenal address space. + */ +int t4_cim_write(struct adapter *adap, unsigned int addr, unsigned int n, + const unsigned int *valp) +{ + int ret = 0; + + if (t4_read_reg(adap, CIM_HOST_ACC_CTRL_A) & HOSTBUSY_F) + return -EBUSY; + + for ( ; !ret && n--; addr += 4) { + t4_write_reg(adap, CIM_HOST_ACC_DATA_A, *valp++); + t4_write_reg(adap, CIM_HOST_ACC_CTRL_A, addr | HOSTWRITE_F); + ret = t4_wait_op_done(adap, CIM_HOST_ACC_CTRL_A, HOSTBUSY_F, + 0, 5, 2); + } + return ret; +} + +static int t4_cim_write1(struct adapter *adap, unsigned int addr, + unsigned int val) +{ + return t4_cim_write(adap, addr, 1, &val); +} + +/** + * t4_cim_read_la - read CIM LA capture buffer + * @adap: the adapter + * @la_buf: where to store the LA data + * @wrptr: the HW write pointer within the capture buffer + * + * Reads the contents of the CIM LA buffer with the most recent entry at + * the end of the returned data and with the entry at @wrptr first. + * We try to leave the LA in the running state we find it in. + */ +int t4_cim_read_la(struct adapter *adap, u32 *la_buf, unsigned int *wrptr) +{ + int i, ret; + unsigned int cfg, val, idx; + + ret = t4_cim_read(adap, UP_UP_DBG_LA_CFG_A, 1, &cfg); + if (ret) + return ret; + + if (cfg & UPDBGLAEN_F) { /* LA is running, freeze it */ + ret = t4_cim_write1(adap, UP_UP_DBG_LA_CFG_A, 0); + if (ret) + return ret; + } + + ret = t4_cim_read(adap, UP_UP_DBG_LA_CFG_A, 1, &val); + if (ret) + goto restart; + + idx = UPDBGLAWRPTR_G(val); + if (wrptr) + *wrptr = idx; + + for (i = 0; i < adap->params.cim_la_size; i++) { + ret = t4_cim_write1(adap, UP_UP_DBG_LA_CFG_A, + UPDBGLARDPTR_V(idx) | UPDBGLARDEN_F); + if (ret) + break; + ret = t4_cim_read(adap, UP_UP_DBG_LA_CFG_A, 1, &val); + if (ret) + break; + if (val & UPDBGLARDEN_F) { + ret = -ETIMEDOUT; + break; + } + ret = t4_cim_read(adap, UP_UP_DBG_LA_DATA_A, 1, &la_buf[i]); + if (ret) + break; + idx = (idx + 1) & UPDBGLARDPTR_M; + } +restart: + if (cfg & UPDBGLAEN_F) { + int r = t4_cim_write1(adap, UP_UP_DBG_LA_CFG_A, + cfg & ~UPDBGLARDEN_F); + if (!ret) + ret = r; + } + return ret; +} + +/** + * t4_tp_read_la - read TP LA capture buffer + * @adap: the adapter + * @la_buf: where to store the LA data + * @wrptr: the HW write pointer within the capture buffer + * + * Reads the contents of the TP LA buffer with the most recent entry at + * the end of the returned data and with the entry at @wrptr first. + * We leave the LA in the running state we find it in. + */ +void t4_tp_read_la(struct adapter *adap, u64 *la_buf, unsigned int *wrptr) +{ + bool last_incomplete; + unsigned int i, cfg, val, idx; + + cfg = t4_read_reg(adap, TP_DBG_LA_CONFIG_A) & 0xffff; + if (cfg & DBGLAENABLE_F) /* freeze LA */ + t4_write_reg(adap, TP_DBG_LA_CONFIG_A, + adap->params.tp.la_mask | (cfg ^ DBGLAENABLE_F)); + + val = t4_read_reg(adap, TP_DBG_LA_CONFIG_A); + idx = DBGLAWPTR_G(val); + last_incomplete = DBGLAMODE_G(val) >= 2 && (val & DBGLAWHLF_F) == 0; + if (last_incomplete) + idx = (idx + 1) & DBGLARPTR_M; + if (wrptr) + *wrptr = idx; + + val &= 0xffff; + val &= ~DBGLARPTR_V(DBGLARPTR_M); + val |= adap->params.tp.la_mask; + + for (i = 0; i < TPLA_SIZE; i++) { + t4_write_reg(adap, TP_DBG_LA_CONFIG_A, DBGLARPTR_V(idx) | val); + la_buf[i] = t4_read_reg64(adap, TP_DBG_LA_DATAL_A); + idx = (idx + 1) & DBGLARPTR_M; + } + + /* Wipe out last entry if it isn't valid */ + if (last_incomplete) + la_buf[TPLA_SIZE - 1] = ~0ULL; + + if (cfg & DBGLAENABLE_F) /* restore running state */ + t4_write_reg(adap, TP_DBG_LA_CONFIG_A, + cfg | adap->params.tp.la_mask); +} diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h index c19a90e7f7d1..380b15c0417a 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.h @@ -48,6 +48,7 @@ enum { NMTUS = 16, /* size of MTU table */ NCCTRL_WIN = 32, /* # of congestion control windows */ L2T_SIZE = 4096, /* # of L2T entries */ + PM_NSTATS = 5, /* # of PM stats */ MBOX_LEN = 64, /* mailbox size in bytes */ TRACE_LEN = 112, /* length of trace data and mask */ FILTER_OPT_LEN = 36, /* filter tuple width for optional components */ @@ -56,6 +57,17 @@ enum { }; enum { + CIM_NUM_IBQ = 6, /* # of CIM IBQs */ + CIM_NUM_OBQ = 6, /* # of CIM OBQs */ + CIM_NUM_OBQ_T5 = 8, /* # of CIM OBQs for T5 adapter */ + CIMLA_SIZE = 2048, /* # of 32-bit words in CIM LA */ + CIM_IBQ_SIZE = 128, /* # of 128-bit words in a CIM IBQ */ + CIM_OBQ_SIZE = 128, /* # of 128-bit words in a CIM OBQ */ + TPLA_SIZE = 128, /* # of 64-bit words in TP LA */ + ULPRX_LA_SIZE = 512, /* # of 256-bit words in ULP_RX LA */ +}; + +enum { SF_PAGE_SIZE = 256, /* serial flash page size */ SF_SEC_SIZE = 64 * 1024, /* serial flash sector size */ }; @@ -110,6 +122,18 @@ enum { SGE_INGPADBOUNDARY_SHIFT = 5,/* ingress queue pad boundary */ }; +/* PCI-e memory window access */ +enum pcie_memwin { + MEMWIN_NIC = 0, + MEMWIN_RSVD1 = 1, + MEMWIN_RSVD2 = 2, + MEMWIN_RDMA = 3, + MEMWIN_RSVD4 = 4, + MEMWIN_FOISCSI = 5, + MEMWIN_CSIOSTOR = 6, + MEMWIN_RSVD7 = 7, +}; + struct sge_qstat { /* data written to SGE queue status entries */ __be32 qid; __be16 cidx; diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h index 0f89f68948ab..0fb975e258b3 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h @@ -124,6 +124,13 @@ enum CPL_error { }; enum { + CPL_CONN_POLICY_AUTO = 0, + CPL_CONN_POLICY_ASK = 1, + CPL_CONN_POLICY_FILTER = 2, + CPL_CONN_POLICY_DENY = 3 +}; + +enum { ULP_MODE_NONE = 0, ULP_MODE_ISCSI = 2, ULP_MODE_RDMA = 4, @@ -160,16 +167,28 @@ union opcode_tid { u8 opcode; }; -#define CPL_OPCODE(x) ((x) << 24) -#define G_CPL_OPCODE(x) (((x) >> 24) & 0xFF) -#define MK_OPCODE_TID(opcode, tid) (CPL_OPCODE(opcode) | (tid)) +#define CPL_OPCODE_S 24 +#define CPL_OPCODE_V(x) ((x) << CPL_OPCODE_S) +#define CPL_OPCODE_G(x) (((x) >> CPL_OPCODE_S) & 0xFF) +#define TID_G(x) ((x) & 0xFFFFFF) + +/* tid is assumed to be 24-bits */ +#define MK_OPCODE_TID(opcode, tid) (CPL_OPCODE_V(opcode) | (tid)) + #define OPCODE_TID(cmd) ((cmd)->ot.opcode_tid) -#define GET_TID(cmd) (ntohl(OPCODE_TID(cmd)) & 0xFFFFFF) + +/* extract the TID from a CPL command */ +#define GET_TID(cmd) (TID_G(be32_to_cpu(OPCODE_TID(cmd)))) /* partitioning of TID fields that also carry a queue id */ -#define GET_TID_TID(x) ((x) & 0x3fff) -#define GET_TID_QID(x) (((x) >> 14) & 0x3ff) -#define TID_QID(x) ((x) << 14) +#define TID_TID_S 0 +#define TID_TID_M 0x3fff +#define TID_TID_G(x) (((x) >> TID_TID_S) & TID_TID_M) + +#define TID_QID_S 14 +#define TID_QID_M 0x3ff +#define TID_QID_V(x) ((x) << TID_QID_S) +#define TID_QID_G(x) (((x) >> TID_QID_S) & TID_QID_M) struct rss_header { u8 opcode; @@ -199,8 +218,8 @@ struct work_request_hdr { }; /* wr_hi fields */ -#define S_WR_OP 24 -#define V_WR_OP(x) ((__u64)(x) << S_WR_OP) +#define WR_OP_S 24 +#define WR_OP_V(x) ((__u64)(x) << WR_OP_S) #define WR_HDR struct work_request_hdr wr @@ -270,17 +289,42 @@ struct cpl_pass_open_req { __be32 local_ip; __be32 peer_ip; __be64 opt0; -#define NO_CONG(x) ((x) << 4) -#define DELACK(x) ((x) << 5) -#define DSCP(x) ((x) << 22) -#define TCAM_BYPASS(x) ((u64)(x) << 48) -#define NAGLE(x) ((u64)(x) << 49) __be64 opt1; -#define SYN_RSS_ENABLE (1 << 0) -#define SYN_RSS_QUEUE(x) ((x) << 2) -#define CONN_POLICY_ASK (1 << 22) }; +/* option 0 fields */ +#define NO_CONG_S 4 +#define NO_CONG_V(x) ((x) << NO_CONG_S) +#define NO_CONG_F NO_CONG_V(1U) + +#define DELACK_S 5 +#define DELACK_V(x) ((x) << DELACK_S) +#define DELACK_F DELACK_V(1U) + +#define DSCP_S 22 +#define DSCP_M 0x3F +#define DSCP_V(x) ((x) << DSCP_S) +#define DSCP_G(x) (((x) >> DSCP_S) & DSCP_M) + +#define TCAM_BYPASS_S 48 +#define TCAM_BYPASS_V(x) ((__u64)(x) << TCAM_BYPASS_S) +#define TCAM_BYPASS_F TCAM_BYPASS_V(1ULL) + +#define NAGLE_S 49 +#define NAGLE_V(x) ((__u64)(x) << NAGLE_S) +#define NAGLE_F NAGLE_V(1ULL) + +/* option 1 fields */ +#define SYN_RSS_ENABLE_S 0 +#define SYN_RSS_ENABLE_V(x) ((x) << SYN_RSS_ENABLE_S) +#define SYN_RSS_ENABLE_F SYN_RSS_ENABLE_V(1U) + +#define SYN_RSS_QUEUE_S 2 +#define SYN_RSS_QUEUE_V(x) ((x) << SYN_RSS_QUEUE_S) + +#define CONN_POLICY_S 22 +#define CONN_POLICY_V(x) ((x) << CONN_POLICY_S) + struct cpl_pass_open_req6 { WR_HDR; union opcode_tid ot; @@ -304,16 +348,37 @@ struct cpl_pass_accept_rpl { WR_HDR; union opcode_tid ot; __be32 opt2; -#define RX_COALESCE_VALID(x) ((x) << 11) -#define RX_COALESCE(x) ((x) << 12) -#define PACE(x) ((x) << 16) -#define TX_QUEUE(x) ((x) << 23) -#define CCTRL_ECN(x) ((x) << 27) -#define TSTAMPS_EN(x) ((x) << 29) -#define SACK_EN(x) ((x) << 30) __be64 opt0; }; +/* option 2 fields */ +#define RX_COALESCE_VALID_S 11 +#define RX_COALESCE_VALID_V(x) ((x) << RX_COALESCE_VALID_S) +#define RX_COALESCE_VALID_F RX_COALESCE_VALID_V(1U) + +#define RX_COALESCE_S 12 +#define RX_COALESCE_V(x) ((x) << RX_COALESCE_S) + +#define PACE_S 16 +#define PACE_V(x) ((x) << PACE_S) + +#define TX_QUEUE_S 23 +#define TX_QUEUE_M 0x7 +#define TX_QUEUE_V(x) ((x) << TX_QUEUE_S) +#define TX_QUEUE_G(x) (((x) >> TX_QUEUE_S) & TX_QUEUE_M) + +#define CCTRL_ECN_S 27 +#define CCTRL_ECN_V(x) ((x) << CCTRL_ECN_S) +#define CCTRL_ECN_F CCTRL_ECN_V(1U) + +#define TSTAMPS_EN_S 29 +#define TSTAMPS_EN_V(x) ((x) << TSTAMPS_EN_S) +#define TSTAMPS_EN_F TSTAMPS_EN_V(1U) + +#define SACK_EN_S 30 +#define SACK_EN_V(x) ((x) << SACK_EN_S) +#define SACK_EN_F SACK_EN_V(1U) + struct cpl_t5_pass_accept_rpl { WR_HDR; union opcode_tid ot; @@ -384,30 +449,61 @@ struct cpl_t5_act_open_req6 { struct cpl_act_open_rpl { union opcode_tid ot; __be32 atid_status; -#define GET_AOPEN_STATUS(x) ((x) & 0xff) -#define GET_AOPEN_ATID(x) (((x) >> 8) & 0xffffff) }; +/* cpl_act_open_rpl.atid_status fields */ +#define AOPEN_STATUS_S 0 +#define AOPEN_STATUS_M 0xFF +#define AOPEN_STATUS_G(x) (((x) >> AOPEN_STATUS_S) & AOPEN_STATUS_M) + +#define AOPEN_ATID_S 8 +#define AOPEN_ATID_M 0xFFFFFF +#define AOPEN_ATID_G(x) (((x) >> AOPEN_ATID_S) & AOPEN_ATID_M) + struct cpl_pass_establish { union opcode_tid ot; __be32 rsvd; __be32 tos_stid; -#define PASS_OPEN_TID(x) ((x) << 0) -#define PASS_OPEN_TOS(x) ((x) << 24) -#define GET_PASS_OPEN_TID(x) (((x) >> 0) & 0xFFFFFF) -#define GET_POPEN_TID(x) ((x) & 0xffffff) -#define GET_POPEN_TOS(x) (((x) >> 24) & 0xff) __be16 mac_idx; __be16 tcp_opt; -#define GET_TCPOPT_WSCALE_OK(x) (((x) >> 5) & 1) -#define GET_TCPOPT_SACK(x) (((x) >> 6) & 1) -#define GET_TCPOPT_TSTAMP(x) (((x) >> 7) & 1) -#define GET_TCPOPT_SND_WSCALE(x) (((x) >> 8) & 0xf) -#define GET_TCPOPT_MSS(x) (((x) >> 12) & 0xf) __be32 snd_isn; __be32 rcv_isn; }; +/* cpl_pass_establish.tos_stid fields */ +#define PASS_OPEN_TID_S 0 +#define PASS_OPEN_TID_M 0xFFFFFF +#define PASS_OPEN_TID_V(x) ((x) << PASS_OPEN_TID_S) +#define PASS_OPEN_TID_G(x) (((x) >> PASS_OPEN_TID_S) & PASS_OPEN_TID_M) + +#define PASS_OPEN_TOS_S 24 +#define PASS_OPEN_TOS_M 0xFF +#define PASS_OPEN_TOS_V(x) ((x) << PASS_OPEN_TOS_S) +#define PASS_OPEN_TOS_G(x) (((x) >> PASS_OPEN_TOS_S) & PASS_OPEN_TOS_M) + +/* cpl_pass_establish.tcp_opt fields (also applies to act_open_establish) */ +#define TCPOPT_WSCALE_OK_S 5 +#define TCPOPT_WSCALE_OK_M 0x1 +#define TCPOPT_WSCALE_OK_G(x) \ + (((x) >> TCPOPT_WSCALE_OK_S) & TCPOPT_WSCALE_OK_M) + +#define TCPOPT_SACK_S 6 +#define TCPOPT_SACK_M 0x1 +#define TCPOPT_SACK_G(x) (((x) >> TCPOPT_SACK_S) & TCPOPT_SACK_M) + +#define TCPOPT_TSTAMP_S 7 +#define TCPOPT_TSTAMP_M 0x1 +#define TCPOPT_TSTAMP_G(x) (((x) >> TCPOPT_TSTAMP_S) & TCPOPT_TSTAMP_M) + +#define TCPOPT_SND_WSCALE_S 8 +#define TCPOPT_SND_WSCALE_M 0xF +#define TCPOPT_SND_WSCALE_G(x) \ + (((x) >> TCPOPT_SND_WSCALE_S) & TCPOPT_SND_WSCALE_M) + +#define TCPOPT_MSS_S 12 +#define TCPOPT_MSS_M 0xF +#define TCPOPT_MSS_G(x) (((x) >> TCPOPT_MSS_S) & TCPOPT_MSS_M) + struct cpl_act_establish { union opcode_tid ot; __be32 rsvd; @@ -422,24 +518,39 @@ struct cpl_get_tcb { WR_HDR; union opcode_tid ot; __be16 reply_ctrl; -#define QUEUENO(x) ((x) << 0) -#define REPLY_CHAN(x) ((x) << 14) -#define NO_REPLY(x) ((x) << 15) __be16 cookie; }; +/* cpl_get_tcb.reply_ctrl fields */ +#define QUEUENO_S 0 +#define QUEUENO_V(x) ((x) << QUEUENO_S) + +#define REPLY_CHAN_S 14 +#define REPLY_CHAN_V(x) ((x) << REPLY_CHAN_S) +#define REPLY_CHAN_F REPLY_CHAN_V(1U) + +#define NO_REPLY_S 15 +#define NO_REPLY_V(x) ((x) << NO_REPLY_S) +#define NO_REPLY_F NO_REPLY_V(1U) + struct cpl_set_tcb_field { WR_HDR; union opcode_tid ot; __be16 reply_ctrl; __be16 word_cookie; -#define TCB_WORD(x) ((x) << 0) -#define TCB_COOKIE(x) ((x) << 5) -#define GET_TCB_COOKIE(x) (((x) >> 5) & 7) __be64 mask; __be64 val; }; +/* cpl_set_tcb_field.word_cookie fields */ +#define TCB_WORD_S 0 +#define TCB_WORD(x) ((x) << TCB_WORD_S) + +#define TCB_COOKIE_S 5 +#define TCB_COOKIE_M 0x7 +#define TCB_COOKIE_V(x) ((x) << TCB_COOKIE_S) +#define TCB_COOKIE_G(x) (((x) >> TCB_COOKIE_S) & TCB_COOKIE_M) + struct cpl_set_tcb_rpl { union opcode_tid ot; __be16 rsvd; @@ -466,10 +577,14 @@ struct cpl_close_listsvr_req { WR_HDR; union opcode_tid ot; __be16 reply_ctrl; -#define LISTSVR_IPV6(x) ((x) << 14) __be16 rsvd; }; +/* additional cpl_close_listsvr_req.reply_ctrl field */ +#define LISTSVR_IPV6_S 14 +#define LISTSVR_IPV6_V(x) ((x) << LISTSVR_IPV6_S) +#define LISTSVR_IPV6_F LISTSVR_IPV6_V(1U) + struct cpl_close_listsvr_rpl { union opcode_tid ot; u8 rsvd[3]; @@ -565,6 +680,34 @@ struct cpl_tx_pkt_lso_core { /* encapsulated CPL (TX_PKT, TX_PKT_XT or TX_DATA) follows here */ }; +/* cpl_tx_pkt_lso_core.lso_ctrl fields */ +#define LSO_TCPHDR_LEN_S 0 +#define LSO_TCPHDR_LEN_V(x) ((x) << LSO_TCPHDR_LEN_S) + +#define LSO_IPHDR_LEN_S 4 +#define LSO_IPHDR_LEN_V(x) ((x) << LSO_IPHDR_LEN_S) + +#define LSO_ETHHDR_LEN_S 16 +#define LSO_ETHHDR_LEN_V(x) ((x) << LSO_ETHHDR_LEN_S) + +#define LSO_IPV6_S 20 +#define LSO_IPV6_V(x) ((x) << LSO_IPV6_S) +#define LSO_IPV6_F LSO_IPV6_V(1U) + +#define LSO_LAST_SLICE_S 22 +#define LSO_LAST_SLICE_V(x) ((x) << LSO_LAST_SLICE_S) +#define LSO_LAST_SLICE_F LSO_LAST_SLICE_V(1U) + +#define LSO_FIRST_SLICE_S 23 +#define LSO_FIRST_SLICE_V(x) ((x) << LSO_FIRST_SLICE_S) +#define LSO_FIRST_SLICE_F LSO_FIRST_SLICE_V(1U) + +#define LSO_OPCODE_S 24 +#define LSO_OPCODE_V(x) ((x) << LSO_OPCODE_S) + +#define LSO_T5_XFER_SIZE_S 0 +#define LSO_T5_XFER_SIZE_V(x) ((x) << LSO_T5_XFER_SIZE_S) + struct cpl_tx_pkt_lso { WR_HDR; struct cpl_tx_pkt_lso_core c; @@ -574,8 +717,6 @@ struct cpl_tx_pkt_lso { struct cpl_iscsi_hdr { union opcode_tid ot; __be16 pdu_len_ddp; -#define ISCSI_PDU_LEN(x) ((x) & 0x7FFF) -#define ISCSI_DDP (1 << 15) __be16 len; __be32 seq; __be16 urg; @@ -583,6 +724,16 @@ struct cpl_iscsi_hdr { u8 status; }; +/* cpl_iscsi_hdr.pdu_len_ddp fields */ +#define ISCSI_PDU_LEN_S 0 +#define ISCSI_PDU_LEN_M 0x7FFF +#define ISCSI_PDU_LEN_V(x) ((x) << ISCSI_PDU_LEN_S) +#define ISCSI_PDU_LEN_G(x) (((x) >> ISCSI_PDU_LEN_S) & ISCSI_PDU_LEN_M) + +#define ISCSI_DDP_S 15 +#define ISCSI_DDP_V(x) ((x) << ISCSI_DDP_S) +#define ISCSI_DDP_F ISCSI_DDP_V(1U) + struct cpl_rx_data { union opcode_tid ot; __be16 rsvd; @@ -639,49 +790,61 @@ struct cpl_rx_pkt { __be16 vlan; __be16 len; __be32 l2info; -#define RXF_UDP (1 << 22) -#define RXF_TCP (1 << 23) -#define RXF_IP (1 << 24) -#define RXF_IP6 (1 << 25) __be16 hdr_len; __be16 err_vec; }; +#define RXF_UDP_S 22 +#define RXF_UDP_V(x) ((x) << RXF_UDP_S) +#define RXF_UDP_F RXF_UDP_V(1U) + +#define RXF_TCP_S 23 +#define RXF_TCP_V(x) ((x) << RXF_TCP_S) +#define RXF_TCP_F RXF_TCP_V(1U) + +#define RXF_IP_S 24 +#define RXF_IP_V(x) ((x) << RXF_IP_S) +#define RXF_IP_F RXF_IP_V(1U) + +#define RXF_IP6_S 25 +#define RXF_IP6_V(x) ((x) << RXF_IP6_S) +#define RXF_IP6_F RXF_IP6_V(1U) + /* rx_pkt.l2info fields */ -#define S_RX_ETHHDR_LEN 0 -#define M_RX_ETHHDR_LEN 0x1F -#define V_RX_ETHHDR_LEN(x) ((x) << S_RX_ETHHDR_LEN) -#define G_RX_ETHHDR_LEN(x) (((x) >> S_RX_ETHHDR_LEN) & M_RX_ETHHDR_LEN) - -#define S_RX_T5_ETHHDR_LEN 0 -#define M_RX_T5_ETHHDR_LEN 0x3F -#define V_RX_T5_ETHHDR_LEN(x) ((x) << S_RX_T5_ETHHDR_LEN) -#define G_RX_T5_ETHHDR_LEN(x) (((x) >> S_RX_T5_ETHHDR_LEN) & M_RX_T5_ETHHDR_LEN) - -#define S_RX_MACIDX 8 -#define M_RX_MACIDX 0x1FF -#define V_RX_MACIDX(x) ((x) << S_RX_MACIDX) -#define G_RX_MACIDX(x) (((x) >> S_RX_MACIDX) & M_RX_MACIDX) - -#define S_RXF_SYN 21 -#define V_RXF_SYN(x) ((x) << S_RXF_SYN) -#define F_RXF_SYN V_RXF_SYN(1U) - -#define S_RX_CHAN 28 -#define M_RX_CHAN 0xF -#define V_RX_CHAN(x) ((x) << S_RX_CHAN) -#define G_RX_CHAN(x) (((x) >> S_RX_CHAN) & M_RX_CHAN) +#define RX_ETHHDR_LEN_S 0 +#define RX_ETHHDR_LEN_M 0x1F +#define RX_ETHHDR_LEN_V(x) ((x) << RX_ETHHDR_LEN_S) +#define RX_ETHHDR_LEN_G(x) (((x) >> RX_ETHHDR_LEN_S) & RX_ETHHDR_LEN_M) + +#define RX_T5_ETHHDR_LEN_S 0 +#define RX_T5_ETHHDR_LEN_M 0x3F +#define RX_T5_ETHHDR_LEN_V(x) ((x) << RX_T5_ETHHDR_LEN_S) +#define RX_T5_ETHHDR_LEN_G(x) (((x) >> RX_T5_ETHHDR_LEN_S) & RX_T5_ETHHDR_LEN_M) + +#define RX_MACIDX_S 8 +#define RX_MACIDX_M 0x1FF +#define RX_MACIDX_V(x) ((x) << RX_MACIDX_S) +#define RX_MACIDX_G(x) (((x) >> RX_MACIDX_S) & RX_MACIDX_M) + +#define RXF_SYN_S 21 +#define RXF_SYN_V(x) ((x) << RXF_SYN_S) +#define RXF_SYN_F RXF_SYN_V(1U) + +#define RX_CHAN_S 28 +#define RX_CHAN_M 0xF +#define RX_CHAN_V(x) ((x) << RX_CHAN_S) +#define RX_CHAN_G(x) (((x) >> RX_CHAN_S) & RX_CHAN_M) /* rx_pkt.hdr_len fields */ -#define S_RX_TCPHDR_LEN 0 -#define M_RX_TCPHDR_LEN 0x3F -#define V_RX_TCPHDR_LEN(x) ((x) << S_RX_TCPHDR_LEN) -#define G_RX_TCPHDR_LEN(x) (((x) >> S_RX_TCPHDR_LEN) & M_RX_TCPHDR_LEN) +#define RX_TCPHDR_LEN_S 0 +#define RX_TCPHDR_LEN_M 0x3F +#define RX_TCPHDR_LEN_V(x) ((x) << RX_TCPHDR_LEN_S) +#define RX_TCPHDR_LEN_G(x) (((x) >> RX_TCPHDR_LEN_S) & RX_TCPHDR_LEN_M) -#define S_RX_IPHDR_LEN 6 -#define M_RX_IPHDR_LEN 0x3FF -#define V_RX_IPHDR_LEN(x) ((x) << S_RX_IPHDR_LEN) -#define G_RX_IPHDR_LEN(x) (((x) >> S_RX_IPHDR_LEN) & M_RX_IPHDR_LEN) +#define RX_IPHDR_LEN_S 6 +#define RX_IPHDR_LEN_M 0x3FF +#define RX_IPHDR_LEN_V(x) ((x) << RX_IPHDR_LEN_S) +#define RX_IPHDR_LEN_G(x) (((x) >> RX_IPHDR_LEN_S) & RX_IPHDR_LEN_M) struct cpl_trace_pkt { u8 opcode; @@ -730,14 +893,22 @@ struct cpl_l2t_write_req { WR_HDR; union opcode_tid ot; __be16 params; -#define L2T_W_INFO(x) ((x) << 2) -#define L2T_W_PORT(x) ((x) << 8) -#define L2T_W_NOREPLY(x) ((x) << 15) __be16 l2t_idx; __be16 vlan; u8 dst_mac[6]; }; +/* cpl_l2t_write_req.params fields */ +#define L2T_W_INFO_S 2 +#define L2T_W_INFO_V(x) ((x) << L2T_W_INFO_S) + +#define L2T_W_PORT_S 8 +#define L2T_W_PORT_V(x) ((x) << L2T_W_PORT_S) + +#define L2T_W_NOREPLY_S 15 +#define L2T_W_NOREPLY_V(x) ((x) << L2T_W_NOREPLY_S) +#define L2T_W_NOREPLY_F L2T_W_NOREPLY_V(1U) + struct cpl_l2t_write_rpl { union opcode_tid ot; u8 status; @@ -752,11 +923,15 @@ struct cpl_rdma_terminate { struct cpl_sge_egr_update { __be32 opcode_qid; -#define EGR_QID(x) ((x) & 0x1FFFF) __be16 cidx; __be16 pidx; }; +/* cpl_sge_egr_update.ot fields */ +#define EGR_QID_S 0 +#define EGR_QID_M 0x1FFFF +#define EGR_QID_G(x) (((x) >> EGR_QID_S) & EGR_QID_M) + /* cpl_fw*.type values */ enum { FW_TYPE_CMD_RPL = 0, @@ -849,22 +1024,30 @@ struct ulptx_sge_pair { struct ulptx_sgl { __be32 cmd_nsge; -#define ULPTX_NSGE(x) ((x) << 0) -#define ULPTX_MORE (1U << 23) __be32 len0; __be64 addr0; struct ulptx_sge_pair sge[0]; }; +#define ULPTX_NSGE_S 0 +#define ULPTX_NSGE_V(x) ((x) << ULPTX_NSGE_S) + +#define ULPTX_MORE_S 23 +#define ULPTX_MORE_V(x) ((x) << ULPTX_MORE_S) +#define ULPTX_MORE_F ULPTX_MORE_V(1U) + struct ulp_mem_io { WR_HDR; __be32 cmd; __be32 len16; /* command length */ __be32 dlen; /* data length in 32-byte units */ __be32 lock_addr; -#define ULP_MEMIO_LOCK(x) ((x) << 31) }; +#define ULP_MEMIO_LOCK_S 31 +#define ULP_MEMIO_LOCK_V(x) ((x) << ULP_MEMIO_LOCK_S) +#define ULP_MEMIO_LOCK_F ULP_MEMIO_LOCK_V(1U) + /* additional ulp_mem_io.cmd fields */ #define ULP_MEMIO_ORDER_S 23 #define ULP_MEMIO_ORDER_V(x) ((x) << ULP_MEMIO_ORDER_S) @@ -874,13 +1057,9 @@ struct ulp_mem_io { #define T5_ULP_MEMIO_IMM_V(x) ((x) << T5_ULP_MEMIO_IMM_S) #define T5_ULP_MEMIO_IMM_F T5_ULP_MEMIO_IMM_V(1U) -#define S_T5_ULP_MEMIO_IMM 23 -#define V_T5_ULP_MEMIO_IMM(x) ((x) << S_T5_ULP_MEMIO_IMM) -#define F_T5_ULP_MEMIO_IMM V_T5_ULP_MEMIO_IMM(1U) - -#define S_T5_ULP_MEMIO_ORDER 22 -#define V_T5_ULP_MEMIO_ORDER(x) ((x) << S_T5_ULP_MEMIO_ORDER) -#define F_T5_ULP_MEMIO_ORDER V_T5_ULP_MEMIO_ORDER(1U) +#define T5_ULP_MEMIO_ORDER_S 22 +#define T5_ULP_MEMIO_ORDER_V(x) ((x) << T5_ULP_MEMIO_ORDER_S) +#define T5_ULP_MEMIO_ORDER_F T5_ULP_MEMIO_ORDER_V(1U) /* ulp_mem_io.lock_addr fields */ #define ULP_MEMIO_ADDR_S 0 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 9e4f95a91fb4..ddfb5b846045 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_pci_id_tbl.h @@ -153,6 +153,7 @@ CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN CH_PCI_ID_TABLE_FENTRY(0x5086), /* Custom 2x T580-CR */ CH_PCI_ID_TABLE_FENTRY(0x5087), /* Custom T580-CR */ CH_PCI_ID_TABLE_FENTRY(0x5088), /* Custom T570-CR */ + CH_PCI_ID_TABLE_FENTRY(0x5089), /* Custom T520-CR */ CH_PCI_DEVICE_ID_TABLE_DEFINE_END; #endif /* CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h index d7bd34ee65bd..231a725f6d5d 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h @@ -63,460 +63,779 @@ #define MC_BIST_STATUS_REG(reg_addr, idx) ((reg_addr) + (idx) * 4) #define EDC_BIST_STATUS_REG(reg_addr, idx) ((reg_addr) + (idx) * 4) -#define SGE_PF_KDOORBELL 0x0 -#define QID_MASK 0xffff8000U -#define QID_SHIFT 15 -#define QID(x) ((x) << QID_SHIFT) -#define DBPRIO(x) ((x) << 14) -#define DBTYPE(x) ((x) << 13) -#define PIDX_MASK 0x00003fffU -#define PIDX_SHIFT 0 -#define PIDX(x) ((x) << PIDX_SHIFT) -#define PIDX_SHIFT_T5 0 -#define PIDX_T5(x) ((x) << PIDX_SHIFT_T5) - - -#define SGE_TIMERREGS 6 -#define SGE_PF_GTS 0x4 -#define INGRESSQID_MASK 0xffff0000U -#define INGRESSQID_SHIFT 16 -#define INGRESSQID(x) ((x) << INGRESSQID_SHIFT) -#define TIMERREG_MASK 0x0000e000U -#define TIMERREG_SHIFT 13 -#define TIMERREG(x) ((x) << TIMERREG_SHIFT) -#define SEINTARM_MASK 0x00001000U -#define SEINTARM_SHIFT 12 -#define SEINTARM(x) ((x) << SEINTARM_SHIFT) -#define CIDXINC_MASK 0x00000fffU -#define CIDXINC_SHIFT 0 -#define CIDXINC(x) ((x) << CIDXINC_SHIFT) - -#define X_RXPKTCPLMODE_SPLIT 1 -#define X_INGPADBOUNDARY_SHIFT 5 - -#define SGE_CONTROL 0x1008 -#define SGE_CONTROL2_A 0x1124 -#define DCASYSTYPE 0x00080000U -#define RXPKTCPLMODE_MASK 0x00040000U -#define RXPKTCPLMODE_SHIFT 18 -#define RXPKTCPLMODE(x) ((x) << RXPKTCPLMODE_SHIFT) -#define EGRSTATUSPAGESIZE_MASK 0x00020000U -#define EGRSTATUSPAGESIZE_SHIFT 17 -#define EGRSTATUSPAGESIZE(x) ((x) << EGRSTATUSPAGESIZE_SHIFT) -#define PKTSHIFT_MASK 0x00001c00U -#define PKTSHIFT_SHIFT 10 -#define PKTSHIFT(x) ((x) << PKTSHIFT_SHIFT) -#define PKTSHIFT_GET(x) (((x) & PKTSHIFT_MASK) >> PKTSHIFT_SHIFT) -#define INGPCIEBOUNDARY_32B_X 0 -#define INGPCIEBOUNDARY_MASK 0x00000380U -#define INGPCIEBOUNDARY_SHIFT 7 -#define INGPCIEBOUNDARY(x) ((x) << INGPCIEBOUNDARY_SHIFT) -#define INGPADBOUNDARY_MASK 0x00000070U -#define INGPADBOUNDARY_SHIFT 4 -#define INGPADBOUNDARY(x) ((x) << INGPADBOUNDARY_SHIFT) -#define INGPADBOUNDARY_GET(x) (((x) & INGPADBOUNDARY_MASK) \ - >> INGPADBOUNDARY_SHIFT) -#define INGPACKBOUNDARY_16B_X 0 -#define INGPACKBOUNDARY_SHIFT_X 5 +#define SGE_PF_KDOORBELL_A 0x0 + +#define QID_S 15 +#define QID_V(x) ((x) << QID_S) + +#define DBPRIO_S 14 +#define DBPRIO_V(x) ((x) << DBPRIO_S) +#define DBPRIO_F DBPRIO_V(1U) + +#define PIDX_S 0 +#define PIDX_V(x) ((x) << PIDX_S) + +#define SGE_VF_KDOORBELL_A 0x0 + +#define DBTYPE_S 13 +#define DBTYPE_V(x) ((x) << DBTYPE_S) +#define DBTYPE_F DBTYPE_V(1U) + +#define PIDX_T5_S 0 +#define PIDX_T5_M 0x1fffU +#define PIDX_T5_V(x) ((x) << PIDX_T5_S) +#define PIDX_T5_G(x) (((x) >> PIDX_T5_S) & PIDX_T5_M) + +#define SGE_PF_GTS_A 0x4 + +#define INGRESSQID_S 16 +#define INGRESSQID_V(x) ((x) << INGRESSQID_S) + +#define TIMERREG_S 13 +#define TIMERREG_V(x) ((x) << TIMERREG_S) + +#define SEINTARM_S 12 +#define SEINTARM_V(x) ((x) << SEINTARM_S) + +#define CIDXINC_S 0 +#define CIDXINC_M 0xfffU +#define CIDXINC_V(x) ((x) << CIDXINC_S) + +#define SGE_CONTROL_A 0x1008 +#define SGE_CONTROL2_A 0x1124 + +#define RXPKTCPLMODE_S 18 +#define RXPKTCPLMODE_V(x) ((x) << RXPKTCPLMODE_S) +#define RXPKTCPLMODE_F RXPKTCPLMODE_V(1U) + +#define EGRSTATUSPAGESIZE_S 17 +#define EGRSTATUSPAGESIZE_V(x) ((x) << EGRSTATUSPAGESIZE_S) +#define EGRSTATUSPAGESIZE_F EGRSTATUSPAGESIZE_V(1U) + +#define PKTSHIFT_S 10 +#define PKTSHIFT_M 0x7U +#define PKTSHIFT_V(x) ((x) << PKTSHIFT_S) +#define PKTSHIFT_G(x) (((x) >> PKTSHIFT_S) & PKTSHIFT_M) + +#define INGPCIEBOUNDARY_S 7 +#define INGPCIEBOUNDARY_V(x) ((x) << INGPCIEBOUNDARY_S) + +#define INGPADBOUNDARY_S 4 +#define INGPADBOUNDARY_M 0x7U +#define INGPADBOUNDARY_V(x) ((x) << INGPADBOUNDARY_S) +#define INGPADBOUNDARY_G(x) (((x) >> INGPADBOUNDARY_S) & INGPADBOUNDARY_M) + +#define EGRPCIEBOUNDARY_S 1 +#define EGRPCIEBOUNDARY_V(x) ((x) << EGRPCIEBOUNDARY_S) #define INGPACKBOUNDARY_S 16 #define INGPACKBOUNDARY_M 0x7U #define INGPACKBOUNDARY_V(x) ((x) << INGPACKBOUNDARY_S) #define INGPACKBOUNDARY_G(x) (((x) >> INGPACKBOUNDARY_S) \ & INGPACKBOUNDARY_M) -#define EGRPCIEBOUNDARY_MASK 0x0000000eU -#define EGRPCIEBOUNDARY_SHIFT 1 -#define EGRPCIEBOUNDARY(x) ((x) << EGRPCIEBOUNDARY_SHIFT) -#define GLOBALENABLE 0x00000001U -#define SGE_HOST_PAGE_SIZE 0x100c +#define GLOBALENABLE_S 0 +#define GLOBALENABLE_V(x) ((x) << GLOBALENABLE_S) +#define GLOBALENABLE_F GLOBALENABLE_V(1U) + +#define SGE_HOST_PAGE_SIZE_A 0x100c + +#define HOSTPAGESIZEPF7_S 28 +#define HOSTPAGESIZEPF7_M 0xfU +#define HOSTPAGESIZEPF7_V(x) ((x) << HOSTPAGESIZEPF7_S) +#define HOSTPAGESIZEPF7_G(x) (((x) >> HOSTPAGESIZEPF7_S) & HOSTPAGESIZEPF7_M) + +#define HOSTPAGESIZEPF6_S 24 +#define HOSTPAGESIZEPF6_M 0xfU +#define HOSTPAGESIZEPF6_V(x) ((x) << HOSTPAGESIZEPF6_S) +#define HOSTPAGESIZEPF6_G(x) (((x) >> HOSTPAGESIZEPF6_S) & HOSTPAGESIZEPF6_M) + +#define HOSTPAGESIZEPF5_S 20 +#define HOSTPAGESIZEPF5_M 0xfU +#define HOSTPAGESIZEPF5_V(x) ((x) << HOSTPAGESIZEPF5_S) +#define HOSTPAGESIZEPF5_G(x) (((x) >> HOSTPAGESIZEPF5_S) & HOSTPAGESIZEPF5_M) + +#define HOSTPAGESIZEPF4_S 16 +#define HOSTPAGESIZEPF4_M 0xfU +#define HOSTPAGESIZEPF4_V(x) ((x) << HOSTPAGESIZEPF4_S) +#define HOSTPAGESIZEPF4_G(x) (((x) >> HOSTPAGESIZEPF4_S) & HOSTPAGESIZEPF4_M) + +#define HOSTPAGESIZEPF3_S 12 +#define HOSTPAGESIZEPF3_M 0xfU +#define HOSTPAGESIZEPF3_V(x) ((x) << HOSTPAGESIZEPF3_S) +#define HOSTPAGESIZEPF3_G(x) (((x) >> HOSTPAGESIZEPF3_S) & HOSTPAGESIZEPF3_M) + +#define HOSTPAGESIZEPF2_S 8 +#define HOSTPAGESIZEPF2_M 0xfU +#define HOSTPAGESIZEPF2_V(x) ((x) << HOSTPAGESIZEPF2_S) +#define HOSTPAGESIZEPF2_G(x) (((x) >> HOSTPAGESIZEPF2_S) & HOSTPAGESIZEPF2_M) + +#define HOSTPAGESIZEPF1_S 4 +#define HOSTPAGESIZEPF1_M 0xfU +#define HOSTPAGESIZEPF1_V(x) ((x) << HOSTPAGESIZEPF1_S) +#define HOSTPAGESIZEPF1_G(x) (((x) >> HOSTPAGESIZEPF1_S) & HOSTPAGESIZEPF1_M) + +#define HOSTPAGESIZEPF0_S 0 +#define HOSTPAGESIZEPF0_M 0xfU +#define HOSTPAGESIZEPF0_V(x) ((x) << HOSTPAGESIZEPF0_S) +#define HOSTPAGESIZEPF0_G(x) (((x) >> HOSTPAGESIZEPF0_S) & HOSTPAGESIZEPF0_M) + +#define SGE_EGRESS_QUEUES_PER_PAGE_PF_A 0x1010 +#define SGE_EGRESS_QUEUES_PER_PAGE_VF_A 0x1014 -#define HOSTPAGESIZEPF7_MASK 0x0000000fU -#define HOSTPAGESIZEPF7_SHIFT 28 -#define HOSTPAGESIZEPF7(x) ((x) << HOSTPAGESIZEPF7_SHIFT) +#define QUEUESPERPAGEPF1_S 4 -#define HOSTPAGESIZEPF6_MASK 0x0000000fU -#define HOSTPAGESIZEPF6_SHIFT 24 -#define HOSTPAGESIZEPF6(x) ((x) << HOSTPAGESIZEPF6_SHIFT) +#define QUEUESPERPAGEPF0_S 0 +#define QUEUESPERPAGEPF0_M 0xfU +#define QUEUESPERPAGEPF0_V(x) ((x) << QUEUESPERPAGEPF0_S) +#define QUEUESPERPAGEPF0_G(x) (((x) >> QUEUESPERPAGEPF0_S) & QUEUESPERPAGEPF0_M) -#define HOSTPAGESIZEPF5_MASK 0x0000000fU -#define HOSTPAGESIZEPF5_SHIFT 20 -#define HOSTPAGESIZEPF5(x) ((x) << HOSTPAGESIZEPF5_SHIFT) +#define SGE_INT_CAUSE1_A 0x1024 +#define SGE_INT_CAUSE2_A 0x1030 +#define SGE_INT_CAUSE3_A 0x103c + +#define ERR_FLM_DBP_S 31 +#define ERR_FLM_DBP_V(x) ((x) << ERR_FLM_DBP_S) +#define ERR_FLM_DBP_F ERR_FLM_DBP_V(1U) + +#define ERR_FLM_IDMA1_S 30 +#define ERR_FLM_IDMA1_V(x) ((x) << ERR_FLM_IDMA1_S) +#define ERR_FLM_IDMA1_F ERR_FLM_IDMA1_V(1U) + +#define ERR_FLM_IDMA0_S 29 +#define ERR_FLM_IDMA0_V(x) ((x) << ERR_FLM_IDMA0_S) +#define ERR_FLM_IDMA0_F ERR_FLM_IDMA0_V(1U) + +#define ERR_FLM_HINT_S 28 +#define ERR_FLM_HINT_V(x) ((x) << ERR_FLM_HINT_S) +#define ERR_FLM_HINT_F ERR_FLM_HINT_V(1U) + +#define ERR_PCIE_ERROR3_S 27 +#define ERR_PCIE_ERROR3_V(x) ((x) << ERR_PCIE_ERROR3_S) +#define ERR_PCIE_ERROR3_F ERR_PCIE_ERROR3_V(1U) + +#define ERR_PCIE_ERROR2_S 26 +#define ERR_PCIE_ERROR2_V(x) ((x) << ERR_PCIE_ERROR2_S) +#define ERR_PCIE_ERROR2_F ERR_PCIE_ERROR2_V(1U) + +#define ERR_PCIE_ERROR1_S 25 +#define ERR_PCIE_ERROR1_V(x) ((x) << ERR_PCIE_ERROR1_S) +#define ERR_PCIE_ERROR1_F ERR_PCIE_ERROR1_V(1U) + +#define ERR_PCIE_ERROR0_S 24 +#define ERR_PCIE_ERROR0_V(x) ((x) << ERR_PCIE_ERROR0_S) +#define ERR_PCIE_ERROR0_F ERR_PCIE_ERROR0_V(1U) + +#define ERR_CPL_EXCEED_IQE_SIZE_S 22 +#define ERR_CPL_EXCEED_IQE_SIZE_V(x) ((x) << ERR_CPL_EXCEED_IQE_SIZE_S) +#define ERR_CPL_EXCEED_IQE_SIZE_F ERR_CPL_EXCEED_IQE_SIZE_V(1U) + +#define ERR_INVALID_CIDX_INC_S 21 +#define ERR_INVALID_CIDX_INC_V(x) ((x) << ERR_INVALID_CIDX_INC_S) +#define ERR_INVALID_CIDX_INC_F ERR_INVALID_CIDX_INC_V(1U) + +#define ERR_CPL_OPCODE_0_S 19 +#define ERR_CPL_OPCODE_0_V(x) ((x) << ERR_CPL_OPCODE_0_S) +#define ERR_CPL_OPCODE_0_F ERR_CPL_OPCODE_0_V(1U) + +#define ERR_DROPPED_DB_S 18 +#define ERR_DROPPED_DB_V(x) ((x) << ERR_DROPPED_DB_S) +#define ERR_DROPPED_DB_F ERR_DROPPED_DB_V(1U) + +#define ERR_DATA_CPL_ON_HIGH_QID1_S 17 +#define ERR_DATA_CPL_ON_HIGH_QID1_V(x) ((x) << ERR_DATA_CPL_ON_HIGH_QID1_S) +#define ERR_DATA_CPL_ON_HIGH_QID1_F ERR_DATA_CPL_ON_HIGH_QID1_V(1U) + +#define ERR_DATA_CPL_ON_HIGH_QID0_S 16 +#define ERR_DATA_CPL_ON_HIGH_QID0_V(x) ((x) << ERR_DATA_CPL_ON_HIGH_QID0_S) +#define ERR_DATA_CPL_ON_HIGH_QID0_F ERR_DATA_CPL_ON_HIGH_QID0_V(1U) + +#define ERR_BAD_DB_PIDX3_S 15 +#define ERR_BAD_DB_PIDX3_V(x) ((x) << ERR_BAD_DB_PIDX3_S) +#define ERR_BAD_DB_PIDX3_F ERR_BAD_DB_PIDX3_V(1U) + +#define ERR_BAD_DB_PIDX2_S 14 +#define ERR_BAD_DB_PIDX2_V(x) ((x) << ERR_BAD_DB_PIDX2_S) +#define ERR_BAD_DB_PIDX2_F ERR_BAD_DB_PIDX2_V(1U) + +#define ERR_BAD_DB_PIDX1_S 13 +#define ERR_BAD_DB_PIDX1_V(x) ((x) << ERR_BAD_DB_PIDX1_S) +#define ERR_BAD_DB_PIDX1_F ERR_BAD_DB_PIDX1_V(1U) + +#define ERR_BAD_DB_PIDX0_S 12 +#define ERR_BAD_DB_PIDX0_V(x) ((x) << ERR_BAD_DB_PIDX0_S) +#define ERR_BAD_DB_PIDX0_F ERR_BAD_DB_PIDX0_V(1U) + +#define ERR_ING_CTXT_PRIO_S 10 +#define ERR_ING_CTXT_PRIO_V(x) ((x) << ERR_ING_CTXT_PRIO_S) +#define ERR_ING_CTXT_PRIO_F ERR_ING_CTXT_PRIO_V(1U) + +#define ERR_EGR_CTXT_PRIO_S 9 +#define ERR_EGR_CTXT_PRIO_V(x) ((x) << ERR_EGR_CTXT_PRIO_S) +#define ERR_EGR_CTXT_PRIO_F ERR_EGR_CTXT_PRIO_V(1U) + +#define DBFIFO_HP_INT_S 8 +#define DBFIFO_HP_INT_V(x) ((x) << DBFIFO_HP_INT_S) +#define DBFIFO_HP_INT_F DBFIFO_HP_INT_V(1U) + +#define DBFIFO_LP_INT_S 7 +#define DBFIFO_LP_INT_V(x) ((x) << DBFIFO_LP_INT_S) +#define DBFIFO_LP_INT_F DBFIFO_LP_INT_V(1U) + +#define INGRESS_SIZE_ERR_S 5 +#define INGRESS_SIZE_ERR_V(x) ((x) << INGRESS_SIZE_ERR_S) +#define INGRESS_SIZE_ERR_F INGRESS_SIZE_ERR_V(1U) + +#define EGRESS_SIZE_ERR_S 4 +#define EGRESS_SIZE_ERR_V(x) ((x) << EGRESS_SIZE_ERR_S) +#define EGRESS_SIZE_ERR_F EGRESS_SIZE_ERR_V(1U) + +#define SGE_INT_ENABLE3_A 0x1040 +#define SGE_FL_BUFFER_SIZE0_A 0x1044 +#define SGE_FL_BUFFER_SIZE1_A 0x1048 +#define SGE_FL_BUFFER_SIZE2_A 0x104c +#define SGE_FL_BUFFER_SIZE3_A 0x1050 +#define SGE_FL_BUFFER_SIZE4_A 0x1054 +#define SGE_FL_BUFFER_SIZE5_A 0x1058 +#define SGE_FL_BUFFER_SIZE6_A 0x105c +#define SGE_FL_BUFFER_SIZE7_A 0x1060 +#define SGE_FL_BUFFER_SIZE8_A 0x1064 + +#define SGE_INGRESS_RX_THRESHOLD_A 0x10a0 + +#define THRESHOLD_0_S 24 +#define THRESHOLD_0_M 0x3fU +#define THRESHOLD_0_V(x) ((x) << THRESHOLD_0_S) +#define THRESHOLD_0_G(x) (((x) >> THRESHOLD_0_S) & THRESHOLD_0_M) + +#define THRESHOLD_1_S 16 +#define THRESHOLD_1_M 0x3fU +#define THRESHOLD_1_V(x) ((x) << THRESHOLD_1_S) +#define THRESHOLD_1_G(x) (((x) >> THRESHOLD_1_S) & THRESHOLD_1_M) + +#define THRESHOLD_2_S 8 +#define THRESHOLD_2_M 0x3fU +#define THRESHOLD_2_V(x) ((x) << THRESHOLD_2_S) +#define THRESHOLD_2_G(x) (((x) >> THRESHOLD_2_S) & THRESHOLD_2_M) + +#define THRESHOLD_3_S 0 +#define THRESHOLD_3_M 0x3fU +#define THRESHOLD_3_V(x) ((x) << THRESHOLD_3_S) +#define THRESHOLD_3_G(x) (((x) >> THRESHOLD_3_S) & THRESHOLD_3_M) + +#define SGE_CONM_CTRL_A 0x1094 + +#define EGRTHRESHOLD_S 8 +#define EGRTHRESHOLD_M 0x3fU +#define EGRTHRESHOLD_V(x) ((x) << EGRTHRESHOLD_S) +#define EGRTHRESHOLD_G(x) (((x) >> EGRTHRESHOLD_S) & EGRTHRESHOLD_M) + +#define EGRTHRESHOLDPACKING_S 14 +#define EGRTHRESHOLDPACKING_M 0x3fU +#define EGRTHRESHOLDPACKING_V(x) ((x) << EGRTHRESHOLDPACKING_S) +#define EGRTHRESHOLDPACKING_G(x) \ + (((x) >> EGRTHRESHOLDPACKING_S) & EGRTHRESHOLDPACKING_M) + +#define SGE_TIMESTAMP_LO_A 0x1098 +#define SGE_TIMESTAMP_HI_A 0x109c + +#define TSOP_S 28 +#define TSOP_M 0x3U +#define TSOP_V(x) ((x) << TSOP_S) +#define TSOP_G(x) (((x) >> TSOP_S) & TSOP_M) + +#define TSVAL_S 0 +#define TSVAL_M 0xfffffffU +#define TSVAL_V(x) ((x) << TSVAL_S) +#define TSVAL_G(x) (((x) >> TSVAL_S) & TSVAL_M) + +#define SGE_DBFIFO_STATUS_A 0x10a4 + +#define HP_INT_THRESH_S 28 +#define HP_INT_THRESH_M 0xfU +#define HP_INT_THRESH_V(x) ((x) << HP_INT_THRESH_S) + +#define LP_INT_THRESH_S 12 +#define LP_INT_THRESH_M 0xfU +#define LP_INT_THRESH_V(x) ((x) << LP_INT_THRESH_S) + +#define SGE_DOORBELL_CONTROL_A 0x10a8 + +#define NOCOALESCE_S 26 +#define NOCOALESCE_V(x) ((x) << NOCOALESCE_S) +#define NOCOALESCE_F NOCOALESCE_V(1U) + +#define ENABLE_DROP_S 13 +#define ENABLE_DROP_V(x) ((x) << ENABLE_DROP_S) +#define ENABLE_DROP_F ENABLE_DROP_V(1U) + +#define SGE_TIMER_VALUE_0_AND_1_A 0x10b8 + +#define TIMERVALUE0_S 16 +#define TIMERVALUE0_M 0xffffU +#define TIMERVALUE0_V(x) ((x) << TIMERVALUE0_S) +#define TIMERVALUE0_G(x) (((x) >> TIMERVALUE0_S) & TIMERVALUE0_M) + +#define TIMERVALUE1_S 0 +#define TIMERVALUE1_M 0xffffU +#define TIMERVALUE1_V(x) ((x) << TIMERVALUE1_S) +#define TIMERVALUE1_G(x) (((x) >> TIMERVALUE1_S) & TIMERVALUE1_M) + +#define SGE_TIMER_VALUE_2_AND_3_A 0x10bc + +#define TIMERVALUE2_S 16 +#define TIMERVALUE2_M 0xffffU +#define TIMERVALUE2_V(x) ((x) << TIMERVALUE2_S) +#define TIMERVALUE2_G(x) (((x) >> TIMERVALUE2_S) & TIMERVALUE2_M) + +#define TIMERVALUE3_S 0 +#define TIMERVALUE3_M 0xffffU +#define TIMERVALUE3_V(x) ((x) << TIMERVALUE3_S) +#define TIMERVALUE3_G(x) (((x) >> TIMERVALUE3_S) & TIMERVALUE3_M) + +#define SGE_TIMER_VALUE_4_AND_5_A 0x10c0 + +#define TIMERVALUE4_S 16 +#define TIMERVALUE4_M 0xffffU +#define TIMERVALUE4_V(x) ((x) << TIMERVALUE4_S) +#define TIMERVALUE4_G(x) (((x) >> TIMERVALUE4_S) & TIMERVALUE4_M) -#define HOSTPAGESIZEPF4_MASK 0x0000000fU -#define HOSTPAGESIZEPF4_SHIFT 16 -#define HOSTPAGESIZEPF4(x) ((x) << HOSTPAGESIZEPF4_SHIFT) +#define TIMERVALUE5_S 0 +#define TIMERVALUE5_M 0xffffU +#define TIMERVALUE5_V(x) ((x) << TIMERVALUE5_S) +#define TIMERVALUE5_G(x) (((x) >> TIMERVALUE5_S) & TIMERVALUE5_M) -#define HOSTPAGESIZEPF3_MASK 0x0000000fU -#define HOSTPAGESIZEPF3_SHIFT 12 -#define HOSTPAGESIZEPF3(x) ((x) << HOSTPAGESIZEPF3_SHIFT) +#define SGE_DEBUG_INDEX_A 0x10cc +#define SGE_DEBUG_DATA_HIGH_A 0x10d0 +#define SGE_DEBUG_DATA_LOW_A 0x10d4 -#define HOSTPAGESIZEPF2_MASK 0x0000000fU -#define HOSTPAGESIZEPF2_SHIFT 8 -#define HOSTPAGESIZEPF2(x) ((x) << HOSTPAGESIZEPF2_SHIFT) +#define SGE_DEBUG_DATA_LOW_INDEX_2_A 0x12c8 +#define SGE_DEBUG_DATA_LOW_INDEX_3_A 0x12cc +#define SGE_DEBUG_DATA_HIGH_INDEX_10_A 0x12a8 -#define HOSTPAGESIZEPF1_M 0x0000000fU -#define HOSTPAGESIZEPF1_S 4 -#define HOSTPAGESIZEPF1(x) ((x) << HOSTPAGESIZEPF1_S) +#define SGE_INGRESS_QUEUES_PER_PAGE_PF_A 0x10f4 +#define SGE_INGRESS_QUEUES_PER_PAGE_VF_A 0x10f8 -#define HOSTPAGESIZEPF0_M 0x0000000fU -#define HOSTPAGESIZEPF0_S 0 -#define HOSTPAGESIZEPF0(x) ((x) << HOSTPAGESIZEPF0_S) +#define HP_INT_THRESH_S 28 +#define HP_INT_THRESH_M 0xfU +#define HP_INT_THRESH_V(x) ((x) << HP_INT_THRESH_S) -#define SGE_EGRESS_QUEUES_PER_PAGE_PF 0x1010 -#define SGE_EGRESS_QUEUES_PER_PAGE_VF_A 0x1014 +#define HP_COUNT_S 16 +#define HP_COUNT_M 0x7ffU +#define HP_COUNT_G(x) (((x) >> HP_COUNT_S) & HP_COUNT_M) -#define QUEUESPERPAGEPF1_S 4 +#define LP_INT_THRESH_S 12 +#define LP_INT_THRESH_M 0xfU +#define LP_INT_THRESH_V(x) ((x) << LP_INT_THRESH_S) -#define QUEUESPERPAGEPF0_S 0 -#define QUEUESPERPAGEPF0_MASK 0x0000000fU -#define QUEUESPERPAGEPF0_GET(x) ((x) & QUEUESPERPAGEPF0_MASK) +#define LP_COUNT_S 0 +#define LP_COUNT_M 0x7ffU +#define LP_COUNT_G(x) (((x) >> LP_COUNT_S) & LP_COUNT_M) -#define QUEUESPERPAGEPF0 0 -#define QUEUESPERPAGEPF1 4 +#define LP_INT_THRESH_T5_S 18 +#define LP_INT_THRESH_T5_M 0xfffU +#define LP_INT_THRESH_T5_V(x) ((x) << LP_INT_THRESH_T5_S) -/* T5 and later support a new BAR2-based doorbell mechanism for Egress Queues. - * The User Doorbells are each 128 bytes in length with a Simple Doorbell at - * offsets 8x and a Write Combining single 64-byte Egress Queue Unit - * (X_IDXSIZE_UNIT) Gather Buffer interface at offset 64. For Ingress Queues, - * we have a Going To Sleep register at offsets 8x+4. - * - * As noted above, we have many instances of the Simple Doorbell and Going To - * Sleep registers at offsets 8x and 8x+4, respectively. We want to use a - * non-64-byte aligned offset for the Simple Doorbell in order to attempt to - * avoid buffering of the writes to the Simple Doorbell and we want to use a - * non-contiguous offset for the Going To Sleep writes in order to avoid - * possible combining between them. - */ -#define SGE_UDB_SIZE 128 -#define SGE_UDB_KDOORBELL 8 -#define SGE_UDB_GTS 20 -#define SGE_UDB_WCDOORBELL 64 - -#define SGE_INT_CAUSE1 0x1024 -#define SGE_INT_CAUSE2 0x1030 -#define SGE_INT_CAUSE3 0x103c -#define ERR_FLM_DBP 0x80000000U -#define ERR_FLM_IDMA1 0x40000000U -#define ERR_FLM_IDMA0 0x20000000U -#define ERR_FLM_HINT 0x10000000U -#define ERR_PCIE_ERROR3 0x08000000U -#define ERR_PCIE_ERROR2 0x04000000U -#define ERR_PCIE_ERROR1 0x02000000U -#define ERR_PCIE_ERROR0 0x01000000U -#define ERR_TIMER_ABOVE_MAX_QID 0x00800000U -#define ERR_CPL_EXCEED_IQE_SIZE 0x00400000U -#define ERR_INVALID_CIDX_INC 0x00200000U -#define ERR_ITP_TIME_PAUSED 0x00100000U -#define ERR_CPL_OPCODE_0 0x00080000U -#define ERR_DROPPED_DB 0x00040000U -#define ERR_DATA_CPL_ON_HIGH_QID1 0x00020000U -#define ERR_DATA_CPL_ON_HIGH_QID0 0x00010000U -#define ERR_BAD_DB_PIDX3 0x00008000U -#define ERR_BAD_DB_PIDX2 0x00004000U -#define ERR_BAD_DB_PIDX1 0x00002000U -#define ERR_BAD_DB_PIDX0 0x00001000U -#define ERR_ING_PCIE_CHAN 0x00000800U -#define ERR_ING_CTXT_PRIO 0x00000400U -#define ERR_EGR_CTXT_PRIO 0x00000200U -#define DBFIFO_HP_INT 0x00000100U -#define DBFIFO_LP_INT 0x00000080U -#define REG_ADDRESS_ERR 0x00000040U -#define INGRESS_SIZE_ERR 0x00000020U -#define EGRESS_SIZE_ERR 0x00000010U -#define ERR_INV_CTXT3 0x00000008U -#define ERR_INV_CTXT2 0x00000004U -#define ERR_INV_CTXT1 0x00000002U -#define ERR_INV_CTXT0 0x00000001U - -#define SGE_INT_ENABLE3 0x1040 -#define SGE_FL_BUFFER_SIZE0 0x1044 -#define SGE_FL_BUFFER_SIZE1 0x1048 -#define SGE_FL_BUFFER_SIZE2 0x104c -#define SGE_FL_BUFFER_SIZE3 0x1050 -#define SGE_FL_BUFFER_SIZE4 0x1054 -#define SGE_FL_BUFFER_SIZE5 0x1058 -#define SGE_FL_BUFFER_SIZE6 0x105c -#define SGE_FL_BUFFER_SIZE7 0x1060 -#define SGE_FL_BUFFER_SIZE8 0x1064 - -#define SGE_INGRESS_RX_THRESHOLD 0x10a0 -#define THRESHOLD_0_MASK 0x3f000000U -#define THRESHOLD_0_SHIFT 24 -#define THRESHOLD_0(x) ((x) << THRESHOLD_0_SHIFT) -#define THRESHOLD_0_GET(x) (((x) & THRESHOLD_0_MASK) >> THRESHOLD_0_SHIFT) -#define THRESHOLD_1_MASK 0x003f0000U -#define THRESHOLD_1_SHIFT 16 -#define THRESHOLD_1(x) ((x) << THRESHOLD_1_SHIFT) -#define THRESHOLD_1_GET(x) (((x) & THRESHOLD_1_MASK) >> THRESHOLD_1_SHIFT) -#define THRESHOLD_2_MASK 0x00003f00U -#define THRESHOLD_2_SHIFT 8 -#define THRESHOLD_2(x) ((x) << THRESHOLD_2_SHIFT) -#define THRESHOLD_2_GET(x) (((x) & THRESHOLD_2_MASK) >> THRESHOLD_2_SHIFT) -#define THRESHOLD_3_MASK 0x0000003fU -#define THRESHOLD_3_SHIFT 0 -#define THRESHOLD_3(x) ((x) << THRESHOLD_3_SHIFT) -#define THRESHOLD_3_GET(x) (((x) & THRESHOLD_3_MASK) >> THRESHOLD_3_SHIFT) - -#define SGE_CONM_CTRL 0x1094 -#define EGRTHRESHOLD_MASK 0x00003f00U -#define EGRTHRESHOLDshift 8 -#define EGRTHRESHOLD(x) ((x) << EGRTHRESHOLDshift) -#define EGRTHRESHOLD_GET(x) (((x) & EGRTHRESHOLD_MASK) >> EGRTHRESHOLDshift) - -#define EGRTHRESHOLDPACKING_MASK 0x3fU -#define EGRTHRESHOLDPACKING_SHIFT 14 -#define EGRTHRESHOLDPACKING(x) ((x) << EGRTHRESHOLDPACKING_SHIFT) -#define EGRTHRESHOLDPACKING_GET(x) (((x) >> EGRTHRESHOLDPACKING_SHIFT) & \ - EGRTHRESHOLDPACKING_MASK) - -#define SGE_DBFIFO_STATUS 0x10a4 -#define HP_INT_THRESH_SHIFT 28 -#define HP_INT_THRESH_MASK 0xfU -#define HP_INT_THRESH(x) ((x) << HP_INT_THRESH_SHIFT) -#define LP_INT_THRESH_SHIFT 12 -#define LP_INT_THRESH_MASK 0xfU -#define LP_INT_THRESH(x) ((x) << LP_INT_THRESH_SHIFT) - -#define SGE_DOORBELL_CONTROL 0x10a8 -#define ENABLE_DROP (1 << 13) - -#define S_NOCOALESCE 26 -#define V_NOCOALESCE(x) ((x) << S_NOCOALESCE) -#define F_NOCOALESCE V_NOCOALESCE(1U) - -#define SGE_TIMESTAMP_LO 0x1098 -#define SGE_TIMESTAMP_HI 0x109c -#define S_TSVAL 0 -#define M_TSVAL 0xfffffffU -#define GET_TSVAL(x) (((x) >> S_TSVAL) & M_TSVAL) - -#define SGE_TIMER_VALUE_0_AND_1 0x10b8 -#define TIMERVALUE0_MASK 0xffff0000U -#define TIMERVALUE0_SHIFT 16 -#define TIMERVALUE0(x) ((x) << TIMERVALUE0_SHIFT) -#define TIMERVALUE0_GET(x) (((x) & TIMERVALUE0_MASK) >> TIMERVALUE0_SHIFT) -#define TIMERVALUE1_MASK 0x0000ffffU -#define TIMERVALUE1_SHIFT 0 -#define TIMERVALUE1(x) ((x) << TIMERVALUE1_SHIFT) -#define TIMERVALUE1_GET(x) (((x) & TIMERVALUE1_MASK) >> TIMERVALUE1_SHIFT) - -#define SGE_TIMER_VALUE_2_AND_3 0x10bc -#define TIMERVALUE2_MASK 0xffff0000U -#define TIMERVALUE2_SHIFT 16 -#define TIMERVALUE2(x) ((x) << TIMERVALUE2_SHIFT) -#define TIMERVALUE2_GET(x) (((x) & TIMERVALUE2_MASK) >> TIMERVALUE2_SHIFT) -#define TIMERVALUE3_MASK 0x0000ffffU -#define TIMERVALUE3_SHIFT 0 -#define TIMERVALUE3(x) ((x) << TIMERVALUE3_SHIFT) -#define TIMERVALUE3_GET(x) (((x) & TIMERVALUE3_MASK) >> TIMERVALUE3_SHIFT) - -#define SGE_TIMER_VALUE_4_AND_5 0x10c0 -#define TIMERVALUE4_MASK 0xffff0000U -#define TIMERVALUE4_SHIFT 16 -#define TIMERVALUE4(x) ((x) << TIMERVALUE4_SHIFT) -#define TIMERVALUE4_GET(x) (((x) & TIMERVALUE4_MASK) >> TIMERVALUE4_SHIFT) -#define TIMERVALUE5_MASK 0x0000ffffU -#define TIMERVALUE5_SHIFT 0 -#define TIMERVALUE5(x) ((x) << TIMERVALUE5_SHIFT) -#define TIMERVALUE5_GET(x) (((x) & TIMERVALUE5_MASK) >> TIMERVALUE5_SHIFT) - -#define SGE_DEBUG_INDEX 0x10cc -#define SGE_DEBUG_DATA_HIGH 0x10d0 -#define SGE_DEBUG_DATA_LOW 0x10d4 -#define SGE_DEBUG_DATA_LOW_INDEX_2 0x12c8 -#define SGE_DEBUG_DATA_LOW_INDEX_3 0x12cc -#define SGE_DEBUG_DATA_HIGH_INDEX_10 0x12a8 -#define SGE_INGRESS_QUEUES_PER_PAGE_PF 0x10f4 -#define SGE_INGRESS_QUEUES_PER_PAGE_VF_A 0x10f8 +#define LP_COUNT_T5_S 0 +#define LP_COUNT_T5_M 0x3ffffU +#define LP_COUNT_T5_G(x) (((x) >> LP_COUNT_T5_S) & LP_COUNT_T5_M) + +#define SGE_DOORBELL_CONTROL_A 0x10a8 + +#define SGE_STAT_TOTAL_A 0x10e4 +#define SGE_STAT_MATCH_A 0x10e8 +#define SGE_STAT_CFG_A 0x10ec + +#define STATSOURCE_T5_S 9 +#define STATSOURCE_T5_V(x) ((x) << STATSOURCE_T5_S) + +#define SGE_DBFIFO_STATUS2_A 0x1118 + +#define HP_INT_THRESH_T5_S 10 +#define HP_INT_THRESH_T5_M 0xfU +#define HP_INT_THRESH_T5_V(x) ((x) << HP_INT_THRESH_T5_S) + +#define HP_COUNT_T5_S 0 +#define HP_COUNT_T5_M 0x3ffU +#define HP_COUNT_T5_G(x) (((x) >> HP_COUNT_T5_S) & HP_COUNT_T5_M) + +#define ENABLE_DROP_S 13 +#define ENABLE_DROP_V(x) ((x) << ENABLE_DROP_S) +#define ENABLE_DROP_F ENABLE_DROP_V(1U) + +#define DROPPED_DB_S 0 +#define DROPPED_DB_V(x) ((x) << DROPPED_DB_S) +#define DROPPED_DB_F DROPPED_DB_V(1U) + +#define SGE_CTXT_CMD_A 0x11fc +#define SGE_DBQ_CTXT_BADDR_A 0x1084 + +/* registers for module PCIE */ +#define PCIE_PF_CFG_A 0x40 + +#define AIVEC_S 4 +#define AIVEC_M 0x3ffU +#define AIVEC_V(x) ((x) << AIVEC_S) + +#define PCIE_PF_CLI_A 0x44 +#define PCIE_INT_CAUSE_A 0x3004 + +#define UNXSPLCPLERR_S 29 +#define UNXSPLCPLERR_V(x) ((x) << UNXSPLCPLERR_S) +#define UNXSPLCPLERR_F UNXSPLCPLERR_V(1U) + +#define PCIEPINT_S 28 +#define PCIEPINT_V(x) ((x) << PCIEPINT_S) +#define PCIEPINT_F PCIEPINT_V(1U) + +#define PCIESINT_S 27 +#define PCIESINT_V(x) ((x) << PCIESINT_S) +#define PCIESINT_F PCIESINT_V(1U) + +#define RPLPERR_S 26 +#define RPLPERR_V(x) ((x) << RPLPERR_S) +#define RPLPERR_F RPLPERR_V(1U) + +#define RXWRPERR_S 25 +#define RXWRPERR_V(x) ((x) << RXWRPERR_S) +#define RXWRPERR_F RXWRPERR_V(1U) + +#define RXCPLPERR_S 24 +#define RXCPLPERR_V(x) ((x) << RXCPLPERR_S) +#define RXCPLPERR_F RXCPLPERR_V(1U) + +#define PIOTAGPERR_S 23 +#define PIOTAGPERR_V(x) ((x) << PIOTAGPERR_S) +#define PIOTAGPERR_F PIOTAGPERR_V(1U) + +#define MATAGPERR_S 22 +#define MATAGPERR_V(x) ((x) << MATAGPERR_S) +#define MATAGPERR_F MATAGPERR_V(1U) + +#define INTXCLRPERR_S 21 +#define INTXCLRPERR_V(x) ((x) << INTXCLRPERR_S) +#define INTXCLRPERR_F INTXCLRPERR_V(1U) + +#define FIDPERR_S 20 +#define FIDPERR_V(x) ((x) << FIDPERR_S) +#define FIDPERR_F FIDPERR_V(1U) + +#define CFGSNPPERR_S 19 +#define CFGSNPPERR_V(x) ((x) << CFGSNPPERR_S) +#define CFGSNPPERR_F CFGSNPPERR_V(1U) + +#define HRSPPERR_S 18 +#define HRSPPERR_V(x) ((x) << HRSPPERR_S) +#define HRSPPERR_F HRSPPERR_V(1U) + +#define HREQPERR_S 17 +#define HREQPERR_V(x) ((x) << HREQPERR_S) +#define HREQPERR_F HREQPERR_V(1U) + +#define HCNTPERR_S 16 +#define HCNTPERR_V(x) ((x) << HCNTPERR_S) +#define HCNTPERR_F HCNTPERR_V(1U) + +#define DRSPPERR_S 15 +#define DRSPPERR_V(x) ((x) << DRSPPERR_S) +#define DRSPPERR_F DRSPPERR_V(1U) + +#define DREQPERR_S 14 +#define DREQPERR_V(x) ((x) << DREQPERR_S) +#define DREQPERR_F DREQPERR_V(1U) + +#define DCNTPERR_S 13 +#define DCNTPERR_V(x) ((x) << DCNTPERR_S) +#define DCNTPERR_F DCNTPERR_V(1U) + +#define CRSPPERR_S 12 +#define CRSPPERR_V(x) ((x) << CRSPPERR_S) +#define CRSPPERR_F CRSPPERR_V(1U) + +#define CREQPERR_S 11 +#define CREQPERR_V(x) ((x) << CREQPERR_S) +#define CREQPERR_F CREQPERR_V(1U) + +#define CCNTPERR_S 10 +#define CCNTPERR_V(x) ((x) << CCNTPERR_S) +#define CCNTPERR_F CCNTPERR_V(1U) + +#define TARTAGPERR_S 9 +#define TARTAGPERR_V(x) ((x) << TARTAGPERR_S) +#define TARTAGPERR_F TARTAGPERR_V(1U) + +#define PIOREQPERR_S 8 +#define PIOREQPERR_V(x) ((x) << PIOREQPERR_S) +#define PIOREQPERR_F PIOREQPERR_V(1U) + +#define PIOCPLPERR_S 7 +#define PIOCPLPERR_V(x) ((x) << PIOCPLPERR_S) +#define PIOCPLPERR_F PIOCPLPERR_V(1U) + +#define MSIXDIPERR_S 6 +#define MSIXDIPERR_V(x) ((x) << MSIXDIPERR_S) +#define MSIXDIPERR_F MSIXDIPERR_V(1U) + +#define MSIXDATAPERR_S 5 +#define MSIXDATAPERR_V(x) ((x) << MSIXDATAPERR_S) +#define MSIXDATAPERR_F MSIXDATAPERR_V(1U) + +#define MSIXADDRHPERR_S 4 +#define MSIXADDRHPERR_V(x) ((x) << MSIXADDRHPERR_S) +#define MSIXADDRHPERR_F MSIXADDRHPERR_V(1U) + +#define MSIXADDRLPERR_S 3 +#define MSIXADDRLPERR_V(x) ((x) << MSIXADDRLPERR_S) +#define MSIXADDRLPERR_F MSIXADDRLPERR_V(1U) + +#define MSIDATAPERR_S 2 +#define MSIDATAPERR_V(x) ((x) << MSIDATAPERR_S) +#define MSIDATAPERR_F MSIDATAPERR_V(1U) + +#define MSIADDRHPERR_S 1 +#define MSIADDRHPERR_V(x) ((x) << MSIADDRHPERR_S) +#define MSIADDRHPERR_F MSIADDRHPERR_V(1U) + +#define MSIADDRLPERR_S 0 +#define MSIADDRLPERR_V(x) ((x) << MSIADDRLPERR_S) +#define MSIADDRLPERR_F MSIADDRLPERR_V(1U) + +#define READRSPERR_S 29 +#define READRSPERR_V(x) ((x) << READRSPERR_S) +#define READRSPERR_F READRSPERR_V(1U) + +#define TRGT1GRPPERR_S 28 +#define TRGT1GRPPERR_V(x) ((x) << TRGT1GRPPERR_S) +#define TRGT1GRPPERR_F TRGT1GRPPERR_V(1U) + +#define IPSOTPERR_S 27 +#define IPSOTPERR_V(x) ((x) << IPSOTPERR_S) +#define IPSOTPERR_F IPSOTPERR_V(1U) + +#define IPRETRYPERR_S 26 +#define IPRETRYPERR_V(x) ((x) << IPRETRYPERR_S) +#define IPRETRYPERR_F IPRETRYPERR_V(1U) + +#define IPRXDATAGRPPERR_S 25 +#define IPRXDATAGRPPERR_V(x) ((x) << IPRXDATAGRPPERR_S) +#define IPRXDATAGRPPERR_F IPRXDATAGRPPERR_V(1U) + +#define IPRXHDRGRPPERR_S 24 +#define IPRXHDRGRPPERR_V(x) ((x) << IPRXHDRGRPPERR_S) +#define IPRXHDRGRPPERR_F IPRXHDRGRPPERR_V(1U) + +#define MAGRPPERR_S 22 +#define MAGRPPERR_V(x) ((x) << MAGRPPERR_S) +#define MAGRPPERR_F MAGRPPERR_V(1U) + +#define VFIDPERR_S 21 +#define VFIDPERR_V(x) ((x) << VFIDPERR_S) +#define VFIDPERR_F VFIDPERR_V(1U) + +#define HREQWRPERR_S 16 +#define HREQWRPERR_V(x) ((x) << HREQWRPERR_S) +#define HREQWRPERR_F HREQWRPERR_V(1U) + +#define DREQWRPERR_S 13 +#define DREQWRPERR_V(x) ((x) << DREQWRPERR_S) +#define DREQWRPERR_F DREQWRPERR_V(1U) + +#define CREQRDPERR_S 11 +#define CREQRDPERR_V(x) ((x) << CREQRDPERR_S) +#define CREQRDPERR_F CREQRDPERR_V(1U) + +#define MSTTAGQPERR_S 10 +#define MSTTAGQPERR_V(x) ((x) << MSTTAGQPERR_S) +#define MSTTAGQPERR_F MSTTAGQPERR_V(1U) + +#define PIOREQGRPPERR_S 8 +#define PIOREQGRPPERR_V(x) ((x) << PIOREQGRPPERR_S) +#define PIOREQGRPPERR_F PIOREQGRPPERR_V(1U) + +#define PIOCPLGRPPERR_S 7 +#define PIOCPLGRPPERR_V(x) ((x) << PIOCPLGRPPERR_S) +#define PIOCPLGRPPERR_F PIOCPLGRPPERR_V(1U) + +#define MSIXSTIPERR_S 2 +#define MSIXSTIPERR_V(x) ((x) << MSIXSTIPERR_S) +#define MSIXSTIPERR_F MSIXSTIPERR_V(1U) + +#define MSTTIMEOUTPERR_S 1 +#define MSTTIMEOUTPERR_V(x) ((x) << MSTTIMEOUTPERR_S) +#define MSTTIMEOUTPERR_F MSTTIMEOUTPERR_V(1U) + +#define MSTGRPPERR_S 0 +#define MSTGRPPERR_V(x) ((x) << MSTGRPPERR_S) +#define MSTGRPPERR_F MSTGRPPERR_V(1U) + +#define PCIE_NONFAT_ERR_A 0x3010 +#define PCIE_CFG_SPACE_REQ_A 0x3060 +#define PCIE_CFG_SPACE_DATA_A 0x3064 +#define PCIE_MEM_ACCESS_BASE_WIN_A 0x3068 + +#define PCIEOFST_S 10 +#define PCIEOFST_M 0x3fffffU +#define PCIEOFST_G(x) (((x) >> PCIEOFST_S) & PCIEOFST_M) + +#define BIR_S 8 +#define BIR_M 0x3U +#define BIR_V(x) ((x) << BIR_S) +#define BIR_G(x) (((x) >> BIR_S) & BIR_M) + +#define WINDOW_S 0 +#define WINDOW_M 0xffU +#define WINDOW_V(x) ((x) << WINDOW_S) +#define WINDOW_G(x) (((x) >> WINDOW_S) & WINDOW_M) + +#define PCIE_MEM_ACCESS_OFFSET_A 0x306c + +#define ENABLE_S 30 +#define ENABLE_V(x) ((x) << ENABLE_S) +#define ENABLE_F ENABLE_V(1U) + +#define LOCALCFG_S 28 +#define LOCALCFG_V(x) ((x) << LOCALCFG_S) +#define LOCALCFG_F LOCALCFG_V(1U) + +#define FUNCTION_S 12 +#define FUNCTION_V(x) ((x) << FUNCTION_S) + +#define REGISTER_S 0 +#define REGISTER_V(x) ((x) << REGISTER_S) + +#define PFNUM_S 0 +#define PFNUM_V(x) ((x) << PFNUM_S) + +#define PCIE_FW_A 0x30b8 + +#define PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS_A 0x5908 + +#define RNPP_S 31 +#define RNPP_V(x) ((x) << RNPP_S) +#define RNPP_F RNPP_V(1U) + +#define RPCP_S 29 +#define RPCP_V(x) ((x) << RPCP_S) +#define RPCP_F RPCP_V(1U) + +#define RCIP_S 27 +#define RCIP_V(x) ((x) << RCIP_S) +#define RCIP_F RCIP_V(1U) + +#define RCCP_S 26 +#define RCCP_V(x) ((x) << RCCP_S) +#define RCCP_F RCCP_V(1U) + +#define RFTP_S 23 +#define RFTP_V(x) ((x) << RFTP_S) +#define RFTP_F RFTP_V(1U) + +#define PTRP_S 20 +#define PTRP_V(x) ((x) << PTRP_S) +#define PTRP_F PTRP_V(1U) -#define S_HP_INT_THRESH 28 -#define M_HP_INT_THRESH 0xfU -#define V_HP_INT_THRESH(x) ((x) << S_HP_INT_THRESH) -#define S_LP_INT_THRESH_T5 18 -#define V_LP_INT_THRESH_T5(x) ((x) << S_LP_INT_THRESH_T5) -#define M_LP_COUNT_T5 0x3ffffU -#define G_LP_COUNT_T5(x) (((x) >> S_LP_COUNT) & M_LP_COUNT_T5) -#define M_HP_COUNT 0x7ffU -#define S_HP_COUNT 16 -#define G_HP_COUNT(x) (((x) >> S_HP_COUNT) & M_HP_COUNT) -#define S_LP_INT_THRESH 12 -#define M_LP_INT_THRESH 0xfU -#define M_LP_INT_THRESH_T5 0xfffU -#define V_LP_INT_THRESH(x) ((x) << S_LP_INT_THRESH) -#define M_LP_COUNT 0x7ffU -#define S_LP_COUNT 0 -#define G_LP_COUNT(x) (((x) >> S_LP_COUNT) & M_LP_COUNT) -#define A_SGE_DBFIFO_STATUS 0x10a4 - -#define SGE_STAT_TOTAL 0x10e4 -#define SGE_STAT_MATCH 0x10e8 - -#define SGE_STAT_CFG 0x10ec -#define S_STATSOURCE_T5 9 -#define STATSOURCE_T5(x) ((x) << S_STATSOURCE_T5) - -#define SGE_DBFIFO_STATUS2 0x1118 -#define M_HP_COUNT_T5 0x3ffU -#define G_HP_COUNT_T5(x) ((x) & M_HP_COUNT_T5) -#define S_HP_INT_THRESH_T5 10 -#define M_HP_INT_THRESH_T5 0xfU -#define V_HP_INT_THRESH_T5(x) ((x) << S_HP_INT_THRESH_T5) - -#define S_ENABLE_DROP 13 -#define V_ENABLE_DROP(x) ((x) << S_ENABLE_DROP) -#define F_ENABLE_DROP V_ENABLE_DROP(1U) -#define S_DROPPED_DB 0 -#define V_DROPPED_DB(x) ((x) << S_DROPPED_DB) -#define F_DROPPED_DB V_DROPPED_DB(1U) -#define A_SGE_DOORBELL_CONTROL 0x10a8 - -#define A_SGE_CTXT_CMD 0x11fc -#define A_SGE_DBQ_CTXT_BADDR 0x1084 - -#define PCIE_PF_CFG 0x40 -#define AIVEC(x) ((x) << 4) -#define AIVEC_MASK 0x3ffU - -#define PCIE_PF_CLI 0x44 -#define PCIE_INT_CAUSE 0x3004 -#define UNXSPLCPLERR 0x20000000U -#define PCIEPINT 0x10000000U -#define PCIESINT 0x08000000U -#define RPLPERR 0x04000000U -#define RXWRPERR 0x02000000U -#define RXCPLPERR 0x01000000U -#define PIOTAGPERR 0x00800000U -#define MATAGPERR 0x00400000U -#define INTXCLRPERR 0x00200000U -#define FIDPERR 0x00100000U -#define CFGSNPPERR 0x00080000U -#define HRSPPERR 0x00040000U -#define HREQPERR 0x00020000U -#define HCNTPERR 0x00010000U -#define DRSPPERR 0x00008000U -#define DREQPERR 0x00004000U -#define DCNTPERR 0x00002000U -#define CRSPPERR 0x00001000U -#define CREQPERR 0x00000800U -#define CCNTPERR 0x00000400U -#define TARTAGPERR 0x00000200U -#define PIOREQPERR 0x00000100U -#define PIOCPLPERR 0x00000080U -#define MSIXDIPERR 0x00000040U -#define MSIXDATAPERR 0x00000020U -#define MSIXADDRHPERR 0x00000010U -#define MSIXADDRLPERR 0x00000008U -#define MSIDATAPERR 0x00000004U -#define MSIADDRHPERR 0x00000002U -#define MSIADDRLPERR 0x00000001U - -#define READRSPERR 0x20000000U -#define TRGT1GRPPERR 0x10000000U -#define IPSOTPERR 0x08000000U -#define IPRXDATAGRPPERR 0x02000000U -#define IPRXHDRGRPPERR 0x01000000U -#define MAGRPPERR 0x00400000U -#define VFIDPERR 0x00200000U -#define HREQWRPERR 0x00010000U -#define DREQWRPERR 0x00002000U -#define MSTTAGQPERR 0x00000400U -#define PIOREQGRPPERR 0x00000100U -#define PIOCPLGRPPERR 0x00000080U -#define MSIXSTIPERR 0x00000004U -#define MSTTIMEOUTPERR 0x00000002U -#define MSTGRPPERR 0x00000001U - -#define PCIE_NONFAT_ERR 0x3010 -#define PCIE_CFG_SPACE_REQ 0x3060 -#define PCIE_CFG_SPACE_DATA 0x3064 -#define PCIE_MEM_ACCESS_BASE_WIN 0x3068 -#define S_PCIEOFST 10 -#define M_PCIEOFST 0x3fffffU -#define GET_PCIEOFST(x) (((x) >> S_PCIEOFST) & M_PCIEOFST) -#define PCIEOFST_MASK 0xfffffc00U -#define BIR_MASK 0x00000300U -#define BIR_SHIFT 8 -#define BIR(x) ((x) << BIR_SHIFT) -#define WINDOW_MASK 0x000000ffU -#define WINDOW_SHIFT 0 -#define WINDOW(x) ((x) << WINDOW_SHIFT) -#define GET_WINDOW(x) (((x) >> WINDOW_SHIFT) & WINDOW_MASK) -#define PCIE_MEM_ACCESS_OFFSET 0x306c -#define ENABLE (1U << 30) -#define FUNCTION(x) ((x) << 12) -#define F_LOCALCFG (1U << 28) - -#define S_PFNUM 0 -#define V_PFNUM(x) ((x) << S_PFNUM) - -#define PCIE_FW 0x30b8 -#define PCIE_FW_ERR 0x80000000U -#define PCIE_FW_INIT 0x40000000U -#define PCIE_FW_HALT 0x20000000U -#define PCIE_FW_MASTER_VLD 0x00008000U -#define PCIE_FW_MASTER(x) ((x) << 12) -#define PCIE_FW_MASTER_MASK 0x7 -#define PCIE_FW_MASTER_GET(x) (((x) >> 12) & PCIE_FW_MASTER_MASK) - -#define PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS 0x5908 -#define RNPP 0x80000000U -#define RPCP 0x20000000U -#define RCIP 0x08000000U -#define RCCP 0x04000000U -#define RFTP 0x00800000U -#define PTRP 0x00100000U - -#define PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS 0x59a4 -#define TPCP 0x40000000U -#define TNPP 0x20000000U -#define TFTP 0x10000000U -#define TCAP 0x08000000U -#define TCIP 0x04000000U -#define RCAP 0x02000000U -#define PLUP 0x00800000U -#define PLDN 0x00400000U -#define OTDD 0x00200000U -#define GTRP 0x00100000U -#define RDPE 0x00040000U -#define TDCE 0x00020000U -#define TDUE 0x00010000U - -#define MC_INT_CAUSE 0x7518 -#define MC_P_INT_CAUSE 0x41318 -#define ECC_UE_INT_CAUSE 0x00000004U -#define ECC_CE_INT_CAUSE 0x00000002U -#define PERR_INT_CAUSE 0x00000001U - -#define MC_ECC_STATUS 0x751c -#define MC_P_ECC_STATUS 0x4131c -#define ECC_CECNT_MASK 0xffff0000U -#define ECC_CECNT_SHIFT 16 -#define ECC_CECNT(x) ((x) << ECC_CECNT_SHIFT) -#define ECC_CECNT_GET(x) (((x) & ECC_CECNT_MASK) >> ECC_CECNT_SHIFT) -#define ECC_UECNT_MASK 0x0000ffffU -#define ECC_UECNT_SHIFT 0 -#define ECC_UECNT(x) ((x) << ECC_UECNT_SHIFT) -#define ECC_UECNT_GET(x) (((x) & ECC_UECNT_MASK) >> ECC_UECNT_SHIFT) - -#define MC_BIST_CMD 0x7600 -#define START_BIST 0x80000000U -#define BIST_CMD_GAP_MASK 0x0000ff00U -#define BIST_CMD_GAP_SHIFT 8 -#define BIST_CMD_GAP(x) ((x) << BIST_CMD_GAP_SHIFT) -#define BIST_OPCODE_MASK 0x00000003U -#define BIST_OPCODE_SHIFT 0 -#define BIST_OPCODE(x) ((x) << BIST_OPCODE_SHIFT) - -#define MC_BIST_CMD_ADDR 0x7604 -#define MC_BIST_CMD_LEN 0x7608 -#define MC_BIST_DATA_PATTERN 0x760c -#define BIST_DATA_TYPE_MASK 0x0000000fU -#define BIST_DATA_TYPE_SHIFT 0 -#define BIST_DATA_TYPE(x) ((x) << BIST_DATA_TYPE_SHIFT) - -#define MC_BIST_STATUS_RDATA 0x7688 +#define PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS_A 0x59a4 +#define TPCP_S 30 +#define TPCP_V(x) ((x) << TPCP_S) +#define TPCP_F TPCP_V(1U) + +#define TNPP_S 29 +#define TNPP_V(x) ((x) << TNPP_S) +#define TNPP_F TNPP_V(1U) + +#define TFTP_S 28 +#define TFTP_V(x) ((x) << TFTP_S) +#define TFTP_F TFTP_V(1U) + +#define TCAP_S 27 +#define TCAP_V(x) ((x) << TCAP_S) +#define TCAP_F TCAP_V(1U) + +#define TCIP_S 26 +#define TCIP_V(x) ((x) << TCIP_S) +#define TCIP_F TCIP_V(1U) + +#define RCAP_S 25 +#define RCAP_V(x) ((x) << RCAP_S) +#define RCAP_F RCAP_V(1U) + +#define PLUP_S 23 +#define PLUP_V(x) ((x) << PLUP_S) +#define PLUP_F PLUP_V(1U) + +#define PLDN_S 22 +#define PLDN_V(x) ((x) << PLDN_S) +#define PLDN_F PLDN_V(1U) + +#define OTDD_S 21 +#define OTDD_V(x) ((x) << OTDD_S) +#define OTDD_F OTDD_V(1U) + +#define GTRP_S 20 +#define GTRP_V(x) ((x) << GTRP_S) +#define GTRP_F GTRP_V(1U) + +#define RDPE_S 18 +#define RDPE_V(x) ((x) << RDPE_S) +#define RDPE_F RDPE_V(1U) + +#define TDCE_S 17 +#define TDCE_V(x) ((x) << TDCE_S) +#define TDCE_F TDCE_V(1U) + +#define TDUE_S 16 +#define TDUE_V(x) ((x) << TDUE_S) +#define TDUE_F TDUE_V(1U) + +/* registers for module MC */ +#define MC_INT_CAUSE_A 0x7518 +#define MC_P_INT_CAUSE_A 0x41318 + +#define ECC_UE_INT_CAUSE_S 2 +#define ECC_UE_INT_CAUSE_V(x) ((x) << ECC_UE_INT_CAUSE_S) +#define ECC_UE_INT_CAUSE_F ECC_UE_INT_CAUSE_V(1U) + +#define ECC_CE_INT_CAUSE_S 1 +#define ECC_CE_INT_CAUSE_V(x) ((x) << ECC_CE_INT_CAUSE_S) +#define ECC_CE_INT_CAUSE_F ECC_CE_INT_CAUSE_V(1U) + +#define PERR_INT_CAUSE_S 0 +#define PERR_INT_CAUSE_V(x) ((x) << PERR_INT_CAUSE_S) +#define PERR_INT_CAUSE_F PERR_INT_CAUSE_V(1U) + +#define MC_ECC_STATUS_A 0x751c +#define MC_P_ECC_STATUS_A 0x4131c + +#define ECC_CECNT_S 16 +#define ECC_CECNT_M 0xffffU +#define ECC_CECNT_V(x) ((x) << ECC_CECNT_S) +#define ECC_CECNT_G(x) (((x) >> ECC_CECNT_S) & ECC_CECNT_M) + +#define ECC_UECNT_S 0 +#define ECC_UECNT_M 0xffffU +#define ECC_UECNT_V(x) ((x) << ECC_UECNT_S) +#define ECC_UECNT_G(x) (((x) >> ECC_UECNT_S) & ECC_UECNT_M) + +#define MC_BIST_CMD_A 0x7600 + +#define START_BIST_S 31 +#define START_BIST_V(x) ((x) << START_BIST_S) +#define START_BIST_F START_BIST_V(1U) + +#define BIST_CMD_GAP_S 8 +#define BIST_CMD_GAP_V(x) ((x) << BIST_CMD_GAP_S) + +#define BIST_OPCODE_S 0 +#define BIST_OPCODE_V(x) ((x) << BIST_OPCODE_S) + +#define MC_BIST_CMD_ADDR_A 0x7604 +#define MC_BIST_CMD_LEN_A 0x7608 +#define MC_BIST_DATA_PATTERN_A 0x760c + +#define MC_BIST_STATUS_RDATA_A 0x7688 + +/* registers for module MA */ #define MA_EDRAM0_BAR_A 0x77c0 #define EDRAM0_SIZE_S 0 @@ -574,263 +893,608 @@ #define EXT_MEM0_ENABLE_V(x) ((x) << EXT_MEM0_ENABLE_S) #define EXT_MEM0_ENABLE_F EXT_MEM0_ENABLE_V(1U) -#define MA_INT_CAUSE 0x77e0 -#define MEM_PERR_INT_CAUSE 0x00000002U -#define MEM_WRAP_INT_CAUSE 0x00000001U - -#define MA_INT_WRAP_STATUS 0x77e4 -#define MEM_WRAP_ADDRESS_MASK 0xfffffff0U -#define MEM_WRAP_ADDRESS_SHIFT 4 -#define MEM_WRAP_ADDRESS_GET(x) (((x) & MEM_WRAP_ADDRESS_MASK) >> MEM_WRAP_ADDRESS_SHIFT) -#define MEM_WRAP_CLIENT_NUM_MASK 0x0000000fU -#define MEM_WRAP_CLIENT_NUM_SHIFT 0 -#define MEM_WRAP_CLIENT_NUM_GET(x) (((x) & MEM_WRAP_CLIENT_NUM_MASK) >> MEM_WRAP_CLIENT_NUM_SHIFT) -#define MA_PCIE_FW 0x30b8 -#define MA_PARITY_ERROR_STATUS 0x77f4 -#define MA_PARITY_ERROR_STATUS2 0x7804 - -#define EDC_0_BASE_ADDR 0x7900 - -#define EDC_BIST_CMD 0x7904 -#define EDC_BIST_CMD_ADDR 0x7908 -#define EDC_BIST_CMD_LEN 0x790c -#define EDC_BIST_DATA_PATTERN 0x7910 -#define EDC_BIST_STATUS_RDATA 0x7928 -#define EDC_INT_CAUSE 0x7978 -#define ECC_UE_PAR 0x00000020U -#define ECC_CE_PAR 0x00000010U -#define PERR_PAR_CAUSE 0x00000008U - -#define EDC_ECC_STATUS 0x797c - -#define EDC_1_BASE_ADDR 0x7980 - -#define CIM_BOOT_CFG 0x7b00 -#define BOOTADDR_MASK 0xffffff00U -#define UPCRST 0x1U - -#define CIM_PF_MAILBOX_DATA 0x240 -#define CIM_PF_MAILBOX_CTRL 0x280 -#define MBMSGVALID 0x00000008U -#define MBINTREQ 0x00000004U -#define MBOWNER_MASK 0x00000003U -#define MBOWNER_SHIFT 0 -#define MBOWNER(x) ((x) << MBOWNER_SHIFT) -#define MBOWNER_GET(x) (((x) & MBOWNER_MASK) >> MBOWNER_SHIFT) - -#define CIM_PF_HOST_INT_ENABLE 0x288 -#define MBMSGRDYINTEN(x) ((x) << 19) - -#define CIM_PF_HOST_INT_CAUSE 0x28c -#define MBMSGRDYINT 0x00080000U - -#define CIM_HOST_INT_CAUSE 0x7b2c -#define TIEQOUTPARERRINT 0x00100000U -#define TIEQINPARERRINT 0x00080000U -#define MBHOSTPARERR 0x00040000U -#define MBUPPARERR 0x00020000U -#define IBQPARERR 0x0001f800U -#define IBQTP0PARERR 0x00010000U -#define IBQTP1PARERR 0x00008000U -#define IBQULPPARERR 0x00004000U -#define IBQSGELOPARERR 0x00002000U -#define IBQSGEHIPARERR 0x00001000U -#define IBQNCSIPARERR 0x00000800U -#define OBQPARERR 0x000007e0U -#define OBQULP0PARERR 0x00000400U -#define OBQULP1PARERR 0x00000200U -#define OBQULP2PARERR 0x00000100U -#define OBQULP3PARERR 0x00000080U -#define OBQSGEPARERR 0x00000040U -#define OBQNCSIPARERR 0x00000020U -#define PREFDROPINT 0x00000002U -#define UPACCNONZERO 0x00000001U - -#define CIM_HOST_UPACC_INT_CAUSE 0x7b34 -#define EEPROMWRINT 0x40000000U -#define TIMEOUTMAINT 0x20000000U -#define TIMEOUTINT 0x10000000U -#define RSPOVRLOOKUPINT 0x08000000U -#define REQOVRLOOKUPINT 0x04000000U -#define BLKWRPLINT 0x02000000U -#define BLKRDPLINT 0x01000000U -#define SGLWRPLINT 0x00800000U -#define SGLRDPLINT 0x00400000U -#define BLKWRCTLINT 0x00200000U -#define BLKRDCTLINT 0x00100000U -#define SGLWRCTLINT 0x00080000U -#define SGLRDCTLINT 0x00040000U -#define BLKWREEPROMINT 0x00020000U -#define BLKRDEEPROMINT 0x00010000U -#define SGLWREEPROMINT 0x00008000U -#define SGLRDEEPROMINT 0x00004000U -#define BLKWRFLASHINT 0x00002000U -#define BLKRDFLASHINT 0x00001000U -#define SGLWRFLASHINT 0x00000800U -#define SGLRDFLASHINT 0x00000400U -#define BLKWRBOOTINT 0x00000200U -#define BLKRDBOOTINT 0x00000100U -#define SGLWRBOOTINT 0x00000080U -#define SGLRDBOOTINT 0x00000040U -#define ILLWRBEINT 0x00000020U -#define ILLRDBEINT 0x00000010U -#define ILLRDINT 0x00000008U -#define ILLWRINT 0x00000004U -#define ILLTRANSINT 0x00000002U -#define RSVDSPACEINT 0x00000001U - -#define TP_OUT_CONFIG 0x7d04 -#define VLANEXTENABLE_MASK 0x0000f000U -#define VLANEXTENABLE_SHIFT 12 - -#define TP_GLOBAL_CONFIG 0x7d08 -#define FIVETUPLELOOKUP_SHIFT 17 -#define FIVETUPLELOOKUP_MASK 0x00060000U -#define FIVETUPLELOOKUP(x) ((x) << FIVETUPLELOOKUP_SHIFT) -#define FIVETUPLELOOKUP_GET(x) (((x) & FIVETUPLELOOKUP_MASK) >> \ - FIVETUPLELOOKUP_SHIFT) - -#define TP_PARA_REG2 0x7d68 -#define MAXRXDATA_MASK 0xffff0000U -#define MAXRXDATA_SHIFT 16 -#define MAXRXDATA_GET(x) (((x) & MAXRXDATA_MASK) >> MAXRXDATA_SHIFT) - -#define TP_TIMER_RESOLUTION 0x7d90 -#define TIMERRESOLUTION_MASK 0x00ff0000U -#define TIMERRESOLUTION_SHIFT 16 -#define TIMERRESOLUTION_GET(x) (((x) & TIMERRESOLUTION_MASK) >> TIMERRESOLUTION_SHIFT) -#define DELAYEDACKRESOLUTION_MASK 0x000000ffU -#define DELAYEDACKRESOLUTION_SHIFT 0 -#define DELAYEDACKRESOLUTION_GET(x) \ - (((x) & DELAYEDACKRESOLUTION_MASK) >> DELAYEDACKRESOLUTION_SHIFT) - -#define TP_SHIFT_CNT 0x7dc0 -#define SYNSHIFTMAX_SHIFT 24 -#define SYNSHIFTMAX_MASK 0xff000000U -#define SYNSHIFTMAX(x) ((x) << SYNSHIFTMAX_SHIFT) -#define SYNSHIFTMAX_GET(x) (((x) & SYNSHIFTMAX_MASK) >> \ - SYNSHIFTMAX_SHIFT) -#define RXTSHIFTMAXR1_SHIFT 20 -#define RXTSHIFTMAXR1_MASK 0x00f00000U -#define RXTSHIFTMAXR1(x) ((x) << RXTSHIFTMAXR1_SHIFT) -#define RXTSHIFTMAXR1_GET(x) (((x) & RXTSHIFTMAXR1_MASK) >> \ - RXTSHIFTMAXR1_SHIFT) -#define RXTSHIFTMAXR2_SHIFT 16 -#define RXTSHIFTMAXR2_MASK 0x000f0000U -#define RXTSHIFTMAXR2(x) ((x) << RXTSHIFTMAXR2_SHIFT) -#define RXTSHIFTMAXR2_GET(x) (((x) & RXTSHIFTMAXR2_MASK) >> \ - RXTSHIFTMAXR2_SHIFT) -#define PERSHIFTBACKOFFMAX_SHIFT 12 -#define PERSHIFTBACKOFFMAX_MASK 0x0000f000U -#define PERSHIFTBACKOFFMAX(x) ((x) << PERSHIFTBACKOFFMAX_SHIFT) -#define PERSHIFTBACKOFFMAX_GET(x) (((x) & PERSHIFTBACKOFFMAX_MASK) >> \ - PERSHIFTBACKOFFMAX_SHIFT) -#define PERSHIFTMAX_SHIFT 8 -#define PERSHIFTMAX_MASK 0x00000f00U -#define PERSHIFTMAX(x) ((x) << PERSHIFTMAX_SHIFT) -#define PERSHIFTMAX_GET(x) (((x) & PERSHIFTMAX_MASK) >> \ - PERSHIFTMAX_SHIFT) -#define KEEPALIVEMAXR1_SHIFT 4 -#define KEEPALIVEMAXR1_MASK 0x000000f0U -#define KEEPALIVEMAXR1(x) ((x) << KEEPALIVEMAXR1_SHIFT) -#define KEEPALIVEMAXR1_GET(x) (((x) & KEEPALIVEMAXR1_MASK) >> \ - KEEPALIVEMAXR1_SHIFT) -#define KEEPALIVEMAXR2_SHIFT 0 -#define KEEPALIVEMAXR2_MASK 0x0000000fU -#define KEEPALIVEMAXR2(x) ((x) << KEEPALIVEMAXR2_SHIFT) -#define KEEPALIVEMAXR2_GET(x) (((x) & KEEPALIVEMAXR2_MASK) >> \ - KEEPALIVEMAXR2_SHIFT) - -#define TP_CCTRL_TABLE 0x7ddc -#define TP_MTU_TABLE 0x7de4 -#define MTUINDEX_MASK 0xff000000U -#define MTUINDEX_SHIFT 24 -#define MTUINDEX(x) ((x) << MTUINDEX_SHIFT) -#define MTUWIDTH_MASK 0x000f0000U -#define MTUWIDTH_SHIFT 16 -#define MTUWIDTH(x) ((x) << MTUWIDTH_SHIFT) -#define MTUWIDTH_GET(x) (((x) & MTUWIDTH_MASK) >> MTUWIDTH_SHIFT) -#define MTUVALUE_MASK 0x00003fffU -#define MTUVALUE_SHIFT 0 -#define MTUVALUE(x) ((x) << MTUVALUE_SHIFT) -#define MTUVALUE_GET(x) (((x) & MTUVALUE_MASK) >> MTUVALUE_SHIFT) - -#define TP_RSS_LKP_TABLE 0x7dec -#define LKPTBLROWVLD 0x80000000U -#define LKPTBLQUEUE1_MASK 0x000ffc00U -#define LKPTBLQUEUE1_SHIFT 10 -#define LKPTBLQUEUE1(x) ((x) << LKPTBLQUEUE1_SHIFT) -#define LKPTBLQUEUE1_GET(x) (((x) & LKPTBLQUEUE1_MASK) >> LKPTBLQUEUE1_SHIFT) -#define LKPTBLQUEUE0_MASK 0x000003ffU -#define LKPTBLQUEUE0_SHIFT 0 -#define LKPTBLQUEUE0(x) ((x) << LKPTBLQUEUE0_SHIFT) -#define LKPTBLQUEUE0_GET(x) (((x) & LKPTBLQUEUE0_MASK) >> LKPTBLQUEUE0_SHIFT) - -#define TP_PIO_ADDR 0x7e40 -#define TP_PIO_DATA 0x7e44 -#define TP_MIB_INDEX 0x7e50 -#define TP_MIB_DATA 0x7e54 -#define TP_INT_CAUSE 0x7e74 -#define FLMTXFLSTEMPTY 0x40000000U - -#define TP_VLAN_PRI_MAP 0x140 -#define FRAGMENTATION_SHIFT 9 -#define FRAGMENTATION_MASK 0x00000200U -#define MPSHITTYPE_MASK 0x00000100U -#define MACMATCH_MASK 0x00000080U -#define ETHERTYPE_MASK 0x00000040U -#define PROTOCOL_MASK 0x00000020U -#define TOS_MASK 0x00000010U -#define VLAN_MASK 0x00000008U -#define VNIC_ID_MASK 0x00000004U -#define PORT_MASK 0x00000002U -#define FCOE_SHIFT 0 -#define FCOE_MASK 0x00000001U - -#define TP_INGRESS_CONFIG 0x141 -#define VNIC 0x00000800U -#define CSUM_HAS_PSEUDO_HDR 0x00000400U -#define RM_OVLAN 0x00000200U -#define LOOKUPEVERYPKT 0x00000100U - -#define TP_MIB_MAC_IN_ERR_0 0x0 -#define TP_MIB_TCP_OUT_RST 0xc -#define TP_MIB_TCP_IN_SEG_HI 0x10 -#define TP_MIB_TCP_IN_SEG_LO 0x11 -#define TP_MIB_TCP_OUT_SEG_HI 0x12 -#define TP_MIB_TCP_OUT_SEG_LO 0x13 -#define TP_MIB_TCP_RXT_SEG_HI 0x14 -#define TP_MIB_TCP_RXT_SEG_LO 0x15 -#define TP_MIB_TNL_CNG_DROP_0 0x18 -#define TP_MIB_TCP_V6IN_ERR_0 0x28 -#define TP_MIB_TCP_V6OUT_RST 0x2c -#define TP_MIB_OFD_ARP_DROP 0x36 -#define TP_MIB_TNL_DROP_0 0x44 -#define TP_MIB_OFD_VLN_DROP_0 0x58 - -#define ULP_TX_INT_CAUSE 0x8dcc -#define PBL_BOUND_ERR_CH3 0x80000000U -#define PBL_BOUND_ERR_CH2 0x40000000U -#define PBL_BOUND_ERR_CH1 0x20000000U -#define PBL_BOUND_ERR_CH0 0x10000000U - -#define PM_RX_INT_CAUSE 0x8fdc -#define ZERO_E_CMD_ERROR 0x00400000U -#define PMRX_FRAMING_ERROR 0x003ffff0U -#define OCSPI_PAR_ERROR 0x00000008U -#define DB_OPTIONS_PAR_ERROR 0x00000004U -#define IESPI_PAR_ERROR 0x00000002U -#define E_PCMD_PAR_ERROR 0x00000001U - -#define PM_TX_INT_CAUSE 0x8ffc -#define PCMD_LEN_OVFL0 0x80000000U -#define PCMD_LEN_OVFL1 0x40000000U -#define PCMD_LEN_OVFL2 0x20000000U -#define ZERO_C_CMD_ERROR 0x10000000U -#define PMTX_FRAMING_ERROR 0x0ffffff0U -#define OESPI_PAR_ERROR 0x00000008U -#define ICSPI_PAR_ERROR 0x00000002U -#define C_PCMD_PAR_ERROR 0x00000001U +#define MA_INT_CAUSE_A 0x77e0 + +#define MEM_PERR_INT_CAUSE_S 1 +#define MEM_PERR_INT_CAUSE_V(x) ((x) << MEM_PERR_INT_CAUSE_S) +#define MEM_PERR_INT_CAUSE_F MEM_PERR_INT_CAUSE_V(1U) + +#define MEM_WRAP_INT_CAUSE_S 0 +#define MEM_WRAP_INT_CAUSE_V(x) ((x) << MEM_WRAP_INT_CAUSE_S) +#define MEM_WRAP_INT_CAUSE_F MEM_WRAP_INT_CAUSE_V(1U) + +#define MA_INT_WRAP_STATUS_A 0x77e4 + +#define MEM_WRAP_ADDRESS_S 4 +#define MEM_WRAP_ADDRESS_M 0xfffffffU +#define MEM_WRAP_ADDRESS_G(x) (((x) >> MEM_WRAP_ADDRESS_S) & MEM_WRAP_ADDRESS_M) + +#define MEM_WRAP_CLIENT_NUM_S 0 +#define MEM_WRAP_CLIENT_NUM_M 0xfU +#define MEM_WRAP_CLIENT_NUM_G(x) \ + (((x) >> MEM_WRAP_CLIENT_NUM_S) & MEM_WRAP_CLIENT_NUM_M) + +#define MA_PARITY_ERROR_STATUS_A 0x77f4 +#define MA_PARITY_ERROR_STATUS1_A 0x77f4 +#define MA_PARITY_ERROR_STATUS2_A 0x7804 + +/* registers for module EDC_0 */ +#define EDC_0_BASE_ADDR 0x7900 + +#define EDC_BIST_CMD_A 0x7904 +#define EDC_BIST_CMD_ADDR_A 0x7908 +#define EDC_BIST_CMD_LEN_A 0x790c +#define EDC_BIST_DATA_PATTERN_A 0x7910 +#define EDC_BIST_STATUS_RDATA_A 0x7928 +#define EDC_INT_CAUSE_A 0x7978 + +#define ECC_UE_PAR_S 5 +#define ECC_UE_PAR_V(x) ((x) << ECC_UE_PAR_S) +#define ECC_UE_PAR_F ECC_UE_PAR_V(1U) + +#define ECC_CE_PAR_S 4 +#define ECC_CE_PAR_V(x) ((x) << ECC_CE_PAR_S) +#define ECC_CE_PAR_F ECC_CE_PAR_V(1U) + +#define PERR_PAR_CAUSE_S 3 +#define PERR_PAR_CAUSE_V(x) ((x) << PERR_PAR_CAUSE_S) +#define PERR_PAR_CAUSE_F PERR_PAR_CAUSE_V(1U) + +#define EDC_ECC_STATUS_A 0x797c + +/* registers for module EDC_1 */ +#define EDC_1_BASE_ADDR 0x7980 + +/* registers for module CIM */ +#define CIM_BOOT_CFG_A 0x7b00 +#define CIM_PF_MAILBOX_CTRL_SHADOW_COPY_A 0x290 + +#define BOOTADDR_M 0xffffff00U + +#define UPCRST_S 0 +#define UPCRST_V(x) ((x) << UPCRST_S) +#define UPCRST_F UPCRST_V(1U) + +#define CIM_PF_MAILBOX_DATA_A 0x240 +#define CIM_PF_MAILBOX_CTRL_A 0x280 + +#define MBMSGVALID_S 3 +#define MBMSGVALID_V(x) ((x) << MBMSGVALID_S) +#define MBMSGVALID_F MBMSGVALID_V(1U) + +#define MBINTREQ_S 2 +#define MBINTREQ_V(x) ((x) << MBINTREQ_S) +#define MBINTREQ_F MBINTREQ_V(1U) + +#define MBOWNER_S 0 +#define MBOWNER_M 0x3U +#define MBOWNER_V(x) ((x) << MBOWNER_S) +#define MBOWNER_G(x) (((x) >> MBOWNER_S) & MBOWNER_M) + +#define CIM_PF_HOST_INT_ENABLE_A 0x288 + +#define MBMSGRDYINTEN_S 19 +#define MBMSGRDYINTEN_V(x) ((x) << MBMSGRDYINTEN_S) +#define MBMSGRDYINTEN_F MBMSGRDYINTEN_V(1U) + +#define CIM_PF_HOST_INT_CAUSE_A 0x28c + +#define MBMSGRDYINT_S 19 +#define MBMSGRDYINT_V(x) ((x) << MBMSGRDYINT_S) +#define MBMSGRDYINT_F MBMSGRDYINT_V(1U) + +#define CIM_HOST_INT_CAUSE_A 0x7b2c + +#define TIEQOUTPARERRINT_S 20 +#define TIEQOUTPARERRINT_V(x) ((x) << TIEQOUTPARERRINT_S) +#define TIEQOUTPARERRINT_F TIEQOUTPARERRINT_V(1U) + +#define TIEQINPARERRINT_S 19 +#define TIEQINPARERRINT_V(x) ((x) << TIEQINPARERRINT_S) +#define TIEQINPARERRINT_F TIEQINPARERRINT_V(1U) + +#define PREFDROPINT_S 1 +#define PREFDROPINT_V(x) ((x) << PREFDROPINT_S) +#define PREFDROPINT_F PREFDROPINT_V(1U) + +#define UPACCNONZERO_S 0 +#define UPACCNONZERO_V(x) ((x) << UPACCNONZERO_S) +#define UPACCNONZERO_F UPACCNONZERO_V(1U) + +#define MBHOSTPARERR_S 18 +#define MBHOSTPARERR_V(x) ((x) << MBHOSTPARERR_S) +#define MBHOSTPARERR_F MBHOSTPARERR_V(1U) + +#define MBUPPARERR_S 17 +#define MBUPPARERR_V(x) ((x) << MBUPPARERR_S) +#define MBUPPARERR_F MBUPPARERR_V(1U) + +#define IBQTP0PARERR_S 16 +#define IBQTP0PARERR_V(x) ((x) << IBQTP0PARERR_S) +#define IBQTP0PARERR_F IBQTP0PARERR_V(1U) + +#define IBQTP1PARERR_S 15 +#define IBQTP1PARERR_V(x) ((x) << IBQTP1PARERR_S) +#define IBQTP1PARERR_F IBQTP1PARERR_V(1U) + +#define IBQULPPARERR_S 14 +#define IBQULPPARERR_V(x) ((x) << IBQULPPARERR_S) +#define IBQULPPARERR_F IBQULPPARERR_V(1U) + +#define IBQSGELOPARERR_S 13 +#define IBQSGELOPARERR_V(x) ((x) << IBQSGELOPARERR_S) +#define IBQSGELOPARERR_F IBQSGELOPARERR_V(1U) + +#define IBQSGEHIPARERR_S 12 +#define IBQSGEHIPARERR_V(x) ((x) << IBQSGEHIPARERR_S) +#define IBQSGEHIPARERR_F IBQSGEHIPARERR_V(1U) + +#define IBQNCSIPARERR_S 11 +#define IBQNCSIPARERR_V(x) ((x) << IBQNCSIPARERR_S) +#define IBQNCSIPARERR_F IBQNCSIPARERR_V(1U) + +#define OBQULP0PARERR_S 10 +#define OBQULP0PARERR_V(x) ((x) << OBQULP0PARERR_S) +#define OBQULP0PARERR_F OBQULP0PARERR_V(1U) + +#define OBQULP1PARERR_S 9 +#define OBQULP1PARERR_V(x) ((x) << OBQULP1PARERR_S) +#define OBQULP1PARERR_F OBQULP1PARERR_V(1U) + +#define OBQULP2PARERR_S 8 +#define OBQULP2PARERR_V(x) ((x) << OBQULP2PARERR_S) +#define OBQULP2PARERR_F OBQULP2PARERR_V(1U) + +#define OBQULP3PARERR_S 7 +#define OBQULP3PARERR_V(x) ((x) << OBQULP3PARERR_S) +#define OBQULP3PARERR_F OBQULP3PARERR_V(1U) + +#define OBQSGEPARERR_S 6 +#define OBQSGEPARERR_V(x) ((x) << OBQSGEPARERR_S) +#define OBQSGEPARERR_F OBQSGEPARERR_V(1U) + +#define OBQNCSIPARERR_S 5 +#define OBQNCSIPARERR_V(x) ((x) << OBQNCSIPARERR_S) +#define OBQNCSIPARERR_F OBQNCSIPARERR_V(1U) + +#define CIM_HOST_UPACC_INT_CAUSE_A 0x7b34 + +#define EEPROMWRINT_S 30 +#define EEPROMWRINT_V(x) ((x) << EEPROMWRINT_S) +#define EEPROMWRINT_F EEPROMWRINT_V(1U) + +#define TIMEOUTMAINT_S 29 +#define TIMEOUTMAINT_V(x) ((x) << TIMEOUTMAINT_S) +#define TIMEOUTMAINT_F TIMEOUTMAINT_V(1U) + +#define TIMEOUTINT_S 28 +#define TIMEOUTINT_V(x) ((x) << TIMEOUTINT_S) +#define TIMEOUTINT_F TIMEOUTINT_V(1U) + +#define RSPOVRLOOKUPINT_S 27 +#define RSPOVRLOOKUPINT_V(x) ((x) << RSPOVRLOOKUPINT_S) +#define RSPOVRLOOKUPINT_F RSPOVRLOOKUPINT_V(1U) + +#define REQOVRLOOKUPINT_S 26 +#define REQOVRLOOKUPINT_V(x) ((x) << REQOVRLOOKUPINT_S) +#define REQOVRLOOKUPINT_F REQOVRLOOKUPINT_V(1U) + +#define BLKWRPLINT_S 25 +#define BLKWRPLINT_V(x) ((x) << BLKWRPLINT_S) +#define BLKWRPLINT_F BLKWRPLINT_V(1U) + +#define BLKRDPLINT_S 24 +#define BLKRDPLINT_V(x) ((x) << BLKRDPLINT_S) +#define BLKRDPLINT_F BLKRDPLINT_V(1U) + +#define SGLWRPLINT_S 23 +#define SGLWRPLINT_V(x) ((x) << SGLWRPLINT_S) +#define SGLWRPLINT_F SGLWRPLINT_V(1U) + +#define SGLRDPLINT_S 22 +#define SGLRDPLINT_V(x) ((x) << SGLRDPLINT_S) +#define SGLRDPLINT_F SGLRDPLINT_V(1U) + +#define BLKWRCTLINT_S 21 +#define BLKWRCTLINT_V(x) ((x) << BLKWRCTLINT_S) +#define BLKWRCTLINT_F BLKWRCTLINT_V(1U) + +#define BLKRDCTLINT_S 20 +#define BLKRDCTLINT_V(x) ((x) << BLKRDCTLINT_S) +#define BLKRDCTLINT_F BLKRDCTLINT_V(1U) + +#define SGLWRCTLINT_S 19 +#define SGLWRCTLINT_V(x) ((x) << SGLWRCTLINT_S) +#define SGLWRCTLINT_F SGLWRCTLINT_V(1U) + +#define SGLRDCTLINT_S 18 +#define SGLRDCTLINT_V(x) ((x) << SGLRDCTLINT_S) +#define SGLRDCTLINT_F SGLRDCTLINT_V(1U) + +#define BLKWREEPROMINT_S 17 +#define BLKWREEPROMINT_V(x) ((x) << BLKWREEPROMINT_S) +#define BLKWREEPROMINT_F BLKWREEPROMINT_V(1U) + +#define BLKRDEEPROMINT_S 16 +#define BLKRDEEPROMINT_V(x) ((x) << BLKRDEEPROMINT_S) +#define BLKRDEEPROMINT_F BLKRDEEPROMINT_V(1U) + +#define SGLWREEPROMINT_S 15 +#define SGLWREEPROMINT_V(x) ((x) << SGLWREEPROMINT_S) +#define SGLWREEPROMINT_F SGLWREEPROMINT_V(1U) + +#define SGLRDEEPROMINT_S 14 +#define SGLRDEEPROMINT_V(x) ((x) << SGLRDEEPROMINT_S) +#define SGLRDEEPROMINT_F SGLRDEEPROMINT_V(1U) + +#define BLKWRFLASHINT_S 13 +#define BLKWRFLASHINT_V(x) ((x) << BLKWRFLASHINT_S) +#define BLKWRFLASHINT_F BLKWRFLASHINT_V(1U) + +#define BLKRDFLASHINT_S 12 +#define BLKRDFLASHINT_V(x) ((x) << BLKRDFLASHINT_S) +#define BLKRDFLASHINT_F BLKRDFLASHINT_V(1U) + +#define SGLWRFLASHINT_S 11 +#define SGLWRFLASHINT_V(x) ((x) << SGLWRFLASHINT_S) +#define SGLWRFLASHINT_F SGLWRFLASHINT_V(1U) + +#define SGLRDFLASHINT_S 10 +#define SGLRDFLASHINT_V(x) ((x) << SGLRDFLASHINT_S) +#define SGLRDFLASHINT_F SGLRDFLASHINT_V(1U) + +#define BLKWRBOOTINT_S 9 +#define BLKWRBOOTINT_V(x) ((x) << BLKWRBOOTINT_S) +#define BLKWRBOOTINT_F BLKWRBOOTINT_V(1U) + +#define BLKRDBOOTINT_S 8 +#define BLKRDBOOTINT_V(x) ((x) << BLKRDBOOTINT_S) +#define BLKRDBOOTINT_F BLKRDBOOTINT_V(1U) + +#define SGLWRBOOTINT_S 7 +#define SGLWRBOOTINT_V(x) ((x) << SGLWRBOOTINT_S) +#define SGLWRBOOTINT_F SGLWRBOOTINT_V(1U) + +#define SGLRDBOOTINT_S 6 +#define SGLRDBOOTINT_V(x) ((x) << SGLRDBOOTINT_S) +#define SGLRDBOOTINT_F SGLRDBOOTINT_V(1U) + +#define ILLWRBEINT_S 5 +#define ILLWRBEINT_V(x) ((x) << ILLWRBEINT_S) +#define ILLWRBEINT_F ILLWRBEINT_V(1U) + +#define ILLRDBEINT_S 4 +#define ILLRDBEINT_V(x) ((x) << ILLRDBEINT_S) +#define ILLRDBEINT_F ILLRDBEINT_V(1U) + +#define ILLRDINT_S 3 +#define ILLRDINT_V(x) ((x) << ILLRDINT_S) +#define ILLRDINT_F ILLRDINT_V(1U) + +#define ILLWRINT_S 2 +#define ILLWRINT_V(x) ((x) << ILLWRINT_S) +#define ILLWRINT_F ILLWRINT_V(1U) + +#define ILLTRANSINT_S 1 +#define ILLTRANSINT_V(x) ((x) << ILLTRANSINT_S) +#define ILLTRANSINT_F ILLTRANSINT_V(1U) + +#define RSVDSPACEINT_S 0 +#define RSVDSPACEINT_V(x) ((x) << RSVDSPACEINT_S) +#define RSVDSPACEINT_F RSVDSPACEINT_V(1U) + +/* registers for module TP */ +#define DBGLAWHLF_S 23 +#define DBGLAWHLF_V(x) ((x) << DBGLAWHLF_S) +#define DBGLAWHLF_F DBGLAWHLF_V(1U) + +#define DBGLAWPTR_S 16 +#define DBGLAWPTR_M 0x7fU +#define DBGLAWPTR_G(x) (((x) >> DBGLAWPTR_S) & DBGLAWPTR_M) + +#define DBGLAENABLE_S 12 +#define DBGLAENABLE_V(x) ((x) << DBGLAENABLE_S) +#define DBGLAENABLE_F DBGLAENABLE_V(1U) + +#define DBGLARPTR_S 0 +#define DBGLARPTR_M 0x7fU +#define DBGLARPTR_V(x) ((x) << DBGLARPTR_S) + +#define TP_DBG_LA_DATAL_A 0x7ed8 +#define TP_DBG_LA_CONFIG_A 0x7ed4 +#define TP_OUT_CONFIG_A 0x7d04 +#define TP_GLOBAL_CONFIG_A 0x7d08 + +#define DBGLAMODE_S 14 +#define DBGLAMODE_M 0x3U +#define DBGLAMODE_G(x) (((x) >> DBGLAMODE_S) & DBGLAMODE_M) + +#define FIVETUPLELOOKUP_S 17 +#define FIVETUPLELOOKUP_M 0x3U +#define FIVETUPLELOOKUP_V(x) ((x) << FIVETUPLELOOKUP_S) +#define FIVETUPLELOOKUP_G(x) (((x) >> FIVETUPLELOOKUP_S) & FIVETUPLELOOKUP_M) + +#define TP_PARA_REG2_A 0x7d68 + +#define MAXRXDATA_S 16 +#define MAXRXDATA_M 0xffffU +#define MAXRXDATA_G(x) (((x) >> MAXRXDATA_S) & MAXRXDATA_M) + +#define TP_TIMER_RESOLUTION_A 0x7d90 + +#define TIMERRESOLUTION_S 16 +#define TIMERRESOLUTION_M 0xffU +#define TIMERRESOLUTION_G(x) (((x) >> TIMERRESOLUTION_S) & TIMERRESOLUTION_M) + +#define TIMESTAMPRESOLUTION_S 8 +#define TIMESTAMPRESOLUTION_M 0xffU +#define TIMESTAMPRESOLUTION_G(x) \ + (((x) >> TIMESTAMPRESOLUTION_S) & TIMESTAMPRESOLUTION_M) + +#define DELAYEDACKRESOLUTION_S 0 +#define DELAYEDACKRESOLUTION_M 0xffU +#define DELAYEDACKRESOLUTION_G(x) \ + (((x) >> DELAYEDACKRESOLUTION_S) & DELAYEDACKRESOLUTION_M) + +#define TP_SHIFT_CNT_A 0x7dc0 +#define TP_RXT_MIN_A 0x7d98 +#define TP_RXT_MAX_A 0x7d9c +#define TP_PERS_MIN_A 0x7da0 +#define TP_PERS_MAX_A 0x7da4 +#define TP_KEEP_IDLE_A 0x7da8 +#define TP_KEEP_INTVL_A 0x7dac +#define TP_INIT_SRTT_A 0x7db0 +#define TP_DACK_TIMER_A 0x7db4 +#define TP_FINWAIT2_TIMER_A 0x7db8 + +#define INITSRTT_S 0 +#define INITSRTT_M 0xffffU +#define INITSRTT_G(x) (((x) >> INITSRTT_S) & INITSRTT_M) + +#define PERSMAX_S 0 +#define PERSMAX_M 0x3fffffffU +#define PERSMAX_V(x) ((x) << PERSMAX_S) +#define PERSMAX_G(x) (((x) >> PERSMAX_S) & PERSMAX_M) + +#define SYNSHIFTMAX_S 24 +#define SYNSHIFTMAX_M 0xffU +#define SYNSHIFTMAX_V(x) ((x) << SYNSHIFTMAX_S) +#define SYNSHIFTMAX_G(x) (((x) >> SYNSHIFTMAX_S) & SYNSHIFTMAX_M) + +#define RXTSHIFTMAXR1_S 20 +#define RXTSHIFTMAXR1_M 0xfU +#define RXTSHIFTMAXR1_V(x) ((x) << RXTSHIFTMAXR1_S) +#define RXTSHIFTMAXR1_G(x) (((x) >> RXTSHIFTMAXR1_S) & RXTSHIFTMAXR1_M) + +#define RXTSHIFTMAXR2_S 16 +#define RXTSHIFTMAXR2_M 0xfU +#define RXTSHIFTMAXR2_V(x) ((x) << RXTSHIFTMAXR2_S) +#define RXTSHIFTMAXR2_G(x) (((x) >> RXTSHIFTMAXR2_S) & RXTSHIFTMAXR2_M) + +#define PERSHIFTBACKOFFMAX_S 12 +#define PERSHIFTBACKOFFMAX_M 0xfU +#define PERSHIFTBACKOFFMAX_V(x) ((x) << PERSHIFTBACKOFFMAX_S) +#define PERSHIFTBACKOFFMAX_G(x) \ + (((x) >> PERSHIFTBACKOFFMAX_S) & PERSHIFTBACKOFFMAX_M) + +#define PERSHIFTMAX_S 8 +#define PERSHIFTMAX_M 0xfU +#define PERSHIFTMAX_V(x) ((x) << PERSHIFTMAX_S) +#define PERSHIFTMAX_G(x) (((x) >> PERSHIFTMAX_S) & PERSHIFTMAX_M) + +#define KEEPALIVEMAXR1_S 4 +#define KEEPALIVEMAXR1_M 0xfU +#define KEEPALIVEMAXR1_V(x) ((x) << KEEPALIVEMAXR1_S) +#define KEEPALIVEMAXR1_G(x) (((x) >> KEEPALIVEMAXR1_S) & KEEPALIVEMAXR1_M) + +#define KEEPALIVEMAXR2_S 0 +#define KEEPALIVEMAXR2_M 0xfU +#define KEEPALIVEMAXR2_V(x) ((x) << KEEPALIVEMAXR2_S) +#define KEEPALIVEMAXR2_G(x) (((x) >> KEEPALIVEMAXR2_S) & KEEPALIVEMAXR2_M) + +#define ROWINDEX_S 16 +#define ROWINDEX_V(x) ((x) << ROWINDEX_S) + +#define TP_CCTRL_TABLE_A 0x7ddc +#define TP_MTU_TABLE_A 0x7de4 + +#define MTUINDEX_S 24 +#define MTUINDEX_V(x) ((x) << MTUINDEX_S) + +#define MTUWIDTH_S 16 +#define MTUWIDTH_M 0xfU +#define MTUWIDTH_V(x) ((x) << MTUWIDTH_S) +#define MTUWIDTH_G(x) (((x) >> MTUWIDTH_S) & MTUWIDTH_M) + +#define MTUVALUE_S 0 +#define MTUVALUE_M 0x3fffU +#define MTUVALUE_V(x) ((x) << MTUVALUE_S) +#define MTUVALUE_G(x) (((x) >> MTUVALUE_S) & MTUVALUE_M) + +#define TP_RSS_LKP_TABLE_A 0x7dec + +#define LKPTBLROWVLD_S 31 +#define LKPTBLROWVLD_V(x) ((x) << LKPTBLROWVLD_S) +#define LKPTBLROWVLD_F LKPTBLROWVLD_V(1U) + +#define LKPTBLQUEUE1_S 10 +#define LKPTBLQUEUE1_M 0x3ffU +#define LKPTBLQUEUE1_G(x) (((x) >> LKPTBLQUEUE1_S) & LKPTBLQUEUE1_M) + +#define LKPTBLQUEUE0_S 0 +#define LKPTBLQUEUE0_M 0x3ffU +#define LKPTBLQUEUE0_G(x) (((x) >> LKPTBLQUEUE0_S) & LKPTBLQUEUE0_M) + +#define TP_PIO_ADDR_A 0x7e40 +#define TP_PIO_DATA_A 0x7e44 +#define TP_MIB_INDEX_A 0x7e50 +#define TP_MIB_DATA_A 0x7e54 +#define TP_INT_CAUSE_A 0x7e74 + +#define FLMTXFLSTEMPTY_S 30 +#define FLMTXFLSTEMPTY_V(x) ((x) << FLMTXFLSTEMPTY_S) +#define FLMTXFLSTEMPTY_F FLMTXFLSTEMPTY_V(1U) + +#define TP_VLAN_PRI_MAP_A 0x140 + +#define FRAGMENTATION_S 9 +#define FRAGMENTATION_V(x) ((x) << FRAGMENTATION_S) +#define FRAGMENTATION_F FRAGMENTATION_V(1U) + +#define MPSHITTYPE_S 8 +#define MPSHITTYPE_V(x) ((x) << MPSHITTYPE_S) +#define MPSHITTYPE_F MPSHITTYPE_V(1U) + +#define MACMATCH_S 7 +#define MACMATCH_V(x) ((x) << MACMATCH_S) +#define MACMATCH_F MACMATCH_V(1U) + +#define ETHERTYPE_S 6 +#define ETHERTYPE_V(x) ((x) << ETHERTYPE_S) +#define ETHERTYPE_F ETHERTYPE_V(1U) + +#define PROTOCOL_S 5 +#define PROTOCOL_V(x) ((x) << PROTOCOL_S) +#define PROTOCOL_F PROTOCOL_V(1U) + +#define TOS_S 4 +#define TOS_V(x) ((x) << TOS_S) +#define TOS_F TOS_V(1U) + +#define VLAN_S 3 +#define VLAN_V(x) ((x) << VLAN_S) +#define VLAN_F VLAN_V(1U) + +#define VNIC_ID_S 2 +#define VNIC_ID_V(x) ((x) << VNIC_ID_S) +#define VNIC_ID_F VNIC_ID_V(1U) + +#define PORT_S 1 +#define PORT_V(x) ((x) << PORT_S) +#define PORT_F PORT_V(1U) + +#define FCOE_S 0 +#define FCOE_V(x) ((x) << FCOE_S) +#define FCOE_F FCOE_V(1U) + +#define FILTERMODE_S 15 +#define FILTERMODE_V(x) ((x) << FILTERMODE_S) +#define FILTERMODE_F FILTERMODE_V(1U) + +#define FCOEMASK_S 14 +#define FCOEMASK_V(x) ((x) << FCOEMASK_S) +#define FCOEMASK_F FCOEMASK_V(1U) + +#define TP_INGRESS_CONFIG_A 0x141 + +#define VNIC_S 11 +#define VNIC_V(x) ((x) << VNIC_S) +#define VNIC_F VNIC_V(1U) + +#define CSUM_HAS_PSEUDO_HDR_S 10 +#define CSUM_HAS_PSEUDO_HDR_V(x) ((x) << CSUM_HAS_PSEUDO_HDR_S) +#define CSUM_HAS_PSEUDO_HDR_F CSUM_HAS_PSEUDO_HDR_V(1U) + +#define TP_MIB_MAC_IN_ERR_0_A 0x0 +#define TP_MIB_TCP_OUT_RST_A 0xc +#define TP_MIB_TCP_IN_SEG_HI_A 0x10 +#define TP_MIB_TCP_IN_SEG_LO_A 0x11 +#define TP_MIB_TCP_OUT_SEG_HI_A 0x12 +#define TP_MIB_TCP_OUT_SEG_LO_A 0x13 +#define TP_MIB_TCP_RXT_SEG_HI_A 0x14 +#define TP_MIB_TCP_RXT_SEG_LO_A 0x15 +#define TP_MIB_TNL_CNG_DROP_0_A 0x18 +#define TP_MIB_TCP_V6IN_ERR_0_A 0x28 +#define TP_MIB_TCP_V6OUT_RST_A 0x2c +#define TP_MIB_OFD_ARP_DROP_A 0x36 +#define TP_MIB_TNL_DROP_0_A 0x44 +#define TP_MIB_OFD_VLN_DROP_0_A 0x58 + +#define ULP_TX_INT_CAUSE_A 0x8dcc + +#define PBL_BOUND_ERR_CH3_S 31 +#define PBL_BOUND_ERR_CH3_V(x) ((x) << PBL_BOUND_ERR_CH3_S) +#define PBL_BOUND_ERR_CH3_F PBL_BOUND_ERR_CH3_V(1U) + +#define PBL_BOUND_ERR_CH2_S 30 +#define PBL_BOUND_ERR_CH2_V(x) ((x) << PBL_BOUND_ERR_CH2_S) +#define PBL_BOUND_ERR_CH2_F PBL_BOUND_ERR_CH2_V(1U) + +#define PBL_BOUND_ERR_CH1_S 29 +#define PBL_BOUND_ERR_CH1_V(x) ((x) << PBL_BOUND_ERR_CH1_S) +#define PBL_BOUND_ERR_CH1_F PBL_BOUND_ERR_CH1_V(1U) + +#define PBL_BOUND_ERR_CH0_S 28 +#define PBL_BOUND_ERR_CH0_V(x) ((x) << PBL_BOUND_ERR_CH0_S) +#define PBL_BOUND_ERR_CH0_F PBL_BOUND_ERR_CH0_V(1U) + +#define PM_RX_INT_CAUSE_A 0x8fdc +#define PM_RX_STAT_CONFIG_A 0x8fc8 +#define PM_RX_STAT_COUNT_A 0x8fcc +#define PM_RX_STAT_LSB_A 0x8fd0 +#define PM_RX_DBG_CTRL_A 0x8fd0 +#define PM_RX_DBG_DATA_A 0x8fd4 +#define PM_RX_DBG_STAT_MSB_A 0x10013 + +#define PMRX_FRAMING_ERROR_F 0x003ffff0U + +#define ZERO_E_CMD_ERROR_S 22 +#define ZERO_E_CMD_ERROR_V(x) ((x) << ZERO_E_CMD_ERROR_S) +#define ZERO_E_CMD_ERROR_F ZERO_E_CMD_ERROR_V(1U) + +#define OCSPI_PAR_ERROR_S 3 +#define OCSPI_PAR_ERROR_V(x) ((x) << OCSPI_PAR_ERROR_S) +#define OCSPI_PAR_ERROR_F OCSPI_PAR_ERROR_V(1U) + +#define DB_OPTIONS_PAR_ERROR_S 2 +#define DB_OPTIONS_PAR_ERROR_V(x) ((x) << DB_OPTIONS_PAR_ERROR_S) +#define DB_OPTIONS_PAR_ERROR_F DB_OPTIONS_PAR_ERROR_V(1U) + +#define IESPI_PAR_ERROR_S 1 +#define IESPI_PAR_ERROR_V(x) ((x) << IESPI_PAR_ERROR_S) +#define IESPI_PAR_ERROR_F IESPI_PAR_ERROR_V(1U) + +#define PMRX_E_PCMD_PAR_ERROR_S 0 +#define PMRX_E_PCMD_PAR_ERROR_V(x) ((x) << PMRX_E_PCMD_PAR_ERROR_S) +#define PMRX_E_PCMD_PAR_ERROR_F PMRX_E_PCMD_PAR_ERROR_V(1U) + +#define PM_TX_INT_CAUSE_A 0x8ffc +#define PM_TX_STAT_CONFIG_A 0x8fe8 +#define PM_TX_STAT_COUNT_A 0x8fec +#define PM_TX_STAT_LSB_A 0x8ff0 +#define PM_TX_DBG_CTRL_A 0x8ff0 +#define PM_TX_DBG_DATA_A 0x8ff4 +#define PM_TX_DBG_STAT_MSB_A 0x1001a + +#define PCMD_LEN_OVFL0_S 31 +#define PCMD_LEN_OVFL0_V(x) ((x) << PCMD_LEN_OVFL0_S) +#define PCMD_LEN_OVFL0_F PCMD_LEN_OVFL0_V(1U) + +#define PCMD_LEN_OVFL1_S 30 +#define PCMD_LEN_OVFL1_V(x) ((x) << PCMD_LEN_OVFL1_S) +#define PCMD_LEN_OVFL1_F PCMD_LEN_OVFL1_V(1U) + +#define PCMD_LEN_OVFL2_S 29 +#define PCMD_LEN_OVFL2_V(x) ((x) << PCMD_LEN_OVFL2_S) +#define PCMD_LEN_OVFL2_F PCMD_LEN_OVFL2_V(1U) + +#define ZERO_C_CMD_ERROR_S 28 +#define ZERO_C_CMD_ERROR_V(x) ((x) << ZERO_C_CMD_ERROR_S) +#define ZERO_C_CMD_ERROR_F ZERO_C_CMD_ERROR_V(1U) + +#define PMTX_FRAMING_ERROR_F 0x0ffffff0U + +#define OESPI_PAR_ERROR_S 3 +#define OESPI_PAR_ERROR_V(x) ((x) << OESPI_PAR_ERROR_S) +#define OESPI_PAR_ERROR_F OESPI_PAR_ERROR_V(1U) + +#define ICSPI_PAR_ERROR_S 1 +#define ICSPI_PAR_ERROR_V(x) ((x) << ICSPI_PAR_ERROR_S) +#define ICSPI_PAR_ERROR_F ICSPI_PAR_ERROR_V(1U) + +#define PMTX_C_PCMD_PAR_ERROR_S 0 +#define PMTX_C_PCMD_PAR_ERROR_V(x) ((x) << PMTX_C_PCMD_PAR_ERROR_S) +#define PMTX_C_PCMD_PAR_ERROR_F PMTX_C_PCMD_PAR_ERROR_V(1U) #define MPS_PORT_STAT_TX_PORT_BYTES_L 0x400 #define MPS_PORT_STAT_TX_PORT_BYTES_H 0x404 @@ -959,41 +1623,57 @@ #define MPS_PORT_STAT_RX_PORT_PPP7_H 0x60c #define MPS_PORT_STAT_RX_PORT_LESS_64B_L 0x610 #define MPS_PORT_STAT_RX_PORT_LESS_64B_H 0x614 -#define MAC_PORT_CFG2 0x818 #define MAC_PORT_MAGIC_MACID_LO 0x824 #define MAC_PORT_MAGIC_MACID_HI 0x828 -#define MAC_PORT_EPIO_DATA0 0x8c0 -#define MAC_PORT_EPIO_DATA1 0x8c4 -#define MAC_PORT_EPIO_DATA2 0x8c8 -#define MAC_PORT_EPIO_DATA3 0x8cc -#define MAC_PORT_EPIO_OP 0x8d0 - -#define MPS_CMN_CTL 0x9000 -#define NUMPORTS_MASK 0x00000003U -#define NUMPORTS_SHIFT 0 -#define NUMPORTS_GET(x) (((x) & NUMPORTS_MASK) >> NUMPORTS_SHIFT) - -#define MPS_INT_CAUSE 0x9008 -#define STATINT 0x00000020U -#define TXINT 0x00000010U -#define RXINT 0x00000008U -#define TRCINT 0x00000004U -#define CLSINT 0x00000002U -#define PLINT 0x00000001U - -#define MPS_TX_INT_CAUSE 0x9408 -#define PORTERR 0x00010000U -#define FRMERR 0x00008000U -#define SECNTERR 0x00004000U -#define BUBBLE 0x00002000U -#define TXDESCFIFO 0x00001e00U -#define TXDATAFIFO 0x000001e0U -#define NCSIFIFO 0x00000010U -#define TPFIFO 0x0000000fU - -#define MPS_STAT_PERR_INT_CAUSE_SRAM 0x9614 -#define MPS_STAT_PERR_INT_CAUSE_TX_FIFO 0x9620 -#define MPS_STAT_PERR_INT_CAUSE_RX_FIFO 0x962c + +#define MAC_PORT_EPIO_DATA0_A 0x8c0 +#define MAC_PORT_EPIO_DATA1_A 0x8c4 +#define MAC_PORT_EPIO_DATA2_A 0x8c8 +#define MAC_PORT_EPIO_DATA3_A 0x8cc +#define MAC_PORT_EPIO_OP_A 0x8d0 + +#define MAC_PORT_CFG2_A 0x818 + +#define MPS_CMN_CTL_A 0x9000 + +#define NUMPORTS_S 0 +#define NUMPORTS_M 0x3U +#define NUMPORTS_G(x) (((x) >> NUMPORTS_S) & NUMPORTS_M) + +#define MPS_INT_CAUSE_A 0x9008 +#define MPS_TX_INT_CAUSE_A 0x9408 + +#define FRMERR_S 15 +#define FRMERR_V(x) ((x) << FRMERR_S) +#define FRMERR_F FRMERR_V(1U) + +#define SECNTERR_S 14 +#define SECNTERR_V(x) ((x) << SECNTERR_S) +#define SECNTERR_F SECNTERR_V(1U) + +#define BUBBLE_S 13 +#define BUBBLE_V(x) ((x) << BUBBLE_S) +#define BUBBLE_F BUBBLE_V(1U) + +#define TXDESCFIFO_S 9 +#define TXDESCFIFO_M 0xfU +#define TXDESCFIFO_V(x) ((x) << TXDESCFIFO_S) + +#define TXDATAFIFO_S 5 +#define TXDATAFIFO_M 0xfU +#define TXDATAFIFO_V(x) ((x) << TXDATAFIFO_S) + +#define NCSIFIFO_S 4 +#define NCSIFIFO_V(x) ((x) << NCSIFIFO_S) +#define NCSIFIFO_F NCSIFIFO_V(1U) + +#define TPFIFO_S 0 +#define TPFIFO_M 0xfU +#define TPFIFO_V(x) ((x) << TPFIFO_S) + +#define MPS_STAT_PERR_INT_CAUSE_SRAM_A 0x9614 +#define MPS_STAT_PERR_INT_CAUSE_TX_FIFO_A 0x9620 +#define MPS_STAT_PERR_INT_CAUSE_RX_FIFO_A 0x962c #define MPS_STAT_RX_BG_0_MAC_DROP_FRAME_L 0x9640 #define MPS_STAT_RX_BG_0_MAC_DROP_FRAME_H 0x9644 @@ -1027,294 +1707,851 @@ #define MPS_STAT_RX_BG_2_LB_TRUNC_FRAME_H 0x96b4 #define MPS_STAT_RX_BG_3_LB_TRUNC_FRAME_L 0x96b8 #define MPS_STAT_RX_BG_3_LB_TRUNC_FRAME_H 0x96bc -#define MPS_TRC_CFG 0x9800 -#define TRCFIFOEMPTY 0x00000010U -#define TRCIGNOREDROPINPUT 0x00000008U -#define TRCKEEPDUPLICATES 0x00000004U -#define TRCEN 0x00000002U -#define TRCMULTIFILTER 0x00000001U - -#define MPS_TRC_RSS_CONTROL 0x9808 -#define MPS_T5_TRC_RSS_CONTROL 0xa00c -#define RSSCONTROL_MASK 0x00ff0000U -#define RSSCONTROL_SHIFT 16 -#define RSSCONTROL(x) ((x) << RSSCONTROL_SHIFT) -#define QUEUENUMBER_MASK 0x0000ffffU -#define QUEUENUMBER_SHIFT 0 -#define QUEUENUMBER(x) ((x) << QUEUENUMBER_SHIFT) - -#define MPS_TRC_FILTER_MATCH_CTL_A 0x9810 -#define TFINVERTMATCH 0x01000000U -#define TFPKTTOOLARGE 0x00800000U -#define TFEN 0x00400000U -#define TFPORT_MASK 0x003c0000U -#define TFPORT_SHIFT 18 -#define TFPORT(x) ((x) << TFPORT_SHIFT) -#define TFPORT_GET(x) (((x) & TFPORT_MASK) >> TFPORT_SHIFT) -#define TFDROP 0x00020000U -#define TFSOPEOPERR 0x00010000U -#define TFLENGTH_MASK 0x00001f00U -#define TFLENGTH_SHIFT 8 -#define TFLENGTH(x) ((x) << TFLENGTH_SHIFT) -#define TFLENGTH_GET(x) (((x) & TFLENGTH_MASK) >> TFLENGTH_SHIFT) -#define TFOFFSET_MASK 0x0000001fU -#define TFOFFSET_SHIFT 0 -#define TFOFFSET(x) ((x) << TFOFFSET_SHIFT) -#define TFOFFSET_GET(x) (((x) & TFOFFSET_MASK) >> TFOFFSET_SHIFT) - -#define MPS_TRC_FILTER_MATCH_CTL_B 0x9820 -#define TFMINPKTSIZE_MASK 0x01ff0000U -#define TFMINPKTSIZE_SHIFT 16 -#define TFMINPKTSIZE(x) ((x) << TFMINPKTSIZE_SHIFT) -#define TFMINPKTSIZE_GET(x) (((x) & TFMINPKTSIZE_MASK) >> TFMINPKTSIZE_SHIFT) -#define TFCAPTUREMAX_MASK 0x00003fffU -#define TFCAPTUREMAX_SHIFT 0 -#define TFCAPTUREMAX(x) ((x) << TFCAPTUREMAX_SHIFT) -#define TFCAPTUREMAX_GET(x) (((x) & TFCAPTUREMAX_MASK) >> TFCAPTUREMAX_SHIFT) - -#define MPS_TRC_INT_CAUSE 0x985c -#define MISCPERR 0x00000100U -#define PKTFIFO 0x000000f0U -#define FILTMEM 0x0000000fU - -#define MPS_TRC_FILTER0_MATCH 0x9c00 -#define MPS_TRC_FILTER0_DONT_CARE 0x9c80 -#define MPS_TRC_FILTER1_MATCH 0x9d00 -#define MPS_CLS_INT_CAUSE 0xd028 -#define PLERRENB 0x00000008U -#define HASHSRAM 0x00000004U -#define MATCHTCAM 0x00000002U -#define MATCHSRAM 0x00000001U - -#define MPS_RX_PERR_INT_CAUSE 0x11074 - -#define CPL_INTR_CAUSE 0x19054 -#define CIM_OP_MAP_PERR 0x00000020U -#define CIM_OVFL_ERROR 0x00000010U -#define TP_FRAMING_ERROR 0x00000008U -#define SGE_FRAMING_ERROR 0x00000004U -#define CIM_FRAMING_ERROR 0x00000002U -#define ZERO_SWITCH_ERROR 0x00000001U - -#define SMB_INT_CAUSE 0x19090 -#define MSTTXFIFOPARINT 0x00200000U -#define MSTRXFIFOPARINT 0x00100000U -#define SLVFIFOPARINT 0x00080000U - -#define ULP_RX_INT_CAUSE 0x19158 -#define ULP_RX_ISCSI_TAGMASK 0x19164 -#define ULP_RX_ISCSI_PSZ 0x19168 -#define HPZ3_MASK 0x0f000000U -#define HPZ3_SHIFT 24 -#define HPZ3(x) ((x) << HPZ3_SHIFT) -#define HPZ2_MASK 0x000f0000U -#define HPZ2_SHIFT 16 -#define HPZ2(x) ((x) << HPZ2_SHIFT) -#define HPZ1_MASK 0x00000f00U -#define HPZ1_SHIFT 8 -#define HPZ1(x) ((x) << HPZ1_SHIFT) -#define HPZ0_MASK 0x0000000fU -#define HPZ0_SHIFT 0 -#define HPZ0(x) ((x) << HPZ0_SHIFT) - -#define ULP_RX_TDDP_PSZ 0x19178 - -#define SF_DATA 0x193f8 -#define SF_OP 0x193fc -#define SF_BUSY 0x80000000U -#define SF_LOCK 0x00000010U -#define SF_CONT 0x00000008U -#define BYTECNT_MASK 0x00000006U -#define BYTECNT_SHIFT 1 -#define BYTECNT(x) ((x) << BYTECNT_SHIFT) -#define OP_WR 0x00000001U - -#define PL_PF_INT_CAUSE 0x3c0 -#define PFSW 0x00000008U -#define PFSGE 0x00000004U -#define PFCIM 0x00000002U -#define PFMPS 0x00000001U - -#define PL_PF_INT_ENABLE 0x3c4 -#define PL_PF_CTL 0x3c8 -#define SWINT 0x00000001U - -#define PL_WHOAMI 0x19400 -#define SOURCEPF_MASK 0x00000700U -#define SOURCEPF_SHIFT 8 -#define SOURCEPF(x) ((x) << SOURCEPF_SHIFT) -#define SOURCEPF_GET(x) (((x) & SOURCEPF_MASK) >> SOURCEPF_SHIFT) -#define ISVF 0x00000080U -#define VFID_MASK 0x0000007fU -#define VFID_SHIFT 0 -#define VFID(x) ((x) << VFID_SHIFT) -#define VFID_GET(x) (((x) & VFID_MASK) >> VFID_SHIFT) - -#define PL_INT_CAUSE 0x1940c -#define ULP_TX 0x08000000U -#define SGE 0x04000000U -#define HMA 0x02000000U -#define CPL_SWITCH 0x01000000U -#define ULP_RX 0x00800000U -#define PM_RX 0x00400000U -#define PM_TX 0x00200000U -#define MA 0x00100000U -#define TP 0x00080000U -#define LE 0x00040000U -#define EDC1 0x00020000U -#define EDC0 0x00010000U -#define MC 0x00008000U -#define PCIE 0x00004000U -#define PMU 0x00002000U -#define XGMAC_KR1 0x00001000U -#define XGMAC_KR0 0x00000800U -#define XGMAC1 0x00000400U -#define XGMAC0 0x00000200U -#define SMB 0x00000100U -#define SF 0x00000080U -#define PL 0x00000040U -#define NCSI 0x00000020U -#define MPS 0x00000010U -#define MI 0x00000008U -#define DBG 0x00000004U -#define I2CM 0x00000002U -#define CIM 0x00000001U - -#define MC1 0x31 -#define PL_INT_ENABLE 0x19410 -#define PL_INT_MAP0 0x19414 -#define PL_RST 0x19428 -#define PIORST 0x00000002U -#define PIORSTMODE 0x00000001U - -#define PL_PL_INT_CAUSE 0x19430 -#define FATALPERR 0x00000010U -#define PERRVFID 0x00000001U - -#define PL_REV 0x1943c - -#define S_REV 0 -#define M_REV 0xfU -#define V_REV(x) ((x) << S_REV) -#define G_REV(x) (((x) >> S_REV) & M_REV) - -#define LE_DB_CONFIG 0x19c04 -#define HASHEN 0x00100000U - -#define LE_DB_SERVER_INDEX 0x19c18 -#define LE_DB_ACT_CNT_IPV4 0x19c20 -#define LE_DB_ACT_CNT_IPV6 0x19c24 - -#define LE_DB_INT_CAUSE 0x19c3c -#define REQQPARERR 0x00010000U -#define UNKNOWNCMD 0x00008000U -#define PARITYERR 0x00000040U -#define LIPMISS 0x00000020U -#define LIP0 0x00000010U - -#define LE_DB_TID_HASHBASE 0x19df8 - -#define NCSI_INT_CAUSE 0x1a0d8 -#define CIM_DM_PRTY_ERR 0x00000100U -#define MPS_DM_PRTY_ERR 0x00000080U -#define TXFIFO_PRTY_ERR 0x00000002U -#define RXFIFO_PRTY_ERR 0x00000001U - -#define XGMAC_PORT_CFG2 0x1018 -#define PATEN 0x00040000U -#define MAGICEN 0x00020000U -#define XGMAC_PORT_MAGIC_MACID_LO 0x1024 -#define XGMAC_PORT_MAGIC_MACID_HI 0x1028 +#define MPS_TRC_CFG_A 0x9800 + +#define TRCFIFOEMPTY_S 4 +#define TRCFIFOEMPTY_V(x) ((x) << TRCFIFOEMPTY_S) +#define TRCFIFOEMPTY_F TRCFIFOEMPTY_V(1U) + +#define TRCIGNOREDROPINPUT_S 3 +#define TRCIGNOREDROPINPUT_V(x) ((x) << TRCIGNOREDROPINPUT_S) +#define TRCIGNOREDROPINPUT_F TRCIGNOREDROPINPUT_V(1U) + +#define TRCKEEPDUPLICATES_S 2 +#define TRCKEEPDUPLICATES_V(x) ((x) << TRCKEEPDUPLICATES_S) +#define TRCKEEPDUPLICATES_F TRCKEEPDUPLICATES_V(1U) + +#define TRCEN_S 1 +#define TRCEN_V(x) ((x) << TRCEN_S) +#define TRCEN_F TRCEN_V(1U) + +#define TRCMULTIFILTER_S 0 +#define TRCMULTIFILTER_V(x) ((x) << TRCMULTIFILTER_S) +#define TRCMULTIFILTER_F TRCMULTIFILTER_V(1U) + +#define MPS_TRC_RSS_CONTROL_A 0x9808 +#define MPS_T5_TRC_RSS_CONTROL_A 0xa00c + +#define RSSCONTROL_S 16 +#define RSSCONTROL_V(x) ((x) << RSSCONTROL_S) + +#define QUEUENUMBER_S 0 +#define QUEUENUMBER_V(x) ((x) << QUEUENUMBER_S) + +#define TP_RSS_CONFIG_A 0x7df0 + +#define TNL4TUPENIPV6_S 31 +#define TNL4TUPENIPV6_V(x) ((x) << TNL4TUPENIPV6_S) +#define TNL4TUPENIPV6_F TNL4TUPENIPV6_V(1U) + +#define TNL2TUPENIPV6_S 30 +#define TNL2TUPENIPV6_V(x) ((x) << TNL2TUPENIPV6_S) +#define TNL2TUPENIPV6_F TNL2TUPENIPV6_V(1U) + +#define TNL4TUPENIPV4_S 29 +#define TNL4TUPENIPV4_V(x) ((x) << TNL4TUPENIPV4_S) +#define TNL4TUPENIPV4_F TNL4TUPENIPV4_V(1U) + +#define TNL2TUPENIPV4_S 28 +#define TNL2TUPENIPV4_V(x) ((x) << TNL2TUPENIPV4_S) +#define TNL2TUPENIPV4_F TNL2TUPENIPV4_V(1U) + +#define TNLTCPSEL_S 27 +#define TNLTCPSEL_V(x) ((x) << TNLTCPSEL_S) +#define TNLTCPSEL_F TNLTCPSEL_V(1U) + +#define TNLIP6SEL_S 26 +#define TNLIP6SEL_V(x) ((x) << TNLIP6SEL_S) +#define TNLIP6SEL_F TNLIP6SEL_V(1U) + +#define TNLVRTSEL_S 25 +#define TNLVRTSEL_V(x) ((x) << TNLVRTSEL_S) +#define TNLVRTSEL_F TNLVRTSEL_V(1U) + +#define TNLMAPEN_S 24 +#define TNLMAPEN_V(x) ((x) << TNLMAPEN_S) +#define TNLMAPEN_F TNLMAPEN_V(1U) + +#define OFDHASHSAVE_S 19 +#define OFDHASHSAVE_V(x) ((x) << OFDHASHSAVE_S) +#define OFDHASHSAVE_F OFDHASHSAVE_V(1U) + +#define OFDVRTSEL_S 18 +#define OFDVRTSEL_V(x) ((x) << OFDVRTSEL_S) +#define OFDVRTSEL_F OFDVRTSEL_V(1U) + +#define OFDMAPEN_S 17 +#define OFDMAPEN_V(x) ((x) << OFDMAPEN_S) +#define OFDMAPEN_F OFDMAPEN_V(1U) + +#define OFDLKPEN_S 16 +#define OFDLKPEN_V(x) ((x) << OFDLKPEN_S) +#define OFDLKPEN_F OFDLKPEN_V(1U) + +#define SYN4TUPENIPV6_S 15 +#define SYN4TUPENIPV6_V(x) ((x) << SYN4TUPENIPV6_S) +#define SYN4TUPENIPV6_F SYN4TUPENIPV6_V(1U) + +#define SYN2TUPENIPV6_S 14 +#define SYN2TUPENIPV6_V(x) ((x) << SYN2TUPENIPV6_S) +#define SYN2TUPENIPV6_F SYN2TUPENIPV6_V(1U) + +#define SYN4TUPENIPV4_S 13 +#define SYN4TUPENIPV4_V(x) ((x) << SYN4TUPENIPV4_S) +#define SYN4TUPENIPV4_F SYN4TUPENIPV4_V(1U) + +#define SYN2TUPENIPV4_S 12 +#define SYN2TUPENIPV4_V(x) ((x) << SYN2TUPENIPV4_S) +#define SYN2TUPENIPV4_F SYN2TUPENIPV4_V(1U) + +#define SYNIP6SEL_S 11 +#define SYNIP6SEL_V(x) ((x) << SYNIP6SEL_S) +#define SYNIP6SEL_F SYNIP6SEL_V(1U) + +#define SYNVRTSEL_S 10 +#define SYNVRTSEL_V(x) ((x) << SYNVRTSEL_S) +#define SYNVRTSEL_F SYNVRTSEL_V(1U) + +#define SYNMAPEN_S 9 +#define SYNMAPEN_V(x) ((x) << SYNMAPEN_S) +#define SYNMAPEN_F SYNMAPEN_V(1U) + +#define SYNLKPEN_S 8 +#define SYNLKPEN_V(x) ((x) << SYNLKPEN_S) +#define SYNLKPEN_F SYNLKPEN_V(1U) + +#define CHANNELENABLE_S 7 +#define CHANNELENABLE_V(x) ((x) << CHANNELENABLE_S) +#define CHANNELENABLE_F CHANNELENABLE_V(1U) + +#define PORTENABLE_S 6 +#define PORTENABLE_V(x) ((x) << PORTENABLE_S) +#define PORTENABLE_F PORTENABLE_V(1U) + +#define TNLALLLOOKUP_S 5 +#define TNLALLLOOKUP_V(x) ((x) << TNLALLLOOKUP_S) +#define TNLALLLOOKUP_F TNLALLLOOKUP_V(1U) + +#define VIRTENABLE_S 4 +#define VIRTENABLE_V(x) ((x) << VIRTENABLE_S) +#define VIRTENABLE_F VIRTENABLE_V(1U) + +#define CONGESTIONENABLE_S 3 +#define CONGESTIONENABLE_V(x) ((x) << CONGESTIONENABLE_S) +#define CONGESTIONENABLE_F CONGESTIONENABLE_V(1U) + +#define HASHTOEPLITZ_S 2 +#define HASHTOEPLITZ_V(x) ((x) << HASHTOEPLITZ_S) +#define HASHTOEPLITZ_F HASHTOEPLITZ_V(1U) + +#define UDPENABLE_S 1 +#define UDPENABLE_V(x) ((x) << UDPENABLE_S) +#define UDPENABLE_F UDPENABLE_V(1U) + +#define DISABLE_S 0 +#define DISABLE_V(x) ((x) << DISABLE_S) +#define DISABLE_F DISABLE_V(1U) + +#define TP_RSS_CONFIG_TNL_A 0x7df4 + +#define MASKSIZE_S 28 +#define MASKSIZE_M 0xfU +#define MASKSIZE_V(x) ((x) << MASKSIZE_S) +#define MASKSIZE_G(x) (((x) >> MASKSIZE_S) & MASKSIZE_M) + +#define MASKFILTER_S 16 +#define MASKFILTER_M 0x7ffU +#define MASKFILTER_V(x) ((x) << MASKFILTER_S) +#define MASKFILTER_G(x) (((x) >> MASKFILTER_S) & MASKFILTER_M) + +#define USEWIRECH_S 0 +#define USEWIRECH_V(x) ((x) << USEWIRECH_S) +#define USEWIRECH_F USEWIRECH_V(1U) + +#define HASHALL_S 2 +#define HASHALL_V(x) ((x) << HASHALL_S) +#define HASHALL_F HASHALL_V(1U) + +#define HASHETH_S 1 +#define HASHETH_V(x) ((x) << HASHETH_S) +#define HASHETH_F HASHETH_V(1U) + +#define TP_RSS_CONFIG_OFD_A 0x7df8 + +#define RRCPLMAPEN_S 20 +#define RRCPLMAPEN_V(x) ((x) << RRCPLMAPEN_S) +#define RRCPLMAPEN_F RRCPLMAPEN_V(1U) + +#define RRCPLQUEWIDTH_S 16 +#define RRCPLQUEWIDTH_M 0xfU +#define RRCPLQUEWIDTH_V(x) ((x) << RRCPLQUEWIDTH_S) +#define RRCPLQUEWIDTH_G(x) (((x) >> RRCPLQUEWIDTH_S) & RRCPLQUEWIDTH_M) + +#define TP_RSS_CONFIG_SYN_A 0x7dfc +#define TP_RSS_CONFIG_VRT_A 0x7e00 + +#define VFRDRG_S 25 +#define VFRDRG_V(x) ((x) << VFRDRG_S) +#define VFRDRG_F VFRDRG_V(1U) + +#define VFRDEN_S 24 +#define VFRDEN_V(x) ((x) << VFRDEN_S) +#define VFRDEN_F VFRDEN_V(1U) + +#define VFPERREN_S 23 +#define VFPERREN_V(x) ((x) << VFPERREN_S) +#define VFPERREN_F VFPERREN_V(1U) + +#define KEYPERREN_S 22 +#define KEYPERREN_V(x) ((x) << KEYPERREN_S) +#define KEYPERREN_F KEYPERREN_V(1U) + +#define DISABLEVLAN_S 21 +#define DISABLEVLAN_V(x) ((x) << DISABLEVLAN_S) +#define DISABLEVLAN_F DISABLEVLAN_V(1U) + +#define ENABLEUP0_S 20 +#define ENABLEUP0_V(x) ((x) << ENABLEUP0_S) +#define ENABLEUP0_F ENABLEUP0_V(1U) + +#define HASHDELAY_S 16 +#define HASHDELAY_M 0xfU +#define HASHDELAY_V(x) ((x) << HASHDELAY_S) +#define HASHDELAY_G(x) (((x) >> HASHDELAY_S) & HASHDELAY_M) + +#define VFWRADDR_S 8 +#define VFWRADDR_M 0x7fU +#define VFWRADDR_V(x) ((x) << VFWRADDR_S) +#define VFWRADDR_G(x) (((x) >> VFWRADDR_S) & VFWRADDR_M) + +#define KEYMODE_S 6 +#define KEYMODE_M 0x3U +#define KEYMODE_V(x) ((x) << KEYMODE_S) +#define KEYMODE_G(x) (((x) >> KEYMODE_S) & KEYMODE_M) + +#define VFWREN_S 5 +#define VFWREN_V(x) ((x) << VFWREN_S) +#define VFWREN_F VFWREN_V(1U) + +#define KEYWREN_S 4 +#define KEYWREN_V(x) ((x) << KEYWREN_S) +#define KEYWREN_F KEYWREN_V(1U) + +#define KEYWRADDR_S 0 +#define KEYWRADDR_M 0xfU +#define KEYWRADDR_V(x) ((x) << KEYWRADDR_S) +#define KEYWRADDR_G(x) (((x) >> KEYWRADDR_S) & KEYWRADDR_M) + +#define KEYWRADDRX_S 30 +#define KEYWRADDRX_M 0x3U +#define KEYWRADDRX_V(x) ((x) << KEYWRADDRX_S) +#define KEYWRADDRX_G(x) (((x) >> KEYWRADDRX_S) & KEYWRADDRX_M) + +#define KEYEXTEND_S 26 +#define KEYEXTEND_V(x) ((x) << KEYEXTEND_S) +#define KEYEXTEND_F KEYEXTEND_V(1U) + +#define LKPIDXSIZE_S 24 +#define LKPIDXSIZE_M 0x3U +#define LKPIDXSIZE_V(x) ((x) << LKPIDXSIZE_S) +#define LKPIDXSIZE_G(x) (((x) >> LKPIDXSIZE_S) & LKPIDXSIZE_M) + +#define TP_RSS_VFL_CONFIG_A 0x3a +#define TP_RSS_VFH_CONFIG_A 0x3b + +#define ENABLEUDPHASH_S 31 +#define ENABLEUDPHASH_V(x) ((x) << ENABLEUDPHASH_S) +#define ENABLEUDPHASH_F ENABLEUDPHASH_V(1U) + +#define VFUPEN_S 30 +#define VFUPEN_V(x) ((x) << VFUPEN_S) +#define VFUPEN_F VFUPEN_V(1U) + +#define VFVLNEX_S 28 +#define VFVLNEX_V(x) ((x) << VFVLNEX_S) +#define VFVLNEX_F VFVLNEX_V(1U) + +#define VFPRTEN_S 27 +#define VFPRTEN_V(x) ((x) << VFPRTEN_S) +#define VFPRTEN_F VFPRTEN_V(1U) + +#define VFCHNEN_S 26 +#define VFCHNEN_V(x) ((x) << VFCHNEN_S) +#define VFCHNEN_F VFCHNEN_V(1U) + +#define DEFAULTQUEUE_S 16 +#define DEFAULTQUEUE_M 0x3ffU +#define DEFAULTQUEUE_G(x) (((x) >> DEFAULTQUEUE_S) & DEFAULTQUEUE_M) + +#define VFIP6TWOTUPEN_S 6 +#define VFIP6TWOTUPEN_V(x) ((x) << VFIP6TWOTUPEN_S) +#define VFIP6TWOTUPEN_F VFIP6TWOTUPEN_V(1U) + +#define VFIP4FOURTUPEN_S 5 +#define VFIP4FOURTUPEN_V(x) ((x) << VFIP4FOURTUPEN_S) +#define VFIP4FOURTUPEN_F VFIP4FOURTUPEN_V(1U) + +#define VFIP4TWOTUPEN_S 4 +#define VFIP4TWOTUPEN_V(x) ((x) << VFIP4TWOTUPEN_S) +#define VFIP4TWOTUPEN_F VFIP4TWOTUPEN_V(1U) + +#define KEYINDEX_S 0 +#define KEYINDEX_M 0xfU +#define KEYINDEX_G(x) (((x) >> KEYINDEX_S) & KEYINDEX_M) + +#define MAPENABLE_S 31 +#define MAPENABLE_V(x) ((x) << MAPENABLE_S) +#define MAPENABLE_F MAPENABLE_V(1U) + +#define CHNENABLE_S 30 +#define CHNENABLE_V(x) ((x) << CHNENABLE_S) +#define CHNENABLE_F CHNENABLE_V(1U) + +#define PRTENABLE_S 29 +#define PRTENABLE_V(x) ((x) << PRTENABLE_S) +#define PRTENABLE_F PRTENABLE_V(1U) + +#define UDPFOURTUPEN_S 28 +#define UDPFOURTUPEN_V(x) ((x) << UDPFOURTUPEN_S) +#define UDPFOURTUPEN_F UDPFOURTUPEN_V(1U) + +#define IP6FOURTUPEN_S 27 +#define IP6FOURTUPEN_V(x) ((x) << IP6FOURTUPEN_S) +#define IP6FOURTUPEN_F IP6FOURTUPEN_V(1U) -#define XGMAC_PORT_EPIO_DATA0 0x10c0 -#define XGMAC_PORT_EPIO_DATA1 0x10c4 -#define XGMAC_PORT_EPIO_DATA2 0x10c8 -#define XGMAC_PORT_EPIO_DATA3 0x10cc -#define XGMAC_PORT_EPIO_OP 0x10d0 -#define EPIOWR 0x00000100U -#define ADDRESS_MASK 0x000000ffU -#define ADDRESS_SHIFT 0 -#define ADDRESS(x) ((x) << ADDRESS_SHIFT) +#define IP6TWOTUPEN_S 26 +#define IP6TWOTUPEN_V(x) ((x) << IP6TWOTUPEN_S) +#define IP6TWOTUPEN_F IP6TWOTUPEN_V(1U) -#define MAC_PORT_INT_CAUSE 0x8dc -#define XGMAC_PORT_INT_CAUSE 0x10dc +#define IP4FOURTUPEN_S 25 +#define IP4FOURTUPEN_V(x) ((x) << IP4FOURTUPEN_S) +#define IP4FOURTUPEN_F IP4FOURTUPEN_V(1U) -#define A_TP_TX_MOD_QUEUE_REQ_MAP 0x7e28 +#define IP4TWOTUPEN_S 24 +#define IP4TWOTUPEN_V(x) ((x) << IP4TWOTUPEN_S) +#define IP4TWOTUPEN_F IP4TWOTUPEN_V(1U) -#define A_TP_TX_MOD_CHANNEL_WEIGHT 0x7e34 +#define IVFWIDTH_S 20 +#define IVFWIDTH_M 0xfU +#define IVFWIDTH_V(x) ((x) << IVFWIDTH_S) +#define IVFWIDTH_G(x) (((x) >> IVFWIDTH_S) & IVFWIDTH_M) -#define S_TX_MOD_QUEUE_REQ_MAP 0 -#define M_TX_MOD_QUEUE_REQ_MAP 0xffffU -#define V_TX_MOD_QUEUE_REQ_MAP(x) ((x) << S_TX_MOD_QUEUE_REQ_MAP) +#define CH1DEFAULTQUEUE_S 10 +#define CH1DEFAULTQUEUE_M 0x3ffU +#define CH1DEFAULTQUEUE_V(x) ((x) << CH1DEFAULTQUEUE_S) +#define CH1DEFAULTQUEUE_G(x) (((x) >> CH1DEFAULTQUEUE_S) & CH1DEFAULTQUEUE_M) -#define A_TP_TX_MOD_QUEUE_WEIGHT0 0x7e30 +#define CH0DEFAULTQUEUE_S 0 +#define CH0DEFAULTQUEUE_M 0x3ffU +#define CH0DEFAULTQUEUE_V(x) ((x) << CH0DEFAULTQUEUE_S) +#define CH0DEFAULTQUEUE_G(x) (((x) >> CH0DEFAULTQUEUE_S) & CH0DEFAULTQUEUE_M) -#define S_TX_MODQ_WEIGHT3 24 -#define M_TX_MODQ_WEIGHT3 0xffU -#define V_TX_MODQ_WEIGHT3(x) ((x) << S_TX_MODQ_WEIGHT3) +#define VFLKPIDX_S 8 +#define VFLKPIDX_M 0xffU +#define VFLKPIDX_G(x) (((x) >> VFLKPIDX_S) & VFLKPIDX_M) -#define S_TX_MODQ_WEIGHT2 16 -#define M_TX_MODQ_WEIGHT2 0xffU -#define V_TX_MODQ_WEIGHT2(x) ((x) << S_TX_MODQ_WEIGHT2) +#define TP_RSS_CONFIG_CNG_A 0x7e04 +#define TP_RSS_SECRET_KEY0_A 0x40 +#define TP_RSS_PF0_CONFIG_A 0x30 +#define TP_RSS_PF_MAP_A 0x38 +#define TP_RSS_PF_MSK_A 0x39 -#define S_TX_MODQ_WEIGHT1 8 -#define M_TX_MODQ_WEIGHT1 0xffU -#define V_TX_MODQ_WEIGHT1(x) ((x) << S_TX_MODQ_WEIGHT1) +#define PF1LKPIDX_S 3 -#define S_TX_MODQ_WEIGHT0 0 -#define M_TX_MODQ_WEIGHT0 0xffU -#define V_TX_MODQ_WEIGHT0(x) ((x) << S_TX_MODQ_WEIGHT0) +#define PF0LKPIDX_M 0x7U -#define A_TP_TX_SCHED_HDR 0x23 +#define PF1MSKSIZE_S 4 +#define PF1MSKSIZE_M 0xfU -#define A_TP_TX_SCHED_FIFO 0x24 +#define CHNCOUNT3_S 31 +#define CHNCOUNT3_V(x) ((x) << CHNCOUNT3_S) +#define CHNCOUNT3_F CHNCOUNT3_V(1U) -#define A_TP_TX_SCHED_PCMD 0x25 +#define CHNCOUNT2_S 30 +#define CHNCOUNT2_V(x) ((x) << CHNCOUNT2_S) +#define CHNCOUNT2_F CHNCOUNT2_V(1U) -#define S_VNIC 11 -#define V_VNIC(x) ((x) << S_VNIC) -#define F_VNIC V_VNIC(1U) +#define CHNCOUNT1_S 29 +#define CHNCOUNT1_V(x) ((x) << CHNCOUNT1_S) +#define CHNCOUNT1_F CHNCOUNT1_V(1U) -#define S_FRAGMENTATION 9 -#define V_FRAGMENTATION(x) ((x) << S_FRAGMENTATION) -#define F_FRAGMENTATION V_FRAGMENTATION(1U) +#define CHNCOUNT0_S 28 +#define CHNCOUNT0_V(x) ((x) << CHNCOUNT0_S) +#define CHNCOUNT0_F CHNCOUNT0_V(1U) -#define S_MPSHITTYPE 8 -#define V_MPSHITTYPE(x) ((x) << S_MPSHITTYPE) -#define F_MPSHITTYPE V_MPSHITTYPE(1U) +#define CHNUNDFLOW3_S 27 +#define CHNUNDFLOW3_V(x) ((x) << CHNUNDFLOW3_S) +#define CHNUNDFLOW3_F CHNUNDFLOW3_V(1U) -#define S_MACMATCH 7 -#define V_MACMATCH(x) ((x) << S_MACMATCH) -#define F_MACMATCH V_MACMATCH(1U) +#define CHNUNDFLOW2_S 26 +#define CHNUNDFLOW2_V(x) ((x) << CHNUNDFLOW2_S) +#define CHNUNDFLOW2_F CHNUNDFLOW2_V(1U) -#define S_ETHERTYPE 6 -#define V_ETHERTYPE(x) ((x) << S_ETHERTYPE) -#define F_ETHERTYPE V_ETHERTYPE(1U) +#define CHNUNDFLOW1_S 25 +#define CHNUNDFLOW1_V(x) ((x) << CHNUNDFLOW1_S) +#define CHNUNDFLOW1_F CHNUNDFLOW1_V(1U) -#define S_PROTOCOL 5 -#define V_PROTOCOL(x) ((x) << S_PROTOCOL) -#define F_PROTOCOL V_PROTOCOL(1U) +#define CHNUNDFLOW0_S 24 +#define CHNUNDFLOW0_V(x) ((x) << CHNUNDFLOW0_S) +#define CHNUNDFLOW0_F CHNUNDFLOW0_V(1U) -#define S_TOS 4 -#define V_TOS(x) ((x) << S_TOS) -#define F_TOS V_TOS(1U) +#define RSTCHN3_S 19 +#define RSTCHN3_V(x) ((x) << RSTCHN3_S) +#define RSTCHN3_F RSTCHN3_V(1U) -#define S_VLAN 3 -#define V_VLAN(x) ((x) << S_VLAN) -#define F_VLAN V_VLAN(1U) +#define RSTCHN2_S 18 +#define RSTCHN2_V(x) ((x) << RSTCHN2_S) +#define RSTCHN2_F RSTCHN2_V(1U) -#define S_VNIC_ID 2 -#define V_VNIC_ID(x) ((x) << S_VNIC_ID) -#define F_VNIC_ID V_VNIC_ID(1U) +#define RSTCHN1_S 17 +#define RSTCHN1_V(x) ((x) << RSTCHN1_S) +#define RSTCHN1_F RSTCHN1_V(1U) -#define S_PORT 1 -#define V_PORT(x) ((x) << S_PORT) -#define F_PORT V_PORT(1U) +#define RSTCHN0_S 16 +#define RSTCHN0_V(x) ((x) << RSTCHN0_S) +#define RSTCHN0_F RSTCHN0_V(1U) -#define S_FCOE 0 -#define V_FCOE(x) ((x) << S_FCOE) -#define F_FCOE V_FCOE(1U) +#define UPDVLD_S 15 +#define UPDVLD_V(x) ((x) << UPDVLD_S) +#define UPDVLD_F UPDVLD_V(1U) + +#define XOFF_S 14 +#define XOFF_V(x) ((x) << XOFF_S) +#define XOFF_F XOFF_V(1U) + +#define UPDCHN3_S 13 +#define UPDCHN3_V(x) ((x) << UPDCHN3_S) +#define UPDCHN3_F UPDCHN3_V(1U) + +#define UPDCHN2_S 12 +#define UPDCHN2_V(x) ((x) << UPDCHN2_S) +#define UPDCHN2_F UPDCHN2_V(1U) + +#define UPDCHN1_S 11 +#define UPDCHN1_V(x) ((x) << UPDCHN1_S) +#define UPDCHN1_F UPDCHN1_V(1U) + +#define UPDCHN0_S 10 +#define UPDCHN0_V(x) ((x) << UPDCHN0_S) +#define UPDCHN0_F UPDCHN0_V(1U) + +#define QUEUE_S 0 +#define QUEUE_M 0x3ffU +#define QUEUE_V(x) ((x) << QUEUE_S) +#define QUEUE_G(x) (((x) >> QUEUE_S) & QUEUE_M) + +#define MPS_TRC_INT_CAUSE_A 0x985c + +#define MISCPERR_S 8 +#define MISCPERR_V(x) ((x) << MISCPERR_S) +#define MISCPERR_F MISCPERR_V(1U) + +#define PKTFIFO_S 4 +#define PKTFIFO_M 0xfU +#define PKTFIFO_V(x) ((x) << PKTFIFO_S) + +#define FILTMEM_S 0 +#define FILTMEM_M 0xfU +#define FILTMEM_V(x) ((x) << FILTMEM_S) + +#define MPS_CLS_INT_CAUSE_A 0xd028 + +#define HASHSRAM_S 2 +#define HASHSRAM_V(x) ((x) << HASHSRAM_S) +#define HASHSRAM_F HASHSRAM_V(1U) + +#define MATCHTCAM_S 1 +#define MATCHTCAM_V(x) ((x) << MATCHTCAM_S) +#define MATCHTCAM_F MATCHTCAM_V(1U) + +#define MATCHSRAM_S 0 +#define MATCHSRAM_V(x) ((x) << MATCHSRAM_S) +#define MATCHSRAM_F MATCHSRAM_V(1U) + +#define MPS_RX_PERR_INT_CAUSE_A 0x11074 + +#define MPS_CLS_TCAM_Y_L_A 0xf000 +#define MPS_CLS_TCAM_X_L_A 0xf008 + +#define MPS_CLS_TCAM_Y_L(idx) (MPS_CLS_TCAM_Y_L_A + (idx) * 16) +#define NUM_MPS_CLS_TCAM_Y_L_INSTANCES 512 + +#define MPS_CLS_TCAM_X_L(idx) (MPS_CLS_TCAM_X_L_A + (idx) * 16) +#define NUM_MPS_CLS_TCAM_X_L_INSTANCES 512 + +#define MPS_CLS_SRAM_L_A 0xe000 +#define MPS_CLS_SRAM_H_A 0xe004 + +#define MPS_CLS_SRAM_L(idx) (MPS_CLS_SRAM_L_A + (idx) * 8) +#define NUM_MPS_CLS_SRAM_L_INSTANCES 336 + +#define MPS_CLS_SRAM_H(idx) (MPS_CLS_SRAM_H_A + (idx) * 8) +#define NUM_MPS_CLS_SRAM_H_INSTANCES 336 + +#define MULTILISTEN0_S 25 + +#define REPLICATE_S 11 +#define REPLICATE_V(x) ((x) << REPLICATE_S) +#define REPLICATE_F REPLICATE_V(1U) + +#define PF_S 8 +#define PF_M 0x7U +#define PF_G(x) (((x) >> PF_S) & PF_M) + +#define VF_VALID_S 7 +#define VF_VALID_V(x) ((x) << VF_VALID_S) +#define VF_VALID_F VF_VALID_V(1U) + +#define VF_S 0 +#define VF_M 0x7fU +#define VF_G(x) (((x) >> VF_S) & VF_M) + +#define SRAM_PRIO3_S 22 +#define SRAM_PRIO3_M 0x7U +#define SRAM_PRIO3_G(x) (((x) >> SRAM_PRIO3_S) & SRAM_PRIO3_M) + +#define SRAM_PRIO2_S 19 +#define SRAM_PRIO2_M 0x7U +#define SRAM_PRIO2_G(x) (((x) >> SRAM_PRIO2_S) & SRAM_PRIO2_M) + +#define SRAM_PRIO1_S 16 +#define SRAM_PRIO1_M 0x7U +#define SRAM_PRIO1_G(x) (((x) >> SRAM_PRIO1_S) & SRAM_PRIO1_M) + +#define SRAM_PRIO0_S 13 +#define SRAM_PRIO0_M 0x7U +#define SRAM_PRIO0_G(x) (((x) >> SRAM_PRIO0_S) & SRAM_PRIO0_M) + +#define SRAM_VLD_S 12 +#define SRAM_VLD_V(x) ((x) << SRAM_VLD_S) +#define SRAM_VLD_F SRAM_VLD_V(1U) + +#define PORTMAP_S 0 +#define PORTMAP_M 0xfU +#define PORTMAP_G(x) (((x) >> PORTMAP_S) & PORTMAP_M) + +#define CPL_INTR_CAUSE_A 0x19054 + +#define CIM_OP_MAP_PERR_S 5 +#define CIM_OP_MAP_PERR_V(x) ((x) << CIM_OP_MAP_PERR_S) +#define CIM_OP_MAP_PERR_F CIM_OP_MAP_PERR_V(1U) + +#define CIM_OVFL_ERROR_S 4 +#define CIM_OVFL_ERROR_V(x) ((x) << CIM_OVFL_ERROR_S) +#define CIM_OVFL_ERROR_F CIM_OVFL_ERROR_V(1U) + +#define TP_FRAMING_ERROR_S 3 +#define TP_FRAMING_ERROR_V(x) ((x) << TP_FRAMING_ERROR_S) +#define TP_FRAMING_ERROR_F TP_FRAMING_ERROR_V(1U) + +#define SGE_FRAMING_ERROR_S 2 +#define SGE_FRAMING_ERROR_V(x) ((x) << SGE_FRAMING_ERROR_S) +#define SGE_FRAMING_ERROR_F SGE_FRAMING_ERROR_V(1U) + +#define CIM_FRAMING_ERROR_S 1 +#define CIM_FRAMING_ERROR_V(x) ((x) << CIM_FRAMING_ERROR_S) +#define CIM_FRAMING_ERROR_F CIM_FRAMING_ERROR_V(1U) + +#define ZERO_SWITCH_ERROR_S 0 +#define ZERO_SWITCH_ERROR_V(x) ((x) << ZERO_SWITCH_ERROR_S) +#define ZERO_SWITCH_ERROR_F ZERO_SWITCH_ERROR_V(1U) + +#define SMB_INT_CAUSE_A 0x19090 + +#define MSTTXFIFOPARINT_S 21 +#define MSTTXFIFOPARINT_V(x) ((x) << MSTTXFIFOPARINT_S) +#define MSTTXFIFOPARINT_F MSTTXFIFOPARINT_V(1U) + +#define MSTRXFIFOPARINT_S 20 +#define MSTRXFIFOPARINT_V(x) ((x) << MSTRXFIFOPARINT_S) +#define MSTRXFIFOPARINT_F MSTRXFIFOPARINT_V(1U) + +#define SLVFIFOPARINT_S 19 +#define SLVFIFOPARINT_V(x) ((x) << SLVFIFOPARINT_S) +#define SLVFIFOPARINT_F SLVFIFOPARINT_V(1U) + +#define ULP_RX_INT_CAUSE_A 0x19158 +#define ULP_RX_ISCSI_TAGMASK_A 0x19164 +#define ULP_RX_ISCSI_PSZ_A 0x19168 +#define ULP_RX_LA_CTL_A 0x1923c +#define ULP_RX_LA_RDPTR_A 0x19240 +#define ULP_RX_LA_RDDATA_A 0x19244 +#define ULP_RX_LA_WRPTR_A 0x19248 + +#define HPZ3_S 24 +#define HPZ3_V(x) ((x) << HPZ3_S) + +#define HPZ2_S 16 +#define HPZ2_V(x) ((x) << HPZ2_S) + +#define HPZ1_S 8 +#define HPZ1_V(x) ((x) << HPZ1_S) + +#define HPZ0_S 0 +#define HPZ0_V(x) ((x) << HPZ0_S) + +#define ULP_RX_TDDP_PSZ_A 0x19178 + +/* registers for module SF */ +#define SF_DATA_A 0x193f8 +#define SF_OP_A 0x193fc + +#define SF_BUSY_S 31 +#define SF_BUSY_V(x) ((x) << SF_BUSY_S) +#define SF_BUSY_F SF_BUSY_V(1U) + +#define SF_LOCK_S 4 +#define SF_LOCK_V(x) ((x) << SF_LOCK_S) +#define SF_LOCK_F SF_LOCK_V(1U) + +#define SF_CONT_S 3 +#define SF_CONT_V(x) ((x) << SF_CONT_S) +#define SF_CONT_F SF_CONT_V(1U) + +#define BYTECNT_S 1 +#define BYTECNT_V(x) ((x) << BYTECNT_S) + +#define OP_S 0 +#define OP_V(x) ((x) << OP_S) +#define OP_F OP_V(1U) + +#define PL_PF_INT_CAUSE_A 0x3c0 + +#define PFSW_S 3 +#define PFSW_V(x) ((x) << PFSW_S) +#define PFSW_F PFSW_V(1U) + +#define PFCIM_S 1 +#define PFCIM_V(x) ((x) << PFCIM_S) +#define PFCIM_F PFCIM_V(1U) + +#define PL_PF_INT_ENABLE_A 0x3c4 +#define PL_PF_CTL_A 0x3c8 + +#define PL_WHOAMI_A 0x19400 + +#define SOURCEPF_S 8 +#define SOURCEPF_M 0x7U +#define SOURCEPF_G(x) (((x) >> SOURCEPF_S) & SOURCEPF_M) + +#define PL_INT_CAUSE_A 0x1940c + +#define ULP_TX_S 27 +#define ULP_TX_V(x) ((x) << ULP_TX_S) +#define ULP_TX_F ULP_TX_V(1U) + +#define SGE_S 26 +#define SGE_V(x) ((x) << SGE_S) +#define SGE_F SGE_V(1U) + +#define CPL_SWITCH_S 24 +#define CPL_SWITCH_V(x) ((x) << CPL_SWITCH_S) +#define CPL_SWITCH_F CPL_SWITCH_V(1U) + +#define ULP_RX_S 23 +#define ULP_RX_V(x) ((x) << ULP_RX_S) +#define ULP_RX_F ULP_RX_V(1U) + +#define PM_RX_S 22 +#define PM_RX_V(x) ((x) << PM_RX_S) +#define PM_RX_F PM_RX_V(1U) + +#define PM_TX_S 21 +#define PM_TX_V(x) ((x) << PM_TX_S) +#define PM_TX_F PM_TX_V(1U) + +#define MA_S 20 +#define MA_V(x) ((x) << MA_S) +#define MA_F MA_V(1U) + +#define TP_S 19 +#define TP_V(x) ((x) << TP_S) +#define TP_F TP_V(1U) + +#define LE_S 18 +#define LE_V(x) ((x) << LE_S) +#define LE_F LE_V(1U) + +#define EDC1_S 17 +#define EDC1_V(x) ((x) << EDC1_S) +#define EDC1_F EDC1_V(1U) + +#define EDC0_S 16 +#define EDC0_V(x) ((x) << EDC0_S) +#define EDC0_F EDC0_V(1U) + +#define MC_S 15 +#define MC_V(x) ((x) << MC_S) +#define MC_F MC_V(1U) + +#define PCIE_S 14 +#define PCIE_V(x) ((x) << PCIE_S) +#define PCIE_F PCIE_V(1U) + +#define XGMAC_KR1_S 12 +#define XGMAC_KR1_V(x) ((x) << XGMAC_KR1_S) +#define XGMAC_KR1_F XGMAC_KR1_V(1U) + +#define XGMAC_KR0_S 11 +#define XGMAC_KR0_V(x) ((x) << XGMAC_KR0_S) +#define XGMAC_KR0_F XGMAC_KR0_V(1U) + +#define XGMAC1_S 10 +#define XGMAC1_V(x) ((x) << XGMAC1_S) +#define XGMAC1_F XGMAC1_V(1U) + +#define XGMAC0_S 9 +#define XGMAC0_V(x) ((x) << XGMAC0_S) +#define XGMAC0_F XGMAC0_V(1U) + +#define SMB_S 8 +#define SMB_V(x) ((x) << SMB_S) +#define SMB_F SMB_V(1U) + +#define SF_S 7 +#define SF_V(x) ((x) << SF_S) +#define SF_F SF_V(1U) + +#define PL_S 6 +#define PL_V(x) ((x) << PL_S) +#define PL_F PL_V(1U) + +#define NCSI_S 5 +#define NCSI_V(x) ((x) << NCSI_S) +#define NCSI_F NCSI_V(1U) + +#define MPS_S 4 +#define MPS_V(x) ((x) << MPS_S) +#define MPS_F MPS_V(1U) + +#define CIM_S 0 +#define CIM_V(x) ((x) << CIM_S) +#define CIM_F CIM_V(1U) + +#define MC1_S 31 + +#define PL_INT_ENABLE_A 0x19410 +#define PL_INT_MAP0_A 0x19414 +#define PL_RST_A 0x19428 + +#define PIORST_S 1 +#define PIORST_V(x) ((x) << PIORST_S) +#define PIORST_F PIORST_V(1U) + +#define PIORSTMODE_S 0 +#define PIORSTMODE_V(x) ((x) << PIORSTMODE_S) +#define PIORSTMODE_F PIORSTMODE_V(1U) + +#define PL_PL_INT_CAUSE_A 0x19430 + +#define FATALPERR_S 4 +#define FATALPERR_V(x) ((x) << FATALPERR_S) +#define FATALPERR_F FATALPERR_V(1U) + +#define PERRVFID_S 0 +#define PERRVFID_V(x) ((x) << PERRVFID_S) +#define PERRVFID_F PERRVFID_V(1U) + +#define PL_REV_A 0x1943c + +#define REV_S 0 +#define REV_M 0xfU +#define REV_V(x) ((x) << REV_S) +#define REV_G(x) (((x) >> REV_S) & REV_M) + +#define LE_DB_INT_CAUSE_A 0x19c3c + +#define REQQPARERR_S 16 +#define REQQPARERR_V(x) ((x) << REQQPARERR_S) +#define REQQPARERR_F REQQPARERR_V(1U) + +#define UNKNOWNCMD_S 15 +#define UNKNOWNCMD_V(x) ((x) << UNKNOWNCMD_S) +#define UNKNOWNCMD_F UNKNOWNCMD_V(1U) + +#define PARITYERR_S 6 +#define PARITYERR_V(x) ((x) << PARITYERR_S) +#define PARITYERR_F PARITYERR_V(1U) + +#define LIPMISS_S 5 +#define LIPMISS_V(x) ((x) << LIPMISS_S) +#define LIPMISS_F LIPMISS_V(1U) + +#define LIP0_S 4 +#define LIP0_V(x) ((x) << LIP0_S) +#define LIP0_F LIP0_V(1U) + +#define NCSI_INT_CAUSE_A 0x1a0d8 + +#define CIM_DM_PRTY_ERR_S 8 +#define CIM_DM_PRTY_ERR_V(x) ((x) << CIM_DM_PRTY_ERR_S) +#define CIM_DM_PRTY_ERR_F CIM_DM_PRTY_ERR_V(1U) + +#define MPS_DM_PRTY_ERR_S 7 +#define MPS_DM_PRTY_ERR_V(x) ((x) << MPS_DM_PRTY_ERR_S) +#define MPS_DM_PRTY_ERR_F MPS_DM_PRTY_ERR_V(1U) + +#define TXFIFO_PRTY_ERR_S 1 +#define TXFIFO_PRTY_ERR_V(x) ((x) << TXFIFO_PRTY_ERR_S) +#define TXFIFO_PRTY_ERR_F TXFIFO_PRTY_ERR_V(1U) + +#define RXFIFO_PRTY_ERR_S 0 +#define RXFIFO_PRTY_ERR_V(x) ((x) << RXFIFO_PRTY_ERR_S) +#define RXFIFO_PRTY_ERR_F RXFIFO_PRTY_ERR_V(1U) + +#define XGMAC_PORT_CFG2_A 0x1018 + +#define PATEN_S 18 +#define PATEN_V(x) ((x) << PATEN_S) +#define PATEN_F PATEN_V(1U) + +#define MAGICEN_S 17 +#define MAGICEN_V(x) ((x) << MAGICEN_S) +#define MAGICEN_F MAGICEN_V(1U) + +#define XGMAC_PORT_MAGIC_MACID_LO 0x1024 +#define XGMAC_PORT_MAGIC_MACID_HI 0x1028 + +#define XGMAC_PORT_EPIO_DATA0_A 0x10c0 +#define XGMAC_PORT_EPIO_DATA1_A 0x10c4 +#define XGMAC_PORT_EPIO_DATA2_A 0x10c8 +#define XGMAC_PORT_EPIO_DATA3_A 0x10cc +#define XGMAC_PORT_EPIO_OP_A 0x10d0 + +#define EPIOWR_S 8 +#define EPIOWR_V(x) ((x) << EPIOWR_S) +#define EPIOWR_F EPIOWR_V(1U) + +#define ADDRESS_S 0 +#define ADDRESS_V(x) ((x) << ADDRESS_S) + +#define MAC_PORT_INT_CAUSE_A 0x8dc +#define XGMAC_PORT_INT_CAUSE_A 0x10dc + +#define TP_TX_MOD_QUEUE_REQ_MAP_A 0x7e28 + +#define TP_TX_MOD_QUEUE_WEIGHT0_A 0x7e30 +#define TP_TX_MOD_CHANNEL_WEIGHT_A 0x7e34 + +#define TX_MOD_QUEUE_REQ_MAP_S 0 +#define TX_MOD_QUEUE_REQ_MAP_V(x) ((x) << TX_MOD_QUEUE_REQ_MAP_S) + +#define TX_MODQ_WEIGHT3_S 24 +#define TX_MODQ_WEIGHT3_V(x) ((x) << TX_MODQ_WEIGHT3_S) + +#define TX_MODQ_WEIGHT2_S 16 +#define TX_MODQ_WEIGHT2_V(x) ((x) << TX_MODQ_WEIGHT2_S) + +#define TX_MODQ_WEIGHT1_S 8 +#define TX_MODQ_WEIGHT1_V(x) ((x) << TX_MODQ_WEIGHT1_S) + +#define TX_MODQ_WEIGHT0_S 0 +#define TX_MODQ_WEIGHT0_V(x) ((x) << TX_MODQ_WEIGHT0_S) + +#define TP_TX_SCHED_HDR_A 0x23 +#define TP_TX_SCHED_FIFO_A 0x24 +#define TP_TX_SCHED_PCMD_A 0x25 #define NUM_MPS_CLS_SRAM_L_INSTANCES 336 #define NUM_MPS_T5_CLS_SRAM_L_INSTANCES 512 @@ -1329,62 +2566,149 @@ #define MC_STRIDE (MC_1_BASE_ADDR - MC_0_BASE_ADDR) #define MC_REG(reg, idx) (reg + MC_STRIDE * idx) -#define MC_P_BIST_CMD 0x41400 -#define MC_P_BIST_CMD_ADDR 0x41404 -#define MC_P_BIST_CMD_LEN 0x41408 -#define MC_P_BIST_DATA_PATTERN 0x4140c -#define MC_P_BIST_STATUS_RDATA 0x41488 -#define EDC_T50_BASE_ADDR 0x50000 -#define EDC_H_BIST_CMD 0x50004 -#define EDC_H_BIST_CMD_ADDR 0x50008 -#define EDC_H_BIST_CMD_LEN 0x5000c -#define EDC_H_BIST_DATA_PATTERN 0x50010 -#define EDC_H_BIST_STATUS_RDATA 0x50028 - -#define EDC_T51_BASE_ADDR 0x50800 +#define MC_P_BIST_CMD_A 0x41400 +#define MC_P_BIST_CMD_ADDR_A 0x41404 +#define MC_P_BIST_CMD_LEN_A 0x41408 +#define MC_P_BIST_DATA_PATTERN_A 0x4140c +#define MC_P_BIST_STATUS_RDATA_A 0x41488 + +#define EDC_T50_BASE_ADDR 0x50000 + +#define EDC_H_BIST_CMD_A 0x50004 +#define EDC_H_BIST_CMD_ADDR_A 0x50008 +#define EDC_H_BIST_CMD_LEN_A 0x5000c +#define EDC_H_BIST_DATA_PATTERN_A 0x50010 +#define EDC_H_BIST_STATUS_RDATA_A 0x50028 + +#define EDC_T51_BASE_ADDR 0x50800 + #define EDC_STRIDE_T5 (EDC_T51_BASE_ADDR - EDC_T50_BASE_ADDR) #define EDC_REG_T5(reg, idx) (reg + EDC_STRIDE_T5 * idx) -#define A_PL_VF_REV 0x4 -#define A_PL_VF_WHOAMI 0x0 -#define A_PL_VF_REVISION 0x8 +#define PL_VF_REV_A 0x4 +#define PL_VF_WHOAMI_A 0x0 +#define PL_VF_REVISION_A 0x8 -#define S_CHIPID 4 -#define M_CHIPID 0xfU -#define V_CHIPID(x) ((x) << S_CHIPID) -#define G_CHIPID(x) (((x) >> S_CHIPID) & M_CHIPID) +/* registers for module CIM */ +#define CIM_HOST_ACC_CTRL_A 0x7b50 +#define CIM_HOST_ACC_DATA_A 0x7b54 +#define UP_UP_DBG_LA_CFG_A 0x140 +#define UP_UP_DBG_LA_DATA_A 0x144 -/* TP_VLAN_PRI_MAP controls which subset of fields will be present in the - * Compressed Filter Tuple for LE filters. Each bit set in TP_VLAN_PRI_MAP - * selects for a particular field being present. These fields, when present - * in the Compressed Filter Tuple, have the following widths in bits. - */ -#define W_FT_FCOE 1 -#define W_FT_PORT 3 -#define W_FT_VNIC_ID 17 -#define W_FT_VLAN 17 -#define W_FT_TOS 8 -#define W_FT_PROTOCOL 8 -#define W_FT_ETHERTYPE 16 -#define W_FT_MACMATCH 9 -#define W_FT_MPSHITTYPE 3 -#define W_FT_FRAGMENTATION 1 - -/* Some of the Compressed Filter Tuple fields have internal structure. These - * bit shifts/masks describe those structures. All shifts are relative to the - * base position of the fields within the Compressed Filter Tuple - */ -#define S_FT_VLAN_VLD 16 -#define V_FT_VLAN_VLD(x) ((x) << S_FT_VLAN_VLD) -#define F_FT_VLAN_VLD V_FT_VLAN_VLD(1U) +#define HOSTBUSY_S 17 +#define HOSTBUSY_V(x) ((x) << HOSTBUSY_S) +#define HOSTBUSY_F HOSTBUSY_V(1U) + +#define HOSTWRITE_S 16 +#define HOSTWRITE_V(x) ((x) << HOSTWRITE_S) +#define HOSTWRITE_F HOSTWRITE_V(1U) + +#define CIM_IBQ_DBG_CFG_A 0x7b60 + +#define IBQDBGADDR_S 16 +#define IBQDBGADDR_M 0xfffU +#define IBQDBGADDR_V(x) ((x) << IBQDBGADDR_S) +#define IBQDBGADDR_G(x) (((x) >> IBQDBGADDR_S) & IBQDBGADDR_M) + +#define IBQDBGBUSY_S 1 +#define IBQDBGBUSY_V(x) ((x) << IBQDBGBUSY_S) +#define IBQDBGBUSY_F IBQDBGBUSY_V(1U) + +#define IBQDBGEN_S 0 +#define IBQDBGEN_V(x) ((x) << IBQDBGEN_S) +#define IBQDBGEN_F IBQDBGEN_V(1U) + +#define CIM_OBQ_DBG_CFG_A 0x7b64 + +#define OBQDBGADDR_S 16 +#define OBQDBGADDR_M 0xfffU +#define OBQDBGADDR_V(x) ((x) << OBQDBGADDR_S) +#define OBQDBGADDR_G(x) (((x) >> OBQDBGADDR_S) & OBQDBGADDR_M) + +#define OBQDBGBUSY_S 1 +#define OBQDBGBUSY_V(x) ((x) << OBQDBGBUSY_S) +#define OBQDBGBUSY_F OBQDBGBUSY_V(1U) + +#define OBQDBGEN_S 0 +#define OBQDBGEN_V(x) ((x) << OBQDBGEN_S) +#define OBQDBGEN_F OBQDBGEN_V(1U) + +#define CIM_IBQ_DBG_DATA_A 0x7b68 +#define CIM_OBQ_DBG_DATA_A 0x7b6c + +#define UPDBGLARDEN_S 1 +#define UPDBGLARDEN_V(x) ((x) << UPDBGLARDEN_S) +#define UPDBGLARDEN_F UPDBGLARDEN_V(1U) + +#define UPDBGLAEN_S 0 +#define UPDBGLAEN_V(x) ((x) << UPDBGLAEN_S) +#define UPDBGLAEN_F UPDBGLAEN_V(1U) + +#define UPDBGLARDPTR_S 2 +#define UPDBGLARDPTR_M 0xfffU +#define UPDBGLARDPTR_V(x) ((x) << UPDBGLARDPTR_S) + +#define UPDBGLAWRPTR_S 16 +#define UPDBGLAWRPTR_M 0xfffU +#define UPDBGLAWRPTR_G(x) (((x) >> UPDBGLAWRPTR_S) & UPDBGLAWRPTR_M) + +#define UPDBGLACAPTPCONLY_S 30 +#define UPDBGLACAPTPCONLY_V(x) ((x) << UPDBGLACAPTPCONLY_S) +#define UPDBGLACAPTPCONLY_F UPDBGLACAPTPCONLY_V(1U) + +#define CIM_QUEUE_CONFIG_REF_A 0x7b48 +#define CIM_QUEUE_CONFIG_CTRL_A 0x7b4c + +#define CIMQSIZE_S 24 +#define CIMQSIZE_M 0x3fU +#define CIMQSIZE_G(x) (((x) >> CIMQSIZE_S) & CIMQSIZE_M) + +#define CIMQBASE_S 16 +#define CIMQBASE_M 0x3fU +#define CIMQBASE_G(x) (((x) >> CIMQBASE_S) & CIMQBASE_M) + +#define QUEFULLTHRSH_S 0 +#define QUEFULLTHRSH_M 0x1ffU +#define QUEFULLTHRSH_G(x) (((x) >> QUEFULLTHRSH_S) & QUEFULLTHRSH_M) + +#define UP_IBQ_0_RDADDR_A 0x10 +#define UP_IBQ_0_SHADOW_RDADDR_A 0x280 +#define UP_OBQ_0_REALADDR_A 0x104 +#define UP_OBQ_0_SHADOW_REALADDR_A 0x394 + +#define IBQRDADDR_S 0 +#define IBQRDADDR_M 0x1fffU +#define IBQRDADDR_G(x) (((x) >> IBQRDADDR_S) & IBQRDADDR_M) + +#define IBQWRADDR_S 0 +#define IBQWRADDR_M 0x1fffU +#define IBQWRADDR_G(x) (((x) >> IBQWRADDR_S) & IBQWRADDR_M) + +#define QUERDADDR_S 0 +#define QUERDADDR_M 0x7fffU +#define QUERDADDR_G(x) (((x) >> QUERDADDR_S) & QUERDADDR_M) + +#define QUEREMFLITS_S 0 +#define QUEREMFLITS_M 0x7ffU +#define QUEREMFLITS_G(x) (((x) >> QUEREMFLITS_S) & QUEREMFLITS_M) + +#define QUEEOPCNT_S 16 +#define QUEEOPCNT_M 0xfffU +#define QUEEOPCNT_G(x) (((x) >> QUEEOPCNT_S) & QUEEOPCNT_M) + +#define QUESOPCNT_S 0 +#define QUESOPCNT_M 0xfffU +#define QUESOPCNT_G(x) (((x) >> QUESOPCNT_S) & QUESOPCNT_M) -#define S_FT_VNID_ID_VF 0 -#define V_FT_VNID_ID_VF(x) ((x) << S_FT_VNID_ID_VF) +#define OBQSELECT_S 4 +#define OBQSELECT_V(x) ((x) << OBQSELECT_S) +#define OBQSELECT_F OBQSELECT_V(1U) -#define S_FT_VNID_ID_PF 7 -#define V_FT_VNID_ID_PF(x) ((x) << S_FT_VNID_ID_PF) +#define IBQSELECT_S 3 +#define IBQSELECT_V(x) ((x) << IBQSELECT_S) +#define IBQSELECT_F IBQSELECT_V(1U) -#define S_FT_VNID_ID_VLD 16 -#define V_FT_VNID_ID_VLD(x) ((x) << S_FT_VNID_ID_VLD) +#define QUENUMSELECT_S 0 +#define QUENUMSELECT_V(x) ((x) << QUENUMSELECT_S) #endif /* __T4_REGS_H */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_values.h b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h new file mode 100644 index 000000000000..19b2dcf6acde --- /dev/null +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_values.h @@ -0,0 +1,124 @@ +/* + * This file is part of the Chelsio T4 Ethernet driver for Linux. + * + * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __T4_VALUES_H__ +#define __T4_VALUES_H__ + +/* This file contains definitions for various T4 register value hardware + * constants. The types of values encoded here are predominantly those for + * register fields which control "modal" behavior. For the most part, we do + * not include definitions for register fields which are simple numeric + * metrics, etc. + */ + +/* SGE register field values. + */ + +/* CONTROL1 register */ +#define RXPKTCPLMODE_SPLIT_X 1 + +#define INGPCIEBOUNDARY_SHIFT_X 5 +#define INGPCIEBOUNDARY_32B_X 0 + +#define INGPADBOUNDARY_SHIFT_X 5 + +/* CONTROL2 register */ +#define INGPACKBOUNDARY_SHIFT_X 5 +#define INGPACKBOUNDARY_16B_X 0 + +/* GTS register */ +#define SGE_TIMERREGS 6 +#define TIMERREG_COUNTER0_X 0 + +/* T5 and later support a new BAR2-based doorbell mechanism for Egress Queues. + * The User Doorbells are each 128 bytes in length with a Simple Doorbell at + * offsets 8x and a Write Combining single 64-byte Egress Queue Unit + * (IDXSIZE_UNIT_X) Gather Buffer interface at offset 64. For Ingress Queues, + * we have a Going To Sleep register at offsets 8x+4. + * + * As noted above, we have many instances of the Simple Doorbell and Going To + * Sleep registers at offsets 8x and 8x+4, respectively. We want to use a + * non-64-byte aligned offset for the Simple Doorbell in order to attempt to + * avoid buffering of the writes to the Simple Doorbell and we want to use a + * non-contiguous offset for the Going To Sleep writes in order to avoid + * possible combining between them. + */ +#define SGE_UDB_SIZE 128 +#define SGE_UDB_KDOORBELL 8 +#define SGE_UDB_GTS 20 +#define SGE_UDB_WCDOORBELL 64 + +/* CIM register field values. + */ +#define X_MBOWNER_FW 1 +#define X_MBOWNER_PL 2 + +/* PCI-E definitions */ +#define WINDOW_SHIFT_X 10 +#define PCIEOFST_SHIFT_X 10 + +/* TP_VLAN_PRI_MAP controls which subset of fields will be present in the + * Compressed Filter Tuple for LE filters. Each bit set in TP_VLAN_PRI_MAP + * selects for a particular field being present. These fields, when present + * in the Compressed Filter Tuple, have the following widths in bits. + */ +#define FT_FCOE_W 1 +#define FT_PORT_W 3 +#define FT_VNIC_ID_W 17 +#define FT_VLAN_W 17 +#define FT_TOS_W 8 +#define FT_PROTOCOL_W 8 +#define FT_ETHERTYPE_W 16 +#define FT_MACMATCH_W 9 +#define FT_MPSHITTYPE_W 3 +#define FT_FRAGMENTATION_W 1 + +/* Some of the Compressed Filter Tuple fields have internal structure. These + * bit shifts/masks describe those structures. All shifts are relative to the + * base position of the fields within the Compressed Filter Tuple + */ +#define FT_VLAN_VLD_S 16 +#define FT_VLAN_VLD_V(x) ((x) << FT_VLAN_VLD_S) +#define FT_VLAN_VLD_F FT_VLAN_VLD_V(1U) + +#define FT_VNID_ID_VF_S 0 +#define FT_VNID_ID_VF_V(x) ((x) << FT_VNID_ID_VF_S) + +#define FT_VNID_ID_PF_S 7 +#define FT_VNID_ID_PF_V(x) ((x) << FT_VNID_ID_PF_S) + +#define FT_VNID_ID_VLD_S 16 +#define FT_VNID_ID_VLD_V(x) ((x) << FT_VNID_ID_VLD_S) + +#endif /* __T4_VALUES_H__ */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h index 7c0aec85137a..9b353a88cbda 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h @@ -673,6 +673,7 @@ enum fw_cmd_opcodes { FW_RSS_IND_TBL_CMD = 0x20, FW_RSS_GLB_CONFIG_CMD = 0x22, FW_RSS_VI_CONFIG_CMD = 0x23, + FW_DEVLOG_CMD = 0x25, FW_CLIP_CMD = 0x28, FW_LASTC2E_CMD = 0x40, FW_ERROR_CMD = 0x80, @@ -1058,9 +1059,11 @@ enum fw_params_param_dev { FW_PARAMS_PARAM_DEV_FWREV = 0x0B, FW_PARAMS_PARAM_DEV_TPREV = 0x0C, FW_PARAMS_PARAM_DEV_CF = 0x0D, + FW_PARAMS_PARAM_DEV_DIAG = 0x11, FW_PARAMS_PARAM_DEV_MAXORDIRD_QP = 0x13, /* max supported QP IRD/ORD */ FW_PARAMS_PARAM_DEV_MAXIRD_ADAPTER = 0x14, /* max supported adap IRD */ FW_PARAMS_PARAM_DEV_ULPTX_MEMWRITE_DSGL = 0x17, + FW_PARAMS_PARAM_DEV_FWCACHE = 0x18, }; /* @@ -1120,6 +1123,16 @@ enum fw_params_param_dmaq { FW_PARAMS_PARAM_DMAQ_EQ_DCBPRIO_ETH = 0x13, }; +enum fw_params_param_dev_diag { + FW_PARAM_DEV_DIAG_TMP = 0x00, + FW_PARAM_DEV_DIAG_VDD = 0x01, +}; + +enum fw_params_param_dev_fwcache { + FW_PARAM_DEV_FWCACHE_FLUSH = 0x00, + FW_PARAM_DEV_FWCACHE_FLUSHINV = 0x01, +}; + #define FW_PARAMS_MNEM_S 24 #define FW_PARAMS_MNEM_V(x) ((x) << FW_PARAMS_MNEM_S) @@ -3005,21 +3018,29 @@ enum fw_hdr_chip { #define FW_HDR_FW_VER_MAJOR_S 24 #define FW_HDR_FW_VER_MAJOR_M 0xff +#define FW_HDR_FW_VER_MAJOR_V(x) \ + ((x) << FW_HDR_FW_VER_MAJOR_S) #define FW_HDR_FW_VER_MAJOR_G(x) \ (((x) >> FW_HDR_FW_VER_MAJOR_S) & FW_HDR_FW_VER_MAJOR_M) #define FW_HDR_FW_VER_MINOR_S 16 #define FW_HDR_FW_VER_MINOR_M 0xff +#define FW_HDR_FW_VER_MINOR_V(x) \ + ((x) << FW_HDR_FW_VER_MINOR_S) #define FW_HDR_FW_VER_MINOR_G(x) \ (((x) >> FW_HDR_FW_VER_MINOR_S) & FW_HDR_FW_VER_MINOR_M) #define FW_HDR_FW_VER_MICRO_S 8 #define FW_HDR_FW_VER_MICRO_M 0xff +#define FW_HDR_FW_VER_MICRO_V(x) \ + ((x) << FW_HDR_FW_VER_MICRO_S) #define FW_HDR_FW_VER_MICRO_G(x) \ (((x) >> FW_HDR_FW_VER_MICRO_S) & FW_HDR_FW_VER_MICRO_M) #define FW_HDR_FW_VER_BUILD_S 0 #define FW_HDR_FW_VER_BUILD_M 0xff +#define FW_HDR_FW_VER_BUILD_V(x) \ + ((x) << FW_HDR_FW_VER_BUILD_S) #define FW_HDR_FW_VER_BUILD_G(x) \ (((x) >> FW_HDR_FW_VER_BUILD_S) & FW_HDR_FW_VER_BUILD_M) @@ -3038,4 +3059,84 @@ enum fw_hdr_flags { FW_HDR_FLAGS_RESET_HALT = 0x00000001, }; +/* length of the formatting string */ +#define FW_DEVLOG_FMT_LEN 192 + +/* maximum number of the formatting string parameters */ +#define FW_DEVLOG_FMT_PARAMS_NUM 8 + +/* priority levels */ +enum fw_devlog_level { + FW_DEVLOG_LEVEL_EMERG = 0x0, + FW_DEVLOG_LEVEL_CRIT = 0x1, + FW_DEVLOG_LEVEL_ERR = 0x2, + FW_DEVLOG_LEVEL_NOTICE = 0x3, + FW_DEVLOG_LEVEL_INFO = 0x4, + FW_DEVLOG_LEVEL_DEBUG = 0x5, + FW_DEVLOG_LEVEL_MAX = 0x5, +}; + +/* facilities that may send a log message */ +enum fw_devlog_facility { + FW_DEVLOG_FACILITY_CORE = 0x00, + FW_DEVLOG_FACILITY_CF = 0x01, + FW_DEVLOG_FACILITY_SCHED = 0x02, + FW_DEVLOG_FACILITY_TIMER = 0x04, + FW_DEVLOG_FACILITY_RES = 0x06, + FW_DEVLOG_FACILITY_HW = 0x08, + FW_DEVLOG_FACILITY_FLR = 0x10, + FW_DEVLOG_FACILITY_DMAQ = 0x12, + FW_DEVLOG_FACILITY_PHY = 0x14, + FW_DEVLOG_FACILITY_MAC = 0x16, + FW_DEVLOG_FACILITY_PORT = 0x18, + FW_DEVLOG_FACILITY_VI = 0x1A, + FW_DEVLOG_FACILITY_FILTER = 0x1C, + FW_DEVLOG_FACILITY_ACL = 0x1E, + FW_DEVLOG_FACILITY_TM = 0x20, + FW_DEVLOG_FACILITY_QFC = 0x22, + FW_DEVLOG_FACILITY_DCB = 0x24, + FW_DEVLOG_FACILITY_ETH = 0x26, + FW_DEVLOG_FACILITY_OFLD = 0x28, + FW_DEVLOG_FACILITY_RI = 0x2A, + FW_DEVLOG_FACILITY_ISCSI = 0x2C, + FW_DEVLOG_FACILITY_FCOE = 0x2E, + FW_DEVLOG_FACILITY_FOISCSI = 0x30, + FW_DEVLOG_FACILITY_FOFCOE = 0x32, + FW_DEVLOG_FACILITY_MAX = 0x32, +}; + +/* log message format */ +struct fw_devlog_e { + __be64 timestamp; + __be32 seqno; + __be16 reserved1; + __u8 level; + __u8 facility; + __u8 fmt[FW_DEVLOG_FMT_LEN]; + __be32 params[FW_DEVLOG_FMT_PARAMS_NUM]; + __be32 reserved3[4]; +}; + +struct fw_devlog_cmd { + __be32 op_to_write; + __be32 retval_len16; + __u8 level; + __u8 r2[7]; + __be32 memtype_devlog_memaddr16_devlog; + __be32 memsize_devlog; + __be32 r3[2]; +}; + +#define FW_DEVLOG_CMD_MEMTYPE_DEVLOG_S 28 +#define FW_DEVLOG_CMD_MEMTYPE_DEVLOG_M 0xf +#define FW_DEVLOG_CMD_MEMTYPE_DEVLOG_G(x) \ + (((x) >> FW_DEVLOG_CMD_MEMTYPE_DEVLOG_S) & \ + FW_DEVLOG_CMD_MEMTYPE_DEVLOG_M) + +#define FW_DEVLOG_CMD_MEMADDR16_DEVLOG_S 0 +#define FW_DEVLOG_CMD_MEMADDR16_DEVLOG_M 0xfffffff +#define FW_DEVLOG_CMD_MEMADDR16_DEVLOG_G(x) \ + (((x) >> FW_DEVLOG_CMD_MEMADDR16_DEVLOG_S) & \ + FW_DEVLOG_CMD_MEMADDR16_DEVLOG_M) + #endif /* _T4FW_INTERFACE_H_ */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_version.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_version.h new file mode 100644 index 000000000000..e2bd3f747858 --- /dev/null +++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_version.h @@ -0,0 +1,48 @@ +/* + * This file is part of the Chelsio T4 Ethernet driver for Linux. + * + * Copyright (c) 2003-2014 Chelsio Communications, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __T4FW_VERSION_H__ +#define __T4FW_VERSION_H__ + +#define T4FW_VERSION_MAJOR 0x01 +#define T4FW_VERSION_MINOR 0x0C +#define T4FW_VERSION_MICRO 0x19 +#define T4FW_VERSION_BUILD 0x00 + +#define T5FW_VERSION_MAJOR 0x01 +#define T5FW_VERSION_MINOR 0x0C +#define T5FW_VERSION_MICRO 0x19 +#define T5FW_VERSION_BUILD 0x00 + +#endif diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c index 2215d432a059..122e2964e63b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c @@ -380,9 +380,9 @@ static void qenable(struct sge_rspq *rspq) * enable interrupts. */ t4_write_reg(rspq->adapter, T4VF_SGE_BASE_ADDR + SGE_VF_GTS, - CIDXINC(0) | - SEINTARM(rspq->intr_params) | - INGRESSQID(rspq->cntxt_id)); + CIDXINC_V(0) | + SEINTARM_V(rspq->intr_params) | + INGRESSQID_V(rspq->cntxt_id)); } /* @@ -403,9 +403,9 @@ static void enable_rx(struct adapter *adapter) */ if (adapter->flags & USING_MSI) t4_write_reg(adapter, T4VF_SGE_BASE_ADDR + SGE_VF_GTS, - CIDXINC(0) | - SEINTARM(s->intrq.intr_params) | - INGRESSQID(s->intrq.cntxt_id)); + CIDXINC_V(0) | + SEINTARM_V(s->intrq.intr_params) | + INGRESSQID_V(s->intrq.cntxt_id)); } @@ -450,7 +450,7 @@ static int fwevtq_handler(struct sge_rspq *rspq, const __be64 *rsp, /* FW can send EGR_UPDATEs encapsulated in a CPL_FW4_MSG. */ const struct cpl_sge_egr_update *p = (void *)(rsp + 3); - opcode = G_CPL_OPCODE(ntohl(p->opcode_qid)); + opcode = CPL_OPCODE_G(ntohl(p->opcode_qid)); if (opcode != CPL_SGE_EGR_UPDATE) { dev_err(adapter->pdev_dev, "unexpected FW4/CPL %#x on FW event queue\n" , opcode); @@ -471,7 +471,7 @@ static int fwevtq_handler(struct sge_rspq *rspq, const __be64 *rsp, * free TX Queue Descriptors ... */ const struct cpl_sge_egr_update *p = cpl; - unsigned int qid = EGR_QID(be32_to_cpu(p->opcode_qid)); + unsigned int qid = EGR_QID_G(be32_to_cpu(p->opcode_qid)); struct sge *s = &adapter->sge; struct sge_txq *tq; struct sge_eth_txq *txq; @@ -1673,7 +1673,7 @@ static void cxgb4vf_get_regs(struct net_device *dev, reg_block_dump(adapter, regbuf, T4VF_PL_BASE_ADDR + T4VF_MOD_MAP_PL_FIRST, T4VF_PL_BASE_ADDR + (is_t4(adapter->params.chip) - ? A_PL_VF_WHOAMI : A_PL_VF_REVISION)); + ? PL_VF_WHOAMI_A : PL_VF_REVISION_A)); reg_block_dump(adapter, regbuf, T4VF_CIM_BASE_ADDR + T4VF_MOD_MAP_CIM_FIRST, T4VF_CIM_BASE_ADDR + T4VF_MOD_MAP_CIM_LAST); @@ -2294,26 +2294,22 @@ static int adap_init0(struct adapter *adapter) * threshold values from the SGE parameters. */ s->timer_val[0] = core_ticks_to_us(adapter, - TIMERVALUE0_GET(sge_params->sge_timer_value_0_and_1)); + TIMERVALUE0_G(sge_params->sge_timer_value_0_and_1)); s->timer_val[1] = core_ticks_to_us(adapter, - TIMERVALUE1_GET(sge_params->sge_timer_value_0_and_1)); + TIMERVALUE1_G(sge_params->sge_timer_value_0_and_1)); s->timer_val[2] = core_ticks_to_us(adapter, - TIMERVALUE0_GET(sge_params->sge_timer_value_2_and_3)); + TIMERVALUE0_G(sge_params->sge_timer_value_2_and_3)); s->timer_val[3] = core_ticks_to_us(adapter, - TIMERVALUE1_GET(sge_params->sge_timer_value_2_and_3)); + TIMERVALUE1_G(sge_params->sge_timer_value_2_and_3)); s->timer_val[4] = core_ticks_to_us(adapter, - TIMERVALUE0_GET(sge_params->sge_timer_value_4_and_5)); + TIMERVALUE0_G(sge_params->sge_timer_value_4_and_5)); s->timer_val[5] = core_ticks_to_us(adapter, - TIMERVALUE1_GET(sge_params->sge_timer_value_4_and_5)); + TIMERVALUE1_G(sge_params->sge_timer_value_4_and_5)); - s->counter_val[0] = - THRESHOLD_0_GET(sge_params->sge_ingress_rx_threshold); - s->counter_val[1] = - THRESHOLD_1_GET(sge_params->sge_ingress_rx_threshold); - s->counter_val[2] = - THRESHOLD_2_GET(sge_params->sge_ingress_rx_threshold); - s->counter_val[3] = - THRESHOLD_3_GET(sge_params->sge_ingress_rx_threshold); + s->counter_val[0] = THRESHOLD_0_G(sge_params->sge_ingress_rx_threshold); + s->counter_val[1] = THRESHOLD_1_G(sge_params->sge_ingress_rx_threshold); + s->counter_val[2] = THRESHOLD_2_G(sge_params->sge_ingress_rx_threshold); + s->counter_val[3] = THRESHOLD_3_G(sge_params->sge_ingress_rx_threshold); /* * Grab our Virtual Interface resource allocation, extract the @@ -2430,7 +2426,7 @@ static void cfg_queues(struct adapter *adapter) */ n10g = 0; for_each_port(adapter, pidx) - n10g += is_10g_port(&adap2pinfo(adapter, pidx)->link_cfg); + n10g += is_x_10g_port(&adap2pinfo(adapter, pidx)->link_cfg); /* * We default to 1 queue per non-10G port and up to # of cores queues diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c index f7fd1317d996..0545f0de1c52 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c @@ -47,6 +47,7 @@ #include "t4vf_defs.h" #include "../cxgb4/t4_regs.h" +#include "../cxgb4/t4_values.h" #include "../cxgb4/t4fw_api.h" #include "../cxgb4/t4_msg.h" @@ -531,11 +532,11 @@ static inline void ring_fl_db(struct adapter *adapter, struct sge_fl *fl) */ if (fl->pend_cred >= FL_PER_EQ_UNIT) { if (is_t4(adapter->params.chip)) - val = PIDX(fl->pend_cred / FL_PER_EQ_UNIT); + val = PIDX_V(fl->pend_cred / FL_PER_EQ_UNIT); else - val = PIDX_T5(fl->pend_cred / FL_PER_EQ_UNIT) | - DBTYPE(1); - val |= DBPRIO(1); + val = PIDX_T5_V(fl->pend_cred / FL_PER_EQ_UNIT) | + DBTYPE_F; + val |= DBPRIO_F; /* Make sure all memory writes to the Free List queue are * committed before we tell the hardware about them. @@ -549,9 +550,9 @@ static inline void ring_fl_db(struct adapter *adapter, struct sge_fl *fl) if (unlikely(fl->bar2_addr == NULL)) { t4_write_reg(adapter, T4VF_SGE_BASE_ADDR + SGE_VF_KDOORBELL, - QID(fl->cntxt_id) | val); + QID_V(fl->cntxt_id) | val); } else { - writel(val | QID(fl->bar2_qid), + writel(val | QID_V(fl->bar2_qid), fl->bar2_addr + SGE_UDB_KDOORBELL); /* This Write memory Barrier will force the write to @@ -925,7 +926,7 @@ static void write_sgl(const struct sk_buff *skb, struct sge_txq *tq, } sgl->cmd_nsge = htonl(ULPTX_CMD_V(ULP_TX_SC_DSGL) | - ULPTX_NSGE(nfrags)); + ULPTX_NSGE_V(nfrags)); if (likely(--nfrags == 0)) return; /* @@ -979,12 +980,12 @@ static inline void ring_tx_db(struct adapter *adapter, struct sge_txq *tq, * doorbell mechanism; otherwise use the new BAR2 mechanism. */ if (unlikely(tq->bar2_addr == NULL)) { - u32 val = PIDX(n); + u32 val = PIDX_V(n); t4_write_reg(adapter, T4VF_SGE_BASE_ADDR + SGE_VF_KDOORBELL, - QID(tq->cntxt_id) | val); + QID_V(tq->cntxt_id) | val); } else { - u32 val = PIDX_T5(n); + u32 val = PIDX_T5_V(n); /* T4 and later chips share the same PIDX field offset within * the doorbell, but T5 and later shrank the field in order to @@ -992,7 +993,7 @@ static inline void ring_tx_db(struct adapter *adapter, struct sge_txq *tq, * large in the first place (14 bits) so we just use the T5 * and later limits and warn if a Queue ID is too large. */ - WARN_ON(val & DBPRIO(1)); + WARN_ON(val & DBPRIO_F); /* If we're only writing a single Egress Unit and the BAR2 * Queue ID is 0, we can use the Write Combining Doorbell @@ -1023,7 +1024,7 @@ static inline void ring_tx_db(struct adapter *adapter, struct sge_txq *tq, count--; } } else - writel(val | QID(tq->bar2_qid), + writel(val | QID_V(tq->bar2_qid), tq->bar2_addr + SGE_UDB_KDOORBELL); /* This Write Memory Barrier will force the write to the User @@ -1325,9 +1326,9 @@ int t4vf_eth_xmit(struct sk_buff *skb, struct net_device *dev) * If there's a VLAN tag present, add that to the list of things to * do in this Work Request. */ - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { txq->vlan_ins++; - cntrl |= TXPKT_VLAN_VLD | TXPKT_VLAN(vlan_tx_tag_get(skb)); + cntrl |= TXPKT_VLAN_VLD | TXPKT_VLAN(skb_vlan_tag_get(skb)); } /* @@ -1603,7 +1604,7 @@ int t4vf_ethrx_handler(struct sge_rspq *rspq, const __be64 *rsp, * If this is a good TCP packet and we have Generic Receive Offload * enabled, handle the packet in the GRO path. */ - if ((pkt->l2info & cpu_to_be32(RXF_TCP)) && + if ((pkt->l2info & cpu_to_be32(RXF_TCP_F)) && (rspq->netdev->features & NETIF_F_GRO) && csum_ok && !pkt->ip_frag) { do_gro(rxq, gl, pkt); @@ -1625,7 +1626,7 @@ int t4vf_ethrx_handler(struct sge_rspq *rspq, const __be64 *rsp, rxq->stats.pkts++; if (csum_ok && !pkt->err_vec && - (be32_to_cpu(pkt->l2info) & (RXF_UDP|RXF_TCP))) { + (be32_to_cpu(pkt->l2info) & (RXF_UDP_F | RXF_TCP_F))) { if (!pkt->ip_frag) skb->ip_summed = CHECKSUM_UNNECESSARY; else { @@ -1875,13 +1876,13 @@ static int napi_rx_handler(struct napi_struct *napi, int budget) if (unlikely(work_done == 0)) rspq->unhandled_irqs++; - val = CIDXINC(work_done) | SEINTARM(intr_params); + val = CIDXINC_V(work_done) | SEINTARM_V(intr_params); if (is_t4(rspq->adapter->params.chip)) { t4_write_reg(rspq->adapter, T4VF_SGE_BASE_ADDR + SGE_VF_GTS, - val | INGRESSQID((u32)rspq->cntxt_id)); + val | INGRESSQID_V((u32)rspq->cntxt_id)); } else { - writel(val | INGRESSQID(rspq->bar2_qid), + writel(val | INGRESSQID_V(rspq->bar2_qid), rspq->bar2_addr + SGE_UDB_GTS); wmb(); } @@ -1975,12 +1976,12 @@ static unsigned int process_intrq(struct adapter *adapter) rspq_next(intrq); } - val = CIDXINC(work_done) | SEINTARM(intrq->intr_params); + val = CIDXINC_V(work_done) | SEINTARM_V(intrq->intr_params); if (is_t4(adapter->params.chip)) t4_write_reg(adapter, T4VF_SGE_BASE_ADDR + SGE_VF_GTS, - val | INGRESSQID(intrq->cntxt_id)); + val | INGRESSQID_V(intrq->cntxt_id)); else { - writel(val | INGRESSQID(intrq->bar2_qid), + writel(val | INGRESSQID_V(intrq->bar2_qid), intrq->bar2_addr + SGE_UDB_GTS); wmb(); } @@ -2583,7 +2584,7 @@ int t4vf_sge_init(struct adapter *adapter) fl0, fl1); return -EINVAL; } - if ((sge_params->sge_control & RXPKTCPLMODE_MASK) == 0) { + if ((sge_params->sge_control & RXPKTCPLMODE_F) == 0) { dev_err(adapter->pdev_dev, "bad SGE CPL MODE\n"); return -EINVAL; } @@ -2593,9 +2594,9 @@ int t4vf_sge_init(struct adapter *adapter) */ if (fl1) s->fl_pg_order = ilog2(fl1) - PAGE_SHIFT; - s->stat_len = ((sge_params->sge_control & EGRSTATUSPAGESIZE_MASK) + s->stat_len = ((sge_params->sge_control & EGRSTATUSPAGESIZE_F) ? 128 : 64); - s->pktshift = PKTSHIFT_GET(sge_params->sge_control); + s->pktshift = PKTSHIFT_G(sge_params->sge_control); /* T4 uses a single control field to specify both the PCIe Padding and * Packing Boundary. T5 introduced the ability to specify these @@ -2607,8 +2608,8 @@ int t4vf_sge_init(struct adapter *adapter) * end doing this because it would initialize the Padding Boundary and * leave the Packing Boundary initialized to 0 (16 bytes).) */ - ingpadboundary = 1 << (INGPADBOUNDARY_GET(sge_params->sge_control) + - X_INGPADBOUNDARY_SHIFT); + ingpadboundary = 1 << (INGPADBOUNDARY_G(sge_params->sge_control) + + INGPADBOUNDARY_SHIFT_X); if (is_t4(adapter->params.chip)) { s->fl_align = ingpadboundary; } else { @@ -2633,7 +2634,7 @@ int t4vf_sge_init(struct adapter *adapter) * Congestion Threshold is in units of 2 Free List pointers.) */ s->fl_starve_thres - = EGRTHRESHOLD_GET(sge_params->sge_congestion_control)*2 + 1; + = EGRTHRESHOLD_G(sge_params->sge_congestion_control)*2 + 1; /* * Set up tasklet timers. diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h index c7b127d93767..b516b12b1884 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h +++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_defs.h @@ -64,8 +64,8 @@ * Mailbox Data in the fixed CIM PF map and the programmable VF map must * match. However, it's a useful convention ... */ -#if T4VF_MBDATA_BASE_ADDR != CIM_PF_MAILBOX_DATA -#error T4VF_MBDATA_BASE_ADDR must match CIM_PF_MAILBOX_DATA! +#if T4VF_MBDATA_BASE_ADDR != CIM_PF_MAILBOX_DATA_A +#error T4VF_MBDATA_BASE_ADDR must match CIM_PF_MAILBOX_DATA_A! #endif /* diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c index 21dc9a20308c..1b5506df35b1 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c @@ -39,6 +39,7 @@ #include "t4vf_defs.h" #include "../cxgb4/t4_regs.h" +#include "../cxgb4/t4_values.h" #include "../cxgb4/t4fw_api.h" /* @@ -137,9 +138,9 @@ int t4vf_wr_mbox_core(struct adapter *adapter, const void *cmd, int size, * Loop trying to get ownership of the mailbox. Return an error * if we can't gain ownership. */ - v = MBOWNER_GET(t4_read_reg(adapter, mbox_ctl)); + v = MBOWNER_G(t4_read_reg(adapter, mbox_ctl)); for (i = 0; v == MBOX_OWNER_NONE && i < 3; i++) - v = MBOWNER_GET(t4_read_reg(adapter, mbox_ctl)); + v = MBOWNER_G(t4_read_reg(adapter, mbox_ctl)); if (v != MBOX_OWNER_DRV) return v == MBOX_OWNER_FW ? -EBUSY : -ETIMEDOUT; @@ -161,7 +162,7 @@ int t4vf_wr_mbox_core(struct adapter *adapter, const void *cmd, int size, t4_read_reg(adapter, mbox_data); /* flush write */ t4_write_reg(adapter, mbox_ctl, - MBMSGVALID | MBOWNER(MBOX_OWNER_FW)); + MBMSGVALID_F | MBOWNER_V(MBOX_OWNER_FW)); t4_read_reg(adapter, mbox_ctl); /* flush write */ /* @@ -183,14 +184,14 @@ int t4vf_wr_mbox_core(struct adapter *adapter, const void *cmd, int size, * If we're the owner, see if this is the reply we wanted. */ v = t4_read_reg(adapter, mbox_ctl); - if (MBOWNER_GET(v) == MBOX_OWNER_DRV) { + if (MBOWNER_G(v) == MBOX_OWNER_DRV) { /* * If the Message Valid bit isn't on, revoke ownership * of the mailbox and continue waiting for our reply. */ - if ((v & MBMSGVALID) == 0) { + if ((v & MBMSGVALID_F) == 0) { t4_write_reg(adapter, mbox_ctl, - MBOWNER(MBOX_OWNER_NONE)); + MBOWNER_V(MBOX_OWNER_NONE)); continue; } @@ -216,7 +217,7 @@ int t4vf_wr_mbox_core(struct adapter *adapter, const void *cmd, int size, & FW_CMD_REQUEST_F) != 0); } t4_write_reg(adapter, mbox_ctl, - MBOWNER(MBOX_OWNER_NONE)); + MBOWNER_V(MBOX_OWNER_NONE)); return -FW_CMD_RETVAL_G(v); } } @@ -323,6 +324,8 @@ int t4vf_port_init(struct adapter *adapter, int pidx) return v; v = be32_to_cpu(port_rpl.u.info.lstatus_to_modtype); + pi->mdio_addr = (v & FW_PORT_CMD_MDIOCAP_F) ? + FW_PORT_CMD_MDIOADDR_G(v) : -1; pi->port_type = FW_PORT_CMD_PTYPE_G(v); pi->mod_type = FW_PORT_MOD_TYPE_NA; @@ -528,19 +531,19 @@ int t4vf_get_sge_params(struct adapter *adapter) int v; params[0] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | - FW_PARAMS_PARAM_XYZ_V(SGE_CONTROL)); + FW_PARAMS_PARAM_XYZ_V(SGE_CONTROL_A)); params[1] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | - FW_PARAMS_PARAM_XYZ_V(SGE_HOST_PAGE_SIZE)); + FW_PARAMS_PARAM_XYZ_V(SGE_HOST_PAGE_SIZE_A)); params[2] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | - FW_PARAMS_PARAM_XYZ_V(SGE_FL_BUFFER_SIZE0)); + FW_PARAMS_PARAM_XYZ_V(SGE_FL_BUFFER_SIZE0_A)); params[3] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | - FW_PARAMS_PARAM_XYZ_V(SGE_FL_BUFFER_SIZE1)); + FW_PARAMS_PARAM_XYZ_V(SGE_FL_BUFFER_SIZE1_A)); params[4] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | - FW_PARAMS_PARAM_XYZ_V(SGE_TIMER_VALUE_0_AND_1)); + FW_PARAMS_PARAM_XYZ_V(SGE_TIMER_VALUE_0_AND_1_A)); params[5] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | - FW_PARAMS_PARAM_XYZ_V(SGE_TIMER_VALUE_2_AND_3)); + FW_PARAMS_PARAM_XYZ_V(SGE_TIMER_VALUE_2_AND_3_A)); params[6] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | - FW_PARAMS_PARAM_XYZ_V(SGE_TIMER_VALUE_4_AND_5)); + FW_PARAMS_PARAM_XYZ_V(SGE_TIMER_VALUE_4_AND_5_A)); v = t4vf_query_params(adapter, 7, params, vals); if (v) return v; @@ -576,9 +579,9 @@ int t4vf_get_sge_params(struct adapter *adapter) } params[0] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | - FW_PARAMS_PARAM_XYZ_V(SGE_INGRESS_RX_THRESHOLD)); + FW_PARAMS_PARAM_XYZ_V(SGE_INGRESS_RX_THRESHOLD_A)); params[1] = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_REG) | - FW_PARAMS_PARAM_XYZ_V(SGE_CONM_CTRL)); + FW_PARAMS_PARAM_XYZ_V(SGE_CONM_CTRL_A)); v = t4vf_query_params(adapter, 2, params, vals); if (v) return v; @@ -615,8 +618,8 @@ int t4vf_get_sge_params(struct adapter *adapter) * the driver can just use it. */ whoami = t4_read_reg(adapter, - T4VF_PL_BASE_ADDR + A_PL_VF_WHOAMI); - pf = SOURCEPF_GET(whoami); + T4VF_PL_BASE_ADDR + PL_VF_WHOAMI_A); + pf = SOURCEPF_G(whoami); s_hps = (HOSTPAGESIZEPF0_S + (HOSTPAGESIZEPF1_S - HOSTPAGESIZEPF0_S) * pf); @@ -628,10 +631,10 @@ int t4vf_get_sge_params(struct adapter *adapter) (QUEUESPERPAGEPF1_S - QUEUESPERPAGEPF0_S) * pf); sge_params->sge_vf_eq_qpp = ((sge_params->sge_egress_queues_per_page >> s_qpp) - & QUEUESPERPAGEPF0_MASK); + & QUEUESPERPAGEPF0_M); sge_params->sge_vf_iq_qpp = ((sge_params->sge_ingress_queues_per_page >> s_qpp) - & QUEUESPERPAGEPF0_MASK); + & QUEUESPERPAGEPF0_M); } return 0; @@ -1590,7 +1593,7 @@ int t4vf_prep_adapter(struct adapter *adapter) break; case CHELSIO_T5: - chipid = G_REV(t4_read_reg(adapter, A_PL_VF_REV)); + chipid = REV_G(t4_read_reg(adapter, PL_VF_REV_A)); adapter->params.chip |= CHELSIO_CHIP_CODE(CHELSIO_T5, chipid); break; } diff --git a/drivers/net/ethernet/cirrus/Kconfig b/drivers/net/ethernet/cirrus/Kconfig index 7403dff8f14a..905ac5f5d9a6 100644 --- a/drivers/net/ethernet/cirrus/Kconfig +++ b/drivers/net/ethernet/cirrus/Kconfig @@ -32,7 +32,8 @@ config CS89x0 will be called cs89x0. config CS89x0_PLATFORM - bool "CS89x0 platform driver support" + bool "CS89x0 platform driver support" if HAS_IOPORT_MAP + default !HAS_IOPORT_MAP depends on CS89x0 help Say Y to compile the cs89x0 driver as a platform driver. This diff --git a/drivers/net/ethernet/cirrus/ep93xx_eth.c b/drivers/net/ethernet/cirrus/ep93xx_eth.c index 3a12c096ea1c..de9f7c97d916 100644 --- a/drivers/net/ethernet/cirrus/ep93xx_eth.c +++ b/drivers/net/ethernet/cirrus/ep93xx_eth.c @@ -475,8 +475,7 @@ static void ep93xx_free_buffers(struct ep93xx_priv *ep) if (d) dma_unmap_single(dev, d, PKT_BUF_SIZE, DMA_FROM_DEVICE); - if (ep->rx_buf[i] != NULL) - kfree(ep->rx_buf[i]); + kfree(ep->rx_buf[i]); } for (i = 0; i < TX_QUEUE_ENTRIES; i++) { @@ -486,8 +485,7 @@ static void ep93xx_free_buffers(struct ep93xx_priv *ep) if (d) dma_unmap_single(dev, d, PKT_BUF_SIZE, DMA_TO_DEVICE); - if (ep->tx_buf[i] != NULL) - kfree(ep->tx_buf[i]); + kfree(ep->tx_buf[i]); } dma_free_coherent(dev, sizeof(struct ep93xx_descs), ep->descs, diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h index 25c4d88853d8..84b6a2b46aec 100644 --- a/drivers/net/ethernet/cisco/enic/enic.h +++ b/drivers/net/ethernet/cisco/enic/enic.h @@ -33,7 +33,7 @@ #define DRV_NAME "enic" #define DRV_DESCRIPTION "Cisco VIC Ethernet NIC Driver" -#define DRV_VERSION "2.1.1.67" +#define DRV_VERSION "2.1.1.83" #define DRV_COPYRIGHT "Copyright 2008-2013 Cisco Systems, Inc" #define ENIC_BARS_MAX 6 @@ -188,6 +188,7 @@ struct enic { struct enic_rfs_flw_tbl rfs_h; u32 rx_copybreak; u8 rss_key[ENIC_RSS_LEN]; + struct vnic_gen_stats gen_stats; }; static inline struct device *enic_get_dev(struct enic *enic) @@ -242,6 +243,19 @@ static inline unsigned int enic_msix_notify_intr(struct enic *enic) return enic->rq_count + enic->wq_count + 1; } +static inline int enic_dma_map_check(struct enic *enic, dma_addr_t dma_addr) +{ + if (unlikely(pci_dma_mapping_error(enic->pdev, dma_addr))) { + net_warn_ratelimited("%s: PCI dma mapping failed!\n", + enic->netdev->name); + enic->gen_stats.dma_map_error++; + + return -ENOMEM; + } + + return 0; +} + void enic_reset_addr_lists(struct enic *enic); int enic_sriov_enabled(struct enic *enic); int enic_is_valid_vf(struct enic *enic, int vf); diff --git a/drivers/net/ethernet/cisco/enic/enic_dev.c b/drivers/net/ethernet/cisco/enic/enic_dev.c index 87ddc44b590e..f8d2a6a34282 100644 --- a/drivers/net/ethernet/cisco/enic/enic_dev.c +++ b/drivers/net/ethernet/cisco/enic/enic_dev.c @@ -177,40 +177,6 @@ int enic_dev_intr_coal_timer_info(struct enic *enic) return err; } -int enic_vnic_dev_deinit(struct enic *enic) -{ - int err; - - spin_lock_bh(&enic->devcmd_lock); - err = vnic_dev_deinit(enic->vdev); - spin_unlock_bh(&enic->devcmd_lock); - - return err; -} - -int enic_dev_init_prov2(struct enic *enic, struct vic_provinfo *vp) -{ - int err; - - spin_lock_bh(&enic->devcmd_lock); - err = vnic_dev_init_prov2(enic->vdev, - (u8 *)vp, vic_provinfo_size(vp)); - spin_unlock_bh(&enic->devcmd_lock); - - return err; -} - -int enic_dev_deinit_done(struct enic *enic, int *status) -{ - int err; - - spin_lock_bh(&enic->devcmd_lock); - err = vnic_dev_deinit_done(enic->vdev, status); - spin_unlock_bh(&enic->devcmd_lock); - - return err; -} - /* rtnl lock is held */ int enic_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, u16 vid) { @@ -237,28 +203,6 @@ int enic_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, u16 vid) return err; } -int enic_dev_enable2(struct enic *enic, int active) -{ - int err; - - spin_lock_bh(&enic->devcmd_lock); - err = vnic_dev_enable2(enic->vdev, active); - spin_unlock_bh(&enic->devcmd_lock); - - return err; -} - -int enic_dev_enable2_done(struct enic *enic, int *status) -{ - int err; - - spin_lock_bh(&enic->devcmd_lock); - err = vnic_dev_enable2_done(enic->vdev, status); - spin_unlock_bh(&enic->devcmd_lock); - - return err; -} - int enic_dev_status_to_errno(int devcmd_status) { switch (devcmd_status) { diff --git a/drivers/net/ethernet/cisco/enic/enic_dev.h b/drivers/net/ethernet/cisco/enic/enic_dev.h index 10bb970b2f35..f5bb058b3f96 100644 --- a/drivers/net/ethernet/cisco/enic/enic_dev.h +++ b/drivers/net/ethernet/cisco/enic/enic_dev.h @@ -55,11 +55,6 @@ int enic_dev_set_ig_vlan_rewrite_mode(struct enic *enic); int enic_dev_enable(struct enic *enic); int enic_dev_disable(struct enic *enic); int enic_dev_intr_coal_timer_info(struct enic *enic); -int enic_vnic_dev_deinit(struct enic *enic); -int enic_dev_init_prov2(struct enic *enic, struct vic_provinfo *vp); -int enic_dev_deinit_done(struct enic *enic, int *status); -int enic_dev_enable2(struct enic *enic, int arg); -int enic_dev_enable2_done(struct enic *enic, int *status); int enic_dev_status_to_errno(int devcmd_status); #endif /* _ENIC_DEV_H_ */ diff --git a/drivers/net/ethernet/cisco/enic/enic_ethtool.c b/drivers/net/ethernet/cisco/enic/enic_ethtool.c index eba1eb846d34..28d9ca675a27 100644 --- a/drivers/net/ethernet/cisco/enic/enic_ethtool.c +++ b/drivers/net/ethernet/cisco/enic/enic_ethtool.c @@ -24,6 +24,7 @@ #include "enic_dev.h" #include "enic_clsf.h" #include "vnic_rss.h" +#include "vnic_stats.h" struct enic_stat { char name[ETH_GSTRING_LEN]; @@ -40,6 +41,11 @@ struct enic_stat { .index = offsetof(struct vnic_rx_stats, stat) / sizeof(u64) \ } +#define ENIC_GEN_STAT(stat) { \ + .name = #stat, \ + .index = offsetof(struct vnic_gen_stats, stat) / sizeof(u64)\ +} + static const struct enic_stat enic_tx_stats[] = { ENIC_TX_STAT(tx_frames_ok), ENIC_TX_STAT(tx_unicast_frames_ok), @@ -78,10 +84,15 @@ static const struct enic_stat enic_rx_stats[] = { ENIC_RX_STAT(rx_frames_to_max), }; +static const struct enic_stat enic_gen_stats[] = { + ENIC_GEN_STAT(dma_map_error), +}; + static const unsigned int enic_n_tx_stats = ARRAY_SIZE(enic_tx_stats); static const unsigned int enic_n_rx_stats = ARRAY_SIZE(enic_rx_stats); +static const unsigned int enic_n_gen_stats = ARRAY_SIZE(enic_gen_stats); -void enic_intr_coal_set_rx(struct enic *enic, u32 timer) +static void enic_intr_coal_set_rx(struct enic *enic, u32 timer) { int i; int intr; @@ -146,6 +157,10 @@ static void enic_get_strings(struct net_device *netdev, u32 stringset, memcpy(data, enic_rx_stats[i].name, ETH_GSTRING_LEN); data += ETH_GSTRING_LEN; } + for (i = 0; i < enic_n_gen_stats; i++) { + memcpy(data, enic_gen_stats[i].name, ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } break; } } @@ -154,7 +169,7 @@ static int enic_get_sset_count(struct net_device *netdev, int sset) { switch (sset) { case ETH_SS_STATS: - return enic_n_tx_stats + enic_n_rx_stats; + return enic_n_tx_stats + enic_n_rx_stats + enic_n_gen_stats; default: return -EOPNOTSUPP; } @@ -173,6 +188,8 @@ static void enic_get_ethtool_stats(struct net_device *netdev, *(data++) = ((u64 *)&vstats->tx)[enic_tx_stats[i].index]; for (i = 0; i < enic_n_rx_stats; i++) *(data++) = ((u64 *)&vstats->rx)[enic_rx_stats[i].index]; + for (i = 0; i < enic_n_gen_stats; i++) + *(data++) = ((u64 *)&enic->gen_stats)[enic_gen_stats[i].index]; } static u32 enic_get_msglevel(struct net_device *netdev) diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index 705f334ebb85..9cbe038a388e 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -45,6 +45,7 @@ #ifdef CONFIG_NET_RX_BUSY_POLL #include <net/busy_poll.h> #endif +#include <linux/crash_dump.h> #include "cq_enet_desc.h" #include "vnic_dev.h" @@ -88,7 +89,7 @@ MODULE_DEVICE_TABLE(pci, enic_id_table); * coalescing timer values * {rx_rate in Mbps, mapping percentage of the range} */ -struct enic_intr_mod_table mod_table[ENIC_MAX_COALESCE_TIMERS + 1] = { +static struct enic_intr_mod_table mod_table[ENIC_MAX_COALESCE_TIMERS + 1] = { {4000, 0}, {4400, 10}, {5060, 20}, @@ -105,7 +106,7 @@ struct enic_intr_mod_table mod_table[ENIC_MAX_COALESCE_TIMERS + 1] = { /* This table helps the driver to pick different ranges for rx coalescing * timer depending on the link speed. */ -struct enic_intr_mod_range mod_range[ENIC_MAX_LINK_SPEEDS] = { +static struct enic_intr_mod_range mod_range[ENIC_MAX_LINK_SPEEDS] = { {0, 0}, /* 0 - 4 Gbps */ {0, 3}, /* 4 - 10 Gbps */ {3, 6}, /* 10 - 40 Gbps */ @@ -351,80 +352,94 @@ static irqreturn_t enic_isr_msix_notify(int irq, void *data) return IRQ_HANDLED; } -static inline void enic_queue_wq_skb_cont(struct enic *enic, - struct vnic_wq *wq, struct sk_buff *skb, - unsigned int len_left, int loopback) +static int enic_queue_wq_skb_cont(struct enic *enic, struct vnic_wq *wq, + struct sk_buff *skb, unsigned int len_left, + int loopback) { const skb_frag_t *frag; + dma_addr_t dma_addr; /* Queue additional data fragments */ for (frag = skb_shinfo(skb)->frags; len_left; frag++) { len_left -= skb_frag_size(frag); - enic_queue_wq_desc_cont(wq, skb, - skb_frag_dma_map(&enic->pdev->dev, - frag, 0, skb_frag_size(frag), - DMA_TO_DEVICE), - skb_frag_size(frag), - (len_left == 0), /* EOP? */ - loopback); + dma_addr = skb_frag_dma_map(&enic->pdev->dev, frag, 0, + skb_frag_size(frag), + DMA_TO_DEVICE); + if (unlikely(enic_dma_map_check(enic, dma_addr))) + return -ENOMEM; + enic_queue_wq_desc_cont(wq, skb, dma_addr, skb_frag_size(frag), + (len_left == 0), /* EOP? */ + loopback); } + + return 0; } -static inline void enic_queue_wq_skb_vlan(struct enic *enic, - struct vnic_wq *wq, struct sk_buff *skb, - int vlan_tag_insert, unsigned int vlan_tag, int loopback) +static int enic_queue_wq_skb_vlan(struct enic *enic, struct vnic_wq *wq, + struct sk_buff *skb, int vlan_tag_insert, + unsigned int vlan_tag, int loopback) { unsigned int head_len = skb_headlen(skb); unsigned int len_left = skb->len - head_len; int eop = (len_left == 0); + dma_addr_t dma_addr; + int err = 0; + + dma_addr = pci_map_single(enic->pdev, skb->data, head_len, + PCI_DMA_TODEVICE); + if (unlikely(enic_dma_map_check(enic, dma_addr))) + return -ENOMEM; /* Queue the main skb fragment. The fragments are no larger * than max MTU(9000)+ETH_HDR_LEN(14) bytes, which is less * than WQ_ENET_MAX_DESC_LEN length. So only one descriptor * per fragment is queued. */ - enic_queue_wq_desc(wq, skb, - pci_map_single(enic->pdev, skb->data, - head_len, PCI_DMA_TODEVICE), - head_len, - vlan_tag_insert, vlan_tag, - eop, loopback); + enic_queue_wq_desc(wq, skb, dma_addr, head_len, vlan_tag_insert, + vlan_tag, eop, loopback); if (!eop) - enic_queue_wq_skb_cont(enic, wq, skb, len_left, loopback); + err = enic_queue_wq_skb_cont(enic, wq, skb, len_left, loopback); + + return err; } -static inline void enic_queue_wq_skb_csum_l4(struct enic *enic, - struct vnic_wq *wq, struct sk_buff *skb, - int vlan_tag_insert, unsigned int vlan_tag, int loopback) +static int enic_queue_wq_skb_csum_l4(struct enic *enic, struct vnic_wq *wq, + struct sk_buff *skb, int vlan_tag_insert, + unsigned int vlan_tag, int loopback) { unsigned int head_len = skb_headlen(skb); unsigned int len_left = skb->len - head_len; unsigned int hdr_len = skb_checksum_start_offset(skb); unsigned int csum_offset = hdr_len + skb->csum_offset; int eop = (len_left == 0); + dma_addr_t dma_addr; + int err = 0; + + dma_addr = pci_map_single(enic->pdev, skb->data, head_len, + PCI_DMA_TODEVICE); + if (unlikely(enic_dma_map_check(enic, dma_addr))) + return -ENOMEM; /* Queue the main skb fragment. The fragments are no larger * than max MTU(9000)+ETH_HDR_LEN(14) bytes, which is less * than WQ_ENET_MAX_DESC_LEN length. So only one descriptor * per fragment is queued. */ - enic_queue_wq_desc_csum_l4(wq, skb, - pci_map_single(enic->pdev, skb->data, - head_len, PCI_DMA_TODEVICE), - head_len, - csum_offset, - hdr_len, - vlan_tag_insert, vlan_tag, - eop, loopback); + enic_queue_wq_desc_csum_l4(wq, skb, dma_addr, head_len, csum_offset, + hdr_len, vlan_tag_insert, vlan_tag, eop, + loopback); if (!eop) - enic_queue_wq_skb_cont(enic, wq, skb, len_left, loopback); + err = enic_queue_wq_skb_cont(enic, wq, skb, len_left, loopback); + + return err; } -static inline void enic_queue_wq_skb_tso(struct enic *enic, - struct vnic_wq *wq, struct sk_buff *skb, unsigned int mss, - int vlan_tag_insert, unsigned int vlan_tag, int loopback) +static int enic_queue_wq_skb_tso(struct enic *enic, struct vnic_wq *wq, + struct sk_buff *skb, unsigned int mss, + int vlan_tag_insert, unsigned int vlan_tag, + int loopback) { unsigned int frag_len_left = skb_headlen(skb); unsigned int len_left = skb->len - frag_len_left; @@ -454,20 +469,19 @@ static inline void enic_queue_wq_skb_tso(struct enic *enic, */ while (frag_len_left) { len = min(frag_len_left, (unsigned int)WQ_ENET_MAX_DESC_LEN); - dma_addr = pci_map_single(enic->pdev, skb->data + offset, - len, PCI_DMA_TODEVICE); - enic_queue_wq_desc_tso(wq, skb, - dma_addr, - len, - mss, hdr_len, - vlan_tag_insert, vlan_tag, - eop && (len == frag_len_left), loopback); + dma_addr = pci_map_single(enic->pdev, skb->data + offset, len, + PCI_DMA_TODEVICE); + if (unlikely(enic_dma_map_check(enic, dma_addr))) + return -ENOMEM; + enic_queue_wq_desc_tso(wq, skb, dma_addr, len, mss, hdr_len, + vlan_tag_insert, vlan_tag, + eop && (len == frag_len_left), loopback); frag_len_left -= len; offset += len; } if (eop) - return; + return 0; /* Queue WQ_ENET_MAX_DESC_LEN length descriptors * for additional data fragments @@ -483,16 +497,18 @@ static inline void enic_queue_wq_skb_tso(struct enic *enic, dma_addr = skb_frag_dma_map(&enic->pdev->dev, frag, offset, len, DMA_TO_DEVICE); - enic_queue_wq_desc_cont(wq, skb, - dma_addr, - len, - (len_left == 0) && - (len == frag_len_left), /* EOP? */ - loopback); + if (unlikely(enic_dma_map_check(enic, dma_addr))) + return -ENOMEM; + enic_queue_wq_desc_cont(wq, skb, dma_addr, len, + (len_left == 0) && + (len == frag_len_left),/*EOP*/ + loopback); frag_len_left -= len; offset += len; } } + + return 0; } static inline void enic_queue_wq_skb(struct enic *enic, @@ -502,25 +518,42 @@ static inline void enic_queue_wq_skb(struct enic *enic, unsigned int vlan_tag = 0; int vlan_tag_insert = 0; int loopback = 0; + int err; - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { /* VLAN tag from trunking driver */ vlan_tag_insert = 1; - vlan_tag = vlan_tx_tag_get(skb); + vlan_tag = skb_vlan_tag_get(skb); } else if (enic->loop_enable) { vlan_tag = enic->loop_tag; loopback = 1; } if (mss) - enic_queue_wq_skb_tso(enic, wq, skb, mss, - vlan_tag_insert, vlan_tag, loopback); + err = enic_queue_wq_skb_tso(enic, wq, skb, mss, + vlan_tag_insert, vlan_tag, + loopback); else if (skb->ip_summed == CHECKSUM_PARTIAL) - enic_queue_wq_skb_csum_l4(enic, wq, skb, - vlan_tag_insert, vlan_tag, loopback); + err = enic_queue_wq_skb_csum_l4(enic, wq, skb, vlan_tag_insert, + vlan_tag, loopback); else - enic_queue_wq_skb_vlan(enic, wq, skb, - vlan_tag_insert, vlan_tag, loopback); + err = enic_queue_wq_skb_vlan(enic, wq, skb, vlan_tag_insert, + vlan_tag, loopback); + if (unlikely(err)) { + struct vnic_wq_buf *buf; + + buf = wq->to_use->prev; + /* while not EOP of previous pkt && queue not empty. + * For all non EOP bufs, os_buf is NULL. + */ + while (!buf->os_buf && (buf->next != wq->to_clean)) { + enic_free_wq_buf(wq, buf); + wq->ring.desc_avail++; + buf = buf->prev; + } + wq->to_use = buf->next; + dev_kfree_skb(skb); + } } /* netif_tx_lock held, process context with BHs disabled, or BH */ @@ -950,8 +983,12 @@ static int enic_rq_alloc_buf(struct vnic_rq *rq) if (!skb) return -ENOMEM; - dma_addr = pci_map_single(enic->pdev, skb->data, - len, PCI_DMA_FROMDEVICE); + dma_addr = pci_map_single(enic->pdev, skb->data, len, + PCI_DMA_FROMDEVICE); + if (unlikely(enic_dma_map_check(enic, dma_addr))) { + dev_kfree_skb(skb); + return -ENOMEM; + } enic_queue_rq_desc(rq, skb, os_buf_index, dma_addr, len); @@ -1266,7 +1303,7 @@ static void enic_set_rx_cpu_rmap(struct enic *enic) #endif /* CONFIG_RFS_ACCEL */ #ifdef CONFIG_NET_RX_BUSY_POLL -int enic_busy_poll(struct napi_struct *napi) +static int enic_busy_poll(struct napi_struct *napi) { struct net_device *netdev = napi->dev; struct enic *enic = netdev_priv(netdev); @@ -1335,7 +1372,7 @@ static int enic_poll_msix_rq(struct napi_struct *napi, int budget) int err; if (!enic_poll_lock_napi(&enic->rq[rq])) - return work_done; + return budget; /* Service RQ */ @@ -1616,7 +1653,7 @@ static int enic_open(struct net_device *netdev) if (vnic_rq_desc_used(&enic->rq[i]) == 0) { netdev_err(netdev, "Unable to alloc receive buffers\n"); err = -ENOMEM; - goto err_out_notify_unset; + goto err_out_free_rq; } } @@ -1649,7 +1686,9 @@ static int enic_open(struct net_device *netdev) return 0; -err_out_notify_unset: +err_out_free_rq: + for (i = 0; i < enic->rq_count; i++) + vnic_rq_clean(&enic->rq[i], enic_free_rq_buf); enic_dev_notify_unset(enic); err_out_free_intr: enic_free_intr(enic); @@ -2229,6 +2268,18 @@ static void enic_dev_deinit(struct enic *enic) enic_clear_intr_mode(enic); } +static void enic_kdump_kernel_config(struct enic *enic) +{ + if (is_kdump_kernel()) { + dev_info(enic_get_dev(enic), "Running from within kdump kernel. Using minimal resources\n"); + enic->rq_count = 1; + enic->wq_count = 1; + enic->config.rq_desc_count = ENIC_MIN_RQ_DESCS; + enic->config.wq_desc_count = ENIC_MIN_WQ_DESCS; + enic->config.mtu = min_t(u16, 1500, enic->config.mtu); + } +} + static int enic_dev_init(struct enic *enic) { struct device *dev = enic_get_dev(enic); @@ -2258,6 +2309,10 @@ static int enic_dev_init(struct enic *enic) enic_get_res_counts(enic); + /* modify resource count if we are in kdump_kernel + */ + enic_kdump_kernel_config(enic); + /* Set interrupt mode based on resource counts and system * capabilities */ diff --git a/drivers/net/ethernet/cisco/enic/vnic_stats.h b/drivers/net/ethernet/cisco/enic/vnic_stats.h index 77750ec93954..74c81ed6fdab 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_stats.h +++ b/drivers/net/ethernet/cisco/enic/vnic_stats.h @@ -62,6 +62,11 @@ struct vnic_rx_stats { u64 rsvd[16]; }; +/* Generic statistics */ +struct vnic_gen_stats { + u64 dma_map_error; +}; + struct vnic_stats { struct vnic_tx_stats tx; struct vnic_rx_stats rx; diff --git a/drivers/net/ethernet/cisco/enic/vnic_wq.c b/drivers/net/ethernet/cisco/enic/vnic_wq.c index 3e6b8d54dafc..b5a1c937fad2 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_wq.c +++ b/drivers/net/ethernet/cisco/enic/vnic_wq.c @@ -47,11 +47,14 @@ static int vnic_wq_alloc_bufs(struct vnic_wq *wq) wq->ring.desc_size * buf->index; if (buf->index + 1 == count) { buf->next = wq->bufs[0]; + buf->next->prev = buf; break; } else if (j + 1 == VNIC_WQ_BUF_BLK_ENTRIES(count)) { buf->next = wq->bufs[i + 1]; + buf->next->prev = buf; } else { buf->next = buf + 1; + buf->next->prev = buf; buf++; } } diff --git a/drivers/net/ethernet/cisco/enic/vnic_wq.h b/drivers/net/ethernet/cisco/enic/vnic_wq.h index 816f1ad6072f..296154351823 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_wq.h +++ b/drivers/net/ethernet/cisco/enic/vnic_wq.h @@ -62,6 +62,7 @@ struct vnic_wq_buf { uint8_t cq_entry; /* Gets completion event from hw */ uint8_t desc_skip_cnt; /* Num descs to occupy */ uint8_t compressed_send; /* Both hdr and payload in one desc */ + struct vnic_wq_buf *prev; }; /* Break the vnic_wq_buf allocations into blocks of 32/64 entries */ diff --git a/drivers/net/ethernet/davicom/dm9000.c b/drivers/net/ethernet/davicom/dm9000.c index ef0bb58750e6..c0a7813603c3 100644 --- a/drivers/net/ethernet/davicom/dm9000.c +++ b/drivers/net/ethernet/davicom/dm9000.c @@ -36,6 +36,9 @@ #include <linux/platform_device.h> #include <linux/irq.h> #include <linux/slab.h> +#include <linux/regulator/consumer.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> #include <asm/delay.h> #include <asm/irq.h> @@ -1426,11 +1429,48 @@ dm9000_probe(struct platform_device *pdev) struct dm9000_plat_data *pdata = dev_get_platdata(&pdev->dev); struct board_info *db; /* Point a board information structure */ struct net_device *ndev; + struct device *dev = &pdev->dev; const unsigned char *mac_src; int ret = 0; int iosize; int i; u32 id_val; + int reset_gpios; + enum of_gpio_flags flags; + struct regulator *power; + + power = devm_regulator_get(dev, "vcc"); + if (IS_ERR(power)) { + if (PTR_ERR(power) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_dbg(dev, "no regulator provided\n"); + } else { + ret = regulator_enable(power); + if (ret != 0) { + dev_err(dev, + "Failed to enable power regulator: %d\n", ret); + return ret; + } + dev_dbg(dev, "regulator enabled\n"); + } + + reset_gpios = of_get_named_gpio_flags(dev->of_node, "reset-gpios", 0, + &flags); + if (gpio_is_valid(reset_gpios)) { + ret = devm_gpio_request_one(dev, reset_gpios, flags, + "dm9000_reset"); + if (ret) { + dev_err(dev, "failed to request reset gpio %d: %d\n", + reset_gpios, ret); + return -ENODEV; + } + + /* According to manual PWRST# Low Period Min 1ms */ + msleep(2); + gpio_set_value(reset_gpios, 1); + /* Needs 3ms to read eeprom when PWRST is deasserted */ + msleep(4); + } if (!pdata) { pdata = dm9000_parse_dt(&pdev->dev); diff --git a/drivers/net/ethernet/dec/tulip/winbond-840.c b/drivers/net/ethernet/dec/tulip/winbond-840.c index 6aa887e0e1cb..9beb3d34d4ba 100644 --- a/drivers/net/ethernet/dec/tulip/winbond-840.c +++ b/drivers/net/ethernet/dec/tulip/winbond-840.c @@ -904,7 +904,7 @@ static void init_registers(struct net_device *dev) } #elif defined(__powerpc__) || defined(__i386__) || defined(__alpha__) || defined(__ia64__) || defined(__x86_64__) i |= 0xE000; -#elif defined(CONFIG_SPARC) || defined (CONFIG_PARISC) +#elif defined(CONFIG_SPARC) || defined (CONFIG_PARISC) || defined(CONFIG_ARM) i |= 0x4800; #else #warning Processor architecture undefined diff --git a/drivers/net/ethernet/dnet.c b/drivers/net/ethernet/dnet.c index a379c3e4b57f..13d00a38a5bd 100644 --- a/drivers/net/ethernet/dnet.c +++ b/drivers/net/ethernet/dnet.c @@ -398,13 +398,8 @@ static int dnet_poll(struct napi_struct *napi, int budget) * break out of while loop if there are no more * packets waiting */ - if (!(dnet_readl(bp, RX_FIFO_WCNT) >> 16)) { - napi_complete(napi); - int_enable = dnet_readl(bp, INTR_ENB); - int_enable |= DNET_INTR_SRC_RX_CMDFIFOAF; - dnet_writel(bp, int_enable, INTR_ENB); - return 0; - } + if (!(dnet_readl(bp, RX_FIFO_WCNT) >> 16)) + break; cmd_word = dnet_readl(bp, RX_LEN_FIFO); pkt_len = cmd_word & 0xFFFF; @@ -433,20 +428,17 @@ static int dnet_poll(struct napi_struct *napi, int budget) "size %u.\n", dev->name, pkt_len); } - budget -= npackets; - if (npackets < budget) { /* We processed all packets available. Tell NAPI it can - * stop polling then re-enable rx interrupts */ + * stop polling then re-enable rx interrupts. + */ napi_complete(napi); int_enable = dnet_readl(bp, INTR_ENB); int_enable |= DNET_INTR_SRC_RX_CMDFIFOAF; dnet_writel(bp, int_enable, INTR_ENB); - return 0; } - /* There are still packets waiting */ - return 1; + return npackets; } static irqreturn_t dnet_interrupt(int irq, void *dev_id) diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index 712e7f8e1df7..27de37aa90af 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -59,26 +59,6 @@ #define OC_SUBSYS_DEVICE_ID3 0xE612 #define OC_SUBSYS_DEVICE_ID4 0xE652 -static inline char *nic_name(struct pci_dev *pdev) -{ - switch (pdev->device) { - case OC_DEVICE_ID1: - return OC_NAME; - case OC_DEVICE_ID2: - return OC_NAME_BE; - case OC_DEVICE_ID3: - case OC_DEVICE_ID4: - return OC_NAME_LANCER; - case BE_DEVICE_ID2: - return BE3_NAME; - case OC_DEVICE_ID5: - case OC_DEVICE_ID6: - return OC_NAME_SH; - default: - return BE_NAME; - } -} - /* Number of bytes of an RX frame that are copied to skb->data */ #define BE_HDR_LEN ((u16) 64) /* allocate extra space to allow tunneling decapsulation without head reallocation */ @@ -243,7 +223,6 @@ struct be_tx_stats { u64 tx_bytes; u64 tx_pkts; u64 tx_reqs; - u64 tx_wrbs; u64 tx_compl; ulong tx_jiffies; u32 tx_stops; @@ -266,6 +245,9 @@ struct be_tx_obj { /* Remember the skbs that were transmitted */ struct sk_buff *sent_skb_list[TX_Q_LEN]; struct be_tx_stats stats; + u16 pend_wrb_cnt; /* Number of WRBs yet to be given to HW */ + u16 last_req_wrb_cnt; /* wrb cnt of the last req in the Q */ + u16 last_req_hdr; /* index of the last req's hdr-wrb */ } ____cacheline_aligned_in_smp; /* Struct to remember the pages posted for rx frags */ @@ -379,15 +361,14 @@ enum vf_state { ASSIGNED = 1 }; -#define BE_FLAGS_LINK_STATUS_INIT 1 -#define BE_FLAGS_SRIOV_ENABLED (1 << 2) -#define BE_FLAGS_WORKER_SCHEDULED (1 << 3) -#define BE_FLAGS_VLAN_PROMISC (1 << 4) -#define BE_FLAGS_MCAST_PROMISC (1 << 5) -#define BE_FLAGS_NAPI_ENABLED (1 << 9) -#define BE_FLAGS_QNQ_ASYNC_EVT_RCVD (1 << 11) -#define BE_FLAGS_VXLAN_OFFLOADS (1 << 12) -#define BE_FLAGS_SETUP_DONE (1 << 13) +#define BE_FLAGS_LINK_STATUS_INIT BIT(1) +#define BE_FLAGS_SRIOV_ENABLED BIT(2) +#define BE_FLAGS_WORKER_SCHEDULED BIT(3) +#define BE_FLAGS_NAPI_ENABLED BIT(6) +#define BE_FLAGS_QNQ_ASYNC_EVT_RCVD BIT(7) +#define BE_FLAGS_VXLAN_OFFLOADS BIT(8) +#define BE_FLAGS_SETUP_DONE BIT(9) +#define BE_FLAGS_EVT_INCOMPATIBLE_SFP BIT(10) #define BE_UC_PMAC_COUNT 30 #define BE_VF_UC_PMAC_COUNT 2 @@ -397,6 +378,8 @@ enum vf_state { #define LANCER_DELETE_FW_DUMP 0x2 struct phy_info { +/* From SFF-8472 spec */ +#define SFP_VENDOR_NAME_LEN 17 u8 transceiver; u8 autoneg; u8 fc_autoneg; @@ -410,6 +393,8 @@ struct phy_info { u32 advertising; u32 supported; u8 cable_type; + u8 vendor_name[SFP_VENDOR_NAME_LEN]; + u8 vendor_pn[SFP_VENDOR_NAME_LEN]; }; struct be_resources { @@ -467,8 +452,6 @@ struct be_adapter { struct be_drv_stats drv_stats; struct be_aic_obj aic_obj[MAX_EVT_QS]; - u16 vlans_added; - unsigned long vids[BITS_TO_LONGS(VLAN_N_VID)]; u8 vlan_prio_bmap; /* Available Priority BitMap */ u16 recommended_prio; /* Recommended Priority */ struct be_dma_mem rx_filter; /* Cmd DMA mem for rx-filter */ @@ -484,8 +467,15 @@ struct be_adapter { /* Ethtool knobs and info */ char fw_ver[FW_VER_LEN]; char fw_on_flash[FW_VER_LEN]; + + /* IFACE filtering fields */ int if_handle; /* Used to configure filtering */ + u32 if_flags; /* Interface filtering flags */ u32 *pmac_id; /* MAC addr handle used by BE card */ + u32 uc_macs; /* Count of secondary UC MAC programmed */ + unsigned long vids[BITS_TO_LONGS(VLAN_N_VID)]; + u16 vlans_added; + u32 beacon_state; /* for set_phys_id */ bool eeh_error; @@ -493,7 +483,7 @@ struct be_adapter { bool hw_error; u32 port_num; - bool promiscuous; + char port_name; u8 mc_type; u32 function_mode; u32 function_caps; @@ -526,7 +516,6 @@ struct be_adapter { struct phy_info phy; u8 wol_cap; bool wol_en; - u32 uc_macs; /* Count of secondary UC MAC programmed */ u16 asic_rev; u16 qnq_vid; u32 msg_enable; @@ -732,19 +721,6 @@ static inline bool is_ipv4_pkt(struct sk_buff *skb) return skb->protocol == htons(ETH_P_IP) && ip_hdr(skb)->version == 4; } -static inline void be_vf_eth_addr_generate(struct be_adapter *adapter, u8 *mac) -{ - u32 addr; - - addr = jhash(adapter->netdev->dev_addr, ETH_ALEN, 0); - - mac[5] = (u8)(addr & 0xFF); - mac[4] = (u8)((addr >> 8) & 0xFF); - mac[3] = (u8)((addr >> 16) & 0xFF); - /* Use the OUI from the current MAC address */ - memcpy(mac, adapter->netdev->dev_addr, 3); -} - static inline bool be_multi_rxq(const struct be_adapter *adapter) { return adapter->num_rx_qs > 1; @@ -767,129 +743,6 @@ static inline void be_clear_all_error(struct be_adapter *adapter) adapter->fw_timeout = false; } -static inline bool be_is_wol_excluded(struct be_adapter *adapter) -{ - struct pci_dev *pdev = adapter->pdev; - - if (!be_physfn(adapter)) - return true; - - switch (pdev->subsystem_device) { - case OC_SUBSYS_DEVICE_ID1: - case OC_SUBSYS_DEVICE_ID2: - case OC_SUBSYS_DEVICE_ID3: - case OC_SUBSYS_DEVICE_ID4: - return true; - default: - return false; - } -} - -static inline int qnq_async_evt_rcvd(struct be_adapter *adapter) -{ - return adapter->flags & BE_FLAGS_QNQ_ASYNC_EVT_RCVD; -} - -#ifdef CONFIG_NET_RX_BUSY_POLL -static inline bool be_lock_napi(struct be_eq_obj *eqo) -{ - bool status = true; - - spin_lock(&eqo->lock); /* BH is already disabled */ - if (eqo->state & BE_EQ_LOCKED) { - WARN_ON(eqo->state & BE_EQ_NAPI); - eqo->state |= BE_EQ_NAPI_YIELD; - status = false; - } else { - eqo->state = BE_EQ_NAPI; - } - spin_unlock(&eqo->lock); - return status; -} - -static inline void be_unlock_napi(struct be_eq_obj *eqo) -{ - spin_lock(&eqo->lock); /* BH is already disabled */ - - WARN_ON(eqo->state & (BE_EQ_POLL | BE_EQ_NAPI_YIELD)); - eqo->state = BE_EQ_IDLE; - - spin_unlock(&eqo->lock); -} - -static inline bool be_lock_busy_poll(struct be_eq_obj *eqo) -{ - bool status = true; - - spin_lock_bh(&eqo->lock); - if (eqo->state & BE_EQ_LOCKED) { - eqo->state |= BE_EQ_POLL_YIELD; - status = false; - } else { - eqo->state |= BE_EQ_POLL; - } - spin_unlock_bh(&eqo->lock); - return status; -} - -static inline void be_unlock_busy_poll(struct be_eq_obj *eqo) -{ - spin_lock_bh(&eqo->lock); - - WARN_ON(eqo->state & (BE_EQ_NAPI)); - eqo->state = BE_EQ_IDLE; - - spin_unlock_bh(&eqo->lock); -} - -static inline void be_enable_busy_poll(struct be_eq_obj *eqo) -{ - spin_lock_init(&eqo->lock); - eqo->state = BE_EQ_IDLE; -} - -static inline void be_disable_busy_poll(struct be_eq_obj *eqo) -{ - local_bh_disable(); - - /* It's enough to just acquire napi lock on the eqo to stop - * be_busy_poll() from processing any queueus. - */ - while (!be_lock_napi(eqo)) - mdelay(1); - - local_bh_enable(); -} - -#else /* CONFIG_NET_RX_BUSY_POLL */ - -static inline bool be_lock_napi(struct be_eq_obj *eqo) -{ - return true; -} - -static inline void be_unlock_napi(struct be_eq_obj *eqo) -{ -} - -static inline bool be_lock_busy_poll(struct be_eq_obj *eqo) -{ - return false; -} - -static inline void be_unlock_busy_poll(struct be_eq_obj *eqo) -{ -} - -static inline void be_enable_busy_poll(struct be_eq_obj *eqo) -{ -} - -static inline void be_disable_busy_poll(struct be_eq_obj *eqo) -{ -} -#endif /* CONFIG_NET_RX_BUSY_POLL */ - void be_cq_notify(struct be_adapter *adapter, u16 qid, bool arm, u16 num_popped); void be_link_status_update(struct be_adapter *adapter, u8 link_status); @@ -898,16 +751,6 @@ int be_load_fw(struct be_adapter *adapter, u8 *func); bool be_is_wol_supported(struct be_adapter *adapter); bool be_pause_supported(struct be_adapter *adapter); u32 be_get_fw_log_level(struct be_adapter *adapter); - -static inline int fw_major_num(const char *fw_ver) -{ - int fw_major = 0; - - sscanf(fw_ver, "%d.", &fw_major); - - return fw_major; -} - int be_update_queues(struct be_adapter *adapter); int be_poll(struct napi_struct *napi, int budget); diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index fead5c65a4f0..36916cfa70f9 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -19,6 +19,22 @@ #include "be.h" #include "be_cmds.h" +static char *be_port_misconfig_evt_desc[] = { + "A valid SFP module detected", + "Optics faulted/ incorrectly installed/ not installed.", + "Optics of two types installed.", + "Incompatible optics.", + "Unknown port SFP status" +}; + +static char *be_port_misconfig_remedy_desc[] = { + "", + "Reseat optics. If issue not resolved, replace", + "Remove one optic or install matching pair of optics", + "Replace with compatible optics for card to function", + "" +}; + static struct be_cmd_priv_map cmd_priv_map[] = { { OPCODE_ETH_ACPI_WOL_MAGIC_CONFIG, @@ -249,6 +265,29 @@ static void be_async_link_state_process(struct be_adapter *adapter, evt->port_link_status & LINK_STATUS_MASK); } +static void be_async_port_misconfig_event_process(struct be_adapter *adapter, + struct be_mcc_compl *compl) +{ + struct be_async_event_misconfig_port *evt = + (struct be_async_event_misconfig_port *)compl; + u32 sfp_mismatch_evt = le32_to_cpu(evt->event_data_word1); + struct device *dev = &adapter->pdev->dev; + u8 port_misconfig_evt; + + port_misconfig_evt = + ((sfp_mismatch_evt >> (adapter->hba_port_num * 8)) & 0xff); + + /* Log an error message that would allow a user to determine + * whether the SFPs have an issue + */ + dev_info(dev, "Port %c: %s %s", adapter->port_name, + be_port_misconfig_evt_desc[port_misconfig_evt], + be_port_misconfig_remedy_desc[port_misconfig_evt]); + + if (port_misconfig_evt == INCOMPATIBLE_SFP) + adapter->flags |= BE_FLAGS_EVT_INCOMPATIBLE_SFP; +} + /* Grp5 CoS Priority evt */ static void be_async_grp5_cos_priority_process(struct be_adapter *adapter, struct be_mcc_compl *compl) @@ -334,6 +373,16 @@ static void be_async_dbg_evt_process(struct be_adapter *adapter, } } +static void be_async_sliport_evt_process(struct be_adapter *adapter, + struct be_mcc_compl *cmp) +{ + u8 event_type = (cmp->flags >> ASYNC_EVENT_TYPE_SHIFT) & + ASYNC_EVENT_TYPE_MASK; + + if (event_type == ASYNC_EVENT_PORT_MISCONFIG) + be_async_port_misconfig_event_process(adapter, cmp); +} + static inline bool is_link_state_evt(u32 flags) { return ((flags >> ASYNC_EVENT_CODE_SHIFT) & ASYNC_EVENT_CODE_MASK) == @@ -352,6 +401,12 @@ static inline bool is_dbg_evt(u32 flags) ASYNC_EVENT_CODE_QNQ; } +static inline bool is_sliport_evt(u32 flags) +{ + return ((flags >> ASYNC_EVENT_CODE_SHIFT) & ASYNC_EVENT_CODE_MASK) == + ASYNC_EVENT_CODE_SLIPORT; +} + static void be_mcc_event_process(struct be_adapter *adapter, struct be_mcc_compl *compl) { @@ -361,6 +416,8 @@ static void be_mcc_event_process(struct be_adapter *adapter, be_async_grp5_evt_process(adapter, compl); else if (is_dbg_evt(compl->flags)) be_async_dbg_evt_process(adapter, compl); + else if (is_sliport_evt(compl->flags)) + be_async_sliport_evt_process(adapter, compl); } static struct be_mcc_compl *be_mcc_compl_get(struct be_adapter *adapter) @@ -573,7 +630,7 @@ static int lancer_wait_ready(struct be_adapter *adapter) { #define SLIPORT_READY_TIMEOUT 30 u32 sliport_status; - int status = 0, i; + int i; for (i = 0; i < SLIPORT_READY_TIMEOUT; i++) { sliport_status = ioread32(adapter->db + SLIPORT_STATUS_OFFSET); @@ -584,9 +641,9 @@ static int lancer_wait_ready(struct be_adapter *adapter) } if (i == SLIPORT_READY_TIMEOUT) - status = -1; + return sliport_status ? : -1; - return status; + return 0; } static bool lancer_provisioning_error(struct be_adapter *adapter) @@ -624,7 +681,7 @@ int lancer_test_and_set_rdy_state(struct be_adapter *adapter) iowrite32(SLI_PORT_CONTROL_IP_MASK, adapter->db + SLIPORT_CONTROL_OFFSET); - /* check adapter has corrected the error */ + /* check if adapter has corrected the error */ status = lancer_wait_ready(adapter); sliport_status = ioread32(adapter->db + SLIPORT_STATUS_OFFSET); @@ -655,7 +712,11 @@ int be_fw_wait_ready(struct be_adapter *adapter) if (lancer_chip(adapter)) { status = lancer_wait_ready(adapter); - return status; + if (status) { + stage = status; + goto err; + } + return 0; } do { @@ -671,7 +732,8 @@ int be_fw_wait_ready(struct be_adapter *adapter) timeout += 2; } while (timeout < 60); - dev_err(dev, "POST timeout; stage=0x%x\n", stage); +err: + dev_err(dev, "POST timeout; stage=%#x\n", stage); return -1; } @@ -1166,9 +1228,15 @@ static int be_cmd_mccq_ext_create(struct be_adapter *adapter, ctxt, 1); } - /* Subscribe to Link State and Group 5 Events(bits 1 and 5 set) */ - req->async_event_bitmap[0] = cpu_to_le32(0x00000022); - req->async_event_bitmap[0] |= cpu_to_le32(1 << ASYNC_EVENT_CODE_QNQ); + /* Subscribe to Link State, Sliport Event and Group 5 Events + * (bits 1, 5 and 17 set) + */ + req->async_event_bitmap[0] = + cpu_to_le32(BIT(ASYNC_EVENT_CODE_LINK_STATE) | + BIT(ASYNC_EVENT_CODE_GRP_5) | + BIT(ASYNC_EVENT_CODE_QNQ) | + BIT(ASYNC_EVENT_CODE_SLIPORT)); + be_dws_cpu_to_le(ctxt, sizeof(req->context)); be_cmd_page_addrs_prepare(req->pages, ARRAY_SIZE(req->pages), q_mem); @@ -1881,7 +1949,7 @@ err: return status; } -int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value) +static int __be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value) { struct be_mcc_wrb *wrb; struct be_dma_mem *mem = &adapter->rx_filter; @@ -1901,31 +1969,13 @@ int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value) wrb, mem); req->if_id = cpu_to_le32(adapter->if_handle); - if (flags & IFF_PROMISC) { - req->if_flags_mask = cpu_to_le32(BE_IF_FLAGS_PROMISCUOUS | - BE_IF_FLAGS_VLAN_PROMISCUOUS | - BE_IF_FLAGS_MCAST_PROMISCUOUS); - if (value == ON) - req->if_flags = - cpu_to_le32(BE_IF_FLAGS_PROMISCUOUS | - BE_IF_FLAGS_VLAN_PROMISCUOUS | - BE_IF_FLAGS_MCAST_PROMISCUOUS); - } else if (flags & IFF_ALLMULTI) { - req->if_flags_mask = cpu_to_le32(BE_IF_FLAGS_MCAST_PROMISCUOUS); - req->if_flags = cpu_to_le32(BE_IF_FLAGS_MCAST_PROMISCUOUS); - } else if (flags & BE_FLAGS_VLAN_PROMISC) { - req->if_flags_mask = cpu_to_le32(BE_IF_FLAGS_VLAN_PROMISCUOUS); - - if (value == ON) - req->if_flags = - cpu_to_le32(BE_IF_FLAGS_VLAN_PROMISCUOUS); - } else { + req->if_flags_mask = cpu_to_le32(flags); + req->if_flags = (value == ON) ? req->if_flags_mask : 0; + + if (flags & BE_IF_FLAGS_MULTICAST) { struct netdev_hw_addr *ha; int i = 0; - req->if_flags_mask = cpu_to_le32(BE_IF_FLAGS_MULTICAST); - req->if_flags = cpu_to_le32(BE_IF_FLAGS_MULTICAST); - /* Reset mcast promisc mode if already set by setting mask * and not setting flags field */ @@ -1937,24 +1987,26 @@ int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value) memcpy(req->mcast_mac[i++].byte, ha->addr, ETH_ALEN); } - if ((req->if_flags_mask & cpu_to_le32(be_if_cap_flags(adapter))) != - req->if_flags_mask) { - dev_warn(&adapter->pdev->dev, - "Cannot set rx filter flags 0x%x\n", - req->if_flags_mask); - dev_warn(&adapter->pdev->dev, - "Interface is capable of 0x%x flags only\n", - be_if_cap_flags(adapter)); - } - req->if_flags_mask &= cpu_to_le32(be_if_cap_flags(adapter)); - status = be_mcc_notify_wait(adapter); - err: spin_unlock_bh(&adapter->mcc_lock); return status; } +int be_cmd_rx_filter(struct be_adapter *adapter, u32 flags, u32 value) +{ + struct device *dev = &adapter->pdev->dev; + + if ((flags & be_if_cap_flags(adapter)) != flags) { + dev_warn(dev, "Cannot set rx filter flags 0x%x\n", flags); + dev_warn(dev, "Interface is capable of 0x%x flags only\n", + be_if_cap_flags(adapter)); + } + flags &= be_if_cap_flags(adapter); + + return __be_cmd_rx_filter(adapter, flags, value); +} + /* Uses synchrounous mcc */ int be_cmd_set_flow_control(struct be_adapter *adapter, u32 tx_fc, u32 rx_fc) { @@ -2355,6 +2407,24 @@ int be_cmd_query_cable_type(struct be_adapter *adapter) return status; } +int be_cmd_query_sfp_info(struct be_adapter *adapter) +{ + u8 page_data[PAGE_DATA_LEN]; + int status; + + status = be_cmd_read_port_transceiver_data(adapter, TR_PAGE_A0, + page_data); + if (!status) { + strlcpy(adapter->phy.vendor_name, page_data + + SFP_VENDOR_NAME_OFFSET, SFP_VENDOR_NAME_LEN - 1); + strlcpy(adapter->phy.vendor_pn, + page_data + SFP_VENDOR_PN_OFFSET, + SFP_VENDOR_NAME_LEN - 1); + } + + return status; +} + int lancer_cmd_delete_object(struct be_adapter *adapter, const char *obj_name) { struct lancer_cmd_req_delete_object *req; @@ -2431,7 +2501,8 @@ err_unlock: } int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd, - u32 flash_type, u32 flash_opcode, u32 buf_size) + u32 flash_type, u32 flash_opcode, u32 img_offset, + u32 buf_size) { struct be_mcc_wrb *wrb; struct be_cmd_write_flashrom *req; @@ -2452,6 +2523,9 @@ int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd, cmd); req->params.op_type = cpu_to_le32(flash_type); + if (flash_type == OPTYPE_OFFSET_SPECIFIED) + req->params.offset = cpu_to_le32(img_offset); + req->params.op_code = cpu_to_le32(flash_opcode); req->params.data_buf_size = cpu_to_le32(buf_size); @@ -2472,10 +2546,10 @@ err_unlock: } int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc, - u16 optype, int offset) + u16 img_optype, u32 img_offset, u32 crc_offset) { - struct be_mcc_wrb *wrb; struct be_cmd_read_flash_crc *req; + struct be_mcc_wrb *wrb; int status; spin_lock_bh(&adapter->mcc_lock); @@ -2491,9 +2565,13 @@ int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc, OPCODE_COMMON_READ_FLASHROM, sizeof(*req), wrb, NULL); - req->params.op_type = cpu_to_le32(optype); + req->params.op_type = cpu_to_le32(img_optype); + if (img_optype == OPTYPE_OFFSET_SPECIFIED) + req->params.offset = cpu_to_le32(img_offset + crc_offset); + else + req->params.offset = cpu_to_le32(crc_offset); + req->params.op_code = cpu_to_le32(FLASHROM_OPER_REPORT); - req->params.offset = cpu_to_le32(offset); req->params.data_buf_size = cpu_to_le32(0x4); status = be_mcc_notify_wait(adapter); @@ -2742,7 +2820,7 @@ err: return status; } -int be_cmd_set_qos(struct be_adapter *adapter, u32 bps, u32 domain) +static int be_cmd_set_qos(struct be_adapter *adapter, u32 bps, u32 domain) { struct be_mcc_wrb *wrb; struct be_cmd_req_set_qos *req; @@ -3236,6 +3314,24 @@ err: return status; } +static bool be_is_wol_excluded(struct be_adapter *adapter) +{ + struct pci_dev *pdev = adapter->pdev; + + if (!be_physfn(adapter)) + return true; + + switch (pdev->subsystem_device) { + case OC_SUBSYS_DEVICE_ID1: + case OC_SUBSYS_DEVICE_ID2: + case OC_SUBSYS_DEVICE_ID3: + case OC_SUBSYS_DEVICE_ID4: + return true; + default: + return false; + } +} + int be_cmd_get_acpi_wol_cap(struct be_adapter *adapter) { struct be_mcc_wrb *wrb; @@ -3422,42 +3518,34 @@ err: return status; } -int be_cmd_query_port_name(struct be_adapter *adapter, u8 *port_name) +int be_cmd_query_port_name(struct be_adapter *adapter) { - struct be_mcc_wrb *wrb; struct be_cmd_req_get_port_name *req; + struct be_mcc_wrb *wrb; int status; - if (!lancer_chip(adapter)) { - *port_name = adapter->hba_port_num + '0'; - return 0; - } - - spin_lock_bh(&adapter->mcc_lock); - - wrb = wrb_from_mccq(adapter); - if (!wrb) { - status = -EBUSY; - goto err; - } + if (mutex_lock_interruptible(&adapter->mbox_lock)) + return -1; + wrb = wrb_from_mbox(adapter); req = embedded_payload(wrb); be_wrb_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_GET_PORT_NAME, sizeof(*req), wrb, NULL); - req->hdr.version = 1; + if (!BEx_chip(adapter)) + req->hdr.version = 1; - status = be_mcc_notify_wait(adapter); + status = be_mbox_notify_wait(adapter); if (!status) { struct be_cmd_resp_get_port_name *resp = embedded_payload(wrb); - *port_name = resp->port_name[adapter->hba_port_num]; + adapter->port_name = resp->port_name[adapter->hba_port_num]; } else { - *port_name = adapter->hba_port_num + '0'; + adapter->port_name = adapter->hba_port_num + '0'; } -err: - spin_unlock_bh(&adapter->mcc_lock); + + mutex_unlock(&adapter->mbox_lock); return status; } @@ -3751,6 +3839,7 @@ int be_cmd_config_qos(struct be_adapter *adapter, u32 max_rate, u16 link_speed, be_reset_nic_desc(&nic_desc); nic_desc.pf_num = adapter->pf_number; nic_desc.vf_num = domain; + nic_desc.bw_min = 0; if (lancer_chip(adapter)) { nic_desc.hdr.desc_type = NIC_RESOURCE_DESC_TYPE_V0; nic_desc.hdr.desc_len = RESOURCE_DESC_SIZE_V0; @@ -4092,7 +4181,7 @@ int be_cmd_set_logical_link_config(struct be_adapter *adapter, int status; if (BEx_chip(adapter) || lancer_chip(adapter)) - return 0; + return -EOPNOTSUPP; spin_lock_bh(&adapter->mcc_lock); diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h index eb5085d6794f..db761e8e42a3 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.h +++ b/drivers/net/ethernet/emulex/benet/be_cmds.h @@ -44,10 +44,10 @@ struct be_mcc_wrb { } payload; }; -#define CQE_FLAGS_VALID_MASK (1 << 31) -#define CQE_FLAGS_ASYNC_MASK (1 << 30) -#define CQE_FLAGS_COMPLETED_MASK (1 << 28) -#define CQE_FLAGS_CONSUMED_MASK (1 << 27) +#define CQE_FLAGS_VALID_MASK BIT(31) +#define CQE_FLAGS_ASYNC_MASK BIT(30) +#define CQE_FLAGS_COMPLETED_MASK BIT(28) +#define CQE_FLAGS_CONSUMED_MASK BIT(27) /* Completion Status */ enum mcc_base_status { @@ -102,6 +102,8 @@ struct be_mcc_compl { #define ASYNC_EVENT_PVID_STATE 0x3 #define ASYNC_EVENT_CODE_QNQ 0x6 #define ASYNC_DEBUG_EVENT_TYPE_QNQ 1 +#define ASYNC_EVENT_CODE_SLIPORT 0x11 +#define ASYNC_EVENT_PORT_MISCONFIG 0x9 enum { LINK_DOWN = 0x0, @@ -169,6 +171,15 @@ struct be_async_event_qnq { u32 flags; } __packed; +#define INCOMPATIBLE_SFP 0x3 +/* async event indicating misconfigured port */ +struct be_async_event_misconfig_port { + u32 event_data_word1; + u32 event_data_word2; + u32 rsvd0; + u32 flags; +} __packed; + struct be_mcc_mailbox { struct be_mcc_wrb wrb; struct be_mcc_compl compl; @@ -586,6 +597,10 @@ enum be_if_flags { BE_IF_FLAGS_PASS_L3L4_ERRORS | BE_IF_FLAGS_MULTICAST |\ BE_IF_FLAGS_UNTAGGED) +#define BE_IF_FLAGS_ALL_PROMISCUOUS (BE_IF_FLAGS_PROMISCUOUS | \ + BE_IF_FLAGS_VLAN_PROMISCUOUS |\ + BE_IF_FLAGS_MCAST_PROMISCUOUS) + /* An RX interface is an object with one or more MAC addresses and * filtering capabilities. */ struct be_cmd_req_if_create { @@ -1024,6 +1039,8 @@ enum { #define SFP_PLUS_SFF_8472_COMP 0x5E #define SFP_PLUS_CABLE_TYPE_OFFSET 0x8 #define SFP_PLUS_COPPER_CABLE 0x4 +#define SFP_VENDOR_NAME_OFFSET 0x14 +#define SFP_VENDOR_PN_OFFSET 0x28 #define PAGE_DATA_LEN 256 struct be_cmd_resp_port_type { @@ -1091,6 +1108,10 @@ struct be_cmd_req_query_fw_cfg { u32 rsvd[31]; }; +/* ASIC revisions */ +#define ASIC_REV_B0 0x10 +#define ASIC_REV_P2 0x11 + struct be_cmd_resp_query_fw_cfg { struct be_cmd_resp_hdr hdr; u32 be_config_number; @@ -1161,7 +1182,173 @@ struct be_cmd_resp_get_beacon_state { u8 rsvd0[3]; } __packed; +/* Flashrom related descriptors */ +#define MAX_FLASH_COMP 32 + +#define OPTYPE_ISCSI_ACTIVE 0 +#define OPTYPE_REDBOOT 1 +#define OPTYPE_BIOS 2 +#define OPTYPE_PXE_BIOS 3 +#define OPTYPE_OFFSET_SPECIFIED 7 +#define OPTYPE_FCOE_BIOS 8 +#define OPTYPE_ISCSI_BACKUP 9 +#define OPTYPE_FCOE_FW_ACTIVE 10 +#define OPTYPE_FCOE_FW_BACKUP 11 +#define OPTYPE_NCSI_FW 13 +#define OPTYPE_REDBOOT_DIR 18 +#define OPTYPE_REDBOOT_CONFIG 19 +#define OPTYPE_SH_PHY_FW 21 +#define OPTYPE_FLASHISM_JUMPVECTOR 22 +#define OPTYPE_UFI_DIR 23 +#define OPTYPE_PHY_FW 99 + +#define FLASH_BIOS_IMAGE_MAX_SIZE_g2 262144 /* Max OPTION ROM image sz */ +#define FLASH_REDBOOT_IMAGE_MAX_SIZE_g2 262144 /* Max Redboot image sz */ +#define FLASH_IMAGE_MAX_SIZE_g2 1310720 /* Max firmware image size */ + +#define FLASH_NCSI_IMAGE_MAX_SIZE_g3 262144 +#define FLASH_PHY_FW_IMAGE_MAX_SIZE_g3 262144 +#define FLASH_BIOS_IMAGE_MAX_SIZE_g3 524288 /* Max OPTION ROM image sz */ +#define FLASH_REDBOOT_IMAGE_MAX_SIZE_g3 1048576 /* Max Redboot image sz */ +#define FLASH_IMAGE_MAX_SIZE_g3 2097152 /* Max firmware image size */ + +/* Offsets for components on Flash. */ +#define FLASH_REDBOOT_START_g2 0 +#define FLASH_FCoE_BIOS_START_g2 524288 +#define FLASH_iSCSI_PRIMARY_IMAGE_START_g2 1048576 +#define FLASH_iSCSI_BACKUP_IMAGE_START_g2 2359296 +#define FLASH_FCoE_PRIMARY_IMAGE_START_g2 3670016 +#define FLASH_FCoE_BACKUP_IMAGE_START_g2 4980736 +#define FLASH_iSCSI_BIOS_START_g2 7340032 +#define FLASH_PXE_BIOS_START_g2 7864320 + +#define FLASH_REDBOOT_START_g3 262144 +#define FLASH_PHY_FW_START_g3 1310720 +#define FLASH_iSCSI_PRIMARY_IMAGE_START_g3 2097152 +#define FLASH_iSCSI_BACKUP_IMAGE_START_g3 4194304 +#define FLASH_FCoE_PRIMARY_IMAGE_START_g3 6291456 +#define FLASH_FCoE_BACKUP_IMAGE_START_g3 8388608 +#define FLASH_iSCSI_BIOS_START_g3 12582912 +#define FLASH_PXE_BIOS_START_g3 13107200 +#define FLASH_FCoE_BIOS_START_g3 13631488 +#define FLASH_NCSI_START_g3 15990784 + +#define IMAGE_NCSI 16 +#define IMAGE_OPTION_ROM_PXE 32 +#define IMAGE_OPTION_ROM_FCoE 33 +#define IMAGE_OPTION_ROM_ISCSI 34 +#define IMAGE_FLASHISM_JUMPVECTOR 48 +#define IMAGE_FIRMWARE_iSCSI 160 +#define IMAGE_FIRMWARE_FCoE 162 +#define IMAGE_FIRMWARE_BACKUP_iSCSI 176 +#define IMAGE_FIRMWARE_BACKUP_FCoE 178 +#define IMAGE_FIRMWARE_PHY 192 +#define IMAGE_REDBOOT_DIR 208 +#define IMAGE_REDBOOT_CONFIG 209 +#define IMAGE_UFI_DIR 210 +#define IMAGE_BOOT_CODE 224 + +struct controller_id { + u32 vendor; + u32 device; + u32 subvendor; + u32 subdevice; +}; + +struct flash_comp { + unsigned long offset; + int optype; + int size; + int img_type; +}; + +struct image_hdr { + u32 imageid; + u32 imageoffset; + u32 imagelength; + u32 image_checksum; + u8 image_version[32]; +}; + +struct flash_file_hdr_g2 { + u8 sign[32]; + u32 cksum; + u32 antidote; + struct controller_id cont_id; + u32 file_len; + u32 chunk_num; + u32 total_chunks; + u32 num_imgs; + u8 build[24]; +}; + +/* First letter of the build version of the image */ +#define BLD_STR_UFI_TYPE_BE2 '2' +#define BLD_STR_UFI_TYPE_BE3 '3' +#define BLD_STR_UFI_TYPE_SH '4' + +struct flash_file_hdr_g3 { + u8 sign[52]; + u8 ufi_version[4]; + u32 file_len; + u32 cksum; + u32 antidote; + u32 num_imgs; + u8 build[24]; + u8 asic_type_rev; + u8 rsvd[31]; +}; + +struct flash_section_hdr { + u32 format_rev; + u32 cksum; + u32 antidote; + u32 num_images; + u8 id_string[128]; + u32 rsvd[4]; +} __packed; + +struct flash_section_hdr_g2 { + u32 format_rev; + u32 cksum; + u32 antidote; + u32 build_num; + u8 id_string[128]; + u32 rsvd[8]; +} __packed; + +struct flash_section_entry { + u32 type; + u32 offset; + u32 pad_size; + u32 image_size; + u32 cksum; + u32 entry_point; + u16 optype; + u16 rsvd0; + u32 rsvd1; + u8 ver_data[32]; +} __packed; + +struct flash_section_info { + u8 cookie[32]; + struct flash_section_hdr fsec_hdr; + struct flash_section_entry fsec_entry[32]; +} __packed; + +struct flash_section_info_g2 { + u8 cookie[32]; + struct flash_section_hdr_g2 fsec_hdr; + struct flash_section_entry fsec_entry[32]; +} __packed; + /****************** Firmware Flash ******************/ +#define FLASHROM_OPER_FLASH 1 +#define FLASHROM_OPER_SAVE 2 +#define FLASHROM_OPER_REPORT 4 +#define FLASHROM_OPER_PHY_FLASH 9 +#define FLASHROM_OPER_PHY_SAVE 10 + struct flashrom_params { u32 op_code; u32 op_type; @@ -1366,6 +1553,7 @@ enum { PHY_TYPE_QSFP, PHY_TYPE_KR4_40GB, PHY_TYPE_KR2_20GB, + PHY_TYPE_TN_8022, PHY_TYPE_DISABLED = 255 }; @@ -1429,6 +1617,20 @@ struct be_cmd_req_set_qos { }; /*********************** Controller Attributes ***********************/ +struct mgmt_hba_attribs { + u32 rsvd0[24]; + u8 controller_model_number[32]; + u32 rsvd1[79]; + u8 rsvd2[3]; + u8 phy_port; + u32 rsvd3[13]; +} __packed; + +struct mgmt_controller_attrib { + struct mgmt_hba_attribs hba_attribs; + u32 rsvd0[10]; +} __packed; + struct be_cmd_req_cntl_attribs { struct be_cmd_req_hdr hdr; }; @@ -2070,8 +2272,10 @@ int be_cmd_get_beacon_state(struct be_adapter *adapter, u8 port_num, int be_cmd_read_port_transceiver_data(struct be_adapter *adapter, u8 page_num, u8 *data); int be_cmd_query_cable_type(struct be_adapter *adapter); +int be_cmd_query_sfp_info(struct be_adapter *adapter); int be_cmd_write_flashrom(struct be_adapter *adapter, struct be_dma_mem *cmd, - u32 flash_oper, u32 flash_opcode, u32 buf_size); + u32 flash_oper, u32 flash_opcode, u32 img_offset, + u32 buf_size); int lancer_cmd_write_object(struct be_adapter *adapter, struct be_dma_mem *cmd, u32 data_size, u32 data_offset, const char *obj_name, u32 *data_written, @@ -2081,7 +2285,7 @@ int lancer_cmd_read_object(struct be_adapter *adapter, struct be_dma_mem *cmd, u32 *data_read, u32 *eof, u8 *addn_status); int lancer_cmd_delete_object(struct be_adapter *adapter, const char *obj_name); int be_cmd_get_flash_crc(struct be_adapter *adapter, u8 *flashed_crc, - u16 optype, int offset); + u16 img_optype, u32 img_offset, u32 crc_offset); int be_cmd_enable_magic_wol(struct be_adapter *adapter, u8 *mac, struct be_dma_mem *nonemb_cmd); int be_cmd_fw_init(struct be_adapter *adapter); @@ -2136,7 +2340,7 @@ int lancer_initiate_dump(struct be_adapter *adapter); int lancer_delete_dump(struct be_adapter *adapter); bool dump_present(struct be_adapter *adapter); int lancer_test_and_set_rdy_state(struct be_adapter *adapter); -int be_cmd_query_port_name(struct be_adapter *adapter, u8 *port_name); +int be_cmd_query_port_name(struct be_adapter *adapter); int be_cmd_get_func_config(struct be_adapter *adapter, struct be_resources *res); int be_cmd_get_profile_config(struct be_adapter *adapter, diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c index 73a500ccbf69..4d2de4700769 100644 --- a/drivers/net/ethernet/emulex/benet/be_ethtool.c +++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c @@ -193,8 +193,6 @@ static const struct be_ethtool_stat et_tx_stats[] = { {DRVSTAT_TX_INFO(tx_pkts)}, /* Number of skbs queued for trasmission by the driver */ {DRVSTAT_TX_INFO(tx_reqs)}, - /* Number of TX work request blocks DMAed to HW */ - {DRVSTAT_TX_INFO(tx_wrbs)}, /* Number of times the TX queue was stopped due to lack * of spaces in the TXQ. */ @@ -707,15 +705,17 @@ be_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *ecmd) if (ecmd->autoneg != adapter->phy.fc_autoneg) return -EINVAL; - adapter->tx_fc = ecmd->tx_pause; - adapter->rx_fc = ecmd->rx_pause; - status = be_cmd_set_flow_control(adapter, - adapter->tx_fc, adapter->rx_fc); - if (status) + status = be_cmd_set_flow_control(adapter, ecmd->tx_pause, + ecmd->rx_pause); + if (status) { dev_warn(&adapter->pdev->dev, "Pause param set failed\n"); + return be_cmd_status(status); + } - return be_cmd_status(status); + adapter->tx_fc = ecmd->tx_pause; + adapter->rx_fc = ecmd->rx_pause; + return 0; } static int be_set_phys_id(struct net_device *netdev, diff --git a/drivers/net/ethernet/emulex/benet/be_hw.h b/drivers/net/ethernet/emulex/benet/be_hw.h index 295ee0835ba0..48840889db62 100644 --- a/drivers/net/ethernet/emulex/benet/be_hw.h +++ b/drivers/net/ethernet/emulex/benet/be_hw.h @@ -75,7 +75,7 @@ * atomically without having to arbitrate for the PCI Interrupt Disable bit * with the OS. */ -#define MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK (1 << 29) /* bit 29 */ +#define MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK BIT(29) /* bit 29 */ /********* PCI Function Capability *********/ #define BE_FUNCTION_CAPS_RSS 0x2 @@ -171,94 +171,6 @@ #define RETRIEVE_FAT 0 #define QUERY_FAT 1 -/* Flashrom related descriptors */ -#define MAX_FLASH_COMP 32 -#define IMAGE_TYPE_FIRMWARE 160 -#define IMAGE_TYPE_BOOTCODE 224 -#define IMAGE_TYPE_OPTIONROM 32 - -#define NUM_FLASHDIR_ENTRIES 32 - -#define OPTYPE_ISCSI_ACTIVE 0 -#define OPTYPE_REDBOOT 1 -#define OPTYPE_BIOS 2 -#define OPTYPE_PXE_BIOS 3 -#define OPTYPE_FCOE_BIOS 8 -#define OPTYPE_ISCSI_BACKUP 9 -#define OPTYPE_FCOE_FW_ACTIVE 10 -#define OPTYPE_FCOE_FW_BACKUP 11 -#define OPTYPE_NCSI_FW 13 -#define OPTYPE_REDBOOT_DIR 18 -#define OPTYPE_REDBOOT_CONFIG 19 -#define OPTYPE_SH_PHY_FW 21 -#define OPTYPE_FLASHISM_JUMPVECTOR 22 -#define OPTYPE_UFI_DIR 23 -#define OPTYPE_PHY_FW 99 -#define TN_8022 13 - -#define FLASHROM_OPER_PHY_FLASH 9 -#define FLASHROM_OPER_PHY_SAVE 10 -#define FLASHROM_OPER_FLASH 1 -#define FLASHROM_OPER_SAVE 2 -#define FLASHROM_OPER_REPORT 4 - -#define FLASH_IMAGE_MAX_SIZE_g2 (1310720) /* Max firmware image size */ -#define FLASH_BIOS_IMAGE_MAX_SIZE_g2 (262144) /* Max OPTION ROM image sz */ -#define FLASH_REDBOOT_IMAGE_MAX_SIZE_g2 (262144) /* Max Redboot image sz */ -#define FLASH_IMAGE_MAX_SIZE_g3 (2097152) /* Max firmware image size */ -#define FLASH_BIOS_IMAGE_MAX_SIZE_g3 (524288) /* Max OPTION ROM image sz */ -#define FLASH_REDBOOT_IMAGE_MAX_SIZE_g3 (1048576) /* Max Redboot image sz */ -#define FLASH_NCSI_IMAGE_MAX_SIZE_g3 (262144) -#define FLASH_PHY_FW_IMAGE_MAX_SIZE_g3 262144 - -#define FLASH_NCSI_MAGIC (0x16032009) -#define FLASH_NCSI_DISABLED (0) -#define FLASH_NCSI_ENABLED (1) - -#define FLASH_NCSI_BITFILE_HDR_OFFSET (0x600000) - -/* Offsets for components on Flash. */ -#define FLASH_iSCSI_PRIMARY_IMAGE_START_g2 (1048576) -#define FLASH_iSCSI_BACKUP_IMAGE_START_g2 (2359296) -#define FLASH_FCoE_PRIMARY_IMAGE_START_g2 (3670016) -#define FLASH_FCoE_BACKUP_IMAGE_START_g2 (4980736) -#define FLASH_iSCSI_BIOS_START_g2 (7340032) -#define FLASH_PXE_BIOS_START_g2 (7864320) -#define FLASH_FCoE_BIOS_START_g2 (524288) -#define FLASH_REDBOOT_START_g2 (0) - -#define FLASH_NCSI_START_g3 (15990784) -#define FLASH_iSCSI_PRIMARY_IMAGE_START_g3 (2097152) -#define FLASH_iSCSI_BACKUP_IMAGE_START_g3 (4194304) -#define FLASH_FCoE_PRIMARY_IMAGE_START_g3 (6291456) -#define FLASH_FCoE_BACKUP_IMAGE_START_g3 (8388608) -#define FLASH_iSCSI_BIOS_START_g3 (12582912) -#define FLASH_PXE_BIOS_START_g3 (13107200) -#define FLASH_FCoE_BIOS_START_g3 (13631488) -#define FLASH_REDBOOT_START_g3 (262144) -#define FLASH_PHY_FW_START_g3 1310720 - -#define IMAGE_NCSI 16 -#define IMAGE_OPTION_ROM_PXE 32 -#define IMAGE_OPTION_ROM_FCoE 33 -#define IMAGE_OPTION_ROM_ISCSI 34 -#define IMAGE_FLASHISM_JUMPVECTOR 48 -#define IMAGE_FLASH_ISM 49 -#define IMAGE_JUMP_VECTOR 50 -#define IMAGE_FIRMWARE_iSCSI 160 -#define IMAGE_FIRMWARE_COMP_iSCSI 161 -#define IMAGE_FIRMWARE_FCoE 162 -#define IMAGE_FIRMWARE_COMP_FCoE 163 -#define IMAGE_FIRMWARE_BACKUP_iSCSI 176 -#define IMAGE_FIRMWARE_BACKUP_COMP_iSCSI 177 -#define IMAGE_FIRMWARE_BACKUP_FCoE 178 -#define IMAGE_FIRMWARE_BACKUP_COMP_FCoE 179 -#define IMAGE_FIRMWARE_PHY 192 -#define IMAGE_REDBOOT_DIR 208 -#define IMAGE_REDBOOT_CONFIG 209 -#define IMAGE_UFI_DIR 210 -#define IMAGE_BOOT_CODE 224 - /************* Rx Packet Type Encoding **************/ #define BE_UNICAST_PACKET 0 #define BE_MULTICAST_PACKET 1 @@ -281,10 +193,10 @@ struct be_eq_entry { /* TX Queue Descriptor */ #define ETH_WRB_FRAG_LEN_MASK 0xFFFF struct be_eth_wrb { - u32 frag_pa_hi; /* dword 0 */ - u32 frag_pa_lo; /* dword 1 */ - u32 rsvd0; /* dword 2 */ - u32 frag_len; /* dword 3: bits 0 - 15 */ + __le32 frag_pa_hi; /* dword 0 */ + __le32 frag_pa_lo; /* dword 1 */ + u32 rsvd0; /* dword 2 */ + __le32 frag_len; /* dword 3: bits 0 - 15 */ } __packed; /* Pseudo amap definition for eth_hdr_wrb in which each bit of the @@ -311,8 +223,13 @@ struct amap_eth_hdr_wrb { u8 vlan_tag[16]; } __packed; +#define TX_HDR_WRB_COMPL 1 /* word 2 */ +#define TX_HDR_WRB_EVT BIT(1) /* word 2 */ +#define TX_HDR_WRB_NUM_SHIFT 13 /* word 2: bits 13:17 */ +#define TX_HDR_WRB_NUM_MASK 0x1F /* word 2: bits 13:17 */ + struct be_eth_hdr_wrb { - u32 dw[4]; + __le32 dw[4]; }; /********* Tx Compl Status Encoding *********/ @@ -435,138 +352,3 @@ struct amap_eth_rx_compl_v1 { struct be_eth_rx_compl { u32 dw[4]; }; - -struct mgmt_hba_attribs { - u8 flashrom_version_string[32]; - u8 manufacturer_name[32]; - u32 supported_modes; - u32 rsvd0[3]; - u8 ncsi_ver_string[12]; - u32 default_extended_timeout; - u8 controller_model_number[32]; - u8 controller_description[64]; - u8 controller_serial_number[32]; - u8 ip_version_string[32]; - u8 firmware_version_string[32]; - u8 bios_version_string[32]; - u8 redboot_version_string[32]; - u8 driver_version_string[32]; - u8 fw_on_flash_version_string[32]; - u32 functionalities_supported; - u16 max_cdblength; - u8 asic_revision; - u8 generational_guid[16]; - u8 hba_port_count; - u16 default_link_down_timeout; - u8 iscsi_ver_min_max; - u8 multifunction_device; - u8 cache_valid; - u8 hba_status; - u8 max_domains_supported; - u8 phy_port; - u32 firmware_post_status; - u32 hba_mtu[8]; - u32 rsvd1[4]; -}; - -struct mgmt_controller_attrib { - struct mgmt_hba_attribs hba_attribs; - u16 pci_vendor_id; - u16 pci_device_id; - u16 pci_sub_vendor_id; - u16 pci_sub_system_id; - u8 pci_bus_number; - u8 pci_device_number; - u8 pci_function_number; - u8 interface_type; - u64 unique_identifier; - u32 rsvd0[5]; -}; - -struct controller_id { - u32 vendor; - u32 device; - u32 subvendor; - u32 subdevice; -}; - -struct flash_comp { - unsigned long offset; - int optype; - int size; - int img_type; -}; - -struct image_hdr { - u32 imageid; - u32 imageoffset; - u32 imagelength; - u32 image_checksum; - u8 image_version[32]; -}; -struct flash_file_hdr_g2 { - u8 sign[32]; - u32 cksum; - u32 antidote; - struct controller_id cont_id; - u32 file_len; - u32 chunk_num; - u32 total_chunks; - u32 num_imgs; - u8 build[24]; -}; - -struct flash_file_hdr_g3 { - u8 sign[52]; - u8 ufi_version[4]; - u32 file_len; - u32 cksum; - u32 antidote; - u32 num_imgs; - u8 build[24]; - u8 asic_type_rev; - u8 rsvd[31]; -}; - -struct flash_section_hdr { - u32 format_rev; - u32 cksum; - u32 antidote; - u32 num_images; - u8 id_string[128]; - u32 rsvd[4]; -} __packed; - -struct flash_section_hdr_g2 { - u32 format_rev; - u32 cksum; - u32 antidote; - u32 build_num; - u8 id_string[128]; - u32 rsvd[8]; -} __packed; - -struct flash_section_entry { - u32 type; - u32 offset; - u32 pad_size; - u32 image_size; - u32 cksum; - u32 entry_point; - u16 optype; - u16 rsvd0; - u32 rsvd1; - u8 ver_data[32]; -} __packed; - -struct flash_section_info { - u8 cookie[32]; - struct flash_section_hdr fsec_hdr; - struct flash_section_entry fsec_entry[32]; -} __packed; - -struct flash_section_info_g2 { - u8 cookie[32]; - struct flash_section_hdr_g2 fsec_hdr; - struct flash_section_entry fsec_entry[32]; -} __packed; diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 41a0a5498da7..932b93a14965 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -662,48 +662,40 @@ void be_link_status_update(struct be_adapter *adapter, u8 link_status) netif_carrier_off(netdev); } -static void be_tx_stats_update(struct be_tx_obj *txo, - u32 wrb_cnt, u32 copied, u32 gso_segs, - bool stopped) +static void be_tx_stats_update(struct be_tx_obj *txo, struct sk_buff *skb) { struct be_tx_stats *stats = tx_stats(txo); u64_stats_update_begin(&stats->sync); stats->tx_reqs++; - stats->tx_wrbs += wrb_cnt; - stats->tx_bytes += copied; - stats->tx_pkts += (gso_segs ? gso_segs : 1); - if (stopped) - stats->tx_stops++; + stats->tx_bytes += skb->len; + stats->tx_pkts += (skb_shinfo(skb)->gso_segs ? : 1); u64_stats_update_end(&stats->sync); } -/* Determine number of WRB entries needed to xmit data in an skb */ -static u32 wrb_cnt_for_skb(struct be_adapter *adapter, struct sk_buff *skb, - bool *dummy) +/* Returns number of WRBs needed for the skb */ +static u32 skb_wrb_cnt(struct sk_buff *skb) { - int cnt = (skb->len > skb->data_len); - - cnt += skb_shinfo(skb)->nr_frags; - - /* to account for hdr wrb */ - cnt++; - if (lancer_chip(adapter) || !(cnt & 1)) { - *dummy = false; - } else { - /* add a dummy to make it an even num */ - cnt++; - *dummy = true; - } - BUG_ON(cnt > BE_MAX_TX_FRAG_COUNT); - return cnt; + /* +1 for the header wrb */ + return 1 + (skb_headlen(skb) ? 1 : 0) + skb_shinfo(skb)->nr_frags; } static inline void wrb_fill(struct be_eth_wrb *wrb, u64 addr, int len) { - wrb->frag_pa_hi = upper_32_bits(addr); - wrb->frag_pa_lo = addr & 0xFFFFFFFF; - wrb->frag_len = len & ETH_WRB_FRAG_LEN_MASK; + wrb->frag_pa_hi = cpu_to_le32(upper_32_bits(addr)); + wrb->frag_pa_lo = cpu_to_le32(lower_32_bits(addr)); + wrb->frag_len = cpu_to_le32(len & ETH_WRB_FRAG_LEN_MASK); + wrb->rsvd0 = 0; +} + +/* A dummy wrb is just all zeros. Using a separate routine for dummy-wrb + * to avoid the swap and shift/mask operations in wrb_fill(). + */ +static inline void wrb_fill_dummy(struct be_eth_wrb *wrb) +{ + wrb->frag_pa_hi = 0; + wrb->frag_pa_lo = 0; + wrb->frag_len = 0; wrb->rsvd0 = 0; } @@ -713,7 +705,7 @@ static inline u16 be_get_tx_vlan_tag(struct be_adapter *adapter, u8 vlan_prio; u16 vlan_tag; - vlan_tag = vlan_tx_tag_get(skb); + vlan_tag = skb_vlan_tag_get(skb); vlan_prio = (vlan_tag & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT; /* If vlan priority provided by OS is NOT in available bmap */ if (!(adapter->vlan_prio_bmap & (1 << vlan_prio))) @@ -764,52 +756,57 @@ static void wrb_fill_hdr(struct be_adapter *adapter, struct be_eth_hdr_wrb *hdr, SET_TX_WRB_HDR_BITS(udpcs, hdr, 1); } - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { SET_TX_WRB_HDR_BITS(vlan, hdr, 1); vlan_tag = be_get_tx_vlan_tag(adapter, skb); SET_TX_WRB_HDR_BITS(vlan_tag, hdr, vlan_tag); } - /* To skip HW VLAN tagging: evt = 1, compl = 0 */ - SET_TX_WRB_HDR_BITS(complete, hdr, !skip_hw_vlan); - SET_TX_WRB_HDR_BITS(event, hdr, 1); SET_TX_WRB_HDR_BITS(num_wrb, hdr, wrb_cnt); SET_TX_WRB_HDR_BITS(len, hdr, len); + + /* Hack to skip HW VLAN tagging needs evt = 1, compl = 0 + * When this hack is not needed, the evt bit is set while ringing DB + */ + if (skip_hw_vlan) + SET_TX_WRB_HDR_BITS(event, hdr, 1); } static void unmap_tx_frag(struct device *dev, struct be_eth_wrb *wrb, bool unmap_single) { dma_addr_t dma; + u32 frag_len = le32_to_cpu(wrb->frag_len); - be_dws_le_to_cpu(wrb, sizeof(*wrb)); - dma = (u64)wrb->frag_pa_hi << 32 | (u64)wrb->frag_pa_lo; - if (wrb->frag_len) { + dma = (u64)le32_to_cpu(wrb->frag_pa_hi) << 32 | + (u64)le32_to_cpu(wrb->frag_pa_lo); + if (frag_len) { if (unmap_single) - dma_unmap_single(dev, dma, wrb->frag_len, - DMA_TO_DEVICE); + dma_unmap_single(dev, dma, frag_len, DMA_TO_DEVICE); else - dma_unmap_page(dev, dma, wrb->frag_len, DMA_TO_DEVICE); + dma_unmap_page(dev, dma, frag_len, DMA_TO_DEVICE); } } -static int make_tx_wrbs(struct be_adapter *adapter, struct be_queue_info *txq, - struct sk_buff *skb, u32 wrb_cnt, bool dummy_wrb, - bool skip_hw_vlan) +/* Returns the number of WRBs used up by the skb */ +static u32 be_xmit_enqueue(struct be_adapter *adapter, struct be_tx_obj *txo, + struct sk_buff *skb, bool skip_hw_vlan) { - dma_addr_t busaddr; - int i, copied = 0; + u32 i, copied = 0, wrb_cnt = skb_wrb_cnt(skb); struct device *dev = &adapter->pdev->dev; - struct sk_buff *first_skb = skb; - struct be_eth_wrb *wrb; + struct be_queue_info *txq = &txo->q; struct be_eth_hdr_wrb *hdr; bool map_single = false; - u16 map_head; + struct be_eth_wrb *wrb; + dma_addr_t busaddr; + u16 head = txq->head; hdr = queue_head_node(txq); + wrb_fill_hdr(adapter, hdr, skb, wrb_cnt, skb->len, skip_hw_vlan); + be_dws_cpu_to_le(hdr, sizeof(*hdr)); + queue_head_inc(txq); - map_head = txq->head; if (skb->len > skb->data_len) { int len = skb_headlen(skb); @@ -820,7 +817,6 @@ static int make_tx_wrbs(struct be_adapter *adapter, struct be_queue_info *txq, map_single = true; wrb = queue_head_node(txq); wrb_fill(wrb, busaddr, len); - be_dws_cpu_to_le(wrb, sizeof(*wrb)); queue_head_inc(txq); copied += len; } @@ -834,35 +830,44 @@ static int make_tx_wrbs(struct be_adapter *adapter, struct be_queue_info *txq, goto dma_err; wrb = queue_head_node(txq); wrb_fill(wrb, busaddr, skb_frag_size(frag)); - be_dws_cpu_to_le(wrb, sizeof(*wrb)); queue_head_inc(txq); copied += skb_frag_size(frag); } - if (dummy_wrb) { - wrb = queue_head_node(txq); - wrb_fill(wrb, 0, 0); - be_dws_cpu_to_le(wrb, sizeof(*wrb)); - queue_head_inc(txq); - } + BUG_ON(txo->sent_skb_list[head]); + txo->sent_skb_list[head] = skb; + txo->last_req_hdr = head; + atomic_add(wrb_cnt, &txq->used); + txo->last_req_wrb_cnt = wrb_cnt; + txo->pend_wrb_cnt += wrb_cnt; - wrb_fill_hdr(adapter, hdr, first_skb, wrb_cnt, copied, skip_hw_vlan); - be_dws_cpu_to_le(hdr, sizeof(*hdr)); + be_tx_stats_update(txo, skb); + return wrb_cnt; - return copied; dma_err: - txq->head = map_head; + /* Bring the queue back to the state it was in before this + * routine was invoked. + */ + txq->head = head; + /* skip the first wrb (hdr); it's not mapped */ + queue_head_inc(txq); while (copied) { wrb = queue_head_node(txq); unmap_tx_frag(dev, wrb, map_single); map_single = false; - copied -= wrb->frag_len; + copied -= le32_to_cpu(wrb->frag_len); adapter->drv_stats.dma_map_errors++; queue_head_inc(txq); } + txq->head = head; return 0; } +static inline int qnq_async_evt_rcvd(struct be_adapter *adapter) +{ + return adapter->flags & BE_FLAGS_QNQ_ASYNC_EVT_RCVD; +} + static struct sk_buff *be_insert_vlan_in_pkt(struct be_adapter *adapter, struct sk_buff *skb, bool *skip_hw_vlan) @@ -873,7 +878,7 @@ static struct sk_buff *be_insert_vlan_in_pkt(struct be_adapter *adapter, if (unlikely(!skb)) return skb; - if (vlan_tx_tag_present(skb)) + if (skb_vlan_tag_present(skb)) vlan_tag = be_get_tx_vlan_tag(adapter, skb); if (qnq_async_evt_rcvd(adapter) && adapter->pvid) { @@ -932,7 +937,7 @@ static bool be_ipv6_exthdr_check(struct sk_buff *skb) static int be_vlan_tag_tx_chk(struct be_adapter *adapter, struct sk_buff *skb) { - return vlan_tx_tag_present(skb) || adapter->pvid || adapter->qnq_vid; + return skb_vlan_tag_present(skb) || adapter->pvid || adapter->qnq_vid; } static int be_ipv6_tx_stall_chk(struct be_adapter *adapter, struct sk_buff *skb) @@ -955,7 +960,7 @@ static struct sk_buff *be_lancer_xmit_workarounds(struct be_adapter *adapter, eth_hdr_len = ntohs(skb->protocol) == ETH_P_8021Q ? VLAN_ETH_HLEN : ETH_HLEN; if (skb->len <= 60 && - (lancer_chip(adapter) || vlan_tx_tag_present(skb)) && + (lancer_chip(adapter) || skb_vlan_tag_present(skb)) && is_ipv4_pkt(skb)) { ip = (struct iphdr *)ip_hdr(skb); pskb_trim(skb, eth_hdr_len + ntohs(ip->tot_len)); @@ -973,7 +978,7 @@ static struct sk_buff *be_lancer_xmit_workarounds(struct be_adapter *adapter, * Manually insert VLAN in pkt. */ if (skb->ip_summed != CHECKSUM_PARTIAL && - vlan_tx_tag_present(skb)) { + skb_vlan_tag_present(skb)) { skb = be_insert_vlan_in_pkt(adapter, skb, skip_hw_vlan); if (unlikely(!skb)) goto err; @@ -1030,52 +1035,64 @@ static struct sk_buff *be_xmit_workarounds(struct be_adapter *adapter, return skb; } +static void be_xmit_flush(struct be_adapter *adapter, struct be_tx_obj *txo) +{ + struct be_queue_info *txq = &txo->q; + struct be_eth_hdr_wrb *hdr = queue_index_node(txq, txo->last_req_hdr); + + /* Mark the last request eventable if it hasn't been marked already */ + if (!(hdr->dw[2] & cpu_to_le32(TX_HDR_WRB_EVT))) + hdr->dw[2] |= cpu_to_le32(TX_HDR_WRB_EVT | TX_HDR_WRB_COMPL); + + /* compose a dummy wrb if there are odd set of wrbs to notify */ + if (!lancer_chip(adapter) && (txo->pend_wrb_cnt & 1)) { + wrb_fill_dummy(queue_head_node(txq)); + queue_head_inc(txq); + atomic_inc(&txq->used); + txo->pend_wrb_cnt++; + hdr->dw[2] &= ~cpu_to_le32(TX_HDR_WRB_NUM_MASK << + TX_HDR_WRB_NUM_SHIFT); + hdr->dw[2] |= cpu_to_le32((txo->last_req_wrb_cnt + 1) << + TX_HDR_WRB_NUM_SHIFT); + } + be_txq_notify(adapter, txo, txo->pend_wrb_cnt); + txo->pend_wrb_cnt = 0; +} + static netdev_tx_t be_xmit(struct sk_buff *skb, struct net_device *netdev) { + bool skip_hw_vlan = false, flush = !skb->xmit_more; struct be_adapter *adapter = netdev_priv(netdev); - struct be_tx_obj *txo = &adapter->tx_obj[skb_get_queue_mapping(skb)]; + u16 q_idx = skb_get_queue_mapping(skb); + struct be_tx_obj *txo = &adapter->tx_obj[q_idx]; struct be_queue_info *txq = &txo->q; - bool dummy_wrb, stopped = false; - u32 wrb_cnt = 0, copied = 0; - bool skip_hw_vlan = false; - u32 start = txq->head; + u16 wrb_cnt; skb = be_xmit_workarounds(adapter, skb, &skip_hw_vlan); - if (!skb) { - tx_stats(txo)->tx_drv_drops++; - return NETDEV_TX_OK; - } + if (unlikely(!skb)) + goto drop; - wrb_cnt = wrb_cnt_for_skb(adapter, skb, &dummy_wrb); + wrb_cnt = be_xmit_enqueue(adapter, txo, skb, skip_hw_vlan); + if (unlikely(!wrb_cnt)) { + dev_kfree_skb_any(skb); + goto drop; + } - copied = make_tx_wrbs(adapter, txq, skb, wrb_cnt, dummy_wrb, - skip_hw_vlan); - if (copied) { - int gso_segs = skb_shinfo(skb)->gso_segs; + if ((atomic_read(&txq->used) + BE_MAX_TX_FRAG_COUNT) >= txq->len) { + netif_stop_subqueue(netdev, q_idx); + tx_stats(txo)->tx_stops++; + } - /* record the sent skb in the sent_skb table */ - BUG_ON(txo->sent_skb_list[start]); - txo->sent_skb_list[start] = skb; + if (flush || __netif_subqueue_stopped(netdev, q_idx)) + be_xmit_flush(adapter, txo); - /* Ensure txq has space for the next skb; Else stop the queue - * *BEFORE* ringing the tx doorbell, so that we serialze the - * tx compls of the current transmit which'll wake up the queue - */ - atomic_add(wrb_cnt, &txq->used); - if ((BE_MAX_TX_FRAG_COUNT + atomic_read(&txq->used)) >= - txq->len) { - netif_stop_subqueue(netdev, skb_get_queue_mapping(skb)); - stopped = true; - } - - be_txq_notify(adapter, txo, wrb_cnt); + return NETDEV_TX_OK; +drop: + tx_stats(txo)->tx_drv_drops++; + /* Flush the already enqueued tx requests */ + if (flush && txo->pend_wrb_cnt) + be_xmit_flush(adapter, txo); - be_tx_stats_update(txo, wrb_cnt, copied, gso_segs, stopped); - } else { - txq->head = start; - tx_stats(txo)->tx_drv_drops++; - dev_kfree_skb_any(skb); - } return NETDEV_TX_OK; } @@ -1096,6 +1113,43 @@ static int be_change_mtu(struct net_device *netdev, int new_mtu) return 0; } +static inline bool be_in_all_promisc(struct be_adapter *adapter) +{ + return (adapter->if_flags & BE_IF_FLAGS_ALL_PROMISCUOUS) == + BE_IF_FLAGS_ALL_PROMISCUOUS; +} + +static int be_set_vlan_promisc(struct be_adapter *adapter) +{ + struct device *dev = &adapter->pdev->dev; + int status; + + if (adapter->if_flags & BE_IF_FLAGS_VLAN_PROMISCUOUS) + return 0; + + status = be_cmd_rx_filter(adapter, BE_IF_FLAGS_VLAN_PROMISCUOUS, ON); + if (!status) { + dev_info(dev, "Enabled VLAN promiscuous mode\n"); + adapter->if_flags |= BE_IF_FLAGS_VLAN_PROMISCUOUS; + } else { + dev_err(dev, "Failed to enable VLAN promiscuous mode\n"); + } + return status; +} + +static int be_clear_vlan_promisc(struct be_adapter *adapter) +{ + struct device *dev = &adapter->pdev->dev; + int status; + + status = be_cmd_rx_filter(adapter, BE_IF_FLAGS_VLAN_PROMISCUOUS, OFF); + if (!status) { + dev_info(dev, "Disabling VLAN promiscuous mode\n"); + adapter->if_flags &= ~BE_IF_FLAGS_VLAN_PROMISCUOUS; + } + return status; +} + /* * A max of 64 (BE_NUM_VLANS_SUPPORTED) vlans can be configured in BE. * If the user configures more, place BE in vlan promiscuous mode. @@ -1108,11 +1162,11 @@ static int be_vid_config(struct be_adapter *adapter) int status = 0; /* No need to further configure vids if in promiscuous mode */ - if (adapter->promiscuous) + if (be_in_all_promisc(adapter)) return 0; if (adapter->vlans_added > be_max_vlans(adapter)) - goto set_vlan_promisc; + return be_set_vlan_promisc(adapter); /* Construct VLAN Table to give to HW */ for_each_set_bit(i, adapter->vids, VLAN_N_VID) @@ -1120,36 +1174,14 @@ static int be_vid_config(struct be_adapter *adapter) status = be_cmd_vlan_config(adapter, adapter->if_handle, vids, num); if (status) { + dev_err(dev, "Setting HW VLAN filtering failed\n"); /* Set to VLAN promisc mode as setting VLAN filter failed */ if (addl_status(status) == MCC_ADDL_STATUS_INSUFFICIENT_RESOURCES) - goto set_vlan_promisc; - dev_err(dev, "Setting HW VLAN filtering failed\n"); - } else { - if (adapter->flags & BE_FLAGS_VLAN_PROMISC) { - /* hw VLAN filtering re-enabled. */ - status = be_cmd_rx_filter(adapter, - BE_FLAGS_VLAN_PROMISC, OFF); - if (!status) { - dev_info(dev, - "Disabling VLAN Promiscuous mode\n"); - adapter->flags &= ~BE_FLAGS_VLAN_PROMISC; - } - } + return be_set_vlan_promisc(adapter); + } else if (adapter->if_flags & BE_IF_FLAGS_VLAN_PROMISCUOUS) { + status = be_clear_vlan_promisc(adapter); } - - return status; - -set_vlan_promisc: - if (adapter->flags & BE_FLAGS_VLAN_PROMISC) - return 0; - - status = be_cmd_rx_filter(adapter, BE_FLAGS_VLAN_PROMISC, ON); - if (!status) { - dev_info(dev, "Enable VLAN Promiscuous mode\n"); - adapter->flags |= BE_FLAGS_VLAN_PROMISC; - } else - dev_err(dev, "Failed to enable VLAN Promiscuous mode\n"); return status; } @@ -1191,79 +1223,99 @@ static int be_vlan_rem_vid(struct net_device *netdev, __be16 proto, u16 vid) return be_vid_config(adapter); } -static void be_clear_promisc(struct be_adapter *adapter) +static void be_clear_all_promisc(struct be_adapter *adapter) { - adapter->promiscuous = false; - adapter->flags &= ~(BE_FLAGS_VLAN_PROMISC | BE_FLAGS_MCAST_PROMISC); + be_cmd_rx_filter(adapter, BE_IF_FLAGS_ALL_PROMISCUOUS, OFF); + adapter->if_flags &= ~BE_IF_FLAGS_ALL_PROMISCUOUS; +} - be_cmd_rx_filter(adapter, IFF_PROMISC, OFF); +static void be_set_all_promisc(struct be_adapter *adapter) +{ + be_cmd_rx_filter(adapter, BE_IF_FLAGS_ALL_PROMISCUOUS, ON); + adapter->if_flags |= BE_IF_FLAGS_ALL_PROMISCUOUS; } -static void be_set_rx_mode(struct net_device *netdev) +static void be_set_mc_promisc(struct be_adapter *adapter) { - struct be_adapter *adapter = netdev_priv(netdev); int status; - if (netdev->flags & IFF_PROMISC) { - be_cmd_rx_filter(adapter, IFF_PROMISC, ON); - adapter->promiscuous = true; - goto done; - } + if (adapter->if_flags & BE_IF_FLAGS_MCAST_PROMISCUOUS) + return; - /* BE was previously in promiscuous mode; disable it */ - if (adapter->promiscuous) { - be_clear_promisc(adapter); - if (adapter->vlans_added) - be_vid_config(adapter); + status = be_cmd_rx_filter(adapter, BE_IF_FLAGS_MCAST_PROMISCUOUS, ON); + if (!status) + adapter->if_flags |= BE_IF_FLAGS_MCAST_PROMISCUOUS; +} + +static void be_set_mc_list(struct be_adapter *adapter) +{ + int status; + + status = be_cmd_rx_filter(adapter, BE_IF_FLAGS_MULTICAST, ON); + if (!status) + adapter->if_flags &= ~BE_IF_FLAGS_MCAST_PROMISCUOUS; + else + be_set_mc_promisc(adapter); +} + +static void be_set_uc_list(struct be_adapter *adapter) +{ + struct netdev_hw_addr *ha; + int i = 1; /* First slot is claimed by the Primary MAC */ + + for (; adapter->uc_macs > 0; adapter->uc_macs--, i++) + be_cmd_pmac_del(adapter, adapter->if_handle, + adapter->pmac_id[i], 0); + + if (netdev_uc_count(adapter->netdev) > be_max_uc(adapter)) { + be_set_all_promisc(adapter); + return; } - /* Enable multicast promisc if num configured exceeds what we support */ - if (netdev->flags & IFF_ALLMULTI || - netdev_mc_count(netdev) > be_max_mc(adapter)) - goto set_mcast_promisc; + netdev_for_each_uc_addr(ha, adapter->netdev) { + adapter->uc_macs++; /* First slot is for Primary MAC */ + be_cmd_pmac_add(adapter, (u8 *)ha->addr, adapter->if_handle, + &adapter->pmac_id[adapter->uc_macs], 0); + } +} - if (netdev_uc_count(netdev) != adapter->uc_macs) { - struct netdev_hw_addr *ha; - int i = 1; /* First slot is claimed by the Primary MAC */ +static void be_clear_uc_list(struct be_adapter *adapter) +{ + int i; - for (; adapter->uc_macs > 0; adapter->uc_macs--, i++) { - be_cmd_pmac_del(adapter, adapter->if_handle, - adapter->pmac_id[i], 0); - } + for (i = 1; i < (adapter->uc_macs + 1); i++) + be_cmd_pmac_del(adapter, adapter->if_handle, + adapter->pmac_id[i], 0); + adapter->uc_macs = 0; +} - if (netdev_uc_count(netdev) > be_max_uc(adapter)) { - be_cmd_rx_filter(adapter, IFF_PROMISC, ON); - adapter->promiscuous = true; - goto done; - } +static void be_set_rx_mode(struct net_device *netdev) +{ + struct be_adapter *adapter = netdev_priv(netdev); - netdev_for_each_uc_addr(ha, adapter->netdev) { - adapter->uc_macs++; /* First slot is for Primary MAC */ - be_cmd_pmac_add(adapter, (u8 *)ha->addr, - adapter->if_handle, - &adapter->pmac_id[adapter->uc_macs], 0); - } + if (netdev->flags & IFF_PROMISC) { + be_set_all_promisc(adapter); + return; } - status = be_cmd_rx_filter(adapter, IFF_MULTICAST, ON); - if (!status) { - if (adapter->flags & BE_FLAGS_MCAST_PROMISC) - adapter->flags &= ~BE_FLAGS_MCAST_PROMISC; - goto done; + /* Interface was previously in promiscuous mode; disable it */ + if (be_in_all_promisc(adapter)) { + be_clear_all_promisc(adapter); + if (adapter->vlans_added) + be_vid_config(adapter); } -set_mcast_promisc: - if (adapter->flags & BE_FLAGS_MCAST_PROMISC) + /* Enable multicast promisc if num configured exceeds what we support */ + if (netdev->flags & IFF_ALLMULTI || + netdev_mc_count(netdev) > be_max_mc(adapter)) { + be_set_mc_promisc(adapter); return; + } - /* Set to MCAST promisc mode if setting MULTICAST address fails - * or if num configured exceeds what we support - */ - status = be_cmd_rx_filter(adapter, IFF_ALLMULTI, ON); - if (!status) - adapter->flags |= BE_FLAGS_MCAST_PROMISC; -done: - return; + if (netdev_uc_count(netdev) != adapter->uc_macs) + be_set_uc_list(adapter); + + be_set_mc_list(adapter); } static int be_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) @@ -1959,32 +2011,34 @@ static struct be_eth_tx_compl *be_tx_compl_get(struct be_queue_info *tx_cq) static u16 be_tx_compl_process(struct be_adapter *adapter, struct be_tx_obj *txo, u16 last_index) { + struct sk_buff **sent_skbs = txo->sent_skb_list; struct be_queue_info *txq = &txo->q; + u16 frag_index, num_wrbs = 0; + struct sk_buff *skb = NULL; + bool unmap_skb_hdr = false; struct be_eth_wrb *wrb; - struct sk_buff **sent_skbs = txo->sent_skb_list; - struct sk_buff *sent_skb; - u16 cur_index, num_wrbs = 1; /* account for hdr wrb */ - bool unmap_skb_hdr = true; - - sent_skb = sent_skbs[txq->tail]; - BUG_ON(!sent_skb); - sent_skbs[txq->tail] = NULL; - - /* skip header wrb */ - queue_tail_inc(txq); do { - cur_index = txq->tail; + if (sent_skbs[txq->tail]) { + /* Free skb from prev req */ + if (skb) + dev_consume_skb_any(skb); + skb = sent_skbs[txq->tail]; + sent_skbs[txq->tail] = NULL; + queue_tail_inc(txq); /* skip hdr wrb */ + num_wrbs++; + unmap_skb_hdr = true; + } wrb = queue_tail_node(txq); + frag_index = txq->tail; unmap_tx_frag(&adapter->pdev->dev, wrb, - (unmap_skb_hdr && skb_headlen(sent_skb))); + (unmap_skb_hdr && skb_headlen(skb))); unmap_skb_hdr = false; - - num_wrbs++; queue_tail_inc(txq); - } while (cur_index != last_index); + num_wrbs++; + } while (frag_index != last_index); + dev_consume_skb_any(skb); - dev_consume_skb_any(sent_skb); return num_wrbs; } @@ -2068,12 +2122,11 @@ static void be_rx_cq_clean(struct be_rx_obj *rxo) static void be_tx_compl_clean(struct be_adapter *adapter) { + u16 end_idx, notified_idx, cmpl = 0, timeo = 0, num_wrbs = 0; + struct device *dev = &adapter->pdev->dev; struct be_tx_obj *txo; struct be_queue_info *txq; struct be_eth_tx_compl *txcp; - u16 end_idx, cmpl = 0, timeo = 0, num_wrbs = 0; - struct sk_buff *sent_skb; - bool dummy_wrb; int i, pending_txqs; /* Stop polling for compls when HW has been silent for 10ms */ @@ -2095,7 +2148,7 @@ static void be_tx_compl_clean(struct be_adapter *adapter) atomic_sub(num_wrbs, &txq->used); timeo = 0; } - if (atomic_read(&txq->used) == 0) + if (atomic_read(&txq->used) == txo->pend_wrb_cnt) pending_txqs--; } @@ -2105,21 +2158,29 @@ static void be_tx_compl_clean(struct be_adapter *adapter) mdelay(1); } while (true); + /* Free enqueued TX that was never notified to HW */ for_all_tx_queues(adapter, txo, i) { txq = &txo->q; - if (atomic_read(&txq->used)) - dev_err(&adapter->pdev->dev, "%d pending tx-compls\n", - atomic_read(&txq->used)); - /* free posted tx for which compls will never arrive */ - while (atomic_read(&txq->used)) { - sent_skb = txo->sent_skb_list[txq->tail]; + if (atomic_read(&txq->used)) { + dev_info(dev, "txq%d: cleaning %d pending tx-wrbs\n", + i, atomic_read(&txq->used)); + notified_idx = txq->tail; end_idx = txq->tail; - num_wrbs = wrb_cnt_for_skb(adapter, sent_skb, - &dummy_wrb); - index_adv(&end_idx, num_wrbs - 1, txq->len); + index_adv(&end_idx, atomic_read(&txq->used) - 1, + txq->len); + /* Use the tx-compl process logic to handle requests + * that were not sent to the HW. + */ num_wrbs = be_tx_compl_process(adapter, txo, end_idx); atomic_sub(num_wrbs, &txq->used); + BUG_ON(atomic_read(&txq->used)); + txo->pend_wrb_cnt = 0; + /* Since hw was never notified of these requests, + * reset TXQ indices + */ + txq->head = notified_idx; + txq->tail = notified_idx; } } } @@ -2514,6 +2575,106 @@ static void be_process_tx(struct be_adapter *adapter, struct be_tx_obj *txo, } } +#ifdef CONFIG_NET_RX_BUSY_POLL +static inline bool be_lock_napi(struct be_eq_obj *eqo) +{ + bool status = true; + + spin_lock(&eqo->lock); /* BH is already disabled */ + if (eqo->state & BE_EQ_LOCKED) { + WARN_ON(eqo->state & BE_EQ_NAPI); + eqo->state |= BE_EQ_NAPI_YIELD; + status = false; + } else { + eqo->state = BE_EQ_NAPI; + } + spin_unlock(&eqo->lock); + return status; +} + +static inline void be_unlock_napi(struct be_eq_obj *eqo) +{ + spin_lock(&eqo->lock); /* BH is already disabled */ + + WARN_ON(eqo->state & (BE_EQ_POLL | BE_EQ_NAPI_YIELD)); + eqo->state = BE_EQ_IDLE; + + spin_unlock(&eqo->lock); +} + +static inline bool be_lock_busy_poll(struct be_eq_obj *eqo) +{ + bool status = true; + + spin_lock_bh(&eqo->lock); + if (eqo->state & BE_EQ_LOCKED) { + eqo->state |= BE_EQ_POLL_YIELD; + status = false; + } else { + eqo->state |= BE_EQ_POLL; + } + spin_unlock_bh(&eqo->lock); + return status; +} + +static inline void be_unlock_busy_poll(struct be_eq_obj *eqo) +{ + spin_lock_bh(&eqo->lock); + + WARN_ON(eqo->state & (BE_EQ_NAPI)); + eqo->state = BE_EQ_IDLE; + + spin_unlock_bh(&eqo->lock); +} + +static inline void be_enable_busy_poll(struct be_eq_obj *eqo) +{ + spin_lock_init(&eqo->lock); + eqo->state = BE_EQ_IDLE; +} + +static inline void be_disable_busy_poll(struct be_eq_obj *eqo) +{ + local_bh_disable(); + + /* It's enough to just acquire napi lock on the eqo to stop + * be_busy_poll() from processing any queueus. + */ + while (!be_lock_napi(eqo)) + mdelay(1); + + local_bh_enable(); +} + +#else /* CONFIG_NET_RX_BUSY_POLL */ + +static inline bool be_lock_napi(struct be_eq_obj *eqo) +{ + return true; +} + +static inline void be_unlock_napi(struct be_eq_obj *eqo) +{ +} + +static inline bool be_lock_busy_poll(struct be_eq_obj *eqo) +{ + return false; +} + +static inline void be_unlock_busy_poll(struct be_eq_obj *eqo) +{ +} + +static inline void be_enable_busy_poll(struct be_eq_obj *eqo) +{ +} + +static inline void be_disable_busy_poll(struct be_eq_obj *eqo) +{ +} +#endif /* CONFIG_NET_RX_BUSY_POLL */ + int be_poll(struct napi_struct *napi, int budget) { struct be_eq_obj *eqo = container_of(napi, struct be_eq_obj, napi); @@ -2833,11 +2994,7 @@ static int be_close(struct net_device *netdev) be_tx_compl_clean(adapter); be_rx_qs_destroy(adapter); - - for (i = 1; i < (adapter->uc_macs + 1); i++) - be_cmd_pmac_del(adapter, adapter->if_handle, - adapter->pmac_id[i], 0); - adapter->uc_macs = 0; + be_clear_uc_list(adapter); for_all_evt_queues(adapter, eqo, i) { if (msix_enabled(adapter)) @@ -3008,6 +3165,19 @@ static int be_setup_wol(struct be_adapter *adapter, bool enable) return status; } +static void be_vf_eth_addr_generate(struct be_adapter *adapter, u8 *mac) +{ + u32 addr; + + addr = jhash(adapter->netdev->dev_addr, ETH_ALEN, 0); + + mac[5] = (u8)(addr & 0xFF); + mac[4] = (u8)((addr >> 8) & 0xFF); + mac[3] = (u8)((addr >> 16) & 0xFF); + /* Use the OUI from the current MAC address */ + memcpy(mac, adapter->netdev->dev_addr, 3); +} + /* * Generate a seed MAC address from the PF MAC Address using jhash. * MAC Address for VFs are assigned incrementally starting from the seed. @@ -3108,14 +3278,9 @@ static void be_cancel_worker(struct be_adapter *adapter) static void be_mac_clear(struct be_adapter *adapter) { - int i; - if (adapter->pmac_id) { - for (i = 0; i < (adapter->uc_macs + 1); i++) - be_cmd_pmac_del(adapter, adapter->if_handle, - adapter->pmac_id[i], 0); - adapter->uc_macs = 0; - + be_cmd_pmac_del(adapter, adapter->if_handle, + adapter->pmac_id[0], 0); kfree(adapter->pmac_id); adapter->pmac_id = NULL; } @@ -3171,13 +3336,32 @@ static int be_clear(struct be_adapter *adapter) return 0; } +static int be_if_create(struct be_adapter *adapter, u32 *if_handle, + u32 cap_flags, u32 vf) +{ + u32 en_flags; + int status; + + en_flags = BE_IF_FLAGS_UNTAGGED | BE_IF_FLAGS_BROADCAST | + BE_IF_FLAGS_MULTICAST | BE_IF_FLAGS_PASS_L3L4_ERRORS | + BE_IF_FLAGS_RSS; + + en_flags &= cap_flags; + + status = be_cmd_if_create(adapter, cap_flags, en_flags, + if_handle, vf); + + return status; +} + static int be_vfs_if_create(struct be_adapter *adapter) { struct be_resources res = {0}; struct be_vf_cfg *vf_cfg; - u32 cap_flags, en_flags, vf; - int status = 0; + u32 cap_flags, vf; + int status; + /* If a FW profile exists, then cap_flags are updated */ cap_flags = BE_IF_FLAGS_UNTAGGED | BE_IF_FLAGS_BROADCAST | BE_IF_FLAGS_MULTICAST; @@ -3189,18 +3373,13 @@ static int be_vfs_if_create(struct be_adapter *adapter) cap_flags = res.if_cap_flags; } - /* If a FW profile exists, then cap_flags are updated */ - en_flags = cap_flags & (BE_IF_FLAGS_UNTAGGED | - BE_IF_FLAGS_BROADCAST | - BE_IF_FLAGS_MULTICAST); - status = - be_cmd_if_create(adapter, cap_flags, en_flags, - &vf_cfg->if_handle, vf + 1); + status = be_if_create(adapter, &vf_cfg->if_handle, + cap_flags, vf + 1); if (status) - goto err; + return status; } -err: - return status; + + return 0; } static int be_vf_setup_init(struct be_adapter *adapter) @@ -3385,7 +3564,7 @@ static void be_setup_init(struct be_adapter *adapter) adapter->phy.link_speed = -1; adapter->if_handle = -1; adapter->be3_native = false; - adapter->promiscuous = false; + adapter->if_flags = 0; if (be_physfn(adapter)) adapter->cmd_privileges = MAX_PRIVILEGES; else @@ -3512,7 +3691,9 @@ static int be_get_config(struct be_adapter *adapter) if (status) return status; - if (be_physfn(adapter)) { + be_cmd_query_port_name(adapter); + + if (be_physfn(adapter)) { status = be_cmd_get_active_profile(adapter, &profile_id); if (!status) dev_info(&adapter->pdev->dev, @@ -3638,10 +3819,20 @@ int be_update_queues(struct be_adapter *adapter) return status; } +static inline int fw_major_num(const char *fw_ver) +{ + int fw_major = 0, i; + + i = sscanf(fw_ver, "%d.", &fw_major); + if (i != 1) + return 0; + + return fw_major; +} + static int be_setup(struct be_adapter *adapter) { struct device *dev = &adapter->pdev->dev; - u32 tx_fc, rx_fc, en_flags; int status; be_setup_init(adapter); @@ -3657,13 +3848,8 @@ static int be_setup(struct be_adapter *adapter) if (status) goto err; - en_flags = BE_IF_FLAGS_UNTAGGED | BE_IF_FLAGS_BROADCAST | - BE_IF_FLAGS_MULTICAST | BE_IF_FLAGS_PASS_L3L4_ERRORS; - if (adapter->function_caps & BE_FUNCTION_CAPS_RSS) - en_flags |= BE_IF_FLAGS_RSS; - en_flags = en_flags & be_if_cap_flags(adapter); - status = be_cmd_if_create(adapter, be_if_cap_flags(adapter), en_flags, - &adapter->if_handle, 0); + status = be_if_create(adapter, &adapter->if_handle, + be_if_cap_flags(adapter), 0); if (status) goto err; @@ -3696,11 +3882,14 @@ static int be_setup(struct be_adapter *adapter) be_cmd_get_acpi_wol_cap(adapter); - be_cmd_get_flow_control(adapter, &tx_fc, &rx_fc); + status = be_cmd_set_flow_control(adapter, adapter->tx_fc, + adapter->rx_fc); + if (status) + be_cmd_get_flow_control(adapter, &adapter->tx_fc, + &adapter->rx_fc); - if (rx_fc != adapter->rx_fc || tx_fc != adapter->tx_fc) - be_cmd_set_flow_control(adapter, adapter->tx_fc, - adapter->rx_fc); + dev_info(&adapter->pdev->dev, "HW Flow control - TX:%d RX:%d\n", + adapter->tx_fc, adapter->rx_fc); if (be_physfn(adapter)) be_cmd_set_logical_link_config(adapter, @@ -3739,7 +3928,7 @@ static char flash_cookie[2][16] = {"*** SE FLAS", "H DIRECTORY *** "}; static bool phy_flashing_required(struct be_adapter *adapter) { - return (adapter->phy.phy_type == TN_8022 && + return (adapter->phy.phy_type == PHY_TYPE_TN_8022 && adapter->phy.interface_type == PHY_TYPE_BASET_10GB); } @@ -3790,7 +3979,8 @@ static int be_check_flash_crc(struct be_adapter *adapter, const u8 *p, int status; u8 crc[4]; - status = be_cmd_get_flash_crc(adapter, crc, img_optype, img_size - 4); + status = be_cmd_get_flash_crc(adapter, crc, img_optype, img_offset, + img_size - 4); if (status) return status; @@ -3806,13 +3996,13 @@ static int be_check_flash_crc(struct be_adapter *adapter, const u8 *p, } static int be_flash(struct be_adapter *adapter, const u8 *img, - struct be_dma_mem *flash_cmd, int optype, int img_size) + struct be_dma_mem *flash_cmd, int optype, int img_size, + u32 img_offset) { + u32 flash_op, num_bytes, total_bytes = img_size, bytes_sent = 0; struct be_cmd_write_flashrom *req = flash_cmd->va; - u32 total_bytes, flash_op, num_bytes; int status; - total_bytes = img_size; while (total_bytes) { num_bytes = min_t(u32, 32*1024, total_bytes); @@ -3833,12 +4023,15 @@ static int be_flash(struct be_adapter *adapter, const u8 *img, memcpy(req->data_buf, img, num_bytes); img += num_bytes; status = be_cmd_write_flashrom(adapter, flash_cmd, optype, - flash_op, num_bytes); + flash_op, img_offset + + bytes_sent, num_bytes); if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST && optype == OPTYPE_PHY_FW) break; else if (status) return status; + + bytes_sent += num_bytes; } return 0; } @@ -3906,6 +4099,7 @@ static int be_flash_BEx(struct be_adapter *adapter, pflashcomp = gen2_flash_types; filehdr_size = sizeof(struct flash_file_hdr_g2); num_comp = ARRAY_SIZE(gen2_flash_types); + img_hdrs_size = 0; } /* Get flash section info*/ @@ -3950,7 +4144,7 @@ static int be_flash_BEx(struct be_adapter *adapter, return -1; status = be_flash(adapter, p, flash_cmd, pflashcomp[i].optype, - pflashcomp[i].size); + pflashcomp[i].size, 0); if (status) { dev_err(dev, "Flashing section type 0x%x failed\n", pflashcomp[i].img_type); @@ -4017,12 +4211,12 @@ static int be_flash_skyhawk(struct be_adapter *adapter, struct be_dma_mem *flash_cmd, int num_of_images) { int img_hdrs_size = num_of_images * sizeof(struct image_hdr); + bool crc_match, old_fw_img, flash_offset_support = true; struct device *dev = &adapter->pdev->dev; struct flash_section_info *fsec = NULL; u32 img_offset, img_size, img_type; + u16 img_optype, flash_optype; int status, i, filehdr_size; - bool crc_match, old_fw_img; - u16 img_optype; const u8 *p; filehdr_size = sizeof(struct flash_file_hdr_g3); @@ -4032,6 +4226,7 @@ static int be_flash_skyhawk(struct be_adapter *adapter, return -EINVAL; } +retry_flash: for (i = 0; i < le32_to_cpu(fsec->fsec_hdr.num_images); i++) { img_offset = le32_to_cpu(fsec->fsec_entry[i].offset); img_size = le32_to_cpu(fsec->fsec_entry[i].pad_size); @@ -4041,6 +4236,12 @@ static int be_flash_skyhawk(struct be_adapter *adapter, if (img_optype == 0xFFFF) continue; + + if (flash_offset_support) + flash_optype = OPTYPE_OFFSET_SPECIFIED; + else + flash_optype = img_optype; + /* Don't bother verifying CRC if an old FW image is being * flashed */ @@ -4049,16 +4250,26 @@ static int be_flash_skyhawk(struct be_adapter *adapter, status = be_check_flash_crc(adapter, fw->data, img_offset, img_size, filehdr_size + - img_hdrs_size, img_optype, + img_hdrs_size, flash_optype, &crc_match); - /* The current FW image on the card does not recognize the new - * FLASH op_type. The FW download is partially complete. - * Reboot the server now to enable FW image to recognize the - * new FLASH op_type. To complete the remaining process, - * download the same FW again after the reboot. - */ if (base_status(status) == MCC_STATUS_ILLEGAL_REQUEST || base_status(status) == MCC_STATUS_ILLEGAL_FIELD) { + /* The current FW image on the card does not support + * OFFSET based flashing. Retry using older mechanism + * of OPTYPE based flashing + */ + if (flash_optype == OPTYPE_OFFSET_SPECIFIED) { + flash_offset_support = false; + goto retry_flash; + } + + /* The current FW image on the card does not recognize + * the new FLASH op_type. The FW download is partially + * complete. Reboot the server now to enable FW image + * to recognize the new FLASH op_type. To complete the + * remaining process, download the same FW again after + * the reboot. + */ dev_err(dev, "Flash incomplete. Reset the server\n"); dev_err(dev, "Download FW image again after reset\n"); return -EAGAIN; @@ -4076,7 +4287,19 @@ flash: if (p + img_size > fw->data + fw->size) return -1; - status = be_flash(adapter, p, flash_cmd, img_optype, img_size); + status = be_flash(adapter, p, flash_cmd, flash_optype, img_size, + img_offset); + + /* The current FW image on the card does not support OFFSET + * based flashing. Retry using older mechanism of OPTYPE based + * flashing + */ + if (base_status(status) == MCC_STATUS_ILLEGAL_FIELD && + flash_optype == OPTYPE_OFFSET_SPECIFIED) { + flash_offset_support = false; + goto retry_flash; + } + /* For old FW images ignore ILLEGAL_FIELD error or errors on * UFI_DIR region */ @@ -4179,98 +4402,105 @@ static int lancer_fw_download(struct be_adapter *adapter, return 0; } -#define UFI_TYPE2 2 -#define UFI_TYPE3 3 -#define UFI_TYPE3R 10 -#define UFI_TYPE4 4 +#define BE2_UFI 2 +#define BE3_UFI 3 +#define BE3R_UFI 10 +#define SH_UFI 4 +#define SH_P2_UFI 11 + static int be_get_ufi_type(struct be_adapter *adapter, struct flash_file_hdr_g3 *fhdr) { - if (!fhdr) - goto be_get_ufi_exit; + if (!fhdr) { + dev_err(&adapter->pdev->dev, "Invalid FW UFI file"); + return -1; + } - if (skyhawk_chip(adapter) && fhdr->build[0] == '4') - return UFI_TYPE4; - else if (BE3_chip(adapter) && fhdr->build[0] == '3') { - if (fhdr->asic_type_rev == 0x10) - return UFI_TYPE3R; - else - return UFI_TYPE3; - } else if (BE2_chip(adapter) && fhdr->build[0] == '2') - return UFI_TYPE2; + /* First letter of the build version is used to identify + * which chip this image file is meant for. + */ + switch (fhdr->build[0]) { + case BLD_STR_UFI_TYPE_SH: + return (fhdr->asic_type_rev == ASIC_REV_P2) ? SH_P2_UFI : + SH_UFI; + case BLD_STR_UFI_TYPE_BE3: + return (fhdr->asic_type_rev == ASIC_REV_B0) ? BE3R_UFI : + BE3_UFI; + case BLD_STR_UFI_TYPE_BE2: + return BE2_UFI; + default: + return -1; + } +} -be_get_ufi_exit: - dev_err(&adapter->pdev->dev, - "UFI and Interface are not compatible for flashing\n"); - return -1; +/* Check if the flash image file is compatible with the adapter that + * is being flashed. + * BE3 chips with asic-rev B0 must be flashed only with BE3R_UFI type. + * Skyhawk chips with asic-rev P2 must be flashed only with SH_P2_UFI type. + */ +static bool be_check_ufi_compatibility(struct be_adapter *adapter, + struct flash_file_hdr_g3 *fhdr) +{ + int ufi_type = be_get_ufi_type(adapter, fhdr); + + switch (ufi_type) { + case SH_P2_UFI: + return skyhawk_chip(adapter); + case SH_UFI: + return (skyhawk_chip(adapter) && + adapter->asic_rev < ASIC_REV_P2); + case BE3R_UFI: + return BE3_chip(adapter); + case BE3_UFI: + return (BE3_chip(adapter) && adapter->asic_rev < ASIC_REV_B0); + case BE2_UFI: + return BE2_chip(adapter); + default: + return false; + } } static int be_fw_download(struct be_adapter *adapter, const struct firmware* fw) { + struct device *dev = &adapter->pdev->dev; struct flash_file_hdr_g3 *fhdr3; - struct image_hdr *img_hdr_ptr = NULL; + struct image_hdr *img_hdr_ptr; + int status = 0, i, num_imgs; struct be_dma_mem flash_cmd; - const u8 *p; - int status = 0, i = 0, num_imgs = 0, ufi_type = 0; - flash_cmd.size = sizeof(struct be_cmd_write_flashrom); - flash_cmd.va = dma_alloc_coherent(&adapter->pdev->dev, flash_cmd.size, - &flash_cmd.dma, GFP_KERNEL); - if (!flash_cmd.va) { - status = -ENOMEM; - goto be_fw_exit; + fhdr3 = (struct flash_file_hdr_g3 *)fw->data; + if (!be_check_ufi_compatibility(adapter, fhdr3)) { + dev_err(dev, "Flash image is not compatible with adapter\n"); + return -EINVAL; } - p = fw->data; - fhdr3 = (struct flash_file_hdr_g3 *)p; - - ufi_type = be_get_ufi_type(adapter, fhdr3); + flash_cmd.size = sizeof(struct be_cmd_write_flashrom); + flash_cmd.va = dma_alloc_coherent(dev, flash_cmd.size, &flash_cmd.dma, + GFP_KERNEL); + if (!flash_cmd.va) + return -ENOMEM; num_imgs = le32_to_cpu(fhdr3->num_imgs); for (i = 0; i < num_imgs; i++) { img_hdr_ptr = (struct image_hdr *)(fw->data + (sizeof(struct flash_file_hdr_g3) + i * sizeof(struct image_hdr))); - if (le32_to_cpu(img_hdr_ptr->imageid) == 1) { - switch (ufi_type) { - case UFI_TYPE4: - status = be_flash_skyhawk(adapter, fw, - &flash_cmd, num_imgs); - break; - case UFI_TYPE3R: - status = be_flash_BEx(adapter, fw, &flash_cmd, - num_imgs); - break; - case UFI_TYPE3: - /* Do not flash this ufi on BE3-R cards */ - if (adapter->asic_rev < 0x10) - status = be_flash_BEx(adapter, fw, - &flash_cmd, - num_imgs); - else { - status = -EINVAL; - dev_err(&adapter->pdev->dev, - "Can't load BE3 UFI on BE3R\n"); - } - } - } - } - - if (ufi_type == UFI_TYPE2) - status = be_flash_BEx(adapter, fw, &flash_cmd, 0); - else if (ufi_type == -1) - status = -EINVAL; + if (!BE2_chip(adapter) && + le32_to_cpu(img_hdr_ptr->imageid) != 1) + continue; - dma_free_coherent(&adapter->pdev->dev, flash_cmd.size, flash_cmd.va, - flash_cmd.dma); - if (status) { - dev_err(&adapter->pdev->dev, "Firmware load error\n"); - goto be_fw_exit; + if (skyhawk_chip(adapter)) + status = be_flash_skyhawk(adapter, fw, &flash_cmd, + num_imgs); + else + status = be_flash_BEx(adapter, fw, &flash_cmd, + num_imgs); } - dev_info(&adapter->pdev->dev, "Firmware flashed successfully\n"); + dma_free_coherent(dev, flash_cmd.size, flash_cmd.va, flash_cmd.dma); + if (!status) + dev_info(dev, "Firmware flashed successfully\n"); -be_fw_exit: return status; } @@ -4304,7 +4534,8 @@ fw_exit: return status; } -static int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh) +static int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh, + u16 flags) { struct be_adapter *adapter = netdev_priv(dev); struct nlattr *attr, *br_spec; @@ -4383,8 +4614,9 @@ static int be_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, * distinguish various types of transports (VxLAN, GRE, NVGRE ..). So, offload * is expected to work across all types of IP tunnels once exported. Skyhawk * supports offloads for either VxLAN or NVGRE, exclusively. So we export VxLAN - * offloads in hw_enc_features only when a VxLAN port is added. Note this only - * ensures that other tunnels work fine while VxLAN offloads are not enabled. + * offloads in hw_enc_features only when a VxLAN port is added. If other (non + * VxLAN) tunnels are configured while VxLAN offloads are enabled, offloads for + * those other tunnels are unexported on the fly through ndo_features_check(). * * Skyhawk supports VxLAN offloads only for one UDP dport. So, if the stack * adds more than one port, disable offloads and don't re-enable them again @@ -4463,7 +4695,41 @@ static netdev_features_t be_features_check(struct sk_buff *skb, struct net_device *dev, netdev_features_t features) { - return vxlan_features_check(skb, features); + struct be_adapter *adapter = netdev_priv(dev); + u8 l4_hdr = 0; + + /* The code below restricts offload features for some tunneled packets. + * Offload features for normal (non tunnel) packets are unchanged. + */ + if (!skb->encapsulation || + !(adapter->flags & BE_FLAGS_VXLAN_OFFLOADS)) + return features; + + /* It's an encapsulated packet and VxLAN offloads are enabled. We + * should disable tunnel offload features if it's not a VxLAN packet, + * as tunnel offloads have been enabled only for VxLAN. This is done to + * allow other tunneled traffic like GRE work fine while VxLAN + * offloads are configured in Skyhawk-R. + */ + switch (vlan_get_protocol(skb)) { + case htons(ETH_P_IP): + l4_hdr = ip_hdr(skb)->protocol; + break; + case htons(ETH_P_IPV6): + l4_hdr = ipv6_hdr(skb)->nexthdr; + break; + default: + return features; + } + + if (l4_hdr != IPPROTO_UDP || + skb->inner_protocol_type != ENCAP_TYPE_ETHER || + skb->inner_protocol != htons(ETH_P_TEB) || + skb_inner_mac_header(skb) - skb_transport_header(skb) != + sizeof(struct udphdr) + sizeof(struct vxlanhdr)) + return features & ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK); + + return features; } #endif @@ -4797,6 +5063,20 @@ static void be_func_recovery_task(struct work_struct *work) msecs_to_jiffies(1000)); } +static void be_log_sfp_info(struct be_adapter *adapter) +{ + int status; + + status = be_cmd_query_sfp_info(adapter); + if (!status) { + dev_err(&adapter->pdev->dev, + "Unqualified SFP+ detected on %c from %s part no: %s", + adapter->port_name, adapter->phy.vendor_name, + adapter->phy.vendor_pn); + } + adapter->flags &= ~BE_FLAGS_EVT_INCOMPATIBLE_SFP; +} + static void be_worker(struct work_struct *work) { struct be_adapter *adapter = @@ -4835,6 +5115,9 @@ static void be_worker(struct work_struct *work) be_eqd_update(adapter); + if (adapter->flags & BE_FLAGS_EVT_INCOMPATIBLE_SFP) + be_log_sfp_info(adapter); + reschedule: adapter->work_counter++; schedule_delayed_work(&adapter->work, msecs_to_jiffies(1000)); @@ -4881,12 +5164,31 @@ static inline char *func_name(struct be_adapter *adapter) return be_physfn(adapter) ? "PF" : "VF"; } +static inline char *nic_name(struct pci_dev *pdev) +{ + switch (pdev->device) { + case OC_DEVICE_ID1: + return OC_NAME; + case OC_DEVICE_ID2: + return OC_NAME_BE; + case OC_DEVICE_ID3: + case OC_DEVICE_ID4: + return OC_NAME_LANCER; + case BE_DEVICE_ID2: + return BE3_NAME; + case OC_DEVICE_ID5: + case OC_DEVICE_ID6: + return OC_NAME_SH; + default: + return BE_NAME; + } +} + static int be_probe(struct pci_dev *pdev, const struct pci_device_id *pdev_id) { - int status = 0; struct be_adapter *adapter; struct net_device *netdev; - char port_name; + int status = 0; dev_info(&pdev->dev, "%s version is %s\n", DRV_NAME, DRV_VER); @@ -4980,10 +5282,8 @@ static int be_probe(struct pci_dev *pdev, const struct pci_device_id *pdev_id) schedule_delayed_work(&adapter->func_recovery_work, msecs_to_jiffies(1000)); - be_cmd_query_port_name(adapter, &port_name); - dev_info(&pdev->dev, "%s: %s %s port %c\n", nic_name(pdev), - func_name(adapter), mc_name(adapter), port_name); + func_name(adapter), mc_name(adapter), adapter->port_name); return 0; @@ -5048,6 +5348,10 @@ static int be_resume(struct pci_dev *pdev) if (status) return status; + status = be_cmd_reset_function(adapter); + if (status) + return status; + be_intr_set(adapter, true); /* tell fw we're ready to fire cmds */ status = be_cmd_fw_init(adapter); diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig index 270308315d43..ba84c4a9ce32 100644 --- a/drivers/net/ethernet/freescale/Kconfig +++ b/drivers/net/ethernet/freescale/Kconfig @@ -69,7 +69,8 @@ config FSL_XGMAC_MDIO select PHYLIB select OF_MDIO ---help--- - This driver supports the MDIO bus on the Fman 10G Ethernet MACs. + This driver supports the MDIO bus on the Fman 10G Ethernet MACs, and + on the FMan mEMAC (which supports both Clauses 22 and 45) config UCC_GETH tristate "Freescale QE Gigabit Ethernet" diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 469691ad4a1e..a86af8a7485d 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -16,6 +16,7 @@ #include <linux/clocksource.h> #include <linux/net_tstamp.h> #include <linux/ptp_clock_kernel.h> +#include <linux/timecounter.h> #if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \ defined(CONFIG_M520x) || defined(CONFIG_M532x) || \ @@ -356,6 +357,7 @@ struct bufdesc_ex { #define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */ #define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */ #define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */ +#define FEC_ENET_WAKEUP ((uint)0x00020000) /* Wakeup request */ #define FEC_ENET_TXF (FEC_ENET_TXF_0 | FEC_ENET_TXF_1 | FEC_ENET_TXF_2) #define FEC_ENET_RXF (FEC_ENET_RXF_0 | FEC_ENET_RXF_1 | FEC_ENET_RXF_2) #define FEC_ENET_TS_AVAIL ((uint)0x00010000) @@ -424,6 +426,8 @@ struct bufdesc_ex { * (40ns * 6). */ #define FEC_QUIRK_BUG_CAPTURE (1 << 10) +/* Controller has only one MDIO bus */ +#define FEC_QUIRK_SINGLE_MDIO (1 << 11) struct fec_enet_priv_tx_q { int index; @@ -511,6 +515,7 @@ struct fec_enet_private { int irq[FEC_IRQ_NUM]; bool bufdesc_ex; int pause_flag; + int wol_flag; u32 quirks; struct napi_struct napi; diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 5ebdf8dc8a31..9bb6220663b2 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -91,7 +91,8 @@ static struct platform_device_id fec_devtype[] = { .driver_data = 0, }, { .name = "imx28-fec", - .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME, + .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME | + FEC_QUIRK_SINGLE_MDIO, }, { .name = "imx6q-fec", .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_HAS_GBIT | @@ -187,6 +188,9 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); #define FEC_MMFR_RA(v) ((v & 0x1f) << 18) #define FEC_MMFR_TA (2 << 16) #define FEC_MMFR_DATA(v) (v & 0xffff) +/* FEC ECR bits definition */ +#define FEC_ECR_MAGICEN (1 << 2) +#define FEC_ECR_SLEEP (1 << 3) #define FEC_MII_TIMEOUT 30000 /* us */ @@ -195,6 +199,9 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); #define FEC_PAUSE_FLAG_AUTONEG 0x1 #define FEC_PAUSE_FLAG_ENABLE 0x2 +#define FEC_WOL_HAS_MAGIC_PACKET (0x1 << 0) +#define FEC_WOL_FLAG_ENABLE (0x1 << 1) +#define FEC_WOL_FLAG_SLEEP_ON (0x1 << 2) #define COPYBREAK_DEFAULT 256 @@ -1089,7 +1096,9 @@ static void fec_stop(struct net_device *ndev) { struct fec_enet_private *fep = netdev_priv(ndev); + struct fec_platform_data *pdata = fep->pdev->dev.platform_data; u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8); + u32 val; /* We cannot expect a graceful transmit stop without link !!! */ if (fep->link) { @@ -1103,17 +1112,28 @@ fec_stop(struct net_device *ndev) * For i.MX6SX SOC, enet use AXI bus, we use disable MAC * instead of reset MAC itself. */ - if (fep->quirks & FEC_QUIRK_HAS_AVB) { - writel(0, fep->hwp + FEC_ECNTRL); + if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { + if (fep->quirks & FEC_QUIRK_HAS_AVB) { + writel(0, fep->hwp + FEC_ECNTRL); + } else { + writel(1, fep->hwp + FEC_ECNTRL); + udelay(10); + } + writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); } else { - writel(1, fep->hwp + FEC_ECNTRL); - udelay(10); + writel(FEC_DEFAULT_IMASK | FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK); + val = readl(fep->hwp + FEC_ECNTRL); + val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP); + writel(val, fep->hwp + FEC_ECNTRL); + + if (pdata && pdata->sleep_mode_enable) + pdata->sleep_mode_enable(true); } writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED); - writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); /* We have to keep ENET enabled to have MII interrupt stay working */ - if (fep->quirks & FEC_QUIRK_ENET_MAC) { + if (fep->quirks & FEC_QUIRK_ENET_MAC && + !(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) { writel(2, fep->hwp + FEC_ECNTRL); writel(rmii_mode, fep->hwp + FEC_R_CNTRL); } @@ -1169,12 +1189,13 @@ static void fec_enet_tx_queue(struct net_device *ndev, u16 queue_id) { struct fec_enet_private *fep; - struct bufdesc *bdp; + struct bufdesc *bdp, *bdp_t; unsigned short status; struct sk_buff *skb; struct fec_enet_priv_tx_q *txq; struct netdev_queue *nq; int index = 0; + int i, bdnum; int entries_free; fep = netdev_priv(ndev); @@ -1195,18 +1216,29 @@ fec_enet_tx_queue(struct net_device *ndev, u16 queue_id) if (bdp == txq->cur_tx) break; - index = fec_enet_get_bd_index(txq->tx_bd_base, bdp, fep); - + bdp_t = bdp; + bdnum = 1; + index = fec_enet_get_bd_index(txq->tx_bd_base, bdp_t, fep); skb = txq->tx_skbuff[index]; - txq->tx_skbuff[index] = NULL; - if (!IS_TSO_HEADER(txq, bdp->cbd_bufaddr)) - dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, - bdp->cbd_datlen, DMA_TO_DEVICE); - bdp->cbd_bufaddr = 0; - if (!skb) { - bdp = fec_enet_get_nextdesc(bdp, fep, queue_id); - continue; + while (!skb) { + bdp_t = fec_enet_get_nextdesc(bdp_t, fep, queue_id); + index = fec_enet_get_bd_index(txq->tx_bd_base, bdp_t, fep); + skb = txq->tx_skbuff[index]; + bdnum++; } + if (skb_shinfo(skb)->nr_frags && + (status = bdp_t->cbd_sc) & BD_ENET_TX_READY) + break; + + for (i = 0; i < bdnum; i++) { + if (!IS_TSO_HEADER(txq, bdp->cbd_bufaddr)) + dma_unmap_single(&fep->pdev->dev, bdp->cbd_bufaddr, + bdp->cbd_datlen, DMA_TO_DEVICE); + bdp->cbd_bufaddr = 0; + if (i < bdnum - 1) + bdp = fec_enet_get_nextdesc(bdp, fep, queue_id); + } + txq->tx_skbuff[index] = NULL; /* Check for errors. */ if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | @@ -1937,7 +1969,7 @@ static int fec_enet_mii_init(struct platform_device *pdev) int err = -ENXIO, i; /* - * The dual fec interfaces are not equivalent with enet-mac. + * The i.MX28 dual fec interfaces are not equal. * Here are the differences: * * - fec0 supports MII & RMII modes while fec1 only supports RMII @@ -1952,7 +1984,7 @@ static int fec_enet_mii_init(struct platform_device *pdev) * mdio interface in board design, and need to be configured by * fec0 mii_bus. */ - if ((fep->quirks & FEC_QUIRK_ENET_MAC) && fep->dev_id > 0) { + if ((fep->quirks & FEC_QUIRK_SINGLE_MDIO) && fep->dev_id > 0) { /* fec1 uses fec0 mii_bus */ if (mii_cnt && fec0_mii_bus) { fep->mii_bus = fec0_mii_bus; @@ -2015,7 +2047,7 @@ static int fec_enet_mii_init(struct platform_device *pdev) mii_cnt++; /* save fec0 mii_bus */ - if (fep->quirks & FEC_QUIRK_ENET_MAC) + if (fep->quirks & FEC_QUIRK_SINGLE_MDIO) fec0_mii_bus = fep->mii_bus; return 0; @@ -2427,6 +2459,44 @@ static int fec_enet_set_tunable(struct net_device *netdev, return ret; } +static void +fec_enet_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + + if (fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET) { + wol->supported = WAKE_MAGIC; + wol->wolopts = fep->wol_flag & FEC_WOL_FLAG_ENABLE ? WAKE_MAGIC : 0; + } else { + wol->supported = wol->wolopts = 0; + } +} + +static int +fec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) +{ + struct fec_enet_private *fep = netdev_priv(ndev); + + if (!(fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET)) + return -EINVAL; + + if (wol->wolopts & ~WAKE_MAGIC) + return -EINVAL; + + device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC); + if (device_may_wakeup(&ndev->dev)) { + fep->wol_flag |= FEC_WOL_FLAG_ENABLE; + if (fep->irq[0] > 0) + enable_irq_wake(fep->irq[0]); + } else { + fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE); + if (fep->irq[0] > 0) + disable_irq_wake(fep->irq[0]); + } + + return 0; +} + static const struct ethtool_ops fec_enet_ethtool_ops = { .get_settings = fec_enet_get_settings, .set_settings = fec_enet_set_settings, @@ -2445,6 +2515,8 @@ static const struct ethtool_ops fec_enet_ethtool_ops = { .get_ts_info = fec_enet_get_ts_info, .get_tunable = fec_enet_get_tunable, .set_tunable = fec_enet_set_tunable, + .get_wol = fec_enet_get_wol, + .set_wol = fec_enet_set_wol, }; static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd) @@ -2524,12 +2596,9 @@ static void fec_enet_free_queue(struct net_device *ndev) } for (i = 0; i < fep->num_rx_queues; i++) - if (fep->rx_queue[i]) - kfree(fep->rx_queue[i]); - + kfree(fep->rx_queue[i]); for (i = 0; i < fep->num_tx_queues; i++) - if (fep->tx_queue[i]) - kfree(fep->tx_queue[i]); + kfree(fep->tx_queue[i]); } static int fec_enet_alloc_queue(struct net_device *ndev) @@ -2705,6 +2774,9 @@ fec_enet_open(struct net_device *ndev) phy_start(fep->phy_dev); netif_tx_start_all_queues(ndev); + device_set_wakeup_enable(&ndev->dev, fep->wol_flag & + FEC_WOL_FLAG_ENABLE); + return 0; err_enet_mii_probe: @@ -3129,6 +3201,7 @@ fec_probe(struct platform_device *pdev) pdev->id_entry = of_id->data; fep->quirks = pdev->id_entry->driver_data; + fep->netdev = ndev; fep->num_rx_queues = num_rx_qs; fep->num_tx_queues = num_tx_qs; @@ -3153,6 +3226,9 @@ fec_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ndev); + if (of_get_property(np, "fsl,magic-packet", NULL)) + fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET; + phy_node = of_parse_phandle(np, "phy-handle", 0); if (!phy_node && of_phy_is_fixed_link(np)) { ret = of_phy_register_fixed_link(np); @@ -3247,6 +3323,8 @@ fec_probe(struct platform_device *pdev) 0, pdev->name, ndev); if (ret) goto failed_irq; + + fep->irq[i] = irq; } init_completion(&fep->mdio_done); @@ -3263,6 +3341,9 @@ fec_probe(struct platform_device *pdev) if (ret) goto failed_register; + device_init_wakeup(&ndev->dev, fep->wol_flag & + FEC_WOL_HAS_MAGIC_PACKET); + if (fep->bufdesc_ex && fep->ptp_clock) netdev_info(ndev, "registered PHC device %d\n", fep->dev_id); @@ -3316,6 +3397,8 @@ static int __maybe_unused fec_suspend(struct device *dev) rtnl_lock(); if (netif_running(ndev)) { + if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) + fep->wol_flag |= FEC_WOL_FLAG_SLEEP_ON; phy_stop(fep->phy_dev); napi_disable(&fep->napi); netif_tx_lock_bh(ndev); @@ -3323,11 +3406,12 @@ static int __maybe_unused fec_suspend(struct device *dev) netif_tx_unlock_bh(ndev); fec_stop(ndev); fec_enet_clk_enable(ndev, false); - pinctrl_pm_select_sleep_state(&fep->pdev->dev); + if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) + pinctrl_pm_select_sleep_state(&fep->pdev->dev); } rtnl_unlock(); - if (fep->reg_phy) + if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) regulator_disable(fep->reg_phy); /* SOC supply clock to phy, when clock is disabled, phy link down @@ -3343,9 +3427,11 @@ static int __maybe_unused fec_resume(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); struct fec_enet_private *fep = netdev_priv(ndev); + struct fec_platform_data *pdata = fep->pdev->dev.platform_data; int ret; + int val; - if (fep->reg_phy) { + if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) { ret = regulator_enable(fep->reg_phy); if (ret) return ret; @@ -3353,12 +3439,21 @@ static int __maybe_unused fec_resume(struct device *dev) rtnl_lock(); if (netif_running(ndev)) { - pinctrl_pm_select_default_state(&fep->pdev->dev); ret = fec_enet_clk_enable(ndev, true); if (ret) { rtnl_unlock(); goto failed_clk; } + if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) { + if (pdata && pdata->sleep_mode_enable) + pdata->sleep_mode_enable(false); + val = readl(fep->hwp + FEC_ECNTRL); + val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP); + writel(val, fep->hwp + FEC_ECNTRL); + fep->wol_flag &= ~FEC_WOL_FLAG_SLEEP_ON; + } else { + pinctrl_pm_select_default_state(&fep->pdev->dev); + } fec_restart(ndev); netif_tx_lock_bh(ndev); netif_device_attach(ndev); diff --git a/drivers/net/ethernet/freescale/fec_ptp.c b/drivers/net/ethernet/freescale/fec_ptp.c index 992c8c3db553..1f9cf2345266 100644 --- a/drivers/net/ethernet/freescale/fec_ptp.c +++ b/drivers/net/ethernet/freescale/fec_ptp.c @@ -374,23 +374,9 @@ static int fec_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) struct fec_enet_private *fep = container_of(ptp, struct fec_enet_private, ptp_caps); unsigned long flags; - u64 now; - u32 counter; spin_lock_irqsave(&fep->tmreg_lock, flags); - - now = timecounter_read(&fep->tc); - now += delta; - - /* Get the timer value based on adjusted timestamp. - * Update the counter with the masked value. - */ - counter = now & fep->cc.mask; - writel(counter, fep->hwp + FEC_ATIME); - - /* reset the timecounter */ - timecounter_init(&fep->tc, &fep->cc, now); - + timecounter_adjtime(&fep->tc, delta); spin_unlock_irqrestore(&fep->tmreg_lock, flags); return 0; diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c index 9e2bcb807923..a17628769a1f 100644 --- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c +++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c @@ -278,14 +278,20 @@ static int fs_enet_tx_napi(struct napi_struct *napi, int budget) fep->stats.collisions++; /* unmap */ - dma_unmap_single(fep->dev, CBDR_BUFADDR(bdp), - skb->len, DMA_TO_DEVICE); + if (fep->mapped_as_page[dirtyidx]) + dma_unmap_page(fep->dev, CBDR_BUFADDR(bdp), + CBDR_DATLEN(bdp), DMA_TO_DEVICE); + else + dma_unmap_single(fep->dev, CBDR_BUFADDR(bdp), + CBDR_DATLEN(bdp), DMA_TO_DEVICE); /* * Free the sk buffer associated with this last transmit. */ - dev_kfree_skb(skb); - fep->tx_skbuff[dirtyidx] = NULL; + if (skb) { + dev_kfree_skb(skb); + fep->tx_skbuff[dirtyidx] = NULL; + } /* * Update pointer to next buffer descriptor to be transmitted. @@ -299,7 +305,7 @@ static int fs_enet_tx_napi(struct napi_struct *napi, int budget) * Since we have freed up a buffer, the ring is no longer * full. */ - if (!fep->tx_free++) + if (++fep->tx_free >= MAX_SKB_FRAGS) do_wake = 1; has_tx_work = 1; } @@ -509,6 +515,9 @@ static int fs_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) cbd_t __iomem *bdp; int curidx; u16 sc; + int nr_frags = skb_shinfo(skb)->nr_frags; + skb_frag_t *frag; + int len; #ifdef CONFIG_FS_ENET_MPC5121_FEC if (((unsigned long)skb->data) & 0x3) { @@ -530,7 +539,7 @@ static int fs_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) */ bdp = fep->cur_tx; - if (!fep->tx_free || (CBDR_SC(bdp) & BD_ENET_TX_READY)) { + if (fep->tx_free <= nr_frags || (CBDR_SC(bdp) & BD_ENET_TX_READY)) { netif_stop_queue(dev); spin_unlock(&fep->tx_lock); @@ -543,35 +552,42 @@ static int fs_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) } curidx = bdp - fep->tx_bd_base; - /* - * Clear all of the status flags. - */ - CBDC_SC(bdp, BD_ENET_TX_STATS); - - /* - * Save skb pointer. - */ - fep->tx_skbuff[curidx] = skb; - - fep->stats.tx_bytes += skb->len; + len = skb->len; + fep->stats.tx_bytes += len; + if (nr_frags) + len -= skb->data_len; + fep->tx_free -= nr_frags + 1; /* * Push the data cache so the CPM does not get stale memory data. */ CBDW_BUFADDR(bdp, dma_map_single(fep->dev, - skb->data, skb->len, DMA_TO_DEVICE)); - CBDW_DATLEN(bdp, skb->len); + skb->data, len, DMA_TO_DEVICE)); + CBDW_DATLEN(bdp, len); + + fep->mapped_as_page[curidx] = 0; + frag = skb_shinfo(skb)->frags; + while (nr_frags) { + CBDC_SC(bdp, + BD_ENET_TX_STATS | BD_ENET_TX_LAST | BD_ENET_TX_TC); + CBDS_SC(bdp, BD_ENET_TX_READY); + + if ((CBDR_SC(bdp) & BD_ENET_TX_WRAP) == 0) + bdp++, curidx++; + else + bdp = fep->tx_bd_base, curidx = 0; - /* - * If this was the last BD in the ring, start at the beginning again. - */ - if ((CBDR_SC(bdp) & BD_ENET_TX_WRAP) == 0) - fep->cur_tx++; - else - fep->cur_tx = fep->tx_bd_base; + len = skb_frag_size(frag); + CBDW_BUFADDR(bdp, skb_frag_dma_map(fep->dev, frag, 0, len, + DMA_TO_DEVICE)); + CBDW_DATLEN(bdp, len); - if (!--fep->tx_free) - netif_stop_queue(dev); + fep->tx_skbuff[curidx] = NULL; + fep->mapped_as_page[curidx] = 1; + + frag++; + nr_frags--; + } /* Trigger transmission start */ sc = BD_ENET_TX_READY | BD_ENET_TX_INTR | @@ -582,8 +598,22 @@ static int fs_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) * yay for hw reuse :) */ if (skb->len <= 60) sc |= BD_ENET_TX_PAD; + CBDC_SC(bdp, BD_ENET_TX_STATS); CBDS_SC(bdp, sc); + /* Save skb pointer. */ + fep->tx_skbuff[curidx] = skb; + + /* If this was the last BD in the ring, start at the beginning again. */ + if ((CBDR_SC(bdp) & BD_ENET_TX_WRAP) == 0) + bdp++; + else + bdp = fep->tx_bd_base; + fep->cur_tx = bdp; + + if (fep->tx_free < MAX_SKB_FRAGS) + netif_stop_queue(dev); + skb_tx_timestamp(skb); (*fep->ops->tx_kickstart)(dev); @@ -917,7 +947,7 @@ static int fs_enet_probe(struct platform_device *ofdev) } fpi->rx_ring = 32; - fpi->tx_ring = 32; + fpi->tx_ring = 64; fpi->rx_copybreak = 240; fpi->napi_weight = 17; fpi->phy_node = of_parse_phandle(ofdev->dev.of_node, "phy-handle", 0); @@ -955,7 +985,8 @@ static int fs_enet_probe(struct platform_device *ofdev) privsize = sizeof(*fep) + sizeof(struct sk_buff **) * - (fpi->rx_ring + fpi->tx_ring); + (fpi->rx_ring + fpi->tx_ring) + + sizeof(char) * fpi->tx_ring; ndev = alloc_etherdev(privsize); if (!ndev) { @@ -978,6 +1009,8 @@ static int fs_enet_probe(struct platform_device *ofdev) fep->rx_skbuff = (struct sk_buff **)&fep[1]; fep->tx_skbuff = fep->rx_skbuff + fpi->rx_ring; + fep->mapped_as_page = (char *)(fep->rx_skbuff + fpi->rx_ring + + fpi->tx_ring); spin_lock_init(&fep->lock); spin_lock_init(&fep->tx_lock); @@ -1007,6 +1040,8 @@ static int fs_enet_probe(struct platform_device *ofdev) netif_carrier_off(ndev); + ndev->features |= NETIF_F_SG; + ret = register_netdev(ndev); if (ret) goto out_free_bd; diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet.h b/drivers/net/ethernet/freescale/fs_enet/fs_enet.h index 3a4b49e0e717..f184d8f952e2 100644 --- a/drivers/net/ethernet/freescale/fs_enet/fs_enet.h +++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet.h @@ -134,6 +134,7 @@ struct fs_enet_private { void __iomem *ring_base; struct sk_buff **rx_skbuff; struct sk_buff **tx_skbuff; + char *mapped_as_page; cbd_t __iomem *rx_bd_base; /* Address of Rx and Tx buffers. */ cbd_t __iomem *tx_bd_base; cbd_t __iomem *dirty_tx; /* ring entries to be free()ed. */ diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 5645342f5b28..43df78882e48 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -116,7 +116,8 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev); static void gfar_reset_task(struct work_struct *work); static void gfar_timeout(struct net_device *dev); static int gfar_close(struct net_device *dev); -struct sk_buff *gfar_new_skb(struct net_device *dev, dma_addr_t *bufaddr); +static struct sk_buff *gfar_new_skb(struct net_device *dev, + dma_addr_t *bufaddr); static int gfar_set_mac_address(struct net_device *dev); static int gfar_change_mtu(struct net_device *dev, int new_mtu); static irqreturn_t gfar_error(int irq, void *dev_id); @@ -176,7 +177,7 @@ static int gfar_init_bds(struct net_device *ndev) struct gfar_priv_rx_q *rx_queue = NULL; struct txbd8 *txbdp; struct rxbd8 *rxbdp; - u32 *rfbptr; + u32 __iomem *rfbptr; int i, j; dma_addr_t bufaddr; @@ -554,7 +555,7 @@ static void gfar_ints_enable(struct gfar_private *priv) } } -void lock_tx_qs(struct gfar_private *priv) +static void lock_tx_qs(struct gfar_private *priv) { int i; @@ -562,7 +563,7 @@ void lock_tx_qs(struct gfar_private *priv) spin_lock(&priv->tx_queue[i]->txlock); } -void unlock_tx_qs(struct gfar_private *priv) +static void unlock_tx_qs(struct gfar_private *priv) { int i; @@ -763,7 +764,7 @@ static int gfar_of_init(struct platform_device *ofdev, struct net_device **pdev) u32 *tx_queues, *rx_queues; unsigned short mode, poll_mode; - if (!np || !of_device_is_available(np)) + if (!np) return -ENODEV; if (of_device_is_compatible(np, "fsl,etsec2")) { @@ -2169,7 +2170,7 @@ static inline void gfar_tx_checksum(struct sk_buff *skb, struct txfcb *fcb, void inline gfar_tx_vlan(struct sk_buff *skb, struct txfcb *fcb) { fcb->flags |= TXFCB_VLN; - fcb->vlctl = vlan_tx_tag_get(skb); + fcb->vlctl = skb_vlan_tag_get(skb); } static inline struct txbd8 *skip_txbd(struct txbd8 *bdp, int stride, @@ -2229,7 +2230,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) regs = tx_queue->grp->regs; do_csum = (CHECKSUM_PARTIAL == skb->ip_summed); - do_vlan = vlan_tx_tag_present(skb); + do_vlan = skb_vlan_tag_present(skb); do_tstamp = (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && priv->hwts_tx_en; @@ -2671,7 +2672,7 @@ static struct sk_buff *gfar_alloc_skb(struct net_device *dev) return skb; } -struct sk_buff *gfar_new_skb(struct net_device *dev, dma_addr_t *bufaddr) +static struct sk_buff *gfar_new_skb(struct net_device *dev, dma_addr_t *bufaddr) { struct gfar_private *priv = netdev_priv(dev); struct sk_buff *skb; diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h index b581b8823a2a..9e1802400c23 100644 --- a/drivers/net/ethernet/freescale/gianfar.h +++ b/drivers/net/ethernet/freescale/gianfar.h @@ -1039,7 +1039,7 @@ struct gfar_priv_rx_q { /* RX Coalescing values */ unsigned char rxcoalescing; unsigned long rxic; - u32 *rfbptr; + u32 __iomem *rfbptr; }; enum gfar_irqinfo_id { diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c index 3e1a9c1a67a9..fda12fb32ec7 100644 --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c @@ -1586,7 +1586,7 @@ static int gfar_write_filer_table(struct gfar_private *priv, return -EBUSY; /* Fill regular entries */ - for (; i < MAX_FILER_IDX - 1 && (tab->fe[i].ctrl | tab->fe[i].ctrl); + for (; i < MAX_FILER_IDX - 1 && (tab->fe[i].ctrl | tab->fe[i].prop); i++) gfar_write_filer(priv, i, tab->fe[i].ctrl, tab->fe[i].prop); /* Fill the rest with fall-troughs */ diff --git a/drivers/net/ethernet/freescale/xgmac_mdio.c b/drivers/net/ethernet/freescale/xgmac_mdio.c index 6e7db66069aa..3a83bc2c613c 100644 --- a/drivers/net/ethernet/freescale/xgmac_mdio.c +++ b/drivers/net/ethernet/freescale/xgmac_mdio.c @@ -32,18 +32,19 @@ struct tgec_mdio_controller { __be32 mdio_addr; /* MDIO address */ } __packed; +#define MDIO_STAT_ENC BIT(6) #define MDIO_STAT_CLKDIV(x) (((x>>1) & 0xff) << 8) -#define MDIO_STAT_BSY (1 << 0) -#define MDIO_STAT_RD_ER (1 << 1) +#define MDIO_STAT_BSY BIT(0) +#define MDIO_STAT_RD_ER BIT(1) #define MDIO_CTL_DEV_ADDR(x) (x & 0x1f) #define MDIO_CTL_PORT_ADDR(x) ((x & 0x1f) << 5) -#define MDIO_CTL_PRE_DIS (1 << 10) -#define MDIO_CTL_SCAN_EN (1 << 11) -#define MDIO_CTL_POST_INC (1 << 14) -#define MDIO_CTL_READ (1 << 15) +#define MDIO_CTL_PRE_DIS BIT(10) +#define MDIO_CTL_SCAN_EN BIT(11) +#define MDIO_CTL_POST_INC BIT(14) +#define MDIO_CTL_READ BIT(15) #define MDIO_DATA(x) (x & 0xffff) -#define MDIO_DATA_BSY (1 << 31) +#define MDIO_DATA_BSY BIT(31) /* * Wait until the MDIO bus is free @@ -51,12 +52,16 @@ struct tgec_mdio_controller { static int xgmac_wait_until_free(struct device *dev, struct tgec_mdio_controller __iomem *regs) { - uint32_t status; + unsigned int timeout; /* Wait till the bus is free */ - status = spin_event_timeout( - !((in_be32(®s->mdio_stat)) & MDIO_STAT_BSY), TIMEOUT, 0); - if (!status) { + timeout = TIMEOUT; + while ((ioread32be(®s->mdio_stat) & MDIO_STAT_BSY) && timeout) { + cpu_relax(); + timeout--; + } + + if (!timeout) { dev_err(dev, "timeout waiting for bus to be free\n"); return -ETIMEDOUT; } @@ -70,12 +75,16 @@ static int xgmac_wait_until_free(struct device *dev, static int xgmac_wait_until_done(struct device *dev, struct tgec_mdio_controller __iomem *regs) { - uint32_t status; + unsigned int timeout; /* Wait till the MDIO write is complete */ - status = spin_event_timeout( - !((in_be32(®s->mdio_data)) & MDIO_DATA_BSY), TIMEOUT, 0); - if (!status) { + timeout = TIMEOUT; + while ((ioread32be(®s->mdio_data) & MDIO_DATA_BSY) && timeout) { + cpu_relax(); + timeout--; + } + + if (!timeout) { dev_err(dev, "timeout waiting for operation to complete\n"); return -ETIMEDOUT; } @@ -91,29 +100,42 @@ static int xgmac_wait_until_done(struct device *dev, static int xgmac_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value) { struct tgec_mdio_controller __iomem *regs = bus->priv; - uint16_t dev_addr = regnum >> 16; + uint16_t dev_addr; + u32 mdio_ctl, mdio_stat; int ret; - /* Setup the MII Mgmt clock speed */ - out_be32(®s->mdio_stat, MDIO_STAT_CLKDIV(100)); + mdio_stat = ioread32be(®s->mdio_stat); + if (regnum & MII_ADDR_C45) { + /* Clause 45 (ie 10G) */ + dev_addr = (regnum >> 16) & 0x1f; + mdio_stat |= MDIO_STAT_ENC; + } else { + /* Clause 22 (ie 1G) */ + dev_addr = regnum & 0x1f; + mdio_stat &= ~MDIO_STAT_ENC; + } + + iowrite32be(mdio_stat, ®s->mdio_stat); ret = xgmac_wait_until_free(&bus->dev, regs); if (ret) return ret; /* Set the port and dev addr */ - out_be32(®s->mdio_ctl, - MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr)); + mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr); + iowrite32be(mdio_ctl, ®s->mdio_ctl); /* Set the register address */ - out_be32(®s->mdio_addr, regnum & 0xffff); + if (regnum & MII_ADDR_C45) { + iowrite32be(regnum & 0xffff, ®s->mdio_addr); - ret = xgmac_wait_until_free(&bus->dev, regs); - if (ret) - return ret; + ret = xgmac_wait_until_free(&bus->dev, regs); + if (ret) + return ret; + } /* Write the value to the register */ - out_be32(®s->mdio_data, MDIO_DATA(value)); + iowrite32be(MDIO_DATA(value), ®s->mdio_data); ret = xgmac_wait_until_done(&bus->dev, regs); if (ret) @@ -130,13 +152,22 @@ static int xgmac_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 val static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum) { struct tgec_mdio_controller __iomem *regs = bus->priv; - uint16_t dev_addr = regnum >> 16; + uint16_t dev_addr; + uint32_t mdio_stat; uint32_t mdio_ctl; uint16_t value; int ret; - /* Setup the MII Mgmt clock speed */ - out_be32(®s->mdio_stat, MDIO_STAT_CLKDIV(100)); + mdio_stat = ioread32be(®s->mdio_stat); + if (regnum & MII_ADDR_C45) { + dev_addr = (regnum >> 16) & 0x1f; + mdio_stat |= MDIO_STAT_ENC; + } else { + dev_addr = regnum & 0x1f; + mdio_stat &= ~MDIO_STAT_ENC; + } + + iowrite32be(mdio_stat, ®s->mdio_stat); ret = xgmac_wait_until_free(&bus->dev, regs); if (ret) @@ -144,54 +175,38 @@ static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum) /* Set the Port and Device Addrs */ mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr); - out_be32(®s->mdio_ctl, mdio_ctl); + iowrite32be(mdio_ctl, ®s->mdio_ctl); /* Set the register address */ - out_be32(®s->mdio_addr, regnum & 0xffff); + if (regnum & MII_ADDR_C45) { + iowrite32be(regnum & 0xffff, ®s->mdio_addr); - ret = xgmac_wait_until_free(&bus->dev, regs); - if (ret) - return ret; + ret = xgmac_wait_until_free(&bus->dev, regs); + if (ret) + return ret; + } /* Initiate the read */ - out_be32(®s->mdio_ctl, mdio_ctl | MDIO_CTL_READ); + iowrite32be(mdio_ctl | MDIO_CTL_READ, ®s->mdio_ctl); ret = xgmac_wait_until_done(&bus->dev, regs); if (ret) return ret; /* Return all Fs if nothing was there */ - if (in_be32(®s->mdio_stat) & MDIO_STAT_RD_ER) { + if (ioread32be(®s->mdio_stat) & MDIO_STAT_RD_ER) { dev_err(&bus->dev, "Error while reading PHY%d reg at %d.%hhu\n", phy_id, dev_addr, regnum); return 0xffff; } - value = in_be32(®s->mdio_data) & 0xffff; + value = ioread32be(®s->mdio_data) & 0xffff; dev_dbg(&bus->dev, "read %04x\n", value); return value; } -/* Reset the MIIM registers, and wait for the bus to free */ -static int xgmac_mdio_reset(struct mii_bus *bus) -{ - struct tgec_mdio_controller __iomem *regs = bus->priv; - int ret; - - mutex_lock(&bus->mdio_lock); - - /* Setup the MII Mgmt clock speed */ - out_be32(®s->mdio_stat, MDIO_STAT_CLKDIV(100)); - - ret = xgmac_wait_until_free(&bus->dev, regs); - - mutex_unlock(&bus->mdio_lock); - - return ret; -} - static int xgmac_mdio_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -205,15 +220,13 @@ static int xgmac_mdio_probe(struct platform_device *pdev) return ret; } - bus = mdiobus_alloc_size(PHY_MAX_ADDR * sizeof(int)); + bus = mdiobus_alloc(); if (!bus) return -ENOMEM; bus->name = "Freescale XGMAC MDIO Bus"; bus->read = xgmac_mdio_read; bus->write = xgmac_mdio_write; - bus->reset = xgmac_mdio_reset; - bus->irq = bus->priv; bus->parent = &pdev->dev; snprintf(bus->id, MII_BUS_ID_SIZE, "%llx", (unsigned long long)res.start); @@ -258,6 +271,9 @@ static struct of_device_id xgmac_mdio_match[] = { { .compatible = "fsl,fman-xmdio", }, + { + .compatible = "fsl,fman-memac-mdio", + }, {}, }; MODULE_DEVICE_TABLE(of, xgmac_mdio_match); diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig index e9421731b05e..a54d89791311 100644 --- a/drivers/net/ethernet/hisilicon/Kconfig +++ b/drivers/net/ethernet/hisilicon/Kconfig @@ -24,4 +24,13 @@ config HIX5HD2_GMAC help This selects the hix5hd2 mac family network device. +config HIP04_ETH + tristate "HISILICON P04 Ethernet support" + select PHYLIB + select MARVELL_PHY + select MFD_SYSCON + ---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. + endif # NET_VENDOR_HISILICON diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile index 9175e84622d4..6c14540a4dc5 100644 --- a/drivers/net/ethernet/hisilicon/Makefile +++ b/drivers/net/ethernet/hisilicon/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_HIX5HD2_GMAC) += hix5hd2_gmac.o +obj-$(CONFIG_HIP04_ETH) += hip04_mdio.o hip04_eth.o diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c new file mode 100644 index 000000000000..b72d238695d7 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hip04_eth.c @@ -0,0 +1,971 @@ + +/* 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/etherdevice.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/ktime.h> +#include <linux/of_address.h> +#include <linux/phy.h> +#include <linux/of_mdio.h> +#include <linux/of_net.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> + +#define PPE_CFG_RX_ADDR 0x100 +#define PPE_CFG_POOL_GRP 0x300 +#define PPE_CFG_RX_BUF_SIZE 0x400 +#define PPE_CFG_RX_FIFO_SIZE 0x500 +#define PPE_CURR_BUF_CNT 0xa200 + +#define GE_DUPLEX_TYPE 0x08 +#define GE_MAX_FRM_SIZE_REG 0x3c +#define GE_PORT_MODE 0x40 +#define GE_PORT_EN 0x44 +#define GE_SHORT_RUNTS_THR_REG 0x50 +#define GE_TX_LOCAL_PAGE_REG 0x5c +#define GE_TRANSMIT_CONTROL_REG 0x60 +#define GE_CF_CRC_STRIP_REG 0x1b0 +#define GE_MODE_CHANGE_REG 0x1b4 +#define GE_RECV_CONTROL_REG 0x1e0 +#define GE_STATION_MAC_ADDRESS 0x210 +#define PPE_CFG_CPU_ADD_ADDR 0x580 +#define PPE_CFG_MAX_FRAME_LEN_REG 0x408 +#define PPE_CFG_BUS_CTRL_REG 0x424 +#define PPE_CFG_RX_CTRL_REG 0x428 +#define PPE_CFG_RX_PKT_MODE_REG 0x438 +#define PPE_CFG_QOS_VMID_GEN 0x500 +#define PPE_CFG_RX_PKT_INT 0x538 +#define PPE_INTEN 0x600 +#define PPE_INTSTS 0x608 +#define PPE_RINT 0x604 +#define PPE_CFG_STS_MODE 0x700 +#define PPE_HIS_RX_PKT_CNT 0x804 + +/* REG_INTERRUPT */ +#define RCV_INT BIT(10) +#define RCV_NOBUF BIT(8) +#define RCV_DROP BIT(7) +#define TX_DROP BIT(6) +#define DEF_INT_ERR (RCV_NOBUF | RCV_DROP | TX_DROP) +#define DEF_INT_MASK (RCV_INT | DEF_INT_ERR) + +/* TX descriptor config */ +#define TX_FREE_MEM BIT(0) +#define TX_READ_ALLOC_L3 BIT(1) +#define TX_FINISH_CACHE_INV BIT(2) +#define TX_CLEAR_WB BIT(4) +#define TX_L3_CHECKSUM BIT(5) +#define TX_LOOP_BACK BIT(11) + +/* RX error */ +#define RX_PKT_DROP BIT(0) +#define RX_L2_ERR BIT(1) +#define RX_PKT_ERR (RX_PKT_DROP | RX_L2_ERR) + +#define SGMII_SPEED_1000 0x08 +#define SGMII_SPEED_100 0x07 +#define SGMII_SPEED_10 0x06 +#define MII_SPEED_100 0x01 +#define MII_SPEED_10 0x00 + +#define GE_DUPLEX_FULL BIT(0) +#define GE_DUPLEX_HALF 0x00 +#define GE_MODE_CHANGE_EN BIT(0) + +#define GE_TX_AUTO_NEG BIT(5) +#define GE_TX_ADD_CRC BIT(6) +#define GE_TX_SHORT_PAD_THROUGH BIT(7) + +#define GE_RX_STRIP_CRC BIT(0) +#define GE_RX_STRIP_PAD BIT(3) +#define GE_RX_PAD_EN BIT(4) + +#define GE_AUTO_NEG_CTL BIT(0) + +#define GE_RX_INT_THRESHOLD BIT(6) +#define GE_RX_TIMEOUT 0x04 + +#define GE_RX_PORT_EN BIT(1) +#define GE_TX_PORT_EN BIT(2) + +#define PPE_CFG_STS_RX_PKT_CNT_RC BIT(12) + +#define PPE_CFG_RX_PKT_ALIGN BIT(18) +#define PPE_CFG_QOS_VMID_MODE BIT(14) +#define PPE_CFG_QOS_VMID_GRP_SHIFT 8 + +#define PPE_CFG_RX_FIFO_FSFU BIT(11) +#define PPE_CFG_RX_DEPTH_SHIFT 16 +#define PPE_CFG_RX_START_SHIFT 0 +#define PPE_CFG_RX_CTRL_ALIGN_SHIFT 11 + +#define PPE_CFG_BUS_LOCAL_REL BIT(14) +#define PPE_CFG_BUS_BIG_ENDIEN BIT(0) + +#define RX_DESC_NUM 128 +#define TX_DESC_NUM 256 +#define TX_NEXT(N) (((N) + 1) & (TX_DESC_NUM-1)) +#define RX_NEXT(N) (((N) + 1) & (RX_DESC_NUM-1)) + +#define GMAC_PPE_RX_PKT_MAX_LEN 379 +#define GMAC_MAX_PKT_LEN 1516 +#define GMAC_MIN_PKT_LEN 31 +#define RX_BUF_SIZE 1600 +#define RESET_TIMEOUT 1000 +#define TX_TIMEOUT (6 * HZ) + +#define DRV_NAME "hip04-ether" +#define DRV_VERSION "v1.0" + +#define HIP04_MAX_TX_COALESCE_USECS 200 +#define HIP04_MIN_TX_COALESCE_USECS 100 +#define HIP04_MAX_TX_COALESCE_FRAMES 200 +#define HIP04_MIN_TX_COALESCE_FRAMES 100 + +struct tx_desc { + u32 send_addr; + u32 send_size; + u32 next_addr; + u32 cfg; + u32 wb_addr; +} __aligned(64); + +struct rx_desc { + u16 reserved_16; + u16 pkt_len; + u32 reserve1[3]; + u32 pkt_err; + u32 reserve2[4]; +}; + +struct hip04_priv { + void __iomem *base; + int phy_mode; + int chan; + unsigned int port; + unsigned int speed; + unsigned int duplex; + unsigned int reg_inten; + + struct napi_struct napi; + struct net_device *ndev; + + struct tx_desc *tx_desc; + dma_addr_t tx_desc_dma; + struct sk_buff *tx_skb[TX_DESC_NUM]; + dma_addr_t tx_phys[TX_DESC_NUM]; + unsigned int tx_head; + + int tx_coalesce_frames; + int tx_coalesce_usecs; + struct hrtimer tx_coalesce_timer; + + unsigned char *rx_buf[RX_DESC_NUM]; + dma_addr_t rx_phys[RX_DESC_NUM]; + unsigned int rx_head; + unsigned int rx_buf_size; + + struct device_node *phy_node; + struct phy_device *phy; + struct regmap *map; + struct work_struct tx_timeout_task; + + /* written only by tx cleanup */ + unsigned int tx_tail ____cacheline_aligned_in_smp; +}; + +static inline unsigned int tx_count(unsigned int head, unsigned int tail) +{ + return (head - tail) % (TX_DESC_NUM - 1); +} + +static void hip04_config_port(struct net_device *ndev, u32 speed, u32 duplex) +{ + struct hip04_priv *priv = netdev_priv(ndev); + u32 val; + + priv->speed = speed; + priv->duplex = duplex; + + switch (priv->phy_mode) { + case PHY_INTERFACE_MODE_SGMII: + if (speed == SPEED_1000) + val = SGMII_SPEED_1000; + else if (speed == SPEED_100) + val = SGMII_SPEED_100; + else + val = SGMII_SPEED_10; + break; + case PHY_INTERFACE_MODE_MII: + if (speed == SPEED_100) + val = MII_SPEED_100; + else + val = MII_SPEED_10; + break; + default: + netdev_warn(ndev, "not supported mode\n"); + val = MII_SPEED_10; + break; + } + writel_relaxed(val, priv->base + GE_PORT_MODE); + + val = duplex ? GE_DUPLEX_FULL : GE_DUPLEX_HALF; + writel_relaxed(val, priv->base + GE_DUPLEX_TYPE); + + val = GE_MODE_CHANGE_EN; + writel_relaxed(val, priv->base + GE_MODE_CHANGE_REG); +} + +static void hip04_reset_ppe(struct hip04_priv *priv) +{ + u32 val, tmp, timeout = 0; + + do { + regmap_read(priv->map, priv->port * 4 + PPE_CURR_BUF_CNT, &val); + regmap_read(priv->map, priv->port * 4 + PPE_CFG_RX_ADDR, &tmp); + if (timeout++ > RESET_TIMEOUT) + break; + } while (val & 0xfff); +} + +static void hip04_config_fifo(struct hip04_priv *priv) +{ + u32 val; + + val = readl_relaxed(priv->base + PPE_CFG_STS_MODE); + val |= PPE_CFG_STS_RX_PKT_CNT_RC; + writel_relaxed(val, priv->base + PPE_CFG_STS_MODE); + + val = BIT(priv->port); + regmap_write(priv->map, priv->port * 4 + PPE_CFG_POOL_GRP, val); + + val = priv->port << PPE_CFG_QOS_VMID_GRP_SHIFT; + val |= PPE_CFG_QOS_VMID_MODE; + writel_relaxed(val, priv->base + PPE_CFG_QOS_VMID_GEN); + + val = RX_BUF_SIZE; + regmap_write(priv->map, priv->port * 4 + PPE_CFG_RX_BUF_SIZE, val); + + val = RX_DESC_NUM << PPE_CFG_RX_DEPTH_SHIFT; + val |= PPE_CFG_RX_FIFO_FSFU; + val |= priv->chan << PPE_CFG_RX_START_SHIFT; + regmap_write(priv->map, priv->port * 4 + PPE_CFG_RX_FIFO_SIZE, val); + + val = NET_IP_ALIGN << PPE_CFG_RX_CTRL_ALIGN_SHIFT; + writel_relaxed(val, priv->base + PPE_CFG_RX_CTRL_REG); + + val = PPE_CFG_RX_PKT_ALIGN; + writel_relaxed(val, priv->base + PPE_CFG_RX_PKT_MODE_REG); + + val = PPE_CFG_BUS_LOCAL_REL | PPE_CFG_BUS_BIG_ENDIEN; + writel_relaxed(val, priv->base + PPE_CFG_BUS_CTRL_REG); + + val = GMAC_PPE_RX_PKT_MAX_LEN; + writel_relaxed(val, priv->base + PPE_CFG_MAX_FRAME_LEN_REG); + + val = GMAC_MAX_PKT_LEN; + writel_relaxed(val, priv->base + GE_MAX_FRM_SIZE_REG); + + val = GMAC_MIN_PKT_LEN; + writel_relaxed(val, priv->base + GE_SHORT_RUNTS_THR_REG); + + val = readl_relaxed(priv->base + GE_TRANSMIT_CONTROL_REG); + val |= GE_TX_AUTO_NEG | GE_TX_ADD_CRC | GE_TX_SHORT_PAD_THROUGH; + writel_relaxed(val, priv->base + GE_TRANSMIT_CONTROL_REG); + + val = GE_RX_STRIP_CRC; + writel_relaxed(val, priv->base + GE_CF_CRC_STRIP_REG); + + val = readl_relaxed(priv->base + GE_RECV_CONTROL_REG); + val |= GE_RX_STRIP_PAD | GE_RX_PAD_EN; + writel_relaxed(val, priv->base + GE_RECV_CONTROL_REG); + + val = GE_AUTO_NEG_CTL; + writel_relaxed(val, priv->base + GE_TX_LOCAL_PAGE_REG); +} + +static void hip04_mac_enable(struct net_device *ndev) +{ + struct hip04_priv *priv = netdev_priv(ndev); + u32 val; + + /* enable tx & rx */ + val = readl_relaxed(priv->base + GE_PORT_EN); + val |= GE_RX_PORT_EN | GE_TX_PORT_EN; + writel_relaxed(val, priv->base + GE_PORT_EN); + + /* clear rx int */ + val = RCV_INT; + writel_relaxed(val, priv->base + PPE_RINT); + + /* config recv int */ + val = GE_RX_INT_THRESHOLD | GE_RX_TIMEOUT; + writel_relaxed(val, priv->base + PPE_CFG_RX_PKT_INT); + + /* enable interrupt */ + priv->reg_inten = DEF_INT_MASK; + writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN); +} + +static void hip04_mac_disable(struct net_device *ndev) +{ + struct hip04_priv *priv = netdev_priv(ndev); + u32 val; + + /* disable int */ + priv->reg_inten &= ~(DEF_INT_MASK); + writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN); + + /* disable tx & rx */ + val = readl_relaxed(priv->base + GE_PORT_EN); + val &= ~(GE_RX_PORT_EN | GE_TX_PORT_EN); + writel_relaxed(val, priv->base + GE_PORT_EN); +} + +static void hip04_set_xmit_desc(struct hip04_priv *priv, dma_addr_t phys) +{ + writel(phys, priv->base + PPE_CFG_CPU_ADD_ADDR); +} + +static void hip04_set_recv_desc(struct hip04_priv *priv, dma_addr_t phys) +{ + regmap_write(priv->map, priv->port * 4 + PPE_CFG_RX_ADDR, phys); +} + +static u32 hip04_recv_cnt(struct hip04_priv *priv) +{ + return readl(priv->base + PPE_HIS_RX_PKT_CNT); +} + +static void hip04_update_mac_address(struct net_device *ndev) +{ + struct hip04_priv *priv = netdev_priv(ndev); + + writel_relaxed(((ndev->dev_addr[0] << 8) | (ndev->dev_addr[1])), + priv->base + GE_STATION_MAC_ADDRESS); + writel_relaxed(((ndev->dev_addr[2] << 24) | (ndev->dev_addr[3] << 16) | + (ndev->dev_addr[4] << 8) | (ndev->dev_addr[5])), + priv->base + GE_STATION_MAC_ADDRESS + 4); +} + +static int hip04_set_mac_address(struct net_device *ndev, void *addr) +{ + eth_mac_addr(ndev, addr); + hip04_update_mac_address(ndev); + return 0; +} + +static int hip04_tx_reclaim(struct net_device *ndev, bool force) +{ + struct hip04_priv *priv = netdev_priv(ndev); + unsigned tx_tail = priv->tx_tail; + struct tx_desc *desc; + unsigned int bytes_compl = 0, pkts_compl = 0; + unsigned int count; + + smp_rmb(); + count = tx_count(ACCESS_ONCE(priv->tx_head), tx_tail); + if (count == 0) + goto out; + + while (count) { + desc = &priv->tx_desc[tx_tail]; + if (desc->send_addr != 0) { + if (force) + desc->send_addr = 0; + else + break; + } + + if (priv->tx_phys[tx_tail]) { + dma_unmap_single(&ndev->dev, priv->tx_phys[tx_tail], + priv->tx_skb[tx_tail]->len, + DMA_TO_DEVICE); + priv->tx_phys[tx_tail] = 0; + } + pkts_compl++; + bytes_compl += priv->tx_skb[tx_tail]->len; + dev_kfree_skb(priv->tx_skb[tx_tail]); + priv->tx_skb[tx_tail] = NULL; + tx_tail = TX_NEXT(tx_tail); + count--; + } + + priv->tx_tail = tx_tail; + smp_wmb(); /* Ensure tx_tail visible to xmit */ + +out: + if (pkts_compl || bytes_compl) + netdev_completed_queue(ndev, pkts_compl, bytes_compl); + + if (unlikely(netif_queue_stopped(ndev)) && (count < (TX_DESC_NUM - 1))) + netif_wake_queue(ndev); + + return count; +} + +static int hip04_mac_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct hip04_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + unsigned int tx_head = priv->tx_head, count; + struct tx_desc *desc = &priv->tx_desc[tx_head]; + dma_addr_t phys; + + smp_rmb(); + count = tx_count(tx_head, ACCESS_ONCE(priv->tx_tail)); + if (count == (TX_DESC_NUM - 1)) { + netif_stop_queue(ndev); + return NETDEV_TX_BUSY; + } + + phys = dma_map_single(&ndev->dev, skb->data, skb->len, DMA_TO_DEVICE); + if (dma_mapping_error(&ndev->dev, phys)) { + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + priv->tx_skb[tx_head] = skb; + priv->tx_phys[tx_head] = phys; + desc->send_addr = cpu_to_be32(phys); + desc->send_size = cpu_to_be32(skb->len); + desc->cfg = cpu_to_be32(TX_CLEAR_WB | TX_FINISH_CACHE_INV); + phys = priv->tx_desc_dma + tx_head * sizeof(struct tx_desc); + desc->wb_addr = cpu_to_be32(phys); + skb_tx_timestamp(skb); + + hip04_set_xmit_desc(priv, phys); + priv->tx_head = TX_NEXT(tx_head); + count++; + netdev_sent_queue(ndev, skb->len); + + stats->tx_bytes += skb->len; + stats->tx_packets++; + + /* Ensure tx_head update visible to tx reclaim */ + smp_wmb(); + + /* queue is getting full, better start cleaning up now */ + if (count >= priv->tx_coalesce_frames) { + if (napi_schedule_prep(&priv->napi)) { + /* disable rx interrupt and timer */ + priv->reg_inten &= ~(RCV_INT); + writel_relaxed(DEF_INT_MASK & ~RCV_INT, + priv->base + PPE_INTEN); + hrtimer_cancel(&priv->tx_coalesce_timer); + __napi_schedule(&priv->napi); + } + } else if (!hrtimer_is_queued(&priv->tx_coalesce_timer)) { + /* cleanup not pending yet, start a new timer */ + hrtimer_start_expires(&priv->tx_coalesce_timer, + HRTIMER_MODE_REL); + } + + return NETDEV_TX_OK; +} + +static int hip04_rx_poll(struct napi_struct *napi, int budget) +{ + struct hip04_priv *priv = container_of(napi, struct hip04_priv, napi); + struct net_device *ndev = priv->ndev; + struct net_device_stats *stats = &ndev->stats; + unsigned int cnt = hip04_recv_cnt(priv); + struct rx_desc *desc; + struct sk_buff *skb; + unsigned char *buf; + bool last = false; + dma_addr_t phys; + int rx = 0; + int tx_remaining; + u16 len; + u32 err; + + while (cnt && !last) { + buf = priv->rx_buf[priv->rx_head]; + skb = build_skb(buf, priv->rx_buf_size); + if (unlikely(!skb)) + net_dbg_ratelimited("build_skb failed\n"); + + dma_unmap_single(&ndev->dev, priv->rx_phys[priv->rx_head], + RX_BUF_SIZE, DMA_FROM_DEVICE); + priv->rx_phys[priv->rx_head] = 0; + + desc = (struct rx_desc *)skb->data; + len = be16_to_cpu(desc->pkt_len); + err = be32_to_cpu(desc->pkt_err); + + if (0 == len) { + dev_kfree_skb_any(skb); + last = true; + } else if ((err & RX_PKT_ERR) || (len >= GMAC_MAX_PKT_LEN)) { + dev_kfree_skb_any(skb); + stats->rx_dropped++; + stats->rx_errors++; + } else { + skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN); + skb_put(skb, len); + skb->protocol = eth_type_trans(skb, ndev); + napi_gro_receive(&priv->napi, skb); + stats->rx_packets++; + stats->rx_bytes += len; + rx++; + } + + buf = netdev_alloc_frag(priv->rx_buf_size); + if (!buf) + goto done; + phys = dma_map_single(&ndev->dev, buf, + RX_BUF_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(&ndev->dev, phys)) + goto done; + priv->rx_buf[priv->rx_head] = buf; + priv->rx_phys[priv->rx_head] = phys; + hip04_set_recv_desc(priv, phys); + + priv->rx_head = RX_NEXT(priv->rx_head); + if (rx >= budget) + goto done; + + if (--cnt == 0) + cnt = hip04_recv_cnt(priv); + } + + if (!(priv->reg_inten & RCV_INT)) { + /* enable rx interrupt */ + priv->reg_inten |= RCV_INT; + writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN); + } + napi_complete(napi); +done: + /* clean up tx descriptors and start a new timer if necessary */ + tx_remaining = hip04_tx_reclaim(ndev, false); + if (rx < budget && tx_remaining) + hrtimer_start_expires(&priv->tx_coalesce_timer, HRTIMER_MODE_REL); + + return rx; +} + +static irqreturn_t hip04_mac_interrupt(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + struct hip04_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + u32 ists = readl_relaxed(priv->base + PPE_INTSTS); + + if (!ists) + return IRQ_NONE; + + writel_relaxed(DEF_INT_MASK, priv->base + PPE_RINT); + + if (unlikely(ists & DEF_INT_ERR)) { + if (ists & (RCV_NOBUF | RCV_DROP)) { + stats->rx_errors++; + stats->rx_dropped++; + netdev_err(ndev, "rx drop\n"); + } + if (ists & TX_DROP) { + stats->tx_dropped++; + netdev_err(ndev, "tx drop\n"); + } + } + + if (ists & RCV_INT && napi_schedule_prep(&priv->napi)) { + /* disable rx interrupt */ + priv->reg_inten &= ~(RCV_INT); + writel_relaxed(DEF_INT_MASK & ~RCV_INT, priv->base + PPE_INTEN); + hrtimer_cancel(&priv->tx_coalesce_timer); + __napi_schedule(&priv->napi); + } + + return IRQ_HANDLED; +} + +enum hrtimer_restart tx_done(struct hrtimer *hrtimer) +{ + struct hip04_priv *priv; + + priv = container_of(hrtimer, struct hip04_priv, tx_coalesce_timer); + + if (napi_schedule_prep(&priv->napi)) { + /* disable rx interrupt */ + priv->reg_inten &= ~(RCV_INT); + writel_relaxed(DEF_INT_MASK & ~RCV_INT, priv->base + PPE_INTEN); + __napi_schedule(&priv->napi); + } + + return HRTIMER_NORESTART; +} + +static void hip04_adjust_link(struct net_device *ndev) +{ + struct hip04_priv *priv = netdev_priv(ndev); + struct phy_device *phy = priv->phy; + + if ((priv->speed != phy->speed) || (priv->duplex != phy->duplex)) { + hip04_config_port(ndev, phy->speed, phy->duplex); + phy_print_status(phy); + } +} + +static int hip04_mac_open(struct net_device *ndev) +{ + struct hip04_priv *priv = netdev_priv(ndev); + int i; + + priv->rx_head = 0; + priv->tx_head = 0; + priv->tx_tail = 0; + hip04_reset_ppe(priv); + + for (i = 0; i < RX_DESC_NUM; i++) { + dma_addr_t phys; + + phys = dma_map_single(&ndev->dev, priv->rx_buf[i], + RX_BUF_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(&ndev->dev, phys)) + return -EIO; + + priv->rx_phys[i] = phys; + hip04_set_recv_desc(priv, phys); + } + + if (priv->phy) + phy_start(priv->phy); + + netdev_reset_queue(ndev); + netif_start_queue(ndev); + hip04_mac_enable(ndev); + napi_enable(&priv->napi); + + return 0; +} + +static int hip04_mac_stop(struct net_device *ndev) +{ + struct hip04_priv *priv = netdev_priv(ndev); + int i; + + napi_disable(&priv->napi); + netif_stop_queue(ndev); + hip04_mac_disable(ndev); + hip04_tx_reclaim(ndev, true); + hip04_reset_ppe(priv); + + if (priv->phy) + phy_stop(priv->phy); + + for (i = 0; i < RX_DESC_NUM; i++) { + if (priv->rx_phys[i]) { + dma_unmap_single(&ndev->dev, priv->rx_phys[i], + RX_BUF_SIZE, DMA_FROM_DEVICE); + priv->rx_phys[i] = 0; + } + } + + return 0; +} + +static void hip04_timeout(struct net_device *ndev) +{ + struct hip04_priv *priv = netdev_priv(ndev); + + schedule_work(&priv->tx_timeout_task); +} + +static void hip04_tx_timeout_task(struct work_struct *work) +{ + struct hip04_priv *priv; + + priv = container_of(work, struct hip04_priv, tx_timeout_task); + hip04_mac_stop(priv->ndev); + hip04_mac_open(priv->ndev); +} + +static struct net_device_stats *hip04_get_stats(struct net_device *ndev) +{ + return &ndev->stats; +} + +static int hip04_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec) +{ + struct hip04_priv *priv = netdev_priv(netdev); + + ec->tx_coalesce_usecs = priv->tx_coalesce_usecs; + ec->tx_max_coalesced_frames = priv->tx_coalesce_frames; + + return 0; +} + +static int hip04_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec) +{ + struct hip04_priv *priv = netdev_priv(netdev); + + /* Check not supported parameters */ + if ((ec->rx_max_coalesced_frames) || (ec->rx_coalesce_usecs_irq) || + (ec->rx_max_coalesced_frames_irq) || (ec->tx_coalesce_usecs_irq) || + (ec->use_adaptive_rx_coalesce) || (ec->use_adaptive_tx_coalesce) || + (ec->pkt_rate_low) || (ec->rx_coalesce_usecs_low) || + (ec->rx_max_coalesced_frames_low) || (ec->tx_coalesce_usecs_high) || + (ec->tx_max_coalesced_frames_low) || (ec->pkt_rate_high) || + (ec->tx_coalesce_usecs_low) || (ec->rx_coalesce_usecs_high) || + (ec->rx_max_coalesced_frames_high) || (ec->rx_coalesce_usecs) || + (ec->tx_max_coalesced_frames_irq) || + (ec->stats_block_coalesce_usecs) || + (ec->tx_max_coalesced_frames_high) || (ec->rate_sample_interval)) + return -EOPNOTSUPP; + + if ((ec->tx_coalesce_usecs > HIP04_MAX_TX_COALESCE_USECS || + ec->tx_coalesce_usecs < HIP04_MIN_TX_COALESCE_USECS) || + (ec->tx_max_coalesced_frames > HIP04_MAX_TX_COALESCE_FRAMES || + ec->tx_max_coalesced_frames < HIP04_MIN_TX_COALESCE_FRAMES)) + return -EINVAL; + + priv->tx_coalesce_usecs = ec->tx_coalesce_usecs; + priv->tx_coalesce_frames = ec->tx_max_coalesced_frames; + + return 0; +} + +static void hip04_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); + strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version)); +} + +static struct ethtool_ops hip04_ethtool_ops = { + .get_coalesce = hip04_get_coalesce, + .set_coalesce = hip04_set_coalesce, + .get_drvinfo = hip04_get_drvinfo, +}; + +static struct net_device_ops hip04_netdev_ops = { + .ndo_open = hip04_mac_open, + .ndo_stop = hip04_mac_stop, + .ndo_get_stats = hip04_get_stats, + .ndo_start_xmit = hip04_mac_start_xmit, + .ndo_set_mac_address = hip04_set_mac_address, + .ndo_tx_timeout = hip04_timeout, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = eth_change_mtu, +}; + +static int hip04_alloc_ring(struct net_device *ndev, struct device *d) +{ + struct hip04_priv *priv = netdev_priv(ndev); + int i; + + priv->tx_desc = dma_alloc_coherent(d, + TX_DESC_NUM * sizeof(struct tx_desc), + &priv->tx_desc_dma, GFP_KERNEL); + if (!priv->tx_desc) + return -ENOMEM; + + priv->rx_buf_size = RX_BUF_SIZE + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + for (i = 0; i < RX_DESC_NUM; i++) { + priv->rx_buf[i] = netdev_alloc_frag(priv->rx_buf_size); + if (!priv->rx_buf[i]) + return -ENOMEM; + } + + return 0; +} + +static void hip04_free_ring(struct net_device *ndev, struct device *d) +{ + struct hip04_priv *priv = netdev_priv(ndev); + int i; + + for (i = 0; i < RX_DESC_NUM; i++) + if (priv->rx_buf[i]) + put_page(virt_to_head_page(priv->rx_buf[i])); + + for (i = 0; i < TX_DESC_NUM; i++) + if (priv->tx_skb[i]) + dev_kfree_skb_any(priv->tx_skb[i]); + + dma_free_coherent(d, TX_DESC_NUM * sizeof(struct tx_desc), + priv->tx_desc, priv->tx_desc_dma); +} + +static int hip04_mac_probe(struct platform_device *pdev) +{ + struct device *d = &pdev->dev; + struct device_node *node = d->of_node; + struct of_phandle_args arg; + struct net_device *ndev; + struct hip04_priv *priv; + struct resource *res; + unsigned int irq; + ktime_t txtime; + int ret; + + ndev = alloc_etherdev(sizeof(struct hip04_priv)); + if (!ndev) + return -ENOMEM; + + priv = netdev_priv(ndev); + priv->ndev = ndev; + platform_set_drvdata(pdev, ndev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->base = devm_ioremap_resource(d, res); + if (IS_ERR(priv->base)) { + ret = PTR_ERR(priv->base); + goto init_fail; + } + + ret = of_parse_phandle_with_fixed_args(node, "port-handle", 2, 0, &arg); + if (ret < 0) { + dev_warn(d, "no port-handle\n"); + goto init_fail; + } + + priv->port = arg.args[0]; + priv->chan = arg.args[1] * RX_DESC_NUM; + + hrtimer_init(&priv->tx_coalesce_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + + /* BQL will try to keep the TX queue as short as possible, but it can't + * be faster than tx_coalesce_usecs, so we need a fast timeout here, + * but also long enough to gather up enough frames to ensure we don't + * get more interrupts than necessary. + * 200us is enough for 16 frames of 1500 bytes at gigabit ethernet rate + */ + priv->tx_coalesce_frames = TX_DESC_NUM * 3 / 4; + priv->tx_coalesce_usecs = 200; + /* allow timer to fire after half the time at the earliest */ + txtime = ktime_set(0, priv->tx_coalesce_usecs * NSEC_PER_USEC / 2); + hrtimer_set_expires_range(&priv->tx_coalesce_timer, txtime, txtime); + priv->tx_coalesce_timer.function = tx_done; + + priv->map = syscon_node_to_regmap(arg.np); + if (IS_ERR(priv->map)) { + dev_warn(d, "no syscon hisilicon,hip04-ppe\n"); + ret = PTR_ERR(priv->map); + goto init_fail; + } + + priv->phy_mode = of_get_phy_mode(node); + if (priv->phy_mode < 0) { + dev_warn(d, "not find phy-mode\n"); + ret = -EINVAL; + goto init_fail; + } + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + ret = -EINVAL; + goto init_fail; + } + + ret = devm_request_irq(d, irq, hip04_mac_interrupt, + 0, pdev->name, ndev); + if (ret) { + netdev_err(ndev, "devm_request_irq failed\n"); + goto init_fail; + } + + priv->phy_node = of_parse_phandle(node, "phy-handle", 0); + if (priv->phy_node) { + priv->phy = of_phy_connect(ndev, priv->phy_node, + &hip04_adjust_link, + 0, priv->phy_mode); + if (!priv->phy) { + ret = -EPROBE_DEFER; + goto init_fail; + } + } + + INIT_WORK(&priv->tx_timeout_task, hip04_tx_timeout_task); + + ether_setup(ndev); + ndev->netdev_ops = &hip04_netdev_ops; + ndev->ethtool_ops = &hip04_ethtool_ops; + ndev->watchdog_timeo = TX_TIMEOUT; + ndev->priv_flags |= IFF_UNICAST_FLT; + ndev->irq = irq; + netif_napi_add(ndev, &priv->napi, hip04_rx_poll, NAPI_POLL_WEIGHT); + SET_NETDEV_DEV(ndev, &pdev->dev); + + hip04_reset_ppe(priv); + if (priv->phy_mode == PHY_INTERFACE_MODE_MII) + hip04_config_port(ndev, SPEED_100, DUPLEX_FULL); + + hip04_config_fifo(priv); + random_ether_addr(ndev->dev_addr); + hip04_update_mac_address(ndev); + + ret = hip04_alloc_ring(ndev, d); + if (ret) { + netdev_err(ndev, "alloc ring fail\n"); + goto alloc_fail; + } + + ret = register_netdev(ndev); + if (ret) { + free_netdev(ndev); + goto alloc_fail; + } + + return 0; + +alloc_fail: + hip04_free_ring(ndev, d); +init_fail: + of_node_put(priv->phy_node); + free_netdev(ndev); + return ret; +} + +static int hip04_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct hip04_priv *priv = netdev_priv(ndev); + struct device *d = &pdev->dev; + + if (priv->phy) + phy_disconnect(priv->phy); + + hip04_free_ring(ndev, d); + unregister_netdev(ndev); + free_irq(ndev->irq, ndev); + of_node_put(priv->phy_node); + cancel_work_sync(&priv->tx_timeout_task); + free_netdev(ndev); + + return 0; +} + +static const struct of_device_id hip04_mac_match[] = { + { .compatible = "hisilicon,hip04-mac" }, + { } +}; + +MODULE_DEVICE_TABLE(of, hip04_mac_match); + +static struct platform_driver hip04_mac_driver = { + .probe = hip04_mac_probe, + .remove = hip04_remove, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = hip04_mac_match, + }, +}; +module_platform_driver(hip04_mac_driver); + +MODULE_DESCRIPTION("HISILICON P04 Ethernet driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/hisilicon/hip04_mdio.c b/drivers/net/ethernet/hisilicon/hip04_mdio.c new file mode 100644 index 000000000000..b3bac25db99c --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hip04_mdio.c @@ -0,0 +1,186 @@ +/* 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", + .owner = THIS_MODULE, + .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/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c index 566b17db135a..e8a1adb7a962 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_main.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c @@ -2064,9 +2064,9 @@ static int ehea_start_xmit(struct sk_buff *skb, struct net_device *dev) memset(swqe, 0, SWQE_HEADER_SIZE); atomic_dec(&pr->swqe_avail); - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { swqe->tx_control |= EHEA_SWQE_VLAN_INSERT; - swqe->vlan_tag = vlan_tx_tag_get(skb); + swqe->vlan_tag = skb_vlan_tag_get(skb); } pr->tx_packets++; diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index 9388a83818f2..162762d1a12c 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -2367,7 +2367,7 @@ static int emac_wait_deps(struct emac_instance *dev) err = emac_check_deps(dev, deps) ? 0 : -ENODEV; for (i = 0; i < EMAC_DEP_COUNT; i++) { of_node_put(deps[i].node); - if (err && deps[i].ofdev) + if (err) of_dev_put(deps[i].ofdev); } if (err == 0) { diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig index 5b8300a32bf5..f4ff465584a0 100644 --- a/drivers/net/ethernet/intel/Kconfig +++ b/drivers/net/ethernet/intel/Kconfig @@ -192,6 +192,17 @@ config IXGBE To compile this driver as a module, choose M here. The module will be called ixgbe. +config IXGBE_VXLAN + bool "Virtual eXtensible Local Area Network Support" + default n + depends on IXGBE && VXLAN && !(IXGBE=y && VXLAN=m) + ---help--- + This allows one to create VXLAN virtual interfaces that provide + Layer 2 Networks over Layer 3 Networks. VXLAN is often used + to tunnel virtual network infrastructure in virtualized environments. + Say Y here if you want to use Virtual eXtensible Local Area Network + (VXLAN) in the driver. + config IXGBE_HWMON bool "Intel(R) 10GbE PCI Express adapters HWMON support" default y @@ -281,6 +292,17 @@ config I40E_DCB If unsure, say N. +config I40E_FCOE + bool "Fibre Channel over Ethernet (FCoE)" + default n + depends on I40E && DCB && FCOE + ---help--- + Say Y here if you want to use Fibre Channel over Ethernet (FCoE) + in the driver. This will create new netdev for exclusive FCoE + use with XL710 FCoE offloads enabled. + + If unsure, say N. + config I40EVF tristate "Intel(R) XL710 X710 Virtual Function Ethernet support" depends on PCI_MSI diff --git a/drivers/net/ethernet/intel/e100.c b/drivers/net/ethernet/intel/e100.c index 781065eb5431..e9c3a87e5b11 100644 --- a/drivers/net/ethernet/intel/e100.c +++ b/drivers/net/ethernet/intel/e100.c @@ -1543,7 +1543,7 @@ static int e100_phy_init(struct nic *nic) mdio_write(netdev, nic->mii.phy_id, MII_BMCR, bmcr); } else if ((nic->mac >= mac_82550_D102) || ((nic->flags & ich) && (mdio_read(netdev, nic->mii.phy_id, MII_TPISTATUS) & 0x8000) && - !(nic->eeprom[eeprom_cnfg_mdix] & eeprom_mdix_enabled))) { + (nic->eeprom[eeprom_cnfg_mdix] & eeprom_mdix_enabled))) { /* enable/disable MDI/MDI-X auto-switching. */ mdio_write(netdev, nic->mii.phy_id, MII_NCONFIG, nic->mii.force_media ? 0 : NCONFIG_AUTO_SWITCH); diff --git a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c index b691eb4f6376..4270ad2d4ddf 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c +++ b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c @@ -24,6 +24,7 @@ /* ethtool support for e1000 */ #include "e1000.h" +#include <linux/jiffies.h> #include <linux/uaccess.h> enum {NETDEV_STATS, E1000_STATS}; @@ -1460,7 +1461,7 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter) ret_val = 13; /* ret_val is the same as mis-compare */ break; } - if (jiffies >= (time + 2)) { + if (time_after_eq(jiffies, time + 2)) { ret_val = 14; /* error code for time out error */ break; } diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index 83140cbb5f01..7f997d36948f 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -2977,7 +2977,6 @@ static void e1000_tx_queue(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring, int tx_flags, int count) { - struct e1000_hw *hw = &adapter->hw; struct e1000_tx_desc *tx_desc = NULL; struct e1000_tx_buffer *buffer_info; u32 txd_upper = 0, txd_lower = E1000_TXD_CMD_IFCS; @@ -3031,11 +3030,6 @@ static void e1000_tx_queue(struct e1000_adapter *adapter, wmb(); tx_ring->next_to_use = i; - writel(i, hw->hw_addr + tx_ring->tdt); - /* we need this if more than one processor can write to our tail - * at a time, it synchronizes IO on IA64/Altix systems - */ - mmiowb(); } /* 82547 workaround to avoid controller hang in half-duplex environment. @@ -3226,9 +3220,10 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb, return NETDEV_TX_BUSY; } - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { tx_flags |= E1000_TX_FLAGS_VLAN; - tx_flags |= (vlan_tx_tag_get(skb) << E1000_TX_FLAGS_VLAN_SHIFT); + tx_flags |= (skb_vlan_tag_get(skb) << + E1000_TX_FLAGS_VLAN_SHIFT); } first = tx_ring->next_to_use; @@ -3263,6 +3258,15 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb, /* Make sure there is space in the ring for the next send. */ e1000_maybe_stop_tx(netdev, tx_ring, MAX_SKB_FRAGS + 2); + if (!skb->xmit_more || + netif_xmit_stopped(netdev_get_tx_queue(netdev, 0))) { + writel(tx_ring->next_to_use, hw->hw_addr + tx_ring->tdt); + /* we need this if more than one processor can write to + * our tail at a time, it synchronizes IO on IA64/Altix + * systems + */ + mmiowb(); + } } else { dev_kfree_skb_any(skb); tx_ring->buffer_info[first].time_stamp = 0; diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h index 7785240a0da1..9416e5a7e0c8 100644 --- a/drivers/net/ethernet/intel/e1000e/e1000.h +++ b/drivers/net/ethernet/intel/e1000e/e1000.h @@ -34,7 +34,7 @@ #include <linux/pci-aspm.h> #include <linux/crc32.h> #include <linux/if_vlan.h> -#include <linux/clocksource.h> +#include <linux/timecounter.h> #include <linux/net_tstamp.h> #include <linux/ptp_clock_kernel.h> #include <linux/ptp_classify.h> diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index e14fd85f64eb..1e8c40fd5c3d 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -4189,7 +4189,7 @@ static int e1000_sw_init(struct e1000_adapter *adapter) /* Setup hardware time stamping cyclecounter */ if (adapter->flags & FLAG_HAS_HW_TIMESTAMP) { adapter->cc.read = e1000e_cyclecounter_read; - adapter->cc.mask = CLOCKSOURCE_MASK(64); + adapter->cc.mask = CYCLECOUNTER_MASK(64); adapter->cc.mult = 1; /* cc.shift set in e1000e_get_base_tininca() */ @@ -5444,16 +5444,6 @@ static void e1000_tx_queue(struct e1000_ring *tx_ring, int tx_flags, int count) wmb(); tx_ring->next_to_use = i; - - if (adapter->flags2 & FLAG2_PCIM2PCI_ARBITER_WA) - e1000e_update_tdt_wa(tx_ring, i); - else - writel(i, tx_ring->tail); - - /* we need this if more than one processor can write to our tail - * at a time, it synchronizes IO on IA64/Altix systems - */ - mmiowb(); } #define MINIMUM_DHCP_PACKET_SIZE 282 @@ -5463,8 +5453,8 @@ static int e1000_transfer_dhcp_info(struct e1000_adapter *adapter, struct e1000_hw *hw = &adapter->hw; u16 length, offset; - if (vlan_tx_tag_present(skb) && - !((vlan_tx_tag_get(skb) == adapter->hw.mng_cookie.vlan_id) && + if (skb_vlan_tag_present(skb) && + !((skb_vlan_tag_get(skb) == adapter->hw.mng_cookie.vlan_id) && (adapter->hw.mng_cookie.status & E1000_MNG_DHCP_COOKIE_STATUS_VLAN))) return 0; @@ -5603,9 +5593,10 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb, if (e1000_maybe_stop_tx(tx_ring, count + 2)) return NETDEV_TX_BUSY; - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { tx_flags |= E1000_TX_FLAGS_VLAN; - tx_flags |= (vlan_tx_tag_get(skb) << E1000_TX_FLAGS_VLAN_SHIFT); + tx_flags |= (skb_vlan_tag_get(skb) << + E1000_TX_FLAGS_VLAN_SHIFT); } first = tx_ring->next_to_use; @@ -5635,8 +5626,9 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb, count = e1000_tx_map(tx_ring, skb, first, adapter->tx_fifo_limit, nr_frags); if (count) { - if (unlikely((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && - !adapter->tx_hwtstamp_skb)) { + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && + (adapter->flags & FLAG_HAS_HW_TIMESTAMP) && + !adapter->tx_hwtstamp_skb) { skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; tx_flags |= E1000_TX_FLAGS_HWTSTAMP; adapter->tx_hwtstamp_skb = skb_get(skb); @@ -5653,6 +5645,21 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb, (MAX_SKB_FRAGS * DIV_ROUND_UP(PAGE_SIZE, adapter->tx_fifo_limit) + 2)); + + if (!skb->xmit_more || + netif_xmit_stopped(netdev_get_tx_queue(netdev, 0))) { + if (adapter->flags2 & FLAG2_PCIM2PCI_ARBITER_WA) + e1000e_update_tdt_wa(tx_ring, + tx_ring->next_to_use); + else + writel(tx_ring->next_to_use, tx_ring->tail); + + /* we need this if more than one processor can write + * to our tail at a time, it synchronizes IO on + *IA64/Altix systems + */ + mmiowb(); + } } else { dev_kfree_skb_any(skb); tx_ring->buffer_info[first].time_stamp = 0; diff --git a/drivers/net/ethernet/intel/e1000e/ptp.c b/drivers/net/ethernet/intel/e1000e/ptp.c index fb1a914a3ad4..978ef9c4a043 100644 --- a/drivers/net/ethernet/intel/e1000e/ptp.c +++ b/drivers/net/ethernet/intel/e1000e/ptp.c @@ -90,12 +90,9 @@ static int e1000e_phc_adjtime(struct ptp_clock_info *ptp, s64 delta) struct e1000_adapter *adapter = container_of(ptp, struct e1000_adapter, ptp_clock_info); unsigned long flags; - s64 now; spin_lock_irqsave(&adapter->systim_lock, flags); - now = timecounter_read(&adapter->tc); - now += delta; - timecounter_init(&adapter->tc, &adapter->cc, now); + timecounter_adjtime(&adapter->tc, delta); spin_unlock_irqrestore(&adapter->systim_lock, flags); return 0; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c index eb088b129bc7..84ab9eea2768 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c @@ -97,7 +97,6 @@ static bool fm10k_alloc_mapped_page(struct fm10k_ring *rx_ring, */ if (dma_mapping_error(rx_ring->dev, dma)) { __free_page(page); - bi->page = NULL; rx_ring->rx_stats.alloc_failed++; return false; @@ -147,8 +146,8 @@ void fm10k_alloc_rx_buffers(struct fm10k_ring *rx_ring, u16 cleaned_count) i -= rx_ring->count; } - /* clear the hdr_addr for the next_to_use descriptor */ - rx_desc->q.hdr_addr = 0; + /* clear the status bits for the next_to_use descriptor */ + rx_desc->d.staterr = 0; cleaned_count--; } while (cleaned_count); @@ -194,7 +193,7 @@ static void fm10k_reuse_rx_page(struct fm10k_ring *rx_ring, rx_ring->next_to_alloc = (nta < rx_ring->count) ? nta : 0; /* transfer page from old buffer to new buffer */ - memcpy(new_buff, old_buff, sizeof(struct fm10k_rx_buffer)); + *new_buff = *old_buff; /* sync the buffer for use by the device */ dma_sync_single_range_for_device(rx_ring->dev, old_buff->dma, @@ -203,12 +202,17 @@ static void fm10k_reuse_rx_page(struct fm10k_ring *rx_ring, DMA_FROM_DEVICE); } +static inline bool fm10k_page_is_reserved(struct page *page) +{ + return (page_to_nid(page) != numa_mem_id()) || page->pfmemalloc; +} + static bool fm10k_can_reuse_rx_page(struct fm10k_rx_buffer *rx_buffer, struct page *page, unsigned int truesize) { /* avoid re-using remote pages */ - if (unlikely(page_to_nid(page) != numa_mem_id())) + if (unlikely(fm10k_page_is_reserved(page))) return false; #if (PAGE_SIZE < 8192) @@ -218,22 +222,19 @@ static bool fm10k_can_reuse_rx_page(struct fm10k_rx_buffer *rx_buffer, /* flip page offset to other buffer */ rx_buffer->page_offset ^= FM10K_RX_BUFSZ; - - /* Even if we own the page, we are not allowed to use atomic_set() - * This would break get_page_unless_zero() users. - */ - atomic_inc(&page->_count); #else /* move offset up to the next cache line */ rx_buffer->page_offset += truesize; if (rx_buffer->page_offset > (PAGE_SIZE - FM10K_RX_BUFSZ)) return false; - - /* bump ref count on page before it is given to the stack */ - get_page(page); #endif + /* Even if we own the page, we are not allowed to use atomic_set() + * This would break get_page_unless_zero() users. + */ + atomic_inc(&page->_count); + return true; } @@ -270,12 +271,12 @@ static bool fm10k_add_rx_frag(struct fm10k_ring *rx_ring, memcpy(__skb_put(skb, size), va, ALIGN(size, sizeof(long))); - /* we can reuse buffer as-is, just make sure it is local */ - if (likely(page_to_nid(page) == numa_mem_id())) + /* page is not reserved, we can reuse buffer as-is */ + if (likely(!fm10k_page_is_reserved(page))) return true; /* this page cannot be reused so discard it */ - put_page(page); + __free_page(page); return false; } @@ -293,7 +294,6 @@ static struct sk_buff *fm10k_fetch_rx_buffer(struct fm10k_ring *rx_ring, struct page *page; rx_buffer = &rx_ring->rx_buffer[rx_ring->next_to_clean]; - page = rx_buffer->page; prefetchw(page); @@ -727,6 +727,12 @@ static __be16 fm10k_tx_encap_offload(struct sk_buff *skb) struct ethhdr *eth_hdr; u8 l4_hdr = 0; +/* fm10k supports 184 octets of outer+inner headers. Minus 20 for inner L4. */ +#define FM10K_MAX_ENCAP_TRANSPORT_OFFSET 164 + if (skb_inner_transport_header(skb) - skb_mac_header(skb) > + FM10K_MAX_ENCAP_TRANSPORT_OFFSET) + return 0; + switch (vlan_get_protocol(skb)) { case htons(ETH_P_IP): l4_hdr = ip_hdr(skb)->protocol; @@ -965,8 +971,8 @@ static void fm10k_tx_map(struct fm10k_ring *tx_ring, tx_desc = FM10K_TX_DESC(tx_ring, i); /* add HW VLAN tag */ - if (vlan_tx_tag_present(skb)) - tx_desc->vlan = cpu_to_le16(vlan_tx_tag_get(skb)); + if (skb_vlan_tag_present(skb)) + tx_desc->vlan = cpu_to_le16(skb_vlan_tag_get(skb)); else tx_desc->vlan = 0; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c index 14a4ea795c01..9f5457c9e627 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_mbx.c @@ -1194,12 +1194,11 @@ static s32 fm10k_mbx_process_disconnect(struct fm10k_hw *hw, { const enum fm10k_mbx_state state = mbx->state; const u32 *hdr = &mbx->mbx_hdr; - u16 head, tail; + u16 head; s32 err; - /* we will need to pull all of the fields for verification */ + /* we will need to pull the header field for verification */ head = FM10K_MSG_HDR_FIELD_GET(*hdr, HEAD); - tail = FM10K_MSG_HDR_FIELD_GET(*hdr, TAIL); /* We should not be receiving disconnect if Rx is incomplete */ if (mbx->pushed) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 8811364b91cb..cfde8bac1aeb 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -609,7 +609,7 @@ static netdev_tx_t fm10k_xmit_frame(struct sk_buff *skb, struct net_device *dev) int err; if ((skb->protocol == htons(ETH_P_8021Q)) && - !vlan_tx_tag_present(skb)) { + !skb_vlan_tag_present(skb)) { /* FM10K only supports hardware tagging, any tags in frame * are considered 2nd level or "outer" tags */ @@ -1414,13 +1414,12 @@ struct net_device *fm10k_alloc_netdev(void) dev->vlan_features |= dev->features; /* configure tunnel offloads */ - dev->hw_enc_features = NETIF_F_IP_CSUM | - NETIF_F_TSO | - NETIF_F_TSO6 | - NETIF_F_TSO_ECN | - NETIF_F_GSO_UDP_TUNNEL | - NETIF_F_IPV6_CSUM | - NETIF_F_SG; + dev->hw_enc_features |= NETIF_F_IP_CSUM | + NETIF_F_TSO | + NETIF_F_TSO6 | + NETIF_F_TSO_ECN | + NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_IPV6_CSUM; /* we want to leave these both on as we cannot disable VLAN tag * insertion or stripping on the hardware since it is contained diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c index 275423d4f777..7e4711958e46 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c @@ -330,13 +330,10 @@ static s32 fm10k_update_xc_addr_pf(struct fm10k_hw *hw, u16 glort, struct fm10k_mac_update mac_update; u32 msg[5]; - /* if glort is not valid return error */ - if (!fm10k_glort_valid_pf(hw, glort)) + /* if glort or vlan are not valid return error */ + if (!fm10k_glort_valid_pf(hw, glort) || vid >= FM10K_VLAN_TABLE_VID_MAX) return FM10K_ERR_PARAM; - /* drop upper 4 bits of VLAN ID */ - vid = (vid << 4) >> 4; - /* record fields */ mac_update.mac_lower = cpu_to_le32(((u32)mac[2] << 24) | ((u32)mac[3] << 16) | diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ptp.c b/drivers/net/ethernet/intel/fm10k/fm10k_ptp.c index 7822809436a3..d966044e017a 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_ptp.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_ptp.c @@ -57,7 +57,6 @@ void fm10k_ts_tx_enqueue(struct fm10k_intfc *interface, struct sk_buff *skb) struct sk_buff_head *list = &interface->ts_tx_skb_queue; struct sk_buff *clone; unsigned long flags; - __le16 dglort; /* create clone for us to return on the Tx path */ clone = skb_clone_sk(skb); @@ -65,8 +64,6 @@ void fm10k_ts_tx_enqueue(struct fm10k_intfc *interface, struct sk_buff *skb) return; FM10K_CB(clone)->ts_tx_timeout = jiffies + FM10K_TS_TX_TIMEOUT; - dglort = FM10K_CB(clone)->fi.w.dglort; - spin_lock_irqsave(&list->lock, flags); /* attempt to locate any buffers with the same dglort, diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_type.h b/drivers/net/ethernet/intel/fm10k/fm10k_type.h index 280296f29154..7c6d9d5a8ae5 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_type.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k_type.h @@ -354,7 +354,7 @@ struct fm10k_hw; /* Define timeouts for resets and disables */ #define FM10K_QUEUE_DISABLE_TIMEOUT 100 -#define FM10K_RESET_TIMEOUT 100 +#define FM10K_RESET_TIMEOUT 150 /* VF registers */ #define FM10K_VFCTRL 0x00000 diff --git a/drivers/net/ethernet/intel/i40e/Makefile b/drivers/net/ethernet/intel/i40e/Makefile index 4b94ddb29c24..c40581999121 100644 --- a/drivers/net/ethernet/intel/i40e/Makefile +++ b/drivers/net/ethernet/intel/i40e/Makefile @@ -44,4 +44,4 @@ i40e-objs := i40e_main.o \ i40e_virtchnl_pf.o i40e-$(CONFIG_I40E_DCB) += i40e_dcb.o i40e_dcb_nl.o -i40e-$(CONFIG_FCOE:m=y) += i40e_fcoe.o +i40e-$(CONFIG_I40E_FCOE) += i40e_fcoe.o diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index fc50f6461b13..2b65cdcad6ba 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -87,11 +87,12 @@ #define I40E_MINIMUM_FCOE 1 /* minimum number of QPs for FCoE */ #endif /* I40E_FCOE */ #define I40E_MAX_AQ_BUF_SIZE 4096 -#define I40E_AQ_LEN 128 -#define I40E_AQ_WORK_LIMIT 16 +#define I40E_AQ_LEN 256 +#define I40E_AQ_WORK_LIMIT 32 #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_NVM_VERSION_LO_SHIFT 0 #define I40E_NVM_VERSION_LO_MASK (0xff << I40E_NVM_VERSION_LO_SHIFT) @@ -147,6 +148,7 @@ enum i40e_state_t { __I40E_FD_FLUSH_REQUESTED, __I40E_RESET_FAILED, __I40E_PORT_TX_SUSPENDED, + __I40E_VF_DISABLE, }; enum i40e_interrupt_policy { @@ -268,7 +270,7 @@ struct i40e_pf { u16 rx_itr_default; u16 tx_itr_default; u16 msg_enable; - char misc_int_name[IFNAMSIZ + 9]; + char int_name[I40E_INT_NAME_STR_LEN]; u16 adminq_work_limit; /* num of admin receive queue desc to process */ unsigned long service_timer_period; unsigned long service_timer_previous; @@ -524,7 +526,7 @@ struct i40e_q_vector { cpumask_t affinity_mask; struct rcu_head rcu; /* to avoid race with update stats on free */ - char name[IFNAMSIZ + 9]; + char name[I40E_INT_NAME_STR_LEN]; } ____cacheline_internodealigned_in_smp; /* lan device */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq.h b/drivers/net/ethernet/intel/i40e/i40e_adminq.h index 564d0b0192f7..de17b6fbcc4e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq.h @@ -148,7 +148,7 @@ static inline int i40e_aq_rc_to_posix(u32 aq_ret, u16 aq_rc) /* general information */ #define I40E_AQ_LARGE_BUF 512 -#define I40E_ASQ_CMD_TIMEOUT 100 /* msecs */ +#define I40E_ASQ_CMD_TIMEOUT 250 /* msecs */ void i40e_fill_default_direct_cmd_desc(struct i40e_aq_desc *desc, u16 opcode); diff --git a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h index 8835aeeff23e..929e3d72a01e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40e/i40e_adminq_cmd.h @@ -256,6 +256,8 @@ enum i40e_admin_queue_opc { i40e_aqc_opc_lldp_stop = 0x0A05, i40e_aqc_opc_lldp_start = 0x0A06, i40e_aqc_opc_get_cee_dcb_cfg = 0x0A07, + i40e_aqc_opc_lldp_set_local_mib = 0x0A08, + i40e_aqc_opc_lldp_stop_start_spec_agent = 0x0A09, /* Tunnel commands */ i40e_aqc_opc_add_udp_tunnel = 0x0B00, @@ -268,6 +270,8 @@ enum i40e_admin_queue_opc { /* OEM commands */ i40e_aqc_opc_oem_parameter_change = 0xFE00, i40e_aqc_opc_oem_device_status_change = 0xFE01, + i40e_aqc_opc_oem_ocsd_initialize = 0xFE02, + i40e_aqc_opc_oem_ocbb_initialize = 0xFE03, /* debug commands */ i40e_aqc_opc_debug_get_deviceid = 0xFF00, @@ -276,7 +280,6 @@ enum i40e_admin_queue_opc { i40e_aqc_opc_debug_write_reg = 0xFF04, i40e_aqc_opc_debug_modify_reg = 0xFF07, i40e_aqc_opc_debug_dump_internals = 0xFF08, - i40e_aqc_opc_debug_modify_internals = 0xFF09, }; /* command structures and indirect data structures */ @@ -410,6 +413,7 @@ struct i40e_aqc_list_capabilities_element_resp { #define I40E_AQ_CAP_ID_VSI 0x0017 #define I40E_AQ_CAP_ID_DCB 0x0018 #define I40E_AQ_CAP_ID_FCOE 0x0021 +#define I40E_AQ_CAP_ID_ISCSI 0x0022 #define I40E_AQ_CAP_ID_RSS 0x0040 #define I40E_AQ_CAP_ID_RXQ 0x0041 #define I40E_AQ_CAP_ID_TXQ 0x0042 @@ -454,8 +458,11 @@ struct i40e_aqc_arp_proxy_data { __le32 pfpm_proxyfc; __le32 ip_addr; u8 mac_addr[6]; + u8 reserved[2]; }; +I40E_CHECK_STRUCT_LEN(0x14, i40e_aqc_arp_proxy_data); + /* Set NS Proxy Table Entry Command (indirect 0x0105) */ struct i40e_aqc_ns_proxy_data { __le16 table_idx_mac_addr_0; @@ -481,6 +488,8 @@ struct i40e_aqc_ns_proxy_data { u8 ipv6_addr_1[16]; }; +I40E_CHECK_STRUCT_LEN(0x3c, i40e_aqc_ns_proxy_data); + /* Manage LAA Command (0x0106) - obsolete */ struct i40e_aqc_mng_laa { __le16 command_flags; @@ -491,6 +500,8 @@ struct i40e_aqc_mng_laa { u8 reserved2[6]; }; +I40E_CHECK_CMD_LENGTH(i40e_aqc_mng_laa); + /* Manage MAC Address Read Command (indirect 0x0107) */ struct i40e_aqc_mac_address_read { __le16 command_flags; @@ -562,6 +573,8 @@ struct i40e_aqc_get_switch_config_header_resp { u8 reserved[12]; }; +I40E_CHECK_CMD_LENGTH(i40e_aqc_get_switch_config_header_resp); + struct i40e_aqc_switch_config_element_resp { u8 element_type; #define I40E_AQ_SW_ELEM_TYPE_MAC 1 @@ -587,6 +600,8 @@ struct i40e_aqc_switch_config_element_resp { __le16 element_info; }; +I40E_CHECK_STRUCT_LEN(0x10, i40e_aqc_switch_config_element_resp); + /* Get Switch Configuration (indirect 0x0200) * an array of elements are returned in the response buffer * the first in the array is the header, remainder are elements @@ -596,6 +611,8 @@ struct i40e_aqc_get_switch_config_resp { struct i40e_aqc_switch_config_element_resp element[1]; }; +I40E_CHECK_STRUCT_LEN(0x20, i40e_aqc_get_switch_config_resp); + /* Add Statistics (direct 0x0201) * Remove Statistics (direct 0x0202) */ @@ -661,6 +678,8 @@ struct i40e_aqc_switch_resource_alloc_element_resp { u8 reserved2[6]; }; +I40E_CHECK_STRUCT_LEN(0x10, i40e_aqc_switch_resource_alloc_element_resp); + /* Add VSI (indirect 0x0210) * this indirect command uses struct i40e_aqc_vsi_properties_data * as the indirect buffer (128 bytes) @@ -1092,6 +1111,8 @@ struct i40e_aqc_remove_tag { u8 reserved[12]; }; +I40E_CHECK_CMD_LENGTH(i40e_aqc_remove_tag); + /* Add multicast E-Tag (direct 0x0257) * del multicast E-Tag (direct 0x0258) only uses pv_seid and etag fields * and no external data @@ -1207,7 +1228,7 @@ struct i40e_aqc_add_remove_cloud_filters_element_data { } ipaddr; __le16 flags; #define I40E_AQC_ADD_CLOUD_FILTER_SHIFT 0 -#define I40E_AQC_ADD_CLOUD_FILTER_MASK (0x3F << \ +#define I40E_AQC_ADD_CLOUD_FILTER_MASK (0x3F << \ I40E_AQC_ADD_CLOUD_FILTER_SHIFT) /* 0x0000 reserved */ #define I40E_AQC_ADD_CLOUD_FILTER_OIP 0x0001 @@ -1240,7 +1261,7 @@ struct i40e_aqc_add_remove_cloud_filters_element_data { u8 reserved[4]; __le16 queue_number; #define I40E_AQC_ADD_CLOUD_QUEUE_SHIFT 0 -#define I40E_AQC_ADD_CLOUD_QUEUE_MASK (0x3F << \ +#define I40E_AQC_ADD_CLOUD_QUEUE_MASK (0x7FF << \ I40E_AQC_ADD_CLOUD_QUEUE_SHIFT) u8 reserved2[14]; /* response section */ @@ -1359,6 +1380,8 @@ struct i40e_aqc_configure_vsi_ets_sla_bw_data { u8 reserved1[28]; }; +I40E_CHECK_STRUCT_LEN(0x40, i40e_aqc_configure_vsi_ets_sla_bw_data); + /* Configure VSI Bandwidth Allocation per Traffic Type (indirect 0x0407) * responds with i40e_aqc_qs_handles_resp */ @@ -1370,6 +1393,8 @@ struct i40e_aqc_configure_vsi_tc_bw_data { __le16 qs_handles[8]; }; +I40E_CHECK_STRUCT_LEN(0x20, i40e_aqc_configure_vsi_tc_bw_data); + /* Query vsi bw configuration (indirect 0x0408) */ struct i40e_aqc_query_vsi_bw_config_resp { u8 tc_valid_bits; @@ -1383,6 +1408,8 @@ struct i40e_aqc_query_vsi_bw_config_resp { u8 reserved3[23]; }; +I40E_CHECK_STRUCT_LEN(0x40, i40e_aqc_query_vsi_bw_config_resp); + /* Query VSI Bandwidth Allocation per Traffic Type (indirect 0x040A) */ struct i40e_aqc_query_vsi_ets_sla_config_resp { u8 tc_valid_bits; @@ -1394,6 +1421,8 @@ struct i40e_aqc_query_vsi_ets_sla_config_resp { __le16 tc_bw_max[2]; }; +I40E_CHECK_STRUCT_LEN(0x20, i40e_aqc_query_vsi_ets_sla_config_resp); + /* Configure Switching Component Bandwidth Limit (direct 0x0410) */ struct i40e_aqc_configure_switching_comp_bw_limit { __le16 seid; @@ -1421,6 +1450,8 @@ struct i40e_aqc_configure_switching_comp_ets_data { u8 reserved2[96]; }; +I40E_CHECK_STRUCT_LEN(0x80, i40e_aqc_configure_switching_comp_ets_data); + /* Configure Switching Component Bandwidth Limits per Tc (indirect 0x0416) */ struct i40e_aqc_configure_switching_comp_ets_bw_limit_data { u8 tc_valid_bits; @@ -1432,6 +1463,9 @@ struct i40e_aqc_configure_switching_comp_ets_bw_limit_data { u8 reserved1[28]; }; +I40E_CHECK_STRUCT_LEN(0x40, + i40e_aqc_configure_switching_comp_ets_bw_limit_data); + /* Configure Switching Component Bandwidth Allocation per Tc * (indirect 0x0417) */ @@ -1443,6 +1477,8 @@ struct i40e_aqc_configure_switching_comp_bw_config_data { u8 reserved1[20]; }; +I40E_CHECK_STRUCT_LEN(0x20, i40e_aqc_configure_switching_comp_bw_config_data); + /* Query Switching Component Configuration (indirect 0x0418) */ struct i40e_aqc_query_switching_comp_ets_config_resp { u8 tc_valid_bits; @@ -1453,6 +1489,8 @@ struct i40e_aqc_query_switching_comp_ets_config_resp { u8 reserved2[23]; }; +I40E_CHECK_STRUCT_LEN(0x40, i40e_aqc_query_switching_comp_ets_config_resp); + /* Query PhysicalPort ETS Configuration (indirect 0x0419) */ struct i40e_aqc_query_port_ets_config_resp { u8 reserved[4]; @@ -1468,6 +1506,8 @@ struct i40e_aqc_query_port_ets_config_resp { u8 reserved3[32]; }; +I40E_CHECK_STRUCT_LEN(0x44, i40e_aqc_query_port_ets_config_resp); + /* Query Switching Component Bandwidth Allocation per Traffic Type * (indirect 0x041A) */ @@ -1482,6 +1522,8 @@ struct i40e_aqc_query_switching_comp_bw_config_resp { __le16 tc_bw_max[2]; }; +I40E_CHECK_STRUCT_LEN(0x20, i40e_aqc_query_switching_comp_bw_config_resp); + /* Suspend/resume port TX traffic * (direct 0x041B and 0x041C) uses the generic SEID struct */ @@ -1495,6 +1537,8 @@ struct i40e_aqc_configure_partition_bw_data { u8 max_bw[16]; /* bandwidth limit */ }; +I40E_CHECK_STRUCT_LEN(0x22, i40e_aqc_configure_partition_bw_data); + /* Get and set the active HMC resource profile and status. * (direct 0x0500) and (direct 0x0501) */ @@ -1577,6 +1621,8 @@ struct i40e_aqc_module_desc { u8 reserved2[8]; }; +I40E_CHECK_STRUCT_LEN(0x20, i40e_aqc_module_desc); + struct i40e_aq_get_phy_abilities_resp { __le32 phy_type; /* bitmap using the above enum for offsets */ u8 link_speed; /* bitmap using the above enum bit patterns */ @@ -1605,6 +1651,8 @@ struct i40e_aq_get_phy_abilities_resp { struct i40e_aqc_module_desc qualified_module[I40E_AQ_PHY_MAX_QMS]; }; +I40E_CHECK_STRUCT_LEN(0x218, i40e_aq_get_phy_abilities_resp); + /* Set PHY Config (direct 0x0601) */ struct i40e_aq_set_phy_config { /* same bits as above in all */ __le32 phy_type; @@ -1788,12 +1836,12 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_nvm_update); /* NVM Config Read (indirect 0x0704) */ struct i40e_aqc_nvm_config_read { __le16 cmd_flags; -#define ANVM_SINGLE_OR_MULTIPLE_FEATURES_MASK 1 -#define ANVM_READ_SINGLE_FEATURE 0 -#define ANVM_READ_MULTIPLE_FEATURES 1 +#define I40E_AQ_ANVM_SINGLE_OR_MULTIPLE_FEATURES_MASK 1 +#define I40E_AQ_ANVM_READ_SINGLE_FEATURE 0 +#define I40E_AQ_ANVM_READ_MULTIPLE_FEATURES 1 __le16 element_count; - __le16 element_id; /* Feature/field ID */ - u8 reserved[2]; + __le16 element_id; /* Feature/field ID */ + __le16 element_id_msw; /* MSWord of field ID */ __le32 address_high; __le32 address_low; }; @@ -1811,21 +1859,32 @@ struct i40e_aqc_nvm_config_write { I40E_CHECK_CMD_LENGTH(i40e_aqc_nvm_config_write); +/* Used for 0x0704 as well as for 0x0705 commands */ +#define I40E_AQ_ANVM_FEATURE_OR_IMMEDIATE_SHIFT 1 +#define I40E_AQ_ANVM_FEATURE_OR_IMMEDIATE_MASK \ + (1 << I40E_AQ_ANVM_FEATURE_OR_IMMEDIATE_SHIFT) +#define I40E_AQ_ANVM_FEATURE 0 +#define I40E_AQ_ANVM_IMMEDIATE_FIELD (1 << FEATURE_OR_IMMEDIATE_SHIFT) struct i40e_aqc_nvm_config_data_feature { __le16 feature_id; - __le16 instance_id; +#define I40E_AQ_ANVM_FEATURE_OPTION_OEM_ONLY 0x01 +#define I40E_AQ_ANVM_FEATURE_OPTION_DWORD_MAP 0x08 +#define I40E_AQ_ANVM_FEATURE_OPTION_POR_CSR 0x10 __le16 feature_options; __le16 feature_selection; }; +I40E_CHECK_STRUCT_LEN(0x6, i40e_aqc_nvm_config_data_feature); + struct i40e_aqc_nvm_config_data_immediate_field { -#define ANVM_FEATURE_OR_IMMEDIATE_MASK 0x2 - __le16 field_id; - __le16 instance_id; + __le32 field_id; + __le32 field_value; __le16 field_options; - __le16 field_value; + __le16 reserved; }; +I40E_CHECK_STRUCT_LEN(0xc, i40e_aqc_nvm_config_data_immediate_field); + /* Send to PF command (indirect 0x0801) id is only used by PF * Send to VF command (indirect 0x0802) id is only used by PF * Send to Peer PF command (indirect 0x0803) @@ -2026,12 +2085,54 @@ struct i40e_aqc_get_cee_dcb_cfg_resp { u8 oper_tc_bw[8]; u8 oper_pfc_en; __le16 oper_app_prio; +#define I40E_AQC_CEE_APP_FCOE_SHIFT 0x0 +#define I40E_AQC_CEE_APP_FCOE_MASK (0x7 << I40E_AQC_CEE_APP_FCOE_SHIFT) +#define I40E_AQC_CEE_APP_ISCSI_SHIFT 0x3 +#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_APP_FIP_MASK (0x7 << I40E_AQC_CEE_APP_FIP_SHIFT) __le32 tlv_status; +#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 +#define I40E_AQC_CEE_PFC_STATUS_MASK (0x7 << I40E_AQC_CEE_PFC_STATUS_SHIFT) +#define I40E_AQC_CEE_APP_STATUS_SHIFT 0x8 +#define I40E_AQC_CEE_APP_STATUS_MASK (0x7 << I40E_AQC_CEE_APP_STATUS_SHIFT) u8 reserved[12]; }; I40E_CHECK_STRUCT_LEN(0x20, i40e_aqc_get_cee_dcb_cfg_resp); +/* Set Local LLDP MIB (indirect 0x0A08) + * Used to replace the local MIB of a given LLDP agent. e.g. DCBx + */ +struct i40e_aqc_lldp_set_local_mib { +#define SET_LOCAL_MIB_AC_TYPE_DCBX_SHIFT 0 +#define SET_LOCAL_MIB_AC_TYPE_DCBX_MASK (1 << SET_LOCAL_MIB_AC_TYPE_DCBX_SHIFT) + u8 type; + u8 reserved0; + __le16 length; + u8 reserved1[4]; + __le32 address_high; + __le32 address_low; +}; + +I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_set_local_mib); + +/* Stop/Start LLDP Agent (direct 0x0A09) + * Used for stopping/starting specific LLDP agent. e.g. DCBx + */ +struct i40e_aqc_lldp_stop_start_specific_agent { +#define I40E_AQC_START_SPECIFIC_AGENT_SHIFT 0 +#define I40E_AQC_START_SPECIFIC_AGENT_MASK \ + (1 << I40E_AQC_START_SPECIFIC_AGENT_SHIFT) + u8 command; + u8 reserved[15]; +}; + +I40E_CHECK_CMD_LENGTH(i40e_aqc_lldp_stop_start_specific_agent); + /* Add Udp Tunnel command and completion (direct 0x0B00) */ struct i40e_aqc_add_udp_tunnel { __le16 udp_port; @@ -2106,7 +2207,8 @@ struct i40e_aqc_oem_param_change { #define I40E_AQ_OEM_PARAM_TYPE_BW_CTL 1 #define I40E_AQ_OEM_PARAM_MAC 2 __le32 param_value1; - u8 param_value2[8]; + __le16 param_value2; + u8 reserved[6]; }; I40E_CHECK_CMD_LENGTH(i40e_aqc_oem_param_change); @@ -2120,6 +2222,28 @@ struct i40e_aqc_oem_state_change { I40E_CHECK_CMD_LENGTH(i40e_aqc_oem_state_change); +/* Initialize OCSD (0xFE02, direct) */ +struct i40e_aqc_opc_oem_ocsd_initialize { + u8 type_status; + u8 reserved1[3]; + __le32 ocsd_memory_block_addr_high; + __le32 ocsd_memory_block_addr_low; + __le32 requested_update_interval; +}; + +I40E_CHECK_CMD_LENGTH(i40e_aqc_opc_oem_ocsd_initialize); + +/* Initialize OCBB (0xFE03, direct) */ +struct i40e_aqc_opc_oem_ocbb_initialize { + u8 type_status; + u8 reserved1[3]; + __le32 ocbb_memory_block_addr_high; + __le32 ocbb_memory_block_addr_low; + u8 reserved2[4]; +}; + +I40E_CHECK_CMD_LENGTH(i40e_aqc_opc_oem_ocbb_initialize); + /* debug commands */ /* get device id (0xFF00) uses the generic structure */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 3d741ee99a2c..11a9ffebf8d8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -742,6 +742,65 @@ i40e_status i40e_get_san_mac_addr(struct i40e_hw *hw, u8 *mac_addr) #endif /** + * i40e_read_pba_string - Reads part number string from EEPROM + * @hw: pointer to hardware structure + * @pba_num: stores the part number string from the EEPROM + * @pba_num_size: part number string buffer length + * + * Reads the part number string from the EEPROM. + **/ +i40e_status i40e_read_pba_string(struct i40e_hw *hw, u8 *pba_num, + u32 pba_num_size) +{ + i40e_status status = 0; + u16 pba_word = 0; + u16 pba_size = 0; + u16 pba_ptr = 0; + u16 i = 0; + + status = i40e_read_nvm_word(hw, I40E_SR_PBA_FLAGS, &pba_word); + if (status || (pba_word != 0xFAFA)) { + hw_dbg(hw, "Failed to read PBA flags or flag is invalid.\n"); + return status; + } + + status = i40e_read_nvm_word(hw, I40E_SR_PBA_BLOCK_PTR, &pba_ptr); + if (status) { + hw_dbg(hw, "Failed to read PBA Block pointer.\n"); + return status; + } + + status = i40e_read_nvm_word(hw, pba_ptr, &pba_size); + if (status) { + hw_dbg(hw, "Failed to read PBA Block size.\n"); + return status; + } + + /* Subtract one to get PBA word count (PBA Size word is included in + * total size) + */ + pba_size--; + if (pba_num_size < (((u32)pba_size * 2) + 1)) { + hw_dbg(hw, "Buffer to small for PBA data.\n"); + return I40E_ERR_PARAM; + } + + for (i = 0; i < pba_size; i++) { + status = i40e_read_nvm_word(hw, (pba_ptr + 1) + i, &pba_word); + if (status) { + hw_dbg(hw, "Failed to read PBA Block word %d.\n", i); + return status; + } + + pba_num[(i * 2)] = (pba_word >> 8) & 0xFF; + pba_num[(i * 2) + 1] = pba_word & 0xFF; + } + pba_num[(pba_size * 2)] = '\0'; + + return status; +} + +/** * i40e_get_media_type - Gets media type * @hw: pointer to the hardware structure **/ @@ -1083,8 +1142,10 @@ void i40e_led_set(struct i40e_hw *hw, u32 mode, bool blink) if (mode == I40E_LINK_ACTIVITY) blink = false; - gpio_val |= (blink ? 1 : 0) << - I40E_GLGEN_GPIO_CTL_LED_BLINK_SHIFT; + if (blink) + gpio_val |= (1 << I40E_GLGEN_GPIO_CTL_LED_BLINK_SHIFT); + else + gpio_val &= ~(1 << I40E_GLGEN_GPIO_CTL_LED_BLINK_SHIFT); wr32(hw, I40E_GLGEN_GPIO_CTL(i), gpio_val); break; @@ -2035,6 +2096,43 @@ i40e_status i40e_aq_send_msg_to_vf(struct i40e_hw *hw, u16 vfid, } /** + * i40e_aq_debug_read_register + * @hw: pointer to the hw struct + * @reg_addr: register address + * @reg_val: register value + * @cmd_details: pointer to command details structure or NULL + * + * Read the register using the admin queue commands + **/ +i40e_status i40e_aq_debug_read_register(struct i40e_hw *hw, + u32 reg_addr, u64 *reg_val, + struct i40e_asq_cmd_details *cmd_details) +{ + struct i40e_aq_desc desc; + struct i40e_aqc_debug_reg_read_write *cmd_resp = + (struct i40e_aqc_debug_reg_read_write *)&desc.params.raw; + i40e_status status; + + if (reg_val == NULL) + return I40E_ERR_PARAM; + + i40e_fill_default_direct_cmd_desc(&desc, + i40e_aqc_opc_debug_read_reg); + + cmd_resp->address = cpu_to_le32(reg_addr); + + status = i40e_asq_send_command(hw, &desc, NULL, 0, cmd_details); + + if (!status) { + *reg_val = ((u64)cmd_resp->value_high << 32) | + (u64)cmd_resp->value_low; + *reg_val = le64_to_cpu(*reg_val); + } + + return status; +} + +/** * i40e_aq_debug_write_register * @hw: pointer to the hw struct * @reg_addr: register address @@ -2264,6 +2362,7 @@ i40e_aq_erase_nvm_exit: #define I40E_DEV_FUNC_CAP_VSI 0x17 #define I40E_DEV_FUNC_CAP_DCB 0x18 #define I40E_DEV_FUNC_CAP_FCOE 0x21 +#define I40E_DEV_FUNC_CAP_ISCSI 0x22 #define I40E_DEV_FUNC_CAP_RSS 0x40 #define I40E_DEV_FUNC_CAP_RX_QUEUES 0x41 #define I40E_DEV_FUNC_CAP_TX_QUEUES 0x42 @@ -2292,6 +2391,7 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff, enum i40e_admin_queue_opc list_type_opc) { struct i40e_aqc_list_capabilities_element_resp *cap; + u32 valid_functions, num_functions; u32 number, logical_id, phys_id; struct i40e_hw_capabilities *p; u32 i = 0; @@ -2362,6 +2462,10 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff, if (number == 1) p->fcoe = true; break; + case I40E_DEV_FUNC_CAP_ISCSI: + if (number == 1) + p->iscsi = true; + break; case I40E_DEV_FUNC_CAP_RSS: p->rss = true; p->rss_table_size = number; @@ -2427,6 +2531,34 @@ static void i40e_parse_discover_capabilities(struct i40e_hw *hw, void *buff, if (p->npar_enable || p->mfp_mode_1) p->fcoe = false; + /* count the enabled ports (aka the "not disabled" ports) */ + hw->num_ports = 0; + for (i = 0; i < 4; i++) { + u32 port_cfg_reg = I40E_PRTGEN_CNF + (4 * i); + u64 port_cfg = 0; + + /* use AQ read to get the physical register offset instead + * of the port relative offset + */ + i40e_aq_debug_read_register(hw, port_cfg_reg, &port_cfg, NULL); + if (!(port_cfg & I40E_PRTGEN_CNF_PORT_DIS_MASK)) + hw->num_ports++; + } + + valid_functions = p->valid_functions; + num_functions = 0; + while (valid_functions) { + if (valid_functions & 1) + num_functions++; + valid_functions >>= 1; + } + + /* partition id is 1-based, and functions are evenly spread + * across the ports as partitions + */ + hw->partition_id = (hw->pf_id / hw->num_ports) + 1; + hw->num_partitions = num_functions / hw->num_ports; + /* additional HW specific goodies that might * someday be HW version specific */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c index 433a55886ad2..61236f983971 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_debugfs.c +++ b/drivers/net/ethernet/intel/i40e/i40e_debugfs.c @@ -829,7 +829,7 @@ static void i40e_dbg_dump_desc(int cnt, int vsi_seid, int ring_id, int desc_n, if (desc_n >= ring->count || desc_n < 0) { dev_info(&pf->pdev->dev, "descriptor %d not found\n", desc_n); - return; + goto out; } if (!is_rx_ring) { txd = I40E_TX_DESC(ring, desc_n); @@ -855,6 +855,8 @@ static void i40e_dbg_dump_desc(int cnt, int vsi_seid, int ring_id, int desc_n, } else { dev_info(&pf->pdev->dev, "dump desc rx/tx <vsi_seid> <ring_id> [<desc_n>]\n"); } + +out: kfree(ring); } @@ -1888,7 +1890,6 @@ static ssize_t i40e_dbg_command_write(struct file *filp, dev_info(&pf->pdev->dev, " dump desc tx <vsi_seid> <ring_id> [<desc_n>]\n"); dev_info(&pf->pdev->dev, " dump desc rx <vsi_seid> <ring_id> [<desc_n>]\n"); dev_info(&pf->pdev->dev, " dump desc aq\n"); - dev_info(&pf->pdev->dev, " dump stats\n"); dev_info(&pf->pdev->dev, " dump reset stats\n"); dev_info(&pf->pdev->dev, " msg_enable [level]\n"); dev_info(&pf->pdev->dev, " read <reg>\n"); diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 951e8767fc50..b8230dc205ec 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -219,6 +219,16 @@ static const char i40e_gstrings_test[][ETH_GSTRING_LEN] = { #define I40E_TEST_LEN (sizeof(i40e_gstrings_test) / ETH_GSTRING_LEN) /** + * i40e_partition_setting_complaint - generic complaint for MFP restriction + * @pf: the PF struct + **/ +static void i40e_partition_setting_complaint(struct i40e_pf *pf) +{ + dev_info(&pf->pdev->dev, + "The link settings are allowed to be changed only from the first partition of a given port. Please switch to the first partition in order to change the setting.\n"); +} + +/** * i40e_get_settings - Get Link Speed and Duplex settings * @netdev: network interface device structure * @ecmd: ethtool command @@ -485,6 +495,14 @@ static int i40e_set_settings(struct net_device *netdev, u8 autoneg; u32 advertise; + /* Changing port settings is not supported if this isn't the + * port's controlling PF + */ + if (hw->partition_id != 1) { + i40e_partition_setting_complaint(pf); + return -EOPNOTSUPP; + } + if (vsi != pf->vsi[pf->lan_vsi]) return -EOPNOTSUPP; @@ -687,6 +705,14 @@ static int i40e_set_pauseparam(struct net_device *netdev, u8 aq_failures; int err = 0; + /* Changing the port's flow control is not supported if this isn't the + * port's controlling PF + */ + if (hw->partition_id != 1) { + i40e_partition_setting_complaint(pf); + return -EOPNOTSUPP; + } + if (vsi != pf->vsi[pf->lan_vsi]) return -EOPNOTSUPP; @@ -1503,7 +1529,7 @@ static void i40e_get_wol(struct net_device *netdev, /* 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) { + if ((1 << hw->port) & wol_nvm_bits || hw->partition_id != 1) { wol->supported = 0; wol->wolopts = 0; } else { @@ -1512,13 +1538,28 @@ static void i40e_get_wol(struct net_device *netdev, } } +/** + * i40e_set_wol - set the WakeOnLAN configuration + * @netdev: the netdev in question + * @wol: the ethtool WoL setting data + **/ static int i40e_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol) { struct i40e_netdev_priv *np = netdev_priv(netdev); struct i40e_pf *pf = np->vsi->back; + struct i40e_vsi *vsi = np->vsi; struct i40e_hw *hw = &pf->hw; u16 wol_nvm_bits; + /* WoL not supported if this isn't the controlling PF on the port */ + if (hw->partition_id != 1) { + i40e_partition_setting_complaint(pf); + return -EOPNOTSUPP; + } + + if (vsi != pf->vsi[pf->lan_vsi]) + return -EOPNOTSUPP; + /* 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)) diff --git a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c index a8b8bd95108d..27c206e62da7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_fcoe.c +++ b/drivers/net/ethernet/intel/i40e/i40e_fcoe.c @@ -39,15 +39,6 @@ #include "i40e_fcoe.h" /** - * i40e_rx_is_fip - returns true if the rx packet type is FIP - * @ptype: the packet type field from rx descriptor write-back - **/ -static inline bool i40e_rx_is_fip(u16 ptype) -{ - return ptype == I40E_RX_PTYPE_L2_FIP_PAY2; -} - -/** * i40e_rx_is_fcoe - returns true if the rx packet type is FCoE * @ptype: the packet type field from rx descriptor write-back **/ @@ -404,6 +395,7 @@ int i40e_fcoe_vsi_init(struct i40e_vsi *vsi, struct i40e_vsi_context *ctxt) I40E_AQ_VSI_PROP_INGRESS_UP_VALID | I40E_AQ_VSI_PROP_EGRESS_UP_VALID)); + info->switch_id = cpu_to_le16(I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB); enabled_tc = i40e_get_fcoe_tc_map(pf); i40e_vsi_setup_queue_map(vsi, ctxt, enabled_tc, true); @@ -1511,12 +1503,16 @@ void i40e_fcoe_config_netdev(struct net_device *netdev, struct i40e_vsi *vsi) strlcpy(netdev->name, "fcoe%d", IFNAMSIZ-1); netdev->mtu = FCOE_MTU; SET_NETDEV_DEV(netdev, &pf->pdev->dev); + /* set different dev_port value 1 for FCoE netdev than the default + * zero dev_port value for PF netdev, this helps biosdevname user + * tool to differentiate them correctly while both attached to the + * same PCI function. + */ + netdev->dev_port = 1; i40e_add_filter(vsi, hw->mac.san_addr, 0, false, false); i40e_add_filter(vsi, (u8[6]) FC_FCOE_FLOGI_MAC, 0, false, false); i40e_add_filter(vsi, FIP_ALL_FCOE_MACS, 0, false, false); i40e_add_filter(vsi, FIP_ALL_ENODE_MACS, 0, false, false); - i40e_add_filter(vsi, FIP_ALL_VN2VN_MACS, 0, false, false); - i40e_add_filter(vsi, FIP_ALL_P2P_MACS, 0, false, false); /* use san mac */ ether_addr_copy(netdev->dev_addr, hw->mac.san_addr); diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index a5f2660d552d..cbe281be1c9f 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 2 -#define DRV_VERSION_BUILD 2 +#define DRV_VERSION_BUILD 6 #define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \ __stringify(DRV_VERSION_MINOR) "." \ __stringify(DRV_VERSION_BUILD) DRV_KERN @@ -2819,8 +2819,9 @@ static void i40e_vsi_configure_msix(struct i40e_vsi *vsi) * i40e_enable_misc_int_causes - enable the non-queue interrupts * @hw: ptr to the hardware info **/ -static void i40e_enable_misc_int_causes(struct i40e_hw *hw) +static void i40e_enable_misc_int_causes(struct i40e_pf *pf) { + struct i40e_hw *hw = &pf->hw; u32 val; /* clear things first */ @@ -2832,11 +2833,13 @@ static void i40e_enable_misc_int_causes(struct i40e_hw *hw) I40E_PFINT_ICR0_ENA_GRST_MASK | I40E_PFINT_ICR0_ENA_PCI_EXCEPTION_MASK | I40E_PFINT_ICR0_ENA_GPIO_MASK | - I40E_PFINT_ICR0_ENA_TIMESYNC_MASK | I40E_PFINT_ICR0_ENA_HMC_ERR_MASK | I40E_PFINT_ICR0_ENA_VFLR_MASK | I40E_PFINT_ICR0_ENA_ADMINQ_MASK; + if (pf->flags & I40E_FLAG_PTP) + val |= I40E_PFINT_ICR0_ENA_TIMESYNC_MASK; + wr32(hw, I40E_PFINT_ICR0_ENA, val); /* SW_ITR_IDX = 0, but don't change INTENA */ @@ -2866,7 +2869,7 @@ static void i40e_configure_msi_and_legacy(struct i40e_vsi *vsi) q_vector->tx.latency_range = I40E_LOW_LATENCY; wr32(hw, I40E_PFINT_ITR0(I40E_TX_ITR), q_vector->tx.itr); - i40e_enable_misc_int_causes(hw); + i40e_enable_misc_int_causes(pf); /* FIRSTQ_INDX = 0, FIRSTQ_TYPE = 0 (rx) */ wr32(hw, I40E_PFINT_LNKLST0, 0); @@ -2937,7 +2940,7 @@ void i40e_irq_dynamic_enable(struct i40e_vsi *vsi, int vector) /** * i40e_irq_dynamic_disable - Disable default interrupt generation settings * @vsi: pointer to a vsi - * @vector: enable a particular Hw Interrupt vector + * @vector: disable a particular Hw Interrupt vector **/ void i40e_irq_dynamic_disable(struct i40e_vsi *vsi, int vector) { @@ -3402,10 +3405,10 @@ static int i40e_vsi_request_irq(struct i40e_vsi *vsi, char *basename) err = i40e_vsi_request_irq_msix(vsi, basename); else if (pf->flags & I40E_FLAG_MSI_ENABLED) err = request_irq(pf->pdev->irq, i40e_intr, 0, - pf->misc_int_name, pf); + pf->int_name, pf); else err = request_irq(pf->pdev->irq, i40e_intr, IRQF_SHARED, - pf->misc_int_name, pf); + pf->int_name, pf); if (err) dev_info(&pf->pdev->dev, "request_irq failed, Error %d\n", err); @@ -3999,6 +4002,35 @@ static int i40e_pf_wait_txq_disabled(struct i40e_pf *pf) #endif /** + * i40e_get_iscsi_tc_map - Return TC map for iSCSI APP + * @pf: pointer to pf + * + * Get TC map for ISCSI PF type that will include iSCSI TC + * and LAN TC. + **/ +static u8 i40e_get_iscsi_tc_map(struct i40e_pf *pf) +{ + struct i40e_dcb_app_priority_table app; + struct i40e_hw *hw = &pf->hw; + u8 enabled_tc = 1; /* TC0 is always enabled */ + u8 tc, i; + /* Get the iSCSI APP TLV */ + struct i40e_dcbx_config *dcbcfg = &hw->local_dcbx_config; + + for (i = 0; i < dcbcfg->numapps; i++) { + app = dcbcfg->app[i]; + if (app.selector == I40E_APP_SEL_TCPIP && + app.protocolid == I40E_APP_PROTOID_ISCSI) { + tc = dcbcfg->etscfg.prioritytable[app.priority]; + enabled_tc |= (1 << tc); + break; + } + } + + return enabled_tc; +} + +/** * i40e_dcb_get_num_tc - Get the number of TCs from DCBx config * @dcbcfg: the corresponding DCBx configuration structure * @@ -4061,18 +4093,23 @@ static u8 i40e_pf_get_num_tc(struct i40e_pf *pf) if (!(pf->flags & I40E_FLAG_DCB_ENABLED)) return 1; + /* SFP mode will be enabled for all TCs on port */ + if (!(pf->flags & I40E_FLAG_MFP_ENABLED)) + return i40e_dcb_get_num_tc(dcbcfg); + /* MFP mode return count of enabled TCs for this PF */ - if (pf->flags & I40E_FLAG_MFP_ENABLED) { + if (pf->hw.func_caps.iscsi) + enabled_tc = i40e_get_iscsi_tc_map(pf); + else enabled_tc = pf->hw.func_caps.enabled_tcmap; - for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { - if (enabled_tc & (1 << i)) - num_tc++; - } - return num_tc; - } - /* SFP mode will be enabled for all TCs on port */ - return i40e_dcb_get_num_tc(dcbcfg); + /* At least have TC0 */ + enabled_tc = (enabled_tc ? enabled_tc : 0x1); + for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { + if (enabled_tc & (1 << i)) + num_tc++; + } + return num_tc; } /** @@ -4110,12 +4147,15 @@ static u8 i40e_pf_get_tc_map(struct i40e_pf *pf) if (!(pf->flags & I40E_FLAG_DCB_ENABLED)) return i40e_pf_get_default_tc(pf); - /* MFP mode will have enabled TCs set by FW */ - if (pf->flags & I40E_FLAG_MFP_ENABLED) - return pf->hw.func_caps.enabled_tcmap; - /* SFP mode we want PF to be enabled for all TCs */ - return i40e_dcb_get_enabled_tc(&pf->hw.local_dcbx_config); + if (!(pf->flags & I40E_FLAG_MFP_ENABLED)) + return i40e_dcb_get_enabled_tc(&pf->hw.local_dcbx_config); + + /* MPF enabled and iSCSI PF type */ + if (pf->hw.func_caps.iscsi) + return i40e_get_iscsi_tc_map(pf); + else + return pf->hw.func_caps.enabled_tcmap; } /** @@ -4505,9 +4545,6 @@ static int i40e_init_pf_dcb(struct i40e_pf *pf) struct i40e_hw *hw = &pf->hw; int err = 0; - if (pf->hw.func_caps.npar_enable) - goto out; - /* Get the initial DCB configuration */ err = i40e_init_dcb(hw); if (!err) { @@ -4533,7 +4570,8 @@ static int i40e_init_pf_dcb(struct i40e_pf *pf) "DCBX offload is supported for this PF.\n"); } } else { - dev_info(&pf->pdev->dev, "AQ Querying DCB configuration failed: %d\n", + dev_info(&pf->pdev->dev, + "AQ Querying DCB configuration failed: aq_err %d\n", pf->hw.aq.asq_last_status); } @@ -4557,6 +4595,15 @@ static void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) return; } + /* Warn user if link speed on NPAR enabled partition is not at + * least 10GB + */ + if (vsi->back->hw.func_caps.npar_enable && + (vsi->back->hw.phy.link_info.link_speed == I40E_LINK_SPEED_1GB || + vsi->back->hw.phy.link_info.link_speed == I40E_LINK_SPEED_100MB)) + netdev_warn(vsi->netdev, + "The partition detected link speed that is less than 10Gbps\n"); + switch (vsi->back->hw.phy.link_info.link_speed) { case I40E_LINK_SPEED_40GB: strlcpy(speed, "40 Gbps", SPEED_SIZE); @@ -4836,7 +4883,7 @@ static int i40e_open(struct net_device *netdev) int i40e_vsi_open(struct i40e_vsi *vsi) { struct i40e_pf *pf = vsi->back; - char int_name[IFNAMSIZ]; + char int_name[I40E_INT_NAME_STR_LEN]; int err; /* allocate descriptors */ @@ -4870,7 +4917,7 @@ int i40e_vsi_open(struct i40e_vsi *vsi) goto err_set_queues; } else if (vsi->type == I40E_VSI_FDIR) { - snprintf(int_name, sizeof(int_name) - 1, "%s-%s-fdir", + snprintf(int_name, sizeof(int_name) - 1, "%s-%s:fdir", dev_driver_string(&pf->pdev->dev), dev_name(&pf->pdev->dev)); err = i40e_vsi_request_irq(vsi, int_name); @@ -5494,14 +5541,18 @@ 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; /* 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); + old_link_speed = pf->hw.phy.link_info_old.link_speed; + new_link_speed = pf->hw.phy.link_info.link_speed; if (new_link == old_link && + new_link_speed == old_link_speed && (test_bit(__I40E_DOWN, &vsi->state) || new_link == netif_carrier_ok(vsi->netdev))) return; @@ -6175,8 +6226,9 @@ static void i40e_reset_and_rebuild(struct i40e_pf *pf, bool reinit) #ifdef CONFIG_I40E_DCB ret = i40e_init_pf_dcb(pf); if (ret) { - dev_info(&pf->pdev->dev, "init_pf_dcb failed: %d\n", ret); - goto end_core_reset; + dev_info(&pf->pdev->dev, "DCB init failed %d, disabled\n", ret); + pf->flags &= ~I40E_FLAG_DCB_CAPABLE; + /* Continue without DCB enabled */ } #endif /* CONFIG_I40E_DCB */ #ifdef I40E_FCOE @@ -6881,17 +6933,17 @@ static int i40e_init_msix(struct i40e_pf *pf) if (pf->flags & I40E_FLAG_FD_SB_ENABLED) other_vecs++; + /* Scale down if necessary, and the rings will share vectors */ + pf->num_lan_msix = min_t(int, pf->num_lan_msix, + (hw->func_caps.num_msix_vectors - other_vecs)); + v_budget = pf->num_lan_msix + other_vecs; + #ifdef I40E_FCOE if (pf->flags & I40E_FLAG_FCOE_ENABLED) { pf->num_fcoe_msix = pf->num_fcoe_qps; v_budget += pf->num_fcoe_msix; } - #endif - /* Scale down if necessary, and the rings will share vectors */ - pf->num_lan_msix = min_t(int, pf->num_lan_msix, - (hw->func_caps.num_msix_vectors - other_vecs)); - v_budget = pf->num_lan_msix + other_vecs; pf->msix_entries = kcalloc(v_budget, sizeof(struct msix_entry), GFP_KERNEL); @@ -7113,16 +7165,16 @@ static int i40e_setup_misc_vector(struct i40e_pf *pf) */ if (!test_bit(__I40E_RESET_RECOVERY_PENDING, &pf->state)) { err = request_irq(pf->msix_entries[0].vector, - i40e_intr, 0, pf->misc_int_name, pf); + i40e_intr, 0, pf->int_name, pf); if (err) { dev_info(&pf->pdev->dev, "request_irq for %s failed: %d\n", - pf->misc_int_name, err); + pf->int_name, err); return -EFAULT; } } - i40e_enable_misc_int_causes(hw); + i40e_enable_misc_int_causes(pf); /* associate no queues to the misc vector */ wr32(hw, I40E_PFINT_LNKLST0, I40E_QUEUE_END_OF_LIST); @@ -7306,7 +7358,7 @@ static int i40e_sw_init(struct i40e_pf *pf) #endif /* I40E_FCOE */ #ifdef CONFIG_PCI_IOV - if (pf->hw.func_caps.num_vfs) { + if (pf->hw.func_caps.num_vfs && pf->hw.partition_id == 1) { pf->num_vf_qps = I40E_DEFAULT_QUEUES_PER_VF; pf->flags |= I40E_FLAG_SRIOV_ENABLED; pf->num_req_vfs = min_t(int, @@ -7766,7 +7818,8 @@ static int i40e_add_vsi(struct i40e_vsi *vsi) enabled_tc = i40e_pf_get_tc_map(pf); /* MFP mode setup queue map and update VSI */ - if (pf->flags & I40E_FLAG_MFP_ENABLED) { + if ((pf->flags & I40E_FLAG_MFP_ENABLED) && + !(pf->hw.func_caps.iscsi)) { /* NIC type PF */ memset(&ctxt, 0, sizeof(ctxt)); ctxt.seid = pf->main_vsi_seid; ctxt.pf_num = pf->hw.pf_id; @@ -7787,6 +7840,8 @@ static int i40e_add_vsi(struct i40e_vsi *vsi) /* Default/Main VSI is only enabled for TC0 * reconfigure it to enable all TCs that are * available on the port in SFP mode. + * For MFP case the iSCSI PF would use this + * flow to enable LAN+iSCSI TC. */ ret = i40e_vsi_config_tc(vsi, enabled_tc); if (ret) { @@ -9164,7 +9219,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) hw->aq.asq_buf_size = I40E_MAX_AQ_BUF_SIZE; pf->adminq_work_limit = I40E_AQ_WORK_LIMIT; - snprintf(pf->misc_int_name, sizeof(pf->misc_int_name) - 1, + snprintf(pf->int_name, sizeof(pf->int_name) - 1, "%s-%s:misc", dev_driver_string(&pf->pdev->dev), dev_name(&pdev->dev)); @@ -9227,6 +9282,16 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_configure_lan_hmc; } + /* Disable LLDP for NICs that have firmware versions lower than v4.3. + * Ignore error return codes because if it was already disabled via + * hardware settings this will fail + */ + if (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver < 3)) || + (pf->hw.aq.fw_maj_ver < 4)) { + dev_info(&pdev->dev, "Stopping firmware LLDP agent.\n"); + i40e_aq_stop_lldp(hw, true, NULL); + } + i40e_get_mac_addr(hw, hw->mac.addr); if (!is_valid_ether_addr(hw->mac.addr)) { dev_info(&pdev->dev, "invalid MAC address %pM\n", hw->mac.addr); @@ -9256,7 +9321,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) #ifdef CONFIG_I40E_DCB err = i40e_init_pf_dcb(pf); if (err) { - dev_info(&pdev->dev, "init_pf_dcb failed: %d\n", err); + dev_info(&pdev->dev, "DCB init failed %d, disabled\n", err); pf->flags &= ~I40E_FLAG_DCB_CAPABLE; /* Continue without DCB enabled */ } @@ -9671,6 +9736,8 @@ static int i40e_suspend(struct pci_dev *pdev, pm_message_t state) set_bit(__I40E_SUSPENDED, &pf->state); set_bit(__I40E_DOWN, &pf->state); + del_timer_sync(&pf->service_timer); + cancel_work_sync(&pf->service_task); rtnl_lock(); i40e_prep_for_reset(pf); rtnl_unlock(); diff --git a/drivers/net/ethernet/intel/i40e/i40e_osdep.h b/drivers/net/ethernet/intel/i40e/i40e_osdep.h index 045b5c4b98b3..ad802dd0f67a 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_osdep.h +++ b/drivers/net/ethernet/intel/i40e/i40e_osdep.h @@ -78,7 +78,7 @@ do { \ } while (0) typedef enum i40e_status_code i40e_status; -#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE) +#ifdef CONFIG_I40E_FCOE #define I40E_FCOE -#endif /* CONFIG_FCOE or CONFIG_FCOE_MODULE */ +#endif #endif /* _I40E_OSDEP_H_ */ diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h index 2fb4306597e8..68e852a96680 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h @@ -71,6 +71,9 @@ i40e_status i40e_aq_get_firmware_version(struct i40e_hw *hw, i40e_status i40e_aq_debug_write_register(struct i40e_hw *hw, u32 reg_addr, u64 reg_val, struct i40e_asq_cmd_details *cmd_details); +i40e_status i40e_aq_debug_read_register(struct i40e_hw *hw, + u32 reg_addr, u64 *reg_val, + struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_set_phy_debug(struct i40e_hw *hw, u8 cmd_flags, struct i40e_asq_cmd_details *cmd_details); i40e_status i40e_aq_set_default_vsi(struct i40e_hw *hw, u16 vsi_id, @@ -245,6 +248,8 @@ void i40e_clear_pxe_mode(struct i40e_hw *hw); bool i40e_get_link_status(struct i40e_hw *hw); i40e_status i40e_get_mac_addr(struct i40e_hw *hw, u8 *mac_addr); i40e_status i40e_get_port_mac_addr(struct i40e_hw *hw, u8 *mac_addr); +i40e_status i40e_read_pba_string(struct i40e_hw *hw, u8 *pba_num, + u32 pba_num_size); i40e_status i40e_validate_mac_addr(u8 *mac_addr); void i40e_pre_tx_queue_cfg(struct i40e_hw *hw, u32 queue, bool enable); #ifdef I40E_FCOE diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index 6d1ec926aa37..fabcfa1b45b2 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -247,7 +247,12 @@ void i40e_ptp_rx_hang(struct i40e_vsi *vsi) u32 prttsyn_stat; int n; - if (!(pf->flags & I40E_FLAG_PTP)) + /* Since we cannot turn off the Rx timestamp logic if the device is + * configured for Tx timestamping, we check if Rx timestamping is + * configured. We don't want to spuriously warn about Rx timestamp + * hangs if we don't care about the timestamps. + */ + if (!(pf->flags & I40E_FLAG_PTP) || !pf->ptp_rx) return; prttsyn_stat = rd32(hw, I40E_PRTTSYN_STAT_1); @@ -305,6 +310,13 @@ void i40e_ptp_tx_hwtstamp(struct i40e_pf *pf) u32 hi, lo; u64 ns; + if (!(pf->flags & I40E_FLAG_PTP) || !pf->ptp_tx) + return; + + /* don't attempt to timestamp if we don't have an skb */ + if (!pf->ptp_tx_skb) + return; + lo = rd32(hw, I40E_PRTTSYN_TXTIME_L); hi = rd32(hw, I40E_PRTTSYN_TXTIME_H); @@ -338,7 +350,7 @@ void i40e_ptp_rx_hwtstamp(struct i40e_pf *pf, struct sk_buff *skb, u8 index) /* Since we cannot turn off the Rx timestamp logic if the device is * doing Tx timestamping, check if Rx timestamping is configured. */ - if (!pf->ptp_rx) + if (!(pf->flags & I40E_FLAG_PTP) || !pf->ptp_rx) return; hw = &pf->hw; @@ -467,7 +479,12 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf, switch (config->rx_filter) { case HWTSTAMP_FILTER_NONE: pf->ptp_rx = false; - tsyntype = 0; + /* We set the type to V1, but do not enable UDP packet + * recognition. In this way, we should be as close to + * disabling PTP Rx timestamps as possible since V1 packets + * are always UDP, since L2 packets are a V2 feature. + */ + tsyntype = I40E_PRTTSYN_CTL1_TSYNTYPE_V1; break; case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: @@ -521,17 +538,18 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf, regval &= ~I40E_PFINT_ICR0_ENA_TIMESYNC_MASK; wr32(hw, I40E_PFINT_ICR0_ENA, regval); - /* There is no simple on/off switch for Rx. To "disable" Rx support, - * ignore any received timestamps, rather than turn off the clock. + /* Although there is no simple on/off switch for Rx, we "disable" Rx + * timestamps by setting to V1 only mode and clear the UDP + * recognition. This ought to disable all PTP Rx timestamps as V1 + * packets are always over UDP. Note that software is configured to + * ignore Rx timestamps via the pf->ptp_rx flag. */ - if (pf->ptp_rx) { - regval = rd32(hw, I40E_PRTTSYN_CTL1); - /* clear everything but the enable bit */ - regval &= I40E_PRTTSYN_CTL1_TSYNENA_MASK; - /* now enable bits for desired Rx timestamps */ - regval |= tsyntype; - wr32(hw, I40E_PRTTSYN_CTL1, regval); - } + regval = rd32(hw, I40E_PRTTSYN_CTL1); + /* clear everything but the enable bit */ + regval &= I40E_PRTTSYN_CTL1_TSYNENA_MASK; + /* now enable bits for desired Rx timestamps */ + regval |= tsyntype; + wr32(hw, I40E_PRTTSYN_CTL1, regval); return 0; } diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 04b441460bbd..2206d2d36f0f 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -658,6 +658,8 @@ static inline u32 i40e_get_head(struct i40e_ring *tx_ring) return le32_to_cpu(*(volatile __le32 *)head); } +#define WB_STRIDE 0x3 + /** * i40e_clean_tx_irq - Reclaim resources after transmit completes * @tx_ring: tx ring to clean @@ -759,6 +761,18 @@ 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" @@ -777,13 +791,16 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) 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 hang detected on queue %d, reset requested\n", tx_ring->queue_index); - tx_ring->netdev->netdev_ops->ndo_tx_timeout(tx_ring->netdev); + /* 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 + */ - /* the adapter is about to reset, no point in enabling stuff */ - return true; + /* the adapter is about to reset, no point in enabling polling */ + budget = 1; } netdev_tx_completed_queue(netdev_get_tx_queue(tx_ring->netdev, @@ -806,7 +823,25 @@ 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 + * @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) +{ + u32 val = I40E_PFINT_DYN_CTLN_INTENA_MASK | + I40E_PFINT_DYN_CTLN_SWINT_TRIG_MASK | + I40E_PFINT_DYN_CTLN_SW_ITR_INDX_ENA_MASK; + /* allow 00 to be written to the index */ + + wr32(&vsi->back->hw, + I40E_PFINT_DYN_CTLN(q_vector->v_idx + vsi->base_vector - 1), + val); } /** @@ -1063,6 +1098,8 @@ int i40e_setup_rx_descriptors(struct i40e_ring *rx_ring) if (!rx_ring->rx_bi) goto err; + u64_stats_init(&rx_ring->syncp); + /* Round up to nearest 4K */ rx_ring->size = ring_is_16byte_desc_enabled(rx_ring) ? rx_ring->count * sizeof(union i40e_16byte_rx_desc) @@ -1290,9 +1327,7 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, * so the total length of IPv4 header is IHL*4 bytes * The UDP_0 bit *may* bet set if the *inner* header is UDP */ - if (ipv4_tunnel && - (decoded.inner_prot != I40E_RX_PTYPE_INNER_PROT_UDP) && - !(rx_status & (1 << I40E_RX_DESC_STATUS_UDP_0_SHIFT))) { + if (ipv4_tunnel) { skb->transport_header = skb->mac_header + sizeof(struct ethhdr) + (ip_hdr(skb)->ihl * 4); @@ -1302,15 +1337,19 @@ static inline void i40e_rx_checksum(struct i40e_vsi *vsi, skb->protocol == htons(ETH_P_8021AD)) ? VLAN_HLEN : 0; - rx_udp_csum = udp_csum(skb); - iph = ip_hdr(skb); - csum = csum_tcpudp_magic( - iph->saddr, iph->daddr, - (skb->len - skb_transport_offset(skb)), - IPPROTO_UDP, rx_udp_csum); + if ((ip_hdr(skb)->protocol == IPPROTO_UDP) && + (udp_hdr(skb)->check != 0)) { + rx_udp_csum = udp_csum(skb); + iph = ip_hdr(skb); + csum = csum_tcpudp_magic( + iph->saddr, iph->daddr, + (skb->len - skb_transport_offset(skb)), + IPPROTO_UDP, rx_udp_csum); + + if (udp_hdr(skb)->check != csum) + goto checksum_fail; - if (udp_hdr(skb)->check != csum) - goto checksum_fail; + } /* else its GRE and so no outer UDP header */ } skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -1581,6 +1620,7 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) struct i40e_vsi *vsi = q_vector->vsi; struct i40e_ring *ring; bool clean_complete = true; + bool arm_wb = false; int budget_per_ring; if (test_bit(__I40E_DOWN, &vsi->state)) { @@ -1591,8 +1631,10 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) /* Since the actual Tx work is minimal, we can give the Tx a larger * budget and be more aggressive about cleaning up the Tx descriptors. */ - i40e_for_each_ring(ring, q_vector->tx) + i40e_for_each_ring(ring, q_vector->tx) { clean_complete &= i40e_clean_tx_irq(ring, vsi->work_limit); + arm_wb |= ring->arm_wb; + } /* We attempt to distribute budget to each Rx queue fairly, but don't * allow the budget to go below 1 because that would exit polling early. @@ -1603,8 +1645,11 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) clean_complete &= i40e_clean_rx_irq(ring, budget_per_ring); /* If work not completed, return budget and polling will return */ - if (!clean_complete) + if (!clean_complete) { + if (arm_wb) + i40e_force_wb(vsi, q_vector); return budget; + } /* Work is done so exit the polling mode and re-enable the interrupt */ napi_complete(napi); @@ -1772,8 +1817,8 @@ static int i40e_tx_prepare_vlan_flags(struct sk_buff *skb, u32 tx_flags = 0; /* if we have a HW VLAN tag being added, default to the HW one */ - if (vlan_tx_tag_present(skb)) { - tx_flags |= vlan_tx_tag_get(skb) << I40E_TX_FLAGS_VLAN_SHIFT; + if (skb_vlan_tag_present(skb)) { + tx_flags |= skb_vlan_tag_get(skb) << I40E_TX_FLAGS_VLAN_SHIFT; tx_flags |= I40E_TX_FLAGS_HW_VLAN; /* else if it is a SW VLAN, check the next protocol and store the tag */ } else if (protocol == htons(ETH_P_8021Q)) { @@ -1840,17 +1885,16 @@ static int i40e_tso(struct i40e_ring *tx_ring, struct sk_buff *skb, if (err < 0) return err; - if (protocol == htons(ETH_P_IP)) { - iph = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb); + iph = skb->encapsulation ? inner_ip_hdr(skb) : ip_hdr(skb); + ipv6h = skb->encapsulation ? inner_ipv6_hdr(skb) : ipv6_hdr(skb); + + if (iph->version == 4) { tcph = skb->encapsulation ? inner_tcp_hdr(skb) : tcp_hdr(skb); iph->tot_len = 0; iph->check = 0; tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, 0, IPPROTO_TCP, 0); - } else if (skb_is_gso_v6(skb)) { - - ipv6h = skb->encapsulation ? inner_ipv6_hdr(skb) - : ipv6_hdr(skb); + } else if (ipv6h->version == 6) { tcph = skb->encapsulation ? inner_tcp_hdr(skb) : tcp_hdr(skb); ipv6h->payload_len = 0; tcph->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, @@ -1897,6 +1941,9 @@ static int i40e_tsyn(struct i40e_ring *tx_ring, struct sk_buff *skb, * we are not already transmitting a packet to be timestamped */ pf = i40e_netdev_to_pf(tx_ring->netdev); + if (!(pf->flags & I40E_FLAG_PTP)) + return 0; + if (pf->ptp_tx && !test_and_set_bit_lock(__I40E_PTP_TX_IN_PROGRESS, &pf->state)) { skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; @@ -1946,13 +1993,9 @@ static void i40e_tx_enable_csum(struct sk_buff *skb, u32 tx_flags, I40E_TX_CTX_EXT_IP_IPV4_NO_CSUM; } } else if (tx_flags & I40E_TX_FLAGS_IPV6) { - if (tx_flags & I40E_TX_FLAGS_TSO) { - *cd_tunneling |= I40E_TX_CTX_EXT_IP_IPV6; + *cd_tunneling |= I40E_TX_CTX_EXT_IP_IPV6; + if (tx_flags & I40E_TX_FLAGS_TSO) ip_hdr(skb)->check = 0; - } else { - *cd_tunneling |= - I40E_TX_CTX_EXT_IP_IPV4_NO_CSUM; - } } /* Now set the ctx descriptor fields */ @@ -1962,7 +2005,10 @@ static void i40e_tx_enable_csum(struct sk_buff *skb, u32 tx_flags, ((skb_inner_network_offset(skb) - skb_transport_offset(skb)) >> 1) << I40E_TXD_CTX_QW0_NATLEN_SHIFT; - + if (this_ip_hdr->version == 6) { + tx_flags &= ~I40E_TX_FLAGS_IPV4; + tx_flags |= I40E_TX_FLAGS_IPV6; + } } else { network_hdr_len = skb_network_header_len(skb); this_ip_hdr = ip_hdr(skb); @@ -2198,7 +2244,6 @@ static void i40e_tx_map(struct i40e_ring *tx_ring, struct sk_buff *skb, /* Place RS bit on last descriptor of any packet that spans across the * 4th descriptor (WB_STRIDE aka 0x3) in a 64B cacheline. */ -#define WB_STRIDE 0x3 if (((i & WB_STRIDE) != WB_STRIDE) && (first <= &tx_ring->tx_bi[i]) && (first >= &tx_ring->tx_bi[i & ~WB_STRIDE])) { diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index e60d3accb2e2..18b00231d2f1 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -241,6 +241,7 @@ struct i40e_ring { unsigned long last_rx_timestamp; bool ring_active; /* is ring online or not */ + bool arm_wb; /* do something to arm write back */ /* stats structs */ struct i40e_queue_stats stats; diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index c1f2eb963357..e9901ef06a63 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -211,6 +211,7 @@ struct i40e_hw_capabilities { bool evb_802_1_qbh; /* Bridge Port Extension */ bool dcb; bool fcoe; + bool iscsi; /* Indicates iSCSI enabled */ bool mfp_mode_1; bool mgmt_cem; bool ieee_1588; @@ -431,7 +432,7 @@ struct i40e_hw { u8 __iomem *hw_addr; void *back; - /* function pointer structs */ + /* subsystem structs */ struct i40e_phy_info phy; struct i40e_mac_info mac; struct i40e_bus_info bus; @@ -458,6 +459,11 @@ struct i40e_hw { u8 pf_id; u16 main_vsi_seid; + /* for multi-function MACs */ + u16 partition_id; + u16 num_partitions; + u16 num_ports; + /* Closest numa node to the device */ u16 numa_node; @@ -1135,6 +1141,8 @@ struct i40e_hw_port_stats { /* Checksum and Shadow RAM pointers */ #define I40E_SR_NVM_CONTROL_WORD 0x00 #define I40E_SR_EMP_MODULE_PTR 0x0F +#define I40E_SR_PBA_FLAGS 0x15 +#define I40E_SR_PBA_BLOCK_PTR 0x16 #define I40E_SR_NVM_IMAGE_VERSION 0x18 #define I40E_SR_NVM_WAKE_ON_LAN 0x19 #define I40E_SR_ALTERNATE_SAN_MAC_ADDRESS_PTR 0x27 diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index 5bae89550657..40f042af4131 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -647,6 +647,9 @@ void i40e_reset_vf(struct i40e_vf *vf, bool flr) int i; u32 reg; + if (test_and_set_bit(__I40E_VF_DISABLE, &pf->state)) + return; + /* warn the VF */ clear_bit(I40E_VF_STAT_ACTIVE, &vf->vf_states); @@ -668,13 +671,13 @@ void i40e_reset_vf(struct i40e_vf *vf, bool flr) /* poll VPGEN_VFRSTAT reg to make sure * that reset is complete */ - for (i = 0; i < 100; i++) { - /* vf reset requires driver to first reset the - * vf and then poll the status register to make sure - * that the requested op was completed - * successfully + for (i = 0; i < 10; i++) { + /* VF reset requires driver to first reset the VF and then + * poll the status register to make sure that the reset + * completed successfully. Due to internal HW FIFO flushes, + * we must wait 10ms before the register will be valid. */ - usleep_range(10, 20); + usleep_range(10000, 20000); reg = rd32(hw, I40E_VPGEN_VFRSTAT(vf->vf_id)); if (reg & I40E_VPGEN_VFRSTAT_VFRD_MASK) { rsd = true; @@ -706,6 +709,7 @@ complete_reset: /* tell the VF the reset is done */ wr32(hw, I40E_VFGEN_RSTAT1(vf->vf_id), I40E_VFR_VFACTIVE); i40e_flush(hw); + clear_bit(__I40E_VF_DISABLE, &pf->state); } /** @@ -790,11 +794,18 @@ void i40e_free_vfs(struct i40e_pf *pf) if (!pf->vf) return; + while (test_and_set_bit(__I40E_VF_DISABLE, &pf->state)) + usleep_range(1000, 2000); - /* Disable interrupt 0 so we don't try to handle the VFLR. */ - i40e_irq_dynamic_disable_icr0(pf); + /* 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. + */ + if (!pci_vfs_assigned(pf->pdev)) + pci_disable_sriov(pf->pdev); + + msleep(20); /* let any messages in transit get finished up */ - mdelay(10); /* let any messages in transit get finished up */ /* free up vf resources */ tmp = pf->num_alloc_vfs; pf->num_alloc_vfs = 0; @@ -813,7 +824,6 @@ void i40e_free_vfs(struct i40e_pf *pf) * before this function ever gets called. */ if (!pci_vfs_assigned(pf->pdev)) { - pci_disable_sriov(pf->pdev); /* Acknowledge VFLR for all VFS. Without this, VFs will fail to * work correctly when SR-IOV gets re-enabled. */ @@ -827,9 +837,7 @@ void i40e_free_vfs(struct i40e_pf *pf) dev_warn(&pf->pdev->dev, "unable to disable SR-IOV because VFs are assigned.\n"); } - - /* Re-enable interrupt 0. */ - i40e_irq_dynamic_enable_icr0(pf); + clear_bit(__I40E_VF_DISABLE, &pf->state); } #ifdef CONFIG_PCI_IOV diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq.h index 6c31bf22c2c3..60f04e96a80e 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq.h @@ -148,7 +148,7 @@ static inline int i40e_aq_rc_to_posix(u32 aq_ret, u16 aq_rc) /* general information */ #define I40E_AQ_LARGE_BUF 512 -#define I40E_ASQ_CMD_TIMEOUT 100 /* msecs */ +#define I40E_ASQ_CMD_TIMEOUT 250 /* msecs */ void i40evf_fill_default_direct_cmd_desc(struct i40e_aq_desc *desc, u16 opcode); diff --git a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h index ff1b16370da9..e715bccfb5d2 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_adminq_cmd.h @@ -268,6 +268,8 @@ enum i40e_admin_queue_opc { /* OEM commands */ i40e_aqc_opc_oem_parameter_change = 0xFE00, i40e_aqc_opc_oem_device_status_change = 0xFE01, + i40e_aqc_opc_oem_ocsd_initialize = 0xFE02, + i40e_aqc_opc_oem_ocbb_initialize = 0xFE03, /* debug commands */ i40e_aqc_opc_debug_get_deviceid = 0xFF00, @@ -276,7 +278,6 @@ enum i40e_admin_queue_opc { i40e_aqc_opc_debug_write_reg = 0xFF04, i40e_aqc_opc_debug_modify_reg = 0xFF07, i40e_aqc_opc_debug_dump_internals = 0xFF08, - i40e_aqc_opc_debug_modify_internals = 0xFF09, }; /* command structures and indirect data structures */ @@ -410,6 +411,7 @@ struct i40e_aqc_list_capabilities_element_resp { #define I40E_AQ_CAP_ID_VSI 0x0017 #define I40E_AQ_CAP_ID_DCB 0x0018 #define I40E_AQ_CAP_ID_FCOE 0x0021 +#define I40E_AQ_CAP_ID_ISCSI 0x0022 #define I40E_AQ_CAP_ID_RSS 0x0040 #define I40E_AQ_CAP_ID_RXQ 0x0041 #define I40E_AQ_CAP_ID_TXQ 0x0042 @@ -454,8 +456,11 @@ struct i40e_aqc_arp_proxy_data { __le32 pfpm_proxyfc; __le32 ip_addr; u8 mac_addr[6]; + u8 reserved[2]; }; +I40E_CHECK_STRUCT_LEN(0x14, i40e_aqc_arp_proxy_data); + /* Set NS Proxy Table Entry Command (indirect 0x0105) */ struct i40e_aqc_ns_proxy_data { __le16 table_idx_mac_addr_0; @@ -481,6 +486,8 @@ struct i40e_aqc_ns_proxy_data { u8 ipv6_addr_1[16]; }; +I40E_CHECK_STRUCT_LEN(0x3c, i40e_aqc_ns_proxy_data); + /* Manage LAA Command (0x0106) - obsolete */ struct i40e_aqc_mng_laa { __le16 command_flags; @@ -491,6 +498,8 @@ struct i40e_aqc_mng_laa { u8 reserved2[6]; }; +I40E_CHECK_CMD_LENGTH(i40e_aqc_mng_laa); + /* Manage MAC Address Read Command (indirect 0x0107) */ struct i40e_aqc_mac_address_read { __le16 command_flags; @@ -562,6 +571,8 @@ struct i40e_aqc_get_switch_config_header_resp { u8 reserved[12]; }; +I40E_CHECK_CMD_LENGTH(i40e_aqc_get_switch_config_header_resp); + struct i40e_aqc_switch_config_element_resp { u8 element_type; #define I40E_AQ_SW_ELEM_TYPE_MAC 1 @@ -587,6 +598,8 @@ struct i40e_aqc_switch_config_element_resp { __le16 element_info; }; +I40E_CHECK_STRUCT_LEN(0x10, i40e_aqc_switch_config_element_resp); + /* Get Switch Configuration (indirect 0x0200) * an array of elements are returned in the response buffer * the first in the array is the header, remainder are elements @@ -596,6 +609,8 @@ struct i40e_aqc_get_switch_config_resp { struct i40e_aqc_switch_config_element_resp element[1]; }; +I40E_CHECK_STRUCT_LEN(0x20, i40e_aqc_get_switch_config_resp); + /* Add Statistics (direct 0x0201) * Remove Statistics (direct 0x0202) */ @@ -661,6 +676,8 @@ struct i40e_aqc_switch_resource_alloc_element_resp { u8 reserved2[6]; }; +I40E_CHECK_STRUCT_LEN(0x10, i40e_aqc_switch_resource_alloc_element_resp); + /* Add VSI (indirect 0x0210) * this indirect command uses struct i40e_aqc_vsi_properties_data * as the indirect buffer (128 bytes) @@ -1092,6 +1109,8 @@ struct i40e_aqc_remove_tag { u8 reserved[12]; }; +I40E_CHECK_CMD_LENGTH(i40e_aqc_remove_tag); + /* Add multicast E-Tag (direct 0x0257) * del multicast E-Tag (direct 0x0258) only uses pv_seid and etag fields * and no external data @@ -1207,7 +1226,7 @@ struct i40e_aqc_add_remove_cloud_filters_element_data { } ipaddr; __le16 flags; #define I40E_AQC_ADD_CLOUD_FILTER_SHIFT 0 -#define I40E_AQC_ADD_CLOUD_FILTER_MASK (0x3F << \ +#define I40E_AQC_ADD_CLOUD_FILTER_MASK (0x3F << \ I40E_AQC_ADD_CLOUD_FILTER_SHIFT) /* 0x0000 reserved */ #define I40E_AQC_ADD_CLOUD_FILTER_OIP 0x0001 @@ -1240,7 +1259,7 @@ struct i40e_aqc_add_remove_cloud_filters_element_data { u8 reserved[4]; __le16 queue_number; #define I40E_AQC_ADD_CLOUD_QUEUE_SHIFT 0 -#define I40E_AQC_ADD_CLOUD_QUEUE_MASK (0x3F << \ +#define I40E_AQC_ADD_CLOUD_QUEUE_MASK (0x7FF << \ I40E_AQC_ADD_CLOUD_QUEUE_SHIFT) u8 reserved2[14]; /* response section */ @@ -1359,6 +1378,8 @@ struct i40e_aqc_configure_vsi_ets_sla_bw_data { u8 reserved1[28]; }; +I40E_CHECK_STRUCT_LEN(0x40, i40e_aqc_configure_vsi_ets_sla_bw_data); + /* Configure VSI Bandwidth Allocation per Traffic Type (indirect 0x0407) * responds with i40e_aqc_qs_handles_resp */ @@ -1370,6 +1391,8 @@ struct i40e_aqc_configure_vsi_tc_bw_data { __le16 qs_handles[8]; }; +I40E_CHECK_STRUCT_LEN(0x20, i40e_aqc_configure_vsi_tc_bw_data); + /* Query vsi bw configuration (indirect 0x0408) */ struct i40e_aqc_query_vsi_bw_config_resp { u8 tc_valid_bits; @@ -1383,6 +1406,8 @@ struct i40e_aqc_query_vsi_bw_config_resp { u8 reserved3[23]; }; +I40E_CHECK_STRUCT_LEN(0x40, i40e_aqc_query_vsi_bw_config_resp); + /* Query VSI Bandwidth Allocation per Traffic Type (indirect 0x040A) */ struct i40e_aqc_query_vsi_ets_sla_config_resp { u8 tc_valid_bits; @@ -1394,6 +1419,8 @@ struct i40e_aqc_query_vsi_ets_sla_config_resp { __le16 tc_bw_max[2]; }; +I40E_CHECK_STRUCT_LEN(0x20, i40e_aqc_query_vsi_ets_sla_config_resp); + /* Configure Switching Component Bandwidth Limit (direct 0x0410) */ struct i40e_aqc_configure_switching_comp_bw_limit { __le16 seid; @@ -1421,6 +1448,8 @@ struct i40e_aqc_configure_switching_comp_ets_data { u8 reserved2[96]; }; +I40E_CHECK_STRUCT_LEN(0x80, i40e_aqc_configure_switching_comp_ets_data); + /* Configure Switching Component Bandwidth Limits per Tc (indirect 0x0416) */ struct i40e_aqc_configure_switching_comp_ets_bw_limit_data { u8 tc_valid_bits; @@ -1432,6 +1461,9 @@ struct i40e_aqc_configure_switching_comp_ets_bw_limit_data { u8 reserved1[28]; }; +I40E_CHECK_STRUCT_LEN(0x40, + i40e_aqc_configure_switching_comp_ets_bw_limit_data); + /* Configure Switching Component Bandwidth Allocation per Tc * (indirect 0x0417) */ @@ -1443,6 +1475,8 @@ struct i40e_aqc_configure_switching_comp_bw_config_data { u8 reserved1[20]; }; +I40E_CHECK_STRUCT_LEN(0x20, i40e_aqc_configure_switching_comp_bw_config_data); + /* Query Switching Component Configuration (indirect 0x0418) */ struct i40e_aqc_query_switching_comp_ets_config_resp { u8 tc_valid_bits; @@ -1453,6 +1487,8 @@ struct i40e_aqc_query_switching_comp_ets_config_resp { u8 reserved2[23]; }; +I40E_CHECK_STRUCT_LEN(0x40, i40e_aqc_query_switching_comp_ets_config_resp); + /* Query PhysicalPort ETS Configuration (indirect 0x0419) */ struct i40e_aqc_query_port_ets_config_resp { u8 reserved[4]; @@ -1468,6 +1504,8 @@ struct i40e_aqc_query_port_ets_config_resp { u8 reserved3[32]; }; +I40E_CHECK_STRUCT_LEN(0x44, i40e_aqc_query_port_ets_config_resp); + /* Query Switching Component Bandwidth Allocation per Traffic Type * (indirect 0x041A) */ @@ -1482,6 +1520,8 @@ struct i40e_aqc_query_switching_comp_bw_config_resp { __le16 tc_bw_max[2]; }; +I40E_CHECK_STRUCT_LEN(0x20, i40e_aqc_query_switching_comp_bw_config_resp); + /* Suspend/resume port TX traffic * (direct 0x041B and 0x041C) uses the generic SEID struct */ @@ -1495,6 +1535,8 @@ struct i40e_aqc_configure_partition_bw_data { u8 max_bw[16]; /* bandwidth limit */ }; +I40E_CHECK_STRUCT_LEN(0x22, i40e_aqc_configure_partition_bw_data); + /* Get and set the active HMC resource profile and status. * (direct 0x0500) and (direct 0x0501) */ @@ -1577,6 +1619,8 @@ struct i40e_aqc_module_desc { u8 reserved2[8]; }; +I40E_CHECK_STRUCT_LEN(0x20, i40e_aqc_module_desc); + struct i40e_aq_get_phy_abilities_resp { __le32 phy_type; /* bitmap using the above enum for offsets */ u8 link_speed; /* bitmap using the above enum bit patterns */ @@ -1605,6 +1649,8 @@ struct i40e_aq_get_phy_abilities_resp { struct i40e_aqc_module_desc qualified_module[I40E_AQ_PHY_MAX_QMS]; }; +I40E_CHECK_STRUCT_LEN(0x218, i40e_aq_get_phy_abilities_resp); + /* Set PHY Config (direct 0x0601) */ struct i40e_aq_set_phy_config { /* same bits as above in all */ __le32 phy_type; @@ -1788,12 +1834,12 @@ I40E_CHECK_CMD_LENGTH(i40e_aqc_nvm_update); /* NVM Config Read (indirect 0x0704) */ struct i40e_aqc_nvm_config_read { __le16 cmd_flags; -#define ANVM_SINGLE_OR_MULTIPLE_FEATURES_MASK 1 -#define ANVM_READ_SINGLE_FEATURE 0 -#define ANVM_READ_MULTIPLE_FEATURES 1 +#define I40E_AQ_ANVM_SINGLE_OR_MULTIPLE_FEATURES_MASK 1 +#define I40E_AQ_ANVM_READ_SINGLE_FEATURE 0 +#define I40E_AQ_ANVM_READ_MULTIPLE_FEATURES 1 __le16 element_count; - __le16 element_id; /* Feature/field ID */ - u8 reserved[2]; + __le16 element_id; /* Feature/field ID */ + __le16 element_id_msw; /* MSWord of field ID */ __le32 address_high; __le32 address_low; }; @@ -1811,21 +1857,32 @@ struct i40e_aqc_nvm_config_write { I40E_CHECK_CMD_LENGTH(i40e_aqc_nvm_config_write); +/* Used for 0x0704 as well as for 0x0705 commands */ +#define I40E_AQ_ANVM_FEATURE_OR_IMMEDIATE_SHIFT 1 +#define I40E_AQ_ANVM_FEATURE_OR_IMMEDIATE_MASK \ + (1 << I40E_AQ_ANVM_FEATURE_OR_IMMEDIATE_SHIFT) +#define I40E_AQ_ANVM_FEATURE 0 +#define I40E_AQ_ANVM_IMMEDIATE_FIELD (1 << FEATURE_OR_IMMEDIATE_SHIFT) struct i40e_aqc_nvm_config_data_feature { __le16 feature_id; - __le16 instance_id; +#define I40E_AQ_ANVM_FEATURE_OPTION_OEM_ONLY 0x01 +#define I40E_AQ_ANVM_FEATURE_OPTION_DWORD_MAP 0x08 +#define I40E_AQ_ANVM_FEATURE_OPTION_POR_CSR 0x10 __le16 feature_options; __le16 feature_selection; }; +I40E_CHECK_STRUCT_LEN(0x6, i40e_aqc_nvm_config_data_feature); + struct i40e_aqc_nvm_config_data_immediate_field { -#define ANVM_FEATURE_OR_IMMEDIATE_MASK 0x2 - __le16 field_id; - __le16 instance_id; + __le32 field_id; + __le32 field_value; __le16 field_options; - __le16 field_value; + __le16 reserved; }; +I40E_CHECK_STRUCT_LEN(0xc, i40e_aqc_nvm_config_data_immediate_field); + /* Send to PF command (indirect 0x0801) id is only used by PF * Send to VF command (indirect 0x0802) id is only used by PF * Send to Peer PF command (indirect 0x0803) @@ -2082,7 +2139,8 @@ struct i40e_aqc_oem_param_change { #define I40E_AQ_OEM_PARAM_TYPE_BW_CTL 1 #define I40E_AQ_OEM_PARAM_MAC 2 __le32 param_value1; - u8 param_value2[8]; + __le16 param_value2; + u8 reserved[6]; }; I40E_CHECK_CMD_LENGTH(i40e_aqc_oem_param_change); @@ -2096,6 +2154,28 @@ struct i40e_aqc_oem_state_change { I40E_CHECK_CMD_LENGTH(i40e_aqc_oem_state_change); +/* Initialize OCSD (0xFE02, direct) */ +struct i40e_aqc_opc_oem_ocsd_initialize { + u8 type_status; + u8 reserved1[3]; + __le32 ocsd_memory_block_addr_high; + __le32 ocsd_memory_block_addr_low; + __le32 requested_update_interval; +}; + +I40E_CHECK_CMD_LENGTH(i40e_aqc_opc_oem_ocsd_initialize); + +/* Initialize OCBB (0xFE03, direct) */ +struct i40e_aqc_opc_oem_ocbb_initialize { + u8 type_status; + u8 reserved1[3]; + __le32 ocbb_memory_block_addr_high; + __le32 ocbb_memory_block_addr_low; + u8 reserved2[4]; +}; + +I40E_CHECK_CMD_LENGTH(i40e_aqc_opc_oem_ocbb_initialize); + /* debug commands */ /* get device id (0xFF00) uses the generic structure */ diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 04c7c1557a0c..29004382f462 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -192,6 +192,8 @@ static inline u32 i40e_get_head(struct i40e_ring *tx_ring) return le32_to_cpu(*(volatile __le32 *)head); } +#define WB_STRIDE 0x3 + /** * i40e_clean_tx_irq - Reclaim resources after transmit completes * @tx_ring: tx ring to clean @@ -293,6 +295,14 @@ 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; + 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" @@ -344,6 +354,24 @@ static bool i40e_clean_tx_irq(struct i40e_ring *tx_ring, int budget) } /** + * i40e_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) +{ + u32 val = I40E_VFINT_DYN_CTLN_INTENA_MASK | + I40E_VFINT_DYN_CTLN_SWINT_TRIG_MASK | + I40E_VFINT_DYN_CTLN_SW_ITR_INDX_ENA_MASK; + /* allow 00 to be written to the index */ + + wr32(&vsi->back->hw, + I40E_VFINT_DYN_CTLN1(q_vector->v_idx + vsi->base_vector - 1), + val); +} + +/** * i40e_set_new_dynamic_itr - Find new ITR level * @rc: structure containing ring performance data * @@ -568,6 +596,8 @@ int i40evf_setup_rx_descriptors(struct i40e_ring *rx_ring) if (!rx_ring->rx_bi) goto err; + u64_stats_init(&rx_ring->syncp); + /* Round up to nearest 4K */ rx_ring->size = ring_is_16byte_desc_enabled(rx_ring) ? rx_ring->count * sizeof(union i40e_16byte_rx_desc) @@ -1065,6 +1095,7 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) struct i40e_vsi *vsi = q_vector->vsi; struct i40e_ring *ring; bool clean_complete = true; + bool arm_wb = false; int budget_per_ring; if (test_bit(__I40E_DOWN, &vsi->state)) { @@ -1075,8 +1106,10 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) /* Since the actual Tx work is minimal, we can give the Tx a larger * budget and be more aggressive about cleaning up the Tx descriptors. */ - i40e_for_each_ring(ring, q_vector->tx) + i40e_for_each_ring(ring, q_vector->tx) { clean_complete &= i40e_clean_tx_irq(ring, vsi->work_limit); + arm_wb |= ring->arm_wb; + } /* We attempt to distribute budget to each Rx queue fairly, but don't * allow the budget to go below 1 because that would exit polling early. @@ -1087,8 +1120,11 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) clean_complete &= i40e_clean_rx_irq(ring, budget_per_ring); /* If work not completed, return budget and polling will return */ - if (!clean_complete) + if (!clean_complete) { + if (arm_wb) + i40e_force_wb(vsi, q_vector); return budget; + } /* Work is done so exit the polling mode and re-enable the interrupt */ napi_complete(napi); @@ -1122,8 +1158,8 @@ static int i40e_tx_prepare_vlan_flags(struct sk_buff *skb, u32 tx_flags = 0; /* if we have a HW VLAN tag being added, default to the HW one */ - if (vlan_tx_tag_present(skb)) { - tx_flags |= vlan_tx_tag_get(skb) << I40E_TX_FLAGS_VLAN_SHIFT; + if (skb_vlan_tag_present(skb)) { + tx_flags |= skb_vlan_tag_get(skb) << I40E_TX_FLAGS_VLAN_SHIFT; tx_flags |= I40E_TX_FLAGS_HW_VLAN; /* else if it is a SW VLAN, check the next protocol and store the tag */ } else if (protocol == htons(ETH_P_8021Q)) { diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index c7f29626eada..4e15903b2b6d 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -238,6 +238,7 @@ struct i40e_ring { u8 atr_count; bool ring_active; /* is ring online or not */ + bool arm_wb; /* do something to arm write back */ /* stats structs */ struct i40e_queue_stats stats; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h index 68aec11f6523..3d0fdaab5cc8 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_type.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h @@ -211,6 +211,7 @@ struct i40e_hw_capabilities { bool evb_802_1_qbh; /* Bridge Port Extension */ bool dcb; bool fcoe; + bool iscsi; /* Indicates iSCSI enabled */ bool mfp_mode_1; bool mgmt_cem; bool ieee_1588; @@ -425,7 +426,7 @@ struct i40e_hw { u8 __iomem *hw_addr; void *back; - /* function pointer structs */ + /* subsystem structs */ struct i40e_phy_info phy; struct i40e_mac_info mac; struct i40e_bus_info bus; @@ -452,6 +453,11 @@ struct i40e_hw { u8 pf_id; u16 main_vsi_seid; + /* for multi-function MACs */ + u16 partition_id; + u16 num_partitions; + u16 num_ports; + /* Closest numa node to the device */ u16 numa_node; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index cabaf599f562..8d8c201c63c1 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -36,7 +36,7 @@ char i40evf_driver_name[] = "i40evf"; static const char i40evf_driver_string[] = "Intel(R) XL710/X710 Virtual Function Network Driver"; -#define DRV_VERSION "1.0.6" +#define DRV_VERSION "1.2.0" const char i40evf_driver_version[] = DRV_VERSION; static const char i40evf_copyright[] = "Copyright (c) 2013 - 2014 Intel Corporation."; @@ -313,10 +313,6 @@ static irqreturn_t i40evf_msix_aq(int irq, void *data) val = val | I40E_PFINT_DYN_CTL0_CLEARPBA_MASK; wr32(hw, I40E_VFINT_DYN_CTL01, val); - /* re-enable interrupt causes */ - wr32(hw, I40E_VFINT_ICR0_ENA1, ena_mask); - wr32(hw, I40E_VFINT_DYN_CTL01, I40E_VFINT_DYN_CTL01_INTENA_MASK); - /* schedule work on the private workqueue */ schedule_work(&adapter->adminq_task); @@ -947,30 +943,6 @@ static int i40evf_up_complete(struct i40evf_adapter *adapter) } /** - * i40evf_clean_all_rx_rings - Free Rx Buffers for all queues - * @adapter: board private structure - **/ -static void i40evf_clean_all_rx_rings(struct i40evf_adapter *adapter) -{ - int i; - - for (i = 0; i < adapter->num_active_queues; i++) - i40evf_clean_rx_ring(adapter->rx_rings[i]); -} - -/** - * i40evf_clean_all_tx_rings - Free Tx Buffers for all queues - * @adapter: board private structure - **/ -static void i40evf_clean_all_tx_rings(struct i40evf_adapter *adapter) -{ - int i; - - for (i = 0; i < adapter->num_active_queues; i++) - i40evf_clean_tx_ring(adapter->tx_rings[i]); -} - -/** * i40e_down - Shutdown the connection processing * @adapter: board private structure **/ @@ -982,6 +954,12 @@ void i40evf_down(struct i40evf_adapter *adapter) if (adapter->state == __I40EVF_DOWN) return; + while (test_and_set_bit(__I40EVF_IN_CRITICAL_TASK, + &adapter->crit_section)) + usleep_range(500, 1000); + + i40evf_irq_disable(adapter); + /* remove all MAC filters */ list_for_each_entry(f, &adapter->mac_filter_list, list) { f->remove = true; @@ -992,25 +970,27 @@ void i40evf_down(struct i40evf_adapter *adapter) } if (!(adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED) && adapter->state != __I40EVF_RESETTING) { - adapter->aq_required |= I40EVF_FLAG_AQ_DEL_MAC_FILTER; + /* cancel any current operation */ + adapter->current_op = I40E_VIRTCHNL_OP_UNKNOWN; + adapter->aq_pending = 0; + /* Schedule operations to close down the HW. Don't wait + * here for this to complete. The watchdog is still running + * and it will take care of this. + */ + adapter->aq_required = I40EVF_FLAG_AQ_DEL_MAC_FILTER; adapter->aq_required |= I40EVF_FLAG_AQ_DEL_VLAN_FILTER; - /* disable receives */ adapter->aq_required |= I40EVF_FLAG_AQ_DISABLE_QUEUES; - mod_timer_pending(&adapter->watchdog_timer, jiffies + 1); - msleep(20); } netif_tx_disable(netdev); netif_tx_stop_all_queues(netdev); - i40evf_irq_disable(adapter); - i40evf_napi_disable_all(adapter); - netif_carrier_off(netdev); + msleep(20); - i40evf_clean_all_tx_rings(adapter); - i40evf_clean_all_rx_rings(adapter); + netif_carrier_off(netdev); + clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section); } /** @@ -1356,8 +1336,13 @@ static void i40evf_watchdog_task(struct work_struct *work) /* Process admin queue tasks. After init, everything gets done * here so we don't race on the admin queue. */ - if (adapter->aq_pending) + if (adapter->aq_pending) { + if (!i40evf_asq_done(hw)) { + dev_dbg(&adapter->pdev->dev, "Admin queue timeout\n"); + i40evf_send_api_ver(adapter); + } goto watchdog_done; + } if (adapter->aq_required & I40EVF_FLAG_AQ_MAP_VECTORS) { i40evf_map_queues(adapter); @@ -1401,11 +1386,14 @@ static void i40evf_watchdog_task(struct work_struct *work) if (adapter->state == __I40EVF_RUNNING) i40evf_request_stats(adapter); - - i40evf_irq_enable(adapter, true); - i40evf_fire_sw_int(adapter, 0xFF); - watchdog_done: + if (adapter->state == __I40EVF_RUNNING) { + i40evf_irq_enable_queues(adapter, ~0); + i40evf_fire_sw_int(adapter, 0xFF); + } else { + i40evf_fire_sw_int(adapter, 0x1); + } + clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section); restart_watchdog: if (adapter->state == __I40EVF_REMOVE) @@ -1633,17 +1621,17 @@ static void i40evf_adminq_task(struct work_struct *work) u16 pending; if (adapter->flags & I40EVF_FLAG_PF_COMMS_FAILED) - return; + goto out; event.buf_len = I40EVF_MAX_AQ_BUF_SIZE; event.msg_buf = kzalloc(event.buf_len, GFP_KERNEL); if (!event.msg_buf) - return; + goto out; v_msg = (struct i40e_virtchnl_msg *)&event.desc; do { ret = i40evf_clean_arq_element(hw, &event, &pending); - if (ret) + if (ret || !v_msg->v_opcode) break; /* No event to process or error cleaning ARQ */ i40evf_virtchnl_completion(adapter, v_msg->v_opcode, @@ -1688,10 +1676,10 @@ static void i40evf_adminq_task(struct work_struct *work) if (oldval != val) wr32(hw, hw->aq.asq.len, val); + kfree(event.msg_buf); +out: /* re-enable Admin queue interrupt cause */ i40evf_misc_irq_enable(adapter); - - kfree(event.msg_buf); } /** @@ -2053,12 +2041,8 @@ static void i40evf_init_task(struct work_struct *work) /* aq msg sent, awaiting reply */ err = i40evf_verify_api_ver(adapter); if (err) { - dev_info(&pdev->dev, "Unable to verify API version (%d), retrying\n", - err); - if (err == I40E_ERR_ADMIN_QUEUE_NO_WORK) { - dev_info(&pdev->dev, "Resending request\n"); + if (err == I40E_ERR_ADMIN_QUEUE_NO_WORK) err = i40evf_send_api_ver(adapter); - } goto err; } err = i40evf_send_vf_config_msg(adapter); @@ -2081,7 +2065,6 @@ static void i40evf_init_task(struct work_struct *work) } err = i40evf_get_vf_config(adapter); if (err == I40E_ERR_ADMIN_QUEUE_NO_WORK) { - dev_info(&pdev->dev, "Resending VF config request\n"); err = i40evf_send_vf_config_msg(adapter); goto err; } @@ -2230,12 +2213,18 @@ err: static void i40evf_shutdown(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); + struct i40evf_adapter *adapter = netdev_priv(netdev); netif_device_detach(netdev); if (netif_running(netdev)) i40evf_close(netdev); + /* Prevent the watchdog from running. */ + adapter->state = __I40EVF_REMOVE; + adapter->aq_required = 0; + adapter->aq_pending = 0; + #ifdef CONFIG_PM pci_save_state(pdev); @@ -2448,7 +2437,18 @@ static void i40evf_remove(struct pci_dev *pdev) unregister_netdev(netdev); adapter->netdev_registered = false; } + + /* Shut down all the garbage mashers on the detention level */ adapter->state = __I40EVF_REMOVE; + adapter->aq_required = 0; + adapter->aq_pending = 0; + i40evf_request_reset(adapter); + msleep(20); + /* If the FW isn't responding, kick it once, but only once. */ + if (!i40evf_asq_done(hw)) { + i40evf_request_reset(adapter); + msleep(20); + } if (adapter->msix_entries) { i40evf_misc_irq_disable(adapter); @@ -2477,6 +2477,10 @@ static void i40evf_remove(struct pci_dev *pdev) list_del(&f->list); kfree(f); } + list_for_each_entry_safe(f, ftmp, &adapter->vlan_filter_list, list) { + list_del(&f->list); + kfree(f); + } free_netdev(netdev); diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index 5fde5a7f4591..3f0c85ecbca6 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -715,14 +715,14 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, } return; } - if (v_opcode != adapter->current_op) - dev_info(&adapter->pdev->dev, "Pending op is %d, received %d\n", - adapter->current_op, v_opcode); if (v_retval) { dev_err(&adapter->pdev->dev, "%s: PF returned error %d to our request %d\n", __func__, v_retval, v_opcode); } switch (v_opcode) { + case I40E_VIRTCHNL_OP_VERSION: + /* no action, but also not an error */ + break; case I40E_VIRTCHNL_OP_GET_STATS: { struct i40e_eth_stats *stats = (struct i40e_eth_stats *)msg; diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c index 051ea94bdcd3..0f69ef81751a 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.c +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c @@ -1125,7 +1125,7 @@ static s32 igb_acquire_swfw_sync_82575(struct e1000_hw *hw, u16 mask) u32 swmask = mask; u32 fwmask = mask << 16; s32 ret_val = 0; - s32 i = 0, timeout = 200; /* FIXME: find real value to use here */ + s32 i = 0, timeout = 200; while (i < timeout) { if (igb_get_hw_semaphore(hw)) { diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index 82d891e183b1..c2bd4f98a837 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -29,7 +29,7 @@ #include "e1000_mac.h" #include "e1000_82575.h" -#include <linux/clocksource.h> +#include <linux/timecounter.h> #include <linux/net_tstamp.h> #include <linux/ptp_clock_kernel.h> #include <linux/bitops.h> @@ -343,6 +343,9 @@ struct hwmon_buff { }; #endif +#define IGB_N_EXTTS 2 +#define IGB_N_PEROUT 2 +#define IGB_N_SDP 4 #define IGB_RETA_SIZE 128 /* board specific private data structure */ @@ -439,6 +442,12 @@ struct igb_adapter { u32 tx_hwtstamp_timeouts; u32 rx_hwtstamp_cleared; + struct ptp_pin_desc sdp_config[IGB_N_SDP]; + struct { + struct timespec start; + struct timespec period; + } perout[IGB_N_PEROUT]; + char fw_version[32]; #ifdef CONFIG_IGB_HWMON struct hwmon_buff *igb_hwmon_buff; diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index ff59897a9463..f366b3b96d03 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -5035,9 +5035,9 @@ netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb, skb_tx_timestamp(skb); - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { tx_flags |= IGB_TX_FLAGS_VLAN; - tx_flags |= (vlan_tx_tag_get(skb) << IGB_TX_FLAGS_VLAN_SHIFT); + tx_flags |= (skb_vlan_tag_get(skb) << IGB_TX_FLAGS_VLAN_SHIFT); } /* record initial flags and protocol */ @@ -5384,6 +5384,80 @@ void igb_update_stats(struct igb_adapter *adapter, } } +static void igb_tsync_interrupt(struct igb_adapter *adapter) +{ + struct e1000_hw *hw = &adapter->hw; + struct ptp_clock_event event; + struct timespec ts; + u32 ack = 0, tsauxc, sec, nsec, tsicr = rd32(E1000_TSICR); + + if (tsicr & TSINTR_SYS_WRAP) { + event.type = PTP_CLOCK_PPS; + if (adapter->ptp_caps.pps) + ptp_clock_event(adapter->ptp_clock, &event); + else + dev_err(&adapter->pdev->dev, "unexpected SYS WRAP"); + ack |= TSINTR_SYS_WRAP; + } + + if (tsicr & E1000_TSICR_TXTS) { + /* retrieve hardware timestamp */ + schedule_work(&adapter->ptp_tx_work); + ack |= E1000_TSICR_TXTS; + } + + if (tsicr & TSINTR_TT0) { + spin_lock(&adapter->tmreg_lock); + ts = timespec_add(adapter->perout[0].start, + adapter->perout[0].period); + wr32(E1000_TRGTTIML0, ts.tv_nsec); + wr32(E1000_TRGTTIMH0, ts.tv_sec); + tsauxc = rd32(E1000_TSAUXC); + tsauxc |= TSAUXC_EN_TT0; + wr32(E1000_TSAUXC, tsauxc); + adapter->perout[0].start = ts; + spin_unlock(&adapter->tmreg_lock); + ack |= TSINTR_TT0; + } + + if (tsicr & TSINTR_TT1) { + spin_lock(&adapter->tmreg_lock); + ts = timespec_add(adapter->perout[1].start, + adapter->perout[1].period); + wr32(E1000_TRGTTIML1, ts.tv_nsec); + wr32(E1000_TRGTTIMH1, ts.tv_sec); + tsauxc = rd32(E1000_TSAUXC); + tsauxc |= TSAUXC_EN_TT1; + wr32(E1000_TSAUXC, tsauxc); + adapter->perout[1].start = ts; + spin_unlock(&adapter->tmreg_lock); + ack |= TSINTR_TT1; + } + + if (tsicr & TSINTR_AUTT0) { + nsec = rd32(E1000_AUXSTMPL0); + sec = rd32(E1000_AUXSTMPH0); + event.type = PTP_CLOCK_EXTTS; + event.index = 0; + event.timestamp = sec * 1000000000ULL + nsec; + ptp_clock_event(adapter->ptp_clock, &event); + ack |= TSINTR_AUTT0; + } + + if (tsicr & TSINTR_AUTT1) { + nsec = rd32(E1000_AUXSTMPL1); + sec = rd32(E1000_AUXSTMPH1); + event.type = PTP_CLOCK_EXTTS; + event.index = 1; + event.timestamp = sec * 1000000000ULL + nsec; + ptp_clock_event(adapter->ptp_clock, &event); + ack |= TSINTR_AUTT1; + } + + /* acknowledge the interrupts */ + wr32(E1000_TSICR, ack); +} + static irqreturn_t igb_msix_other(int irq, void *data) { struct igb_adapter *adapter = data; @@ -5415,16 +5489,8 @@ static irqreturn_t igb_msix_other(int irq, void *data) mod_timer(&adapter->watchdog_timer, jiffies + 1); } - if (icr & E1000_ICR_TS) { - u32 tsicr = rd32(E1000_TSICR); - - if (tsicr & E1000_TSICR_TXTS) { - /* acknowledge the interrupt */ - wr32(E1000_TSICR, E1000_TSICR_TXTS); - /* retrieve hardware timestamp */ - schedule_work(&adapter->ptp_tx_work); - } - } + if (icr & E1000_ICR_TS) + igb_tsync_interrupt(adapter); wr32(E1000_EIMS, adapter->eims_other); @@ -6011,8 +6077,12 @@ static void igb_vf_reset_msg(struct igb_adapter *adapter, u32 vf) adapter->vf_data[vf].flags |= IGB_VF_FLAG_CTS; /* reply to reset with ack and vf mac address */ - msgbuf[0] = E1000_VF_RESET | E1000_VT_MSGTYPE_ACK; - memcpy(addr, vf_mac, ETH_ALEN); + if (!is_zero_ether_addr(vf_mac)) { + msgbuf[0] = E1000_VF_RESET | E1000_VT_MSGTYPE_ACK; + memcpy(addr, vf_mac, ETH_ALEN); + } else { + msgbuf[0] = E1000_VF_RESET | E1000_VT_MSGTYPE_NACK; + } igb_write_mbx(hw, msgbuf, 3, vf); } @@ -6203,16 +6273,8 @@ static irqreturn_t igb_intr_msi(int irq, void *data) mod_timer(&adapter->watchdog_timer, jiffies + 1); } - if (icr & E1000_ICR_TS) { - u32 tsicr = rd32(E1000_TSICR); - - if (tsicr & E1000_TSICR_TXTS) { - /* acknowledge the interrupt */ - wr32(E1000_TSICR, E1000_TSICR_TXTS); - /* retrieve hardware timestamp */ - schedule_work(&adapter->ptp_tx_work); - } - } + if (icr & E1000_ICR_TS) + igb_tsync_interrupt(adapter); napi_schedule(&q_vector->napi); @@ -6257,16 +6319,8 @@ static irqreturn_t igb_intr(int irq, void *data) mod_timer(&adapter->watchdog_timer, jiffies + 1); } - if (icr & E1000_ICR_TS) { - u32 tsicr = rd32(E1000_TSICR); - - if (tsicr & E1000_TSICR_TXTS) { - /* acknowledge the interrupt */ - wr32(E1000_TSICR, E1000_TSICR_TXTS); - /* retrieve hardware timestamp */ - schedule_work(&adapter->ptp_tx_work); - } - } + if (icr & E1000_ICR_TS) + igb_tsync_interrupt(adapter); napi_schedule(&q_vector->napi); @@ -6527,15 +6581,17 @@ static void igb_reuse_rx_page(struct igb_ring *rx_ring, DMA_FROM_DEVICE); } +static inline bool igb_page_is_reserved(struct page *page) +{ + return (page_to_nid(page) != numa_mem_id()) || page->pfmemalloc; +} + static bool igb_can_reuse_rx_page(struct igb_rx_buffer *rx_buffer, struct page *page, unsigned int truesize) { /* avoid re-using remote pages */ - if (unlikely(page_to_nid(page) != numa_node_id())) - return false; - - if (unlikely(page->pfmemalloc)) + if (unlikely(igb_page_is_reserved(page))) return false; #if (PAGE_SIZE < 8192) @@ -6545,22 +6601,19 @@ static bool igb_can_reuse_rx_page(struct igb_rx_buffer *rx_buffer, /* flip page offset to other buffer */ rx_buffer->page_offset ^= IGB_RX_BUFSZ; - - /* Even if we own the page, we are not allowed to use atomic_set() - * This would break get_page_unless_zero() users. - */ - atomic_inc(&page->_count); #else /* move offset up to the next cache line */ rx_buffer->page_offset += truesize; if (rx_buffer->page_offset > (PAGE_SIZE - IGB_RX_BUFSZ)) return false; - - /* bump ref count on page before it is given to the stack */ - get_page(page); #endif + /* Even if we own the page, we are not allowed to use atomic_set() + * This would break get_page_unless_zero() users. + */ + atomic_inc(&page->_count); + return true; } @@ -6603,13 +6656,12 @@ static bool igb_add_rx_frag(struct igb_ring *rx_ring, memcpy(__skb_put(skb, size), va, ALIGN(size, sizeof(long))); - /* we can reuse buffer as-is, just make sure it is local */ - if (likely((page_to_nid(page) == numa_node_id()) && - !page->pfmemalloc)) + /* page is not reserved, we can reuse buffer as-is */ + if (likely(!igb_page_is_reserved(page))) return true; /* this page cannot be reused so discard it */ - put_page(page); + __free_page(page); return false; } @@ -6627,7 +6679,6 @@ static struct sk_buff *igb_fetch_rx_buffer(struct igb_ring *rx_ring, struct page *page; rx_buffer = &rx_ring->rx_buffer_info[rx_ring->next_to_clean]; - page = rx_buffer->page; prefetchw(page); @@ -7042,8 +7093,8 @@ void igb_alloc_rx_buffers(struct igb_ring *rx_ring, u16 cleaned_count) i -= rx_ring->count; } - /* clear the hdr_addr for the next_to_use descriptor */ - rx_desc->read.hdr_addr = 0; + /* clear the status bits for the next_to_use descriptor */ + rx_desc->wb.upper.status_error = 0; cleaned_count--; } while (cleaned_count); diff --git a/drivers/net/ethernet/intel/igb/igb_ptp.c b/drivers/net/ethernet/intel/igb/igb_ptp.c index 794c139f0cc0..d20fc8ed11f1 100644 --- a/drivers/net/ethernet/intel/igb/igb_ptp.c +++ b/drivers/net/ethernet/intel/igb/igb_ptp.c @@ -256,14 +256,9 @@ static int igb_ptp_adjtime_82576(struct ptp_clock_info *ptp, s64 delta) struct igb_adapter *igb = container_of(ptp, struct igb_adapter, ptp_caps); unsigned long flags; - s64 now; spin_lock_irqsave(&igb->tmreg_lock, flags); - - now = timecounter_read(&igb->tc); - now += delta; - timecounter_init(&igb->tc, &igb->cc, now); - + timecounter_adjtime(&igb->tc, delta); spin_unlock_irqrestore(&igb->tmreg_lock, flags); return 0; @@ -360,12 +355,239 @@ static int igb_ptp_settime_i210(struct ptp_clock_info *ptp, return 0; } +static void igb_pin_direction(int pin, int input, u32 *ctrl, u32 *ctrl_ext) +{ + u32 *ptr = pin < 2 ? ctrl : ctrl_ext; + u32 mask[IGB_N_SDP] = { + E1000_CTRL_SDP0_DIR, + E1000_CTRL_SDP1_DIR, + E1000_CTRL_EXT_SDP2_DIR, + E1000_CTRL_EXT_SDP3_DIR, + }; + + if (input) + *ptr &= ~mask[pin]; + else + *ptr |= mask[pin]; +} + +static void igb_pin_extts(struct igb_adapter *igb, int chan, int pin) +{ + struct e1000_hw *hw = &igb->hw; + u32 aux0_sel_sdp[IGB_N_SDP] = { + AUX0_SEL_SDP0, AUX0_SEL_SDP1, AUX0_SEL_SDP2, AUX0_SEL_SDP3, + }; + u32 aux1_sel_sdp[IGB_N_SDP] = { + AUX1_SEL_SDP0, AUX1_SEL_SDP1, AUX1_SEL_SDP2, AUX1_SEL_SDP3, + }; + u32 ts_sdp_en[IGB_N_SDP] = { + TS_SDP0_EN, TS_SDP1_EN, TS_SDP2_EN, TS_SDP3_EN, + }; + u32 ctrl, ctrl_ext, tssdp = 0; + + ctrl = rd32(E1000_CTRL); + ctrl_ext = rd32(E1000_CTRL_EXT); + tssdp = rd32(E1000_TSSDP); + + igb_pin_direction(pin, 1, &ctrl, &ctrl_ext); + + /* Make sure this pin is not enabled as an output. */ + tssdp &= ~ts_sdp_en[pin]; + + if (chan == 1) { + tssdp &= ~AUX1_SEL_SDP3; + tssdp |= aux1_sel_sdp[pin] | AUX1_TS_SDP_EN; + } else { + tssdp &= ~AUX0_SEL_SDP3; + tssdp |= aux0_sel_sdp[pin] | AUX0_TS_SDP_EN; + } + + wr32(E1000_TSSDP, tssdp); + wr32(E1000_CTRL, ctrl); + wr32(E1000_CTRL_EXT, ctrl_ext); +} + +static void igb_pin_perout(struct igb_adapter *igb, int chan, int pin) +{ + struct e1000_hw *hw = &igb->hw; + u32 aux0_sel_sdp[IGB_N_SDP] = { + AUX0_SEL_SDP0, AUX0_SEL_SDP1, AUX0_SEL_SDP2, AUX0_SEL_SDP3, + }; + u32 aux1_sel_sdp[IGB_N_SDP] = { + AUX1_SEL_SDP0, AUX1_SEL_SDP1, AUX1_SEL_SDP2, AUX1_SEL_SDP3, + }; + u32 ts_sdp_en[IGB_N_SDP] = { + TS_SDP0_EN, TS_SDP1_EN, TS_SDP2_EN, TS_SDP3_EN, + }; + u32 ts_sdp_sel_tt0[IGB_N_SDP] = { + TS_SDP0_SEL_TT0, TS_SDP1_SEL_TT0, + TS_SDP2_SEL_TT0, TS_SDP3_SEL_TT0, + }; + u32 ts_sdp_sel_tt1[IGB_N_SDP] = { + TS_SDP0_SEL_TT1, TS_SDP1_SEL_TT1, + TS_SDP2_SEL_TT1, TS_SDP3_SEL_TT1, + }; + u32 ts_sdp_sel_clr[IGB_N_SDP] = { + TS_SDP0_SEL_FC1, TS_SDP1_SEL_FC1, + TS_SDP2_SEL_FC1, TS_SDP3_SEL_FC1, + }; + u32 ctrl, ctrl_ext, tssdp = 0; + + ctrl = rd32(E1000_CTRL); + ctrl_ext = rd32(E1000_CTRL_EXT); + tssdp = rd32(E1000_TSSDP); + + igb_pin_direction(pin, 0, &ctrl, &ctrl_ext); + + /* Make sure this pin is not enabled as an input. */ + if ((tssdp & AUX0_SEL_SDP3) == aux0_sel_sdp[pin]) + tssdp &= ~AUX0_TS_SDP_EN; + + if ((tssdp & AUX1_SEL_SDP3) == aux1_sel_sdp[pin]) + tssdp &= ~AUX1_TS_SDP_EN; + + tssdp &= ~ts_sdp_sel_clr[pin]; + if (chan == 1) + tssdp |= ts_sdp_sel_tt1[pin]; + else + tssdp |= ts_sdp_sel_tt0[pin]; + + tssdp |= ts_sdp_en[pin]; + + wr32(E1000_TSSDP, tssdp); + wr32(E1000_CTRL, ctrl); + wr32(E1000_CTRL_EXT, ctrl_ext); +} + +static int igb_ptp_feature_enable_i210(struct ptp_clock_info *ptp, + struct ptp_clock_request *rq, int on) +{ + struct igb_adapter *igb = + container_of(ptp, struct igb_adapter, ptp_caps); + struct e1000_hw *hw = &igb->hw; + u32 tsauxc, tsim, tsauxc_mask, tsim_mask, trgttiml, trgttimh; + unsigned long flags; + struct timespec ts; + int pin; + s64 ns; + + switch (rq->type) { + case PTP_CLK_REQ_EXTTS: + if (on) { + pin = ptp_find_pin(igb->ptp_clock, PTP_PF_EXTTS, + rq->extts.index); + if (pin < 0) + return -EBUSY; + } + if (rq->extts.index == 1) { + tsauxc_mask = TSAUXC_EN_TS1; + tsim_mask = TSINTR_AUTT1; + } else { + tsauxc_mask = TSAUXC_EN_TS0; + tsim_mask = TSINTR_AUTT0; + } + spin_lock_irqsave(&igb->tmreg_lock, flags); + tsauxc = rd32(E1000_TSAUXC); + tsim = rd32(E1000_TSIM); + if (on) { + igb_pin_extts(igb, rq->extts.index, pin); + tsauxc |= tsauxc_mask; + tsim |= tsim_mask; + } else { + tsauxc &= ~tsauxc_mask; + tsim &= ~tsim_mask; + } + wr32(E1000_TSAUXC, tsauxc); + wr32(E1000_TSIM, tsim); + spin_unlock_irqrestore(&igb->tmreg_lock, flags); + return 0; + + case PTP_CLK_REQ_PEROUT: + if (on) { + pin = ptp_find_pin(igb->ptp_clock, PTP_PF_PEROUT, + rq->perout.index); + if (pin < 0) + return -EBUSY; + } + ts.tv_sec = rq->perout.period.sec; + ts.tv_nsec = rq->perout.period.nsec; + ns = timespec_to_ns(&ts); + ns = ns >> 1; + if (on && ns < 500000LL) { + /* 2k interrupts per second is an awful lot. */ + return -EINVAL; + } + ts = ns_to_timespec(ns); + if (rq->perout.index == 1) { + tsauxc_mask = TSAUXC_EN_TT1; + tsim_mask = TSINTR_TT1; + trgttiml = E1000_TRGTTIML1; + trgttimh = E1000_TRGTTIMH1; + } else { + tsauxc_mask = TSAUXC_EN_TT0; + tsim_mask = TSINTR_TT0; + trgttiml = E1000_TRGTTIML0; + trgttimh = E1000_TRGTTIMH0; + } + spin_lock_irqsave(&igb->tmreg_lock, flags); + tsauxc = rd32(E1000_TSAUXC); + tsim = rd32(E1000_TSIM); + if (on) { + int i = rq->perout.index; + + igb_pin_perout(igb, i, pin); + igb->perout[i].start.tv_sec = rq->perout.start.sec; + igb->perout[i].start.tv_nsec = rq->perout.start.nsec; + igb->perout[i].period.tv_sec = ts.tv_sec; + igb->perout[i].period.tv_nsec = ts.tv_nsec; + wr32(trgttiml, rq->perout.start.sec); + wr32(trgttimh, rq->perout.start.nsec); + tsauxc |= tsauxc_mask; + tsim |= tsim_mask; + } else { + tsauxc &= ~tsauxc_mask; + tsim &= ~tsim_mask; + } + wr32(E1000_TSAUXC, tsauxc); + wr32(E1000_TSIM, tsim); + spin_unlock_irqrestore(&igb->tmreg_lock, flags); + return 0; + + case PTP_CLK_REQ_PPS: + spin_lock_irqsave(&igb->tmreg_lock, flags); + tsim = rd32(E1000_TSIM); + if (on) + tsim |= TSINTR_SYS_WRAP; + else + tsim &= ~TSINTR_SYS_WRAP; + wr32(E1000_TSIM, tsim); + spin_unlock_irqrestore(&igb->tmreg_lock, flags); + return 0; + } + + return -EOPNOTSUPP; +} + static int igb_ptp_feature_enable(struct ptp_clock_info *ptp, struct ptp_clock_request *rq, int on) { return -EOPNOTSUPP; } +static int igb_ptp_verify_pin(struct ptp_clock_info *ptp, unsigned int pin, + enum ptp_pin_function func, unsigned int chan) +{ + switch (func) { + case PTP_PF_NONE: + case PTP_PF_EXTTS: + case PTP_PF_PEROUT: + break; + case PTP_PF_PHYSYNC: + return -1; + } + return 0; +} + /** * igb_ptp_tx_work * @work: pointer to work struct @@ -756,6 +978,7 @@ void igb_ptp_init(struct igb_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; struct net_device *netdev = adapter->netdev; + int i; switch (hw->mac.type) { case e1000_82576: @@ -770,7 +993,7 @@ void igb_ptp_init(struct igb_adapter *adapter) adapter->ptp_caps.settime = igb_ptp_settime_82576; adapter->ptp_caps.enable = igb_ptp_feature_enable; adapter->cc.read = igb_ptp_read_82576; - adapter->cc.mask = CLOCKSOURCE_MASK(64); + adapter->cc.mask = CYCLECOUNTER_MASK(64); adapter->cc.mult = 1; adapter->cc.shift = IGB_82576_TSYNC_SHIFT; /* Dial the nominal frequency. */ @@ -790,7 +1013,7 @@ void igb_ptp_init(struct igb_adapter *adapter) adapter->ptp_caps.settime = igb_ptp_settime_82576; adapter->ptp_caps.enable = igb_ptp_feature_enable; adapter->cc.read = igb_ptp_read_82580; - adapter->cc.mask = CLOCKSOURCE_MASK(IGB_NBITS_82580); + adapter->cc.mask = CYCLECOUNTER_MASK(IGB_NBITS_82580); adapter->cc.mult = 1; adapter->cc.shift = 0; /* Enable the timer functions by clearing bit 31. */ @@ -798,16 +1021,27 @@ void igb_ptp_init(struct igb_adapter *adapter) break; case e1000_i210: case e1000_i211: + for (i = 0; i < IGB_N_SDP; i++) { + struct ptp_pin_desc *ppd = &adapter->sdp_config[i]; + + snprintf(ppd->name, sizeof(ppd->name), "SDP%d", i); + ppd->index = i; + ppd->func = PTP_PF_NONE; + } snprintf(adapter->ptp_caps.name, 16, "%pm", netdev->dev_addr); adapter->ptp_caps.owner = THIS_MODULE; adapter->ptp_caps.max_adj = 62499999; - adapter->ptp_caps.n_ext_ts = 0; - adapter->ptp_caps.pps = 0; + adapter->ptp_caps.n_ext_ts = IGB_N_EXTTS; + adapter->ptp_caps.n_per_out = IGB_N_PEROUT; + adapter->ptp_caps.n_pins = IGB_N_SDP; + adapter->ptp_caps.pps = 1; + adapter->ptp_caps.pin_config = adapter->sdp_config; adapter->ptp_caps.adjfreq = igb_ptp_adjfreq_82580; adapter->ptp_caps.adjtime = igb_ptp_adjtime_i210; adapter->ptp_caps.gettime = igb_ptp_gettime_i210; adapter->ptp_caps.settime = igb_ptp_settime_i210; - adapter->ptp_caps.enable = igb_ptp_feature_enable; + adapter->ptp_caps.enable = igb_ptp_feature_enable_i210; + adapter->ptp_caps.verify = igb_ptp_verify_pin; /* Enable the timer functions by clearing bit 31. */ wr32(E1000_TSAUXC, 0x0); break; @@ -905,6 +1139,7 @@ void igb_ptp_stop(struct igb_adapter *adapter) void igb_ptp_reset(struct igb_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; + unsigned long flags; if (!(adapter->flags & IGB_FLAG_PTP)) return; @@ -912,6 +1147,8 @@ void igb_ptp_reset(struct igb_adapter *adapter) /* reset the tstamp_config */ igb_ptp_set_timestamp_mode(adapter, &adapter->tstamp_config); + spin_lock_irqsave(&adapter->tmreg_lock, flags); + switch (adapter->hw.mac.type) { case e1000_82576: /* Dial the nominal frequency. */ @@ -922,23 +1159,25 @@ void igb_ptp_reset(struct igb_adapter *adapter) case e1000_i350: case e1000_i210: case e1000_i211: - /* Enable the timer functions and interrupts. */ wr32(E1000_TSAUXC, 0x0); + wr32(E1000_TSSDP, 0x0); wr32(E1000_TSIM, TSYNC_INTERRUPTS); wr32(E1000_IMS, E1000_IMS_TS); break; default: /* No work to do. */ - return; + goto out; } /* Re-initialize the timer. */ if ((hw->mac.type == e1000_i210) || (hw->mac.type == e1000_i211)) { struct timespec ts = ktime_to_timespec(ktime_get_real()); - igb_ptp_settime_i210(&adapter->ptp_caps, &ts); + igb_ptp_write_i210(adapter, &ts); } else { timecounter_init(&adapter->tc, &adapter->cc, ktime_to_ns(ktime_get_real())); } +out: + spin_unlock_irqrestore(&adapter->tmreg_lock, flags); } diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index 63c807c9b21c..ebf9d4a42fdd 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -1907,7 +1907,8 @@ static void igbvf_watchdog_task(struct work_struct *work) static int igbvf_tso(struct igbvf_adapter *adapter, struct igbvf_ring *tx_ring, - struct sk_buff *skb, u32 tx_flags, u8 *hdr_len) + struct sk_buff *skb, u32 tx_flags, u8 *hdr_len, + __be16 protocol) { struct e1000_adv_tx_context_desc *context_desc; struct igbvf_buffer *buffer_info; @@ -1927,7 +1928,7 @@ static int igbvf_tso(struct igbvf_adapter *adapter, l4len = tcp_hdrlen(skb); *hdr_len += l4len; - if (skb->protocol == htons(ETH_P_IP)) { + if (protocol == htons(ETH_P_IP)) { struct iphdr *iph = ip_hdr(skb); iph->tot_len = 0; iph->check = 0; @@ -1958,7 +1959,7 @@ static int igbvf_tso(struct igbvf_adapter *adapter, /* ADV DTYP TUCMD MKRLOC/ISCSIHEDLEN */ tu_cmd |= (E1000_TXD_CMD_DEXT | E1000_ADVTXD_DTYP_CTXT); - if (skb->protocol == htons(ETH_P_IP)) + if (protocol == htons(ETH_P_IP)) tu_cmd |= E1000_ADVTXD_TUCMD_IPV4; tu_cmd |= E1000_ADVTXD_TUCMD_L4T_TCP; @@ -1984,7 +1985,8 @@ static int igbvf_tso(struct igbvf_adapter *adapter, static inline bool igbvf_tx_csum(struct igbvf_adapter *adapter, struct igbvf_ring *tx_ring, - struct sk_buff *skb, u32 tx_flags) + struct sk_buff *skb, u32 tx_flags, + __be16 protocol) { struct e1000_adv_tx_context_desc *context_desc; unsigned int i; @@ -2011,7 +2013,7 @@ static inline bool igbvf_tx_csum(struct igbvf_adapter *adapter, tu_cmd |= (E1000_TXD_CMD_DEXT | E1000_ADVTXD_DTYP_CTXT); if (skb->ip_summed == CHECKSUM_PARTIAL) { - switch (skb->protocol) { + switch (protocol) { case htons(ETH_P_IP): tu_cmd |= E1000_ADVTXD_TUCMD_IPV4; if (ip_hdr(skb)->protocol == IPPROTO_TCP) @@ -2211,6 +2213,7 @@ static netdev_tx_t igbvf_xmit_frame_ring_adv(struct sk_buff *skb, u8 hdr_len = 0; int count = 0; int tso = 0; + __be16 protocol = vlan_get_protocol(skb); if (test_bit(__IGBVF_DOWN, &adapter->state)) { dev_kfree_skb_any(skb); @@ -2234,18 +2237,19 @@ static netdev_tx_t igbvf_xmit_frame_ring_adv(struct sk_buff *skb, return NETDEV_TX_BUSY; } - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { tx_flags |= IGBVF_TX_FLAGS_VLAN; - tx_flags |= (vlan_tx_tag_get(skb) << IGBVF_TX_FLAGS_VLAN_SHIFT); + tx_flags |= (skb_vlan_tag_get(skb) << + IGBVF_TX_FLAGS_VLAN_SHIFT); } - if (skb->protocol == htons(ETH_P_IP)) + if (protocol == htons(ETH_P_IP)) tx_flags |= IGBVF_TX_FLAGS_IPV4; first = tx_ring->next_to_use; tso = skb_is_gso(skb) ? - igbvf_tso(adapter, tx_ring, skb, tx_flags, &hdr_len) : 0; + igbvf_tso(adapter, tx_ring, skb, tx_flags, &hdr_len, protocol) : 0; if (unlikely(tso < 0)) { dev_kfree_skb_any(skb); return NETDEV_TX_OK; @@ -2253,7 +2257,7 @@ static netdev_tx_t igbvf_xmit_frame_ring_adv(struct sk_buff *skb, if (tso) tx_flags |= IGBVF_TX_FLAGS_TSO; - else if (igbvf_tx_csum(adapter, tx_ring, skb, tx_flags) && + else if (igbvf_tx_csum(adapter, tx_ring, skb, tx_flags, protocol) && (skb->ip_summed == CHECKSUM_PARTIAL)) tx_flags |= IGBVF_TX_FLAGS_CSUM; diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_main.c b/drivers/net/ethernet/intel/ixgb/ixgb_main.c index aa87605b144a..11a1bdbe3fd9 100644 --- a/drivers/net/ethernet/intel/ixgb/ixgb_main.c +++ b/drivers/net/ethernet/intel/ixgb/ixgb_main.c @@ -1532,9 +1532,9 @@ ixgb_xmit_frame(struct sk_buff *skb, struct net_device *netdev) DESC_NEEDED))) return NETDEV_TX_BUSY; - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { tx_flags |= IXGB_TX_FLAGS_VLAN; - vlan_id = vlan_tx_tag_get(skb); + vlan_id = skb_vlan_tag_get(skb); } first = adapter->tx_ring.next_to_use; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index b6137be43920..7dcbbec09a70 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -38,7 +38,7 @@ #include <linux/if_vlan.h> #include <linux/jiffies.h> -#include <linux/clocksource.h> +#include <linux/timecounter.h> #include <linux/net_tstamp.h> #include <linux/ptp_clock_kernel.h> @@ -76,6 +76,8 @@ #define IXGBE_MAX_RXD 4096 #define IXGBE_MIN_RXD 64 +#define IXGBE_ETH_P_LLDP 0x88CC + /* flow control */ #define IXGBE_MIN_FCRTL 0x40 #define IXGBE_MAX_FCRTL 0x7FF80 @@ -753,6 +755,7 @@ struct ixgbe_adapter { u32 timer_event_accumulator; u32 vferr_refcount; struct ixgbe_mac_addr *mac_table; + u16 vxlan_port; struct kobject *info_kobj; #ifdef CONFIG_IXGBE_HWMON struct hwmon_buff *ixgbe_hwmon_buff; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 2ed2c7de2304..70cc4c5c0a01 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -50,6 +50,7 @@ #include <linux/if_bridge.h> #include <linux/prefetch.h> #include <scsi/fc/fc_fcoe.h> +#include <net/vxlan.h> #ifdef CONFIG_OF #include <linux/of_net.h> @@ -1396,12 +1397,23 @@ static inline void ixgbe_rx_checksum(struct ixgbe_ring *ring, union ixgbe_adv_rx_desc *rx_desc, struct sk_buff *skb) { + __le16 pkt_info = rx_desc->wb.lower.lo_dword.hs_rss.pkt_info; + __le16 hdr_info = rx_desc->wb.lower.lo_dword.hs_rss.hdr_info; + bool encap_pkt = false; + skb_checksum_none_assert(skb); /* Rx csum disabled */ if (!(ring->netdev->features & NETIF_F_RXCSUM)) return; + if ((pkt_info & cpu_to_le16(IXGBE_RXDADV_PKTTYPE_VXLAN)) && + (hdr_info & cpu_to_le16(IXGBE_RXDADV_PKTTYPE_TUNNEL >> 16))) { + encap_pkt = true; + skb->encapsulation = 1; + skb->ip_summed = CHECKSUM_NONE; + } + /* if IP and error */ if (ixgbe_test_staterr(rx_desc, IXGBE_RXD_STAT_IPCS) && ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_ERR_IPE)) { @@ -1413,8 +1425,6 @@ static inline void ixgbe_rx_checksum(struct ixgbe_ring *ring, return; if (ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_ERR_TCPE)) { - __le16 pkt_info = rx_desc->wb.lower.lo_dword.hs_rss.pkt_info; - /* * 82599 errata, UDP frames with a 0 checksum can be marked as * checksum errors. @@ -1429,6 +1439,17 @@ static inline void ixgbe_rx_checksum(struct ixgbe_ring *ring, /* It must be a TCP or UDP packet with a valid checksum */ skb->ip_summed = CHECKSUM_UNNECESSARY; + if (encap_pkt) { + if (!ixgbe_test_staterr(rx_desc, IXGBE_RXD_STAT_OUTERIPCS)) + return; + + if (ixgbe_test_staterr(rx_desc, IXGBE_RXDADV_ERR_OUTERIPER)) { + ring->rx_stats.csum_err++; + return; + } + /* If we checked the outer header let the stack know */ + skb->csum_level = 1; + } } static bool ixgbe_alloc_mapped_page(struct ixgbe_ring *rx_ring, @@ -3564,10 +3585,24 @@ static void ixgbe_configure_virtualization(struct ixgbe_adapter *adapter) /* Enable MAC Anti-Spoofing */ hw->mac.ops.set_mac_anti_spoofing(hw, (adapter->num_vfs != 0), adapter->num_vfs); + + /* Ensure LLDP is set for Ethertype Antispoofing if we will be + * calling set_ethertype_anti_spoofing for each VF in loop below + */ + if (hw->mac.ops.set_ethertype_anti_spoofing) + IXGBE_WRITE_REG(hw, IXGBE_ETQF(IXGBE_ETQF_FILTER_LLDP), + (IXGBE_ETQF_FILTER_EN | /* enable filter */ + IXGBE_ETQF_TX_ANTISPOOF | /* tx antispoof */ + IXGBE_ETH_P_LLDP)); /* LLDP eth type */ + /* For VFs that have spoof checking turned off */ for (i = 0; i < adapter->num_vfs; i++) { if (!adapter->vfinfo[i].spoofchk_enabled) ixgbe_ndo_set_vf_spoofchk(adapter->netdev, i, false); + + /* enable ethertype anti spoofing if hw supports it */ + if (hw->mac.ops.set_ethertype_anti_spoofing) + hw->mac.ops.set_ethertype_anti_spoofing(hw, true, i); } } @@ -5627,6 +5662,10 @@ static int ixgbe_open(struct net_device *netdev) ixgbe_up_complete(adapter); +#if IS_ENABLED(CONFIG_IXGBE_VXLAN) + vxlan_get_rx_port(netdev); + +#endif return 0; err_set_queues: @@ -7217,8 +7256,8 @@ netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb, first->gso_segs = 1; /* if we have a HW VLAN tag being added default to the HW one */ - if (vlan_tx_tag_present(skb)) { - tx_flags |= vlan_tx_tag_get(skb) << IXGBE_TX_FLAGS_VLAN_SHIFT; + if (skb_vlan_tag_present(skb)) { + tx_flags |= skb_vlan_tag_get(skb) << IXGBE_TX_FLAGS_VLAN_SHIFT; tx_flags |= IXGBE_TX_FLAGS_HW_VLAN; /* else if it is a SW VLAN check the next protocol and store the tag */ } else if (protocol == htons(ETH_P_8021Q)) { @@ -7227,11 +7266,11 @@ netdev_tx_t ixgbe_xmit_frame_ring(struct sk_buff *skb, if (!vhdr) goto out_drop; - protocol = vhdr->h_vlan_encapsulated_proto; tx_flags |= ntohs(vhdr->h_vlan_TCI) << IXGBE_TX_FLAGS_VLAN_SHIFT; tx_flags |= IXGBE_TX_FLAGS_SW_VLAN; } + protocol = vlan_get_protocol(skb); if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && adapter->ptp_clock && @@ -7771,6 +7810,64 @@ static int ixgbe_set_features(struct net_device *netdev, return 0; } +/** + * ixgbe_add_vxlan_port - Get notifications about VXLAN ports that come up + * @dev: The port's netdev + * @sa_family: Socket Family that VXLAN is notifiying us about + * @port: New UDP port number that VXLAN started listening to + **/ +static void ixgbe_add_vxlan_port(struct net_device *dev, sa_family_t sa_family, + __be16 port) +{ + struct ixgbe_adapter *adapter = netdev_priv(dev); + struct ixgbe_hw *hw = &adapter->hw; + u16 new_port = ntohs(port); + + if (sa_family == AF_INET6) + return; + + if (adapter->vxlan_port == new_port) { + netdev_info(dev, "Port %d already offloaded\n", new_port); + return; + } + + if (adapter->vxlan_port) { + netdev_info(dev, + "Hit Max num of UDP ports, not adding port %d\n", + new_port); + return; + } + + adapter->vxlan_port = new_port; + IXGBE_WRITE_REG(hw, IXGBE_VXLANCTRL, new_port); +} + +/** + * ixgbe_del_vxlan_port - Get notifications about VXLAN ports that go away + * @dev: The port's netdev + * @sa_family: Socket Family that VXLAN is notifying us about + * @port: UDP port number that VXLAN stopped listening to + **/ +static void ixgbe_del_vxlan_port(struct net_device *dev, sa_family_t sa_family, + __be16 port) +{ + struct ixgbe_adapter *adapter = netdev_priv(dev); + struct ixgbe_hw *hw = &adapter->hw; + u16 new_port = ntohs(port); + + if (sa_family == AF_INET6) + return; + + if (adapter->vxlan_port != new_port) { + netdev_info(dev, "Port %d was not found, not deleting\n", + new_port); + return; + } + + adapter->vxlan_port = 0; + IXGBE_WRITE_REG(hw, IXGBE_VXLANCTRL, 0); +} + static int ixgbe_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], struct net_device *dev, const unsigned char *addr, u16 vid, @@ -7786,7 +7883,7 @@ static int ixgbe_ndo_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], } static int ixgbe_ndo_bridge_setlink(struct net_device *dev, - struct nlmsghdr *nlh) + struct nlmsghdr *nlh, u16 flags) { struct ixgbe_adapter *adapter = netdev_priv(dev); struct nlattr *attr, *br_spec; @@ -7982,6 +8079,8 @@ static const struct net_device_ops ixgbe_netdev_ops = { .ndo_bridge_getlink = ixgbe_ndo_bridge_getlink, .ndo_dfwd_add_station = ixgbe_fwd_add, .ndo_dfwd_del_station = ixgbe_fwd_del, + .ndo_add_vxlan_port = ixgbe_add_vxlan_port, + .ndo_del_vxlan_port = ixgbe_del_vxlan_port, }; /** @@ -8339,6 +8438,15 @@ skip_sriov: netdev->priv_flags |= IFF_UNICAST_FLT; netdev->priv_flags |= IFF_SUPP_NOFCS; + switch (adapter->hw.mac.type) { + case ixgbe_mac_X550: + case ixgbe_mac_X550EM_x: + netdev->hw_enc_features |= NETIF_F_RXCSUM; + break; + default: + break; + } + #ifdef CONFIG_IXGBE_DCB netdev->dcbnl_ops = &dcbnl_ops; #endif diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c index 5fd4b5271f9a..79c00f57d3e7 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c @@ -261,18 +261,9 @@ static int ixgbe_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) struct ixgbe_adapter *adapter = container_of(ptp, struct ixgbe_adapter, ptp_caps); unsigned long flags; - u64 now; spin_lock_irqsave(&adapter->tmreg_lock, flags); - - now = timecounter_read(&adapter->tc); - now += delta; - - /* reset the timecounter */ - timecounter_init(&adapter->tc, - &adapter->cc, - now); - + timecounter_adjtime(&adapter->tc, delta); spin_unlock_irqrestore(&adapter->tmreg_lock, flags); ixgbe_ptp_setup_sdp(adapter); @@ -802,7 +793,7 @@ void ixgbe_ptp_start_cyclecounter(struct ixgbe_adapter *adapter) memset(&adapter->cc, 0, sizeof(adapter->cc)); adapter->cc.read = ixgbe_ptp_read; - adapter->cc.mask = CLOCKSOURCE_MASK(64); + adapter->cc.mask = CYCLECOUNTER_MASK(64); adapter->cc.shift = shift; adapter->cc.mult = 1; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index c76ba90ecc6e..7f37fe7269a7 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -101,9 +101,6 @@ static int __ixgbe_enable_sriov(struct ixgbe_adapter *adapter) adapter->dcb_cfg.num_tcs.pfc_tcs = 1; } - /* We do not support RSS w/ SR-IOV */ - adapter->ring_feature[RING_F_RSS].limit = 1; - /* Disable RSC when in SR-IOV mode */ adapter->flags2 &= ~(IXGBE_FLAG2_RSC_CAPABLE | IXGBE_FLAG2_RSC_ENABLED); @@ -1097,14 +1094,12 @@ static int ixgbe_enable_port_vlan(struct ixgbe_adapter *adapter, int vf, u16 vlan, u8 qos) { struct ixgbe_hw *hw = &adapter->hw; - int err = 0; + int err; - if (adapter->vfinfo[vf].pf_vlan) - err = ixgbe_set_vf_vlan(adapter, false, - adapter->vfinfo[vf].pf_vlan, - vf); + err = ixgbe_set_vf_vlan(adapter, true, vlan, vf); if (err) goto out; + ixgbe_set_vmvir(adapter, vlan, qos, vf); ixgbe_set_vmolr(hw, vf, false); if (adapter->vfinfo[vf].spoofchk_enabled) @@ -1143,6 +1138,11 @@ static int ixgbe_disable_port_vlan(struct ixgbe_adapter *adapter, int vf) hw->mac.ops.set_vlan_anti_spoofing(hw, false, vf); if (adapter->vfinfo[vf].vlan_count) adapter->vfinfo[vf].vlan_count--; + + /* disable hide VLAN on X550 */ + if (hw->mac.type >= ixgbe_mac_X550) + ixgbe_write_qde(adapter, vf, IXGBE_QDE_ENABLE); + adapter->vfinfo[vf].pf_vlan = 0; adapter->vfinfo[vf].pf_qos = 0; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index d101b25dc4b6..fc5ecee56ca8 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -378,6 +378,8 @@ struct ixgbe_thermal_sensor_data { #define IXGBE_SPOOF_MACAS_MASK 0xFF #define IXGBE_SPOOF_VLANAS_MASK 0xFF00 #define IXGBE_SPOOF_VLANAS_SHIFT 8 +#define IXGBE_SPOOF_ETHERTYPEAS 0xFF000000 +#define IXGBE_SPOOF_ETHERTYPEAS_SHIFT 16 #define IXGBE_PFVFSPOOF_REG_COUNT 8 #define IXGBE_DCA_TXCTRL(_i) (0x07200 + ((_i) * 4)) /* 16 of these (0-15) */ @@ -399,6 +401,7 @@ struct ixgbe_thermal_sensor_data { #define IXGBE_WUPL 0x05900 #define IXGBE_WUPM 0x05A00 /* wake up pkt memory 0x5A00-0x5A7C */ +#define IXGBE_VXLANCTRL 0x0000507C /* Rx filter VXLAN UDPPORT Register */ #define IXGBE_FHFT(_n) (0x09000 + ((_n) * 0x100)) /* Flex host filter table */ #define IXGBE_FHFT_EXT(_n) (0x09800 + ((_n) * 0x100)) /* Ext Flexible Host * Filter Table */ @@ -1540,6 +1543,7 @@ enum { #define IXGBE_MAX_ETQF_FILTERS 8 #define IXGBE_ETQF_FCOE 0x08000000 /* bit 27 */ #define IXGBE_ETQF_BCN 0x10000000 /* bit 28 */ +#define IXGBE_ETQF_TX_ANTISPOOF 0x20000000 /* bit 29 */ #define IXGBE_ETQF_1588 0x40000000 /* bit 30 */ #define IXGBE_ETQF_FILTER_EN 0x80000000 /* bit 31 */ #define IXGBE_ETQF_POOL_ENABLE (1 << 26) /* bit 26 */ @@ -1565,6 +1569,9 @@ enum { #define IXGBE_ETQF_FILTER_FCOE 2 #define IXGBE_ETQF_FILTER_1588 3 #define IXGBE_ETQF_FILTER_FIP 4 +#define IXGBE_ETQF_FILTER_LLDP 5 +#define IXGBE_ETQF_FILTER_LACP 6 + /* VLAN Control Bit Masks */ #define IXGBE_VLNCTRL_VET 0x0000FFFF /* bits 0-15 */ #define IXGBE_VLNCTRL_CFI 0x10000000 /* bit 28 */ @@ -2122,6 +2129,7 @@ enum { #define IXGBE_RXD_STAT_IPCS 0x40 /* IP xsum calculated */ #define IXGBE_RXD_STAT_PIF 0x80 /* passed in-exact filter */ #define IXGBE_RXD_STAT_CRCV 0x100 /* Speculative CRC Valid */ +#define IXGBE_RXD_STAT_OUTERIPCS 0x100 /* Cloud IP xsum calculated */ #define IXGBE_RXD_STAT_VEXT 0x200 /* 1st VLAN found */ #define IXGBE_RXD_STAT_UDPV 0x400 /* Valid UDP checksum */ #define IXGBE_RXD_STAT_DYNINT 0x800 /* Pkt caused INT via DYNINT */ @@ -2139,6 +2147,7 @@ enum { #define IXGBE_RXD_ERR_IPE 0x80 /* IP Checksum Error */ #define IXGBE_RXDADV_ERR_MASK 0xfff00000 /* RDESC.ERRORS mask */ #define IXGBE_RXDADV_ERR_SHIFT 20 /* RDESC.ERRORS shift */ +#define IXGBE_RXDADV_ERR_OUTERIPER 0x04000000 /* CRC IP Header error */ #define IXGBE_RXDADV_ERR_FCEOFE 0x80000000 /* FCoEFe/IPE */ #define IXGBE_RXDADV_ERR_FCERR 0x00700000 /* FCERR/FDIRERR */ #define IXGBE_RXDADV_ERR_FDIR_LEN 0x00100000 /* FDIR Length error */ @@ -2227,6 +2236,8 @@ enum { #define IXGBE_RXDADV_PKTTYPE_UDP 0x00000200 /* UDP hdr present */ #define IXGBE_RXDADV_PKTTYPE_SCTP 0x00000400 /* SCTP hdr present */ #define IXGBE_RXDADV_PKTTYPE_NFS 0x00000800 /* NFS hdr present */ +#define IXGBE_RXDADV_PKTTYPE_VXLAN 0x00000800 /* VXLAN hdr present */ +#define IXGBE_RXDADV_PKTTYPE_TUNNEL 0x00010000 /* Tunnel type */ #define IXGBE_RXDADV_PKTTYPE_IPSEC_ESP 0x00001000 /* IPSec ESP */ #define IXGBE_RXDADV_PKTTYPE_IPSEC_AH 0x00002000 /* IPSec AH */ #define IXGBE_RXDADV_PKTTYPE_LINKSEC 0x00004000 /* LinkSec Encap */ @@ -3056,6 +3067,7 @@ struct ixgbe_mac_operations { s32 (*set_fw_drv_ver)(struct ixgbe_hw *, u8, u8, u8, u8); s32 (*get_thermal_sensor_data)(struct ixgbe_hw *); s32 (*init_thermal_sensor_thresh)(struct ixgbe_hw *hw); + void (*set_ethertype_anti_spoofing)(struct ixgbe_hw *, bool, int); /* DMA Coalescing */ s32 (*dmac_config)(struct ixgbe_hw *hw); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c index ba54ff07b438..49395420c9b3 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x540.c @@ -55,9 +55,6 @@ s32 ixgbe_get_invariants_X540(struct ixgbe_hw *hw) { struct ixgbe_mac_info *mac = &hw->mac; - /* Call PHY identify routine to get the phy type */ - ixgbe_identify_phy_generic(hw); - mac->mcft_size = IXGBE_X540_MC_TBL_SIZE; mac->vft_size = IXGBE_X540_VFT_TBL_SIZE; mac->num_rar_entries = IXGBE_X540_RAR_ENTRIES; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index ffdd1231f419..50bf81908dd6 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -80,7 +80,7 @@ static s32 ixgbe_write_phy_reg_x550em(struct ixgbe_hw *hw, u32 reg_addr, * Initializes the EEPROM parameters ixgbe_eeprom_info within the * ixgbe_hw struct in order to set up EEPROM access. **/ -s32 ixgbe_init_eeprom_params_X550(struct ixgbe_hw *hw) +static s32 ixgbe_init_eeprom_params_X550(struct ixgbe_hw *hw) { struct ixgbe_eeprom_info *eeprom = &hw->eeprom; u32 eec; @@ -110,8 +110,8 @@ s32 ixgbe_init_eeprom_params_X550(struct ixgbe_hw *hw) * @device_type: 3 bit device type * @phy_data: Pointer to read data from the register **/ -s32 ixgbe_read_iosf_sb_reg_x550(struct ixgbe_hw *hw, u32 reg_addr, - u32 device_type, u32 *data) +static s32 ixgbe_read_iosf_sb_reg_x550(struct ixgbe_hw *hw, u32 reg_addr, + u32 device_type, u32 *data) { u32 i, command, error; @@ -158,7 +158,8 @@ s32 ixgbe_read_iosf_sb_reg_x550(struct ixgbe_hw *hw, u32 reg_addr, * * Reads a 16 bit word from the EEPROM using the hostif. **/ -s32 ixgbe_read_ee_hostif_data_X550(struct ixgbe_hw *hw, u16 offset, u16 *data) +static s32 ixgbe_read_ee_hostif_data_X550(struct ixgbe_hw *hw, u16 offset, + u16 *data) { s32 status; struct ixgbe_hic_read_shadow_ram buffer; @@ -193,8 +194,8 @@ s32 ixgbe_read_ee_hostif_data_X550(struct ixgbe_hw *hw, u16 offset, u16 *data) * * Reads a 16 bit word(s) from the EEPROM using the hostif. **/ -s32 ixgbe_read_ee_hostif_buffer_X550(struct ixgbe_hw *hw, - u16 offset, u16 words, u16 *data) +static s32 ixgbe_read_ee_hostif_buffer_X550(struct ixgbe_hw *hw, + u16 offset, u16 words, u16 *data) { struct ixgbe_hic_read_shadow_ram buffer; u32 current_word = 0; @@ -331,7 +332,8 @@ static s32 ixgbe_checksum_ptr_x550(struct ixgbe_hw *hw, u16 ptr, * * Returns a negative error code on error, or the 16-bit checksum **/ -s32 ixgbe_calc_checksum_X550(struct ixgbe_hw *hw, u16 *buffer, u32 buffer_size) +static s32 ixgbe_calc_checksum_X550(struct ixgbe_hw *hw, u16 *buffer, + u32 buffer_size) { u16 eeprom_ptrs[IXGBE_EEPROM_LAST_WORD + 1]; u16 *local_buffer; @@ -407,7 +409,7 @@ s32 ixgbe_calc_checksum_X550(struct ixgbe_hw *hw, u16 *buffer, u32 buffer_size) * * Returns a negative error code on error, or the 16-bit checksum **/ -s32 ixgbe_calc_eeprom_checksum_X550(struct ixgbe_hw *hw) +static s32 ixgbe_calc_eeprom_checksum_X550(struct ixgbe_hw *hw) { return ixgbe_calc_checksum_X550(hw, NULL, 0); } @@ -419,7 +421,7 @@ s32 ixgbe_calc_eeprom_checksum_X550(struct ixgbe_hw *hw) * * Reads a 16 bit word from the EEPROM using the hostif. **/ -s32 ixgbe_read_ee_hostif_X550(struct ixgbe_hw *hw, u16 offset, u16 *data) +static s32 ixgbe_read_ee_hostif_X550(struct ixgbe_hw *hw, u16 offset, u16 *data) { s32 status = 0; @@ -440,7 +442,8 @@ s32 ixgbe_read_ee_hostif_X550(struct ixgbe_hw *hw, u16 offset, u16 *data) * Performs checksum calculation and validates the EEPROM checksum. If the * caller does not need checksum_val, the value can be NULL. **/ -s32 ixgbe_validate_eeprom_checksum_X550(struct ixgbe_hw *hw, u16 *checksum_val) +static s32 ixgbe_validate_eeprom_checksum_X550(struct ixgbe_hw *hw, + u16 *checksum_val) { s32 status; u16 checksum; @@ -489,7 +492,8 @@ s32 ixgbe_validate_eeprom_checksum_X550(struct ixgbe_hw *hw, u16 *checksum_val) * * Write a 16 bit word to the EEPROM using the hostif. **/ -s32 ixgbe_write_ee_hostif_data_X550(struct ixgbe_hw *hw, u16 offset, u16 data) +static s32 ixgbe_write_ee_hostif_data_X550(struct ixgbe_hw *hw, u16 offset, + u16 data) { s32 status; struct ixgbe_hic_write_shadow_ram buffer; @@ -517,7 +521,7 @@ s32 ixgbe_write_ee_hostif_data_X550(struct ixgbe_hw *hw, u16 offset, u16 data) * * Write a 16 bit word to the EEPROM using the hostif. **/ -s32 ixgbe_write_ee_hostif_X550(struct ixgbe_hw *hw, u16 offset, u16 data) +static s32 ixgbe_write_ee_hostif_X550(struct ixgbe_hw *hw, u16 offset, u16 data) { s32 status = 0; @@ -537,7 +541,7 @@ s32 ixgbe_write_ee_hostif_X550(struct ixgbe_hw *hw, u16 offset, u16 data) * * Issue a shadow RAM dump to FW to copy EEPROM from shadow RAM to the flash. **/ -s32 ixgbe_update_flash_X550(struct ixgbe_hw *hw) +static s32 ixgbe_update_flash_X550(struct ixgbe_hw *hw) { s32 status = 0; union ixgbe_hic_hdr2 buffer; @@ -560,7 +564,7 @@ s32 ixgbe_update_flash_X550(struct ixgbe_hw *hw) * checksum and updates the EEPROM and instructs the hardware to update * the flash. **/ -s32 ixgbe_update_eeprom_checksum_X550(struct ixgbe_hw *hw) +static s32 ixgbe_update_eeprom_checksum_X550(struct ixgbe_hw *hw) { s32 status; u16 checksum = 0; @@ -600,8 +604,9 @@ s32 ixgbe_update_eeprom_checksum_X550(struct ixgbe_hw *hw) * * Write a 16 bit word(s) to the EEPROM using the hostif. **/ -s32 ixgbe_write_ee_hostif_buffer_X550(struct ixgbe_hw *hw, - u16 offset, u16 words, u16 *data) +static s32 ixgbe_write_ee_hostif_buffer_X550(struct ixgbe_hw *hw, + u16 offset, u16 words, + u16 *data) { s32 status = 0; u32 i = 0; @@ -630,7 +635,7 @@ s32 ixgbe_write_ee_hostif_buffer_X550(struct ixgbe_hw *hw, /** ixgbe_init_mac_link_ops_X550em - init mac link function pointers * @hw: pointer to hardware structure **/ -void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw) +static void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw) { struct ixgbe_mac_info *mac = &hw->mac; @@ -647,7 +652,7 @@ void ixgbe_init_mac_link_ops_X550em(struct ixgbe_hw *hw) /** ixgbe_setup_sfp_modules_X550em - Setup SFP module * @hw: pointer to hardware structure */ -s32 ixgbe_setup_sfp_modules_X550em(struct ixgbe_hw *hw) +static s32 ixgbe_setup_sfp_modules_X550em(struct ixgbe_hw *hw) { bool setup_linear; u16 reg_slice, edc_mode; @@ -703,9 +708,9 @@ s32 ixgbe_setup_sfp_modules_X550em(struct ixgbe_hw *hw) * @speed: pointer to link speed * @autoneg: true when autoneg or autotry is enabled **/ -s32 ixgbe_get_link_capabilities_X550em(struct ixgbe_hw *hw, - ixgbe_link_speed *speed, - bool *autoneg) +static s32 ixgbe_get_link_capabilities_X550em(struct ixgbe_hw *hw, + ixgbe_link_speed *speed, + bool *autoneg) { /* SFP */ if (hw->phy.media_type == ixgbe_media_type_fiber) { @@ -740,8 +745,8 @@ s32 ixgbe_get_link_capabilities_X550em(struct ixgbe_hw *hw, * @device_type: 3 bit device type * @data: Data to write to the register **/ -s32 ixgbe_write_iosf_sb_reg_x550(struct ixgbe_hw *hw, u32 reg_addr, - u32 device_type, u32 data) +static s32 ixgbe_write_iosf_sb_reg_x550(struct ixgbe_hw *hw, u32 reg_addr, + u32 device_type, u32 data) { u32 i, command, error; @@ -904,7 +909,7 @@ static s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed) * * Configures the integrated KX4 PHY. **/ -s32 ixgbe_setup_kx4_x550em(struct ixgbe_hw *hw) +static s32 ixgbe_setup_kx4_x550em(struct ixgbe_hw *hw) { s32 status; u32 reg_val; @@ -942,7 +947,7 @@ s32 ixgbe_setup_kx4_x550em(struct ixgbe_hw *hw) * * Configures the integrated KR PHY. **/ -s32 ixgbe_setup_kr_x550em(struct ixgbe_hw *hw) +static s32 ixgbe_setup_kr_x550em(struct ixgbe_hw *hw) { s32 status; u32 reg_val; @@ -987,7 +992,7 @@ s32 ixgbe_setup_kr_x550em(struct ixgbe_hw *hw) * A return of a non-zero value indicates an error, and the base driver should * not report link up. **/ -s32 ixgbe_setup_internal_phy_x550em(struct ixgbe_hw *hw) +static s32 ixgbe_setup_internal_phy_x550em(struct ixgbe_hw *hw) { u32 status; u16 lasi, autoneg_status, speed; @@ -1049,7 +1054,7 @@ s32 ixgbe_setup_internal_phy_x550em(struct ixgbe_hw *hw) * set during init_shared_code because the PHY/SFP type was * not known. Perform the SFP init if necessary. **/ -s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw) +static s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw) { struct ixgbe_phy_info *phy = &hw->phy; s32 ret_val; @@ -1102,7 +1107,7 @@ s32 ixgbe_init_phy_ops_X550em(struct ixgbe_hw *hw) * Returns the media type (fiber, copper, backplane) * */ -enum ixgbe_media_type ixgbe_get_media_type_X550em(struct ixgbe_hw *hw) +static enum ixgbe_media_type ixgbe_get_media_type_X550em(struct ixgbe_hw *hw) { enum ixgbe_media_type media_type; @@ -1129,7 +1134,7 @@ enum ixgbe_media_type ixgbe_get_media_type_X550em(struct ixgbe_hw *hw) /** ixgbe_init_ext_t_x550em - Start (unstall) the external Base T PHY. ** @hw: pointer to hardware structure **/ -s32 ixgbe_init_ext_t_x550em(struct ixgbe_hw *hw) +static s32 ixgbe_init_ext_t_x550em(struct ixgbe_hw *hw) { u32 status; u16 reg; @@ -1202,7 +1207,7 @@ s32 ixgbe_init_ext_t_x550em(struct ixgbe_hw *hw) ** and clears all interrupts, perform a PHY reset, and perform a link (MAC) ** reset. **/ -s32 ixgbe_reset_hw_X550em(struct ixgbe_hw *hw) +static s32 ixgbe_reset_hw_X550em(struct ixgbe_hw *hw) { ixgbe_link_speed link_speed; s32 status; @@ -1295,6 +1300,28 @@ mac_reset_top: return status; } +/** ixgbe_set_ethertype_anti_spoofing_X550 - Enable/Disable Ethertype + * anti-spoofing + * @hw: pointer to hardware structure + * @enable: enable or disable switch for Ethertype anti-spoofing + * @vf: Virtual Function pool - VF Pool to set for Ethertype anti-spoofing + **/ +void ixgbe_set_ethertype_anti_spoofing_X550(struct ixgbe_hw *hw, bool enable, + int vf) +{ + int vf_target_reg = vf >> 3; + int vf_target_shift = vf % 8 + IXGBE_SPOOF_ETHERTYPEAS_SHIFT; + u32 pfvfspoof; + + pfvfspoof = IXGBE_READ_REG(hw, IXGBE_PFVFSPOOF(vf_target_reg)); + if (enable) + pfvfspoof |= (1 << vf_target_shift); + else + pfvfspoof &= ~(1 << vf_target_shift); + + IXGBE_WRITE_REG(hw, IXGBE_PFVFSPOOF(vf_target_reg), pfvfspoof); +} + #define X550_COMMON_MAC \ .init_hw = &ixgbe_init_hw_generic, \ .start_hw = &ixgbe_start_hw_X540, \ @@ -1329,6 +1356,8 @@ mac_reset_top: .init_uta_tables = &ixgbe_init_uta_tables_generic, \ .set_mac_anti_spoofing = &ixgbe_set_mac_anti_spoofing, \ .set_vlan_anti_spoofing = &ixgbe_set_vlan_anti_spoofing, \ + .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, \ @@ -1345,7 +1374,6 @@ static struct ixgbe_mac_operations mac_ops_X550 = { .get_san_mac_addr = &ixgbe_get_san_mac_addr_generic, .get_wwn_prefix = &ixgbe_get_wwn_prefix_generic, .setup_link = &ixgbe_setup_mac_link_X540, - .set_rxpba = &ixgbe_set_rxpba_generic, .get_link_capabilities = &ixgbe_get_copper_link_capabilities_generic, .setup_sfp = NULL, }; diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h index 8c44ab25f3fa..3a9b356dff01 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf.h @@ -43,6 +43,13 @@ #define BP_EXTENDED_STATS #endif +#define IXGBE_MAX_TXD_PWR 14 +#define IXGBE_MAX_DATA_PER_TXD BIT(IXGBE_MAX_TXD_PWR) + +/* Tx Descriptors needed, worst case */ +#define TXD_USE_COUNT(S) DIV_ROUND_UP((S), IXGBE_MAX_DATA_PER_TXD) +#define DESC_NEEDED (MAX_SKB_FRAGS + 4) + /* wrapper around a pointer to a socket buffer, * so a DMA handle can be stored along with the buffer */ struct ixgbevf_tx_buffer { @@ -85,6 +92,18 @@ struct ixgbevf_rx_queue_stats { u64 csum_err; }; +enum ixgbevf_ring_state_t { + __IXGBEVF_TX_DETECT_HANG, + __IXGBEVF_HANG_CHECK_ARMED, +}; + +#define check_for_tx_hang(ring) \ + test_bit(__IXGBEVF_TX_DETECT_HANG, &(ring)->state) +#define set_check_for_tx_hang(ring) \ + set_bit(__IXGBEVF_TX_DETECT_HANG, &(ring)->state) +#define clear_check_for_tx_hang(ring) \ + clear_bit(__IXGBEVF_TX_DETECT_HANG, &(ring)->state) + struct ixgbevf_ring { struct ixgbevf_ring *next; struct net_device *netdev; @@ -101,7 +120,7 @@ struct ixgbevf_ring { struct ixgbevf_tx_buffer *tx_buffer_info; struct ixgbevf_rx_buffer *rx_buffer_info; }; - + unsigned long state; struct ixgbevf_stats stats; struct u64_stats_sync syncp; union { @@ -124,6 +143,7 @@ struct ixgbevf_ring { #define MAX_RX_QUEUES IXGBE_VF_MAX_RX_QUEUES #define MAX_TX_QUEUES IXGBE_VF_MAX_TX_QUEUES +#define IXGBEVF_MAX_RSS_QUEUES 2 #define IXGBEVF_DEFAULT_TXD 1024 #define IXGBEVF_DEFAULT_RXD 512 @@ -347,8 +367,6 @@ struct ixgbevf_adapter { /* this field must be first, see ixgbevf_process_skb_fields */ unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; - struct timer_list watchdog_timer; - struct work_struct reset_task; struct ixgbevf_q_vector *q_vector[MAX_MSIX_Q_VECTORS]; /* Interrupt Throttle Rate */ @@ -378,8 +396,7 @@ struct ixgbevf_adapter { * thus the additional *_CAPABLE flags. */ u32 flags; -#define IXGBE_FLAG_IN_WATCHDOG_TASK (u32)(1) - +#define IXGBEVF_FLAG_RESET_REQUESTED (u32)(1) #define IXGBEVF_FLAG_QUEUE_RESET_REQUESTED (u32)(1 << 2) struct msix_entry *msix_entries; @@ -415,9 +432,11 @@ struct ixgbevf_adapter { u32 link_speed; bool link_up; - spinlock_t mbx_lock; + struct timer_list service_timer; + struct work_struct service_task; - struct work_struct watchdog_task; + spinlock_t mbx_lock; + unsigned long last_reset; }; enum ixbgevf_state_t { @@ -426,7 +445,8 @@ enum ixbgevf_state_t { __IXGBEVF_DOWN, __IXGBEVF_DISABLED, __IXGBEVF_REMOVING, - __IXGBEVF_WORK_INIT, + __IXGBEVF_SERVICE_SCHED, + __IXGBEVF_SERVICE_INITED, }; enum ixgbevf_boards { diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index 62a0d8e0f17d..4186981e562d 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -98,6 +98,23 @@ static int debug = -1; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); +static void ixgbevf_service_event_schedule(struct ixgbevf_adapter *adapter) +{ + if (!test_bit(__IXGBEVF_DOWN, &adapter->state) && + !test_bit(__IXGBEVF_REMOVING, &adapter->state) && + !test_and_set_bit(__IXGBEVF_SERVICE_SCHED, &adapter->state)) + schedule_work(&adapter->service_task); +} + +static void ixgbevf_service_event_complete(struct ixgbevf_adapter *adapter) +{ + BUG_ON(!test_bit(__IXGBEVF_SERVICE_SCHED, &adapter->state)); + + /* flush memory to make sure state is correct before next watchdog */ + smp_mb__before_atomic(); + clear_bit(__IXGBEVF_SERVICE_SCHED, &adapter->state); +} + /* forward decls */ static void ixgbevf_queue_reset_subtask(struct ixgbevf_adapter *adapter); static void ixgbevf_set_itr(struct ixgbevf_q_vector *q_vector); @@ -111,8 +128,8 @@ static void ixgbevf_remove_adapter(struct ixgbe_hw *hw) return; hw->hw_addr = NULL; dev_err(&adapter->pdev->dev, "Adapter removed\n"); - if (test_bit(__IXGBEVF_WORK_INIT, &adapter->state)) - schedule_work(&adapter->watchdog_task); + if (test_bit(__IXGBEVF_SERVICE_INITED, &adapter->state)) + ixgbevf_service_event_schedule(adapter); } static void ixgbevf_check_remove(struct ixgbe_hw *hw, u32 reg) @@ -199,14 +216,72 @@ static void ixgbevf_unmap_and_free_tx_resource(struct ixgbevf_ring *tx_ring, /* tx_buffer must be completely set up in the transmit path */ } -#define IXGBE_MAX_TXD_PWR 14 -#define IXGBE_MAX_DATA_PER_TXD (1 << IXGBE_MAX_TXD_PWR) +static u64 ixgbevf_get_tx_completed(struct ixgbevf_ring *ring) +{ + return ring->stats.packets; +} + +static u32 ixgbevf_get_tx_pending(struct ixgbevf_ring *ring) +{ + struct ixgbevf_adapter *adapter = netdev_priv(ring->netdev); + struct ixgbe_hw *hw = &adapter->hw; + + u32 head = IXGBE_READ_REG(hw, IXGBE_VFTDH(ring->reg_idx)); + u32 tail = IXGBE_READ_REG(hw, IXGBE_VFTDT(ring->reg_idx)); + + if (head != tail) + return (head < tail) ? + tail - head : (tail + ring->count - head); + + return 0; +} + +static inline bool ixgbevf_check_tx_hang(struct ixgbevf_ring *tx_ring) +{ + u32 tx_done = ixgbevf_get_tx_completed(tx_ring); + u32 tx_done_old = tx_ring->tx_stats.tx_done_old; + u32 tx_pending = ixgbevf_get_tx_pending(tx_ring); + + 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. + */ + if ((tx_done_old == tx_done) && tx_pending) { + /* make sure it is true for two checks in a row */ + return test_and_set_bit(__IXGBEVF_HANG_CHECK_ARMED, + &tx_ring->state); + } + /* reset the countdown */ + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &tx_ring->state); + + /* update completed stats and continue */ + tx_ring->tx_stats.tx_done_old = tx_done; + + return false; +} + +static void ixgbevf_tx_timeout_reset(struct ixgbevf_adapter *adapter) +{ + /* Do the reset outside of interrupt context */ + if (!test_bit(__IXGBEVF_DOWN, &adapter->state)) { + adapter->flags |= IXGBEVF_FLAG_RESET_REQUESTED; + ixgbevf_service_event_schedule(adapter); + } +} -/* Tx Descriptors needed, worst case */ -#define TXD_USE_COUNT(S) DIV_ROUND_UP((S), IXGBE_MAX_DATA_PER_TXD) -#define DESC_NEEDED (MAX_SKB_FRAGS + 4) +/** + * ixgbevf_tx_timeout - Respond to a Tx Hang + * @netdev: network interface device structure + **/ +static void ixgbevf_tx_timeout(struct net_device *netdev) +{ + struct ixgbevf_adapter *adapter = netdev_priv(netdev); -static void ixgbevf_tx_timeout(struct net_device *netdev); + ixgbevf_tx_timeout_reset(adapter); +} /** * ixgbevf_clean_tx_irq - Reclaim resources after transmit completes @@ -311,6 +386,37 @@ static bool ixgbevf_clean_tx_irq(struct ixgbevf_q_vector *q_vector, q_vector->tx.total_bytes += total_bytes; q_vector->tx.total_packets += total_packets; + if (check_for_tx_hang(tx_ring) && ixgbevf_check_tx_hang(tx_ring)) { + struct ixgbe_hw *hw = &adapter->hw; + union ixgbe_adv_tx_desc *eop_desc; + + eop_desc = tx_ring->tx_buffer_info[i].next_to_watch; + + pr_err("Detected Tx Unit Hang\n" + " Tx Queue <%d>\n" + " TDH, TDT <%x>, <%x>\n" + " next_to_use <%x>\n" + " next_to_clean <%x>\n" + "tx_buffer_info[next_to_clean]\n" + " next_to_watch <%p>\n" + " eop_desc->wb.status <%x>\n" + " time_stamp <%lx>\n" + " jiffies <%lx>\n", + tx_ring->queue_index, + IXGBE_READ_REG(hw, IXGBE_VFTDH(tx_ring->reg_idx)), + IXGBE_READ_REG(hw, IXGBE_VFTDT(tx_ring->reg_idx)), + tx_ring->next_to_use, i, + eop_desc, (eop_desc ? eop_desc->wb.status : 0), + tx_ring->tx_buffer_info[i].time_stamp, jiffies); + + netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index); + + /* schedule immediate reset if we believe we hung */ + ixgbevf_tx_timeout_reset(adapter); + + return true; + } + #define TX_WAKE_THRESHOLD (DESC_NEEDED * 2) if (unlikely(total_packets && netif_carrier_ok(tx_ring->netdev) && (ixgbevf_desc_unused(tx_ring) >= TX_WAKE_THRESHOLD))) { @@ -1158,9 +1264,7 @@ static irqreturn_t ixgbevf_msix_other(int irq, void *data) hw->mac.get_link_status = 1; - if (!test_bit(__IXGBEVF_DOWN, &adapter->state) && - !test_bit(__IXGBEVF_REMOVING, &adapter->state)) - mod_timer(&adapter->watchdog_timer, jiffies); + ixgbevf_service_event_schedule(adapter); IXGBE_WRITE_REG(hw, IXGBE_VTEIMS, adapter->eims_other); @@ -1479,6 +1583,8 @@ static void ixgbevf_configure_tx_ring(struct ixgbevf_adapter *adapter, txdctl |= (1 << 8) | /* HTHRESH = 1 */ 32; /* PTHRESH = 32 */ + clear_bit(__IXGBEVF_HANG_CHECK_ARMED, &ring->state); + IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(reg_idx), txdctl); /* poll to verify queue is enabled */ @@ -1584,6 +1690,39 @@ static void ixgbevf_rx_desc_queue_enable(struct ixgbevf_adapter *adapter, reg_idx); } +static void ixgbevf_setup_vfmrqc(struct ixgbevf_adapter *adapter) +{ + struct ixgbe_hw *hw = &adapter->hw; + u32 vfmrqc = 0, vfreta = 0; + u32 rss_key[10]; + u16 rss_i = adapter->num_rx_queues; + int i, j; + + /* Fill out hash function seeds */ + netdev_rss_key_fill(rss_key, sizeof(rss_key)); + for (i = 0; i < 10; i++) + IXGBE_WRITE_REG(hw, IXGBE_VFRSSRK(i), rss_key[i]); + + /* Fill out redirection table */ + for (i = 0, j = 0; i < 64; i++, j++) { + if (j == rss_i) + j = 0; + vfreta = (vfreta << 8) | (j * 0x1); + if ((i & 3) == 3) + IXGBE_WRITE_REG(hw, IXGBE_VFRETA(i >> 2), vfreta); + } + + /* Perform hash on these packet types */ + vfmrqc |= IXGBE_VFMRQC_RSS_FIELD_IPV4 | + IXGBE_VFMRQC_RSS_FIELD_IPV4_TCP | + IXGBE_VFMRQC_RSS_FIELD_IPV6 | + IXGBE_VFMRQC_RSS_FIELD_IPV6_TCP; + + vfmrqc |= IXGBE_VFMRQC_RSSEN; + + IXGBE_WRITE_REG(hw, IXGBE_VFMRQC, vfmrqc); +} + static void ixgbevf_configure_rx_ring(struct ixgbevf_adapter *adapter, struct ixgbevf_ring *ring) { @@ -1640,6 +1779,8 @@ static void ixgbevf_configure_rx(struct ixgbevf_adapter *adapter) struct net_device *netdev = adapter->netdev; ixgbevf_setup_psrtype(adapter); + if (hw->mac.type >= ixgbe_mac_X550_vf) + ixgbevf_setup_vfmrqc(adapter); /* notify the PF of our intent to use this size of frame */ ixgbevf_rlpml_set_vf(hw, netdev->mtu + ETH_HLEN + ETH_FCS_LEN); @@ -1794,7 +1935,8 @@ static int ixgbevf_configure_dcb(struct ixgbevf_adapter *adapter) struct ixgbe_hw *hw = &adapter->hw; unsigned int def_q = 0; unsigned int num_tcs = 0; - unsigned int num_rx_queues = 1; + unsigned int num_rx_queues = adapter->num_rx_queues; + unsigned int num_tx_queues = adapter->num_tx_queues; int err; spin_lock_bh(&adapter->mbx_lock); @@ -1808,6 +1950,9 @@ static int ixgbevf_configure_dcb(struct ixgbevf_adapter *adapter) return err; if (num_tcs > 1) { + /* we need only one Tx queue */ + num_tx_queues = 1; + /* update default Tx ring register index */ adapter->tx_ring[0]->reg_idx = def_q; @@ -1816,7 +1961,8 @@ static int ixgbevf_configure_dcb(struct ixgbevf_adapter *adapter) } /* if we have a bad config abort request queue reset */ - if (adapter->num_rx_queues != num_rx_queues) { + if ((adapter->num_rx_queues != num_rx_queues) || + (adapter->num_tx_queues != num_tx_queues)) { /* force mailbox timeout to prevent further messages */ hw->mbx.timeout = 0; @@ -1917,6 +2063,10 @@ static void ixgbevf_up_complete(struct ixgbevf_adapter *adapter) clear_bit(__IXGBEVF_DOWN, &adapter->state); ixgbevf_napi_enable_all(adapter); + /* clear any pending interrupts, may auto mask */ + IXGBE_READ_REG(hw, IXGBE_VTEICR); + ixgbevf_irq_enable(adapter); + /* enable transmits */ netif_tx_start_all_queues(netdev); @@ -1924,21 +2074,14 @@ static void ixgbevf_up_complete(struct ixgbevf_adapter *adapter) ixgbevf_init_last_counter_stats(adapter); hw->mac.get_link_status = 1; - mod_timer(&adapter->watchdog_timer, jiffies); + mod_timer(&adapter->service_timer, jiffies); } void ixgbevf_up(struct ixgbevf_adapter *adapter) { - struct ixgbe_hw *hw = &adapter->hw; - ixgbevf_configure(adapter); ixgbevf_up_complete(adapter); - - /* clear any pending interrupts, may auto mask */ - IXGBE_READ_REG(hw, IXGBE_VTEICR); - - ixgbevf_irq_enable(adapter); } /** @@ -2045,22 +2188,19 @@ void ixgbevf_down(struct ixgbevf_adapter *adapter) for (i = 0; i < adapter->num_rx_queues; i++) ixgbevf_disable_rx_queue(adapter, adapter->rx_ring[i]); - netif_tx_disable(netdev); - - msleep(10); + usleep_range(10000, 20000); netif_tx_stop_all_queues(netdev); + /* call carrier off first to avoid false dev_watchdog timeouts */ + netif_carrier_off(netdev); + netif_tx_disable(netdev); + ixgbevf_irq_disable(adapter); ixgbevf_napi_disable_all(adapter); - del_timer_sync(&adapter->watchdog_timer); - /* can't call flush scheduled work here because it can deadlock - * if linkwatch_event tries to acquire the rtnl_lock which we are - * holding */ - while (adapter->flags & IXGBE_FLAG_IN_WATCHDOG_TASK) - msleep(1); + del_timer_sync(&adapter->service_timer); /* disable transmits in the hardware now that interrupts are off */ for (i = 0; i < adapter->num_tx_queues; i++) { @@ -2070,8 +2210,6 @@ void ixgbevf_down(struct ixgbevf_adapter *adapter) IXGBE_TXDCTL_SWFLSH); } - netif_carrier_off(netdev); - if (!pci_channel_offline(adapter->pdev)) ixgbevf_reset(adapter); @@ -2110,6 +2248,8 @@ void ixgbevf_reset(struct ixgbevf_adapter *adapter) memcpy(netdev->perm_addr, adapter->hw.mac.addr, netdev->addr_len); } + + adapter->last_reset = jiffies; } static int ixgbevf_acquire_msix_vectors(struct ixgbevf_adapter *adapter, @@ -2181,8 +2321,19 @@ static void ixgbevf_set_num_queues(struct ixgbevf_adapter *adapter) return; /* we need as many queues as traffic classes */ - if (num_tcs > 1) + if (num_tcs > 1) { adapter->num_rx_queues = num_tcs; + } else { + u16 rss = min_t(u16, num_online_cpus(), IXGBEVF_MAX_RSS_QUEUES); + + switch (hw->api_version) { + case ixgbe_mbox_api_11: + adapter->num_rx_queues = rss; + adapter->num_tx_queues = rss; + default: + break; + } + } } /** @@ -2552,7 +2703,8 @@ void ixgbevf_update_stats(struct ixgbevf_adapter *adapter) struct ixgbe_hw *hw = &adapter->hw; int i; - if (!adapter->link_up) + if (test_bit(__IXGBEVF_DOWN, &adapter->state) || + test_bit(__IXGBEVF_RESETTING, &adapter->state)) return; UPDATE_VF_COUNTER_32bit(IXGBE_VFGPRC, adapter->stats.last_vfgprc, @@ -2576,79 +2728,176 @@ void ixgbevf_update_stats(struct ixgbevf_adapter *adapter) } /** - * ixgbevf_watchdog - Timer Call-back + * ixgbevf_service_timer - Timer Call-back * @data: pointer to adapter cast into an unsigned long **/ -static void ixgbevf_watchdog(unsigned long data) +static void ixgbevf_service_timer(unsigned long data) { struct ixgbevf_adapter *adapter = (struct ixgbevf_adapter *)data; + + /* Reset the timer */ + mod_timer(&adapter->service_timer, (HZ * 2) + jiffies); + + ixgbevf_service_event_schedule(adapter); +} + +static void ixgbevf_reset_subtask(struct ixgbevf_adapter *adapter) +{ + if (!(adapter->flags & IXGBEVF_FLAG_RESET_REQUESTED)) + return; + + adapter->flags &= ~IXGBEVF_FLAG_RESET_REQUESTED; + + /* If we're already down or resetting, just bail */ + if (test_bit(__IXGBEVF_DOWN, &adapter->state) || + test_bit(__IXGBEVF_RESETTING, &adapter->state)) + return; + + adapter->tx_timeout_count++; + + ixgbevf_reinit_locked(adapter); +} + +/* ixgbevf_check_hang_subtask - check for hung queues and dropped interrupts + * @adapter - pointer to the device adapter structure + * + * This function serves two purposes. First it strobes the interrupt lines + * in order to make certain interrupts are occurring. Secondly it sets the + * bits needed to check for TX hangs. As a result we should immediately + * determine if a hang has occurred. + */ +static void ixgbevf_check_hang_subtask(struct ixgbevf_adapter *adapter) +{ struct ixgbe_hw *hw = &adapter->hw; u32 eics = 0; int i; - /* - * Do the watchdog outside of interrupt context due to the lovely - * delays that some of the newer hardware requires - */ + /* If we're down or resetting, just bail */ + if (test_bit(__IXGBEVF_DOWN, &adapter->state) || + test_bit(__IXGBEVF_RESETTING, &adapter->state)) + return; - if (test_bit(__IXGBEVF_DOWN, &adapter->state)) - goto watchdog_short_circuit; + /* Force detection of hung controller */ + if (netif_carrier_ok(adapter->netdev)) { + for (i = 0; i < adapter->num_tx_queues; i++) + set_check_for_tx_hang(adapter->tx_ring[i]); + } /* get one bit for every active tx/rx interrupt vector */ for (i = 0; i < adapter->num_msix_vectors - NON_Q_VECTORS; i++) { struct ixgbevf_q_vector *qv = adapter->q_vector[i]; + if (qv->rx.ring || qv->tx.ring) eics |= 1 << i; } + /* Cause software interrupt to ensure rings are cleaned */ IXGBE_WRITE_REG(hw, IXGBE_VTEICS, eics); +} -watchdog_short_circuit: - schedule_work(&adapter->watchdog_task); +/** + * ixgbevf_watchdog_update_link - update the link status + * @adapter - pointer to the device adapter structure + **/ +static void ixgbevf_watchdog_update_link(struct ixgbevf_adapter *adapter) +{ + struct ixgbe_hw *hw = &adapter->hw; + u32 link_speed = adapter->link_speed; + bool link_up = adapter->link_up; + s32 err; + + spin_lock_bh(&adapter->mbx_lock); + + err = hw->mac.ops.check_link(hw, &link_speed, &link_up, false); + + spin_unlock_bh(&adapter->mbx_lock); + + /* if check for link returns error we will need to reset */ + if (err && time_after(jiffies, adapter->last_reset + (10 * HZ))) { + adapter->flags |= IXGBEVF_FLAG_RESET_REQUESTED; + link_up = false; + } + + adapter->link_up = link_up; + adapter->link_speed = link_speed; } /** - * ixgbevf_tx_timeout - Respond to a Tx Hang - * @netdev: network interface device structure + * ixgbevf_watchdog_link_is_up - update netif_carrier status and + * print link up message + * @adapter - pointer to the device adapter structure **/ -static void ixgbevf_tx_timeout(struct net_device *netdev) +static void ixgbevf_watchdog_link_is_up(struct ixgbevf_adapter *adapter) { - struct ixgbevf_adapter *adapter = netdev_priv(netdev); + struct net_device *netdev = adapter->netdev; - /* Do the reset outside of interrupt context */ - schedule_work(&adapter->reset_task); + /* only continue if link was previously down */ + if (netif_carrier_ok(netdev)) + return; + + dev_info(&adapter->pdev->dev, "NIC Link is Up %s\n", + (adapter->link_speed == IXGBE_LINK_SPEED_10GB_FULL) ? + "10 Gbps" : + (adapter->link_speed == IXGBE_LINK_SPEED_1GB_FULL) ? + "1 Gbps" : + (adapter->link_speed == IXGBE_LINK_SPEED_100_FULL) ? + "100 Mbps" : + "unknown speed"); + + netif_carrier_on(netdev); } -static void ixgbevf_reset_task(struct work_struct *work) +/** + * ixgbevf_watchdog_link_is_down - update netif_carrier status and + * print link down message + * @adapter - pointer to the adapter structure + **/ +static void ixgbevf_watchdog_link_is_down(struct ixgbevf_adapter *adapter) { - struct ixgbevf_adapter *adapter; - adapter = container_of(work, struct ixgbevf_adapter, reset_task); + struct net_device *netdev = adapter->netdev; - /* If we're already down or resetting, just bail */ + adapter->link_speed = 0; + + /* only continue if link was up previously */ + if (!netif_carrier_ok(netdev)) + return; + + dev_info(&adapter->pdev->dev, "NIC Link is Down\n"); + + netif_carrier_off(netdev); +} + +/** + * ixgbevf_watchdog_subtask - worker thread to bring link up + * @work: pointer to work_struct containing our data + **/ +static void ixgbevf_watchdog_subtask(struct ixgbevf_adapter *adapter) +{ + /* if interface is down do nothing */ if (test_bit(__IXGBEVF_DOWN, &adapter->state) || - test_bit(__IXGBEVF_REMOVING, &adapter->state) || test_bit(__IXGBEVF_RESETTING, &adapter->state)) return; - adapter->tx_timeout_count++; + ixgbevf_watchdog_update_link(adapter); - ixgbevf_reinit_locked(adapter); + if (adapter->link_up) + ixgbevf_watchdog_link_is_up(adapter); + else + ixgbevf_watchdog_link_is_down(adapter); + + ixgbevf_update_stats(adapter); } /** - * ixgbevf_watchdog_task - worker thread to bring link up + * ixgbevf_service_task - manages and runs subtasks * @work: pointer to work_struct containing our data **/ -static void ixgbevf_watchdog_task(struct work_struct *work) +static void ixgbevf_service_task(struct work_struct *work) { struct ixgbevf_adapter *adapter = container_of(work, struct ixgbevf_adapter, - watchdog_task); - struct net_device *netdev = adapter->netdev; + service_task); struct ixgbe_hw *hw = &adapter->hw; - u32 link_speed = adapter->link_speed; - bool link_up = adapter->link_up; - s32 need_reset; if (IXGBE_REMOVED(hw->hw_addr)) { if (!test_bit(__IXGBEVF_DOWN, &adapter->state)) { @@ -2658,73 +2907,13 @@ static void ixgbevf_watchdog_task(struct work_struct *work) } return; } - ixgbevf_queue_reset_subtask(adapter); - - adapter->flags |= IXGBE_FLAG_IN_WATCHDOG_TASK; - - /* - * Always check the link on the watchdog because we have - * no LSC interrupt - */ - spin_lock_bh(&adapter->mbx_lock); - - need_reset = hw->mac.ops.check_link(hw, &link_speed, &link_up, false); - - spin_unlock_bh(&adapter->mbx_lock); - - if (need_reset) { - adapter->link_up = link_up; - adapter->link_speed = link_speed; - netif_carrier_off(netdev); - netif_tx_stop_all_queues(netdev); - schedule_work(&adapter->reset_task); - goto pf_has_reset; - } - adapter->link_up = link_up; - adapter->link_speed = link_speed; - - if (link_up) { - if (!netif_carrier_ok(netdev)) { - char *link_speed_string; - switch (link_speed) { - case IXGBE_LINK_SPEED_10GB_FULL: - link_speed_string = "10 Gbps"; - break; - case IXGBE_LINK_SPEED_1GB_FULL: - link_speed_string = "1 Gbps"; - break; - case IXGBE_LINK_SPEED_100_FULL: - link_speed_string = "100 Mbps"; - break; - default: - link_speed_string = "unknown speed"; - break; - } - dev_info(&adapter->pdev->dev, - "NIC Link is Up, %s\n", link_speed_string); - netif_carrier_on(netdev); - netif_tx_wake_all_queues(netdev); - } - } else { - adapter->link_up = false; - adapter->link_speed = 0; - if (netif_carrier_ok(netdev)) { - dev_info(&adapter->pdev->dev, "NIC Link is Down\n"); - netif_carrier_off(netdev); - netif_tx_stop_all_queues(netdev); - } - } - ixgbevf_update_stats(adapter); - -pf_has_reset: - /* Reset the timer */ - if (!test_bit(__IXGBEVF_DOWN, &adapter->state) && - !test_bit(__IXGBEVF_REMOVING, &adapter->state)) - mod_timer(&adapter->watchdog_timer, - round_jiffies(jiffies + (2 * HZ))); + ixgbevf_queue_reset_subtask(adapter); + ixgbevf_reset_subtask(adapter); + ixgbevf_watchdog_subtask(adapter); + ixgbevf_check_hang_subtask(adapter); - adapter->flags &= ~IXGBE_FLAG_IN_WATCHDOG_TASK; + ixgbevf_service_event_complete(adapter); } /** @@ -2944,10 +3133,6 @@ static int ixgbevf_open(struct net_device *netdev) if (!adapter->num_msix_vectors) return -ENOMEM; - /* disallow open during test */ - if (test_bit(__IXGBEVF_TESTING, &adapter->state)) - return -EBUSY; - if (hw->adapter_stopped) { ixgbevf_reset(adapter); /* if adapter is still stopped then PF isn't up and @@ -2960,6 +3145,12 @@ static int ixgbevf_open(struct net_device *netdev) } } + /* disallow open during test */ + if (test_bit(__IXGBEVF_TESTING, &adapter->state)) + return -EBUSY; + + netif_carrier_off(netdev); + /* allocate transmit descriptors */ err = ixgbevf_setup_all_tx_resources(adapter); if (err) @@ -2979,15 +3170,11 @@ static int ixgbevf_open(struct net_device *netdev) */ ixgbevf_map_rings_to_vectors(adapter); - ixgbevf_up_complete(adapter); - - /* clear any pending interrupts, may auto mask */ - IXGBE_READ_REG(hw, IXGBE_VTEICR); err = ixgbevf_request_irq(adapter); if (err) goto err_req_irq; - ixgbevf_irq_enable(adapter); + ixgbevf_up_complete(adapter); return 0; @@ -3099,7 +3286,7 @@ static int ixgbevf_tso(struct ixgbevf_ring *tx_ring, /* ADV DTYP TUCMD MKRLOC/ISCSIHEDLEN */ type_tucmd = IXGBE_ADVTXD_TUCMD_L4T_TCP; - if (skb->protocol == htons(ETH_P_IP)) { + if (first->protocol == htons(ETH_P_IP)) { struct iphdr *iph = ip_hdr(skb); iph->tot_len = 0; iph->check = 0; @@ -3156,7 +3343,7 @@ static void ixgbevf_tx_csum(struct ixgbevf_ring *tx_ring, if (skb->ip_summed == CHECKSUM_PARTIAL) { u8 l4_hdr = 0; - switch (skb->protocol) { + switch (first->protocol) { case htons(ETH_P_IP): vlan_macip_lens |= skb_network_header_len(skb); type_tucmd |= IXGBE_ADVTXD_TUCMD_IPV4; @@ -3452,8 +3639,8 @@ static int ixgbevf_xmit_frame(struct sk_buff *skb, struct net_device *netdev) first->bytecount = skb->len; first->gso_segs = 1; - if (vlan_tx_tag_present(skb)) { - tx_flags |= vlan_tx_tag_get(skb); + if (skb_vlan_tag_present(skb)) { + tx_flags |= skb_vlan_tag_get(skb); tx_flags <<= IXGBE_TX_FLAGS_VLAN_SHIFT; tx_flags |= IXGBE_TX_FLAGS_VLAN; } @@ -3822,28 +4009,28 @@ static int ixgbevf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER; - netdev->vlan_features |= NETIF_F_TSO; - netdev->vlan_features |= NETIF_F_TSO6; - netdev->vlan_features |= NETIF_F_IP_CSUM; - netdev->vlan_features |= NETIF_F_IPV6_CSUM; - netdev->vlan_features |= NETIF_F_SG; + netdev->vlan_features |= NETIF_F_TSO | + NETIF_F_TSO6 | + NETIF_F_IP_CSUM | + NETIF_F_IPV6_CSUM | + NETIF_F_SG; if (pci_using_dac) netdev->features |= NETIF_F_HIGHDMA; netdev->priv_flags |= IFF_UNICAST_FLT; - init_timer(&adapter->watchdog_timer); - adapter->watchdog_timer.function = ixgbevf_watchdog; - adapter->watchdog_timer.data = (unsigned long)adapter; - if (IXGBE_REMOVED(hw->hw_addr)) { err = -EIO; goto err_sw_init; } - INIT_WORK(&adapter->reset_task, ixgbevf_reset_task); - INIT_WORK(&adapter->watchdog_task, ixgbevf_watchdog_task); - set_bit(__IXGBEVF_WORK_INIT, &adapter->state); + + setup_timer(&adapter->service_timer, &ixgbevf_service_timer, + (unsigned long)adapter); + + INIT_WORK(&adapter->service_task, ixgbevf_service_task); + set_bit(__IXGBEVF_SERVICE_INITED, &adapter->state); + clear_bit(__IXGBEVF_SERVICE_SCHED, &adapter->state); err = ixgbevf_init_interrupt_scheme(adapter); if (err) @@ -3917,11 +4104,7 @@ static void ixgbevf_remove(struct pci_dev *pdev) adapter = netdev_priv(netdev); set_bit(__IXGBEVF_REMOVING, &adapter->state); - - del_timer_sync(&adapter->watchdog_timer); - - cancel_work_sync(&adapter->reset_task); - cancel_work_sync(&adapter->watchdog_task); + cancel_work_sync(&adapter->service_task); if (netdev->reg_state == NETREG_REGISTERED) unregister_netdev(netdev); @@ -3955,7 +4138,7 @@ static pci_ers_result_t ixgbevf_io_error_detected(struct pci_dev *pdev, struct net_device *netdev = pci_get_drvdata(pdev); struct ixgbevf_adapter *adapter = netdev_priv(netdev); - if (!test_bit(__IXGBEVF_WORK_INIT, &adapter->state)) + if (!test_bit(__IXGBEVF_SERVICE_INITED, &adapter->state)) return PCI_ERS_RESULT_DISCONNECT; rtnl_lock(); diff --git a/drivers/net/ethernet/intel/ixgbevf/regs.h b/drivers/net/ethernet/intel/ixgbevf/regs.h index 09dd8f698bea..3e712fd6e695 100644 --- a/drivers/net/ethernet/intel/ixgbevf/regs.h +++ b/drivers/net/ethernet/intel/ixgbevf/regs.h @@ -69,6 +69,16 @@ #define IXGBE_VFGOTC_LSB 0x02020 #define IXGBE_VFGOTC_MSB 0x02024 #define IXGBE_VFMPRC 0x01034 +#define IXGBE_VFMRQC 0x3000 +#define IXGBE_VFRSSRK(x) (0x3100 + ((x) * 4)) +#define IXGBE_VFRETA(x) (0x3200 + ((x) * 4)) + +/* VFMRQC bits */ +#define IXGBE_VFMRQC_RSSEN 0x00000001 /* RSS Enable */ +#define IXGBE_VFMRQC_RSS_FIELD_IPV4_TCP 0x00010000 +#define IXGBE_VFMRQC_RSS_FIELD_IPV4 0x00020000 +#define IXGBE_VFMRQC_RSS_FIELD_IPV6 0x00100000 +#define IXGBE_VFMRQC_RSS_FIELD_IPV6_TCP 0x00200000 #define IXGBE_WRITE_FLUSH(a) (IXGBE_READ_REG(a, IXGBE_VFSTATUS)) diff --git a/drivers/net/ethernet/jme.c b/drivers/net/ethernet/jme.c index 44ce7d88f554..6e9a792097d3 100644 --- a/drivers/net/ethernet/jme.c +++ b/drivers/net/ethernet/jme.c @@ -2154,9 +2154,9 @@ jme_tx_csum(struct jme_adapter *jme, struct sk_buff *skb, u8 *flags) static inline void jme_tx_vlan(struct sk_buff *skb, __le16 *vlan, u8 *flags) { - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { *flags |= TXFLAG_TAGON; - *vlan = cpu_to_le16(vlan_tx_tag_get(skb)); + *vlan = cpu_to_le16(skb_vlan_tag_get(skb)); } } diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index a62fc38f045e..1c75829eb166 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -192,6 +192,10 @@ static char mv643xx_eth_driver_version[] = "1.4"; #define IS_TSO_HEADER(txq, addr) \ ((addr >= txq->tso_hdrs_dma) && \ (addr < txq->tso_hdrs_dma + txq->tx_ring_size * TSO_HEADER_SIZE)) + +#define DESC_DMA_MAP_SINGLE 0 +#define DESC_DMA_MAP_PAGE 1 + /* * RX/TX descriptors. */ @@ -362,6 +366,7 @@ struct tx_queue { dma_addr_t tso_hdrs_dma; struct tx_desc *tx_desc_area; + char *tx_desc_mapping; /* array to track the type of the dma mapping */ dma_addr_t tx_desc_dma; int tx_desc_area_size; @@ -750,6 +755,7 @@ txq_put_data_tso(struct net_device *dev, struct tx_queue *txq, if (txq->tx_curr_desc == txq->tx_ring_size) txq->tx_curr_desc = 0; desc = &txq->tx_desc_area[tx_index]; + txq->tx_desc_mapping[tx_index] = DESC_DMA_MAP_SINGLE; desc->l4i_chk = 0; desc->byte_cnt = length; @@ -879,14 +885,13 @@ static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb) skb_frag_t *this_frag; int tx_index; struct tx_desc *desc; - void *addr; this_frag = &skb_shinfo(skb)->frags[frag]; - addr = page_address(this_frag->page.p) + this_frag->page_offset; tx_index = txq->tx_curr_desc++; if (txq->tx_curr_desc == txq->tx_ring_size) txq->tx_curr_desc = 0; desc = &txq->tx_desc_area[tx_index]; + txq->tx_desc_mapping[tx_index] = DESC_DMA_MAP_PAGE; /* * The last fragment will generate an interrupt @@ -902,8 +907,9 @@ static void txq_submit_frag_skb(struct tx_queue *txq, struct sk_buff *skb) desc->l4i_chk = 0; desc->byte_cnt = skb_frag_size(this_frag); - desc->buf_ptr = dma_map_single(mp->dev->dev.parent, addr, - desc->byte_cnt, DMA_TO_DEVICE); + desc->buf_ptr = skb_frag_dma_map(mp->dev->dev.parent, + this_frag, 0, desc->byte_cnt, + DMA_TO_DEVICE); } } @@ -936,6 +942,7 @@ static int txq_submit_skb(struct tx_queue *txq, struct sk_buff *skb, if (txq->tx_curr_desc == txq->tx_ring_size) txq->tx_curr_desc = 0; desc = &txq->tx_desc_area[tx_index]; + txq->tx_desc_mapping[tx_index] = DESC_DMA_MAP_SINGLE; if (nr_frags) { txq_submit_frag_skb(txq, skb); @@ -1047,9 +1054,12 @@ static int txq_reclaim(struct tx_queue *txq, int budget, int force) int tx_index; struct tx_desc *desc; u32 cmd_sts; + char desc_dma_map; tx_index = txq->tx_used_desc; desc = &txq->tx_desc_area[tx_index]; + desc_dma_map = txq->tx_desc_mapping[tx_index]; + cmd_sts = desc->cmd_sts; if (cmd_sts & BUFFER_OWNED_BY_DMA) { @@ -1065,9 +1075,19 @@ static int txq_reclaim(struct tx_queue *txq, int budget, int force) reclaimed++; txq->tx_desc_count--; - if (!IS_TSO_HEADER(txq, desc->buf_ptr)) - dma_unmap_single(mp->dev->dev.parent, desc->buf_ptr, - desc->byte_cnt, DMA_TO_DEVICE); + if (!IS_TSO_HEADER(txq, desc->buf_ptr)) { + + if (desc_dma_map == DESC_DMA_MAP_PAGE) + dma_unmap_page(mp->dev->dev.parent, + desc->buf_ptr, + desc->byte_cnt, + DMA_TO_DEVICE); + else + dma_unmap_single(mp->dev->dev.parent, + desc->buf_ptr, + desc->byte_cnt, + DMA_TO_DEVICE); + } if (cmd_sts & TX_ENABLE_INTERRUPT) { struct sk_buff *skb = __skb_dequeue(&txq->tx_skb); @@ -1996,6 +2016,7 @@ static int txq_init(struct mv643xx_eth_private *mp, int index) struct tx_queue *txq = mp->txq + index; struct tx_desc *tx_desc; int size; + int ret; int i; txq->index = index; @@ -2048,18 +2069,34 @@ static int txq_init(struct mv643xx_eth_private *mp, int index) nexti * sizeof(struct tx_desc); } + txq->tx_desc_mapping = kcalloc(txq->tx_ring_size, sizeof(char), + GFP_KERNEL); + if (!txq->tx_desc_mapping) { + ret = -ENOMEM; + goto err_free_desc_area; + } + /* Allocate DMA buffers for TSO MAC/IP/TCP headers */ txq->tso_hdrs = dma_alloc_coherent(mp->dev->dev.parent, txq->tx_ring_size * TSO_HEADER_SIZE, &txq->tso_hdrs_dma, GFP_KERNEL); if (txq->tso_hdrs == NULL) { - dma_free_coherent(mp->dev->dev.parent, txq->tx_desc_area_size, - txq->tx_desc_area, txq->tx_desc_dma); - return -ENOMEM; + ret = -ENOMEM; + goto err_free_desc_mapping; } skb_queue_head_init(&txq->tx_skb); return 0; + +err_free_desc_mapping: + kfree(txq->tx_desc_mapping); +err_free_desc_area: + if (index == 0 && size <= mp->tx_desc_sram_size) + iounmap(txq->tx_desc_area); + else + dma_free_coherent(mp->dev->dev.parent, txq->tx_desc_area_size, + txq->tx_desc_area, txq->tx_desc_dma); + return ret; } static void txq_deinit(struct tx_queue *txq) @@ -2077,6 +2114,8 @@ static void txq_deinit(struct tx_queue *txq) else dma_free_coherent(mp->dev->dev.parent, txq->tx_desc_area_size, txq->tx_desc_area, txq->tx_desc_dma); + kfree(txq->tx_desc_mapping); + if (txq->tso_hdrs) dma_free_coherent(mp->dev->dev.parent, txq->tx_ring_size * TSO_HEADER_SIZE, diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index 867a6a3ef81f..d9f4498832a1 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -1895,14 +1895,14 @@ static netdev_tx_t sky2_xmit_frame(struct sk_buff *skb, ctrl = 0; /* Add VLAN tag, can piggyback on LRGLEN or ADDR64 */ - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { if (!le) { le = get_tx_le(sky2, &slot); le->addr = 0; le->opcode = OP_VLAN|HW_OWNER; } else le->opcode |= OP_VLAN; - le->length = cpu_to_be16(vlan_tx_tag_get(skb)); + le->length = cpu_to_be16(skb_vlan_tag_get(skb)); ctrl |= INS_VLAN; } @@ -2594,7 +2594,7 @@ static struct sk_buff *sky2_receive(struct net_device *dev, sky2->rx_next = (sky2->rx_next + 1) % sky2->rx_pending; prefetch(sky2->rx_ring + sky2->rx_next); - if (vlan_tx_tag_present(re->skb)) + if (skb_vlan_tag_present(re->skb)) count -= VLAN_HLEN; /* Account for vlan tag */ /* This chip has hardware problems that generates bogus status. diff --git a/drivers/net/ethernet/mellanox/mlx4/alloc.c b/drivers/net/ethernet/mellanox/mlx4/alloc.c index 963dd7e6d547..0c51c69f802f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/alloc.c +++ b/drivers/net/ethernet/mellanox/mlx4/alloc.c @@ -592,7 +592,7 @@ int mlx4_buf_alloc(struct mlx4_dev *dev, int size, int max_direct, buf->nbufs = 1; buf->npages = 1; buf->page_shift = get_order(size) + PAGE_SHIFT; - buf->direct.buf = dma_alloc_coherent(&dev->pdev->dev, + buf->direct.buf = dma_alloc_coherent(&dev->persist->pdev->dev, size, &t, gfp); if (!buf->direct.buf) return -ENOMEM; @@ -619,7 +619,8 @@ int mlx4_buf_alloc(struct mlx4_dev *dev, int size, int max_direct, for (i = 0; i < buf->nbufs; ++i) { buf->page_list[i].buf = - dma_alloc_coherent(&dev->pdev->dev, PAGE_SIZE, + dma_alloc_coherent(&dev->persist->pdev->dev, + PAGE_SIZE, &t, gfp); if (!buf->page_list[i].buf) goto err_free; @@ -657,15 +658,17 @@ void mlx4_buf_free(struct mlx4_dev *dev, int size, struct mlx4_buf *buf) int i; if (buf->nbufs == 1) - dma_free_coherent(&dev->pdev->dev, size, buf->direct.buf, + dma_free_coherent(&dev->persist->pdev->dev, size, + buf->direct.buf, buf->direct.map); else { - if (BITS_PER_LONG == 64 && buf->direct.buf) + if (BITS_PER_LONG == 64) vunmap(buf->direct.buf); for (i = 0; i < buf->nbufs; ++i) if (buf->page_list[i].buf) - dma_free_coherent(&dev->pdev->dev, PAGE_SIZE, + dma_free_coherent(&dev->persist->pdev->dev, + PAGE_SIZE, buf->page_list[i].buf, buf->page_list[i].map); kfree(buf->page_list); @@ -738,7 +741,7 @@ int mlx4_db_alloc(struct mlx4_dev *dev, struct mlx4_db *db, int order, gfp_t gfp if (!mlx4_alloc_db_from_pgdir(pgdir, db, order)) goto out; - pgdir = mlx4_alloc_db_pgdir(&(dev->pdev->dev), gfp); + pgdir = mlx4_alloc_db_pgdir(&dev->persist->pdev->dev, gfp); if (!pgdir) { ret = -ENOMEM; goto out; @@ -775,7 +778,7 @@ void mlx4_db_free(struct mlx4_dev *dev, struct mlx4_db *db) set_bit(i, db->u.pgdir->bits[o]); if (bitmap_full(db->u.pgdir->order1, MLX4_DB_PER_PAGE / 2)) { - dma_free_coherent(&(dev->pdev->dev), PAGE_SIZE, + dma_free_coherent(&dev->persist->pdev->dev, PAGE_SIZE, db->u.pgdir->db_page, db->u.pgdir->db_dma); list_del(&db->u.pgdir->list); kfree(db->u.pgdir); diff --git a/drivers/net/ethernet/mellanox/mlx4/catas.c b/drivers/net/ethernet/mellanox/mlx4/catas.c index 9c656fe4983d..715de8affcc9 100644 --- a/drivers/net/ethernet/mellanox/mlx4/catas.c +++ b/drivers/net/ethernet/mellanox/mlx4/catas.c @@ -40,16 +40,177 @@ enum { MLX4_CATAS_POLL_INTERVAL = 5 * HZ, }; -static DEFINE_SPINLOCK(catas_lock); -static LIST_HEAD(catas_list); -static struct work_struct catas_work; -static int internal_err_reset = 1; -module_param(internal_err_reset, int, 0644); +int mlx4_internal_err_reset = 1; +module_param_named(internal_err_reset, mlx4_internal_err_reset, int, 0644); MODULE_PARM_DESC(internal_err_reset, - "Reset device on internal errors if non-zero" - " (default 1, in SRIOV mode default is 0)"); + "Reset device on internal errors if non-zero (default 1)"); + +static int read_vendor_id(struct mlx4_dev *dev) +{ + u16 vendor_id = 0; + int ret; + + ret = pci_read_config_word(dev->persist->pdev, 0, &vendor_id); + if (ret) { + mlx4_err(dev, "Failed to read vendor ID, ret=%d\n", ret); + return ret; + } + + if (vendor_id == 0xffff) { + mlx4_err(dev, "PCI can't be accessed to read vendor id\n"); + return -EINVAL; + } + + return 0; +} + +static int mlx4_reset_master(struct mlx4_dev *dev) +{ + int err = 0; + + if (mlx4_is_master(dev)) + mlx4_report_internal_err_comm_event(dev); + + if (!pci_channel_offline(dev->persist->pdev)) { + err = read_vendor_id(dev); + /* If PCI can't be accessed to read vendor ID we assume that its + * link was disabled and chip was already reset. + */ + if (err) + return 0; + + err = mlx4_reset(dev); + if (err) + mlx4_err(dev, "Fail to reset HCA\n"); + } + + return err; +} + +static int mlx4_reset_slave(struct mlx4_dev *dev) +{ +#define COM_CHAN_RST_REQ_OFFSET 0x10 +#define COM_CHAN_RST_ACK_OFFSET 0x08 + + u32 comm_flags; + u32 rst_req; + u32 rst_ack; + unsigned long end; + struct mlx4_priv *priv = mlx4_priv(dev); + + if (pci_channel_offline(dev->persist->pdev)) + return 0; + + comm_flags = swab32(readl((__iomem char *)priv->mfunc.comm + + MLX4_COMM_CHAN_FLAGS)); + if (comm_flags == 0xffffffff) { + mlx4_err(dev, "VF reset is not needed\n"); + return 0; + } + + if (!(dev->caps.vf_caps & MLX4_VF_CAP_FLAG_RESET)) { + mlx4_err(dev, "VF reset is not supported\n"); + return -EOPNOTSUPP; + } + + rst_req = (comm_flags & (u32)(1 << COM_CHAN_RST_REQ_OFFSET)) >> + COM_CHAN_RST_REQ_OFFSET; + rst_ack = (comm_flags & (u32)(1 << COM_CHAN_RST_ACK_OFFSET)) >> + COM_CHAN_RST_ACK_OFFSET; + if (rst_req != rst_ack) { + mlx4_err(dev, "Communication channel isn't sync, fail to send reset\n"); + return -EIO; + } + + rst_req ^= 1; + mlx4_warn(dev, "VF is sending reset request to Firmware\n"); + comm_flags = rst_req << COM_CHAN_RST_REQ_OFFSET; + __raw_writel((__force u32)cpu_to_be32(comm_flags), + (__iomem char *)priv->mfunc.comm + MLX4_COMM_CHAN_FLAGS); + /* Make sure that our comm channel write doesn't + * get mixed in with writes from another CPU. + */ + mmiowb(); + + end = msecs_to_jiffies(MLX4_COMM_TIME) + jiffies; + while (time_before(jiffies, end)) { + comm_flags = swab32(readl((__iomem char *)priv->mfunc.comm + + MLX4_COMM_CHAN_FLAGS)); + rst_ack = (comm_flags & (u32)(1 << COM_CHAN_RST_ACK_OFFSET)) >> + COM_CHAN_RST_ACK_OFFSET; + + /* Reading rst_req again since the communication channel can + * be reset at any time by the PF and all its bits will be + * set to zero. + */ + rst_req = (comm_flags & (u32)(1 << COM_CHAN_RST_REQ_OFFSET)) >> + COM_CHAN_RST_REQ_OFFSET; + + if (rst_ack == rst_req) { + mlx4_warn(dev, "VF Reset succeed\n"); + return 0; + } + cond_resched(); + } + mlx4_err(dev, "Fail to send reset over the communication channel\n"); + return -ETIMEDOUT; +} + +static int mlx4_comm_internal_err(u32 slave_read) +{ + return (u32)COMM_CHAN_EVENT_INTERNAL_ERR == + (slave_read & (u32)COMM_CHAN_EVENT_INTERNAL_ERR) ? 1 : 0; +} + +void mlx4_enter_error_state(struct mlx4_dev_persistent *persist) +{ + int err; + struct mlx4_dev *dev; + + if (!mlx4_internal_err_reset) + return; + + mutex_lock(&persist->device_state_mutex); + if (persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR) + goto out; + + dev = persist->dev; + mlx4_err(dev, "device is going to be reset\n"); + if (mlx4_is_slave(dev)) + err = mlx4_reset_slave(dev); + else + err = mlx4_reset_master(dev); + BUG_ON(err != 0); + + dev->persist->state |= MLX4_DEVICE_STATE_INTERNAL_ERROR; + mlx4_err(dev, "device was reset successfully\n"); + mutex_unlock(&persist->device_state_mutex); + + /* At that step HW was already reset, now notify clients */ + mlx4_dispatch_event(dev, MLX4_DEV_EVENT_CATASTROPHIC_ERROR, 0); + mlx4_cmd_wake_completions(dev); + return; + +out: + mutex_unlock(&persist->device_state_mutex); +} + +static void mlx4_handle_error_state(struct mlx4_dev_persistent *persist) +{ + int err = 0; + + mlx4_enter_error_state(persist); + mutex_lock(&persist->interface_state_mutex); + if (persist->interface_state & MLX4_INTERFACE_STATE_UP && + !(persist->interface_state & MLX4_INTERFACE_STATE_DELETION)) { + err = mlx4_restart_one(persist->pdev); + mlx4_info(persist->dev, "mlx4_restart_one was ended, ret=%d\n", + err); + } + mutex_unlock(&persist->interface_state_mutex); +} static void dump_err_buf(struct mlx4_dev *dev) { @@ -67,58 +228,40 @@ static void poll_catas(unsigned long dev_ptr) { struct mlx4_dev *dev = (struct mlx4_dev *) dev_ptr; struct mlx4_priv *priv = mlx4_priv(dev); + u32 slave_read; - if (readl(priv->catas_err.map)) { - /* If the device is off-line, we cannot try to recover it */ - if (pci_channel_offline(dev->pdev)) - mod_timer(&priv->catas_err.timer, - round_jiffies(jiffies + MLX4_CATAS_POLL_INTERVAL)); - else { - dump_err_buf(dev); - mlx4_dispatch_event(dev, MLX4_DEV_EVENT_CATASTROPHIC_ERROR, 0); - - if (internal_err_reset) { - spin_lock(&catas_lock); - list_add(&priv->catas_err.list, &catas_list); - spin_unlock(&catas_lock); - - queue_work(mlx4_wq, &catas_work); - } + if (mlx4_is_slave(dev)) { + slave_read = swab32(readl(&priv->mfunc.comm->slave_read)); + if (mlx4_comm_internal_err(slave_read)) { + mlx4_warn(dev, "Internal error detected on the communication channel\n"); + goto internal_err; } - } else - mod_timer(&priv->catas_err.timer, - round_jiffies(jiffies + MLX4_CATAS_POLL_INTERVAL)); + } else if (readl(priv->catas_err.map)) { + dump_err_buf(dev); + goto internal_err; + } + + if (dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR) { + mlx4_warn(dev, "Internal error mark was detected on device\n"); + goto internal_err; + } + + mod_timer(&priv->catas_err.timer, + round_jiffies(jiffies + MLX4_CATAS_POLL_INTERVAL)); + return; + +internal_err: + if (mlx4_internal_err_reset) + queue_work(dev->persist->catas_wq, &dev->persist->catas_work); } static void catas_reset(struct work_struct *work) { - struct mlx4_priv *priv, *tmppriv; - struct mlx4_dev *dev; + struct mlx4_dev_persistent *persist = + container_of(work, struct mlx4_dev_persistent, + catas_work); - LIST_HEAD(tlist); - int ret; - - spin_lock_irq(&catas_lock); - list_splice_init(&catas_list, &tlist); - spin_unlock_irq(&catas_lock); - - list_for_each_entry_safe(priv, tmppriv, &tlist, catas_err.list) { - struct pci_dev *pdev = priv->dev.pdev; - - /* If the device is off-line, we cannot reset it */ - if (pci_channel_offline(pdev)) - continue; - - ret = mlx4_restart_one(priv->dev.pdev); - /* 'priv' now is not valid */ - if (ret) - pr_err("mlx4 %s: Reset failed (%d)\n", - pci_name(pdev), ret); - else { - dev = pci_get_drvdata(pdev); - mlx4_dbg(dev, "Reset succeeded\n"); - } - } + mlx4_handle_error_state(persist); } void mlx4_start_catas_poll(struct mlx4_dev *dev) @@ -126,22 +269,21 @@ void mlx4_start_catas_poll(struct mlx4_dev *dev) struct mlx4_priv *priv = mlx4_priv(dev); phys_addr_t addr; - /*If we are in SRIOV the default of the module param must be 0*/ - if (mlx4_is_mfunc(dev)) - internal_err_reset = 0; - INIT_LIST_HEAD(&priv->catas_err.list); init_timer(&priv->catas_err.timer); priv->catas_err.map = NULL; - addr = pci_resource_start(dev->pdev, priv->fw.catas_bar) + - priv->fw.catas_offset; + if (!mlx4_is_slave(dev)) { + addr = pci_resource_start(dev->persist->pdev, + priv->fw.catas_bar) + + priv->fw.catas_offset; - priv->catas_err.map = ioremap(addr, priv->fw.catas_size * 4); - if (!priv->catas_err.map) { - mlx4_warn(dev, "Failed to map internal error buffer at 0x%llx\n", - (unsigned long long) addr); - return; + priv->catas_err.map = ioremap(addr, priv->fw.catas_size * 4); + if (!priv->catas_err.map) { + mlx4_warn(dev, "Failed to map internal error buffer at 0x%llx\n", + (unsigned long long)addr); + return; + } } priv->catas_err.timer.data = (unsigned long) dev; @@ -157,15 +299,29 @@ void mlx4_stop_catas_poll(struct mlx4_dev *dev) del_timer_sync(&priv->catas_err.timer); - if (priv->catas_err.map) + if (priv->catas_err.map) { iounmap(priv->catas_err.map); + priv->catas_err.map = NULL; + } - spin_lock_irq(&catas_lock); - list_del(&priv->catas_err.list); - spin_unlock_irq(&catas_lock); + if (dev->persist->interface_state & MLX4_INTERFACE_STATE_DELETION) + flush_workqueue(dev->persist->catas_wq); } -void __init mlx4_catas_init(void) +int mlx4_catas_init(struct mlx4_dev *dev) { - INIT_WORK(&catas_work, catas_reset); + INIT_WORK(&dev->persist->catas_work, catas_reset); + dev->persist->catas_wq = create_singlethread_workqueue("mlx4_health"); + if (!dev->persist->catas_wq) + return -ENOMEM; + + return 0; +} + +void mlx4_catas_end(struct mlx4_dev *dev) +{ + if (dev->persist->catas_wq) { + destroy_workqueue(dev->persist->catas_wq); + dev->persist->catas_wq = NULL; + } } diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 5c93d1451c44..a681d7c0bb9f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -42,6 +42,7 @@ #include <linux/mlx4/device.h> #include <linux/semaphore.h> #include <rdma/ib_smi.h> +#include <linux/delay.h> #include <asm/io.h> @@ -182,6 +183,72 @@ static u8 mlx4_errno_to_status(int errno) } } +static int mlx4_internal_err_ret_value(struct mlx4_dev *dev, u16 op, + u8 op_modifier) +{ + switch (op) { + case MLX4_CMD_UNMAP_ICM: + case MLX4_CMD_UNMAP_ICM_AUX: + case MLX4_CMD_UNMAP_FA: + case MLX4_CMD_2RST_QP: + case MLX4_CMD_HW2SW_EQ: + case MLX4_CMD_HW2SW_CQ: + case MLX4_CMD_HW2SW_SRQ: + case MLX4_CMD_HW2SW_MPT: + case MLX4_CMD_CLOSE_HCA: + case MLX4_QP_FLOW_STEERING_DETACH: + case MLX4_CMD_FREE_RES: + case MLX4_CMD_CLOSE_PORT: + return CMD_STAT_OK; + + case MLX4_CMD_QP_ATTACH: + /* On Detach case return success */ + if (op_modifier == 0) + return CMD_STAT_OK; + return mlx4_status_to_errno(CMD_STAT_INTERNAL_ERR); + + default: + return mlx4_status_to_errno(CMD_STAT_INTERNAL_ERR); + } +} + +static int mlx4_closing_cmd_fatal_error(u16 op, u8 fw_status) +{ + /* Any error during the closing commands below is considered fatal */ + if (op == MLX4_CMD_CLOSE_HCA || + op == MLX4_CMD_HW2SW_EQ || + op == MLX4_CMD_HW2SW_CQ || + op == MLX4_CMD_2RST_QP || + op == MLX4_CMD_HW2SW_SRQ || + op == MLX4_CMD_SYNC_TPT || + op == MLX4_CMD_UNMAP_ICM || + op == MLX4_CMD_UNMAP_ICM_AUX || + op == MLX4_CMD_UNMAP_FA) + return 1; + /* Error on MLX4_CMD_HW2SW_MPT is fatal except when fw status equals + * CMD_STAT_REG_BOUND. + * This status indicates that memory region has memory windows bound to it + * which may result from invalid user space usage and is not fatal. + */ + if (op == MLX4_CMD_HW2SW_MPT && fw_status != CMD_STAT_REG_BOUND) + return 1; + return 0; +} + +static int mlx4_cmd_reset_flow(struct mlx4_dev *dev, u16 op, u8 op_modifier, + int err) +{ + /* Only if reset flow is really active return code is based on + * command, otherwise current error code is returned. + */ + if (mlx4_internal_err_reset) { + mlx4_enter_error_state(dev->persist); + err = mlx4_internal_err_ret_value(dev, op, op_modifier); + } + + return err; +} + static int comm_pending(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); @@ -190,16 +257,30 @@ static int comm_pending(struct mlx4_dev *dev) return (swab32(status) >> 31) != priv->cmd.comm_toggle; } -static void mlx4_comm_cmd_post(struct mlx4_dev *dev, u8 cmd, u16 param) +static int mlx4_comm_cmd_post(struct mlx4_dev *dev, u8 cmd, u16 param) { struct mlx4_priv *priv = mlx4_priv(dev); u32 val; + /* To avoid writing to unknown addresses after the device state was + * changed to internal error and the function was rest, + * check the INTERNAL_ERROR flag which is updated under + * device_state_mutex lock. + */ + mutex_lock(&dev->persist->device_state_mutex); + + if (dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR) { + mutex_unlock(&dev->persist->device_state_mutex); + return -EIO; + } + priv->cmd.comm_toggle ^= 1; val = param | (cmd << 16) | (priv->cmd.comm_toggle << 31); __raw_writel((__force u32) cpu_to_be32(val), &priv->mfunc.comm->slave_write); mmiowb(); + mutex_unlock(&dev->persist->device_state_mutex); + return 0; } static int mlx4_comm_cmd_poll(struct mlx4_dev *dev, u8 cmd, u16 param, @@ -219,7 +300,13 @@ static int mlx4_comm_cmd_poll(struct mlx4_dev *dev, u8 cmd, u16 param, /* Write command */ down(&priv->cmd.poll_sem); - mlx4_comm_cmd_post(dev, cmd, param); + if (mlx4_comm_cmd_post(dev, cmd, param)) { + /* Only in case the device state is INTERNAL_ERROR, + * mlx4_comm_cmd_post returns with an error + */ + err = mlx4_status_to_errno(CMD_STAT_INTERNAL_ERR); + goto out; + } end = msecs_to_jiffies(timeout) + jiffies; while (comm_pending(dev) && time_before(jiffies, end)) @@ -231,18 +318,23 @@ static int mlx4_comm_cmd_poll(struct mlx4_dev *dev, u8 cmd, u16 param, * is MLX4_DELAY_RESET_SLAVE*/ if ((MLX4_COMM_CMD_RESET == cmd)) { err = MLX4_DELAY_RESET_SLAVE; + goto out; } else { - mlx4_warn(dev, "Communication channel timed out\n"); - err = -ETIMEDOUT; + mlx4_warn(dev, "Communication channel command 0x%x timed out\n", + cmd); + err = mlx4_status_to_errno(CMD_STAT_INTERNAL_ERR); } } + if (err) + mlx4_enter_error_state(dev->persist); +out: up(&priv->cmd.poll_sem); return err; } -static int mlx4_comm_cmd_wait(struct mlx4_dev *dev, u8 op, - u16 param, unsigned long timeout) +static int mlx4_comm_cmd_wait(struct mlx4_dev *dev, u8 vhcr_cmd, + u16 param, u16 op, unsigned long timeout) { struct mlx4_cmd *cmd = &mlx4_priv(dev)->cmd; struct mlx4_cmd_context *context; @@ -258,34 +350,49 @@ static int mlx4_comm_cmd_wait(struct mlx4_dev *dev, u8 op, cmd->free_head = context->next; spin_unlock(&cmd->context_lock); - init_completion(&context->done); + reinit_completion(&context->done); - mlx4_comm_cmd_post(dev, op, param); + if (mlx4_comm_cmd_post(dev, vhcr_cmd, param)) { + /* Only in case the device state is INTERNAL_ERROR, + * mlx4_comm_cmd_post returns with an error + */ + err = mlx4_status_to_errno(CMD_STAT_INTERNAL_ERR); + goto out; + } if (!wait_for_completion_timeout(&context->done, msecs_to_jiffies(timeout))) { - mlx4_warn(dev, "communication channel command 0x%x timed out\n", - op); - err = -EBUSY; - goto out; + mlx4_warn(dev, "communication channel command 0x%x (op=0x%x) timed out\n", + vhcr_cmd, op); + goto out_reset; } err = context->result; if (err && context->fw_status != CMD_STAT_MULTI_FUNC_REQ) { mlx4_err(dev, "command 0x%x failed: fw status = 0x%x\n", - op, context->fw_status); - goto out; + vhcr_cmd, context->fw_status); + if (mlx4_closing_cmd_fatal_error(op, context->fw_status)) + goto out_reset; } -out: /* wait for comm channel ready * this is necessary for prevention the race * when switching between event to polling mode + * Skipping this section in case the device is in FATAL_ERROR state, + * In this state, no commands are sent via the comm channel until + * the device has returned from reset. */ - end = msecs_to_jiffies(timeout) + jiffies; - while (comm_pending(dev) && time_before(jiffies, end)) - cond_resched(); + if (!(dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR)) { + end = msecs_to_jiffies(timeout) + jiffies; + while (comm_pending(dev) && time_before(jiffies, end)) + cond_resched(); + } + goto out; +out_reset: + err = mlx4_status_to_errno(CMD_STAT_INTERNAL_ERR); + mlx4_enter_error_state(dev->persist); +out: spin_lock(&cmd->context_lock); context->next = cmd->free_head; cmd->free_head = context - cmd->context; @@ -296,10 +403,13 @@ out: } int mlx4_comm_cmd(struct mlx4_dev *dev, u8 cmd, u16 param, - unsigned long timeout) + u16 op, unsigned long timeout) { + if (dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR) + return mlx4_status_to_errno(CMD_STAT_INTERNAL_ERR); + if (mlx4_priv(dev)->cmd.use_events) - return mlx4_comm_cmd_wait(dev, cmd, param, timeout); + return mlx4_comm_cmd_wait(dev, cmd, param, op, timeout); return mlx4_comm_cmd_poll(dev, cmd, param, timeout); } @@ -307,7 +417,7 @@ static int cmd_pending(struct mlx4_dev *dev) { u32 status; - if (pci_channel_offline(dev->pdev)) + if (pci_channel_offline(dev->persist->pdev)) return -EIO; status = readl(mlx4_priv(dev)->cmd.hcr + HCR_STATUS_OFFSET); @@ -323,17 +433,21 @@ static int mlx4_cmd_post(struct mlx4_dev *dev, u64 in_param, u64 out_param, { struct mlx4_cmd *cmd = &mlx4_priv(dev)->cmd; u32 __iomem *hcr = cmd->hcr; - int ret = -EAGAIN; + int ret = -EIO; unsigned long end; - mutex_lock(&cmd->hcr_mutex); - - if (pci_channel_offline(dev->pdev)) { + mutex_lock(&dev->persist->device_state_mutex); + /* To avoid writing to unknown addresses after the device state was + * changed to internal error and the chip was reset, + * check the INTERNAL_ERROR flag which is updated under + * device_state_mutex lock. + */ + if (pci_channel_offline(dev->persist->pdev) || + (dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR)) { /* * Device is going through error recovery * and cannot accept commands. */ - ret = -EIO; goto out; } @@ -342,12 +456,11 @@ static int mlx4_cmd_post(struct mlx4_dev *dev, u64 in_param, u64 out_param, end += msecs_to_jiffies(GO_BIT_TIMEOUT_MSECS); while (cmd_pending(dev)) { - if (pci_channel_offline(dev->pdev)) { + if (pci_channel_offline(dev->persist->pdev)) { /* * Device is going through error recovery * and cannot accept commands. */ - ret = -EIO; goto out; } @@ -391,7 +504,11 @@ static int mlx4_cmd_post(struct mlx4_dev *dev, u64 in_param, u64 out_param, ret = 0; out: - mutex_unlock(&cmd->hcr_mutex); + if (ret) + mlx4_warn(dev, "Could not post command 0x%x: ret=%d, in_param=0x%llx, in_mod=0x%x, op_mod=0x%x\n", + op, ret, in_param, in_modifier, op_modifier); + mutex_unlock(&dev->persist->device_state_mutex); + return ret; } @@ -428,8 +545,11 @@ static int mlx4_slave_cmd(struct mlx4_dev *dev, u64 in_param, u64 *out_param, } ret = mlx4_status_to_errno(vhcr->status); } + if (ret && + dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR) + ret = mlx4_internal_err_ret_value(dev, op, op_modifier); } else { - ret = mlx4_comm_cmd(dev, MLX4_COMM_CMD_VHCR_POST, 0, + ret = mlx4_comm_cmd(dev, MLX4_COMM_CMD_VHCR_POST, 0, op, MLX4_COMM_TIME + timeout); if (!ret) { if (out_is_imm) { @@ -443,9 +563,14 @@ static int mlx4_slave_cmd(struct mlx4_dev *dev, u64 in_param, u64 *out_param, } } ret = mlx4_status_to_errno(vhcr->status); - } else - mlx4_err(dev, "failed execution of VHCR_POST command opcode 0x%x\n", - op); + } else { + if (dev->persist->state & + MLX4_DEVICE_STATE_INTERNAL_ERROR) + ret = mlx4_internal_err_ret_value(dev, op, + op_modifier); + else + mlx4_err(dev, "failed execution of VHCR_POST command opcode 0x%x\n", op); + } } mutex_unlock(&priv->cmd.slave_cmd_mutex); @@ -464,12 +589,12 @@ static int mlx4_cmd_poll(struct mlx4_dev *dev, u64 in_param, u64 *out_param, down(&priv->cmd.poll_sem); - if (pci_channel_offline(dev->pdev)) { + if (dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR) { /* * Device is going through error recovery * and cannot accept commands. */ - err = -EIO; + err = mlx4_internal_err_ret_value(dev, op, op_modifier); goto out; } @@ -483,16 +608,21 @@ static int mlx4_cmd_poll(struct mlx4_dev *dev, u64 in_param, u64 *out_param, err = mlx4_cmd_post(dev, in_param, out_param ? *out_param : 0, in_modifier, op_modifier, op, CMD_POLL_TOKEN, 0); if (err) - goto out; + goto out_reset; end = msecs_to_jiffies(timeout) + jiffies; while (cmd_pending(dev) && time_before(jiffies, end)) { - if (pci_channel_offline(dev->pdev)) { + if (pci_channel_offline(dev->persist->pdev)) { /* * Device is going through error recovery * and cannot accept commands. */ err = -EIO; + goto out_reset; + } + + if (dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR) { + err = mlx4_internal_err_ret_value(dev, op, op_modifier); goto out; } @@ -502,8 +632,8 @@ static int mlx4_cmd_poll(struct mlx4_dev *dev, u64 in_param, u64 *out_param, if (cmd_pending(dev)) { mlx4_warn(dev, "command 0x%x timed out (go bit not cleared)\n", op); - err = -ETIMEDOUT; - goto out; + err = -EIO; + goto out_reset; } if (out_is_imm) @@ -515,10 +645,17 @@ static int mlx4_cmd_poll(struct mlx4_dev *dev, u64 in_param, u64 *out_param, stat = be32_to_cpu((__force __be32) __raw_readl(hcr + HCR_STATUS_OFFSET)) >> 24; err = mlx4_status_to_errno(stat); - if (err) + if (err) { mlx4_err(dev, "command 0x%x failed: fw status = 0x%x\n", op, stat); + if (mlx4_closing_cmd_fatal_error(op, stat)) + goto out_reset; + goto out; + } +out_reset: + if (err) + err = mlx4_cmd_reset_flow(dev, op, op_modifier, err); out: up(&priv->cmd.poll_sem); return err; @@ -565,17 +702,19 @@ static int mlx4_cmd_wait(struct mlx4_dev *dev, u64 in_param, u64 *out_param, goto out; } - init_completion(&context->done); + reinit_completion(&context->done); - mlx4_cmd_post(dev, in_param, out_param ? *out_param : 0, - in_modifier, op_modifier, op, context->token, 1); + err = mlx4_cmd_post(dev, in_param, out_param ? *out_param : 0, + in_modifier, op_modifier, op, context->token, 1); + if (err) + goto out_reset; if (!wait_for_completion_timeout(&context->done, msecs_to_jiffies(timeout))) { mlx4_warn(dev, "command 0x%x timed out (go bit not cleared)\n", op); - err = -EBUSY; - goto out; + err = -EIO; + goto out_reset; } err = context->result; @@ -592,12 +731,20 @@ static int mlx4_cmd_wait(struct mlx4_dev *dev, u64 in_param, u64 *out_param, else mlx4_err(dev, "command 0x%x failed: fw status = 0x%x\n", op, context->fw_status); + if (dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR) + err = mlx4_internal_err_ret_value(dev, op, op_modifier); + else if (mlx4_closing_cmd_fatal_error(op, context->fw_status)) + goto out_reset; + goto out; } if (out_is_imm) *out_param = context->out_param; +out_reset: + if (err) + err = mlx4_cmd_reset_flow(dev, op, op_modifier, err); out: spin_lock(&cmd->context_lock); context->next = cmd->free_head; @@ -612,10 +759,13 @@ int __mlx4_cmd(struct mlx4_dev *dev, u64 in_param, u64 *out_param, int out_is_imm, u32 in_modifier, u8 op_modifier, u16 op, unsigned long timeout, int native) { - if (pci_channel_offline(dev->pdev)) - return -EIO; + if (pci_channel_offline(dev->persist->pdev)) + return mlx4_cmd_reset_flow(dev, op, op_modifier, -EIO); if (!mlx4_is_mfunc(dev) || (native && mlx4_is_master(dev))) { + if (dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR) + return mlx4_internal_err_ret_value(dev, op, + op_modifier); if (mlx4_priv(dev)->cmd.use_events) return mlx4_cmd_wait(dev, in_param, out_param, out_is_imm, in_modifier, @@ -631,7 +781,7 @@ int __mlx4_cmd(struct mlx4_dev *dev, u64 in_param, u64 *out_param, EXPORT_SYMBOL_GPL(__mlx4_cmd); -static int mlx4_ARM_COMM_CHANNEL(struct mlx4_dev *dev) +int mlx4_ARM_COMM_CHANNEL(struct mlx4_dev *dev) { return mlx4_cmd(dev, 0, 0, 0, MLX4_CMD_ARM_COMM_CHANNEL, MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); @@ -751,7 +901,9 @@ static int mlx4_MAD_IFC_wrapper(struct mlx4_dev *dev, int slave, index = be32_to_cpu(smp->attr_mod); if (port < 1 || port > dev->caps.num_ports) return -EINVAL; - table = kcalloc(dev->caps.pkey_table_len[port], sizeof *table, GFP_KERNEL); + table = kcalloc((dev->caps.pkey_table_len[port] / 32) + 1, + sizeof(*table) * 32, GFP_KERNEL); + if (!table) return -ENOMEM; /* need to get the full pkey table because the paravirtualized @@ -1071,7 +1223,7 @@ static struct mlx4_cmd_info cmd_info[] = { { .opcode = MLX4_CMD_HW2SW_EQ, .has_inbox = false, - .has_outbox = true, + .has_outbox = false, .out_is_imm = false, .encode_slave_id = true, .verify = NULL, @@ -1431,6 +1583,15 @@ static struct mlx4_cmd_info cmd_info[] = { .verify = NULL, .wrapper = mlx4_CMD_EPERM_wrapper }, + { + .opcode = MLX4_CMD_VIRT_PORT_MAP, + .has_inbox = false, + .has_outbox = false, + .out_is_imm = false, + .encode_slave_id = false, + .verify = NULL, + .wrapper = mlx4_CMD_EPERM_wrapper + }, }; static int mlx4_master_process_vhcr(struct mlx4_dev *dev, int slave, @@ -1460,8 +1621,10 @@ static int mlx4_master_process_vhcr(struct mlx4_dev *dev, int slave, ALIGN(sizeof(struct mlx4_vhcr_cmd), MLX4_ACCESS_MEM_ALIGN), 1); if (ret) { - mlx4_err(dev, "%s: Failed reading vhcr ret: 0x%x\n", - __func__, ret); + if (!(dev->persist->state & + MLX4_DEVICE_STATE_INTERNAL_ERROR)) + mlx4_err(dev, "%s: Failed reading vhcr ret: 0x%x\n", + __func__, ret); kfree(vhcr); return ret; } @@ -1500,11 +1663,14 @@ static int mlx4_master_process_vhcr(struct mlx4_dev *dev, int slave, goto out_status; } - if (mlx4_ACCESS_MEM(dev, inbox->dma, slave, - vhcr->in_param, - MLX4_MAILBOX_SIZE, 1)) { - mlx4_err(dev, "%s: Failed reading inbox (cmd:0x%x)\n", - __func__, cmd->opcode); + ret = mlx4_ACCESS_MEM(dev, inbox->dma, slave, + vhcr->in_param, + MLX4_MAILBOX_SIZE, 1); + if (ret) { + if (!(dev->persist->state & + MLX4_DEVICE_STATE_INTERNAL_ERROR)) + mlx4_err(dev, "%s: Failed reading inbox (cmd:0x%x)\n", + __func__, cmd->opcode); vhcr_cmd->status = CMD_STAT_INTERNAL_ERR; goto out_status; } @@ -1552,8 +1718,9 @@ static int mlx4_master_process_vhcr(struct mlx4_dev *dev, int slave, } if (err) { - mlx4_warn(dev, "vhcr command:0x%x slave:%d failed with error:%d, status %d\n", - vhcr->op, slave, vhcr->errno, err); + if (!(dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR)) + mlx4_warn(dev, "vhcr command:0x%x slave:%d failed with error:%d, status %d\n", + vhcr->op, slave, vhcr->errno, err); vhcr_cmd->status = mlx4_errno_to_status(err); goto out_status; } @@ -1568,7 +1735,9 @@ static int mlx4_master_process_vhcr(struct mlx4_dev *dev, int slave, /* If we failed to write back the outbox after the *command was successfully executed, we must fail this * slave, as it is now in undefined state */ - mlx4_err(dev, "%s:Failed writing outbox\n", __func__); + if (!(dev->persist->state & + MLX4_DEVICE_STATE_INTERNAL_ERROR)) + mlx4_err(dev, "%s:Failed writing outbox\n", __func__); goto out; } } @@ -1847,8 +2016,11 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd, break; case MLX4_COMM_CMD_VHCR_POST: if ((slave_state[slave].last_cmd != MLX4_COMM_CMD_VHCR_EN) && - (slave_state[slave].last_cmd != MLX4_COMM_CMD_VHCR_POST)) + (slave_state[slave].last_cmd != MLX4_COMM_CMD_VHCR_POST)) { + mlx4_warn(dev, "slave:%d is out of sync, cmd=0x%x, last command=0x%x, reset is needed\n", + slave, cmd, slave_state[slave].last_cmd); goto reset_slave; + } mutex_lock(&priv->cmd.slave_cmd_mutex); if (mlx4_master_process_vhcr(dev, slave, NULL)) { @@ -1882,7 +2054,18 @@ static void mlx4_master_do_cmd(struct mlx4_dev *dev, int slave, u8 cmd, reset_slave: /* cleanup any slave resources */ - mlx4_delete_all_resources_for_slave(dev, slave); + if (dev->persist->interface_state & MLX4_INTERFACE_STATE_UP) + mlx4_delete_all_resources_for_slave(dev, slave); + + if (cmd != MLX4_COMM_CMD_RESET) { + mlx4_warn(dev, "Turn on internal error to force reset, slave=%d, cmd=0x%x\n", + slave, cmd); + /* Turn on internal error letting slave reset itself immeditaly, + * otherwise it might take till timeout on command is passed + */ + reply |= ((u32)COMM_CHAN_EVENT_INTERNAL_ERR); + } + spin_lock_irqsave(&priv->mfunc.master.slave_state_lock, flags); if (!slave_state[slave].is_slave_going_down) slave_state[slave].last_cmd = MLX4_COMM_CMD_RESET; @@ -1958,17 +2141,28 @@ void mlx4_master_comm_channel(struct work_struct *work) static int sync_toggles(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); - int wr_toggle; - int rd_toggle; + u32 wr_toggle; + u32 rd_toggle; unsigned long end; - wr_toggle = swab32(readl(&priv->mfunc.comm->slave_write)) >> 31; - end = jiffies + msecs_to_jiffies(5000); + wr_toggle = swab32(readl(&priv->mfunc.comm->slave_write)); + if (wr_toggle == 0xffffffff) + end = jiffies + msecs_to_jiffies(30000); + else + end = jiffies + msecs_to_jiffies(5000); while (time_before(jiffies, end)) { - rd_toggle = swab32(readl(&priv->mfunc.comm->slave_read)) >> 31; - if (rd_toggle == wr_toggle) { - priv->cmd.comm_toggle = rd_toggle; + rd_toggle = swab32(readl(&priv->mfunc.comm->slave_read)); + if (wr_toggle == 0xffffffff || rd_toggle == 0xffffffff) { + /* PCI might be offline */ + msleep(100); + wr_toggle = swab32(readl(&priv->mfunc.comm-> + slave_write)); + continue; + } + + if (rd_toggle >> 31 == wr_toggle >> 31) { + priv->cmd.comm_toggle = rd_toggle >> 31; return 0; } @@ -1997,11 +2191,12 @@ int mlx4_multi_func_init(struct mlx4_dev *dev) if (mlx4_is_master(dev)) priv->mfunc.comm = - ioremap(pci_resource_start(dev->pdev, priv->fw.comm_bar) + + ioremap(pci_resource_start(dev->persist->pdev, + priv->fw.comm_bar) + priv->fw.comm_base, MLX4_COMM_PAGESIZE); else priv->mfunc.comm = - ioremap(pci_resource_start(dev->pdev, 2) + + ioremap(pci_resource_start(dev->persist->pdev, 2) + MLX4_SLAVE_COMM_BASE, MLX4_COMM_PAGESIZE); if (!priv->mfunc.comm) { mlx4_err(dev, "Couldn't map communication vector\n"); @@ -2073,13 +2268,6 @@ int mlx4_multi_func_init(struct mlx4_dev *dev) if (mlx4_init_resource_tracker(dev)) goto err_thread; - err = mlx4_ARM_COMM_CHANNEL(dev); - if (err) { - mlx4_err(dev, " Failed to arm comm channel eq: %x\n", - err); - goto err_resource; - } - } else { err = sync_toggles(dev); if (err) { @@ -2089,8 +2277,6 @@ int mlx4_multi_func_init(struct mlx4_dev *dev) } return 0; -err_resource: - mlx4_free_resource_tracker(dev, RES_TR_FREE_ALL); err_thread: flush_workqueue(priv->mfunc.master.comm_wq); destroy_workqueue(priv->mfunc.master.comm_wq); @@ -2107,9 +2293,9 @@ err_comm_admin: err_comm: iounmap(priv->mfunc.comm); err_vhcr: - dma_free_coherent(&(dev->pdev->dev), PAGE_SIZE, - priv->mfunc.vhcr, - priv->mfunc.vhcr_dma); + dma_free_coherent(&dev->persist->pdev->dev, PAGE_SIZE, + priv->mfunc.vhcr, + priv->mfunc.vhcr_dma); priv->mfunc.vhcr = NULL; return -ENOMEM; } @@ -2120,7 +2306,6 @@ int mlx4_cmd_init(struct mlx4_dev *dev) int flags = 0; if (!priv->cmd.initialized) { - mutex_init(&priv->cmd.hcr_mutex); mutex_init(&priv->cmd.slave_cmd_mutex); sema_init(&priv->cmd.poll_sem, 1); priv->cmd.use_events = 0; @@ -2130,8 +2315,8 @@ int mlx4_cmd_init(struct mlx4_dev *dev) } if (!mlx4_is_slave(dev) && !priv->cmd.hcr) { - priv->cmd.hcr = ioremap(pci_resource_start(dev->pdev, 0) + - MLX4_HCR_BASE, MLX4_HCR_SIZE); + priv->cmd.hcr = ioremap(pci_resource_start(dev->persist->pdev, + 0) + MLX4_HCR_BASE, MLX4_HCR_SIZE); if (!priv->cmd.hcr) { mlx4_err(dev, "Couldn't map command register\n"); goto err; @@ -2140,7 +2325,8 @@ int mlx4_cmd_init(struct mlx4_dev *dev) } if (mlx4_is_mfunc(dev) && !priv->mfunc.vhcr) { - priv->mfunc.vhcr = dma_alloc_coherent(&(dev->pdev->dev), PAGE_SIZE, + priv->mfunc.vhcr = dma_alloc_coherent(&dev->persist->pdev->dev, + PAGE_SIZE, &priv->mfunc.vhcr_dma, GFP_KERNEL); if (!priv->mfunc.vhcr) @@ -2150,7 +2336,8 @@ int mlx4_cmd_init(struct mlx4_dev *dev) } if (!priv->cmd.pool) { - priv->cmd.pool = pci_pool_create("mlx4_cmd", dev->pdev, + priv->cmd.pool = pci_pool_create("mlx4_cmd", + dev->persist->pdev, MLX4_MAILBOX_SIZE, MLX4_MAILBOX_SIZE, 0); if (!priv->cmd.pool) @@ -2166,6 +2353,27 @@ err: return -ENOMEM; } +void mlx4_report_internal_err_comm_event(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + int slave; + u32 slave_read; + + /* Report an internal error event to all + * communication channels. + */ + for (slave = 0; slave < dev->num_slaves; slave++) { + slave_read = swab32(readl(&priv->mfunc.comm[slave].slave_read)); + slave_read |= (u32)COMM_CHAN_EVENT_INTERNAL_ERR; + __raw_writel((__force u32)cpu_to_be32(slave_read), + &priv->mfunc.comm[slave].slave_read); + /* Make sure that our comm channel write doesn't + * get mixed in with writes from another CPU. + */ + mmiowb(); + } +} + void mlx4_multi_func_cleanup(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); @@ -2181,6 +2389,7 @@ void mlx4_multi_func_cleanup(struct mlx4_dev *dev) kfree(priv->mfunc.master.slave_state); kfree(priv->mfunc.master.vf_admin); kfree(priv->mfunc.master.vf_oper); + dev->num_slaves = 0; } iounmap(priv->mfunc.comm); @@ -2202,7 +2411,7 @@ void mlx4_cmd_cleanup(struct mlx4_dev *dev, int cleanup_mask) } if (mlx4_is_mfunc(dev) && priv->mfunc.vhcr && (cleanup_mask & MLX4_CMD_CLEANUP_VHCR)) { - dma_free_coherent(&(dev->pdev->dev), PAGE_SIZE, + dma_free_coherent(&dev->persist->pdev->dev, PAGE_SIZE, priv->mfunc.vhcr, priv->mfunc.vhcr_dma); priv->mfunc.vhcr = NULL; } @@ -2229,6 +2438,11 @@ int mlx4_cmd_use_events(struct mlx4_dev *dev) for (i = 0; i < priv->cmd.max_cmds; ++i) { priv->cmd.context[i].token = i; priv->cmd.context[i].next = i + 1; + /* To support fatal error flow, initialize all + * cmd contexts to allow simulating completions + * with complete() at any time. + */ + init_completion(&priv->cmd.context[i].done); } priv->cmd.context[priv->cmd.max_cmds - 1].next = -1; @@ -2306,8 +2520,9 @@ u32 mlx4_comm_get_version(void) static int mlx4_get_slave_indx(struct mlx4_dev *dev, int vf) { - if ((vf < 0) || (vf >= dev->num_vfs)) { - mlx4_err(dev, "Bad vf number:%d (number of activated vf: %d)\n", vf, dev->num_vfs); + if ((vf < 0) || (vf >= dev->persist->num_vfs)) { + mlx4_err(dev, "Bad vf number:%d (number of activated vf: %d)\n", + vf, dev->persist->num_vfs); return -EINVAL; } @@ -2316,7 +2531,7 @@ static int mlx4_get_slave_indx(struct mlx4_dev *dev, int vf) int mlx4_get_vf_indx(struct mlx4_dev *dev, int slave) { - if (slave < 1 || slave > dev->num_vfs) { + if (slave < 1 || slave > dev->persist->num_vfs) { mlx4_err(dev, "Bad slave number:%d (number of activated slaves: %lu)\n", slave, dev->num_slaves); @@ -2325,6 +2540,25 @@ int mlx4_get_vf_indx(struct mlx4_dev *dev, int slave) return slave - 1; } +void mlx4_cmd_wake_completions(struct mlx4_dev *dev) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_cmd_context *context; + int i; + + spin_lock(&priv->cmd.context_lock); + if (priv->cmd.context) { + for (i = 0; i < priv->cmd.max_cmds; ++i) { + context = &priv->cmd.context[i]; + context->fw_status = CMD_STAT_INTERNAL_ERR; + context->result = + mlx4_status_to_errno(CMD_STAT_INTERNAL_ERR); + complete(&context->done); + } + } + spin_unlock(&priv->cmd.context_lock); +} + struct mlx4_active_ports mlx4_get_active_ports(struct mlx4_dev *dev, int slave) { struct mlx4_active_ports actv_ports; @@ -2388,7 +2622,7 @@ struct mlx4_slaves_pport mlx4_phys_to_slaves_pport(struct mlx4_dev *dev, if (port <= 0 || port > dev->caps.num_ports) return slaves_pport; - for (i = 0; i < dev->num_vfs + 1; i++) { + for (i = 0; i < dev->persist->num_vfs + 1; i++) { struct mlx4_active_ports actv_ports = mlx4_get_active_ports(dev, i); if (test_bit(port - 1, actv_ports.ports)) @@ -2408,7 +2642,7 @@ struct mlx4_slaves_pport mlx4_phys_to_slaves_pport_actv( bitmap_zero(slaves_pport.slaves, MLX4_MFUNC_MAX); - for (i = 0; i < dev->num_vfs + 1; i++) { + for (i = 0; i < dev->persist->num_vfs + 1; i++) { struct mlx4_active_ports actv_ports = mlx4_get_active_ports(dev, i); if (bitmap_equal(crit_ports->ports, actv_ports.ports, diff --git a/drivers/net/ethernet/mellanox/mlx4/en_clock.c b/drivers/net/ethernet/mellanox/mlx4/en_clock.c index 999014413b1a..90b5309cdb5c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_clock.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_clock.c @@ -32,6 +32,7 @@ */ #include <linux/mlx4/device.h> +#include <linux/clocksource.h> #include "mlx4_en.h" @@ -147,12 +148,9 @@ static int mlx4_en_phc_adjtime(struct ptp_clock_info *ptp, s64 delta) struct mlx4_en_dev *mdev = container_of(ptp, struct mlx4_en_dev, ptp_clock_info); unsigned long flags; - s64 now; write_lock_irqsave(&mdev->clock_lock, flags); - now = timecounter_read(&mdev->clock); - now += delta; - timecounter_init(&mdev->clock, &mdev->cycles, now); + timecounter_adjtime(&mdev->clock, delta); write_unlock_irqrestore(&mdev->clock_lock, flags); return 0; @@ -243,7 +241,7 @@ void mlx4_en_init_timestamp(struct mlx4_en_dev *mdev) { struct mlx4_dev *dev = mdev->dev; unsigned long flags; - u64 ns; + u64 ns, zero = 0; rwlock_init(&mdev->clock_lock); @@ -268,7 +266,7 @@ void mlx4_en_init_timestamp(struct mlx4_en_dev *mdev) /* Calculate period in seconds to call the overflow watchdog - to make * sure counter is checked at least once every wrap around. */ - ns = cyclecounter_cyc2ns(&mdev->cycles, mdev->cycles.mask); + ns = cyclecounter_cyc2ns(&mdev->cycles, mdev->cycles.mask, zero, &zero); do_div(ns, NSEC_PER_SEC / 2 / HZ); mdev->overflow_period = ns; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c index 82322b1c8411..22da4d0d0f05 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c @@ -70,10 +70,10 @@ int mlx4_en_create_cq(struct mlx4_en_priv *priv, /* Allocate HW buffers on provided NUMA node. * dev->numa_node is used in mtt range allocation flow. */ - set_dev_node(&mdev->dev->pdev->dev, node); + set_dev_node(&mdev->dev->persist->pdev->dev, node); err = mlx4_alloc_hwq_res(mdev->dev, &cq->wqres, cq->buf_size, 2 * PAGE_SIZE); - set_dev_node(&mdev->dev->pdev->dev, mdev->dev->numa_node); + set_dev_node(&mdev->dev->persist->pdev->dev, mdev->dev->numa_node); if (err) goto err_cq; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index 90e0f045a6bc..a7b58ba8492b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -92,7 +92,7 @@ mlx4_en_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) (u16) (mdev->dev->caps.fw_ver >> 32), (u16) ((mdev->dev->caps.fw_ver >> 16) & 0xffff), (u16) (mdev->dev->caps.fw_ver & 0xffff)); - strlcpy(drvinfo->bus_info, pci_name(mdev->dev->pdev), + strlcpy(drvinfo->bus_info, pci_name(mdev->dev->persist->pdev), sizeof(drvinfo->bus_info)); drvinfo->n_stats = 0; drvinfo->regdump_len = 0; @@ -770,22 +770,20 @@ static int mlx4_en_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) return 0; } - proto_admin = cpu_to_be32(ptys_adv); - if (speed >= 0 && speed != priv->port_state.link_speed) - /* If speed was set then speed decides :-) */ - proto_admin = speed_set_ptys_admin(priv, speed, - ptys_reg.eth_proto_cap); + proto_admin = cmd->autoneg == AUTONEG_ENABLE ? + cpu_to_be32(ptys_adv) : + speed_set_ptys_admin(priv, speed, + ptys_reg.eth_proto_cap); proto_admin &= ptys_reg.eth_proto_cap; - - if (proto_admin == ptys_reg.eth_proto_admin) - return 0; /* Nothing to change */ - if (!proto_admin) { en_warn(priv, "Not supported link mode(s) requested, check supported link modes.\n"); return -EINVAL; /* nothing to change due to bad input */ } + if (proto_admin == ptys_reg.eth_proto_admin) + return 0; /* Nothing to change */ + en_dbg(DRV, priv, "mlx4_ACCESS_PTYS_REG SET: ptys_reg.eth_proto_admin = 0x%x\n", be32_to_cpu(proto_admin)); @@ -798,9 +796,9 @@ static int mlx4_en_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) return ret; } - en_warn(priv, "Port link mode changed, restarting port...\n"); mutex_lock(&priv->mdev->state_lock); if (priv->port_up) { + en_warn(priv, "Port link mode changed, restarting port...\n"); mlx4_en_stop_port(dev, 1); if (mlx4_en_start_port(dev)) en_err(priv, "Failed restarting port %d\n", priv->port); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_main.c b/drivers/net/ethernet/mellanox/mlx4/en_main.c index 9f16f754137b..58d5a07d0ff4 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_main.c @@ -214,6 +214,8 @@ static void mlx4_en_remove(struct mlx4_dev *dev, void *endev_ptr) iounmap(mdev->uar_map); mlx4_uar_free(dev, &mdev->priv_uar); mlx4_pd_free(dev, mdev->priv_pdn); + if (mdev->nb.notifier_call) + unregister_netdevice_notifier(&mdev->nb); kfree(mdev); } @@ -241,8 +243,8 @@ static void *mlx4_en_add(struct mlx4_dev *dev) spin_lock_init(&mdev->uar_lock); mdev->dev = dev; - mdev->dma_device = &(dev->pdev->dev); - mdev->pdev = dev->pdev; + mdev->dma_device = &dev->persist->pdev->dev; + mdev->pdev = dev->persist->pdev; mdev->device_up = false; mdev->LSO_support = !!(dev->caps.flags & (1 << 15)); @@ -298,6 +300,12 @@ static void *mlx4_en_add(struct mlx4_dev *dev) if (mlx4_en_init_netdev(mdev, i, &mdev->profile.prof[i])) mdev->pndev[i] = NULL; } + /* register notifier */ + mdev->nb.notifier_call = mlx4_en_netdev_event; + if (register_netdevice_notifier(&mdev->nb)) { + mdev->nb.notifier_call = NULL; + mlx4_err(mdev, "Failed to create notifier\n"); + } return mdev; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index d0d6dc1b8e46..2a210c4efb89 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -475,7 +475,8 @@ static int mlx4_en_tunnel_steer_add(struct mlx4_en_priv *priv, unsigned char *ad { int err; - if (priv->mdev->dev->caps.tunnel_offload_mode != MLX4_TUNNEL_OFFLOAD_MODE_VXLAN) + if (priv->mdev->dev->caps.tunnel_offload_mode != MLX4_TUNNEL_OFFLOAD_MODE_VXLAN || + priv->mdev->dev->caps.dmfs_high_steer_mode == MLX4_STEERING_DMFS_A0_STATIC) return 0; /* do nothing */ err = mlx4_tunnel_steer_add(priv->mdev->dev, addr, priv->port, qpn, @@ -2061,6 +2062,7 @@ void mlx4_en_destroy_netdev(struct net_device *dev) /* Detach the netdev so tasks would not attempt to access it */ mutex_lock(&mdev->state_lock); mdev->pndev[priv->port] = NULL; + mdev->upper[priv->port] = NULL; mutex_unlock(&mdev->state_lock); mlx4_en_free_resources(priv); @@ -2200,6 +2202,10 @@ static int mlx4_en_set_features(struct net_device *netdev, return ret; } + if (DEV_FEATURE_CHANGED(netdev, features, NETIF_F_HW_VLAN_CTAG_TX)) + en_info(priv, "Turn %s TX vlan strip offload\n", + (features & NETIF_F_HW_VLAN_CTAG_TX) ? "ON" : "OFF"); + if (features & NETIF_F_LOOPBACK) priv->ctrl_flags |= cpu_to_be32(MLX4_WQE_CTRL_FORCE_LOOPBACK); else @@ -2440,6 +2446,180 @@ static const struct net_device_ops mlx4_netdev_ops_master = { #endif }; +struct mlx4_en_bond { + struct work_struct work; + struct mlx4_en_priv *priv; + int is_bonded; + struct mlx4_port_map port_map; +}; + +static void mlx4_en_bond_work(struct work_struct *work) +{ + struct mlx4_en_bond *bond = container_of(work, + struct mlx4_en_bond, + work); + int err = 0; + struct mlx4_dev *dev = bond->priv->mdev->dev; + + if (bond->is_bonded) { + if (!mlx4_is_bonded(dev)) { + err = mlx4_bond(dev); + if (err) + en_err(bond->priv, "Fail to bond device\n"); + } + if (!err) { + err = mlx4_port_map_set(dev, &bond->port_map); + if (err) + en_err(bond->priv, "Fail to set port map [%d][%d]: %d\n", + bond->port_map.port1, + bond->port_map.port2, + err); + } + } else if (mlx4_is_bonded(dev)) { + err = mlx4_unbond(dev); + if (err) + en_err(bond->priv, "Fail to unbond device\n"); + } + dev_put(bond->priv->dev); + kfree(bond); +} + +static int mlx4_en_queue_bond_work(struct mlx4_en_priv *priv, int is_bonded, + u8 v2p_p1, u8 v2p_p2) +{ + struct mlx4_en_bond *bond = NULL; + + bond = kzalloc(sizeof(*bond), GFP_ATOMIC); + if (!bond) + return -ENOMEM; + + INIT_WORK(&bond->work, mlx4_en_bond_work); + bond->priv = priv; + bond->is_bonded = is_bonded; + bond->port_map.port1 = v2p_p1; + bond->port_map.port2 = v2p_p2; + dev_hold(priv->dev); + queue_work(priv->mdev->workqueue, &bond->work); + return 0; +} + +int mlx4_en_netdev_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct net_device *ndev = netdev_notifier_info_to_dev(ptr); + u8 port = 0; + struct mlx4_en_dev *mdev; + struct mlx4_dev *dev; + int i, num_eth_ports = 0; + bool do_bond = true; + struct mlx4_en_priv *priv; + u8 v2p_port1 = 0; + u8 v2p_port2 = 0; + + if (!net_eq(dev_net(ndev), &init_net)) + return NOTIFY_DONE; + + mdev = container_of(this, struct mlx4_en_dev, nb); + dev = mdev->dev; + + /* Go into this mode only when two network devices set on two ports + * of the same mlx4 device are slaves of the same bonding master + */ + mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_ETH) { + ++num_eth_ports; + if (!port && (mdev->pndev[i] == ndev)) + port = i; + mdev->upper[i] = mdev->pndev[i] ? + netdev_master_upper_dev_get(mdev->pndev[i]) : NULL; + /* condition not met: network device is a slave */ + if (!mdev->upper[i]) + do_bond = false; + if (num_eth_ports < 2) + continue; + /* condition not met: same master */ + if (mdev->upper[i] != mdev->upper[i-1]) + do_bond = false; + } + /* condition not met: 2 salves */ + do_bond = (num_eth_ports == 2) ? do_bond : false; + + /* handle only events that come with enough info */ + if ((do_bond && (event != NETDEV_BONDING_INFO)) || !port) + return NOTIFY_DONE; + + priv = netdev_priv(ndev); + if (do_bond) { + struct netdev_notifier_bonding_info *notifier_info = ptr; + struct netdev_bonding_info *bonding_info = + ¬ifier_info->bonding_info; + + /* required mode 1, 2 or 4 */ + if ((bonding_info->master.bond_mode != BOND_MODE_ACTIVEBACKUP) && + (bonding_info->master.bond_mode != BOND_MODE_XOR) && + (bonding_info->master.bond_mode != BOND_MODE_8023AD)) + do_bond = false; + + /* require exactly 2 slaves */ + if (bonding_info->master.num_slaves != 2) + do_bond = false; + + /* calc v2p */ + if (do_bond) { + if (bonding_info->master.bond_mode == + BOND_MODE_ACTIVEBACKUP) { + /* in active-backup mode virtual ports are + * mapped to the physical port of the active + * slave */ + if (bonding_info->slave.state == + BOND_STATE_BACKUP) { + if (port == 1) { + v2p_port1 = 2; + v2p_port2 = 2; + } else { + v2p_port1 = 1; + v2p_port2 = 1; + } + } else { /* BOND_STATE_ACTIVE */ + if (port == 1) { + v2p_port1 = 1; + v2p_port2 = 1; + } else { + v2p_port1 = 2; + v2p_port2 = 2; + } + } + } else { /* Active-Active */ + /* in active-active mode a virtual port is + * mapped to the native physical port if and only + * if the physical port is up */ + __s8 link = bonding_info->slave.link; + + if (port == 1) + v2p_port2 = 2; + else + v2p_port1 = 1; + if ((link == BOND_LINK_UP) || + (link == BOND_LINK_FAIL)) { + if (port == 1) + v2p_port1 = 1; + else + v2p_port2 = 2; + } else { /* BOND_LINK_DOWN || BOND_LINK_BACK */ + if (port == 1) + v2p_port1 = 2; + else + v2p_port2 = 1; + } + } + } + } + + mlx4_en_queue_bond_work(priv, do_bond, + v2p_port1, v2p_port2); + + return NOTIFY_DONE; +} + int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, struct mlx4_en_port_profile *prof) { @@ -2457,7 +2637,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, netif_set_real_num_tx_queues(dev, prof->tx_ring_num); netif_set_real_num_rx_queues(dev, prof->rx_ring_num); - SET_NETDEV_DEV(dev, &mdev->dev->pdev->dev); + SET_NETDEV_DEV(dev, &mdev->dev->persist->pdev->dev); dev->dev_port = port - 1; /* @@ -2622,6 +2802,7 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, } mdev->pndev[port] = dev; + mdev->upper[port] = NULL; netif_carrier_off(dev); mlx4_en_set_default_moderation(priv); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_resources.c b/drivers/net/ethernet/mellanox/mlx4/en_resources.c index f1a5500ff72d..34f2fdf4fe5d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_resources.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_resources.c @@ -50,10 +50,14 @@ void mlx4_en_fill_qp_context(struct mlx4_en_priv *priv, int size, int stride, context->mtu_msgmax = 0xff; if (!is_tx && !rss) context->rq_size_stride = ilog2(size) << 3 | (ilog2(stride) - 4); - if (is_tx) + if (is_tx) { context->sq_size_stride = ilog2(size) << 3 | (ilog2(stride) - 4); - else + if (mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_PORT_REMAP) + context->params2 |= MLX4_QP_BIT_FPP; + + } else { context->sq_size_stride = ilog2(TXBB_SIZE) - 4; + } context->usr_page = cpu_to_be32(mdev->priv_uar.index); context->local_qpn = cpu_to_be32(qpn); context->pri_path.ackto = 1 & 0x07; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index a0474eb94aa3..698d60de1255 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -162,6 +162,10 @@ static int mlx4_en_init_allocator(struct mlx4_en_priv *priv, if (mlx4_alloc_pages(priv, &ring->page_alloc[i], frag_info, GFP_KERNEL | __GFP_COLD)) goto out; + + en_dbg(DRV, priv, " frag %d allocator: - size:%d frags:%d\n", + i, ring->page_alloc[i].page_size, + atomic_read(&ring->page_alloc[i].page->_count)); } return 0; @@ -387,10 +391,10 @@ int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv, ring->rx_info, tmp); /* Allocate HW buffers on provided NUMA node */ - set_dev_node(&mdev->dev->pdev->dev, node); + set_dev_node(&mdev->dev->persist->pdev->dev, node); err = mlx4_alloc_hwq_res(mdev->dev, &ring->wqres, ring->buf_size, 2 * PAGE_SIZE); - set_dev_node(&mdev->dev->pdev->dev, mdev->dev->numa_node); + set_dev_node(&mdev->dev->persist->pdev->dev, mdev->dev->numa_node); if (err) goto err_info; @@ -1059,8 +1063,9 @@ void mlx4_en_calc_rx_buf(struct net_device *dev) (eff_mtu > buf_size + frag_sizes[i]) ? frag_sizes[i] : eff_mtu - buf_size; priv->frag_info[i].frag_prefix_size = buf_size; - priv->frag_info[i].frag_stride = ALIGN(frag_sizes[i], - SMP_CACHE_BYTES); + priv->frag_info[i].frag_stride = + ALIGN(priv->frag_info[i].frag_size, + SMP_CACHE_BYTES); buf_size += priv->frag_info[i].frag_size; i++; } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index e3357bf523df..55f9f5c5344e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -91,10 +91,10 @@ int mlx4_en_create_tx_ring(struct mlx4_en_priv *priv, ring->buf_size = ALIGN(size * ring->stride, MLX4_EN_PAGE_SIZE); /* Allocate HW buffers on provided NUMA node */ - set_dev_node(&mdev->dev->pdev->dev, node); + set_dev_node(&mdev->dev->persist->pdev->dev, node); err = mlx4_alloc_hwq_res(mdev->dev, &ring->wqres, ring->buf_size, 2 * PAGE_SIZE); - set_dev_node(&mdev->dev->pdev->dev, mdev->dev->numa_node); + set_dev_node(&mdev->dev->persist->pdev->dev, mdev->dev->numa_node); if (err) { en_err(priv, "Failed allocating hwq resources\n"); goto err_bounce; @@ -682,8 +682,8 @@ u16 mlx4_en_select_queue(struct net_device *dev, struct sk_buff *skb, if (dev->num_tc) return skb_tx_hash(dev, skb); - if (vlan_tx_tag_present(skb)) - up = vlan_tx_tag_get(skb) >> VLAN_PRIO_SHIFT; + if (skb_vlan_tag_present(skb)) + up = skb_vlan_tag_get(skb) >> VLAN_PRIO_SHIFT; return fallback(dev, skb) % rings_p_up + up * rings_p_up; } @@ -742,8 +742,8 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) goto tx_drop; } - if (vlan_tx_tag_present(skb)) - vlan_tag = vlan_tx_tag_get(skb); + if (skb_vlan_tag_present(skb)) + vlan_tag = skb_vlan_tag_get(skb); netdev_txq_bql_enqueue_prefetchw(ring->tx_queue); @@ -930,7 +930,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) real_size = (real_size / 16) & 0x3f; if (ring->bf_enabled && desc_size <= MAX_BF && !bounce && - !vlan_tx_tag_present(skb) && send_doorbell) { + !skb_vlan_tag_present(skb) && send_doorbell) { tx_desc->ctrl.bf_qpn = ring->doorbell_qpn | cpu_to_be32(real_size); @@ -952,7 +952,7 @@ netdev_tx_t mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) } else { tx_desc->ctrl.vlan_tag = cpu_to_be16(vlan_tag); tx_desc->ctrl.ins_vlan = MLX4_WQE_CTRL_INS_VLAN * - !!vlan_tx_tag_present(skb); + !!skb_vlan_tag_present(skb); tx_desc->ctrl.fence_size = real_size; /* Ensure new descriptor hits memory diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index 3d275fbaf0eb..264bc15c1ff2 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -88,6 +88,8 @@ static u64 get_async_ev_mask(struct mlx4_dev *dev) u64 async_ev_mask = MLX4_ASYNC_EVENT_MASK; if (dev->caps.flags & MLX4_DEV_CAP_FLAG_PORT_MNG_CHG_EV) async_ev_mask |= (1ull << MLX4_EVENT_TYPE_PORT_MNG_CHG_EVENT); + if (dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_RECOVERABLE_ERROR_EVENT) + async_ev_mask |= (1ull << MLX4_EVENT_TYPE_RECOVERABLE_ERROR_EVENT); return async_ev_mask; } @@ -237,7 +239,7 @@ int mlx4_gen_guid_change_eqe(struct mlx4_dev *dev, int slave, u8 port) struct mlx4_eqe eqe; /*don't send if we don't have the that slave */ - if (dev->num_vfs < slave) + if (dev->persist->num_vfs < slave) return 0; memset(&eqe, 0, sizeof eqe); @@ -255,7 +257,7 @@ int mlx4_gen_port_state_change_eqe(struct mlx4_dev *dev, int slave, u8 port, struct mlx4_eqe eqe; /*don't send if we don't have the that slave */ - if (dev->num_vfs < slave) + if (dev->persist->num_vfs < slave) return 0; memset(&eqe, 0, sizeof eqe); @@ -310,7 +312,7 @@ static void set_all_slave_state(struct mlx4_dev *dev, u8 port, int event) struct mlx4_slaves_pport slaves_pport = mlx4_phys_to_slaves_pport(dev, port); - for (i = 0; i < dev->num_vfs + 1; i++) + for (i = 0; i < dev->persist->num_vfs + 1; i++) if (test_bit(i, slaves_pport.slaves)) set_and_calc_slave_port_state(dev, i, port, event, &gen_event); @@ -429,8 +431,14 @@ void mlx4_master_handle_slave_flr(struct work_struct *work) if (MLX4_COMM_CMD_FLR == slave_state[i].last_cmd) { mlx4_dbg(dev, "mlx4_handle_slave_flr: clean slave: %d\n", i); - - mlx4_delete_all_resources_for_slave(dev, i); + /* In case of 'Reset flow' FLR can be generated for + * a slave before mlx4_load_one is done. + * make sure interface is up before trying to delete + * slave resources which weren't allocated yet. + */ + if (dev->persist->interface_state & + MLX4_INTERFACE_STATE_UP) + mlx4_delete_all_resources_for_slave(dev, i); /*return the slave to running mode*/ spin_lock_irqsave(&priv->mfunc.master.slave_state_lock, flags); slave_state[i].last_cmd = MLX4_COMM_CMD_RESET; @@ -560,7 +568,8 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) mlx4_priv(dev)->sense.do_sense_port[port] = 1; if (!mlx4_is_master(dev)) break; - for (i = 0; i < dev->num_vfs + 1; i++) { + for (i = 0; i < dev->persist->num_vfs + 1; + i++) { if (!test_bit(i, slaves_port.slaves)) continue; if (dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH) { @@ -596,7 +605,9 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) if (!mlx4_is_master(dev)) break; if (dev->caps.port_type[port] == MLX4_PORT_TYPE_ETH) - for (i = 0; i < dev->num_vfs + 1; i++) { + for (i = 0; + i < dev->persist->num_vfs + 1; + i++) { if (!test_bit(i, slaves_port.slaves)) continue; if (i == mlx4_master_func_num(dev)) @@ -727,6 +738,26 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) (unsigned long) eqe); break; + case MLX4_EVENT_TYPE_RECOVERABLE_ERROR_EVENT: + switch (eqe->subtype) { + case MLX4_RECOVERABLE_ERROR_EVENT_SUBTYPE_BAD_CABLE: + mlx4_warn(dev, "Bad cable detected on port %u\n", + eqe->event.bad_cable.port); + break; + case MLX4_RECOVERABLE_ERROR_EVENT_SUBTYPE_UNSUPPORTED_CABLE: + mlx4_warn(dev, "Unsupported cable detected\n"); + break; + default: + mlx4_dbg(dev, + "Unhandled recoverable error event detected: %02x(%02x) on EQ %d at index %u. owner=%x, nent=0x%x, ownership=%s\n", + eqe->type, eqe->subtype, eq->eqn, + eq->cons_index, eqe->owner, eq->nent, + !!(eqe->owner & 0x80) ^ + !!(eq->cons_index & eq->nent) ? "HW" : "SW"); + break; + } + break; + case MLX4_EVENT_TYPE_EEC_CATAS_ERROR: case MLX4_EVENT_TYPE_ECC_DETECT: default: @@ -837,12 +868,10 @@ static int mlx4_SW2HW_EQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, MLX4_CMD_WRAPPED); } -static int mlx4_HW2SW_EQ(struct mlx4_dev *dev, struct mlx4_cmd_mailbox *mailbox, - int eq_num) +static int mlx4_HW2SW_EQ(struct mlx4_dev *dev, int eq_num) { - return mlx4_cmd_box(dev, 0, mailbox->dma, eq_num, - 0, MLX4_CMD_HW2SW_EQ, MLX4_CMD_TIME_CLASS_A, - MLX4_CMD_WRAPPED); + return mlx4_cmd(dev, 0, eq_num, 1, MLX4_CMD_HW2SW_EQ, + MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); } static int mlx4_num_eq_uar(struct mlx4_dev *dev) @@ -865,7 +894,7 @@ static void __iomem *mlx4_get_eq_uar(struct mlx4_dev *dev, struct mlx4_eq *eq) if (!priv->eq_table.uar_map[index]) { priv->eq_table.uar_map[index] = - ioremap(pci_resource_start(dev->pdev, 2) + + ioremap(pci_resource_start(dev->persist->pdev, 2) + ((eq->eqn / 4) << PAGE_SHIFT), PAGE_SIZE); if (!priv->eq_table.uar_map[index]) { @@ -928,8 +957,10 @@ static int mlx4_create_eq(struct mlx4_dev *dev, int nent, eq_context = mailbox->buf; for (i = 0; i < npages; ++i) { - eq->page_list[i].buf = dma_alloc_coherent(&dev->pdev->dev, - PAGE_SIZE, &t, GFP_KERNEL); + eq->page_list[i].buf = dma_alloc_coherent(&dev->persist-> + pdev->dev, + PAGE_SIZE, &t, + GFP_KERNEL); if (!eq->page_list[i].buf) goto err_out_free_pages; @@ -995,7 +1026,7 @@ err_out_free_eq: err_out_free_pages: for (i = 0; i < npages; ++i) if (eq->page_list[i].buf) - dma_free_coherent(&dev->pdev->dev, PAGE_SIZE, + dma_free_coherent(&dev->persist->pdev->dev, PAGE_SIZE, eq->page_list[i].buf, eq->page_list[i].map); @@ -1013,7 +1044,6 @@ static void mlx4_free_eq(struct mlx4_dev *dev, struct mlx4_eq *eq) { struct mlx4_priv *priv = mlx4_priv(dev); - struct mlx4_cmd_mailbox *mailbox; int err; int i; /* CX3 is capable of extending the CQE/EQE from 32 to 64 bytes, with @@ -1021,36 +1051,21 @@ static void mlx4_free_eq(struct mlx4_dev *dev, */ int npages = PAGE_ALIGN(dev->caps.eqe_size * eq->nent) / PAGE_SIZE; - mailbox = mlx4_alloc_cmd_mailbox(dev); - if (IS_ERR(mailbox)) - return; - - err = mlx4_HW2SW_EQ(dev, mailbox, eq->eqn); + err = mlx4_HW2SW_EQ(dev, eq->eqn); if (err) mlx4_warn(dev, "HW2SW_EQ failed (%d)\n", err); - if (0) { - mlx4_dbg(dev, "Dumping EQ context %02x:\n", eq->eqn); - for (i = 0; i < sizeof (struct mlx4_eq_context) / 4; ++i) { - if (i % 4 == 0) - pr_cont("[%02x] ", i * 4); - pr_cont(" %08x", be32_to_cpup(mailbox->buf + i * 4)); - if ((i + 1) % 4 == 0) - pr_cont("\n"); - } - } synchronize_irq(eq->irq); tasklet_disable(&eq->tasklet_ctx.task); mlx4_mtt_cleanup(dev, &eq->mtt); for (i = 0; i < npages; ++i) - dma_free_coherent(&dev->pdev->dev, PAGE_SIZE, - eq->page_list[i].buf, - eq->page_list[i].map); + dma_free_coherent(&dev->persist->pdev->dev, PAGE_SIZE, + eq->page_list[i].buf, + eq->page_list[i].map); kfree(eq->page_list); mlx4_bitmap_free(&priv->eq_table.bitmap, eq->eqn, MLX4_USE_RR); - mlx4_free_cmd_mailbox(dev, mailbox); } static void mlx4_free_irqs(struct mlx4_dev *dev) @@ -1060,7 +1075,7 @@ static void mlx4_free_irqs(struct mlx4_dev *dev) int i, vec; if (eq_table->have_irq) - free_irq(dev->pdev->irq, dev); + free_irq(dev->persist->pdev->irq, dev); for (i = 0; i < dev->caps.num_comp_vectors + 1; ++i) if (eq_table->eq[i].have_irq) { @@ -1089,7 +1104,8 @@ static int mlx4_map_clr_int(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); - priv->clr_base = ioremap(pci_resource_start(dev->pdev, priv->fw.clr_int_bar) + + priv->clr_base = ioremap(pci_resource_start(dev->persist->pdev, + priv->fw.clr_int_bar) + priv->fw.clr_int_base, MLX4_CLR_INT_SIZE); if (!priv->clr_base) { mlx4_err(dev, "Couldn't map interrupt clear register, aborting\n"); @@ -1212,13 +1228,13 @@ int mlx4_init_eq_table(struct mlx4_dev *dev) i * MLX4_IRQNAME_SIZE, MLX4_IRQNAME_SIZE, "mlx4-comp-%d@pci:%s", i, - pci_name(dev->pdev)); + pci_name(dev->persist->pdev)); } else { snprintf(priv->eq_table.irq_names + i * MLX4_IRQNAME_SIZE, MLX4_IRQNAME_SIZE, "mlx4-async@pci:%s", - pci_name(dev->pdev)); + pci_name(dev->persist->pdev)); } eq_name = priv->eq_table.irq_names + @@ -1235,8 +1251,8 @@ int mlx4_init_eq_table(struct mlx4_dev *dev) snprintf(priv->eq_table.irq_names, MLX4_IRQNAME_SIZE, DRV_NAME "@pci:%s", - pci_name(dev->pdev)); - err = request_irq(dev->pdev->irq, mlx4_interrupt, + pci_name(dev->persist->pdev)); + err = request_irq(dev->persist->pdev->irq, mlx4_interrupt, IRQF_SHARED, priv->eq_table.irq_names, dev); if (err) goto err_out_async; diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 982861d1df44..5a21e5dc94cb 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -84,13 +84,10 @@ static void dump_dev_cap_flags(struct mlx4_dev *dev, u64 flags) [ 1] = "UC transport", [ 2] = "UD transport", [ 3] = "XRC transport", - [ 4] = "reliable multicast", - [ 5] = "FCoIB support", [ 6] = "SRQ support", [ 7] = "IPoIB checksum offload", [ 8] = "P_Key violation counter", [ 9] = "Q_Key violation counter", - [10] = "VMM", [12] = "Dual Port Different Protocol (DPDP) support", [15] = "Big LSO headers", [16] = "MW support", @@ -99,12 +96,11 @@ static void dump_dev_cap_flags(struct mlx4_dev *dev, u64 flags) [19] = "Raw multicast support", [20] = "Address vector port checking support", [21] = "UD multicast support", - [24] = "Demand paging support", - [25] = "Router support", [30] = "IBoE support", [32] = "Unicast loopback support", [34] = "FCS header control", - [38] = "Wake On LAN support", + [37] = "Wake On LAN (port1) support", + [38] = "Wake On LAN (port2) support", [40] = "UDP RSS support", [41] = "Unicast VEP steering support", [42] = "Multicast VEP steering support", @@ -145,7 +141,9 @@ static void dump_dev_cap_flags2(struct mlx4_dev *dev, u64 flags) [16] = "CONFIG DEV support", [17] = "Asymmetric EQs support", [18] = "More than 80 VFs support", - [19] = "Performance optimized for limited rule configuration flow steering support" + [19] = "Performance optimized for limited rule configuration flow steering support", + [20] = "Recoverable error events support", + [21] = "Port Remap support" }; int i; @@ -259,6 +257,7 @@ int mlx4_QUERY_FUNC_CAP_wrapper(struct mlx4_dev *dev, int slave, #define QUERY_FUNC_CAP_MCG_QUOTA_OFFSET_DEP 0x28 #define QUERY_FUNC_CAP_MAX_EQ_OFFSET 0x2c #define QUERY_FUNC_CAP_RESERVED_EQ_OFFSET 0x30 +#define QUERY_FUNC_CAP_QP_RESD_LKEY_OFFSET 0x48 #define QUERY_FUNC_CAP_QP_QUOTA_OFFSET 0x50 #define QUERY_FUNC_CAP_CQ_QUOTA_OFFSET 0x54 @@ -273,6 +272,7 @@ int mlx4_QUERY_FUNC_CAP_wrapper(struct mlx4_dev *dev, int slave, #define QUERY_FUNC_CAP_FLAG_RDMA 0x40 #define QUERY_FUNC_CAP_FLAG_ETH 0x80 #define QUERY_FUNC_CAP_FLAG_QUOTAS 0x10 +#define QUERY_FUNC_CAP_FLAG_RESD_LKEY 0x08 #define QUERY_FUNC_CAP_FLAG_VALID_MAILBOX 0x04 #define QUERY_FUNC_CAP_EXTRA_FLAGS_BF_QP_ALLOC_FLAG (1UL << 31) @@ -344,9 +344,12 @@ int mlx4_QUERY_FUNC_CAP_wrapper(struct mlx4_dev *dev, int slave, } else if (vhcr->op_modifier == 0) { struct mlx4_active_ports actv_ports = mlx4_get_active_ports(dev, slave); - /* enable rdma and ethernet interfaces, and new quota locations */ + /* enable rdma and ethernet interfaces, new quota locations, + * and reserved lkey + */ field = (QUERY_FUNC_CAP_FLAG_ETH | QUERY_FUNC_CAP_FLAG_RDMA | - QUERY_FUNC_CAP_FLAG_QUOTAS | QUERY_FUNC_CAP_FLAG_VALID_MAILBOX); + QUERY_FUNC_CAP_FLAG_QUOTAS | QUERY_FUNC_CAP_FLAG_VALID_MAILBOX | + QUERY_FUNC_CAP_FLAG_RESD_LKEY); MLX4_PUT(outbox->buf, field, QUERY_FUNC_CAP_FLAGS_OFFSET); field = min( @@ -411,6 +414,9 @@ int mlx4_QUERY_FUNC_CAP_wrapper(struct mlx4_dev *dev, int slave, size = QUERY_FUNC_CAP_EXTRA_FLAGS_BF_QP_ALLOC_FLAG | QUERY_FUNC_CAP_EXTRA_FLAGS_A0_QP_ALLOC_FLAG; MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_EXTRA_FLAGS_OFFSET); + + size = dev->caps.reserved_lkey + ((slave << 8) & 0xFF00); + MLX4_PUT(outbox->buf, size, QUERY_FUNC_CAP_QP_RESD_LKEY_OFFSET); } else err = -EINVAL; @@ -503,6 +509,13 @@ int mlx4_QUERY_FUNC_CAP(struct mlx4_dev *dev, u8 gen_or_port, MLX4_GET(size, outbox, QUERY_FUNC_CAP_RESERVED_EQ_OFFSET); func_cap->reserved_eq = size & 0xFFFFFF; + if (func_cap->flags & QUERY_FUNC_CAP_FLAG_RESD_LKEY) { + MLX4_GET(size, outbox, QUERY_FUNC_CAP_QP_RESD_LKEY_OFFSET); + func_cap->reserved_lkey = size; + } else { + func_cap->reserved_lkey = 0; + } + func_cap->extra_flags = 0; /* Mailbox data from 0x6c and onward should only be treated if @@ -851,6 +864,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_EQE_STRIDE; MLX4_GET(dev_cap->bmme_flags, outbox, QUERY_DEV_CAP_BMME_FLAGS_OFFSET); + if (dev_cap->bmme_flags & MLX4_FLAG_PORT_REMAP) + dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_PORT_REMAP; MLX4_GET(field, outbox, QUERY_DEV_CAP_CONFIG_DEV_OFFSET); if (field & 0x20) dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_CONFIG_DEV; @@ -859,6 +874,8 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) MLX4_GET(field32, outbox, QUERY_DEV_CAP_ETH_BACKPL_OFFSET); if (field32 & (1 << 0)) dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_ETH_BACKPL_AN_REP; + if (field32 & (1 << 7)) + dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_RECOVERABLE_ERROR_EVENT; MLX4_GET(field, outbox, QUERY_DEV_CAP_FW_REASSIGN_MAC); if (field & 1<<6) dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_REASSIGN_MAC_EN; @@ -1106,9 +1123,10 @@ int mlx4_QUERY_DEV_CAP_wrapper(struct mlx4_dev *dev, int slave, field &= 0x7f; MLX4_PUT(outbox->buf, field, QUERY_DEV_CAP_BF_OFFSET); - /* For guests, disable mw type 2 */ + /* For guests, disable mw type 2 and port remap*/ MLX4_GET(bmme_flags, outbox->buf, QUERY_DEV_CAP_BMME_FLAGS_OFFSET); bmme_flags &= ~MLX4_BMME_FLAG_TYPE_2_WIN; + bmme_flags &= ~MLX4_FLAG_PORT_REMAP; MLX4_PUT(outbox->buf, bmme_flags, QUERY_DEV_CAP_BMME_FLAGS_OFFSET); /* turn off device-managed steering capability if not enabled */ @@ -1562,6 +1580,7 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param) #define INIT_HCA_VXLAN_OFFSET 0x0c #define INIT_HCA_CACHELINE_SZ_OFFSET 0x0e #define INIT_HCA_FLAGS_OFFSET 0x014 +#define INIT_HCA_RECOVERABLE_ERROR_EVENT_OFFSET 0x018 #define INIT_HCA_QPC_OFFSET 0x020 #define INIT_HCA_QPC_BASE_OFFSET (INIT_HCA_QPC_OFFSET + 0x10) #define INIT_HCA_LOG_QP_OFFSET (INIT_HCA_QPC_OFFSET + 0x17) @@ -1668,6 +1687,9 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param) dev->caps.userspace_caps |= MLX4_USER_DEV_CAP_LARGE_CQE; } + if (dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_RECOVERABLE_ERROR_EVENT) + *(inbox + INIT_HCA_RECOVERABLE_ERROR_EVENT_OFFSET / 4) |= cpu_to_be32(1 << 31); + /* QPC/EEC/CQC/EQC/RDMARC attributes */ MLX4_PUT(inbox, param->qpc_base, INIT_HCA_QPC_BASE_OFFSET); @@ -1752,8 +1774,8 @@ int mlx4_INIT_HCA(struct mlx4_dev *dev, struct mlx4_init_hca_param *param) MLX4_PUT(inbox, parser_params, INIT_HCA_VXLAN_OFFSET); } - err = mlx4_cmd(dev, mailbox->dma, 0, 0, MLX4_CMD_INIT_HCA, 10000, - MLX4_CMD_NATIVE); + err = mlx4_cmd(dev, mailbox->dma, 0, 0, MLX4_CMD_INIT_HCA, + MLX4_CMD_TIME_CLASS_C, MLX4_CMD_NATIVE); if (err) mlx4_err(dev, "INIT_HCA returns %d\n", err); @@ -1879,6 +1901,36 @@ out: return err; } +static int mlx4_hca_core_clock_update(struct mlx4_dev *dev) +{ + struct mlx4_cmd_mailbox *mailbox; + __be32 *outbox; + int err; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) { + mlx4_warn(dev, "hca_core_clock mailbox allocation failed\n"); + return PTR_ERR(mailbox); + } + outbox = mailbox->buf; + + err = mlx4_cmd_box(dev, 0, mailbox->dma, 0, 0, + MLX4_CMD_QUERY_HCA, + MLX4_CMD_TIME_CLASS_B, + !mlx4_is_slave(dev)); + if (err) { + mlx4_warn(dev, "hca_core_clock update failed\n"); + goto out; + } + + MLX4_GET(dev->caps.hca_core_clock, outbox, QUERY_HCA_CORE_CLOCK_OFFSET); + +out: + mlx4_free_cmd_mailbox(dev, mailbox); + + return err; +} + /* for IB-type ports only in SRIOV mode. Checks that both proxy QP0 * and real QP0 are active, so that the paravirtualized QP0 is ready * to operate */ @@ -1983,6 +2035,9 @@ int mlx4_INIT_PORT(struct mlx4_dev *dev, int port) err = mlx4_cmd(dev, 0, port, 0, MLX4_CMD_INIT_PORT, MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); + if (!err) + mlx4_hca_core_clock_update(dev); + return err; } EXPORT_SYMBOL_GPL(mlx4_INIT_PORT); @@ -2007,7 +2062,7 @@ int mlx4_CLOSE_PORT_wrapper(struct mlx4_dev *dev, int slave, if (dev->caps.port_mask[port] != MLX4_PORT_TYPE_IB) { if (priv->mfunc.master.init_port_ref[port] == 1) { err = mlx4_cmd(dev, 0, port, 0, MLX4_CMD_CLOSE_PORT, - 1000, MLX4_CMD_NATIVE); + MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE); if (err) return err; } @@ -2018,7 +2073,7 @@ int mlx4_CLOSE_PORT_wrapper(struct mlx4_dev *dev, int slave, if (!priv->mfunc.master.qp0_state[port].qp0_active && priv->mfunc.master.qp0_state[port].port_active) { err = mlx4_cmd(dev, 0, port, 0, MLX4_CMD_CLOSE_PORT, - 1000, MLX4_CMD_NATIVE); + MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE); if (err) return err; priv->mfunc.master.slave_state[slave].init_port_mask &= ~(1 << port); @@ -2033,15 +2088,15 @@ int mlx4_CLOSE_PORT_wrapper(struct mlx4_dev *dev, int slave, int mlx4_CLOSE_PORT(struct mlx4_dev *dev, int port) { - return mlx4_cmd(dev, 0, port, 0, MLX4_CMD_CLOSE_PORT, 1000, - MLX4_CMD_WRAPPED); + return mlx4_cmd(dev, 0, port, 0, MLX4_CMD_CLOSE_PORT, + MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); } EXPORT_SYMBOL_GPL(mlx4_CLOSE_PORT); int mlx4_CLOSE_HCA(struct mlx4_dev *dev, int panic) { - return mlx4_cmd(dev, 0, 0, panic, MLX4_CMD_CLOSE_HCA, 1000, - MLX4_CMD_NATIVE); + return mlx4_cmd(dev, 0, 0, panic, MLX4_CMD_CLOSE_HCA, + MLX4_CMD_TIME_CLASS_C, MLX4_CMD_NATIVE); } struct mlx4_config_dev { @@ -2049,13 +2104,16 @@ struct mlx4_config_dev { __be32 rsvd1[3]; __be16 vxlan_udp_dport; __be16 rsvd2; - __be32 rsvd3[27]; - __be16 rsvd4; - u8 rsvd5; + __be32 rsvd3; + __be32 roce_flags; + __be32 rsvd4[25]; + __be16 rsvd5; + u8 rsvd6; u8 rx_checksum_val; }; #define MLX4_VXLAN_UDP_DPORT (1 << 0) +#define MLX4_DISABLE_RX_PORT BIT(18) static int mlx4_CONFIG_DEV_set(struct mlx4_dev *dev, struct mlx4_config_dev *config_dev) { @@ -2111,7 +2169,7 @@ static const u8 config_dev_csum_flags[] = { int mlx4_config_dev_retrieval(struct mlx4_dev *dev, struct mlx4_config_dev_params *params) { - struct mlx4_config_dev config_dev; + struct mlx4_config_dev config_dev = {0}; int err; u8 csum_mask; @@ -2158,6 +2216,45 @@ int mlx4_config_vxlan_port(struct mlx4_dev *dev, __be16 udp_port) } EXPORT_SYMBOL_GPL(mlx4_config_vxlan_port); +#define CONFIG_DISABLE_RX_PORT BIT(15) +int mlx4_disable_rx_port_check(struct mlx4_dev *dev, bool dis) +{ + struct mlx4_config_dev config_dev; + + memset(&config_dev, 0, sizeof(config_dev)); + config_dev.update_flags = cpu_to_be32(MLX4_DISABLE_RX_PORT); + if (dis) + config_dev.roce_flags = + cpu_to_be32(CONFIG_DISABLE_RX_PORT); + + return mlx4_CONFIG_DEV_set(dev, &config_dev); +} + +int mlx4_virt2phy_port_map(struct mlx4_dev *dev, u32 port1, u32 port2) +{ + struct mlx4_cmd_mailbox *mailbox; + struct { + __be32 v_port1; + __be32 v_port2; + } *v2p; + int err; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return -ENOMEM; + + v2p = mailbox->buf; + v2p->v_port1 = cpu_to_be32(port1); + v2p->v_port2 = cpu_to_be32(port2); + + err = mlx4_cmd(dev, mailbox->dma, 0, + MLX4_SET_PORT_VIRT2PHY, MLX4_CMD_VIRT_PORT_MAP, + MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} + int mlx4_SET_ICM_SIZE(struct mlx4_dev *dev, u64 icm_size, u64 *aux_pages) { @@ -2180,7 +2277,8 @@ int mlx4_SET_ICM_SIZE(struct mlx4_dev *dev, u64 icm_size, u64 *aux_pages) int mlx4_NOP(struct mlx4_dev *dev) { /* Input modifier of 0x1f means "finish as soon as possible." */ - return mlx4_cmd(dev, 0, 0x1f, 0, MLX4_CMD_NOP, 100, MLX4_CMD_NATIVE); + return mlx4_cmd(dev, 0, 0x1f, 0, MLX4_CMD_NOP, MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); } int mlx4_get_phys_port_id(struct mlx4_dev *dev) diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.h b/drivers/net/ethernet/mellanox/mlx4/fw.h index 62562b60fa87..f44f7f6017ed 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.h +++ b/drivers/net/ethernet/mellanox/mlx4/fw.h @@ -147,6 +147,7 @@ struct mlx4_func_cap { u32 qp0_proxy_qpn; u32 qp1_tunnel_qpn; u32 qp1_proxy_qpn; + u32 reserved_lkey; u8 physical_port; u8 port_flags; u8 flags1; diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.c b/drivers/net/ethernet/mellanox/mlx4/icm.c index 97c9b1db1d27..2a9dd460a95f 100644 --- a/drivers/net/ethernet/mellanox/mlx4/icm.c +++ b/drivers/net/ethernet/mellanox/mlx4/icm.c @@ -56,7 +56,7 @@ static void mlx4_free_icm_pages(struct mlx4_dev *dev, struct mlx4_icm_chunk *chu int i; if (chunk->nsg > 0) - pci_unmap_sg(dev->pdev, chunk->mem, chunk->npages, + pci_unmap_sg(dev->persist->pdev, chunk->mem, chunk->npages, PCI_DMA_BIDIRECTIONAL); for (i = 0; i < chunk->npages; ++i) @@ -69,7 +69,8 @@ static void mlx4_free_icm_coherent(struct mlx4_dev *dev, struct mlx4_icm_chunk * int i; for (i = 0; i < chunk->npages; ++i) - dma_free_coherent(&dev->pdev->dev, chunk->mem[i].length, + dma_free_coherent(&dev->persist->pdev->dev, + chunk->mem[i].length, lowmem_page_address(sg_page(&chunk->mem[i])), sg_dma_address(&chunk->mem[i])); } @@ -173,7 +174,7 @@ struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages, --cur_order; if (coherent) - ret = mlx4_alloc_icm_coherent(&dev->pdev->dev, + ret = mlx4_alloc_icm_coherent(&dev->persist->pdev->dev, &chunk->mem[chunk->npages], cur_order, gfp_mask); else @@ -193,7 +194,7 @@ struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages, if (coherent) ++chunk->nsg; else if (chunk->npages == MLX4_ICM_CHUNK_LEN) { - chunk->nsg = pci_map_sg(dev->pdev, chunk->mem, + chunk->nsg = pci_map_sg(dev->persist->pdev, chunk->mem, chunk->npages, PCI_DMA_BIDIRECTIONAL); @@ -208,7 +209,7 @@ struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages, } if (!coherent && chunk) { - chunk->nsg = pci_map_sg(dev->pdev, chunk->mem, + chunk->nsg = pci_map_sg(dev->persist->pdev, chunk->mem, chunk->npages, PCI_DMA_BIDIRECTIONAL); diff --git a/drivers/net/ethernet/mellanox/mlx4/intf.c b/drivers/net/ethernet/mellanox/mlx4/intf.c index 116895ac8b35..6fce58718837 100644 --- a/drivers/net/ethernet/mellanox/mlx4/intf.c +++ b/drivers/net/ethernet/mellanox/mlx4/intf.c @@ -33,11 +33,13 @@ #include <linux/slab.h> #include <linux/export.h> +#include <linux/errno.h> #include "mlx4.h" struct mlx4_device_context { struct list_head list; + struct list_head bond_list; struct mlx4_interface *intf; void *context; }; @@ -115,6 +117,58 @@ void mlx4_unregister_interface(struct mlx4_interface *intf) } EXPORT_SYMBOL_GPL(mlx4_unregister_interface); +int mlx4_do_bond(struct mlx4_dev *dev, bool enable) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + struct mlx4_device_context *dev_ctx = NULL, *temp_dev_ctx; + unsigned long flags; + int ret; + LIST_HEAD(bond_list); + + if (!(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_PORT_REMAP)) + return -ENOTSUPP; + + ret = mlx4_disable_rx_port_check(dev, enable); + if (ret) { + mlx4_err(dev, "Fail to %s rx port check\n", + enable ? "enable" : "disable"); + return ret; + } + if (enable) { + dev->flags |= MLX4_FLAG_BONDED; + } else { + ret = mlx4_virt2phy_port_map(dev, 1, 2); + if (ret) { + mlx4_err(dev, "Fail to reset port map\n"); + return ret; + } + dev->flags &= ~MLX4_FLAG_BONDED; + } + + spin_lock_irqsave(&priv->ctx_lock, flags); + list_for_each_entry_safe(dev_ctx, temp_dev_ctx, &priv->ctx_list, list) { + if (dev_ctx->intf->flags & MLX4_INTFF_BONDING) { + list_add_tail(&dev_ctx->bond_list, &bond_list); + list_del(&dev_ctx->list); + } + } + spin_unlock_irqrestore(&priv->ctx_lock, flags); + + list_for_each_entry(dev_ctx, &bond_list, bond_list) { + dev_ctx->intf->remove(dev, dev_ctx->context); + dev_ctx->context = dev_ctx->intf->add(dev); + + spin_lock_irqsave(&priv->ctx_lock, flags); + list_add_tail(&dev_ctx->list, &priv->ctx_list); + spin_unlock_irqrestore(&priv->ctx_lock, flags); + + mlx4_dbg(dev, "Inrerface for protocol %d restarted with when bonded mode is %s\n", + dev_ctx->intf->protocol, enable ? + "enabled" : "disabled"); + } + return 0; +} + void mlx4_dispatch_event(struct mlx4_dev *dev, enum mlx4_dev_event type, unsigned long param) { @@ -138,13 +192,13 @@ int mlx4_register_device(struct mlx4_dev *dev) mutex_lock(&intf_mutex); + dev->persist->interface_state |= MLX4_INTERFACE_STATE_UP; list_add_tail(&priv->dev_list, &dev_list); list_for_each_entry(intf, &intf_list, list) mlx4_add_device(intf, priv); mutex_unlock(&intf_mutex); - if (!mlx4_is_slave(dev)) - mlx4_start_catas_poll(dev); + mlx4_start_catas_poll(dev); return 0; } @@ -154,14 +208,14 @@ void mlx4_unregister_device(struct mlx4_dev *dev) struct mlx4_priv *priv = mlx4_priv(dev); struct mlx4_interface *intf; - if (!mlx4_is_slave(dev)) - mlx4_stop_catas_poll(dev); + mlx4_stop_catas_poll(dev); mutex_lock(&intf_mutex); list_for_each_entry(intf, &intf_list, list) mlx4_remove_device(intf, priv); list_del(&priv->dev_list); + dev->persist->interface_state &= ~MLX4_INTERFACE_STATE_UP; mutex_unlock(&intf_mutex); } diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index 943cbd47d832..7e487223489a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -108,6 +108,8 @@ MODULE_PARM_DESC(enable_64b_cqe_eqe, MLX4_FUNC_CAP_EQE_CQE_STRIDE | \ MLX4_FUNC_CAP_DMFS_A0_STATIC) +#define RESET_PERSIST_MASK_FLAGS (MLX4_FLAG_SRIOV) + static char mlx4_version[] = DRV_NAME ": Mellanox ConnectX core driver v" DRV_VERSION " (" DRV_RELDATE ")\n"; @@ -249,7 +251,8 @@ static void mlx4_enable_cqe_eqe_stride(struct mlx4_dev *dev) if (mlx4_is_master(dev)) dev_cap->function_caps |= MLX4_FUNC_CAP_EQE_CQE_STRIDE; } else { - mlx4_dbg(dev, "Disabling CQE stride cacheLine unsupported\n"); + if (cache_line_size() != 32 && cache_line_size() != 64) + mlx4_dbg(dev, "Disabling CQE stride, cacheLine size unsupported\n"); dev_cap->flags2 &= ~MLX4_DEV_CAP_FLAG2_CQE_STRIDE; dev_cap->flags2 &= ~MLX4_DEV_CAP_FLAG2_EQE_STRIDE; } @@ -318,10 +321,11 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) return -ENODEV; } - if (dev_cap->uar_size > pci_resource_len(dev->pdev, 2)) { + if (dev_cap->uar_size > pci_resource_len(dev->persist->pdev, 2)) { mlx4_err(dev, "HCA reported UAR size of 0x%x bigger than PCI resource 2 size of 0x%llx, aborting\n", dev_cap->uar_size, - (unsigned long long) pci_resource_len(dev->pdev, 2)); + (unsigned long long) + pci_resource_len(dev->persist->pdev, 2)); return -ENODEV; } @@ -541,8 +545,10 @@ static int mlx4_get_pcie_dev_link_caps(struct mlx4_dev *dev, *speed = PCI_SPEED_UNKNOWN; *width = PCIE_LNK_WIDTH_UNKNOWN; - err1 = pcie_capability_read_dword(dev->pdev, PCI_EXP_LNKCAP, &lnkcap1); - err2 = pcie_capability_read_dword(dev->pdev, PCI_EXP_LNKCAP2, &lnkcap2); + err1 = pcie_capability_read_dword(dev->persist->pdev, PCI_EXP_LNKCAP, + &lnkcap1); + err2 = pcie_capability_read_dword(dev->persist->pdev, PCI_EXP_LNKCAP2, + &lnkcap2); if (!err2 && lnkcap2) { /* PCIe r3.0-compliant */ if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB) *speed = PCIE_SPEED_8_0GT; @@ -587,7 +593,7 @@ static void mlx4_check_pcie_caps(struct mlx4_dev *dev) return; } - err = pcie_get_minimum_link(dev->pdev, &speed, &width); + err = pcie_get_minimum_link(dev->persist->pdev, &speed, &width); if (err || speed == PCI_SPEED_UNKNOWN || width == PCIE_LNK_WIDTH_UNKNOWN) { mlx4_warn(dev, @@ -792,6 +798,7 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) dev->caps.num_mpts = 1 << hca_param.log_mpt_sz; dev->caps.num_eqs = func_cap.max_eq; dev->caps.reserved_eqs = func_cap.reserved_eq; + dev->caps.reserved_lkey = func_cap.reserved_lkey; dev->caps.num_pds = MLX4_NUM_PDS; dev->caps.num_mgms = 0; dev->caps.num_amgms = 0; @@ -837,10 +844,12 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) if (dev->caps.uar_page_size * (dev->caps.num_uars - dev->caps.reserved_uars) > - pci_resource_len(dev->pdev, 2)) { + pci_resource_len(dev->persist->pdev, + 2)) { mlx4_err(dev, "HCA reported UAR region size of 0x%x bigger than PCI resource 2 size of 0x%llx, aborting\n", dev->caps.uar_page_size * dev->caps.num_uars, - (unsigned long long) pci_resource_len(dev->pdev, 2)); + (unsigned long long) + pci_resource_len(dev->persist->pdev, 2)); goto err_mem; } @@ -1152,6 +1161,91 @@ err_set_port: return err ? err : count; } +int mlx4_bond(struct mlx4_dev *dev) +{ + int ret = 0; + struct mlx4_priv *priv = mlx4_priv(dev); + + mutex_lock(&priv->bond_mutex); + + if (!mlx4_is_bonded(dev)) + ret = mlx4_do_bond(dev, true); + else + ret = 0; + + mutex_unlock(&priv->bond_mutex); + if (ret) + mlx4_err(dev, "Failed to bond device: %d\n", ret); + else + mlx4_dbg(dev, "Device is bonded\n"); + return ret; +} +EXPORT_SYMBOL_GPL(mlx4_bond); + +int mlx4_unbond(struct mlx4_dev *dev) +{ + int ret = 0; + struct mlx4_priv *priv = mlx4_priv(dev); + + mutex_lock(&priv->bond_mutex); + + if (mlx4_is_bonded(dev)) + ret = mlx4_do_bond(dev, false); + + mutex_unlock(&priv->bond_mutex); + if (ret) + mlx4_err(dev, "Failed to unbond device: %d\n", ret); + else + mlx4_dbg(dev, "Device is unbonded\n"); + return ret; +} +EXPORT_SYMBOL_GPL(mlx4_unbond); + + +int mlx4_port_map_set(struct mlx4_dev *dev, struct mlx4_port_map *v2p) +{ + u8 port1 = v2p->port1; + u8 port2 = v2p->port2; + struct mlx4_priv *priv = mlx4_priv(dev); + int err; + + if (!(dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_PORT_REMAP)) + return -ENOTSUPP; + + mutex_lock(&priv->bond_mutex); + + /* zero means keep current mapping for this port */ + if (port1 == 0) + port1 = priv->v2p.port1; + if (port2 == 0) + port2 = priv->v2p.port2; + + if ((port1 < 1) || (port1 > MLX4_MAX_PORTS) || + (port2 < 1) || (port2 > MLX4_MAX_PORTS) || + (port1 == 2 && port2 == 1)) { + /* besides boundary checks cross mapping makes + * no sense and therefore not allowed */ + err = -EINVAL; + } else if ((port1 == priv->v2p.port1) && + (port2 == priv->v2p.port2)) { + err = 0; + } else { + err = mlx4_virt2phy_port_map(dev, port1, port2); + if (!err) { + mlx4_dbg(dev, "port map changed: [%d][%d]\n", + port1, port2); + priv->v2p.port1 = port1; + priv->v2p.port2 = port2; + } else { + mlx4_err(dev, "Failed to change port mape: %d\n", err); + } + } + + mutex_unlock(&priv->bond_mutex); + return err; +} +EXPORT_SYMBOL_GPL(mlx4_port_map_set); + static int mlx4_load_fw(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); @@ -1477,7 +1571,8 @@ static void mlx4_slave_exit(struct mlx4_dev *dev) struct mlx4_priv *priv = mlx4_priv(dev); mutex_lock(&priv->cmd.slave_cmd_mutex); - if (mlx4_comm_cmd(dev, MLX4_COMM_CMD_RESET, 0, MLX4_COMM_TIME)) + if (mlx4_comm_cmd(dev, MLX4_COMM_CMD_RESET, 0, MLX4_COMM_CMD_NA_OP, + MLX4_COMM_TIME)) mlx4_warn(dev, "Failed to close slave function\n"); mutex_unlock(&priv->cmd.slave_cmd_mutex); } @@ -1492,9 +1587,9 @@ static int map_bf_area(struct mlx4_dev *dev) if (!dev->caps.bf_reg_size) return -ENXIO; - bf_start = pci_resource_start(dev->pdev, 2) + + bf_start = pci_resource_start(dev->persist->pdev, 2) + (dev->caps.num_uars << PAGE_SHIFT); - bf_len = pci_resource_len(dev->pdev, 2) - + bf_len = pci_resource_len(dev->persist->pdev, 2) - (dev->caps.num_uars << PAGE_SHIFT); priv->bf_mapping = io_mapping_create_wc(bf_start, bf_len); if (!priv->bf_mapping) @@ -1536,7 +1631,8 @@ static int map_internal_clock(struct mlx4_dev *dev) struct mlx4_priv *priv = mlx4_priv(dev); priv->clock_mapping = - ioremap(pci_resource_start(dev->pdev, priv->fw.clock_bar) + + ioremap(pci_resource_start(dev->persist->pdev, + priv->fw.clock_bar) + priv->fw.clock_offset, MLX4_CLOCK_SIZE); if (!priv->clock_mapping) @@ -1573,6 +1669,50 @@ static void mlx4_close_fw(struct mlx4_dev *dev) } } +static int mlx4_comm_check_offline(struct mlx4_dev *dev) +{ +#define COMM_CHAN_OFFLINE_OFFSET 0x09 + + u32 comm_flags; + u32 offline_bit; + unsigned long end; + struct mlx4_priv *priv = mlx4_priv(dev); + + end = msecs_to_jiffies(MLX4_COMM_OFFLINE_TIME_OUT) + jiffies; + while (time_before(jiffies, end)) { + comm_flags = swab32(readl((__iomem char *)priv->mfunc.comm + + MLX4_COMM_CHAN_FLAGS)); + offline_bit = (comm_flags & + (u32)(1 << COMM_CHAN_OFFLINE_OFFSET)); + if (!offline_bit) + return 0; + /* There are cases as part of AER/Reset flow that PF needs + * around 100 msec to load. We therefore sleep for 100 msec + * to allow other tasks to make use of that CPU during this + * time interval. + */ + msleep(100); + } + mlx4_err(dev, "Communication channel is offline.\n"); + return -EIO; +} + +static void mlx4_reset_vf_support(struct mlx4_dev *dev) +{ +#define COMM_CHAN_RST_OFFSET 0x1e + + struct mlx4_priv *priv = mlx4_priv(dev); + u32 comm_rst; + u32 comm_caps; + + comm_caps = swab32(readl((__iomem char *)priv->mfunc.comm + + MLX4_COMM_CHAN_CAPS)); + comm_rst = (comm_caps & (u32)(1 << COMM_CHAN_RST_OFFSET)); + + if (comm_rst) + dev->caps.vf_caps |= MLX4_VF_CAP_FLAG_RESET; +} + static int mlx4_init_slave(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); @@ -1588,9 +1728,15 @@ static int mlx4_init_slave(struct mlx4_dev *dev) mutex_lock(&priv->cmd.slave_cmd_mutex); priv->cmd.max_cmds = 1; + if (mlx4_comm_check_offline(dev)) { + mlx4_err(dev, "PF is not responsive, skipping initialization\n"); + goto err_offline; + } + + mlx4_reset_vf_support(dev); mlx4_warn(dev, "Sending reset\n"); ret_from_reset = mlx4_comm_cmd(dev, MLX4_COMM_CMD_RESET, 0, - MLX4_COMM_TIME); + MLX4_COMM_CMD_NA_OP, MLX4_COMM_TIME); /* if we are in the middle of flr the slave will try * NUM_OF_RESET_RETRIES times before leaving.*/ if (ret_from_reset) { @@ -1615,22 +1761,24 @@ static int mlx4_init_slave(struct mlx4_dev *dev) mlx4_warn(dev, "Sending vhcr0\n"); if (mlx4_comm_cmd(dev, MLX4_COMM_CMD_VHCR0, dma >> 48, - MLX4_COMM_TIME)) + MLX4_COMM_CMD_NA_OP, MLX4_COMM_TIME)) goto err; if (mlx4_comm_cmd(dev, MLX4_COMM_CMD_VHCR1, dma >> 32, - MLX4_COMM_TIME)) + MLX4_COMM_CMD_NA_OP, MLX4_COMM_TIME)) goto err; if (mlx4_comm_cmd(dev, MLX4_COMM_CMD_VHCR2, dma >> 16, - MLX4_COMM_TIME)) + MLX4_COMM_CMD_NA_OP, MLX4_COMM_TIME)) goto err; - if (mlx4_comm_cmd(dev, MLX4_COMM_CMD_VHCR_EN, dma, MLX4_COMM_TIME)) + if (mlx4_comm_cmd(dev, MLX4_COMM_CMD_VHCR_EN, dma, + MLX4_COMM_CMD_NA_OP, MLX4_COMM_TIME)) goto err; mutex_unlock(&priv->cmd.slave_cmd_mutex); return 0; err: - mlx4_comm_cmd(dev, MLX4_COMM_CMD_RESET, 0, 0); + mlx4_comm_cmd(dev, MLX4_COMM_CMD_RESET, 0, MLX4_COMM_CMD_NA_OP, 0); +err_offline: mutex_unlock(&priv->cmd.slave_cmd_mutex); return -EIO; } @@ -1705,7 +1853,8 @@ static void choose_steering_mode(struct mlx4_dev *dev, if (mlx4_log_num_mgm_entry_size <= 0 && dev_cap->flags2 & MLX4_DEV_CAP_FLAG2_FS_EN && (!mlx4_is_mfunc(dev) || - (dev_cap->fs_max_num_qp_per_entry >= (dev->num_vfs + 1))) && + (dev_cap->fs_max_num_qp_per_entry >= + (dev->persist->num_vfs + 1))) && choose_log_fs_mgm_entry_size(dev_cap->fs_max_num_qp_per_entry) >= MLX4_MIN_MGM_LOG_ENTRY_SIZE) { dev->oper_log_mgm_entry_size = @@ -1744,8 +1893,7 @@ static void choose_tunnel_offload_mode(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) { if (dev->caps.steering_mode == MLX4_STEERING_MODE_DEVICE_MANAGED && - dev_cap->flags2 & MLX4_DEV_CAP_FLAG2_VXLAN_OFFLOADS && - dev->caps.dmfs_high_steer_mode != MLX4_STEERING_DMFS_A0_STATIC) + dev_cap->flags2 & MLX4_DEV_CAP_FLAG2_VXLAN_OFFLOADS) dev->caps.tunnel_offload_mode = MLX4_TUNNEL_OFFLOAD_MODE_VXLAN; else dev->caps.tunnel_offload_mode = MLX4_TUNNEL_OFFLOAD_MODE_NONE; @@ -1829,7 +1977,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev) err = mlx4_dev_cap(dev, &dev_cap); if (err) { mlx4_err(dev, "QUERY_DEV_CAP command failed, aborting\n"); - goto err_stop_fw; + return err; } choose_steering_mode(dev, &dev_cap); @@ -1860,7 +2008,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev) &init_hca); if ((long long) icm_size < 0) { err = icm_size; - goto err_stop_fw; + return err; } dev->caps.max_fmr_maps = (1 << (32 - ilog2(dev->caps.num_mpts))) - 1; @@ -1874,7 +2022,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev) err = mlx4_init_icm(dev, &dev_cap, &init_hca, icm_size); if (err) - goto err_stop_fw; + return err; err = mlx4_INIT_HCA(dev, &init_hca); if (err) { @@ -1886,7 +2034,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev) err = mlx4_query_func(dev, &dev_cap); if (err < 0) { mlx4_err(dev, "QUERY_FUNC command failed, aborting.\n"); - goto err_stop_fw; + goto err_close; } else if (err & MLX4_QUERY_FUNC_NUM_SYS_EQS) { dev->caps.num_eqs = dev_cap.max_eqs; dev->caps.reserved_eqs = dev_cap.reserved_eqs; @@ -2006,11 +2154,6 @@ err_free_icm: if (!mlx4_is_slave(dev)) mlx4_free_icms(dev); -err_stop_fw: - if (!mlx4_is_slave(dev)) { - mlx4_UNMAP_FA(dev); - mlx4_free_icm(dev, priv->fw.fw_icm, 0); - } return err; } @@ -2293,7 +2436,8 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev) for (i = 0; i < nreq; ++i) entries[i].entry = i; - nreq = pci_enable_msix_range(dev->pdev, entries, 2, nreq); + nreq = pci_enable_msix_range(dev->persist->pdev, entries, 2, + nreq); if (nreq < 0) { kfree(entries); @@ -2321,7 +2465,7 @@ no_msi: dev->caps.comp_pool = 0; for (i = 0; i < 2; ++i) - priv->eq_table.eq[i].irq = dev->pdev->irq; + priv->eq_table.eq[i].irq = dev->persist->pdev->irq; } static int mlx4_init_port_info(struct mlx4_dev *dev, int port) @@ -2349,7 +2493,7 @@ static int mlx4_init_port_info(struct mlx4_dev *dev, int port) info->port_attr.show = show_port_type; sysfs_attr_init(&info->port_attr.attr); - err = device_create_file(&dev->pdev->dev, &info->port_attr); + err = device_create_file(&dev->persist->pdev->dev, &info->port_attr); if (err) { mlx4_err(dev, "Failed to create file for port %d\n", port); info->port = -1; @@ -2366,10 +2510,12 @@ static int mlx4_init_port_info(struct mlx4_dev *dev, int port) info->port_mtu_attr.show = show_port_ib_mtu; sysfs_attr_init(&info->port_mtu_attr.attr); - err = device_create_file(&dev->pdev->dev, &info->port_mtu_attr); + err = device_create_file(&dev->persist->pdev->dev, + &info->port_mtu_attr); if (err) { mlx4_err(dev, "Failed to create mtu file for port %d\n", port); - device_remove_file(&info->dev->pdev->dev, &info->port_attr); + device_remove_file(&info->dev->persist->pdev->dev, + &info->port_attr); info->port = -1; } @@ -2381,8 +2527,9 @@ static void mlx4_cleanup_port_info(struct mlx4_port_info *info) if (info->port < 0) return; - device_remove_file(&info->dev->pdev->dev, &info->port_attr); - device_remove_file(&info->dev->pdev->dev, &info->port_mtu_attr); + device_remove_file(&info->dev->persist->pdev->dev, &info->port_attr); + device_remove_file(&info->dev->persist->pdev->dev, + &info->port_mtu_attr); } static int mlx4_init_steering(struct mlx4_dev *dev) @@ -2449,10 +2596,11 @@ static int mlx4_get_ownership(struct mlx4_dev *dev) void __iomem *owner; u32 ret; - if (pci_channel_offline(dev->pdev)) + if (pci_channel_offline(dev->persist->pdev)) return -EIO; - owner = ioremap(pci_resource_start(dev->pdev, 0) + MLX4_OWNER_BASE, + owner = ioremap(pci_resource_start(dev->persist->pdev, 0) + + MLX4_OWNER_BASE, MLX4_OWNER_SIZE); if (!owner) { mlx4_err(dev, "Failed to obtain ownership bit\n"); @@ -2468,10 +2616,11 @@ static void mlx4_free_ownership(struct mlx4_dev *dev) { void __iomem *owner; - if (pci_channel_offline(dev->pdev)) + if (pci_channel_offline(dev->persist->pdev)) return; - owner = ioremap(pci_resource_start(dev->pdev, 0) + MLX4_OWNER_BASE, + owner = ioremap(pci_resource_start(dev->persist->pdev, 0) + + MLX4_OWNER_BASE, MLX4_OWNER_SIZE); if (!owner) { mlx4_err(dev, "Failed to obtain ownership bit\n"); @@ -2486,11 +2635,19 @@ static void mlx4_free_ownership(struct mlx4_dev *dev) !!((flags) & MLX4_FLAG_MASTER)) static u64 mlx4_enable_sriov(struct mlx4_dev *dev, struct pci_dev *pdev, - u8 total_vfs, int existing_vfs) + u8 total_vfs, int existing_vfs, int reset_flow) { u64 dev_flags = dev->flags; int err = 0; + if (reset_flow) { + dev->dev_vfs = kcalloc(total_vfs, sizeof(*dev->dev_vfs), + GFP_KERNEL); + if (!dev->dev_vfs) + goto free_mem; + return dev_flags; + } + atomic_inc(&pf_loading); if (dev->flags & MLX4_FLAG_SRIOV) { if (existing_vfs != total_vfs) { @@ -2519,13 +2676,14 @@ static u64 mlx4_enable_sriov(struct mlx4_dev *dev, struct pci_dev *pdev, dev_flags |= MLX4_FLAG_SRIOV | MLX4_FLAG_MASTER; dev_flags &= ~MLX4_FLAG_SLAVE; - dev->num_vfs = total_vfs; + dev->persist->num_vfs = total_vfs; } return dev_flags; disable_sriov: atomic_dec(&pf_loading); - dev->num_vfs = 0; +free_mem: + dev->persist->num_vfs = 0; kfree(dev->dev_vfs); return dev_flags & ~MLX4_FLAG_MASTER; } @@ -2549,7 +2707,8 @@ static int mlx4_check_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap } static int mlx4_load_one(struct pci_dev *pdev, int pci_dev_data, - int total_vfs, int *nvfs, struct mlx4_priv *priv) + int total_vfs, int *nvfs, struct mlx4_priv *priv, + int reset_flow) { struct mlx4_dev *dev; unsigned sum = 0; @@ -2565,6 +2724,7 @@ static int mlx4_load_one(struct pci_dev *pdev, int pci_dev_data, spin_lock_init(&priv->ctx_lock); mutex_init(&priv->port_mutex); + mutex_init(&priv->bond_mutex); INIT_LIST_HEAD(&priv->pgdir_list); mutex_init(&priv->pgdir_mutex); @@ -2612,10 +2772,15 @@ static int mlx4_load_one(struct pci_dev *pdev, int pci_dev_data, existing_vfs = pci_num_vf(pdev); if (existing_vfs) dev->flags |= MLX4_FLAG_SRIOV; - dev->num_vfs = total_vfs; + dev->persist->num_vfs = total_vfs; } } + /* on load remove any previous indication of internal error, + * device is up. + */ + dev->persist->state = MLX4_DEVICE_STATE_UP; + slave_start: err = mlx4_cmd_init(dev); if (err) { @@ -2666,8 +2831,10 @@ slave_start: goto err_fw; if (!(dev_cap->flags2 & MLX4_DEV_CAP_FLAG2_SYS_EQS)) { - u64 dev_flags = mlx4_enable_sriov(dev, pdev, total_vfs, - existing_vfs); + u64 dev_flags = mlx4_enable_sriov(dev, pdev, + total_vfs, + existing_vfs, + reset_flow); mlx4_cmd_cleanup(dev, MLX4_CMD_CLEANUP_ALL); dev->flags = dev_flags; @@ -2709,7 +2876,7 @@ slave_start: if (dev->flags & MLX4_FLAG_SRIOV) { if (!existing_vfs) pci_disable_sriov(pdev); - if (mlx4_is_master(dev)) + if (mlx4_is_master(dev) && !reset_flow) atomic_dec(&pf_loading); dev->flags &= ~MLX4_FLAG_SRIOV; } @@ -2723,7 +2890,8 @@ slave_start: } if (mlx4_is_master(dev) && (dev_cap->flags2 & MLX4_DEV_CAP_FLAG2_SYS_EQS)) { - u64 dev_flags = mlx4_enable_sriov(dev, pdev, total_vfs, existing_vfs); + u64 dev_flags = mlx4_enable_sriov(dev, pdev, total_vfs, + existing_vfs, reset_flow); if ((dev->flags ^ dev_flags) & (MLX4_FLAG_MASTER | MLX4_FLAG_SLAVE)) { mlx4_cmd_cleanup(dev, MLX4_CMD_CLEANUP_VHCR); @@ -2776,12 +2944,14 @@ slave_start: dev->caps.num_ports); goto err_close; } - memcpy(dev->nvfs, nvfs, sizeof(dev->nvfs)); + memcpy(dev->persist->nvfs, nvfs, sizeof(dev->persist->nvfs)); - for (i = 0; i < sizeof(dev->nvfs)/sizeof(dev->nvfs[0]); i++) { + for (i = 0; + i < sizeof(dev->persist->nvfs)/ + sizeof(dev->persist->nvfs[0]); i++) { unsigned j; - for (j = 0; j < dev->nvfs[i]; ++sum, ++j) { + for (j = 0; j < dev->persist->nvfs[i]; ++sum, ++j) { dev->dev_vfs[sum].min_port = i < 2 ? i + 1 : 1; dev->dev_vfs[sum].n_ports = i < 2 ? 1 : dev->caps.num_ports; @@ -2833,6 +3003,17 @@ slave_start: goto err_steer; mlx4_init_quotas(dev); + /* When PF resources are ready arm its comm channel to enable + * getting commands + */ + if (mlx4_is_master(dev)) { + err = mlx4_ARM_COMM_CHANNEL(dev); + if (err) { + mlx4_err(dev, " Failed to arm comm channel eq: %x\n", + err); + goto err_steer; + } + } for (port = 1; port <= dev->caps.num_ports; port++) { err = mlx4_init_port_info(dev, port); @@ -2840,6 +3021,9 @@ slave_start: goto err_port; } + priv->v2p.port1 = 1; + priv->v2p.port2 = 2; + err = mlx4_register_device(dev); if (err) goto err_port; @@ -2851,7 +3035,7 @@ slave_start: priv->removed = 0; - if (mlx4_is_master(dev) && dev->num_vfs) + if (mlx4_is_master(dev) && dev->persist->num_vfs && !reset_flow) atomic_dec(&pf_loading); kfree(dev_cap); @@ -2885,8 +3069,10 @@ err_free_eq: mlx4_free_eq_table(dev); err_master_mfunc: - if (mlx4_is_master(dev)) + if (mlx4_is_master(dev)) { + mlx4_free_resource_tracker(dev, RES_TR_FREE_STRUCTS_ONLY); mlx4_multi_func_cleanup(dev); + } if (mlx4_is_slave(dev)) { kfree(dev->caps.qp0_qkey); @@ -2910,10 +3096,12 @@ err_cmd: mlx4_cmd_cleanup(dev, MLX4_CMD_CLEANUP_ALL); err_sriov: - if (dev->flags & MLX4_FLAG_SRIOV && !existing_vfs) + if (dev->flags & MLX4_FLAG_SRIOV && !existing_vfs) { pci_disable_sriov(pdev); + dev->flags &= ~MLX4_FLAG_SRIOV; + } - if (mlx4_is_master(dev) && dev->num_vfs) + if (mlx4_is_master(dev) && dev->persist->num_vfs && !reset_flow) atomic_dec(&pf_loading); kfree(priv->dev.dev_vfs); @@ -3054,11 +3242,19 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data, } } - err = mlx4_load_one(pdev, pci_dev_data, total_vfs, nvfs, priv); + err = mlx4_catas_init(&priv->dev); if (err) goto err_release_regions; + + err = mlx4_load_one(pdev, pci_dev_data, total_vfs, nvfs, priv, 0); + if (err) + goto err_catas; + return 0; +err_catas: + mlx4_catas_end(&priv->dev); + err_release_regions: pci_release_regions(pdev); @@ -3081,38 +3277,60 @@ static int mlx4_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return -ENOMEM; dev = &priv->dev; - dev->pdev = pdev; - pci_set_drvdata(pdev, dev); + dev->persist = kzalloc(sizeof(*dev->persist), GFP_KERNEL); + if (!dev->persist) { + kfree(priv); + return -ENOMEM; + } + dev->persist->pdev = pdev; + dev->persist->dev = dev; + pci_set_drvdata(pdev, dev->persist); priv->pci_dev_data = id->driver_data; + mutex_init(&dev->persist->device_state_mutex); + mutex_init(&dev->persist->interface_state_mutex); ret = __mlx4_init_one(pdev, id->driver_data, priv); - if (ret) + if (ret) { + kfree(dev->persist); kfree(priv); + } else { + pci_save_state(pdev); + } return ret; } +static void mlx4_clean_dev(struct mlx4_dev *dev) +{ + struct mlx4_dev_persistent *persist = dev->persist; + struct mlx4_priv *priv = mlx4_priv(dev); + unsigned long flags = (dev->flags & RESET_PERSIST_MASK_FLAGS); + + memset(priv, 0, sizeof(*priv)); + priv->dev.persist = persist; + priv->dev.flags = flags; +} + static void mlx4_unload_one(struct pci_dev *pdev) { - struct mlx4_dev *dev = pci_get_drvdata(pdev); + struct mlx4_dev_persistent *persist = pci_get_drvdata(pdev); + struct mlx4_dev *dev = persist->dev; struct mlx4_priv *priv = mlx4_priv(dev); int pci_dev_data; - int p; - int active_vfs = 0; + int p, i; if (priv->removed) return; + /* saving current ports type for further use */ + for (i = 0; i < dev->caps.num_ports; i++) { + dev->persist->curr_port_type[i] = dev->caps.port_type[i + 1]; + dev->persist->curr_port_poss_type[i] = dev->caps. + possible_type[i + 1]; + } + pci_dev_data = priv->pci_dev_data; - /* Disabling SR-IOV is not allowed while there are active vf's */ - if (mlx4_is_master(dev)) { - active_vfs = mlx4_how_many_lives_vf(dev); - if (active_vfs) { - pr_warn("Removing PF when there are active VF's !!\n"); - pr_warn("Will not disable SR-IOV.\n"); - } - } mlx4_stop_sense(dev); mlx4_unregister_device(dev); @@ -3156,12 +3374,6 @@ static void mlx4_unload_one(struct pci_dev *pdev) if (dev->flags & MLX4_FLAG_MSI_X) pci_disable_msix(pdev); - if (dev->flags & MLX4_FLAG_SRIOV && !active_vfs) { - mlx4_warn(dev, "Disabling SR-IOV\n"); - pci_disable_sriov(pdev); - dev->flags &= ~MLX4_FLAG_SRIOV; - dev->num_vfs = 0; - } if (!mlx4_is_slave(dev)) mlx4_free_ownership(dev); @@ -3173,42 +3385,96 @@ static void mlx4_unload_one(struct pci_dev *pdev) kfree(dev->caps.qp1_proxy); kfree(dev->dev_vfs); - memset(priv, 0, sizeof(*priv)); + mlx4_clean_dev(dev); priv->pci_dev_data = pci_dev_data; priv->removed = 1; } static void mlx4_remove_one(struct pci_dev *pdev) { - struct mlx4_dev *dev = pci_get_drvdata(pdev); + struct mlx4_dev_persistent *persist = pci_get_drvdata(pdev); + struct mlx4_dev *dev = persist->dev; struct mlx4_priv *priv = mlx4_priv(dev); + int active_vfs = 0; + + mutex_lock(&persist->interface_state_mutex); + persist->interface_state |= MLX4_INTERFACE_STATE_DELETION; + mutex_unlock(&persist->interface_state_mutex); + + /* Disabling SR-IOV is not allowed while there are active vf's */ + if (mlx4_is_master(dev) && dev->flags & MLX4_FLAG_SRIOV) { + active_vfs = mlx4_how_many_lives_vf(dev); + if (active_vfs) { + pr_warn("Removing PF when there are active VF's !!\n"); + pr_warn("Will not disable SR-IOV.\n"); + } + } + + /* device marked to be under deletion running now without the lock + * letting other tasks to be terminated + */ + if (persist->interface_state & MLX4_INTERFACE_STATE_UP) + mlx4_unload_one(pdev); + else + mlx4_info(dev, "%s: interface is down\n", __func__); + mlx4_catas_end(dev); + if (dev->flags & MLX4_FLAG_SRIOV && !active_vfs) { + mlx4_warn(dev, "Disabling SR-IOV\n"); + pci_disable_sriov(pdev); + } - mlx4_unload_one(pdev); pci_release_regions(pdev); pci_disable_device(pdev); + kfree(dev->persist); kfree(priv); pci_set_drvdata(pdev, NULL); } +static int restore_current_port_types(struct mlx4_dev *dev, + enum mlx4_port_type *types, + enum mlx4_port_type *poss_types) +{ + struct mlx4_priv *priv = mlx4_priv(dev); + int err, i; + + mlx4_stop_sense(dev); + + mutex_lock(&priv->port_mutex); + for (i = 0; i < dev->caps.num_ports; i++) + dev->caps.possible_type[i + 1] = poss_types[i]; + err = mlx4_change_port_types(dev, types); + mlx4_start_sense(dev); + mutex_unlock(&priv->port_mutex); + + return err; +} + int mlx4_restart_one(struct pci_dev *pdev) { - struct mlx4_dev *dev = pci_get_drvdata(pdev); + struct mlx4_dev_persistent *persist = pci_get_drvdata(pdev); + struct mlx4_dev *dev = persist->dev; struct mlx4_priv *priv = mlx4_priv(dev); int nvfs[MLX4_MAX_PORTS + 1] = {0, 0, 0}; int pci_dev_data, err, total_vfs; pci_dev_data = priv->pci_dev_data; - total_vfs = dev->num_vfs; - memcpy(nvfs, dev->nvfs, sizeof(dev->nvfs)); + total_vfs = dev->persist->num_vfs; + memcpy(nvfs, dev->persist->nvfs, sizeof(dev->persist->nvfs)); mlx4_unload_one(pdev); - err = mlx4_load_one(pdev, pci_dev_data, total_vfs, nvfs, priv); + err = mlx4_load_one(pdev, pci_dev_data, total_vfs, nvfs, priv, 1); if (err) { mlx4_err(dev, "%s: ERROR: mlx4_load_one failed, pci_name=%s, err=%d\n", __func__, pci_name(pdev), err); return err; } + err = restore_current_port_types(dev, dev->persist->curr_port_type, + dev->persist->curr_port_poss_type); + if (err) + mlx4_err(dev, "could not restore original port types (%d)\n", + err); + return err; } @@ -3263,23 +3529,79 @@ MODULE_DEVICE_TABLE(pci, mlx4_pci_table); static pci_ers_result_t mlx4_pci_err_detected(struct pci_dev *pdev, pci_channel_state_t state) { - mlx4_unload_one(pdev); + struct mlx4_dev_persistent *persist = pci_get_drvdata(pdev); - return state == pci_channel_io_perm_failure ? - PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_NEED_RESET; + mlx4_err(persist->dev, "mlx4_pci_err_detected was called\n"); + mlx4_enter_error_state(persist); + + mutex_lock(&persist->interface_state_mutex); + if (persist->interface_state & MLX4_INTERFACE_STATE_UP) + mlx4_unload_one(pdev); + + mutex_unlock(&persist->interface_state_mutex); + if (state == pci_channel_io_perm_failure) + return PCI_ERS_RESULT_DISCONNECT; + + pci_disable_device(pdev); + return PCI_ERS_RESULT_NEED_RESET; } static pci_ers_result_t mlx4_pci_slot_reset(struct pci_dev *pdev) { - struct mlx4_dev *dev = pci_get_drvdata(pdev); + struct mlx4_dev_persistent *persist = pci_get_drvdata(pdev); + struct mlx4_dev *dev = persist->dev; struct mlx4_priv *priv = mlx4_priv(dev); int ret; + int nvfs[MLX4_MAX_PORTS + 1] = {0, 0, 0}; + int total_vfs; - ret = __mlx4_init_one(pdev, priv->pci_dev_data, priv); + mlx4_err(dev, "mlx4_pci_slot_reset was called\n"); + ret = pci_enable_device(pdev); + if (ret) { + mlx4_err(dev, "Can not re-enable device, ret=%d\n", ret); + return PCI_ERS_RESULT_DISCONNECT; + } + + pci_set_master(pdev); + pci_restore_state(pdev); + pci_save_state(pdev); + + total_vfs = dev->persist->num_vfs; + memcpy(nvfs, dev->persist->nvfs, sizeof(dev->persist->nvfs)); + + mutex_lock(&persist->interface_state_mutex); + if (!(persist->interface_state & MLX4_INTERFACE_STATE_UP)) { + ret = mlx4_load_one(pdev, priv->pci_dev_data, total_vfs, nvfs, + priv, 1); + if (ret) { + mlx4_err(dev, "%s: mlx4_load_one failed, ret=%d\n", + __func__, ret); + goto end; + } + + ret = restore_current_port_types(dev, dev->persist-> + curr_port_type, dev->persist-> + curr_port_poss_type); + if (ret) + mlx4_err(dev, "could not restore original port types (%d)\n", ret); + } +end: + mutex_unlock(&persist->interface_state_mutex); return ret ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED; } +static void mlx4_shutdown(struct pci_dev *pdev) +{ + struct mlx4_dev_persistent *persist = pci_get_drvdata(pdev); + + mlx4_info(persist->dev, "mlx4_shutdown was called\n"); + mutex_lock(&persist->interface_state_mutex); + if (persist->interface_state & MLX4_INTERFACE_STATE_UP) + mlx4_unload_one(pdev); + mutex_unlock(&persist->interface_state_mutex); +} + static const struct pci_error_handlers mlx4_err_handler = { .error_detected = mlx4_pci_err_detected, .slot_reset = mlx4_pci_slot_reset, @@ -3289,7 +3611,7 @@ static struct pci_driver mlx4_driver = { .name = DRV_NAME, .id_table = mlx4_pci_table, .probe = mlx4_init_one, - .shutdown = mlx4_unload_one, + .shutdown = mlx4_shutdown, .remove = mlx4_remove_one, .err_handler = &mlx4_err_handler, }; @@ -3341,7 +3663,6 @@ static int __init mlx4_init(void) if (mlx4_verify_params()) return -EINVAL; - mlx4_catas_init(); mlx4_wq = create_singlethread_workqueue("mlx4"); if (!mlx4_wq) diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index a3867e7ef885..bd9ea0d01aae 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -1318,6 +1318,9 @@ out: mutex_unlock(&priv->mcg_table.mutex); mlx4_free_cmd_mailbox(dev, mailbox); + if (err && dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR) + /* In case device is under an error, return success as a closing command */ + err = 0; return err; } @@ -1347,6 +1350,9 @@ static int mlx4_QP_ATTACH(struct mlx4_dev *dev, struct mlx4_qp *qp, MLX4_CMD_WRAPPED); mlx4_free_cmd_mailbox(dev, mailbox); + if (err && !attach && + dev->persist->state & MLX4_DEVICE_STATE_INTERNAL_ERROR) + err = 0; return err; } diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index bdd4eea2247c..1409d0cd6143 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -85,7 +85,9 @@ enum { MLX4_CLR_INT_SIZE = 0x00008, MLX4_SLAVE_COMM_BASE = 0x0, MLX4_COMM_PAGESIZE = 0x1000, - MLX4_CLOCK_SIZE = 0x00008 + MLX4_CLOCK_SIZE = 0x00008, + MLX4_COMM_CHAN_CAPS = 0x8, + MLX4_COMM_CHAN_FLAGS = 0xc }; enum { @@ -120,6 +122,10 @@ enum mlx4_mpt_state { }; #define MLX4_COMM_TIME 10000 +#define MLX4_COMM_OFFLINE_TIME_OUT 30000 +#define MLX4_COMM_CMD_NA_OP 0x0 + + enum { MLX4_COMM_CMD_RESET, MLX4_COMM_CMD_VHCR0, @@ -190,6 +196,7 @@ struct mlx4_vhcr { struct mlx4_vhcr_cmd { __be64 in_param; __be32 in_modifier; + u32 reserved1; __be64 out_param; __be16 token; u16 reserved; @@ -221,21 +228,24 @@ extern int mlx4_debug_level; #define mlx4_dbg(mdev, format, ...) \ do { \ if (mlx4_debug_level) \ - dev_printk(KERN_DEBUG, &(mdev)->pdev->dev, format, \ + dev_printk(KERN_DEBUG, \ + &(mdev)->persist->pdev->dev, format, \ ##__VA_ARGS__); \ } while (0) #define mlx4_err(mdev, format, ...) \ - dev_err(&(mdev)->pdev->dev, format, ##__VA_ARGS__) + dev_err(&(mdev)->persist->pdev->dev, format, ##__VA_ARGS__) #define mlx4_info(mdev, format, ...) \ - dev_info(&(mdev)->pdev->dev, format, ##__VA_ARGS__) + dev_info(&(mdev)->persist->pdev->dev, format, ##__VA_ARGS__) #define mlx4_warn(mdev, format, ...) \ - dev_warn(&(mdev)->pdev->dev, format, ##__VA_ARGS__) + dev_warn(&(mdev)->persist->pdev->dev, format, ##__VA_ARGS__) extern int mlx4_log_num_mgm_entry_size; extern int log_mtts_per_seg; +extern int mlx4_internal_err_reset; -#define MLX4_MAX_NUM_SLAVES (MLX4_MAX_NUM_PF + MLX4_MAX_NUM_VF) +#define MLX4_MAX_NUM_SLAVES (min(MLX4_MAX_NUM_PF + MLX4_MAX_NUM_VF, \ + MLX4_MFUNC_MAX)) #define ALL_SLAVES 0xff struct mlx4_bitmap { @@ -606,7 +616,6 @@ struct mlx4_mgm { struct mlx4_cmd { struct pci_pool *pool; void __iomem *hcr; - struct mutex hcr_mutex; struct mutex slave_cmd_mutex; struct semaphore poll_sem; struct semaphore event_sem; @@ -877,6 +886,8 @@ struct mlx4_priv { int reserved_mtts; int fs_hash_mode; u8 virt2phys_pkey[MLX4_MFUNC_MAX][MLX4_MAX_PORTS][MLX4_MAX_PORT_PKEYS]; + struct mlx4_port_map v2p; /* cached port mapping configuration */ + struct mutex bond_mutex; /* for bond mode */ __be64 slave_node_guids[MLX4_MFUNC_MAX]; atomic_t opreq_count; @@ -994,7 +1005,8 @@ void __mlx4_xrcd_free(struct mlx4_dev *dev, u32 xrcdn); void mlx4_start_catas_poll(struct mlx4_dev *dev); void mlx4_stop_catas_poll(struct mlx4_dev *dev); -void mlx4_catas_init(void); +int mlx4_catas_init(struct mlx4_dev *dev); +void mlx4_catas_end(struct mlx4_dev *dev); int mlx4_restart_one(struct pci_dev *pdev); int mlx4_register_device(struct mlx4_dev *dev); void mlx4_unregister_device(struct mlx4_dev *dev); @@ -1160,13 +1172,14 @@ enum { int mlx4_cmd_init(struct mlx4_dev *dev); void mlx4_cmd_cleanup(struct mlx4_dev *dev, int cleanup_mask); int mlx4_multi_func_init(struct mlx4_dev *dev); +int mlx4_ARM_COMM_CHANNEL(struct mlx4_dev *dev); void mlx4_multi_func_cleanup(struct mlx4_dev *dev); void mlx4_cmd_event(struct mlx4_dev *dev, u16 token, u8 status, u64 out_param); int mlx4_cmd_use_events(struct mlx4_dev *dev); void mlx4_cmd_use_polling(struct mlx4_dev *dev); int mlx4_comm_cmd(struct mlx4_dev *dev, u8 cmd, u16 param, - unsigned long timeout); + u16 op, unsigned long timeout); void mlx4_cq_tasklet_cb(unsigned long data); void mlx4_cq_completion(struct mlx4_dev *dev, u32 cqn); @@ -1176,7 +1189,7 @@ void mlx4_qp_event(struct mlx4_dev *dev, u32 qpn, int event_type); void mlx4_srq_event(struct mlx4_dev *dev, u32 srqn, int event_type); -void mlx4_handle_catas_err(struct mlx4_dev *dev); +void mlx4_enter_error_state(struct mlx4_dev_persistent *persist); int mlx4_SENSE_PORT(struct mlx4_dev *dev, int port, enum mlx4_port_type *type); @@ -1354,6 +1367,7 @@ int mlx4_get_slave_num_gids(struct mlx4_dev *dev, int slave, int port); /* Returns the VF index of slave */ int mlx4_get_vf_indx(struct mlx4_dev *dev, int slave); int mlx4_config_mad_demux(struct mlx4_dev *dev); +int mlx4_do_bond(struct mlx4_dev *dev, bool enable); enum mlx4_zone_flags { MLX4_ZONE_ALLOW_ALLOC_FROM_LOWER_PRIO = 1UL << 0, diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index 944a112dff37..2a8268e6be15 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -390,6 +390,7 @@ struct mlx4_en_dev { struct pci_dev *pdev; struct mutex state_lock; struct net_device *pndev[MLX4_MAX_PORTS + 1]; + struct net_device *upper[MLX4_MAX_PORTS + 1]; u32 port_cnt; bool device_up; struct mlx4_en_profile profile; @@ -410,6 +411,7 @@ struct mlx4_en_dev { unsigned long overflow_period; struct ptp_clock *ptp_clock; struct ptp_clock_info ptp_clock_info; + struct notifier_block nb; }; @@ -845,6 +847,9 @@ int mlx4_en_reset_config(struct net_device *dev, struct hwtstamp_config ts_config, netdev_features_t new_features); +int mlx4_en_netdev_event(struct notifier_block *this, + unsigned long event, void *ptr); + /* * Functions for time stamping */ diff --git a/drivers/net/ethernet/mellanox/mlx4/mr.c b/drivers/net/ethernet/mellanox/mlx4/mr.c index d6f549685c0f..78f51e103880 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mr.c +++ b/drivers/net/ethernet/mellanox/mlx4/mr.c @@ -584,6 +584,7 @@ EXPORT_SYMBOL_GPL(mlx4_mr_free); void mlx4_mr_rereg_mem_cleanup(struct mlx4_dev *dev, struct mlx4_mr *mr) { mlx4_mtt_cleanup(dev, &mr->mtt); + mr->mtt.order = -1; } EXPORT_SYMBOL_GPL(mlx4_mr_rereg_mem_cleanup); @@ -593,18 +594,15 @@ int mlx4_mr_rereg_mem_write(struct mlx4_dev *dev, struct mlx4_mr *mr, { int err; - mpt_entry->start = cpu_to_be64(iova); - mpt_entry->length = cpu_to_be64(size); - mpt_entry->entity_size = cpu_to_be32(page_shift); - err = mlx4_mtt_init(dev, npages, page_shift, &mr->mtt); if (err) return err; - mpt_entry->pd_flags &= cpu_to_be32(MLX4_MPT_PD_MASK | - MLX4_MPT_PD_FLAG_EN_INV); - mpt_entry->flags &= cpu_to_be32(MLX4_MPT_FLAG_FREE | - MLX4_MPT_FLAG_SW_OWNS); + mpt_entry->start = cpu_to_be64(iova); + mpt_entry->length = cpu_to_be64(size); + mpt_entry->entity_size = cpu_to_be32(page_shift); + mpt_entry->flags &= ~(cpu_to_be32(MLX4_MPT_FLAG_FREE | + MLX4_MPT_FLAG_SW_OWNS)); if (mr->mtt.order < 0) { mpt_entry->flags |= cpu_to_be32(MLX4_MPT_FLAG_PHYSICAL); mpt_entry->mtt_addr = 0; @@ -707,13 +705,13 @@ static int mlx4_write_mtt_chunk(struct mlx4_dev *dev, struct mlx4_mtt *mtt, if (!mtts) return -ENOMEM; - dma_sync_single_for_cpu(&dev->pdev->dev, dma_handle, + dma_sync_single_for_cpu(&dev->persist->pdev->dev, dma_handle, npages * sizeof (u64), DMA_TO_DEVICE); for (i = 0; i < npages; ++i) mtts[i] = cpu_to_be64(page_list[i] | MLX4_MTT_FLAG_PRESENT); - dma_sync_single_for_device(&dev->pdev->dev, dma_handle, + dma_sync_single_for_device(&dev->persist->pdev->dev, dma_handle, npages * sizeof (u64), DMA_TO_DEVICE); return 0; @@ -1019,13 +1017,13 @@ int mlx4_map_phys_fmr(struct mlx4_dev *dev, struct mlx4_fmr *fmr, u64 *page_list /* Make sure MPT status is visible before writing MTT entries */ wmb(); - dma_sync_single_for_cpu(&dev->pdev->dev, fmr->dma_handle, + dma_sync_single_for_cpu(&dev->persist->pdev->dev, fmr->dma_handle, npages * sizeof(u64), DMA_TO_DEVICE); for (i = 0; i < npages; ++i) fmr->mtts[i] = cpu_to_be64(page_list[i] | MLX4_MTT_FLAG_PRESENT); - dma_sync_single_for_device(&dev->pdev->dev, fmr->dma_handle, + dma_sync_single_for_device(&dev->persist->pdev->dev, fmr->dma_handle, npages * sizeof(u64), DMA_TO_DEVICE); fmr->mpt->key = cpu_to_be32(key); @@ -1154,7 +1152,7 @@ EXPORT_SYMBOL_GPL(mlx4_fmr_free); int mlx4_SYNC_TPT(struct mlx4_dev *dev) { - return mlx4_cmd(dev, 0, 0, 0, MLX4_CMD_SYNC_TPT, 1000, - MLX4_CMD_NATIVE); + return mlx4_cmd(dev, 0, 0, 0, MLX4_CMD_SYNC_TPT, + MLX4_CMD_TIME_CLASS_A, MLX4_CMD_NATIVE); } EXPORT_SYMBOL_GPL(mlx4_SYNC_TPT); diff --git a/drivers/net/ethernet/mellanox/mlx4/pd.c b/drivers/net/ethernet/mellanox/mlx4/pd.c index 74216071201f..609c59dc854e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/pd.c +++ b/drivers/net/ethernet/mellanox/mlx4/pd.c @@ -151,11 +151,13 @@ int mlx4_uar_alloc(struct mlx4_dev *dev, struct mlx4_uar *uar) return -ENOMEM; if (mlx4_is_slave(dev)) - offset = uar->index % ((int) pci_resource_len(dev->pdev, 2) / + offset = uar->index % ((int)pci_resource_len(dev->persist->pdev, + 2) / dev->caps.uar_page_size); else offset = uar->index; - uar->pfn = (pci_resource_start(dev->pdev, 2) >> PAGE_SHIFT) + offset; + uar->pfn = (pci_resource_start(dev->persist->pdev, 2) >> PAGE_SHIFT) + + offset; uar->map = NULL; return 0; } @@ -212,7 +214,6 @@ int mlx4_bf_alloc(struct mlx4_dev *dev, struct mlx4_bf *bf, int node) list_add(&uar->bf_list, &priv->bf_list); } - bf->uar = uar; idx = ffz(uar->free_bf_bmap); uar->free_bf_bmap |= 1 << idx; bf->uar = uar; diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index 30eb1ead0fe6..9f268f05290a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -553,9 +553,9 @@ int mlx4_get_slave_num_gids(struct mlx4_dev *dev, int slave, int port) slaves_pport_actv = mlx4_phys_to_slaves_pport_actv( dev, &exclusive_ports); slave_gid -= bitmap_weight(slaves_pport_actv.slaves, - dev->num_vfs + 1); + dev->persist->num_vfs + 1); } - vfs = bitmap_weight(slaves_pport.slaves, dev->num_vfs + 1) - 1; + vfs = bitmap_weight(slaves_pport.slaves, dev->persist->num_vfs + 1) - 1; if (slave_gid <= ((MLX4_ROCE_MAX_GIDS - MLX4_ROCE_PF_GIDS) % vfs)) return ((MLX4_ROCE_MAX_GIDS - MLX4_ROCE_PF_GIDS) / vfs) + 1; return (MLX4_ROCE_MAX_GIDS - MLX4_ROCE_PF_GIDS) / vfs; @@ -590,10 +590,10 @@ int mlx4_get_base_gid_ix(struct mlx4_dev *dev, int slave, int port) slaves_pport_actv = mlx4_phys_to_slaves_pport_actv( dev, &exclusive_ports); slave_gid -= bitmap_weight(slaves_pport_actv.slaves, - dev->num_vfs + 1); + dev->persist->num_vfs + 1); } gids = MLX4_ROCE_MAX_GIDS - MLX4_ROCE_PF_GIDS; - vfs = bitmap_weight(slaves_pport.slaves, dev->num_vfs + 1) - 1; + vfs = bitmap_weight(slaves_pport.slaves, dev->persist->num_vfs + 1) - 1; if (slave_gid <= gids % vfs) return MLX4_ROCE_PF_GIDS + ((gids / vfs) + 1) * (slave_gid - 1); @@ -644,7 +644,7 @@ void mlx4_reset_roce_gids(struct mlx4_dev *dev, int slave) int num_eth_ports, err; int i; - if (slave < 0 || slave > dev->num_vfs) + if (slave < 0 || slave > dev->persist->num_vfs) return; actv_ports = mlx4_get_active_ports(dev, slave); @@ -1214,7 +1214,8 @@ int mlx4_get_slave_from_roce_gid(struct mlx4_dev *dev, int port, u8 *gid, return -EINVAL; slaves_pport = mlx4_phys_to_slaves_pport(dev, port); - num_vfs = bitmap_weight(slaves_pport.slaves, dev->num_vfs + 1) - 1; + num_vfs = bitmap_weight(slaves_pport.slaves, + dev->persist->num_vfs + 1) - 1; for (i = 0; i < MLX4_ROCE_MAX_GIDS; i++) { if (!memcmp(priv->port[port].gid_table.roce_gids[i].raw, gid, @@ -1258,7 +1259,7 @@ int mlx4_get_slave_from_roce_gid(struct mlx4_dev *dev, int port, u8 *gid, dev, &exclusive_ports); num_vfs_before += bitmap_weight( slaves_pport_actv.slaves, - dev->num_vfs + 1); + dev->persist->num_vfs + 1); } /* candidate_slave_gid isn't necessarily the correct slave, but @@ -1288,7 +1289,7 @@ int mlx4_get_slave_from_roce_gid(struct mlx4_dev *dev, int port, u8 *gid, dev, &exclusive_ports); slave_gid += bitmap_weight( slaves_pport_actv.slaves, - dev->num_vfs + 1); + dev->persist->num_vfs + 1); } } *slave_id = slave_gid; diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index 1586ecce13c7..2bb8553bd905 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -882,6 +882,8 @@ int mlx4_qp_to_ready(struct mlx4_dev *dev, struct mlx4_mtt *mtt, for (i = 0; i < ARRAY_SIZE(states) - 1; i++) { context->flags &= cpu_to_be32(~(0xf << 28)); context->flags |= cpu_to_be32(states[i + 1] << 28); + if (states[i + 1] != MLX4_QP_STATE_RTR) + context->params2 &= ~MLX4_QP_BIT_FPP; err = mlx4_qp_modify(dev, mtt, states[i], states[i + 1], context, 0, 0, qp); if (err) { diff --git a/drivers/net/ethernet/mellanox/mlx4/reset.c b/drivers/net/ethernet/mellanox/mlx4/reset.c index ea1c6d092145..0076d88587ca 100644 --- a/drivers/net/ethernet/mellanox/mlx4/reset.c +++ b/drivers/net/ethernet/mellanox/mlx4/reset.c @@ -76,19 +76,21 @@ int mlx4_reset(struct mlx4_dev *dev) goto out; } - pcie_cap = pci_pcie_cap(dev->pdev); + pcie_cap = pci_pcie_cap(dev->persist->pdev); for (i = 0; i < 64; ++i) { if (i == 22 || i == 23) continue; - if (pci_read_config_dword(dev->pdev, i * 4, hca_header + i)) { + if (pci_read_config_dword(dev->persist->pdev, i * 4, + hca_header + i)) { err = -ENODEV; mlx4_err(dev, "Couldn't save HCA PCI header, aborting\n"); goto out; } } - reset = ioremap(pci_resource_start(dev->pdev, 0) + MLX4_RESET_BASE, + reset = ioremap(pci_resource_start(dev->persist->pdev, 0) + + MLX4_RESET_BASE, MLX4_RESET_SIZE); if (!reset) { err = -ENOMEM; @@ -122,8 +124,8 @@ int mlx4_reset(struct mlx4_dev *dev) end = jiffies + MLX4_RESET_TIMEOUT_JIFFIES; do { - if (!pci_read_config_word(dev->pdev, PCI_VENDOR_ID, &vendor) && - vendor != 0xffff) + if (!pci_read_config_word(dev->persist->pdev, PCI_VENDOR_ID, + &vendor) && vendor != 0xffff) break; msleep(1); @@ -138,14 +140,16 @@ int mlx4_reset(struct mlx4_dev *dev) /* Now restore the PCI headers */ if (pcie_cap) { devctl = hca_header[(pcie_cap + PCI_EXP_DEVCTL) / 4]; - if (pcie_capability_write_word(dev->pdev, PCI_EXP_DEVCTL, + if (pcie_capability_write_word(dev->persist->pdev, + PCI_EXP_DEVCTL, devctl)) { err = -ENODEV; mlx4_err(dev, "Couldn't restore HCA PCI Express Device Control register, aborting\n"); goto out; } linkctl = hca_header[(pcie_cap + PCI_EXP_LNKCTL) / 4]; - if (pcie_capability_write_word(dev->pdev, PCI_EXP_LNKCTL, + if (pcie_capability_write_word(dev->persist->pdev, + PCI_EXP_LNKCTL, linkctl)) { err = -ENODEV; mlx4_err(dev, "Couldn't restore HCA PCI Express Link control register, aborting\n"); @@ -157,7 +161,8 @@ int mlx4_reset(struct mlx4_dev *dev) if (i * 4 == PCI_COMMAND) continue; - if (pci_write_config_dword(dev->pdev, i * 4, hca_header[i])) { + if (pci_write_config_dword(dev->persist->pdev, i * 4, + hca_header[i])) { err = -ENODEV; mlx4_err(dev, "Couldn't restore HCA reg %x, aborting\n", i); @@ -165,7 +170,7 @@ int mlx4_reset(struct mlx4_dev *dev) } } - if (pci_write_config_dword(dev->pdev, PCI_COMMAND, + if (pci_write_config_dword(dev->persist->pdev, PCI_COMMAND, hca_header[PCI_COMMAND / 4])) { err = -ENODEV; mlx4_err(dev, "Couldn't restore HCA COMMAND, aborting\n"); diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 4efbd1eca611..486e3d26cd4a 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -309,12 +309,13 @@ static inline int mlx4_grant_resource(struct mlx4_dev *dev, int slave, int allocated, free, reserved, guaranteed, from_free; int from_rsvd; - if (slave > dev->num_vfs) + if (slave > dev->persist->num_vfs) return -EINVAL; spin_lock(&res_alloc->alloc_lock); allocated = (port > 0) ? - res_alloc->allocated[(port - 1) * (dev->num_vfs + 1) + slave] : + res_alloc->allocated[(port - 1) * + (dev->persist->num_vfs + 1) + slave] : res_alloc->allocated[slave]; free = (port > 0) ? res_alloc->res_port_free[port - 1] : res_alloc->res_free; @@ -352,7 +353,8 @@ static inline int mlx4_grant_resource(struct mlx4_dev *dev, int slave, if (!err) { /* grant the request */ if (port > 0) { - res_alloc->allocated[(port - 1) * (dev->num_vfs + 1) + slave] += count; + res_alloc->allocated[(port - 1) * + (dev->persist->num_vfs + 1) + slave] += count; res_alloc->res_port_free[port - 1] -= count; res_alloc->res_port_rsvd[port - 1] -= from_rsvd; } else { @@ -376,13 +378,14 @@ static inline void mlx4_release_resource(struct mlx4_dev *dev, int slave, &priv->mfunc.master.res_tracker.res_alloc[res_type]; int allocated, guaranteed, from_rsvd; - if (slave > dev->num_vfs) + if (slave > dev->persist->num_vfs) return; spin_lock(&res_alloc->alloc_lock); allocated = (port > 0) ? - res_alloc->allocated[(port - 1) * (dev->num_vfs + 1) + slave] : + res_alloc->allocated[(port - 1) * + (dev->persist->num_vfs + 1) + slave] : res_alloc->allocated[slave]; guaranteed = res_alloc->guaranteed[slave]; @@ -397,7 +400,8 @@ static inline void mlx4_release_resource(struct mlx4_dev *dev, int slave, } if (port > 0) { - res_alloc->allocated[(port - 1) * (dev->num_vfs + 1) + slave] -= count; + res_alloc->allocated[(port - 1) * + (dev->persist->num_vfs + 1) + slave] -= count; res_alloc->res_port_free[port - 1] += count; res_alloc->res_port_rsvd[port - 1] += from_rsvd; } else { @@ -415,7 +419,8 @@ static inline void initialize_res_quotas(struct mlx4_dev *dev, enum mlx4_resource res_type, int vf, int num_instances) { - res_alloc->guaranteed[vf] = num_instances / (2 * (dev->num_vfs + 1)); + res_alloc->guaranteed[vf] = num_instances / + (2 * (dev->persist->num_vfs + 1)); res_alloc->quota[vf] = (num_instances / 2) + res_alloc->guaranteed[vf]; if (vf == mlx4_master_func_num(dev)) { res_alloc->res_free = num_instances; @@ -486,21 +491,26 @@ int mlx4_init_resource_tracker(struct mlx4_dev *dev) for (i = 0; i < MLX4_NUM_OF_RESOURCE_TYPE; i++) { struct resource_allocator *res_alloc = &priv->mfunc.master.res_tracker.res_alloc[i]; - res_alloc->quota = kmalloc((dev->num_vfs + 1) * sizeof(int), GFP_KERNEL); - res_alloc->guaranteed = kmalloc((dev->num_vfs + 1) * sizeof(int), GFP_KERNEL); + res_alloc->quota = kmalloc((dev->persist->num_vfs + 1) * + sizeof(int), GFP_KERNEL); + res_alloc->guaranteed = kmalloc((dev->persist->num_vfs + 1) * + sizeof(int), GFP_KERNEL); if (i == RES_MAC || i == RES_VLAN) res_alloc->allocated = kzalloc(MLX4_MAX_PORTS * - (dev->num_vfs + 1) * sizeof(int), - GFP_KERNEL); + (dev->persist->num_vfs + + 1) * + sizeof(int), GFP_KERNEL); else - res_alloc->allocated = kzalloc((dev->num_vfs + 1) * sizeof(int), GFP_KERNEL); + res_alloc->allocated = kzalloc((dev->persist-> + num_vfs + 1) * + sizeof(int), GFP_KERNEL); if (!res_alloc->quota || !res_alloc->guaranteed || !res_alloc->allocated) goto no_mem_err; spin_lock_init(&res_alloc->alloc_lock); - for (t = 0; t < dev->num_vfs + 1; t++) { + for (t = 0; t < dev->persist->num_vfs + 1; t++) { struct mlx4_active_ports actv_ports = mlx4_get_active_ports(dev, t); switch (i) { @@ -2531,7 +2541,7 @@ int mlx4_SW2HW_MPT_wrapper(struct mlx4_dev *dev, int slave, /* Make sure that the PD bits related to the slave id are zeros. */ pd = mr_get_pd(inbox->buf); pd_slave = (pd >> 17) & 0x7f; - if (pd_slave != 0 && pd_slave != slave) { + if (pd_slave != 0 && --pd_slave != slave) { err = -EPERM; goto ex_abort; } @@ -2934,6 +2944,9 @@ static int verify_qp_parameters(struct mlx4_dev *dev, qp_type = (be32_to_cpu(qp_ctx->flags) >> 16) & 0xff; optpar = be32_to_cpu(*(__be32 *) inbox->buf); + if (slave != mlx4_master_func_num(dev)) + qp_ctx->params2 &= ~MLX4_QP_BIT_FPP; + switch (qp_type) { case MLX4_QP_ST_RC: case MLX4_QP_ST_XRC: @@ -4667,7 +4680,6 @@ static void rem_slave_eqs(struct mlx4_dev *dev, int slave) int state; LIST_HEAD(tlist); int eqn; - struct mlx4_cmd_mailbox *mailbox; err = move_all_busy(dev, slave, RES_EQ); if (err) @@ -4693,20 +4705,13 @@ static void rem_slave_eqs(struct mlx4_dev *dev, int slave) break; case RES_EQ_HW: - mailbox = mlx4_alloc_cmd_mailbox(dev); - if (IS_ERR(mailbox)) { - cond_resched(); - continue; - } - err = mlx4_cmd_box(dev, slave, 0, - eqn & 0xff, 0, - MLX4_CMD_HW2SW_EQ, - MLX4_CMD_TIME_CLASS_A, - MLX4_CMD_NATIVE); + err = mlx4_cmd(dev, slave, eqn & 0xff, + 1, MLX4_CMD_HW2SW_EQ, + MLX4_CMD_TIME_CLASS_A, + MLX4_CMD_NATIVE); if (err) mlx4_dbg(dev, "rem_slave_eqs: failed to move slave %d eqs %d to SW ownership\n", slave, eqn); - mlx4_free_cmd_mailbox(dev, mailbox); atomic_dec(&eq->mtt->ref_count); state = RES_EQ_RESERVED; break; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c index 56779c1c7811..201ca6d76ce5 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c @@ -121,7 +121,7 @@ void mlx5_buf_free(struct mlx5_core_dev *dev, struct mlx5_buf *buf) dma_free_coherent(&dev->pdev->dev, buf->size, buf->direct.buf, buf->direct.map); else { - if (BITS_PER_LONG == 64 && buf->direct.buf) + if (BITS_PER_LONG == 64) vunmap(buf->direct.buf); for (i = 0; i < buf->nbufs; i++) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c index 10e1f1a18255..4878025e231c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/debugfs.c @@ -300,11 +300,11 @@ static u64 qp_read_field(struct mlx5_core_dev *dev, struct mlx5_core_qp *qp, param = qp->pid; break; case QP_STATE: - param = (u64)mlx5_qp_state_str(be32_to_cpu(ctx->flags) >> 28); + param = (unsigned long)mlx5_qp_state_str(be32_to_cpu(ctx->flags) >> 28); *is_str = 1; break; case QP_XPORT: - param = (u64)mlx5_qp_type_str((be32_to_cpu(ctx->flags) >> 16) & 0xff); + param = (unsigned long)mlx5_qp_type_str((be32_to_cpu(ctx->flags) >> 16) & 0xff); *is_str = 1; break; case QP_MTU: @@ -464,7 +464,7 @@ static ssize_t dbg_read(struct file *filp, char __user *buf, size_t count, if (is_str) - ret = snprintf(tbuf, sizeof(tbuf), "%s\n", (const char *)field); + ret = snprintf(tbuf, sizeof(tbuf), "%s\n", (const char *)(unsigned long)field); else ret = snprintf(tbuf, sizeof(tbuf), "0x%llx\n", field); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 3f4525619a07..d6651937d899 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -903,12 +903,12 @@ static void remove_one(struct pci_dev *pdev) } static const struct pci_device_id mlx5_core_pci_table[] = { - { PCI_VDEVICE(MELLANOX, 4113) }, /* Connect-IB */ - { PCI_VDEVICE(MELLANOX, 4114) }, /* Connect-IB VF */ - { PCI_VDEVICE(MELLANOX, 4115) }, /* ConnectX-4 */ - { PCI_VDEVICE(MELLANOX, 4116) }, /* ConnectX-4 VF */ - { PCI_VDEVICE(MELLANOX, 4117) }, /* ConnectX-4LX */ - { PCI_VDEVICE(MELLANOX, 4118) }, /* ConnectX-4LX VF */ + { PCI_VDEVICE(MELLANOX, 0x1011) }, /* Connect-IB */ + { PCI_VDEVICE(MELLANOX, 0x1012) }, /* Connect-IB VF */ + { PCI_VDEVICE(MELLANOX, 0x1013) }, /* ConnectX-4 */ + { PCI_VDEVICE(MELLANOX, 0x1014) }, /* ConnectX-4 VF */ + { PCI_VDEVICE(MELLANOX, 0x1015) }, /* ConnectX-4LX */ + { PCI_VDEVICE(MELLANOX, 0x1016) }, /* ConnectX-4LX VF */ { 0, } }; diff --git a/drivers/net/ethernet/micrel/ksz884x.c b/drivers/net/ethernet/micrel/ksz884x.c index 2fa6ae026e4f..10988fbf47eb 100644 --- a/drivers/net/ethernet/micrel/ksz884x.c +++ b/drivers/net/ethernet/micrel/ksz884x.c @@ -4342,9 +4342,7 @@ static void ksz_init_timer(struct ksz_timer_info *info, int period, { info->max = 0; info->period = period; - init_timer(&info->timer); - info->timer.function = function; - info->timer.data = (unsigned long) data; + setup_timer(&info->timer, function, (unsigned long)data); } static void ksz_update_timer(struct ksz_timer_info *info) diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c index af099057f0e9..1412f5af05ec 100644 --- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c +++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c @@ -4033,8 +4033,10 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent) (void)pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); mgp->cmd = dma_alloc_coherent(&pdev->dev, sizeof(*mgp->cmd), &mgp->cmd_bus, GFP_KERNEL); - if (mgp->cmd == NULL) + if (!mgp->cmd) { + status = -ENOMEM; goto abort_with_enabled; + } mgp->board_span = pci_resource_len(pdev, 0); mgp->iomem_base = pci_resource_start(pdev, 0); @@ -4224,8 +4226,7 @@ static void myri10ge_remove(struct pci_dev *pdev) mtrr_del(mgp->mtrr, mgp->iomem_base, mgp->board_span); #endif myri10ge_free_slices(mgp); - if (mgp->msix_vectors != NULL) - kfree(mgp->msix_vectors); + kfree(mgp->msix_vectors); dma_free_coherent(&pdev->dev, sizeof(*mgp->cmd), mgp->cmd, mgp->cmd_bus); diff --git a/drivers/net/ethernet/natsemi/ns83820.c b/drivers/net/ethernet/natsemi/ns83820.c index 2552e550a78c..eb807b0dc72a 100644 --- a/drivers/net/ethernet/natsemi/ns83820.c +++ b/drivers/net/ethernet/natsemi/ns83820.c @@ -1122,12 +1122,12 @@ again: } #ifdef NS83820_VLAN_ACCEL_SUPPORT - if(vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { /* fetch the vlan tag info out of the * ancillary data if the vlan code * is using hw vlan acceleration */ - short tag = vlan_tx_tag_get(skb); + short tag = skb_vlan_tag_get(skb); extsts |= (EXTSTS_VPKT | htons(tag)); } #endif diff --git a/drivers/net/ethernet/neterion/s2io.c b/drivers/net/ethernet/neterion/s2io.c index f5e4b820128b..a4cdf2f8041a 100644 --- a/drivers/net/ethernet/neterion/s2io.c +++ b/drivers/net/ethernet/neterion/s2io.c @@ -4045,8 +4045,8 @@ static netdev_tx_t s2io_xmit(struct sk_buff *skb, struct net_device *dev) } queue = 0; - if (vlan_tx_tag_present(skb)) - vlan_tag = vlan_tx_tag_get(skb); + if (skb_vlan_tag_present(skb)) + vlan_tag = skb_vlan_tag_get(skb); if (sp->config.tx_steering_type == TX_DEFAULT_STEERING) { if (skb->protocol == htons(ETH_P_IP)) { struct iphdr *ip; @@ -6987,7 +6987,9 @@ static int s2io_add_isr(struct s2io_nic *sp) if (sp->s2io_entries[i].in_use == MSIX_FLG) { if (sp->s2io_entries[i].type == MSIX_RING_TYPE) { - sprintf(sp->desc[i], "%s:MSI-X-%d-RX", + snprintf(sp->desc[i], + sizeof(sp->desc[i]), + "%s:MSI-X-%d-RX", dev->name, i); err = request_irq(sp->entries[i].vector, s2io_msix_ring_handle, @@ -6996,7 +6998,9 @@ static int s2io_add_isr(struct s2io_nic *sp) sp->s2io_entries[i].arg); } else if (sp->s2io_entries[i].type == MSIX_ALARM_TYPE) { - sprintf(sp->desc[i], "%s:MSI-X-%d-TX", + snprintf(sp->desc[i], + sizeof(sp->desc[i]), + "%s:MSI-X-%d-TX", dev->name, i); err = request_irq(sp->entries[i].vector, s2io_msix_fifo_handle, @@ -8154,7 +8158,8 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) "%s: UDP Fragmentation Offload(UFO) enabled\n", dev->name); /* Initialize device name */ - sprintf(sp->name, "%s Neterion %s", dev->name, sp->product_name); + snprintf(sp->name, sizeof(sp->name), "%s Neterion %s", dev->name, + sp->product_name); if (vlan_tag_strip) sp->vlan_strip_flag = 1; diff --git a/drivers/net/ethernet/neterion/vxge/vxge-config.c b/drivers/net/ethernet/neterion/vxge/vxge-config.c index 2bbd01fcb9b0..6223930a8155 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-config.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-config.c @@ -4637,7 +4637,7 @@ static void __vxge_hw_vp_terminate(struct __vxge_hw_device *hldev, u32 vp_id) vpath->ringh = NULL; vpath->fifoh = NULL; memset(&vpath->vpath_handles, 0, sizeof(struct list_head)); - vpath->stats_block = 0; + vpath->stats_block = NULL; vpath->hw_stats = NULL; vpath->hw_stats_sav = NULL; vpath->sw_stats = NULL; diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c index cc0485e3c621..50d5604833ed 100644 --- a/drivers/net/ethernet/neterion/vxge/vxge-main.c +++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c @@ -890,8 +890,8 @@ vxge_xmit(struct sk_buff *skb, struct net_device *dev) dev->name, __func__, __LINE__, fifo_hw, dtr, dtr_priv); - if (vlan_tx_tag_present(skb)) { - u16 vlan_tag = vlan_tx_tag_get(skb); + if (skb_vlan_tag_present(skb)) { + u16 vlan_tag = skb_vlan_tag_get(skb); vxge_hw_fifo_txdl_vlan_set(dtr, vlan_tag); } diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c index f39cae620f61..a41bb5e6b954 100644 --- a/drivers/net/ethernet/nvidia/forcedeth.c +++ b/drivers/net/ethernet/nvidia/forcedeth.c @@ -2462,9 +2462,9 @@ static netdev_tx_t nv_start_xmit_optimized(struct sk_buff *skb, NV_TX2_CHECKSUM_L3 | NV_TX2_CHECKSUM_L4 : 0; /* vlan tag */ - if (vlan_tx_tag_present(skb)) + if (skb_vlan_tag_present(skb)) start_tx->txvlan = cpu_to_le32(NV_TX3_VLAN_TAG_PRESENT | - vlan_tx_tag_get(skb)); + skb_vlan_tag_get(skb)); else start_tx->txvlan = 0; diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index 613037584d08..e0c31e3947d1 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -176,9 +176,7 @@ netxen_alloc_sds_rings(struct netxen_recv_context *recv_ctx, int count) static void netxen_free_sds_rings(struct netxen_recv_context *recv_ctx) { - if (recv_ctx->sds_rings != NULL) - kfree(recv_ctx->sds_rings); - + kfree(recv_ctx->sds_rings); recv_ctx->sds_rings = NULL; } @@ -1893,9 +1891,9 @@ netxen_tso_check(struct net_device *netdev, protocol = vh->h_vlan_encapsulated_proto; flags = FLAGS_VLAN_TAGGED; - } else if (vlan_tx_tag_present(skb)) { + } else if (skb_vlan_tag_present(skb)) { flags = FLAGS_VLAN_OOB; - vid = vlan_tx_tag_get(skb); + vid = skb_vlan_tag_get(skb); netxen_set_tx_vlan_tci(first_desc, vid); vlan_oob = 1; } @@ -2388,7 +2386,10 @@ static int netxen_nic_poll(struct napi_struct *napi, int budget) work_done = netxen_process_rcv_ring(sds_ring, budget); - if ((work_done < budget) && tx_complete) { + if (!tx_complete) + work_done = budget; + + if (work_done < budget) { napi_complete(&sds_ring->napi); if (test_bit(__NX_DEV_UP, &adapter->state)) netxen_nic_enable_int(sds_ring); diff --git a/drivers/net/ethernet/qlogic/qla3xxx.c b/drivers/net/ethernet/qlogic/qla3xxx.c index c2f09af5c25b..4847713211ca 100644 --- a/drivers/net/ethernet/qlogic/qla3xxx.c +++ b/drivers/net/ethernet/qlogic/qla3xxx.c @@ -146,10 +146,7 @@ static int ql_wait_for_drvr_lock(struct ql3_adapter *qdev) { int i = 0; - while (i < 10) { - if (i) - ssleep(1); - + do { if (ql_sem_lock(qdev, QL_DRVR_SEM_MASK, (QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) @@ -158,7 +155,8 @@ static int ql_wait_for_drvr_lock(struct ql3_adapter *qdev) "driver lock acquired\n"); return 1; } - } + ssleep(1); + } while (++i < 10); netdev_err(qdev->ndev, "Timed out waiting for driver lock...\n"); return 0; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c index 18e5de72e9b4..d4b5085a21fa 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c @@ -10,6 +10,7 @@ #include <net/ip.h> #include <linux/ipv6.h> #include <net/checksum.h> +#include <linux/printk.h> #include "qlcnic.h" @@ -320,8 +321,8 @@ static void qlcnic_send_filter(struct qlcnic_adapter *adapter, if (protocol == ETH_P_8021Q) { vh = (struct vlan_ethhdr *)skb->data; vlan_id = ntohs(vh->h_vlan_TCI); - } else if (vlan_tx_tag_present(skb)) { - vlan_id = vlan_tx_tag_get(skb); + } else if (skb_vlan_tag_present(skb)) { + vlan_id = skb_vlan_tag_get(skb); } } @@ -472,9 +473,9 @@ static int qlcnic_tx_pkt(struct qlcnic_adapter *adapter, flags = QLCNIC_FLAGS_VLAN_TAGGED; vlan_tci = ntohs(vh->h_vlan_TCI); protocol = ntohs(vh->h_vlan_encapsulated_proto); - } else if (vlan_tx_tag_present(skb)) { + } else if (skb_vlan_tag_present(skb)) { flags = QLCNIC_FLAGS_VLAN_OOB; - vlan_tci = vlan_tx_tag_get(skb); + vlan_tci = skb_vlan_tag_get(skb); } if (unlikely(adapter->tx_pvid)) { if (vlan_tci && !(adapter->flags & QLCNIC_TAGGING_ENABLED)) @@ -967,7 +968,12 @@ static int qlcnic_poll(struct napi_struct *napi, int budget) tx_complete = qlcnic_process_cmd_ring(adapter, tx_ring, budget); work_done = qlcnic_process_rcv_ring(sds_ring, budget); - if ((work_done < budget) && tx_complete) { + + /* Check if we need a repoll */ + if (!tx_complete) + work_done = budget; + + if (work_done < budget) { napi_complete(&sds_ring->napi); if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) { qlcnic_enable_sds_intr(adapter, sds_ring); @@ -992,6 +998,9 @@ static int qlcnic_tx_poll(struct napi_struct *napi, int budget) napi_complete(&tx_ring->napi); if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) qlcnic_enable_tx_intr(adapter, tx_ring); + } else { + /* As qlcnic_process_cmd_ring() returned 0, we need a repoll*/ + work_done = budget; } return work_done; @@ -1465,14 +1474,14 @@ void qlcnic_post_rx_buffers(struct qlcnic_adapter *adapter, static void dump_skb(struct sk_buff *skb, struct qlcnic_adapter *adapter) { - int i; - unsigned char *data = skb->data; + if (adapter->ahw->msg_enable & NETIF_MSG_DRV) { + char prefix[30]; + + scnprintf(prefix, sizeof(prefix), "%s: %s: ", + dev_name(&adapter->pdev->dev), __func__); - pr_info(KERN_INFO "\n"); - for (i = 0; i < skb->len; i++) { - QLCDB(adapter, DRV, "%02x ", data[i]); - if ((i & 0x0f) == 8) - pr_info(KERN_INFO "\n"); + print_hex_dump_debug(prefix, DUMP_PREFIX_NONE, 16, 1, + skb->data, skb->len, true); } } @@ -1950,7 +1959,12 @@ static int qlcnic_83xx_msix_sriov_vf_poll(struct napi_struct *napi, int budget) tx_complete = qlcnic_process_cmd_ring(adapter, tx_ring, budget); work_done = qlcnic_83xx_process_rcv_ring(sds_ring, budget); - if ((work_done < budget) && tx_complete) { + + /* Check if we need a repoll */ + if (!tx_complete) + work_done = budget; + + if (work_done < budget) { napi_complete(&sds_ring->napi); qlcnic_enable_sds_intr(adapter, sds_ring); } @@ -1973,7 +1987,12 @@ static int qlcnic_83xx_poll(struct napi_struct *napi, int budget) tx_complete = qlcnic_process_cmd_ring(adapter, tx_ring, budget); work_done = qlcnic_83xx_process_rcv_ring(sds_ring, budget); - if ((work_done < budget) && tx_complete) { + + /* Check if we need a repoll */ + if (!tx_complete) + work_done = budget; + + if (work_done < budget) { napi_complete(&sds_ring->napi); qlcnic_enable_sds_intr(adapter, sds_ring); } @@ -1995,6 +2014,9 @@ static int qlcnic_83xx_msix_tx_poll(struct napi_struct *napi, int budget) napi_complete(&tx_ring->napi); if (test_bit(__QLCNIC_DEV_UP , &adapter->state)) qlcnic_enable_tx_intr(adapter, tx_ring); + } else { + /* need a repoll */ + work_done = budget; } return work_done; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 9929b97cfb36..a430a34a4434 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -294,9 +294,7 @@ int qlcnic_alloc_sds_rings(struct qlcnic_recv_context *recv_ctx, int count) void qlcnic_free_sds_rings(struct qlcnic_recv_context *recv_ctx) { - if (recv_ctx->sds_rings != NULL) - kfree(recv_ctx->sds_rings); - + kfree(recv_ctx->sds_rings); recv_ctx->sds_rings = NULL; } @@ -1257,8 +1255,7 @@ qlcnic_check_options(struct qlcnic_adapter *adapter) if (ahw->op_mode != QLCNIC_NON_PRIV_FUNC) { if (fw_dump->tmpl_hdr == NULL || adapter->fw_version > prev_fw_version) { - if (fw_dump->tmpl_hdr) - vfree(fw_dump->tmpl_hdr); + vfree(fw_dump->tmpl_hdr); if (!qlcnic_fw_cmd_get_minidump_temp(adapter)) dev_info(&pdev->dev, "Supports FW dump capability\n"); @@ -2374,13 +2371,12 @@ void qlcnic_free_tx_rings(struct qlcnic_adapter *adapter) for (ring = 0; ring < adapter->drv_tx_rings; ring++) { tx_ring = &adapter->tx_ring[ring]; - if (tx_ring && tx_ring->cmd_buf_arr != NULL) { + if (tx_ring) { vfree(tx_ring->cmd_buf_arr); tx_ring->cmd_buf_arr = NULL; } } - if (adapter->tx_ring != NULL) - kfree(adapter->tx_ring); + kfree(adapter->tx_ring); } int qlcnic_alloc_tx_rings(struct qlcnic_adapter *adapter, @@ -2605,6 +2601,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } else { dev_err(&pdev->dev, "%s: failed. Please Reboot\n", __func__); + err = -ENODEV; goto err_out_free_hw; } @@ -2757,13 +2754,9 @@ static void qlcnic_remove(struct pci_dev *pdev) } qlcnic_dcb_free(adapter->dcb); - qlcnic_detach(adapter); - - if (adapter->npars != NULL) - kfree(adapter->npars); - if (adapter->eswitch != NULL) - kfree(adapter->eswitch); + kfree(adapter->npars); + kfree(adapter->eswitch); if (qlcnic_82xx_check(adapter)) qlcnic_clr_all_drv_state(adapter, 0); @@ -2931,13 +2924,13 @@ void qlcnic_alloc_lb_filters_mem(struct qlcnic_adapter *adapter) static void qlcnic_free_lb_filters_mem(struct qlcnic_adapter *adapter) { - if (adapter->fhash.fmax && adapter->fhash.fhead) + if (adapter->fhash.fmax) kfree(adapter->fhash.fhead); adapter->fhash.fhead = NULL; adapter->fhash.fmax = 0; - if (adapter->rx_fhash.fmax && adapter->rx_fhash.fhead) + if (adapter->rx_fhash.fmax) kfree(adapter->rx_fhash.fhead); adapter->rx_fhash.fmax = 0; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c index c9f57fb84b9e..332bb8a3f430 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c @@ -1407,8 +1407,7 @@ void qlcnic_83xx_get_minidump_template(struct qlcnic_adapter *adapter) current_version = qlcnic_83xx_get_fw_version(adapter); if (fw_dump->tmpl_hdr == NULL || current_version > prev_version) { - if (fw_dump->tmpl_hdr) - vfree(fw_dump->tmpl_hdr); + vfree(fw_dump->tmpl_hdr); if (!qlcnic_fw_cmd_get_minidump_temp(adapter)) dev_info(&pdev->dev, "Supports FW dump capability\n"); } diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c index 6c904a6cad2a..8011ef3e7707 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c @@ -2351,23 +2351,29 @@ static int qlge_update_hw_vlan_features(struct net_device *ndev, { struct ql_adapter *qdev = netdev_priv(ndev); int status = 0; + bool need_restart = netif_running(ndev); - status = ql_adapter_down(qdev); - if (status) { - netif_err(qdev, link, qdev->ndev, - "Failed to bring down the adapter\n"); - return status; + if (need_restart) { + status = ql_adapter_down(qdev); + if (status) { + netif_err(qdev, link, qdev->ndev, + "Failed to bring down the adapter\n"); + return status; + } } /* update the features with resent change */ ndev->features = features; - status = ql_adapter_up(qdev); - if (status) { - netif_err(qdev, link, qdev->ndev, - "Failed to bring up the adapter\n"); - return status; + if (need_restart) { + status = ql_adapter_up(qdev); + if (status) { + netif_err(qdev, link, qdev->ndev, + "Failed to bring up the adapter\n"); + return status; + } } + return status; } @@ -2660,11 +2666,11 @@ static netdev_tx_t qlge_send(struct sk_buff *skb, struct net_device *ndev) mac_iocb_ptr->frame_len = cpu_to_le16((u16) skb->len); - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { netif_printk(qdev, tx_queued, KERN_DEBUG, qdev->ndev, - "Adding a vlan tag %d.\n", vlan_tx_tag_get(skb)); + "Adding a vlan tag %d.\n", skb_vlan_tag_get(skb)); mac_iocb_ptr->flags3 |= OB_MAC_IOCB_V; - mac_iocb_ptr->vlan_tci = cpu_to_le16(vlan_tx_tag_get(skb)); + mac_iocb_ptr->vlan_tci = cpu_to_le16(skb_vlan_tag_get(skb)); } tso = ql_tso(skb, (struct ob_mac_tso_iocb_req *)mac_iocb_ptr); if (tso < 0) { diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c index 9c31e46d1eee..d79e33b3c191 100644 --- a/drivers/net/ethernet/realtek/8139cp.c +++ b/drivers/net/ethernet/realtek/8139cp.c @@ -708,8 +708,8 @@ static void cp_tx (struct cp_private *cp) static inline u32 cp_tx_vlan_tag(struct sk_buff *skb) { - return vlan_tx_tag_present(skb) ? - TxVlanTag | swab16(vlan_tx_tag_get(skb)) : 0x00; + return skb_vlan_tag_present(skb) ? + TxVlanTag | swab16(skb_vlan_tag_get(skb)) : 0x00; } static void unwind_tx_frag_mapping(struct cp_private *cp, struct sk_buff *skb, diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 14a1c5cec3a5..ad0020af2193 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -2073,8 +2073,8 @@ static int rtl8169_set_features(struct net_device *dev, static inline u32 rtl8169_tx_vlan_tag(struct sk_buff *skb) { - return (vlan_tx_tag_present(skb)) ? - TxVlanTag | swab16(vlan_tx_tag_get(skb)) : 0x00; + return (skb_vlan_tag_present(skb)) ? + TxVlanTag | swab16(skb_vlan_tag_get(skb)) : 0x00; } static void rtl8169_rx_vlan_tag(struct RxDesc *desc, struct sk_buff *skb) @@ -4915,7 +4915,7 @@ static void r8168c_hw_jumbo_enable(struct rtl8169_private *tp) RTL_W8(Config3, RTL_R8(Config3) | Jumbo_En0); RTL_W8(Config4, RTL_R8(Config4) | Jumbo_En1); - rtl_tx_performance_tweak(tp->pci_dev, 0x2 << MAX_READ_REQUEST_SHIFT); + rtl_tx_performance_tweak(tp->pci_dev, PCI_EXP_DEVCTL_READRQ_512B); } static void r8168c_hw_jumbo_disable(struct rtl8169_private *tp) @@ -4948,7 +4948,7 @@ static void r8168e_hw_jumbo_enable(struct rtl8169_private *tp) RTL_W8(MaxTxPacketSize, 0x3f); RTL_W8(Config3, RTL_R8(Config3) | Jumbo_En0); RTL_W8(Config4, RTL_R8(Config4) | 0x01); - rtl_tx_performance_tweak(tp->pci_dev, 0x2 << MAX_READ_REQUEST_SHIFT); + rtl_tx_performance_tweak(tp->pci_dev, PCI_EXP_DEVCTL_READRQ_512B); } static void r8168e_hw_jumbo_disable(struct rtl8169_private *tp) @@ -4964,7 +4964,7 @@ static void r8168e_hw_jumbo_disable(struct rtl8169_private *tp) static void r8168b_0_hw_jumbo_enable(struct rtl8169_private *tp) { rtl_tx_performance_tweak(tp->pci_dev, - (0x2 << MAX_READ_REQUEST_SHIFT) | PCI_EXP_DEVCTL_NOSNOOP_EN); + PCI_EXP_DEVCTL_READRQ_512B | PCI_EXP_DEVCTL_NOSNOOP_EN); } static void r8168b_0_hw_jumbo_disable(struct rtl8169_private *tp) @@ -7049,6 +7049,7 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, u32 status, len; u32 opts[2]; int frags; + bool stop_queue; if (unlikely(!TX_FRAGS_READY_FOR(tp, skb_shinfo(skb)->nr_frags))) { netif_err(tp, drv, dev, "BUG! Tx Ring full when queue awake!\n"); @@ -7105,11 +7106,16 @@ static netdev_tx_t rtl8169_start_xmit(struct sk_buff *skb, tp->cur_tx += frags + 1; - RTL_W8(TxPoll, NPQ); + stop_queue = !TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS); - mmiowb(); + if (!skb->xmit_more || stop_queue || + netif_xmit_stopped(netdev_get_tx_queue(dev, 0))) { + RTL_W8(TxPoll, NPQ); + + mmiowb(); + } - if (!TX_FRAGS_READY_FOR(tp, MAX_SKB_FRAGS)) { + if (stop_queue) { /* Avoid wrongly optimistic queue wake-up: rtl_tx thread must * not miss a ring update when it notices a stopped queue. */ diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index c29ba80ae02b..4da8bd263997 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -396,6 +396,9 @@ static const u16 sh_eth_offset_fast_sh3_sh2[SH_ETH_MAX_REGISTER_OFFSET] = { [TSU_ADRL31] = 0x01fc, }; +static void sh_eth_rcv_snd_disable(struct net_device *ndev); +static struct net_device_stats *sh_eth_get_stats(struct net_device *ndev); + static bool sh_eth_is_gether(struct sh_eth_private *mdp) { return mdp->reg_offset == sh_eth_offset_gigabit; @@ -473,6 +476,7 @@ static struct sh_eth_cpu_data r8a777x_data = { .eesr_err_check = EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE | EESR_TDE | EESR_ECI, + .fdr_value = 0x00000f0f, .apr = 1, .mpr = 1, @@ -495,6 +499,9 @@ static struct sh_eth_cpu_data r8a779x_data = { .eesr_err_check = EESR_TWB | EESR_TABT | EESR_RABT | EESR_RFE | EESR_RDE | EESR_RFRMER | EESR_TFE | EESR_TDE | EESR_ECI, + .fdr_value = 0x00000f0f, + + .trscer_err_mask = DESC_I_RINT8, .apr = 1, .mpr = 1, @@ -590,7 +597,7 @@ static struct sh_eth_cpu_data sh7757_data = { static void sh_eth_chip_reset_giga(struct net_device *ndev) { int i; - unsigned long mahr[2], malr[2]; + u32 mahr[2], malr[2]; /* save MAHR and MALR */ for (i = 0; i < 2; i++) { @@ -856,6 +863,9 @@ static void sh_eth_set_default_cpu_data(struct sh_eth_cpu_data *cd) if (!cd->eesr_err_check) cd->eesr_err_check = DEFAULT_EESR_ERR_CHECK; + + if (!cd->trscer_err_mask) + cd->trscer_err_mask = DEFAULT_TRSCER_ERR_MASK; } static int sh_eth_check_reset(struct net_device *ndev) @@ -981,7 +991,7 @@ static void read_mac_address(struct net_device *ndev, unsigned char *mac) } } -static unsigned long sh_eth_get_edtrr_trns(struct sh_eth_private *mdp) +static u32 sh_eth_get_edtrr_trns(struct sh_eth_private *mdp) { if (sh_eth_is_gether(mdp) || sh_eth_is_rz_fast_ether(mdp)) return EDTRR_TRNS_GETHER; @@ -1113,6 +1123,7 @@ static void sh_eth_ring_format(struct net_device *ndev) int rx_ringsize = sizeof(*rxdesc) * mdp->num_rx_ring; int tx_ringsize = sizeof(*txdesc) * mdp->num_tx_ring; int skbuff_size = mdp->rx_buf_sz + SH_ETH_RX_ALIGN - 1; + dma_addr_t dma_addr; mdp->cur_rx = 0; mdp->cur_tx = 0; @@ -1126,7 +1137,6 @@ static void sh_eth_ring_format(struct net_device *ndev) /* skb */ mdp->rx_skbuff[i] = NULL; skb = netdev_alloc_skb(ndev, skbuff_size); - mdp->rx_skbuff[i] = skb; if (skb == NULL) break; sh_eth_set_receive_align(skb); @@ -1135,9 +1145,15 @@ static void sh_eth_ring_format(struct net_device *ndev) rxdesc = &mdp->rx_ring[i]; /* The size of the buffer is a multiple of 16 bytes. */ rxdesc->buffer_length = ALIGN(mdp->rx_buf_sz, 16); - dma_map_single(&ndev->dev, skb->data, rxdesc->buffer_length, - DMA_FROM_DEVICE); - rxdesc->addr = virt_to_phys(skb->data); + dma_addr = dma_map_single(&ndev->dev, skb->data, + rxdesc->buffer_length, + DMA_FROM_DEVICE); + if (dma_mapping_error(&ndev->dev, dma_addr)) { + kfree_skb(skb); + break; + } + mdp->rx_skbuff[i] = skb; + rxdesc->addr = dma_addr; rxdesc->status = cpu_to_edmac(mdp, RD_RACT | RD_RFP); /* Rx descriptor address set */ @@ -1294,7 +1310,7 @@ static int sh_eth_dev_init(struct net_device *ndev, bool start) /* Frame recv control (enable multiple-packets per rx irq) */ sh_eth_write(ndev, RMCR_RNC, RMCR); - sh_eth_write(ndev, DESC_I_RINT8 | DESC_I_RINT5 | DESC_I_TINT2, TRSCER); + sh_eth_write(ndev, mdp->cd->trscer_err_mask, TRSCER); if (mdp->cd->bculr) sh_eth_write(ndev, 0x800, BCULR); /* Burst sycle set */ @@ -1309,8 +1325,10 @@ static int sh_eth_dev_init(struct net_device *ndev, bool start) RFLR); sh_eth_write(ndev, sh_eth_read(ndev, EESR), EESR); - if (start) + if (start) { + mdp->irq_enabled = true; sh_eth_write(ndev, mdp->cd->eesipr_value, EESIPR); + } /* PAUSE Prohibition */ val = (sh_eth_read(ndev, ECMR) & ECMR_DM) | @@ -1349,6 +1367,33 @@ static int sh_eth_dev_init(struct net_device *ndev, bool start) return ret; } +static void sh_eth_dev_exit(struct net_device *ndev) +{ + struct sh_eth_private *mdp = netdev_priv(ndev); + int i; + + /* Deactivate all TX descriptors, so DMA should stop at next + * packet boundary if it's currently running + */ + for (i = 0; i < mdp->num_tx_ring; i++) + mdp->tx_ring[i].status &= ~cpu_to_edmac(mdp, TD_TACT); + + /* Disable TX FIFO egress to MAC */ + sh_eth_rcv_snd_disable(ndev); + + /* Stop RX DMA at next packet boundary */ + sh_eth_write(ndev, 0, EDRRR); + + /* Aside from TX DMA, we can't tell when the hardware is + * really stopped, so we need to reset to make sure. + * Before doing that, wait for long enough to *probably* + * finish transmitting the last packet and poll stats. + */ + msleep(2); /* max frame time at 10 Mbps < 1250 us */ + sh_eth_get_stats(ndev); + sh_eth_reset(ndev); +} + /* free Tx skb function */ static int sh_eth_txfree(struct net_device *ndev) { @@ -1393,6 +1438,7 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) u16 pkt_len = 0; u32 desc_status; int skbuff_size = mdp->rx_buf_sz + SH_ETH_RX_ALIGN - 1; + dma_addr_t dma_addr; boguscnt = min(boguscnt, *quota); limit = boguscnt; @@ -1440,9 +1486,9 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) mdp->rx_skbuff[entry] = NULL; if (mdp->cd->rpadir) skb_reserve(skb, NET_IP_ALIGN); - dma_sync_single_for_cpu(&ndev->dev, rxdesc->addr, - ALIGN(mdp->rx_buf_sz, 16), - DMA_FROM_DEVICE); + dma_unmap_single(&ndev->dev, rxdesc->addr, + ALIGN(mdp->rx_buf_sz, 16), + DMA_FROM_DEVICE); skb_put(skb, pkt_len); skb->protocol = eth_type_trans(skb, ndev); netif_receive_skb(skb); @@ -1462,15 +1508,20 @@ static int sh_eth_rx(struct net_device *ndev, u32 intr_status, int *quota) if (mdp->rx_skbuff[entry] == NULL) { skb = netdev_alloc_skb(ndev, skbuff_size); - mdp->rx_skbuff[entry] = skb; if (skb == NULL) break; /* Better luck next round. */ sh_eth_set_receive_align(skb); - dma_map_single(&ndev->dev, skb->data, - rxdesc->buffer_length, DMA_FROM_DEVICE); + dma_addr = dma_map_single(&ndev->dev, skb->data, + rxdesc->buffer_length, + DMA_FROM_DEVICE); + if (dma_mapping_error(&ndev->dev, dma_addr)) { + kfree_skb(skb); + break; + } + mdp->rx_skbuff[entry] = skb; skb_checksum_none_assert(skb); - rxdesc->addr = virt_to_phys(skb->data); + rxdesc->addr = dma_addr; } if (entry >= mdp->num_rx_ring - 1) rxdesc->status |= @@ -1514,7 +1565,7 @@ static void sh_eth_rcv_snd_enable(struct net_device *ndev) } /* error control function */ -static void sh_eth_error(struct net_device *ndev, int intr_status) +static void sh_eth_error(struct net_device *ndev, u32 intr_status) { struct sh_eth_private *mdp = netdev_priv(ndev); u32 felic_stat; @@ -1566,7 +1617,6 @@ ignore_link: if (intr_status & EESR_RFRMER) { /* Receive Frame Overflow int */ ndev->stats.rx_frame_errors++; - netif_err(mdp, rx_err, ndev, "Receive Abort\n"); } } @@ -1585,13 +1635,11 @@ ignore_link: if (intr_status & EESR_RDE) { /* Receive Descriptor Empty int */ ndev->stats.rx_over_errors++; - netif_err(mdp, rx_err, ndev, "Receive Descriptor Empty\n"); } if (intr_status & EESR_RFE) { /* Receive FIFO Overflow int */ ndev->stats.rx_fifo_errors++; - netif_err(mdp, rx_err, ndev, "Receive FIFO Overflow\n"); } if (!mdp->cd->no_ade && (intr_status & EESR_ADE)) { @@ -1630,7 +1678,7 @@ static irqreturn_t sh_eth_interrupt(int irq, void *netdev) struct sh_eth_private *mdp = netdev_priv(ndev); struct sh_eth_cpu_data *cd = mdp->cd; irqreturn_t ret = IRQ_NONE; - unsigned long intr_status, intr_enable; + u32 intr_status, intr_enable; spin_lock(&mdp->lock); @@ -1646,7 +1694,12 @@ static irqreturn_t sh_eth_interrupt(int irq, void *netdev) if (intr_status & (EESR_RX_CHECK | cd->tx_check | cd->eesr_err_check)) ret = IRQ_HANDLED; else - goto other_irq; + goto out; + + if (!likely(mdp->irq_enabled)) { + sh_eth_write(ndev, 0, EESIPR); + goto out; + } if (intr_status & EESR_RX_CHECK) { if (napi_schedule_prep(&mdp->napi)) { @@ -1656,7 +1709,7 @@ static irqreturn_t sh_eth_interrupt(int irq, void *netdev) __napi_schedule(&mdp->napi); } else { netdev_warn(ndev, - "ignoring interrupt, status 0x%08lx, mask 0x%08lx.\n", + "ignoring interrupt, status 0x%08x, mask 0x%08x.\n", intr_status, intr_enable); } } @@ -1677,7 +1730,7 @@ static irqreturn_t sh_eth_interrupt(int irq, void *netdev) sh_eth_error(ndev, intr_status); } -other_irq: +out: spin_unlock(&mdp->lock); return ret; @@ -1689,7 +1742,7 @@ static int sh_eth_poll(struct napi_struct *napi, int budget) napi); struct net_device *ndev = napi->dev; int quota = budget; - unsigned long intr_status; + u32 intr_status; for (;;) { intr_status = sh_eth_read(ndev, EESR); @@ -1705,7 +1758,8 @@ static int sh_eth_poll(struct napi_struct *napi, int budget) napi_complete(napi); /* Reenable Rx interrupts */ - sh_eth_write(ndev, mdp->cd->eesipr_value, EESIPR); + if (mdp->irq_enabled) + sh_eth_write(ndev, mdp->cd->eesipr_value, EESIPR); out: return budget - quota; } @@ -1820,6 +1874,9 @@ static int sh_eth_get_settings(struct net_device *ndev, unsigned long flags; int ret; + if (!mdp->phydev) + return -ENODEV; + spin_lock_irqsave(&mdp->lock, flags); ret = phy_ethtool_gset(mdp->phydev, ecmd); spin_unlock_irqrestore(&mdp->lock, flags); @@ -1834,6 +1891,9 @@ static int sh_eth_set_settings(struct net_device *ndev, unsigned long flags; int ret; + if (!mdp->phydev) + return -ENODEV; + spin_lock_irqsave(&mdp->lock, flags); /* disable tx and rx */ @@ -1868,6 +1928,9 @@ static int sh_eth_nway_reset(struct net_device *ndev) unsigned long flags; int ret; + if (!mdp->phydev) + return -ENODEV; + spin_lock_irqsave(&mdp->lock, flags); ret = phy_start_aneg(mdp->phydev); spin_unlock_irqrestore(&mdp->lock, flags); @@ -1952,40 +2015,50 @@ static int sh_eth_set_ringparam(struct net_device *ndev, return -EINVAL; if (netif_running(ndev)) { + netif_device_detach(ndev); netif_tx_disable(ndev); - /* Disable interrupts by clearing the interrupt mask. */ - sh_eth_write(ndev, 0x0000, EESIPR); - /* Stop the chip's Tx and Rx processes. */ - sh_eth_write(ndev, 0, EDTRR); - sh_eth_write(ndev, 0, EDRRR); + + /* Serialise with the interrupt handler and NAPI, then + * disable interrupts. We have to clear the + * irq_enabled flag first to ensure that interrupts + * won't be re-enabled. + */ + mdp->irq_enabled = false; synchronize_irq(ndev->irq); - } + napi_synchronize(&mdp->napi); + sh_eth_write(ndev, 0x0000, EESIPR); - /* Free all the skbuffs in the Rx queue. */ - sh_eth_ring_free(ndev); - /* Free DMA buffer */ - sh_eth_free_dma_buffer(mdp); + sh_eth_dev_exit(ndev); + + /* Free all the skbuffs in the Rx queue. */ + sh_eth_ring_free(ndev); + /* Free DMA buffer */ + sh_eth_free_dma_buffer(mdp); + } /* Set new parameters */ mdp->num_rx_ring = ring->rx_pending; mdp->num_tx_ring = ring->tx_pending; - ret = sh_eth_ring_init(ndev); - if (ret < 0) { - netdev_err(ndev, "%s: sh_eth_ring_init failed.\n", __func__); - return ret; - } - ret = sh_eth_dev_init(ndev, false); - if (ret < 0) { - netdev_err(ndev, "%s: sh_eth_dev_init failed.\n", __func__); - return ret; - } - if (netif_running(ndev)) { + ret = sh_eth_ring_init(ndev); + if (ret < 0) { + netdev_err(ndev, "%s: sh_eth_ring_init failed.\n", + __func__); + return ret; + } + ret = sh_eth_dev_init(ndev, false); + if (ret < 0) { + netdev_err(ndev, "%s: sh_eth_dev_init failed.\n", + __func__); + return ret; + } + + mdp->irq_enabled = true; sh_eth_write(ndev, mdp->cd->eesipr_value, EESIPR); /* Setting the Rx mode will start the Rx process. */ sh_eth_write(ndev, EDRRR_R, EDRRR); - netif_wake_queue(ndev); + netif_device_attach(ndev); } return 0; @@ -2060,7 +2133,7 @@ static void sh_eth_tx_timeout(struct net_device *ndev) netif_err(mdp, timer, ndev, "transmit timed out, status %8.8x, resetting...\n", - (int)sh_eth_read(ndev, EESR)); + sh_eth_read(ndev, EESR)); /* tx_errors count up */ ndev->stats.tx_errors++; @@ -2101,6 +2174,9 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev) } spin_unlock_irqrestore(&mdp->lock, flags); + if (skb_padto(skb, ETH_ZLEN)) + return NETDEV_TX_OK; + entry = mdp->cur_tx % mdp->num_tx_ring; mdp->tx_skbuff[entry] = skb; txdesc = &mdp->tx_ring[entry]; @@ -2110,10 +2186,11 @@ static int sh_eth_start_xmit(struct sk_buff *skb, struct net_device *ndev) skb->len + 2); txdesc->addr = dma_map_single(&ndev->dev, skb->data, skb->len, DMA_TO_DEVICE); - if (skb->len < ETH_ZLEN) - txdesc->buffer_length = ETH_ZLEN; - else - txdesc->buffer_length = skb->len; + if (dma_mapping_error(&ndev->dev, txdesc->addr)) { + kfree_skb(skb); + return NETDEV_TX_OK; + } + txdesc->buffer_length = skb->len; if (entry >= mdp->num_tx_ring - 1) txdesc->status |= cpu_to_edmac(mdp, TD_TACT | TD_TDLE); @@ -2165,24 +2242,26 @@ static int sh_eth_close(struct net_device *ndev) netif_stop_queue(ndev); - /* Disable interrupts by clearing the interrupt mask. */ + /* Serialise with the interrupt handler and NAPI, then disable + * interrupts. We have to clear the irq_enabled flag first to + * ensure that interrupts won't be re-enabled. + */ + mdp->irq_enabled = false; + synchronize_irq(ndev->irq); + napi_disable(&mdp->napi); sh_eth_write(ndev, 0x0000, EESIPR); - /* Stop the chip's Tx and Rx processes. */ - sh_eth_write(ndev, 0, EDTRR); - sh_eth_write(ndev, 0, EDRRR); + sh_eth_dev_exit(ndev); - sh_eth_get_stats(ndev); /* PHY Disconnect */ if (mdp->phydev) { phy_stop(mdp->phydev); phy_disconnect(mdp->phydev); + mdp->phydev = NULL; } free_irq(ndev->irq, ndev); - napi_disable(&mdp->napi); - /* Free all the skbuffs in the Rx queue. */ sh_eth_ring_free(ndev); @@ -2410,7 +2489,7 @@ static int sh_eth_tsu_purge_all(struct net_device *ndev) struct sh_eth_private *mdp = netdev_priv(ndev); int i, ret; - if (unlikely(!mdp->cd->tsu)) + if (!mdp->cd->tsu) return 0; for (i = 0; i < SH_ETH_TSU_CAM_ENTRIES; i++) { @@ -2433,7 +2512,7 @@ static void sh_eth_tsu_purge_mcast(struct net_device *ndev) void *reg_offset = sh_eth_tsu_get_offset(mdp, TSU_ADRH0); int i; - if (unlikely(!mdp->cd->tsu)) + if (!mdp->cd->tsu) return; for (i = 0; i < SH_ETH_TSU_CAM_ENTRIES; i++, reg_offset += 8) { @@ -2443,8 +2522,8 @@ static void sh_eth_tsu_purge_mcast(struct net_device *ndev) } } -/* Multicast reception directions set */ -static void sh_eth_set_multicast_list(struct net_device *ndev) +/* Update promiscuous flag and multicast filter */ +static void sh_eth_set_rx_mode(struct net_device *ndev) { struct sh_eth_private *mdp = netdev_priv(ndev); u32 ecmr_bits; @@ -2455,7 +2534,9 @@ static void sh_eth_set_multicast_list(struct net_device *ndev) /* Initial condition is MCT = 1, PRM = 0. * Depending on ndev->flags, set PRM or clear MCT */ - ecmr_bits = (sh_eth_read(ndev, ECMR) & ~ECMR_PRM) | ECMR_MCT; + ecmr_bits = sh_eth_read(ndev, ECMR) & ~ECMR_PRM; + if (mdp->cd->tsu) + ecmr_bits |= ECMR_MCT; if (!(ndev->flags & IFF_MULTICAST)) { sh_eth_tsu_purge_mcast(ndev); @@ -2484,9 +2565,6 @@ static void sh_eth_set_multicast_list(struct net_device *ndev) } } } - } else { - /* Normal, unicast/broadcast-only mode. */ - ecmr_bits = (ecmr_bits & ~ECMR_PRM) | ECMR_MCT; } /* update the ethernet mode */ @@ -2694,6 +2772,7 @@ static const struct net_device_ops sh_eth_netdev_ops = { .ndo_stop = sh_eth_close, .ndo_start_xmit = sh_eth_start_xmit, .ndo_get_stats = sh_eth_get_stats, + .ndo_set_rx_mode = sh_eth_set_rx_mode, .ndo_tx_timeout = sh_eth_tx_timeout, .ndo_do_ioctl = sh_eth_do_ioctl, .ndo_validate_addr = eth_validate_addr, @@ -2706,7 +2785,7 @@ static const struct net_device_ops sh_eth_netdev_ops_tsu = { .ndo_stop = sh_eth_close, .ndo_start_xmit = sh_eth_start_xmit, .ndo_get_stats = sh_eth_get_stats, - .ndo_set_rx_mode = sh_eth_set_multicast_list, + .ndo_set_rx_mode = sh_eth_set_rx_mode, .ndo_vlan_rx_add_vid = sh_eth_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = sh_eth_vlan_rx_kill_vid, .ndo_tx_timeout = sh_eth_tx_timeout, @@ -2940,6 +3019,36 @@ static int sh_eth_drv_remove(struct platform_device *pdev) } #ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP +static int sh_eth_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + int ret = 0; + + if (netif_running(ndev)) { + netif_device_detach(ndev); + ret = sh_eth_close(ndev); + } + + return ret; +} + +static int sh_eth_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + int ret = 0; + + if (netif_running(ndev)) { + ret = sh_eth_open(ndev); + if (ret < 0) + return ret; + netif_device_attach(ndev); + } + + return ret; +} +#endif + static int sh_eth_runtime_nop(struct device *dev) { /* Runtime PM callback shared between ->runtime_suspend() @@ -2953,8 +3062,8 @@ static int sh_eth_runtime_nop(struct device *dev) } static const struct dev_pm_ops sh_eth_dev_pm_ops = { - .runtime_suspend = sh_eth_runtime_nop, - .runtime_resume = sh_eth_runtime_nop, + SET_SYSTEM_SLEEP_PM_OPS(sh_eth_suspend, sh_eth_resume) + SET_RUNTIME_PM_OPS(sh_eth_runtime_nop, sh_eth_runtime_nop, NULL) }; #define SH_ETH_PM_OPS (&sh_eth_dev_pm_ops) #else diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h index 22301bf9c21d..259d03f353e1 100644 --- a/drivers/net/ethernet/renesas/sh_eth.h +++ b/drivers/net/ethernet/renesas/sh_eth.h @@ -369,6 +369,8 @@ enum DESC_I_BIT { DESC_I_RINT1 = 0x0001, }; +#define DEFAULT_TRSCER_ERR_MASK (DESC_I_RINT8 | DESC_I_RINT5 | DESC_I_TINT2) + /* RPADIR */ enum RPADIR_BIT { RPADIR_PADS1 = 0x20000, RPADIR_PADS0 = 0x10000, @@ -457,18 +459,21 @@ struct sh_eth_cpu_data { /* mandatory initialize value */ int register_type; - unsigned long eesipr_value; + u32 eesipr_value; /* optional initialize value */ - unsigned long ecsr_value; - unsigned long ecsipr_value; - unsigned long fdr_value; - unsigned long fcftr_value; - unsigned long rpadir_value; + u32 ecsr_value; + u32 ecsipr_value; + u32 fdr_value; + u32 fcftr_value; + u32 rpadir_value; /* interrupt checking mask */ - unsigned long tx_check; - unsigned long eesr_err_check; + u32 tx_check; + u32 eesr_err_check; + + /* Error mask */ + u32 trscer_err_mask; /* hardware features */ unsigned long irq_flags; /* IRQ configuration flags */ @@ -508,6 +513,7 @@ struct sh_eth_private { u32 rx_buf_sz; /* Based on MTU+slack. */ int edmac_endian; struct napi_struct napi; + bool irq_enabled; /* MII transceiver section. */ u32 phy_id; /* PHY ID */ struct mii_bus *mii_bus; /* MDIO bus control */ @@ -537,7 +543,7 @@ static inline void sh_eth_soft_swap(char *src, int len) #endif } -static inline void sh_eth_write(struct net_device *ndev, unsigned long data, +static inline void sh_eth_write(struct net_device *ndev, u32 data, int enum_index) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -545,8 +551,7 @@ static inline void sh_eth_write(struct net_device *ndev, unsigned long data, iowrite32(data, mdp->addr + mdp->reg_offset[enum_index]); } -static inline unsigned long sh_eth_read(struct net_device *ndev, - int enum_index) +static inline u32 sh_eth_read(struct net_device *ndev, int enum_index) { struct sh_eth_private *mdp = netdev_priv(ndev); @@ -559,14 +564,13 @@ static inline void *sh_eth_tsu_get_offset(struct sh_eth_private *mdp, return mdp->tsu_addr + mdp->reg_offset[enum_index]; } -static inline void sh_eth_tsu_write(struct sh_eth_private *mdp, - unsigned long data, int enum_index) +static inline void sh_eth_tsu_write(struct sh_eth_private *mdp, u32 data, + int enum_index) { iowrite32(data, mdp->tsu_addr + mdp->reg_offset[enum_index]); } -static inline unsigned long sh_eth_tsu_read(struct sh_eth_private *mdp, - int enum_index) +static inline u32 sh_eth_tsu_read(struct sh_eth_private *mdp, int enum_index) { return ioread32(mdp->tsu_addr + mdp->reg_offset[enum_index]); } diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c index 2f398fa4b9e6..34389b6aa67c 100644 --- a/drivers/net/ethernet/rocker/rocker.c +++ b/drivers/net/ethernet/rocker/rocker.c @@ -806,13 +806,13 @@ static bool rocker_desc_gen(struct rocker_desc_info *desc_info) static void *rocker_desc_cookie_ptr_get(struct rocker_desc_info *desc_info) { - return (void *) desc_info->desc->cookie; + return (void *)(uintptr_t)desc_info->desc->cookie; } static void rocker_desc_cookie_ptr_set(struct rocker_desc_info *desc_info, void *ptr) { - desc_info->desc->cookie = (long) ptr; + desc_info->desc->cookie = (uintptr_t) ptr; } static struct rocker_desc_info * @@ -3026,11 +3026,17 @@ static void rocker_port_fdb_learn_work(struct work_struct *work) container_of(work, struct rocker_fdb_learn_work, work); bool removing = (lw->flags & ROCKER_OP_FLAG_REMOVE); bool learned = (lw->flags & ROCKER_OP_FLAG_LEARNED); + struct netdev_switch_notifier_fdb_info info; + + info.addr = lw->addr; + info.vid = lw->vid; if (learned && removing) - br_fdb_external_learn_del(lw->dev, lw->addr, lw->vid); + call_netdev_switch_notifiers(NETDEV_SWITCH_FDB_DEL, + lw->dev, &info.info); else if (learned && !removing) - br_fdb_external_learn_add(lw->dev, lw->addr, lw->vid); + call_netdev_switch_notifiers(NETDEV_SWITCH_FDB_ADD, + lw->dev, &info.info); kfree(work); } @@ -3565,6 +3571,8 @@ nest_cancel: rocker_tlv_nest_cancel(desc_info, frags); out: dev_kfree_skb(skb); + dev->stats.tx_dropped++; + return NETDEV_TX_OK; } @@ -3668,7 +3676,8 @@ static int rocker_fdb_fill_info(struct sk_buff *skb, if (vid && nla_put_u16(skb, NDA_VLAN, vid)) goto nla_put_failure; - return nlmsg_end(skb, nlh); + nlmsg_end(skb, nlh); + return 0; nla_put_failure: nlmsg_cancel(skb, nlh); @@ -3713,7 +3722,7 @@ skip: } static int rocker_port_bridge_setlink(struct net_device *dev, - struct nlmsghdr *nlh) + struct nlmsghdr *nlh, u16 flags) { struct rocker_port *rocker_port = netdev_priv(dev); struct nlattr *protinfo; @@ -3824,11 +3833,145 @@ static void rocker_port_get_drvinfo(struct net_device *dev, strlcpy(drvinfo->version, UTS_RELEASE, sizeof(drvinfo->version)); } +static struct rocker_port_stats { + char str[ETH_GSTRING_LEN]; + int type; +} rocker_port_stats[] = { + { "rx_packets", ROCKER_TLV_CMD_PORT_STATS_RX_PKTS, }, + { "rx_bytes", ROCKER_TLV_CMD_PORT_STATS_RX_BYTES, }, + { "rx_dropped", ROCKER_TLV_CMD_PORT_STATS_RX_DROPPED, }, + { "rx_errors", ROCKER_TLV_CMD_PORT_STATS_RX_ERRORS, }, + + { "tx_packets", ROCKER_TLV_CMD_PORT_STATS_TX_PKTS, }, + { "tx_bytes", ROCKER_TLV_CMD_PORT_STATS_TX_BYTES, }, + { "tx_dropped", ROCKER_TLV_CMD_PORT_STATS_TX_DROPPED, }, + { "tx_errors", ROCKER_TLV_CMD_PORT_STATS_TX_ERRORS, }, +}; + +#define ROCKER_PORT_STATS_LEN ARRAY_SIZE(rocker_port_stats) + +static void rocker_port_get_strings(struct net_device *netdev, u32 stringset, + u8 *data) +{ + u8 *p = data; + int i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < ARRAY_SIZE(rocker_port_stats); i++) { + memcpy(p, rocker_port_stats[i].str, ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + break; + } +} + +static int +rocker_cmd_get_port_stats_prep(struct rocker *rocker, + struct rocker_port *rocker_port, + struct rocker_desc_info *desc_info, + void *priv) +{ + struct rocker_tlv *cmd_stats; + + if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, + ROCKER_TLV_CMD_TYPE_GET_PORT_STATS)) + return -EMSGSIZE; + + cmd_stats = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); + if (!cmd_stats) + return -EMSGSIZE; + + if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_STATS_LPORT, + rocker_port->lport)) + return -EMSGSIZE; + + rocker_tlv_nest_end(desc_info, cmd_stats); + + return 0; +} + +static int +rocker_cmd_get_port_stats_ethtool_proc(struct rocker *rocker, + struct rocker_port *rocker_port, + struct rocker_desc_info *desc_info, + void *priv) +{ + struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1]; + struct rocker_tlv *stats_attrs[ROCKER_TLV_CMD_PORT_STATS_MAX + 1]; + struct rocker_tlv *pattr; + u32 lport; + u64 *data = priv; + int i; + + rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info); + + if (!attrs[ROCKER_TLV_CMD_INFO]) + return -EIO; + + rocker_tlv_parse_nested(stats_attrs, ROCKER_TLV_CMD_PORT_STATS_MAX, + attrs[ROCKER_TLV_CMD_INFO]); + + if (!stats_attrs[ROCKER_TLV_CMD_PORT_STATS_LPORT]) + return -EIO; + + lport = rocker_tlv_get_u32(stats_attrs[ROCKER_TLV_CMD_PORT_STATS_LPORT]); + if (lport != rocker_port->lport) + return -EIO; + + for (i = 0; i < ARRAY_SIZE(rocker_port_stats); i++) { + pattr = stats_attrs[rocker_port_stats[i].type]; + if (!pattr) + continue; + + data[i] = rocker_tlv_get_u64(pattr); + } + + return 0; +} + +static int rocker_cmd_get_port_stats_ethtool(struct rocker_port *rocker_port, + void *priv) +{ + return rocker_cmd_exec(rocker_port->rocker, rocker_port, + rocker_cmd_get_port_stats_prep, NULL, + rocker_cmd_get_port_stats_ethtool_proc, + priv, false); +} + +static void rocker_port_get_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct rocker_port *rocker_port = netdev_priv(dev); + + if (rocker_cmd_get_port_stats_ethtool(rocker_port, data) != 0) { + int i; + + for (i = 0; i < ARRAY_SIZE(rocker_port_stats); ++i) + data[i] = 0; + } + + return; +} + +static int rocker_port_get_sset_count(struct net_device *netdev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ROCKER_PORT_STATS_LEN; + default: + return -EOPNOTSUPP; + } +} + static const struct ethtool_ops rocker_port_ethtool_ops = { .get_settings = rocker_port_get_settings, .set_settings = rocker_port_set_settings, .get_drvinfo = rocker_port_get_drvinfo, .get_link = ethtool_op_get_link, + .get_strings = rocker_port_get_strings, + .get_ethtool_stats = rocker_port_get_stats, + .get_sset_count = rocker_port_get_sset_count, }; /***************** @@ -3850,12 +3993,22 @@ static int rocker_port_poll_tx(struct napi_struct *napi, int budget) /* Cleanup tx descriptors */ while ((desc_info = rocker_desc_tail_get(&rocker_port->tx_ring))) { + struct sk_buff *skb; + err = rocker_desc_err(desc_info); if (err && net_ratelimit()) netdev_err(rocker_port->dev, "tx desc received with err %d\n", err); rocker_tx_desc_frags_unmap(rocker_port, desc_info); - dev_kfree_skb_any(rocker_desc_cookie_ptr_get(desc_info)); + + skb = rocker_desc_cookie_ptr_get(desc_info); + if (err == 0) { + rocker_port->dev->stats.tx_packets++; + rocker_port->dev->stats.tx_bytes += skb->len; + } else + rocker_port->dev->stats.tx_errors++; + + dev_kfree_skb_any(skb); credits++; } @@ -3888,6 +4041,10 @@ static int rocker_port_rx_proc(struct rocker *rocker, rx_len = rocker_tlv_get_u16(attrs[ROCKER_TLV_RX_FRAG_LEN]); skb_put(skb, rx_len); skb->protocol = eth_type_trans(skb, rocker_port->dev); + + rocker_port->dev->stats.rx_packets++; + rocker_port->dev->stats.rx_bytes += skb->len; + netif_receive_skb(skb); return rocker_dma_rx_ring_skb_alloc(rocker, rocker_port, desc_info); @@ -3921,6 +4078,9 @@ static int rocker_port_poll_rx(struct napi_struct *napi, int budget) netdev_err(rocker_port->dev, "rx processing failed with err %d\n", err); } + if (err) + rocker_port->dev->stats.rx_errors++; + rocker_desc_gen_clear(desc_info); rocker_desc_head_set(rocker, &rocker_port->rx_ring, desc_info); credits++; @@ -4004,7 +4164,8 @@ static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) NAPI_POLL_WEIGHT); rocker_carrier_init(rocker_port); - dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; + dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER | + NETIF_F_HW_SWITCH_OFFLOAD; err = register_netdev(dev); if (err) { diff --git a/drivers/net/ethernet/rocker/rocker.h b/drivers/net/ethernet/rocker/rocker.h index 8d2865ba634c..a5bc432feada 100644 --- a/drivers/net/ethernet/rocker/rocker.h +++ b/drivers/net/ethernet/rocker/rocker.h @@ -127,6 +127,9 @@ enum { ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL, ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_GET_STATS, + ROCKER_TLV_CMD_TYPE_CLEAR_PORT_STATS, + ROCKER_TLV_CMD_TYPE_GET_PORT_STATS, + __ROCKER_TLV_CMD_TYPE_MAX, ROCKER_TLV_CMD_TYPE_MAX = __ROCKER_TLV_CMD_TYPE_MAX - 1, }; @@ -146,6 +149,24 @@ enum { __ROCKER_TLV_CMD_PORT_SETTINGS_MAX - 1, }; +enum { + ROCKER_TLV_CMD_PORT_STATS_UNSPEC, + ROCKER_TLV_CMD_PORT_STATS_LPORT, /* u32 */ + + ROCKER_TLV_CMD_PORT_STATS_RX_PKTS, /* u64 */ + ROCKER_TLV_CMD_PORT_STATS_RX_BYTES, /* u64 */ + ROCKER_TLV_CMD_PORT_STATS_RX_DROPPED, /* u64 */ + ROCKER_TLV_CMD_PORT_STATS_RX_ERRORS, /* u64 */ + + ROCKER_TLV_CMD_PORT_STATS_TX_PKTS, /* u64 */ + ROCKER_TLV_CMD_PORT_STATS_TX_BYTES, /* u64 */ + ROCKER_TLV_CMD_PORT_STATS_TX_DROPPED, /* u64 */ + ROCKER_TLV_CMD_PORT_STATS_TX_ERRORS, /* u64 */ + + __ROCKER_TLV_CMD_PORT_STATS_MAX, + ROCKER_TLV_CMD_PORT_STATS_MAX = __ROCKER_TLV_CMD_PORT_STATS_MAX - 1, +}; + enum rocker_port_mode { ROCKER_PORT_MODE_OF_DPA, }; diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c index 698494481d18..c8a01ee4d25e 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c @@ -133,9 +133,8 @@ bool sxgbe_eee_init(struct sxgbe_priv_data * const priv) return false; priv->eee_active = 1; - init_timer(&priv->eee_ctrl_timer); - priv->eee_ctrl_timer.function = sxgbe_eee_ctrl_timer; - priv->eee_ctrl_timer.data = (unsigned long)priv; + setup_timer(&priv->eee_ctrl_timer, sxgbe_eee_ctrl_timer, + (unsigned long)priv); priv->eee_ctrl_timer.expires = SXGBE_LPI_TIMER(eee_timer); add_timer(&priv->eee_ctrl_timer); @@ -365,6 +364,26 @@ static int sxgbe_init_rx_buffers(struct net_device *dev, return 0; } + +/** + * sxgbe_free_rx_buffers - free what sxgbe_init_rx_buffers() allocated + * @dev: net device structure + * @rx_ring: ring to be freed + * @rx_rsize: ring size + * Description: this function initializes the DMA RX descriptor + */ +static void sxgbe_free_rx_buffers(struct net_device *dev, + struct sxgbe_rx_norm_desc *p, int i, + unsigned int dma_buf_sz, + struct sxgbe_rx_queue *rx_ring) +{ + struct sxgbe_priv_data *priv = netdev_priv(dev); + + kfree_skb(rx_ring->rx_skbuff[i]); + dma_unmap_single(priv->device, rx_ring->rx_skbuff_dma[i], + dma_buf_sz, DMA_FROM_DEVICE); +} + /** * init_tx_ring - init the TX descriptor ring * @dev: net device structure @@ -457,7 +476,7 @@ static int init_rx_ring(struct net_device *dev, u8 queue_no, /* RX ring is not allcoated */ if (rx_ring == NULL) { netdev_err(dev, "No memory for RX queue\n"); - goto error; + return -ENOMEM; } /* assign queue number */ @@ -469,18 +488,22 @@ static int init_rx_ring(struct net_device *dev, u8 queue_no, &rx_ring->dma_rx_phy, GFP_KERNEL); if (rx_ring->dma_rx == NULL) - goto error; + return -ENOMEM; /* allocate memory for RX skbuff array */ rx_ring->rx_skbuff_dma = kmalloc_array(rx_rsize, sizeof(dma_addr_t), GFP_KERNEL); - if (rx_ring->rx_skbuff_dma == NULL) - goto dmamem_err; + if (!rx_ring->rx_skbuff_dma) { + ret = -ENOMEM; + goto err_free_dma_rx; + } rx_ring->rx_skbuff = kmalloc_array(rx_rsize, sizeof(struct sk_buff *), GFP_KERNEL); - if (rx_ring->rx_skbuff == NULL) - goto rxbuff_err; + if (!rx_ring->rx_skbuff) { + ret = -ENOMEM; + goto err_free_skbuff_dma; + } /* initialise the buffers */ for (desc_index = 0; desc_index < rx_rsize; desc_index++) { @@ -489,7 +512,7 @@ static int init_rx_ring(struct net_device *dev, u8 queue_no, ret = sxgbe_init_rx_buffers(dev, p, desc_index, bfsize, rx_ring); if (ret) - goto err_init_rx_buffers; + goto err_free_rx_buffers; } /* initalise counters */ @@ -499,18 +522,22 @@ static int init_rx_ring(struct net_device *dev, u8 queue_no, return 0; -err_init_rx_buffers: - while (--desc_index >= 0) - free_rx_ring(priv->device, rx_ring, desc_index); +err_free_rx_buffers: + while (--desc_index >= 0) { + struct sxgbe_rx_norm_desc *p; + + p = rx_ring->dma_rx + desc_index; + sxgbe_free_rx_buffers(dev, p, desc_index, bfsize, rx_ring); + } kfree(rx_ring->rx_skbuff); -rxbuff_err: +err_free_skbuff_dma: kfree(rx_ring->rx_skbuff_dma); -dmamem_err: +err_free_dma_rx: dma_free_coherent(priv->device, rx_rsize * sizeof(struct sxgbe_rx_norm_desc), rx_ring->dma_rx, rx_ring->dma_rx_phy); -error: - return -ENOMEM; + + return ret; } /** * free_tx_ring - free the TX descriptor ring @@ -1009,10 +1036,9 @@ static void sxgbe_tx_init_coalesce(struct sxgbe_priv_data *priv) struct sxgbe_tx_queue *p = priv->txq[queue_num]; p->tx_coal_frames = SXGBE_TX_FRAMES; p->tx_coal_timer = SXGBE_COAL_TX_TIMER; - init_timer(&p->txtimer); + setup_timer(&p->txtimer, sxgbe_tx_timer, + (unsigned long)&priv->txq[queue_num]); p->txtimer.expires = SXGBE_COAL_TIMER(p->tx_coal_timer); - p->txtimer.data = (unsigned long)&priv->txq[queue_num]; - p->txtimer.function = sxgbe_tx_timer; add_timer(&p->txtimer); } } @@ -1274,7 +1300,7 @@ static netdev_tx_t sxgbe_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(skb_is_gso(skb) && tqueue->prev_mss != cur_mss)) ctxt_desc_req = 1; - if (unlikely(vlan_tx_tag_present(skb) || + if (unlikely(skb_vlan_tag_present(skb) || ((skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) && tqueue->hwts_tx_en))) ctxt_desc_req = 1; diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c index 866560ea9e18..b02eed12bfc5 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_platform.c @@ -108,10 +108,6 @@ static int sxgbe_platform_probe(struct platform_device *pdev) } } - /* Get MAC address if available (DT) */ - if (mac) - ether_addr_copy(priv->dev->dev_addr, mac); - priv = sxgbe_drv_probe(&(pdev->dev), plat_dat, addr); if (!priv) { pr_err("%s: main driver probe failed\n", __func__); @@ -125,6 +121,10 @@ static int sxgbe_platform_probe(struct platform_device *pdev) goto err_drv_remove; } + /* Get MAC address if available (DT) */ + if (mac) + ether_addr_copy(priv->dev->dev_addr, mac); + /* Get the TX/RX IRQ numbers */ for (i = 0, chan = 1; i < SXGBE_TX_QUEUES; i++) { priv->txq[i]->irq_no = irq_of_parse_and_map(node, chan++); diff --git a/drivers/net/ethernet/smsc/Kconfig b/drivers/net/ethernet/smsc/Kconfig index 9468e64e6007..3e97a8b43147 100644 --- a/drivers/net/ethernet/smsc/Kconfig +++ b/drivers/net/ethernet/smsc/Kconfig @@ -5,8 +5,9 @@ config NET_VENDOR_SMSC bool "SMC (SMSC)/Western Digital devices" default y - depends on ARM || ISA || MAC || ARM64 || MIPS || M32R || SUPERH || \ - BLACKFIN || MN10300 || COLDFIRE || XTENSA || NIOS2 || PCI || PCMCIA + depends on ARM || ARM64 || ATARI_ETHERNAT || BLACKFIN || COLDFIRE || \ + ISA || M32R || MAC || MIPS || MN10300 || NIOS2 || PCI || \ + PCMCIA || SUPERH || XTENSA ---help--- If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available from @@ -38,8 +39,9 @@ config SMC91X tristate "SMC 91C9x/91C1xxx support" select CRC32 select MII - depends on (ARM || M32R || SUPERH || MIPS || BLACKFIN || \ - MN10300 || COLDFIRE || ARM64 || XTENSA || NIOS2) && (!OF || GPIOLIB) + depends on !OF || GPIOLIB + depends on ARM || ARM64 || ATARI_ETHERNAT || BLACKFIN || COLDFIRE || \ + M32R || MIPS || MN10300 || NIOS2 || SUPERH || XTENSA ---help--- This is a driver for SMC's 91x series of Ethernet chipsets, including the SMC91C94 and the SMC91C111. Say Y if you want it diff --git a/drivers/net/ethernet/smsc/smc91x.h b/drivers/net/ethernet/smsc/smc91x.h index 2a38dacbbd27..be67baf5f677 100644 --- a/drivers/net/ethernet/smsc/smc91x.h +++ b/drivers/net/ethernet/smsc/smc91x.h @@ -216,6 +216,27 @@ SMC_outw(u16 val, void __iomem *ioaddr, int reg) #include <unit/smc91111.h> +#elif defined(CONFIG_ATARI) + +#define SMC_CAN_USE_8BIT 1 +#define SMC_CAN_USE_16BIT 1 +#define SMC_CAN_USE_32BIT 1 +#define SMC_NOWAIT 1 + +#define SMC_inb(a, r) readb((a) + (r)) +#define SMC_inw(a, r) readw((a) + (r)) +#define SMC_inl(a, r) readl((a) + (r)) +#define SMC_outb(v, a, r) writeb(v, (a) + (r)) +#define SMC_outw(v, a, r) writew(v, (a) + (r)) +#define SMC_outl(v, a, r) writel(v, (a) + (r)) +#define SMC_insw(a, r, p, l) readsw((a) + (r), p, l) +#define SMC_outsw(a, r, p, l) writesw((a) + (r), p, l) +#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l) +#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l) + +#define RPC_LSA_DEFAULT RPC_LED_100_10 +#define RPC_LSB_DEFAULT RPC_LED_TX_RX + #elif defined(CONFIG_ARCH_MSM) #define SMC_CAN_USE_8BIT 0 diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index ac4d5629d905..73c2715a27f3 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -6,7 +6,7 @@ stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \ obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o stmmac-platform-objs:= stmmac_platform.o dwmac-meson.o dwmac-sunxi.o \ - dwmac-sti.o dwmac-socfpga.o + dwmac-sti.o dwmac-socfpga.o dwmac-rk.o obj-$(CONFIG_STMMAC_PCI) += stmmac-pci.o stmmac-pci-objs:= stmmac_pci.o diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c new file mode 100644 index 000000000000..6249a4ec08f0 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c @@ -0,0 +1,437 @@ +/** + * dwmac-rk.c - Rockchip RK3288 DWMAC specific glue layer + * + * Copyright (C) 2014 Chen-Zhi (Roger Chen) + * + * Chen-Zhi (Roger Chen) <roger.chen@rock-chips.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. + * + * 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/stmmac.h> +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/phy.h> +#include <linux/of_net.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> +#include <linux/of_device.h> +#include <linux/regulator/consumer.h> +#include <linux/delay.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> + +struct rk_priv_data { + struct platform_device *pdev; + int phy_iface; + struct regulator *regulator; + + bool clk_enabled; + bool clock_input; + + struct clk *clk_mac; + struct clk *clk_mac_pll; + struct clk *gmac_clkin; + struct clk *mac_clk_rx; + struct clk *mac_clk_tx; + struct clk *clk_mac_ref; + struct clk *clk_mac_refout; + struct clk *aclk_mac; + struct clk *pclk_mac; + + int tx_delay; + int rx_delay; + + struct regmap *grf; +}; + +#define HIWORD_UPDATE(val, mask, shift) \ + ((val) << (shift) | (mask) << ((shift) + 16)) + +#define GRF_BIT(nr) (BIT(nr) | BIT(nr+16)) +#define GRF_CLR_BIT(nr) (BIT(nr+16)) + +#define RK3288_GRF_SOC_CON1 0x0248 +#define RK3288_GRF_SOC_CON3 0x0250 +#define RK3288_GRF_GPIO3D_E 0x01ec +#define RK3288_GRF_GPIO4A_E 0x01f0 +#define RK3288_GRF_GPIO4B_E 0x01f4 + +/*RK3288_GRF_SOC_CON1*/ +#define GMAC_PHY_INTF_SEL_RGMII (GRF_BIT(6) | GRF_CLR_BIT(7) | GRF_CLR_BIT(8)) +#define GMAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(6) | GRF_CLR_BIT(7) | GRF_BIT(8)) +#define GMAC_FLOW_CTRL GRF_BIT(9) +#define GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(9) +#define GMAC_SPEED_10M GRF_CLR_BIT(10) +#define GMAC_SPEED_100M GRF_BIT(10) +#define GMAC_RMII_CLK_25M GRF_BIT(11) +#define GMAC_RMII_CLK_2_5M GRF_CLR_BIT(11) +#define GMAC_CLK_125M (GRF_CLR_BIT(12) | GRF_CLR_BIT(13)) +#define GMAC_CLK_25M (GRF_BIT(12) | GRF_BIT(13)) +#define GMAC_CLK_2_5M (GRF_CLR_BIT(12) | GRF_BIT(13)) +#define GMAC_RMII_MODE GRF_BIT(14) +#define GMAC_RMII_MODE_CLR GRF_CLR_BIT(14) + +/*RK3288_GRF_SOC_CON3*/ +#define GMAC_TXCLK_DLY_ENABLE GRF_BIT(14) +#define GMAC_TXCLK_DLY_DISABLE GRF_CLR_BIT(14) +#define GMAC_RXCLK_DLY_ENABLE GRF_BIT(15) +#define GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(15) +#define GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 7) +#define GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0) + +static void set_to_rgmii(struct rk_priv_data *bsp_priv, + int tx_delay, int rx_delay) +{ + struct device *dev = &bsp_priv->pdev->dev; + + if (IS_ERR(bsp_priv->grf)) { + dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); + return; + } + + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, + GMAC_PHY_INTF_SEL_RGMII | GMAC_RMII_MODE_CLR); + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON3, + GMAC_RXCLK_DLY_ENABLE | GMAC_TXCLK_DLY_ENABLE | + GMAC_CLK_RX_DL_CFG(rx_delay) | + GMAC_CLK_TX_DL_CFG(tx_delay)); +} + +static void set_to_rmii(struct rk_priv_data *bsp_priv) +{ + struct device *dev = &bsp_priv->pdev->dev; + + if (IS_ERR(bsp_priv->grf)) { + dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); + return; + } + + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, + GMAC_PHY_INTF_SEL_RMII | GMAC_RMII_MODE); +} + +static void set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed) +{ + struct device *dev = &bsp_priv->pdev->dev; + + if (IS_ERR(bsp_priv->grf)) { + dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); + return; + } + + if (speed == 10) + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, GMAC_CLK_2_5M); + else if (speed == 100) + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, GMAC_CLK_25M); + else if (speed == 1000) + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, GMAC_CLK_125M); + else + dev_err(dev, "unknown speed value for RGMII! speed=%d", speed); +} + +static void set_rmii_speed(struct rk_priv_data *bsp_priv, int speed) +{ + struct device *dev = &bsp_priv->pdev->dev; + + if (IS_ERR(bsp_priv->grf)) { + dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); + return; + } + + if (speed == 10) { + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, + GMAC_RMII_CLK_2_5M | GMAC_SPEED_10M); + } else if (speed == 100) { + regmap_write(bsp_priv->grf, RK3288_GRF_SOC_CON1, + GMAC_RMII_CLK_25M | GMAC_SPEED_100M); + } else { + dev_err(dev, "unknown speed value for RMII! speed=%d", speed); + } +} + +static int gmac_clk_init(struct rk_priv_data *bsp_priv) +{ + struct device *dev = &bsp_priv->pdev->dev; + + bsp_priv->clk_enabled = false; + + bsp_priv->mac_clk_rx = devm_clk_get(dev, "mac_clk_rx"); + if (IS_ERR(bsp_priv->mac_clk_rx)) + dev_err(dev, "%s: cannot get clock %s\n", + __func__, "mac_clk_rx"); + + bsp_priv->mac_clk_tx = devm_clk_get(dev, "mac_clk_tx"); + if (IS_ERR(bsp_priv->mac_clk_tx)) + dev_err(dev, "%s: cannot get clock %s\n", + __func__, "mac_clk_tx"); + + bsp_priv->aclk_mac = devm_clk_get(dev, "aclk_mac"); + if (IS_ERR(bsp_priv->aclk_mac)) + dev_err(dev, "%s: cannot get clock %s\n", + __func__, "aclk_mac"); + + bsp_priv->pclk_mac = devm_clk_get(dev, "pclk_mac"); + if (IS_ERR(bsp_priv->pclk_mac)) + dev_err(dev, "%s: cannot get clock %s\n", + __func__, "pclk_mac"); + + bsp_priv->clk_mac = devm_clk_get(dev, "stmmaceth"); + if (IS_ERR(bsp_priv->clk_mac)) + dev_err(dev, "%s: cannot get clock %s\n", + __func__, "stmmaceth"); + + if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) { + bsp_priv->clk_mac_ref = devm_clk_get(dev, "clk_mac_ref"); + if (IS_ERR(bsp_priv->clk_mac_ref)) + dev_err(dev, "%s: cannot get clock %s\n", + __func__, "clk_mac_ref"); + + if (!bsp_priv->clock_input) { + bsp_priv->clk_mac_refout = + devm_clk_get(dev, "clk_mac_refout"); + if (IS_ERR(bsp_priv->clk_mac_refout)) + dev_err(dev, "%s: cannot get clock %s\n", + __func__, "clk_mac_refout"); + } + } + + if (bsp_priv->clock_input) { + dev_info(dev, "%s: clock input from PHY\n", __func__); + } else { + if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) + clk_set_rate(bsp_priv->clk_mac_pll, 50000000); + } + + return 0; +} + +static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable) +{ + int phy_iface = phy_iface = bsp_priv->phy_iface; + + if (enable) { + if (!bsp_priv->clk_enabled) { + if (phy_iface == PHY_INTERFACE_MODE_RMII) { + if (!IS_ERR(bsp_priv->mac_clk_rx)) + clk_prepare_enable( + bsp_priv->mac_clk_rx); + + if (!IS_ERR(bsp_priv->clk_mac_ref)) + clk_prepare_enable( + bsp_priv->clk_mac_ref); + + if (!IS_ERR(bsp_priv->clk_mac_refout)) + clk_prepare_enable( + bsp_priv->clk_mac_refout); + } + + if (!IS_ERR(bsp_priv->aclk_mac)) + clk_prepare_enable(bsp_priv->aclk_mac); + + if (!IS_ERR(bsp_priv->pclk_mac)) + clk_prepare_enable(bsp_priv->pclk_mac); + + if (!IS_ERR(bsp_priv->mac_clk_tx)) + clk_prepare_enable(bsp_priv->mac_clk_tx); + + /** + * if (!IS_ERR(bsp_priv->clk_mac)) + * clk_prepare_enable(bsp_priv->clk_mac); + */ + mdelay(5); + bsp_priv->clk_enabled = true; + } + } else { + if (bsp_priv->clk_enabled) { + if (phy_iface == PHY_INTERFACE_MODE_RMII) { + if (!IS_ERR(bsp_priv->mac_clk_rx)) + clk_disable_unprepare( + bsp_priv->mac_clk_rx); + + if (!IS_ERR(bsp_priv->clk_mac_ref)) + clk_disable_unprepare( + bsp_priv->clk_mac_ref); + + if (!IS_ERR(bsp_priv->clk_mac_refout)) + clk_disable_unprepare( + bsp_priv->clk_mac_refout); + } + + if (!IS_ERR(bsp_priv->aclk_mac)) + clk_disable_unprepare(bsp_priv->aclk_mac); + + if (!IS_ERR(bsp_priv->pclk_mac)) + clk_disable_unprepare(bsp_priv->pclk_mac); + + if (!IS_ERR(bsp_priv->mac_clk_tx)) + clk_disable_unprepare(bsp_priv->mac_clk_tx); + /** + * if (!IS_ERR(bsp_priv->clk_mac)) + * clk_disable_unprepare(bsp_priv->clk_mac); + */ + bsp_priv->clk_enabled = false; + } + } + + return 0; +} + +static int phy_power_on(struct rk_priv_data *bsp_priv, bool enable) +{ + struct regulator *ldo = bsp_priv->regulator; + int ret; + struct device *dev = &bsp_priv->pdev->dev; + + if (!ldo) { + dev_err(dev, "%s: no regulator found\n", __func__); + return -1; + } + + if (enable) { + ret = regulator_enable(ldo); + if (ret) + dev_err(dev, "%s: fail to enable phy-supply\n", + __func__); + } else { + ret = regulator_disable(ldo); + if (ret) + dev_err(dev, "%s: fail to disable phy-supply\n", + __func__); + } + + return 0; +} + +static void *rk_gmac_setup(struct platform_device *pdev) +{ + struct rk_priv_data *bsp_priv; + struct device *dev = &pdev->dev; + int ret; + const char *strings = NULL; + int value; + + bsp_priv = devm_kzalloc(dev, sizeof(*bsp_priv), GFP_KERNEL); + if (!bsp_priv) + return ERR_PTR(-ENOMEM); + + bsp_priv->phy_iface = of_get_phy_mode(dev->of_node); + + bsp_priv->regulator = devm_regulator_get_optional(dev, "phy"); + if (IS_ERR(bsp_priv->regulator)) { + if (PTR_ERR(bsp_priv->regulator) == -EPROBE_DEFER) { + dev_err(dev, "phy regulator is not available yet, deferred probing\n"); + return ERR_PTR(-EPROBE_DEFER); + } + dev_err(dev, "no regulator found\n"); + bsp_priv->regulator = NULL; + } + + ret = of_property_read_string(dev->of_node, "clock_in_out", &strings); + if (ret) { + dev_err(dev, "%s: Can not read property: clock_in_out.\n", + __func__); + bsp_priv->clock_input = true; + } else { + dev_info(dev, "%s: clock input or output? (%s).\n", + __func__, strings); + if (!strcmp(strings, "input")) + bsp_priv->clock_input = true; + else + bsp_priv->clock_input = false; + } + + ret = of_property_read_u32(dev->of_node, "tx_delay", &value); + if (ret) { + bsp_priv->tx_delay = 0x30; + dev_err(dev, "%s: Can not read property: tx_delay.", __func__); + dev_err(dev, "%s: set tx_delay to 0x%x\n", + __func__, bsp_priv->tx_delay); + } else { + dev_info(dev, "%s: TX delay(0x%x).\n", __func__, value); + bsp_priv->tx_delay = value; + } + + ret = of_property_read_u32(dev->of_node, "rx_delay", &value); + if (ret) { + bsp_priv->rx_delay = 0x10; + dev_err(dev, "%s: Can not read property: rx_delay.", __func__); + dev_err(dev, "%s: set rx_delay to 0x%x\n", + __func__, bsp_priv->rx_delay); + } else { + dev_info(dev, "%s: RX delay(0x%x).\n", __func__, value); + bsp_priv->rx_delay = value; + } + + bsp_priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node, + "rockchip,grf"); + bsp_priv->pdev = pdev; + + /*rmii or rgmii*/ + if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RGMII) { + dev_info(dev, "%s: init for RGMII\n", __func__); + set_to_rgmii(bsp_priv, bsp_priv->tx_delay, bsp_priv->rx_delay); + } else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) { + dev_info(dev, "%s: init for RMII\n", __func__); + set_to_rmii(bsp_priv); + } else { + dev_err(dev, "%s: NO interface defined!\n", __func__); + } + + gmac_clk_init(bsp_priv); + + return bsp_priv; +} + +static int rk_gmac_init(struct platform_device *pdev, void *priv) +{ + struct rk_priv_data *bsp_priv = priv; + int ret; + + ret = phy_power_on(bsp_priv, true); + if (ret) + return ret; + + ret = gmac_clk_enable(bsp_priv, true); + if (ret) + return ret; + + return 0; +} + +static void rk_gmac_exit(struct platform_device *pdev, void *priv) +{ + struct rk_priv_data *gmac = priv; + + phy_power_on(gmac, false); + gmac_clk_enable(gmac, false); +} + +static void rk_fix_speed(void *priv, unsigned int speed) +{ + struct rk_priv_data *bsp_priv = priv; + struct device *dev = &bsp_priv->pdev->dev; + + if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RGMII) + set_rgmii_speed(bsp_priv, speed); + else if (bsp_priv->phy_iface == PHY_INTERFACE_MODE_RMII) + set_rmii_speed(bsp_priv, speed); + else + dev_err(dev, "unsupported interface %d", bsp_priv->phy_iface); +} + +const struct stmmac_of_data rk3288_gmac_data = { + .has_gmac = 1, + .fix_mac_speed = rk_fix_speed, + .setup = rk_gmac_setup, + .init = rk_gmac_init, + .exit = rk_gmac_exit, +}; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c index 056b358b4a72..bb6e2dc61bec 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-sti.c @@ -122,7 +122,7 @@ struct sti_dwmac { bool ext_phyclk; /* Clock from external PHY */ u32 tx_retime_src; /* TXCLK Retiming*/ struct clk *clk; /* PHY clock */ - int ctrl_reg; /* GMAC glue-logic control register */ + u32 ctrl_reg; /* GMAC glue-logic control register */ int clk_sel_reg; /* GMAC ext clk selection register */ struct device *dev; struct regmap *regmap; @@ -285,11 +285,6 @@ static int sti_dwmac_parse_data(struct sti_dwmac *dwmac, if (!np) return -EINVAL; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sti-ethconf"); - if (!res) - return -ENODATA; - dwmac->ctrl_reg = res->start; - /* clk selection from extra syscfg register */ dwmac->clk_sel_reg = -ENXIO; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sti-clkconf"); @@ -300,6 +295,12 @@ static int sti_dwmac_parse_data(struct sti_dwmac *dwmac, if (IS_ERR(regmap)) return PTR_ERR(regmap); + err = of_property_read_u32_index(np, "st,syscon", 1, &dwmac->ctrl_reg); + if (err) { + dev_err(dev, "Can't get sysconfig ctrl offset (%d)\n", err); + return err; + } + dwmac->dev = dev; dwmac->interface = of_get_phy_mode(np); dwmac->regmap = regmap; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 8c6b7c1651e5..55e89b3838f1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1097,6 +1097,7 @@ static int init_dma_desc_rings(struct net_device *dev, gfp_t flags) priv->dirty_tx = 0; priv->cur_tx = 0; + netdev_reset_queue(priv->dev); stmmac_clear_descriptors(priv); @@ -1287,7 +1288,7 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv) * that needs to not insert csum in the TDES. */ priv->hw->dma->dma_mode(priv->ioaddr, SF_DMA_MODE, SF_DMA_MODE); - tc = SF_DMA_MODE; + priv->xstats.threshold = SF_DMA_MODE; } else priv->hw->dma->dma_mode(priv->ioaddr, tc, SF_DMA_MODE); } @@ -1300,6 +1301,7 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv) static void stmmac_tx_clean(struct stmmac_priv *priv) { unsigned int txsize = priv->dma_tx_size; + unsigned int bytes_compl = 0, pkts_compl = 0; spin_lock(&priv->tx_lock); @@ -1356,6 +1358,8 @@ static void stmmac_tx_clean(struct stmmac_priv *priv) priv->hw->mode->clean_desc3(priv, p); if (likely(skb != NULL)) { + pkts_compl++; + bytes_compl += skb->len; dev_consume_skb_any(skb); priv->tx_skbuff[entry] = NULL; } @@ -1364,6 +1368,9 @@ static void stmmac_tx_clean(struct stmmac_priv *priv) priv->dirty_tx++; } + + netdev_completed_queue(priv->dev, pkts_compl, bytes_compl); + if (unlikely(netif_queue_stopped(priv->dev) && stmmac_tx_avail(priv) > STMMAC_TX_THRESH(priv))) { netif_tx_lock(priv->dev); @@ -1418,6 +1425,7 @@ static void stmmac_tx_err(struct stmmac_priv *priv) (i == txsize - 1)); priv->dirty_tx = 0; priv->cur_tx = 0; + netdev_reset_queue(priv->dev); priv->hw->dma->start_tx(priv->ioaddr); priv->dev->stats.tx_errors++; @@ -1444,9 +1452,14 @@ static void stmmac_dma_interrupt(struct stmmac_priv *priv) } if (unlikely(status & tx_hard_error_bump_tc)) { /* Try to bump up the dma threshold on this failure */ - if (unlikely(tc != SF_DMA_MODE) && (tc <= 256)) { + if (unlikely(priv->xstats.threshold != SF_DMA_MODE) && + (tc <= 256)) { tc += 64; - priv->hw->dma->dma_mode(priv->ioaddr, tc, SF_DMA_MODE); + if (priv->plat->force_thresh_dma_mode) + priv->hw->dma->dma_mode(priv->ioaddr, tc, tc); + else + priv->hw->dma->dma_mode(priv->ioaddr, tc, + SF_DMA_MODE); priv->xstats.threshold = tc; } } else if (unlikely(status == tx_hard_error)) @@ -2050,6 +2063,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) if (!priv->hwts_tx_en) skb_tx_timestamp(skb); + netdev_sent_queue(dev, skb->len); priv->hw->dma->enable_dma_transmission(priv->ioaddr); spin_unlock(&priv->tx_lock); @@ -2742,7 +2756,11 @@ static int stmmac_hw_init(struct stmmac_priv *priv) priv->plat->enh_desc = priv->dma_cap.enh_desc; priv->plat->pmt = priv->dma_cap.pmt_remote_wake_up; - priv->plat->tx_coe = priv->dma_cap.tx_coe; + /* TXCOE doesn't work in thresh DMA mode */ + if (priv->plat->force_thresh_dma_mode) + priv->plat->tx_coe = 0; + else + priv->plat->tx_coe = priv->dma_cap.tx_coe; if (priv->dma_cap.rx_coe_type2) priv->plat->rx_coe = STMMAC_RX_COE_TYPE2; @@ -2778,6 +2796,9 @@ static int stmmac_hw_init(struct stmmac_priv *priv) * @addr: iobase memory address * Description: this is the main probe function used to * call the alloc_etherdev, allocate the priv structure. + * Return: + * on success the new private structure is returned, otherwise the error + * pointer. */ struct stmmac_priv *stmmac_dvr_probe(struct device *device, struct plat_stmmacenet_data *plat_dat, @@ -2789,7 +2810,7 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device, ndev = alloc_etherdev(sizeof(struct stmmac_priv)); if (!ndev) - return NULL; + return ERR_PTR(-ENOMEM); SET_NETDEV_DEV(ndev, device); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c index 054520d67de4..3bca908716e2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c @@ -24,8 +24,50 @@ *******************************************************************************/ #include <linux/pci.h> +#include <linux/dmi.h> + #include "stmmac.h" +/* + * This struct is used to associate PCI Function of MAC controller on a board, + * discovered via DMI, with the address of PHY connected to the MAC. The + * negative value of the address means that MAC controller is not connected + * with PHY. + */ +struct stmmac_pci_dmi_data { + const char *name; + unsigned int func; + int phy_addr; +}; + +struct stmmac_pci_info { + struct pci_dev *pdev; + int (*setup)(struct plat_stmmacenet_data *plat, + struct stmmac_pci_info *info); + struct stmmac_pci_dmi_data *dmi; +}; + +static int stmmac_pci_find_phy_addr(struct stmmac_pci_info *info) +{ + const char *name = dmi_get_system_info(DMI_BOARD_NAME); + unsigned int func = PCI_FUNC(info->pdev->devfn); + struct stmmac_pci_dmi_data *dmi; + + /* + * Galileo boards with old firmware don't support DMI. We always return + * 1 here, so at least first found MAC controller would be probed. + */ + if (!name) + return 1; + + for (dmi = info->dmi; dmi->name && *dmi->name; dmi++) { + if (!strcmp(dmi->name, name) && dmi->func == func) + return dmi->phy_addr; + } + + return -ENODEV; +} + static void stmmac_default_data(struct plat_stmmacenet_data *plat) { plat->bus_id = 1; @@ -48,6 +90,62 @@ static void stmmac_default_data(struct plat_stmmacenet_data *plat) plat->unicast_filter_entries = 1; } +static int quark_default_data(struct plat_stmmacenet_data *plat, + struct stmmac_pci_info *info) +{ + struct pci_dev *pdev = info->pdev; + int ret; + + /* + * Refuse to load the driver and register net device if MAC controller + * does not connect to any PHY interface. + */ + ret = stmmac_pci_find_phy_addr(info); + if (ret < 0) + return ret; + + plat->bus_id = PCI_DEVID(pdev->bus->number, pdev->devfn); + plat->phy_addr = ret; + plat->interface = PHY_INTERFACE_MODE_RMII; + plat->clk_csr = 2; + plat->has_gmac = 1; + plat->force_sf_dma_mode = 1; + + plat->mdio_bus_data->phy_reset = NULL; + plat->mdio_bus_data->phy_mask = 0; + + plat->dma_cfg->pbl = 16; + plat->dma_cfg->burst_len = DMA_AXI_BLEN_256; + plat->dma_cfg->fixed_burst = 1; + + /* Set default value for multicast hash bins */ + plat->multicast_filter_bins = HASH_TABLE_SIZE; + + /* Set default value for unicast filter entries */ + plat->unicast_filter_entries = 1; + + return 0; +} + +static struct stmmac_pci_dmi_data quark_pci_dmi_data[] = { + { + .name = "Galileo", + .func = 6, + .phy_addr = 1, + }, + { + .name = "GalileoGen2", + .func = 6, + .phy_addr = 1, + }, + {} +}; + +static struct stmmac_pci_info quark_pci_info = { + .setup = quark_default_data, + .dmi = quark_pci_dmi_data, +}; + /** * stmmac_pci_probe * @@ -63,6 +161,7 @@ static void stmmac_default_data(struct plat_stmmacenet_data *plat) static int stmmac_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { + struct stmmac_pci_info *info = (struct stmmac_pci_info *)id->driver_data; struct plat_stmmacenet_data *plat; struct stmmac_priv *priv; int i; @@ -103,7 +202,17 @@ static int stmmac_pci_probe(struct pci_dev *pdev, pci_set_master(pdev); - stmmac_default_data(plat); + if (info) { + info->pdev = pdev; + if (info->setup) { + ret = info->setup(plat, info); + if (ret) + return ret; + } + } else + stmmac_default_data(plat); + + pci_enable_msi(pdev); priv = stmmac_dvr_probe(&pdev->dev, plat, pcim_iomap_table(pdev)[i]); if (IS_ERR(priv)) { @@ -155,11 +264,13 @@ static int stmmac_pci_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(stmmac_pm_ops, stmmac_pci_suspend, stmmac_pci_resume); #define STMMAC_VENDOR_ID 0x700 +#define STMMAC_QUARK_ID 0x0937 #define STMMAC_DEVICE_ID 0x1108 static const struct pci_device_id stmmac_id_table[] = { {PCI_DEVICE(STMMAC_VENDOR_ID, STMMAC_DEVICE_ID)}, {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_MAC)}, + {PCI_VDEVICE(INTEL, STMMAC_QUARK_ID), (kernel_ulong_t)&quark_pci_info}, {} }; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c index 3039de2465ba..fb846ebba1d9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c @@ -33,6 +33,7 @@ static const struct of_device_id stmmac_dt_ids[] = { /* SoC specific glue layers should come before generic bindings */ + { .compatible = "rockchip,rk3288-gmac", .data = &rk3288_gmac_data}, { .compatible = "amlogic,meson6-dwmac", .data = &meson6_dwmac_data}, { .compatible = "allwinner,sun7i-a20-gmac", .data = &sun7i_gmac_data}, { .compatible = "st,stih415-dwmac", .data = &stih4xx_dwmac_data}, @@ -234,6 +235,9 @@ static int stmmac_probe_config_dt(struct platform_device *pdev, of_property_read_bool(np, "snps,fixed-burst"); dma_cfg->mixed_burst = of_property_read_bool(np, "snps,mixed-burst"); + of_property_read_u32(np, "snps,burst_len", &dma_cfg->burst_len); + if (dma_cfg->burst_len < 0 || dma_cfg->burst_len > 256) + dma_cfg->burst_len = 0; } plat->force_thresh_dma_mode = of_property_read_bool(np, "snps,force_thresh_dma_mode"); if (plat->force_thresh_dma_mode) { diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h index 25dd1f7ace02..093eb99e5ffd 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.h @@ -24,5 +24,6 @@ extern const struct stmmac_of_data sun7i_gmac_data; extern const struct stmmac_of_data stih4xx_dwmac_data; extern const struct stmmac_of_data stid127_dwmac_data; extern const struct stmmac_of_data socfpga_gmac_data; +extern const struct stmmac_of_data rk3288_gmac_data; #endif /* __STMMAC_PLATFORM_H__ */ diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c index 0c6416213837..4b51f903fb73 100644 --- a/drivers/net/ethernet/sun/niu.c +++ b/drivers/net/ethernet/sun/niu.c @@ -3341,8 +3341,7 @@ static int niu_rbr_add_page(struct niu *np, struct rx_ring_info *rp, niu_hash_page(rp, page, addr); if (rp->rbr_blocks_per_page > 1) - atomic_add(rp->rbr_blocks_per_page - 1, - &compound_head(page)->_count); + atomic_add(rp->rbr_blocks_per_page - 1, &page->_count); for (i = 0; i < rp->rbr_blocks_per_page; i++) { __le32 *rbr = &rp->rbr[start_index + i]; diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c index d2835bf7b4fb..2b10b85d8a08 100644 --- a/drivers/net/ethernet/sun/sunvnet.c +++ b/drivers/net/ethernet/sun/sunvnet.c @@ -50,6 +50,7 @@ MODULE_VERSION(DRV_MODULE_VERSION); #define VNET_MAX_RETRIES 10 static int __vnet_tx_trigger(struct vnet_port *port, u32 start); +static void vnet_port_reset(struct vnet_port *port); /* Ordered from largest major to lowest */ static struct vio_version vnet_versions[] = { @@ -351,10 +352,15 @@ static int vnet_rx_one(struct vnet_port *port, struct vio_net_desc *desc) unsigned int len = desc->size; unsigned int copy_len; struct sk_buff *skb; + int maxlen; int err; err = -EMSGSIZE; - if (unlikely(len < ETH_ZLEN || len > port->rmtu)) { + if (port->tso && port->tsolen > port->rmtu) + maxlen = port->tsolen; + else + maxlen = port->rmtu; + if (unlikely(len < ETH_ZLEN || len > maxlen)) { dev->stats.rx_length_errors++; goto out_dropped; } @@ -731,9 +737,7 @@ ldc_ctrl: vio_link_state_change(vio, event); if (event == LDC_EVENT_RESET) { - port->rmtu = 0; - port->tso = true; - port->tsolen = 0; + vnet_port_reset(port); vio_port_up(vio); } port->rx_event = 0; @@ -929,36 +933,36 @@ static struct sk_buff *vnet_clean_tx_ring(struct vnet_port *port, *pending = 0; - txi = dr->prod-1; - if (txi < 0) - txi = VNET_TX_RING_SIZE-1; - + txi = dr->prod; for (i = 0; i < VNET_TX_RING_SIZE; ++i) { struct vio_net_desc *d; - d = vio_dring_entry(dr, txi); - - if (d->hdr.state == VIO_DESC_DONE) { - if (port->tx_bufs[txi].skb) { - BUG_ON(port->tx_bufs[txi].skb->next); + --txi; + if (txi < 0) + txi = VNET_TX_RING_SIZE-1; - port->tx_bufs[txi].skb->next = skb; - skb = port->tx_bufs[txi].skb; - port->tx_bufs[txi].skb = NULL; + d = vio_dring_entry(dr, txi); - ldc_unmap(port->vio.lp, - port->tx_bufs[txi].cookies, - port->tx_bufs[txi].ncookies); - } - d->hdr.state = VIO_DESC_FREE; - } else if (d->hdr.state == VIO_DESC_READY) { + if (d->hdr.state == VIO_DESC_READY) { (*pending)++; - } else if (d->hdr.state == VIO_DESC_FREE) { - break; + continue; } - --txi; - if (txi < 0) - txi = VNET_TX_RING_SIZE-1; + if (port->tx_bufs[txi].skb) { + if (d->hdr.state != VIO_DESC_DONE) + pr_notice("invalid ring buffer state %d\n", + d->hdr.state); + BUG_ON(port->tx_bufs[txi].skb->next); + + port->tx_bufs[txi].skb->next = skb; + skb = port->tx_bufs[txi].skb; + port->tx_bufs[txi].skb = NULL; + + ldc_unmap(port->vio.lp, + port->tx_bufs[txi].cookies, + port->tx_bufs[txi].ncookies); + } else if (d->hdr.state == VIO_DESC_FREE) + break; + d->hdr.state = VIO_DESC_FREE; } return skb; } @@ -1119,6 +1123,7 @@ static inline struct sk_buff *vnet_skb_shape(struct sk_buff *skb, int ncookies) skb_shinfo(nskb)->gso_size = skb_shinfo(skb)->gso_size; skb_shinfo(nskb)->gso_type = skb_shinfo(skb)->gso_type; } + nskb->queue_mapping = skb->queue_mapping; dev_kfree_skb(skb); skb = nskb; } @@ -1632,16 +1637,9 @@ static void vnet_port_free_tx_bufs(struct vnet_port *port) int i; dr = &port->vio.drings[VIO_DRIVER_TX_RING]; - if (dr->base) { - ldc_free_exp_dring(port->vio.lp, dr->base, - (dr->entry_size * dr->num_entries), - dr->cookies, dr->ncookies); - dr->base = NULL; - dr->entry_size = 0; - dr->num_entries = 0; - dr->pending = 0; - dr->ncookies = 0; - } + + if (dr->base == NULL) + return; for (i = 0; i < VNET_TX_RING_SIZE; i++) { struct vio_net_desc *d; @@ -1651,8 +1649,6 @@ static void vnet_port_free_tx_bufs(struct vnet_port *port) continue; d = vio_dring_entry(dr, i); - if (d->hdr.state == VIO_DESC_READY) - pr_warn("active transmit buffers freed\n"); ldc_unmap(port->vio.lp, port->tx_bufs[i].cookies, @@ -1661,6 +1657,23 @@ static void vnet_port_free_tx_bufs(struct vnet_port *port) port->tx_bufs[i].skb = NULL; d->hdr.state = VIO_DESC_FREE; } + ldc_free_exp_dring(port->vio.lp, dr->base, + (dr->entry_size * dr->num_entries), + dr->cookies, dr->ncookies); + dr->base = NULL; + dr->entry_size = 0; + dr->num_entries = 0; + dr->pending = 0; + dr->ncookies = 0; +} + +static void vnet_port_reset(struct vnet_port *port) +{ + del_timer(&port->clean_timer); + vnet_port_free_tx_bufs(port); + port->rmtu = 0; + port->tso = true; + port->tsolen = 0; } static int vnet_port_alloc_tx_ring(struct vnet_port *port) diff --git a/drivers/net/ethernet/tehuti/tehuti.c b/drivers/net/ethernet/tehuti/tehuti.c index 6ab36d9ff2ab..a9cac8413e49 100644 --- a/drivers/net/ethernet/tehuti/tehuti.c +++ b/drivers/net/ethernet/tehuti/tehuti.c @@ -1650,9 +1650,9 @@ static netdev_tx_t bdx_tx_transmit(struct sk_buff *skb, txd_mss); } - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { /*Cut VLAN ID to 12 bits */ - txd_vlan_id = vlan_tx_tag_get(skb) & BITS_MASK(12); + txd_vlan_id = skb_vlan_tag_get(skb) & BITS_MASK(12); txd_vtag = 1; } diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig index 605dd909bcc3..3bc992cd70b7 100644 --- a/drivers/net/ethernet/ti/Kconfig +++ b/drivers/net/ethernet/ti/Kconfig @@ -56,12 +56,18 @@ config TI_CPSW_PHY_SEL This driver supports configuring of the phy mode connected to the CPSW. +config TI_CPSW_ALE + tristate "TI CPSW ALE Support" + ---help--- + This driver supports TI's CPSW ALE module. + config TI_CPSW tristate "TI CPSW Switch Support" depends on ARCH_DAVINCI || ARCH_OMAP2PLUS select TI_DAVINCI_CPDMA select TI_DAVINCI_MDIO select TI_CPSW_PHY_SEL + select TI_CPSW_ALE select MFD_SYSCON select REGMAP ---help--- @@ -79,6 +85,25 @@ config TI_CPTS the CPSW Ethernet Switch. The unit can time stamp PTP UDP/IPv4 and Layer 2 packets, and the driver offers a PTP Hardware Clock. +config TI_KEYSTONE_NETCP + tristate "TI Keystone NETCP Core Support" + select TI_CPSW_ALE + depends on OF + depends on KEYSTONE_NAVIGATOR_DMA && KEYSTONE_NAVIGATOR_QMSS + ---help--- + This driver supports TI's Keystone NETCP Core. + + To compile this driver as a module, choose M here: the module + will be called keystone_netcp. + +config TI_KEYSTONE_NETCP_ETHSS + depends on TI_KEYSTONE_NETCP + tristate "TI Keystone NETCP Ethernet subsystem Support" + ---help--- + + To compile this driver as a module, choose M here: the module + will be called keystone_netcp_ethss. + config TLAN tristate "TI ThunderLAN support" depends on (PCI || EISA) diff --git a/drivers/net/ethernet/ti/Makefile b/drivers/net/ethernet/ti/Makefile index 9cfaab8152be..d420d9413e4a 100644 --- a/drivers/net/ethernet/ti/Makefile +++ b/drivers/net/ethernet/ti/Makefile @@ -2,11 +2,20 @@ # Makefile for the TI network device drivers. # +obj-$(CONFIG_TI_CPSW) += cpsw-common.o +obj-$(CONFIG_TI_DAVINCI_EMAC) += cpsw-common.o + obj-$(CONFIG_TLAN) += tlan.o obj-$(CONFIG_CPMAC) += cpmac.o obj-$(CONFIG_TI_DAVINCI_EMAC) += davinci_emac.o obj-$(CONFIG_TI_DAVINCI_MDIO) += davinci_mdio.o obj-$(CONFIG_TI_DAVINCI_CPDMA) += davinci_cpdma.o obj-$(CONFIG_TI_CPSW_PHY_SEL) += cpsw-phy-sel.o +obj-$(CONFIG_TI_CPSW_ALE) += cpsw_ale.o obj-$(CONFIG_TI_CPSW) += ti_cpsw.o -ti_cpsw-y := cpsw_ale.o cpsw.o cpts.o +ti_cpsw-y := cpsw.o cpts.o + +obj-$(CONFIG_TI_KEYSTONE_NETCP) += keystone_netcp.o +keystone_netcp-y := netcp_core.o +obj-$(CONFIG_TI_KEYSTONE_NETCP_ETHSS) += keystone_netcp_ethss.o +keystone_netcp_ethss-y := netcp_ethss.o netcp_sgmii.o netcp_xgbepcsr.o diff --git a/drivers/net/ethernet/ti/cpsw-common.c b/drivers/net/ethernet/ti/cpsw-common.c new file mode 100644 index 000000000000..f59509486113 --- /dev/null +++ b/drivers/net/ethernet/ti/cpsw-common.c @@ -0,0 +1,55 @@ +/* + * 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> + +#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) + +int cpsw_am33xx_cm_get_macid(struct device *dev, u16 offset, int slave, + u8 *mac_addr) +{ + u32 macid_lo; + u32 macid_hi; + 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, AM33XX_CTRL_MAC_LO_REG(offset, slave), + &macid_lo); + regmap_read(syscon, AM33XX_CTRL_MAC_HI_REG(offset, slave), + &macid_hi); + + mac_addr[5] = (macid_lo >> 8) & 0xff; + mac_addr[4] = macid_lo & 0xff; + mac_addr[3] = (macid_hi >> 24) & 0xff; + mac_addr[2] = (macid_hi >> 16) & 0xff; + mac_addr[1] = (macid_hi >> 8) & 0xff; + mac_addr[0] = macid_hi & 0xff; + + return 0; +} +EXPORT_SYMBOL_GPL(cpsw_am33xx_cm_get_macid); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index c560f9aeb55d..7d8dd0d2182e 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -33,8 +33,6 @@ #include <linux/of_net.h> #include <linux/of_device.h> #include <linux/if_vlan.h> -#include <linux/mfd/syscon.h> -#include <linux/regmap.h> #include <linux/pinctrl/consumer.h> @@ -610,7 +608,7 @@ static void cpsw_set_promiscious(struct net_device *ndev, bool enable) /* Clear all mcast from ALE */ cpsw_ale_flush_multicast(ale, ALE_ALL_PORTS << - priv->host_port); + priv->host_port, -1); /* Flood All Unicast Packets to Host port */ cpsw_ale_control_set(ale, 0, ALE_P0_UNI_FLOOD, 1); @@ -634,6 +632,12 @@ static void cpsw_set_promiscious(struct net_device *ndev, bool enable) static void cpsw_ndo_set_rx_mode(struct net_device *ndev) { struct cpsw_priv *priv = netdev_priv(ndev); + int vid; + + if (priv->data.dual_emac) + vid = priv->slaves[priv->emac_port].port_vlan; + else + vid = priv->data.default_vlan; if (ndev->flags & IFF_PROMISC) { /* Enable promiscuous mode */ @@ -649,7 +653,8 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev) cpsw_ale_set_allmulti(priv->ale, priv->ndev->flags & IFF_ALLMULTI); /* Clear all mcast from ALE */ - cpsw_ale_flush_multicast(priv->ale, ALE_ALL_PORTS << priv->host_port); + cpsw_ale_flush_multicast(priv->ale, ALE_ALL_PORTS << priv->host_port, + vid); if (!netdev_mc_empty(ndev)) { struct netdev_hw_addr *ha; @@ -754,10 +759,26 @@ requeue: dev_kfree_skb_any(new_skb); } -static irqreturn_t cpsw_interrupt(int irq, void *dev_id) +static irqreturn_t cpsw_tx_interrupt(int irq, void *dev_id) +{ + struct cpsw_priv *priv = dev_id; + + cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_TX); + cpdma_chan_process(priv->txch, 128); + + priv = cpsw_get_slave_priv(priv, 1); + if (priv) + cpdma_chan_process(priv->txch, 128); + + return IRQ_HANDLED; +} + +static irqreturn_t cpsw_rx_interrupt(int irq, void *dev_id) { struct cpsw_priv *priv = dev_id; + cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX); + cpsw_intr_disable(priv); if (priv->irq_enabled == true) { cpsw_disable_irq(priv); @@ -786,8 +807,6 @@ static int cpsw_poll(struct napi_struct *napi, int budget) int num_tx, num_rx; num_tx = cpdma_chan_process(priv->txch, 128); - if (num_tx) - cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_TX); num_rx = cpdma_chan_process(priv->rxch, budget); if (num_rx < budget) { @@ -795,7 +814,6 @@ static int cpsw_poll(struct napi_struct *napi, int budget) napi_complete(napi); cpsw_intr_enable(priv); - cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX); prim_cpsw = cpsw_get_slave_priv(priv, 0); if (prim_cpsw->irq_enabled == false) { prim_cpsw->irq_enabled = true; @@ -1310,8 +1328,6 @@ static int cpsw_ndo_open(struct net_device *ndev) napi_enable(&priv->napi); cpdma_ctlr_start(priv->dma); cpsw_intr_enable(priv); - cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX); - cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_TX); prim_cpsw = cpsw_get_slave_priv(priv, 0); if (prim_cpsw->irq_enabled == false) { @@ -1578,9 +1594,6 @@ static void cpsw_ndo_tx_timeout(struct net_device *ndev) cpdma_chan_start(priv->txch); cpdma_ctlr_int_ctrl(priv->dma, true); cpsw_intr_enable(priv); - cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX); - cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_TX); - } static int cpsw_ndo_set_mac_address(struct net_device *ndev, void *p) @@ -1617,12 +1630,10 @@ static void cpsw_ndo_poll_controller(struct net_device *ndev) cpsw_intr_disable(priv); cpdma_ctlr_int_ctrl(priv->dma, false); - cpsw_interrupt(ndev->irq, priv); + cpsw_rx_interrupt(priv->irqs_table[0], priv); + cpsw_tx_interrupt(priv->irqs_table[1], priv); cpdma_ctlr_int_ctrl(priv->dma, true); cpsw_intr_enable(priv); - cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_RX); - cpdma_ctlr_eoi(priv->dma, CPDMA_EOI_TX); - } #endif @@ -1630,16 +1641,24 @@ static inline int cpsw_add_vlan_ale_entry(struct cpsw_priv *priv, unsigned short vid) { int ret; - int unreg_mcast_mask; + int unreg_mcast_mask = 0; + u32 port_mask; - if (priv->ndev->flags & IFF_ALLMULTI) - unreg_mcast_mask = ALE_ALL_PORTS; - else - unreg_mcast_mask = ALE_PORT_1 | ALE_PORT_2; + if (priv->data.dual_emac) { + port_mask = (1 << (priv->emac_port + 1)) | ALE_PORT_HOST; + + if (priv->ndev->flags & IFF_ALLMULTI) + unreg_mcast_mask = port_mask; + } else { + port_mask = ALE_ALL_PORTS; - ret = cpsw_ale_add_vlan(priv->ale, vid, - ALE_ALL_PORTS << priv->host_port, - 0, ALE_ALL_PORTS << priv->host_port, + if (priv->ndev->flags & IFF_ALLMULTI) + unreg_mcast_mask = ALE_ALL_PORTS; + else + unreg_mcast_mask = ALE_PORT_1 | ALE_PORT_2; + } + + ret = cpsw_ale_add_vlan(priv->ale, vid, port_mask, 0, port_mask, unreg_mcast_mask << priv->host_port); if (ret != 0) return ret; @@ -1650,8 +1669,7 @@ static inline int cpsw_add_vlan_ale_entry(struct cpsw_priv *priv, goto clean_vid; ret = cpsw_ale_add_mcast(priv->ale, priv->ndev->broadcast, - ALE_ALL_PORTS << priv->host_port, - ALE_VLAN, vid, 0); + port_mask, ALE_VLAN, vid, 0); if (ret != 0) goto clean_vlan_ucast; return 0; @@ -1672,6 +1690,19 @@ static int cpsw_ndo_vlan_rx_add_vid(struct net_device *ndev, if (vid == priv->data.default_vlan) return 0; + if (priv->data.dual_emac) { + /* In dual EMAC, reserved VLAN id should not be used for + * creating VLAN interfaces as this can break the dual + * EMAC port separation + */ + int i; + + for (i = 0; i < priv->data.slaves; i++) { + if (vid == priv->slaves[i].port_vlan) + return -EINVAL; + } + } + dev_info(priv->dev, "Adding vlanid %d to vlan filter\n", vid); return cpsw_add_vlan_ale_entry(priv, vid); } @@ -1685,6 +1716,15 @@ static int cpsw_ndo_vlan_rx_kill_vid(struct net_device *ndev, if (vid == priv->data.default_vlan) return 0; + if (priv->data.dual_emac) { + int i; + + for (i = 0; i < priv->data.slaves; i++) { + if (vid == priv->slaves[i].port_vlan) + return -EINVAL; + } + } + dev_info(priv->dev, "removing vlanid %d from vlan filter\n", vid); ret = cpsw_ale_del_vlan(priv->ale, vid, 0); if (ret != 0) @@ -1894,36 +1934,6 @@ static void cpsw_slave_init(struct cpsw_slave *slave, struct cpsw_priv *priv, slave->port_vlan = data->dual_emac_res_vlan; } -#define AM33XX_CTRL_MAC_LO_REG(id) (0x630 + 0x8 * id) -#define AM33XX_CTRL_MAC_HI_REG(id) (0x630 + 0x8 * id + 0x4) - -static int cpsw_am33xx_cm_get_macid(struct device *dev, int slave, - u8 *mac_addr) -{ - u32 macid_lo; - u32 macid_hi; - 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, AM33XX_CTRL_MAC_LO_REG(slave), &macid_lo); - regmap_read(syscon, AM33XX_CTRL_MAC_HI_REG(slave), &macid_hi); - - mac_addr[5] = (macid_lo >> 8) & 0xff; - mac_addr[4] = macid_lo & 0xff; - mac_addr[3] = (macid_hi >> 24) & 0xff; - mac_addr[2] = (macid_hi >> 16) & 0xff; - mac_addr[1] = (macid_hi >> 8) & 0xff; - mac_addr[0] = macid_hi & 0xff; - - return 0; -} - static int cpsw_probe_dt(struct cpsw_platform_data *data, struct platform_device *pdev) { @@ -2048,7 +2058,8 @@ no_phy_slave: 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, i, + ret = cpsw_am33xx_cm_get_macid(&pdev->dev, + 0x630, i, slave_data->mac_addr); if (ret) return ret; @@ -2159,7 +2170,8 @@ static int cpsw_probe(struct platform_device *pdev) void __iomem *ss_regs; struct resource *res, *ss_res; u32 slave_offset, sliver_offset, slave_size; - int ret = 0, i, k = 0; + int ret = 0, i; + int irq; ndev = alloc_etherdev(sizeof(struct cpsw_priv)); if (!ndev) { @@ -2341,31 +2353,47 @@ static int cpsw_probe(struct platform_device *pdev) goto clean_dma_ret; } - ndev->irq = platform_get_irq(pdev, 0); + ndev->irq = platform_get_irq(pdev, 1); if (ndev->irq < 0) { dev_err(priv->dev, "error getting irq resource\n"); ret = -ENOENT; goto clean_ale_ret; } - while ((res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, k))) { - if (k >= ARRAY_SIZE(priv->irqs_table)) { - ret = -EINVAL; - goto clean_ale_ret; - } + /* Grab RX and TX IRQs. Note that we also have RX_THRESHOLD and + * MISC IRQs which are always kept disabled with this driver so + * we will not request them. + * + * If anyone wants to implement support for those, make sure to + * first request and append them to irqs_table array. + */ - ret = devm_request_irq(&pdev->dev, res->start, cpsw_interrupt, - 0, dev_name(&pdev->dev), priv); - if (ret < 0) { - dev_err(priv->dev, "error attaching irq (%d)\n", ret); - goto clean_ale_ret; - } + /* RX IRQ */ + irq = platform_get_irq(pdev, 1); + if (irq < 0) + goto clean_ale_ret; - priv->irqs_table[k] = res->start; - k++; + priv->irqs_table[0] = irq; + ret = devm_request_irq(&pdev->dev, irq, cpsw_rx_interrupt, + 0, dev_name(&pdev->dev), priv); + if (ret < 0) { + dev_err(priv->dev, "error attaching irq (%d)\n", ret); + goto clean_ale_ret; } - priv->num_irqs = k; + /* TX IRQ */ + irq = platform_get_irq(pdev, 2); + if (irq < 0) + goto clean_ale_ret; + + priv->irqs_table[1] = irq; + ret = devm_request_irq(&pdev->dev, irq, cpsw_tx_interrupt, + 0, dev_name(&pdev->dev), priv); + if (ret < 0) { + dev_err(priv->dev, "error attaching irq (%d)\n", ret); + goto clean_ale_ret; + } + priv->num_irqs = 2; ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; diff --git a/drivers/net/ethernet/ti/cpsw.h b/drivers/net/ethernet/ti/cpsw.h index 1b710674630c..ca90efafd156 100644 --- a/drivers/net/ethernet/ti/cpsw.h +++ b/drivers/net/ethernet/ti/cpsw.h @@ -41,5 +41,7 @@ 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); #endif /* __CPSW_H__ */ diff --git a/drivers/net/ethernet/ti/cpsw_ale.c b/drivers/net/ethernet/ti/cpsw_ale.c index 097ebe7077ac..6e927b4583aa 100644 --- a/drivers/net/ethernet/ti/cpsw_ale.c +++ b/drivers/net/ethernet/ti/cpsw_ale.c @@ -13,6 +13,7 @@ * GNU General Public License for more details. */ #include <linux/kernel.h> +#include <linux/module.h> #include <linux/platform_device.h> #include <linux/seq_file.h> #include <linux/slab.h> @@ -146,7 +147,7 @@ static int cpsw_ale_write(struct cpsw_ale *ale, int idx, u32 *ale_entry) return idx; } -int cpsw_ale_match_addr(struct cpsw_ale *ale, u8 *addr, u16 vid) +static int cpsw_ale_match_addr(struct cpsw_ale *ale, u8 *addr, u16 vid) { u32 ale_entry[ALE_ENTRY_WORDS]; int type, idx; @@ -167,7 +168,7 @@ int cpsw_ale_match_addr(struct cpsw_ale *ale, u8 *addr, u16 vid) return -ENOENT; } -int cpsw_ale_match_vlan(struct cpsw_ale *ale, u16 vid) +static int cpsw_ale_match_vlan(struct cpsw_ale *ale, u16 vid) { u32 ale_entry[ALE_ENTRY_WORDS]; int type, idx; @@ -234,7 +235,7 @@ static void cpsw_ale_flush_mcast(struct cpsw_ale *ale, u32 *ale_entry, cpsw_ale_set_entry_type(ale_entry, ALE_TYPE_FREE); } -int cpsw_ale_flush_multicast(struct cpsw_ale *ale, int port_mask) +int cpsw_ale_flush_multicast(struct cpsw_ale *ale, int port_mask, int vid) { u32 ale_entry[ALE_ENTRY_WORDS]; int ret, idx; @@ -245,6 +246,14 @@ int cpsw_ale_flush_multicast(struct cpsw_ale *ale, int port_mask) if (ret != ALE_TYPE_ADDR && ret != ALE_TYPE_VLAN_ADDR) continue; + /* if vid passed is -1 then remove all multicast entry from + * the table irrespective of vlan id, if a valid vlan id is + * passed then remove only multicast added to that vlan id. + * if vlan id doesn't match then move on to next entry. + */ + if (vid != -1 && cpsw_ale_get_vlan_id(ale_entry) != vid) + continue; + if (cpsw_ale_get_mcast(ale_entry)) { u8 addr[6]; @@ -257,6 +266,7 @@ int cpsw_ale_flush_multicast(struct cpsw_ale *ale, int port_mask) } return 0; } +EXPORT_SYMBOL_GPL(cpsw_ale_flush_multicast); static void cpsw_ale_flush_ucast(struct cpsw_ale *ale, u32 *ale_entry, int port_mask) @@ -289,6 +299,7 @@ int cpsw_ale_flush(struct cpsw_ale *ale, int port_mask) } return 0; } +EXPORT_SYMBOL_GPL(cpsw_ale_flush); static inline void cpsw_ale_set_vlan_entry_type(u32 *ale_entry, int flags, u16 vid) @@ -326,6 +337,7 @@ int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port, cpsw_ale_write(ale, idx, ale_entry); return 0; } +EXPORT_SYMBOL_GPL(cpsw_ale_add_ucast); int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port, int flags, u16 vid) @@ -341,6 +353,7 @@ int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port, cpsw_ale_write(ale, idx, ale_entry); return 0; } +EXPORT_SYMBOL_GPL(cpsw_ale_del_ucast); int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask, int flags, u16 vid, int mcast_state) @@ -372,6 +385,7 @@ int cpsw_ale_add_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask, cpsw_ale_write(ale, idx, ale_entry); return 0; } +EXPORT_SYMBOL_GPL(cpsw_ale_add_mcast); int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask, int flags, u16 vid) @@ -393,6 +407,7 @@ int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask, cpsw_ale_write(ale, idx, ale_entry); return 0; } +EXPORT_SYMBOL_GPL(cpsw_ale_del_mcast); int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag, int reg_mcast, int unreg_mcast) @@ -422,6 +437,7 @@ int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag, cpsw_ale_write(ale, idx, ale_entry); return 0; } +EXPORT_SYMBOL_GPL(cpsw_ale_add_vlan); int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port_mask) { @@ -442,6 +458,7 @@ int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port_mask) cpsw_ale_write(ale, idx, ale_entry); return 0; } +EXPORT_SYMBOL_GPL(cpsw_ale_del_vlan); void cpsw_ale_set_allmulti(struct cpsw_ale *ale, int allmulti) { @@ -471,6 +488,7 @@ void cpsw_ale_set_allmulti(struct cpsw_ale *ale, int allmulti) cpsw_ale_write(ale, idx, ale_entry); } } +EXPORT_SYMBOL_GPL(cpsw_ale_set_allmulti); struct ale_control_info { const char *name; @@ -696,6 +714,7 @@ int cpsw_ale_control_set(struct cpsw_ale *ale, int port, int control, return 0; } +EXPORT_SYMBOL_GPL(cpsw_ale_control_set); int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control) { @@ -719,6 +738,7 @@ int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control) tmp = __raw_readl(ale->params.ale_regs + offset) >> shift; return tmp & BITMASK(info->bits); } +EXPORT_SYMBOL_GPL(cpsw_ale_control_get); static void cpsw_ale_timer(unsigned long arg) { @@ -742,6 +762,7 @@ int cpsw_ale_set_ageout(struct cpsw_ale *ale, int ageout) } return 0; } +EXPORT_SYMBOL_GPL(cpsw_ale_set_ageout); void cpsw_ale_start(struct cpsw_ale *ale) { @@ -761,11 +782,13 @@ void cpsw_ale_start(struct cpsw_ale *ale) add_timer(&ale->timer); } } +EXPORT_SYMBOL_GPL(cpsw_ale_start); void cpsw_ale_stop(struct cpsw_ale *ale) { del_timer_sync(&ale->timer); } +EXPORT_SYMBOL_GPL(cpsw_ale_stop); struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params) { @@ -780,6 +803,7 @@ struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params) return ale; } +EXPORT_SYMBOL_GPL(cpsw_ale_create); int cpsw_ale_destroy(struct cpsw_ale *ale) { @@ -789,6 +813,7 @@ int cpsw_ale_destroy(struct cpsw_ale *ale) kfree(ale); return 0; } +EXPORT_SYMBOL_GPL(cpsw_ale_destroy); void cpsw_ale_dump(struct cpsw_ale *ale, u32 *data) { @@ -799,3 +824,8 @@ void cpsw_ale_dump(struct cpsw_ale *ale, u32 *data) data += ALE_ENTRY_WORDS; } } +EXPORT_SYMBOL_GPL(cpsw_ale_dump); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TI CPSW ALE driver"); +MODULE_AUTHOR("Texas Instruments"); diff --git a/drivers/net/ethernet/ti/cpsw_ale.h b/drivers/net/ethernet/ti/cpsw_ale.h index c0d4127aa549..af1e7ecd87c6 100644 --- a/drivers/net/ethernet/ti/cpsw_ale.h +++ b/drivers/net/ethernet/ti/cpsw_ale.h @@ -92,7 +92,7 @@ void cpsw_ale_stop(struct cpsw_ale *ale); int cpsw_ale_set_ageout(struct cpsw_ale *ale, int ageout); int cpsw_ale_flush(struct cpsw_ale *ale, int port_mask); -int cpsw_ale_flush_multicast(struct cpsw_ale *ale, int port_mask); +int cpsw_ale_flush_multicast(struct cpsw_ale *ale, int port_mask, int vid); int cpsw_ale_add_ucast(struct cpsw_ale *ale, u8 *addr, int port, int flags, u16 vid); int cpsw_ale_del_ucast(struct cpsw_ale *ale, u8 *addr, int port, diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index 4a4388b813ac..fbe42cb107ec 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -157,14 +157,11 @@ static int cpts_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) static int cpts_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) { - s64 now; unsigned long flags; struct cpts *cpts = container_of(ptp, struct cpts, info); spin_lock_irqsave(&cpts->lock, flags); - now = timecounter_read(&cpts->tc); - now += delta; - timecounter_init(&cpts->tc, &cpts->cc, now); + timecounter_adjtime(&cpts->tc, delta); spin_unlock_irqrestore(&cpts->lock, flags); return 0; diff --git a/drivers/net/ethernet/ti/cpts.h b/drivers/net/ethernet/ti/cpts.h index 1a581ef7eee8..69a46b92c7d6 100644 --- a/drivers/net/ethernet/ti/cpts.h +++ b/drivers/net/ethernet/ti/cpts.h @@ -27,6 +27,7 @@ #include <linux/list.h> #include <linux/ptp_clock_kernel.h> #include <linux/skbuff.h> +#include <linux/timecounter.h> struct cpsw_cpts { u32 idver; /* Identification and version */ diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index ea712512c7d1..aeebc0a7bf47 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -52,6 +52,7 @@ #include <linux/dma-mapping.h> #include <linux/clk.h> #include <linux/platform_device.h> +#include <linux/regmap.h> #include <linux/semaphore.h> #include <linux/phy.h> #include <linux/bitops.h> @@ -62,12 +63,15 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_device.h> +#include <linux/of_mdio.h> #include <linux/of_irq.h> #include <linux/of_net.h> +#include <linux/mfd/syscon.h> #include <asm/irq.h> #include <asm/page.h> +#include "cpsw.h" #include "davinci_cpdma.h" static int debug_level; @@ -343,9 +347,7 @@ struct emac_priv { u32 multicast_hash_cnt[EMAC_NUM_MULTICAST_BITS]; u32 rx_addr_type; const char *phy_id; -#ifdef CONFIG_OF struct device_node *phy_node; -#endif struct phy_device *phydev; spinlock_t lock; /*platform specific members*/ @@ -922,6 +924,16 @@ static void emac_int_disable(struct emac_priv *priv) if (priv->int_disable) priv->int_disable(); + /* NOTE: Rx Threshold and Misc interrupts are not enabled */ + + /* ack rxen only then a new pulse will be generated */ + emac_write(EMAC_DM646X_MACEOIVECTOR, + EMAC_DM646X_MAC_EOI_C0_RXEN); + + /* ack txen- only then a new pulse will be generated */ + emac_write(EMAC_DM646X_MACEOIVECTOR, + EMAC_DM646X_MAC_EOI_C0_TXEN); + local_irq_restore(flags); } else { @@ -951,15 +963,6 @@ static void emac_int_enable(struct emac_priv *priv) * register */ /* NOTE: Rx Threshold and Misc interrupts are not enabled */ - - /* ack rxen only then a new pulse will be generated */ - emac_write(EMAC_DM646X_MACEOIVECTOR, - EMAC_DM646X_MAC_EOI_C0_RXEN); - - /* ack txen- only then a new pulse will be generated */ - emac_write(EMAC_DM646X_MACEOIVECTOR, - EMAC_DM646X_MAC_EOI_C0_TXEN); - } else { /* Set DM644x control registers for interrupt control */ emac_ctrl_write(EMAC_CTRL_EWCTL, 0x1); @@ -1537,7 +1540,13 @@ static int emac_dev_open(struct net_device *ndev) int i = 0; struct emac_priv *priv = netdev_priv(ndev); - pm_runtime_get(&priv->pdev->dev); + ret = pm_runtime_get_sync(&priv->pdev->dev); + if (ret < 0) { + pm_runtime_put_noidle(&priv->pdev->dev); + dev_err(&priv->pdev->dev, "%s: failed to get_sync(%d)\n", + __func__, ret); + return ret; + } netif_carrier_off(ndev); for (cnt = 0; cnt < ETH_ALEN; cnt++) @@ -1596,8 +1605,20 @@ static int emac_dev_open(struct net_device *ndev) cpdma_ctlr_start(priv->dma); priv->phydev = NULL; + + if (priv->phy_node) { + priv->phydev = of_phy_connect(ndev, priv->phy_node, + &emac_adjust_link, 0, 0); + if (!priv->phydev) { + dev_err(emac_dev, "could not connect to phy %s\n", + priv->phy_node->full_name); + ret = -ENODEV; + goto err; + } + } + /* use the first phy on the bus if pdata did not give us a phy id */ - if (!priv->phy_id) { + if (!priv->phydev && !priv->phy_id) { struct device *phy; phy = bus_find_device(&mdio_bus_type, NULL, NULL, @@ -1606,7 +1627,7 @@ static int emac_dev_open(struct net_device *ndev) priv->phy_id = dev_name(phy); } - if (priv->phy_id && *priv->phy_id) { + if (!priv->phydev && priv->phy_id && *priv->phy_id) { priv->phydev = phy_connect(ndev, priv->phy_id, &emac_adjust_link, PHY_INTERFACE_MODE_MII); @@ -1627,7 +1648,9 @@ static int emac_dev_open(struct net_device *ndev) "(mii_bus:phy_addr=%s, id=%x)\n", priv->phydev->drv->name, dev_name(&priv->phydev->dev), priv->phydev->phy_id); - } else { + } + + if (!priv->phydev) { /* No PHY , fix the link, speed and duplex settings */ dev_notice(emac_dev, "no phy, defaulting to 100/full\n"); priv->link = 1; @@ -1724,6 +1747,15 @@ static struct net_device_stats *emac_dev_getnetstats(struct net_device *ndev) struct emac_priv *priv = netdev_priv(ndev); u32 mac_control; u32 stats_clear_mask; + int err; + + err = pm_runtime_get_sync(&priv->pdev->dev); + if (err < 0) { + pm_runtime_put_noidle(&priv->pdev->dev); + dev_err(&priv->pdev->dev, "%s: failed to get_sync(%d)\n", + __func__, err); + return &ndev->stats; + } /* update emac hardware stats and reset the registers*/ @@ -1766,6 +1798,8 @@ static struct net_device_stats *emac_dev_getnetstats(struct net_device *ndev) ndev->stats.tx_fifo_errors += emac_read(EMAC_TXUNDERRUN); emac_write(EMAC_TXUNDERRUN, stats_clear_mask); + pm_runtime_put(&priv->pdev->dev); + return &ndev->stats; } @@ -1807,7 +1841,7 @@ davinci_emac_of_get_pdata(struct platform_device *pdev, struct emac_priv *priv) if (!is_valid_ether_addr(pdata->mac_addr)) { mac_addr = of_get_mac_address(np); if (mac_addr) - memcpy(pdata->mac_addr, mac_addr, ETH_ALEN); + ether_addr_copy(pdata->mac_addr, mac_addr); } of_property_read_u32(np, "ti,davinci-ctrl-reg-offset", @@ -1848,6 +1882,53 @@ 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; +} + /** * davinci_emac_probe - EMAC device probe * @pdev: The DaVinci EMAC device that we are removing @@ -1859,7 +1940,7 @@ davinci_emac_of_get_pdata(struct platform_device *pdev, struct emac_priv *priv) static int davinci_emac_probe(struct platform_device *pdev) { int rc = 0; - struct resource *res; + struct resource *res, *res_ctrl; struct net_device *ndev; struct emac_priv *priv; unsigned long hw_ram_addr; @@ -1876,6 +1957,7 @@ static int davinci_emac_probe(struct platform_device *pdev) return -EBUSY; } emac_bus_frequency = clk_get_rate(emac_clk); + devm_clk_put(&pdev->dev, emac_clk); /* TODO: Probe PHY here if possible */ @@ -1917,11 +1999,20 @@ static int davinci_emac_probe(struct platform_device *pdev) rc = PTR_ERR(priv->remap_addr); goto no_pdata; } + + res_ctrl = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res_ctrl) { + priv->ctrl_base = + devm_ioremap_resource(&pdev->dev, res_ctrl); + if (IS_ERR(priv->ctrl_base)) + goto no_pdata; + } else { + priv->ctrl_base = priv->remap_addr + pdata->ctrl_mod_reg_offset; + } + priv->emac_base = priv->remap_addr + pdata->ctrl_reg_offset; ndev->base_addr = (unsigned long)priv->remap_addr; - priv->ctrl_base = priv->remap_addr + pdata->ctrl_mod_reg_offset; - hw_ram_addr = pdata->hw_ram_addr; if (!hw_ram_addr) hw_ram_addr = (u32 __force)res->start + pdata->ctrl_ram_offset; @@ -1968,6 +2059,10 @@ static int davinci_emac_probe(struct platform_device *pdev) } ndev->irq = res->start; + rc = davinci_emac_try_get_mac(pdev, res_ctrl ? 0 : 1, priv->mac_addr); + if (!rc) + ether_addr_copy(ndev->dev_addr, priv->mac_addr); + if (!is_valid_ether_addr(priv->mac_addr)) { /* Use random MAC if none passed */ eth_hw_addr_random(ndev); @@ -1980,12 +2075,22 @@ static int davinci_emac_probe(struct platform_device *pdev) ndev->ethtool_ops = ðtool_ops; netif_napi_add(ndev, &priv->napi, emac_poll, EMAC_POLL_WEIGHT); + pm_runtime_enable(&pdev->dev); + rc = pm_runtime_get_sync(&pdev->dev); + if (rc < 0) { + pm_runtime_put_noidle(&pdev->dev); + dev_err(&pdev->dev, "%s: failed to get_sync(%d)\n", + __func__, rc); + goto no_cpdma_chan; + } + /* register the network device */ SET_NETDEV_DEV(ndev, &pdev->dev); rc = register_netdev(ndev); if (rc) { dev_err(&pdev->dev, "error in register_netdev\n"); rc = -ENODEV; + pm_runtime_put(&pdev->dev); goto no_cpdma_chan; } @@ -1995,9 +2100,7 @@ static int davinci_emac_probe(struct platform_device *pdev) "(regs: %p, irq: %d)\n", (void *)priv->emac_base_phys, ndev->irq); } - - pm_runtime_enable(&pdev->dev); - pm_runtime_resume(&pdev->dev); + pm_runtime_put(&pdev->dev); return 0; @@ -2071,9 +2174,14 @@ static const struct emac_platform_data am3517_emac_data = { .hw_ram_addr = 0x01e20000, }; +static const struct emac_platform_data dm816_emac_data = { + .version = EMAC_VERSION_2, +}; + static const struct of_device_id davinci_emac_of_match[] = { {.compatible = "ti,davinci-dm6467-emac", }, {.compatible = "ti,am3517-emac", .data = &am3517_emac_data, }, + {.compatible = "ti,dm816-emac", .data = &dm816_emac_data, }, {}, }; MODULE_DEVICE_TABLE(of, davinci_emac_of_match); diff --git a/drivers/net/ethernet/ti/netcp.h b/drivers/net/ethernet/ti/netcp.h new file mode 100644 index 000000000000..906e9bc412f5 --- /dev/null +++ b/drivers/net/ethernet/ti/netcp.h @@ -0,0 +1,229 @@ +/* + * NetCP driver local header + * + * Copyright (C) 2014 Texas Instruments Incorporated + * Authors: Sandeep Nair <sandeep_n@ti.com> + * Sandeep Paulraj <s-paulraj@ti.com> + * Cyril Chemparathy <cyril@ti.com> + * Santosh Shilimkar <santosh.shilimkar@ti.com> + * Wingman Kwok <w-kwok2@ti.com> + * Murali Karicheri <m-karicheri2@ti.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 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 __NETCP_H__ +#define __NETCP_H__ + +#include <linux/netdevice.h> +#include <linux/soc/ti/knav_dma.h> + +/* Maximum Ethernet frame size supported by Keystone switch */ +#define NETCP_MAX_FRAME_SIZE 9504 + +#define SGMII_LINK_MAC_MAC_AUTONEG 0 +#define SGMII_LINK_MAC_PHY 1 +#define SGMII_LINK_MAC_MAC_FORCED 2 +#define SGMII_LINK_MAC_FIBER 3 +#define SGMII_LINK_MAC_PHY_NO_MDIO 4 +#define XGMII_LINK_MAC_PHY 10 +#define XGMII_LINK_MAC_MAC_FORCED 11 + +struct netcp_device; + +struct netcp_tx_pipe { + struct netcp_device *netcp_device; + void *dma_queue; + unsigned int dma_queue_id; + u8 dma_psflags; + void *dma_channel; + const char *dma_chan_name; +}; + +#define ADDR_NEW BIT(0) +#define ADDR_VALID BIT(1) + +enum netcp_addr_type { + ADDR_ANY, + ADDR_DEV, + ADDR_UCAST, + ADDR_MCAST, + ADDR_BCAST +}; + +struct netcp_addr { + struct netcp_intf *netcp; + unsigned char addr[ETH_ALEN]; + enum netcp_addr_type type; + unsigned int flags; + struct list_head node; +}; + +struct netcp_intf { + struct device *dev; + struct device *ndev_dev; + struct net_device *ndev; + bool big_endian; + unsigned int tx_compl_qid; + void *tx_pool; + struct list_head txhook_list_head; + unsigned int tx_pause_threshold; + void *tx_compl_q; + + unsigned int tx_resume_threshold; + void *rx_queue; + void *rx_pool; + struct list_head rxhook_list_head; + unsigned int rx_queue_id; + void *rx_fdq[KNAV_DMA_FDQ_PER_CHAN]; + u32 rx_buffer_sizes[KNAV_DMA_FDQ_PER_CHAN]; + struct napi_struct rx_napi; + struct napi_struct tx_napi; + + void *rx_channel; + const char *dma_chan_name; + u32 rx_pool_size; + u32 rx_pool_region_id; + u32 tx_pool_size; + u32 tx_pool_region_id; + struct list_head module_head; + struct list_head interface_list; + struct list_head addr_list; + bool netdev_registered; + bool primary_module_attached; + + /* Lock used for protecting Rx/Tx hook list management */ + spinlock_t lock; + struct netcp_device *netcp_device; + struct device_node *node_interface; + + /* DMA configuration data */ + u32 msg_enable; + u32 rx_queue_depths[KNAV_DMA_FDQ_PER_CHAN]; +}; + +#define NETCP_PSDATA_LEN KNAV_DMA_NUM_PS_WORDS +struct netcp_packet { + struct sk_buff *skb; + u32 *epib; + u32 *psdata; + unsigned int psdata_len; + struct netcp_intf *netcp; + struct netcp_tx_pipe *tx_pipe; + bool rxtstamp_complete; + void *ts_context; + + int (*txtstamp_complete)(void *ctx, struct netcp_packet *pkt); +}; + +static inline u32 *netcp_push_psdata(struct netcp_packet *p_info, + unsigned int bytes) +{ + u32 *buf; + unsigned int words; + + if ((bytes & 0x03) != 0) + return NULL; + words = bytes >> 2; + + if ((p_info->psdata_len + words) > NETCP_PSDATA_LEN) + return NULL; + + p_info->psdata_len += words; + buf = &p_info->psdata[NETCP_PSDATA_LEN - p_info->psdata_len]; + return buf; +} + +static inline int netcp_align_psdata(struct netcp_packet *p_info, + unsigned int byte_align) +{ + int padding; + + switch (byte_align) { + case 0: + padding = -EINVAL; + break; + case 1: + case 2: + case 4: + padding = 0; + break; + case 8: + padding = (p_info->psdata_len << 2) % 8; + break; + case 16: + padding = (p_info->psdata_len << 2) % 16; + break; + default: + padding = (p_info->psdata_len << 2) % byte_align; + break; + } + return padding; +} + +struct netcp_module { + const char *name; + struct module *owner; + bool primary; + + /* probe/remove: called once per NETCP instance */ + int (*probe)(struct netcp_device *netcp_device, + struct device *device, struct device_node *node, + void **inst_priv); + int (*remove)(struct netcp_device *netcp_device, void *inst_priv); + + /* attach/release: called once per network interface */ + int (*attach)(void *inst_priv, struct net_device *ndev, + struct device_node *node, void **intf_priv); + int (*release)(void *intf_priv); + int (*open)(void *intf_priv, struct net_device *ndev); + int (*close)(void *intf_priv, struct net_device *ndev); + int (*add_addr)(void *intf_priv, struct netcp_addr *naddr); + int (*del_addr)(void *intf_priv, struct netcp_addr *naddr); + int (*add_vid)(void *intf_priv, int vid); + int (*del_vid)(void *intf_priv, int vid); + int (*ioctl)(void *intf_priv, struct ifreq *req, int cmd); + + /* used internally */ + struct list_head module_list; + struct list_head interface_list; +}; + +int netcp_register_module(struct netcp_module *module); +void netcp_unregister_module(struct netcp_module *module); +void *netcp_module_get_intf_data(struct netcp_module *module, + struct netcp_intf *intf); + +int netcp_txpipe_init(struct netcp_tx_pipe *tx_pipe, + struct netcp_device *netcp_device, + const char *dma_chan_name, unsigned int dma_queue_id); +int netcp_txpipe_open(struct netcp_tx_pipe *tx_pipe); +int netcp_txpipe_close(struct netcp_tx_pipe *tx_pipe); + +typedef int netcp_hook_rtn(int order, void *data, struct netcp_packet *packet); +int netcp_register_txhook(struct netcp_intf *netcp_priv, int order, + netcp_hook_rtn *hook_rtn, void *hook_data); +int netcp_unregister_txhook(struct netcp_intf *netcp_priv, int order, + netcp_hook_rtn *hook_rtn, void *hook_data); +int netcp_register_rxhook(struct netcp_intf *netcp_priv, int order, + netcp_hook_rtn *hook_rtn, void *hook_data); +int netcp_unregister_rxhook(struct netcp_intf *netcp_priv, int order, + netcp_hook_rtn *hook_rtn, void *hook_data); +void *netcp_device_find_module(struct netcp_device *netcp_device, + const char *name); + +/* SGMII functions */ +int netcp_sgmii_reset(void __iomem *sgmii_ofs, int port); +int netcp_sgmii_get_port_link(void __iomem *sgmii_ofs, int port); +int netcp_sgmii_config(void __iomem *sgmii_ofs, int port, u32 interface); + +/* XGBE SERDES init functions */ +int netcp_xgbe_serdes_init(void __iomem *serdes_regs, void __iomem *xgbe_regs); + +#endif /* __NETCP_H__ */ diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c new file mode 100644 index 000000000000..a31a8c3c8e7c --- /dev/null +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -0,0 +1,2149 @@ +/* + * Keystone NetCP Core driver + * + * Copyright (C) 2014 Texas Instruments Incorporated + * Authors: Sandeep Nair <sandeep_n@ti.com> + * Sandeep Paulraj <s-paulraj@ti.com> + * Cyril Chemparathy <cyril@ti.com> + * Santosh Shilimkar <santosh.shilimkar@ti.com> + * Murali Karicheri <m-karicheri2@ti.com> + * Wingman Kwok <w-kwok2@ti.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 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/io.h> +#include <linux/module.h> +#include <linux/of_net.h> +#include <linux/of_address.h> +#include <linux/if_vlan.h> +#include <linux/pm_runtime.h> +#include <linux/platform_device.h> +#include <linux/soc/ti/knav_qmss.h> +#include <linux/soc/ti/knav_dma.h> + +#include "netcp.h" + +#define NETCP_SOP_OFFSET (NET_IP_ALIGN + NET_SKB_PAD) +#define NETCP_NAPI_WEIGHT 64 +#define NETCP_TX_TIMEOUT (5 * HZ) +#define NETCP_MIN_PACKET_SIZE ETH_ZLEN +#define NETCP_MAX_MCAST_ADDR 16 + +#define NETCP_EFUSE_REG_INDEX 0 + +#define NETCP_MOD_PROBE_SKIPPED 1 +#define NETCP_MOD_PROBE_FAILED 2 + +#define NETCP_DEBUG (NETIF_MSG_HW | NETIF_MSG_WOL | \ + NETIF_MSG_DRV | NETIF_MSG_LINK | \ + NETIF_MSG_IFUP | NETIF_MSG_INTR | \ + NETIF_MSG_PROBE | NETIF_MSG_TIMER | \ + NETIF_MSG_IFDOWN | NETIF_MSG_RX_ERR | \ + NETIF_MSG_TX_ERR | NETIF_MSG_TX_DONE | \ + NETIF_MSG_PKTDATA | NETIF_MSG_TX_QUEUED | \ + NETIF_MSG_RX_STATUS) + +#define knav_queue_get_id(q) knav_queue_device_control(q, \ + KNAV_QUEUE_GET_ID, (unsigned long)NULL) + +#define knav_queue_enable_notify(q) knav_queue_device_control(q, \ + KNAV_QUEUE_ENABLE_NOTIFY, \ + (unsigned long)NULL) + +#define knav_queue_disable_notify(q) knav_queue_device_control(q, \ + KNAV_QUEUE_DISABLE_NOTIFY, \ + (unsigned long)NULL) + +#define knav_queue_get_count(q) knav_queue_device_control(q, \ + KNAV_QUEUE_GET_COUNT, (unsigned long)NULL) + +#define for_each_netcp_module(module) \ + list_for_each_entry(module, &netcp_modules, module_list) + +#define for_each_netcp_device_module(netcp_device, inst_modpriv) \ + list_for_each_entry(inst_modpriv, \ + &((netcp_device)->modpriv_head), inst_list) + +#define for_each_module(netcp, intf_modpriv) \ + list_for_each_entry(intf_modpriv, &netcp->module_head, intf_list) + +/* Module management structures */ +struct netcp_device { + struct list_head device_list; + struct list_head interface_head; + struct list_head modpriv_head; + struct device *device; +}; + +struct netcp_inst_modpriv { + struct netcp_device *netcp_device; + struct netcp_module *netcp_module; + struct list_head inst_list; + void *module_priv; +}; + +struct netcp_intf_modpriv { + struct netcp_intf *netcp_priv; + struct netcp_module *netcp_module; + struct list_head intf_list; + void *module_priv; +}; + +static LIST_HEAD(netcp_devices); +static LIST_HEAD(netcp_modules); +static DEFINE_MUTEX(netcp_modules_lock); + +static int netcp_debug_level = -1; +module_param(netcp_debug_level, int, 0); +MODULE_PARM_DESC(netcp_debug_level, "Netcp debug level (NETIF_MSG bits) (0=none,...,16=all)"); + +/* Helper functions - Get/Set */ +static void get_pkt_info(u32 *buff, u32 *buff_len, u32 *ndesc, + struct knav_dma_desc *desc) +{ + *buff_len = desc->buff_len; + *buff = desc->buff; + *ndesc = desc->next_desc; +} + +static void get_pad_info(u32 *pad0, u32 *pad1, struct knav_dma_desc *desc) +{ + *pad0 = desc->pad[0]; + *pad1 = desc->pad[1]; +} + +static void get_org_pkt_info(u32 *buff, u32 *buff_len, + struct knav_dma_desc *desc) +{ + *buff = desc->orig_buff; + *buff_len = desc->orig_len; +} + +static void get_words(u32 *words, int num_words, u32 *desc) +{ + int i; + + for (i = 0; i < num_words; i++) + words[i] = desc[i]; +} + +static void set_pkt_info(u32 buff, u32 buff_len, u32 ndesc, + struct knav_dma_desc *desc) +{ + desc->buff_len = buff_len; + desc->buff = buff; + desc->next_desc = ndesc; +} + +static void set_desc_info(u32 desc_info, u32 pkt_info, + struct knav_dma_desc *desc) +{ + desc->desc_info = desc_info; + desc->packet_info = pkt_info; +} + +static void set_pad_info(u32 pad0, u32 pad1, struct knav_dma_desc *desc) +{ + desc->pad[0] = pad0; + desc->pad[1] = pad1; +} + +static void set_org_pkt_info(u32 buff, u32 buff_len, + struct knav_dma_desc *desc) +{ + desc->orig_buff = buff; + desc->orig_len = buff_len; +} + +static void set_words(u32 *words, int num_words, u32 *desc) +{ + int i; + + for (i = 0; i < num_words; i++) + desc[i] = words[i]; +} + +/* Read the e-fuse value as 32 bit values to be endian independent */ +static int emac_arch_get_mac_addr(char *x, void __iomem *efuse_mac) +{ + unsigned int addr0, addr1; + + addr1 = readl(efuse_mac + 4); + addr0 = readl(efuse_mac); + + x[0] = (addr1 & 0x0000ff00) >> 8; + x[1] = addr1 & 0x000000ff; + x[2] = (addr0 & 0xff000000) >> 24; + x[3] = (addr0 & 0x00ff0000) >> 16; + x[4] = (addr0 & 0x0000ff00) >> 8; + x[5] = addr0 & 0x000000ff; + + return 0; +} + +static const char *netcp_node_name(struct device_node *node) +{ + const char *name; + + if (of_property_read_string(node, "label", &name) < 0) + name = node->name; + if (!name) + name = "unknown"; + return name; +} + +/* Module management routines */ +static int netcp_register_interface(struct netcp_intf *netcp) +{ + int ret; + + ret = register_netdev(netcp->ndev); + if (!ret) + netcp->netdev_registered = true; + return ret; +} + +static int netcp_module_probe(struct netcp_device *netcp_device, + struct netcp_module *module) +{ + struct device *dev = netcp_device->device; + struct device_node *devices, *interface, *node = dev->of_node; + struct device_node *child; + struct netcp_inst_modpriv *inst_modpriv; + struct netcp_intf *netcp_intf; + struct netcp_module *tmp; + bool primary_module_registered = false; + int ret; + + /* Find this module in the sub-tree for this device */ + devices = of_get_child_by_name(node, "netcp-devices"); + if (!devices) { + dev_err(dev, "could not find netcp-devices node\n"); + return NETCP_MOD_PROBE_SKIPPED; + } + + for_each_available_child_of_node(devices, child) { + const char *name = netcp_node_name(child); + + if (!strcasecmp(module->name, name)) + break; + } + + of_node_put(devices); + /* If module not used for this device, skip it */ + if (!child) { + dev_warn(dev, "module(%s) not used for device\n", module->name); + return NETCP_MOD_PROBE_SKIPPED; + } + + inst_modpriv = devm_kzalloc(dev, sizeof(*inst_modpriv), GFP_KERNEL); + if (!inst_modpriv) { + of_node_put(child); + return -ENOMEM; + } + + inst_modpriv->netcp_device = netcp_device; + inst_modpriv->netcp_module = module; + list_add_tail(&inst_modpriv->inst_list, &netcp_device->modpriv_head); + + ret = module->probe(netcp_device, dev, child, + &inst_modpriv->module_priv); + of_node_put(child); + if (ret) { + dev_err(dev, "Probe of module(%s) failed with %d\n", + module->name, ret); + list_del(&inst_modpriv->inst_list); + devm_kfree(dev, inst_modpriv); + return NETCP_MOD_PROBE_FAILED; + } + + /* Attach modules only if the primary module is probed */ + for_each_netcp_module(tmp) { + if (tmp->primary) + primary_module_registered = true; + } + + if (!primary_module_registered) + return 0; + + /* Attach module to interfaces */ + list_for_each_entry(netcp_intf, &netcp_device->interface_head, + 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) + return -ENOMEM; + + interface = of_parse_phandle(netcp_intf->node_interface, + module->name, 0); + + intf_modpriv->netcp_priv = netcp_intf; + intf_modpriv->netcp_module = module; + list_add_tail(&intf_modpriv->intf_list, + &netcp_intf->module_head); + + ret = module->attach(inst_modpriv->module_priv, + netcp_intf->ndev, interface, + &intf_modpriv->module_priv); + of_node_put(interface); + if (ret) { + dev_dbg(dev, "Attach of module %s declined with %d\n", + module->name, ret); + list_del(&intf_modpriv->intf_list); + devm_kfree(dev, intf_modpriv); + continue; + } + } + return 0; +} + +int netcp_register_module(struct netcp_module *module) +{ + struct netcp_device *netcp_device; + struct netcp_module *tmp; + int ret; + + if (!module->name) { + WARN(1, "error registering netcp module: no name\n"); + return -EINVAL; + } + + if (!module->probe) { + WARN(1, "error registering netcp module: no probe\n"); + return -EINVAL; + } + + mutex_lock(&netcp_modules_lock); + + for_each_netcp_module(tmp) { + if (!strcasecmp(tmp->name, module->name)) { + mutex_unlock(&netcp_modules_lock); + return -EEXIST; + } + } + list_add_tail(&module->module_list, &netcp_modules); + + list_for_each_entry(netcp_device, &netcp_devices, device_list) { + ret = netcp_module_probe(netcp_device, module); + if (ret < 0) + goto fail; + } + + mutex_unlock(&netcp_modules_lock); + return 0; + +fail: + mutex_unlock(&netcp_modules_lock); + netcp_unregister_module(module); + return ret; +} +EXPORT_SYMBOL_GPL(netcp_register_module); + +static void netcp_release_module(struct netcp_device *netcp_device, + struct netcp_module *module) +{ + struct netcp_inst_modpriv *inst_modpriv, *inst_tmp; + struct netcp_intf *netcp_intf, *netcp_tmp; + struct device *dev = netcp_device->device; + + /* Release the module from each interface */ + list_for_each_entry_safe(netcp_intf, netcp_tmp, + &netcp_device->interface_head, + interface_list) { + struct netcp_intf_modpriv *intf_modpriv, *intf_tmp; + + list_for_each_entry_safe(intf_modpriv, intf_tmp, + &netcp_intf->module_head, + intf_list) { + if (intf_modpriv->netcp_module == module) { + module->release(intf_modpriv->module_priv); + list_del(&intf_modpriv->intf_list); + devm_kfree(dev, intf_modpriv); + break; + } + } + } + + /* Remove the module from each instance */ + list_for_each_entry_safe(inst_modpriv, inst_tmp, + &netcp_device->modpriv_head, inst_list) { + if (inst_modpriv->netcp_module == module) { + module->remove(netcp_device, + inst_modpriv->module_priv); + list_del(&inst_modpriv->inst_list); + devm_kfree(dev, inst_modpriv); + break; + } + } +} + +void netcp_unregister_module(struct netcp_module *module) +{ + struct netcp_device *netcp_device; + struct netcp_module *module_tmp; + + mutex_lock(&netcp_modules_lock); + + list_for_each_entry(netcp_device, &netcp_devices, device_list) { + netcp_release_module(netcp_device, module); + } + + /* Remove the module from the module list */ + for_each_netcp_module(module_tmp) { + if (module == module_tmp) { + list_del(&module->module_list); + break; + } + } + + mutex_unlock(&netcp_modules_lock); +} +EXPORT_SYMBOL_GPL(netcp_unregister_module); + +void *netcp_module_get_intf_data(struct netcp_module *module, + struct netcp_intf *intf) +{ + struct netcp_intf_modpriv *intf_modpriv; + + list_for_each_entry(intf_modpriv, &intf->module_head, intf_list) + if (intf_modpriv->netcp_module == module) + return intf_modpriv->module_priv; + return NULL; +} +EXPORT_SYMBOL_GPL(netcp_module_get_intf_data); + +/* Module TX and RX Hook management */ +struct netcp_hook_list { + struct list_head list; + netcp_hook_rtn *hook_rtn; + void *hook_data; + int order; +}; + +int netcp_register_txhook(struct netcp_intf *netcp_priv, int order, + netcp_hook_rtn *hook_rtn, void *hook_data) +{ + struct netcp_hook_list *entry; + struct netcp_hook_list *next; + unsigned long flags; + + entry = devm_kzalloc(netcp_priv->dev, sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->hook_rtn = hook_rtn; + entry->hook_data = hook_data; + entry->order = order; + + spin_lock_irqsave(&netcp_priv->lock, flags); + list_for_each_entry(next, &netcp_priv->txhook_list_head, list) { + if (next->order > order) + break; + } + __list_add(&entry->list, next->list.prev, &next->list); + spin_unlock_irqrestore(&netcp_priv->lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(netcp_register_txhook); + +int netcp_unregister_txhook(struct netcp_intf *netcp_priv, int order, + netcp_hook_rtn *hook_rtn, void *hook_data) +{ + struct netcp_hook_list *next, *n; + unsigned long flags; + + spin_lock_irqsave(&netcp_priv->lock, flags); + list_for_each_entry_safe(next, n, &netcp_priv->txhook_list_head, list) { + if ((next->order == order) && + (next->hook_rtn == hook_rtn) && + (next->hook_data == hook_data)) { + list_del(&next->list); + spin_unlock_irqrestore(&netcp_priv->lock, flags); + devm_kfree(netcp_priv->dev, next); + return 0; + } + } + spin_unlock_irqrestore(&netcp_priv->lock, flags); + return -ENOENT; +} +EXPORT_SYMBOL_GPL(netcp_unregister_txhook); + +int netcp_register_rxhook(struct netcp_intf *netcp_priv, int order, + netcp_hook_rtn *hook_rtn, void *hook_data) +{ + struct netcp_hook_list *entry; + struct netcp_hook_list *next; + unsigned long flags; + + entry = devm_kzalloc(netcp_priv->dev, sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->hook_rtn = hook_rtn; + entry->hook_data = hook_data; + entry->order = order; + + spin_lock_irqsave(&netcp_priv->lock, flags); + list_for_each_entry(next, &netcp_priv->rxhook_list_head, list) { + if (next->order > order) + break; + } + __list_add(&entry->list, next->list.prev, &next->list); + spin_unlock_irqrestore(&netcp_priv->lock, flags); + + return 0; +} + +int netcp_unregister_rxhook(struct netcp_intf *netcp_priv, int order, + netcp_hook_rtn *hook_rtn, void *hook_data) +{ + struct netcp_hook_list *next, *n; + unsigned long flags; + + spin_lock_irqsave(&netcp_priv->lock, flags); + list_for_each_entry_safe(next, n, &netcp_priv->rxhook_list_head, list) { + if ((next->order == order) && + (next->hook_rtn == hook_rtn) && + (next->hook_data == hook_data)) { + list_del(&next->list); + spin_unlock_irqrestore(&netcp_priv->lock, flags); + devm_kfree(netcp_priv->dev, next); + return 0; + } + } + spin_unlock_irqrestore(&netcp_priv->lock, flags); + + return -ENOENT; +} + +static void netcp_frag_free(bool is_frag, void *ptr) +{ + if (is_frag) + put_page(virt_to_head_page(ptr)); + else + kfree(ptr); +} + +static void netcp_free_rx_desc_chain(struct netcp_intf *netcp, + struct knav_dma_desc *desc) +{ + struct knav_dma_desc *ndesc; + dma_addr_t dma_desc, dma_buf; + unsigned int buf_len, dma_sz = sizeof(*ndesc); + void *buf_ptr; + u32 tmp; + + get_words(&dma_desc, 1, &desc->next_desc); + + while (dma_desc) { + ndesc = knav_pool_desc_unmap(netcp->rx_pool, dma_desc, dma_sz); + if (unlikely(!ndesc)) { + dev_err(netcp->ndev_dev, "failed to unmap Rx desc\n"); + break; + } + get_pkt_info(&dma_buf, &tmp, &dma_desc, ndesc); + get_pad_info((u32 *)&buf_ptr, &tmp, ndesc); + dma_unmap_page(netcp->dev, dma_buf, PAGE_SIZE, DMA_FROM_DEVICE); + __free_page(buf_ptr); + knav_pool_desc_put(netcp->rx_pool, desc); + } + + get_pad_info((u32 *)&buf_ptr, &buf_len, desc); + if (buf_ptr) + netcp_frag_free(buf_len <= PAGE_SIZE, buf_ptr); + knav_pool_desc_put(netcp->rx_pool, desc); +} + +static void netcp_empty_rx_queue(struct netcp_intf *netcp) +{ + struct knav_dma_desc *desc; + unsigned int dma_sz; + dma_addr_t dma; + + for (; ;) { + dma = knav_queue_pop(netcp->rx_queue, &dma_sz); + if (!dma) + break; + + desc = knav_pool_desc_unmap(netcp->rx_pool, dma, dma_sz); + if (unlikely(!desc)) { + dev_err(netcp->ndev_dev, "%s: failed to unmap Rx desc\n", + __func__); + netcp->ndev->stats.rx_errors++; + continue; + } + netcp_free_rx_desc_chain(netcp, desc); + netcp->ndev->stats.rx_dropped++; + } +} + +static int netcp_process_one_rx_packet(struct netcp_intf *netcp) +{ + unsigned int dma_sz, buf_len, org_buf_len; + struct knav_dma_desc *desc, *ndesc; + unsigned int pkt_sz = 0, accum_sz; + struct netcp_hook_list *rx_hook; + dma_addr_t dma_desc, dma_buff; + struct netcp_packet p_info; + struct sk_buff *skb; + void *org_buf_ptr; + u32 tmp; + + dma_desc = knav_queue_pop(netcp->rx_queue, &dma_sz); + if (!dma_desc) + return -1; + + desc = knav_pool_desc_unmap(netcp->rx_pool, dma_desc, dma_sz); + if (unlikely(!desc)) { + dev_err(netcp->ndev_dev, "failed to unmap Rx desc\n"); + return 0; + } + + get_pkt_info(&dma_buff, &buf_len, &dma_desc, desc); + get_pad_info((u32 *)&org_buf_ptr, &org_buf_len, desc); + + if (unlikely(!org_buf_ptr)) { + dev_err(netcp->ndev_dev, "NULL bufptr in desc\n"); + goto free_desc; + } + + pkt_sz &= KNAV_DMA_DESC_PKT_LEN_MASK; + accum_sz = buf_len; + dma_unmap_single(netcp->dev, dma_buff, buf_len, DMA_FROM_DEVICE); + + /* Build a new sk_buff for the primary buffer */ + skb = build_skb(org_buf_ptr, org_buf_len); + if (unlikely(!skb)) { + dev_err(netcp->ndev_dev, "build_skb() failed\n"); + goto free_desc; + } + + /* update data, tail and len */ + skb_reserve(skb, NETCP_SOP_OFFSET); + __skb_put(skb, buf_len); + + /* Fill in the page fragment list */ + while (dma_desc) { + struct page *page; + + ndesc = knav_pool_desc_unmap(netcp->rx_pool, dma_desc, dma_sz); + if (unlikely(!ndesc)) { + dev_err(netcp->ndev_dev, "failed to unmap Rx desc\n"); + goto free_desc; + } + + get_pkt_info(&dma_buff, &buf_len, &dma_desc, ndesc); + get_pad_info((u32 *)&page, &tmp, ndesc); + + if (likely(dma_buff && buf_len && page)) { + dma_unmap_page(netcp->dev, dma_buff, PAGE_SIZE, + DMA_FROM_DEVICE); + } else { + dev_err(netcp->ndev_dev, "Bad Rx desc dma_buff(%p), len(%d), page(%p)\n", + (void *)dma_buff, buf_len, page); + goto free_desc; + } + + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, + offset_in_page(dma_buff), buf_len, PAGE_SIZE); + accum_sz += buf_len; + + /* Free the descriptor */ + knav_pool_desc_put(netcp->rx_pool, ndesc); + } + + /* Free the primary descriptor */ + knav_pool_desc_put(netcp->rx_pool, desc); + + /* check for packet len and warn */ + if (unlikely(pkt_sz != accum_sz)) + dev_dbg(netcp->ndev_dev, "mismatch in packet size(%d) & sum of fragments(%d)\n", + pkt_sz, accum_sz); + + /* Remove ethernet FCS from the packet */ + __pskb_trim(skb, skb->len - ETH_FCS_LEN); + + /* Call each of the RX hooks */ + p_info.skb = skb; + p_info.rxtstamp_complete = false; + list_for_each_entry(rx_hook, &netcp->rxhook_list_head, list) { + int ret; + + ret = rx_hook->hook_rtn(rx_hook->order, rx_hook->hook_data, + &p_info); + if (unlikely(ret)) { + dev_err(netcp->ndev_dev, "RX hook %d failed: %d\n", + rx_hook->order, ret); + netcp->ndev->stats.rx_errors++; + dev_kfree_skb(skb); + return 0; + } + } + + netcp->ndev->last_rx = jiffies; + netcp->ndev->stats.rx_packets++; + netcp->ndev->stats.rx_bytes += skb->len; + + /* push skb up the stack */ + skb->protocol = eth_type_trans(skb, netcp->ndev); + netif_receive_skb(skb); + return 0; + +free_desc: + netcp_free_rx_desc_chain(netcp, desc); + netcp->ndev->stats.rx_errors++; + return 0; +} + +static int netcp_process_rx_packets(struct netcp_intf *netcp, + unsigned int budget) +{ + int i; + + for (i = 0; (i < budget) && !netcp_process_one_rx_packet(netcp); i++) + ; + return i; +} + +/* Release descriptors and attached buffers from Rx FDQ */ +static void netcp_free_rx_buf(struct netcp_intf *netcp, int fdq) +{ + struct knav_dma_desc *desc; + unsigned int buf_len, dma_sz; + dma_addr_t dma; + void *buf_ptr; + u32 tmp; + + /* Allocate descriptor */ + while ((dma = knav_queue_pop(netcp->rx_fdq[fdq], &dma_sz))) { + desc = knav_pool_desc_unmap(netcp->rx_pool, dma, dma_sz); + if (unlikely(!desc)) { + dev_err(netcp->ndev_dev, "failed to unmap Rx desc\n"); + continue; + } + + get_org_pkt_info(&dma, &buf_len, desc); + get_pad_info((u32 *)&buf_ptr, &tmp, desc); + + if (unlikely(!dma)) { + dev_err(netcp->ndev_dev, "NULL orig_buff in desc\n"); + knav_pool_desc_put(netcp->rx_pool, desc); + continue; + } + + if (unlikely(!buf_ptr)) { + dev_err(netcp->ndev_dev, "NULL bufptr in desc\n"); + knav_pool_desc_put(netcp->rx_pool, desc); + continue; + } + + if (fdq == 0) { + dma_unmap_single(netcp->dev, dma, buf_len, + DMA_FROM_DEVICE); + netcp_frag_free((buf_len <= PAGE_SIZE), buf_ptr); + } else { + dma_unmap_page(netcp->dev, dma, buf_len, + DMA_FROM_DEVICE); + __free_page(buf_ptr); + } + + knav_pool_desc_put(netcp->rx_pool, desc); + } +} + +static void netcp_rxpool_free(struct netcp_intf *netcp) +{ + int i; + + for (i = 0; i < KNAV_DMA_FDQ_PER_CHAN && + !IS_ERR_OR_NULL(netcp->rx_fdq[i]); i++) + netcp_free_rx_buf(netcp, i); + + if (knav_pool_count(netcp->rx_pool) != netcp->rx_pool_size) + dev_err(netcp->ndev_dev, "Lost Rx (%d) descriptors\n", + netcp->rx_pool_size - knav_pool_count(netcp->rx_pool)); + + knav_pool_destroy(netcp->rx_pool); + netcp->rx_pool = NULL; +} + +static void netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq) +{ + struct knav_dma_desc *hwdesc; + unsigned int buf_len, dma_sz; + u32 desc_info, pkt_info; + struct page *page; + dma_addr_t dma; + void *bufptr; + u32 pad[2]; + + /* Allocate descriptor */ + 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; + } + + if (likely(fdq == 0)) { + unsigned int primary_buf_len; + /* Allocate a primary receive queue entry */ + buf_len = netcp->rx_buffer_sizes[0] + NETCP_SOP_OFFSET; + primary_buf_len = SKB_DATA_ALIGN(buf_len) + + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); + + if (primary_buf_len <= PAGE_SIZE) { + bufptr = netdev_alloc_frag(primary_buf_len); + pad[1] = primary_buf_len; + } else { + bufptr = kmalloc(primary_buf_len, GFP_ATOMIC | + GFP_DMA32 | __GFP_COLD); + pad[1] = 0; + } + + if (unlikely(!bufptr)) { + dev_warn_ratelimited(netcp->ndev_dev, "Primary RX buffer alloc failed\n"); + goto fail; + } + dma = dma_map_single(netcp->dev, bufptr, buf_len, + DMA_TO_DEVICE); + pad[0] = (u32)bufptr; + + } else { + /* Allocate a secondary receive queue entry */ + page = alloc_page(GFP_ATOMIC | GFP_DMA32 | __GFP_COLD); + if (unlikely(!page)) { + dev_warn_ratelimited(netcp->ndev_dev, "Secondary page alloc failed\n"); + goto fail; + } + buf_len = PAGE_SIZE; + dma = dma_map_page(netcp->dev, page, 0, buf_len, DMA_TO_DEVICE); + pad[0] = (u32)page; + pad[1] = 0; + } + + desc_info = KNAV_DMA_DESC_PS_INFO_IN_DESC; + desc_info |= buf_len & KNAV_DMA_DESC_PKT_LEN_MASK; + pkt_info = KNAV_DMA_DESC_HAS_EPIB; + pkt_info |= KNAV_DMA_NUM_PS_WORDS << KNAV_DMA_DESC_PSLEN_SHIFT; + pkt_info |= (netcp->rx_queue_id & KNAV_DMA_DESC_RETQ_MASK) << + KNAV_DMA_DESC_RETQ_SHIFT; + set_org_pkt_info(dma, buf_len, hwdesc); + set_pad_info(pad[0], pad[1], hwdesc); + set_desc_info(desc_info, pkt_info, hwdesc); + + /* Push to FDQs */ + 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; + +fail: + knav_pool_desc_put(netcp->rx_pool, hwdesc); +} + +/* 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; + + /* 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); + } /* end for fdqs */ +} + +/* NAPI poll */ +static int netcp_rx_poll(struct napi_struct *napi, int budget) +{ + struct netcp_intf *netcp = container_of(napi, struct netcp_intf, + rx_napi); + unsigned int packets; + + packets = netcp_process_rx_packets(netcp, budget); + + if (packets < budget) { + napi_complete(&netcp->rx_napi); + knav_queue_enable_notify(netcp->rx_queue); + } + + netcp_rxpool_refill(netcp); + return packets; +} + +static void netcp_rx_notify(void *arg) +{ + struct netcp_intf *netcp = arg; + + knav_queue_disable_notify(netcp->rx_queue); + napi_schedule(&netcp->rx_napi); +} + +static void netcp_free_tx_desc_chain(struct netcp_intf *netcp, + struct knav_dma_desc *desc, + unsigned int desc_sz) +{ + struct knav_dma_desc *ndesc = desc; + dma_addr_t dma_desc, dma_buf; + unsigned int buf_len; + + while (ndesc) { + get_pkt_info(&dma_buf, &buf_len, &dma_desc, ndesc); + + if (dma_buf && buf_len) + dma_unmap_single(netcp->dev, dma_buf, buf_len, + DMA_TO_DEVICE); + else + dev_warn(netcp->ndev_dev, "bad Tx desc buf(%p), len(%d)\n", + (void *)dma_buf, buf_len); + + knav_pool_desc_put(netcp->tx_pool, ndesc); + ndesc = NULL; + if (dma_desc) { + ndesc = knav_pool_desc_unmap(netcp->tx_pool, dma_desc, + desc_sz); + if (!ndesc) + dev_err(netcp->ndev_dev, "failed to unmap Tx desc\n"); + } + } +} + +static int netcp_process_tx_compl_packets(struct netcp_intf *netcp, + unsigned int budget) +{ + struct knav_dma_desc *desc; + struct sk_buff *skb; + unsigned int dma_sz; + dma_addr_t dma; + int pkts = 0; + u32 tmp; + + while (budget--) { + dma = knav_queue_pop(netcp->tx_compl_q, &dma_sz); + if (!dma) + break; + desc = knav_pool_desc_unmap(netcp->tx_pool, dma, dma_sz); + if (unlikely(!desc)) { + dev_err(netcp->ndev_dev, "failed to unmap Tx desc\n"); + netcp->ndev->stats.tx_errors++; + continue; + } + + get_pad_info((u32 *)&skb, &tmp, desc); + netcp_free_tx_desc_chain(netcp, desc, dma_sz); + if (!skb) { + dev_err(netcp->ndev_dev, "No skb in Tx desc\n"); + netcp->ndev->stats.tx_errors++; + continue; + } + + if (netif_subqueue_stopped(netcp->ndev, skb) && + netif_running(netcp->ndev) && + (knav_pool_count(netcp->tx_pool) > + netcp->tx_resume_threshold)) { + u16 subqueue = skb_get_queue_mapping(skb); + + netif_wake_subqueue(netcp->ndev, subqueue); + } + + netcp->ndev->stats.tx_packets++; + netcp->ndev->stats.tx_bytes += skb->len; + dev_kfree_skb(skb); + pkts++; + } + return pkts; +} + +static int netcp_tx_poll(struct napi_struct *napi, int budget) +{ + int packets; + struct netcp_intf *netcp = container_of(napi, struct netcp_intf, + tx_napi); + + packets = netcp_process_tx_compl_packets(netcp, budget); + if (packets < budget) { + napi_complete(&netcp->tx_napi); + knav_queue_enable_notify(netcp->tx_compl_q); + } + + return packets; +} + +static void netcp_tx_notify(void *arg) +{ + struct netcp_intf *netcp = arg; + + knav_queue_disable_notify(netcp->tx_compl_q); + napi_schedule(&netcp->tx_napi); +} + +static struct knav_dma_desc* +netcp_tx_map_skb(struct sk_buff *skb, struct netcp_intf *netcp) +{ + struct knav_dma_desc *desc, *ndesc, *pdesc; + unsigned int pkt_len = skb_headlen(skb); + struct device *dev = netcp->dev; + dma_addr_t dma_addr; + unsigned int dma_sz; + int i; + + /* Map the linear buffer */ + dma_addr = dma_map_single(dev, skb->data, pkt_len, DMA_TO_DEVICE); + if (unlikely(!dma_addr)) { + dev_err(netcp->ndev_dev, "Failed to map skb buffer\n"); + return NULL; + } + + desc = knav_pool_desc_get(netcp->tx_pool); + if (unlikely(IS_ERR_OR_NULL(desc))) { + dev_err(netcp->ndev_dev, "out of TX desc\n"); + dma_unmap_single(dev, dma_addr, pkt_len, DMA_TO_DEVICE); + return NULL; + } + + set_pkt_info(dma_addr, pkt_len, 0, desc); + if (skb_is_nonlinear(skb)) { + prefetchw(skb_shinfo(skb)); + } else { + desc->next_desc = 0; + goto upd_pkt_len; + } + + pdesc = desc; + + /* Handle the case where skb is fragmented in pages */ + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + struct page *page = skb_frag_page(frag); + u32 page_offset = frag->page_offset; + u32 buf_len = skb_frag_size(frag); + dma_addr_t desc_dma; + u32 pkt_info; + + dma_addr = dma_map_page(dev, page, page_offset, buf_len, + DMA_TO_DEVICE); + if (unlikely(!dma_addr)) { + dev_err(netcp->ndev_dev, "Failed to map skb page\n"); + goto free_descs; + } + + ndesc = knav_pool_desc_get(netcp->tx_pool); + if (unlikely(IS_ERR_OR_NULL(ndesc))) { + dev_err(netcp->ndev_dev, "out of TX desc for frags\n"); + dma_unmap_page(dev, dma_addr, buf_len, DMA_TO_DEVICE); + goto free_descs; + } + + desc_dma = knav_pool_desc_virt_to_dma(netcp->tx_pool, + (void *)ndesc); + pkt_info = + (netcp->tx_compl_qid & KNAV_DMA_DESC_RETQ_MASK) << + KNAV_DMA_DESC_RETQ_SHIFT; + set_pkt_info(dma_addr, buf_len, 0, ndesc); + set_words(&desc_dma, 1, &pdesc->next_desc); + pkt_len += buf_len; + if (pdesc != desc) + knav_pool_desc_map(netcp->tx_pool, pdesc, + sizeof(*pdesc), &desc_dma, &dma_sz); + pdesc = ndesc; + } + if (pdesc != desc) + knav_pool_desc_map(netcp->tx_pool, pdesc, sizeof(*pdesc), + &dma_addr, &dma_sz); + + /* frag list based linkage is not supported for now. */ + if (skb_shinfo(skb)->frag_list) { + dev_err_ratelimited(netcp->ndev_dev, "NETIF_F_FRAGLIST not supported\n"); + goto free_descs; + } + +upd_pkt_len: + WARN_ON(pkt_len != skb->len); + + pkt_len &= KNAV_DMA_DESC_PKT_LEN_MASK; + set_words(&pkt_len, 1, &desc->desc_info); + return desc; + +free_descs: + netcp_free_tx_desc_chain(netcp, desc, sizeof(*desc)); + return NULL; +} + +static int netcp_tx_submit_skb(struct netcp_intf *netcp, + struct sk_buff *skb, + struct knav_dma_desc *desc) +{ + struct netcp_tx_pipe *tx_pipe = NULL; + struct netcp_hook_list *tx_hook; + struct netcp_packet p_info; + u32 packet_info = 0; + unsigned int dma_sz; + dma_addr_t dma; + int ret = 0; + + p_info.netcp = netcp; + p_info.skb = skb; + p_info.tx_pipe = NULL; + p_info.psdata_len = 0; + p_info.ts_context = NULL; + p_info.txtstamp_complete = NULL; + p_info.epib = desc->epib; + p_info.psdata = desc->psdata; + memset(p_info.epib, 0, KNAV_DMA_NUM_EPIB_WORDS * sizeof(u32)); + + /* Find out where to inject the packet for transmission */ + list_for_each_entry(tx_hook, &netcp->txhook_list_head, list) { + ret = tx_hook->hook_rtn(tx_hook->order, tx_hook->hook_data, + &p_info); + if (unlikely(ret != 0)) { + dev_err(netcp->ndev_dev, "TX hook %d rejected the packet with reason(%d)\n", + tx_hook->order, ret); + ret = (ret < 0) ? ret : NETDEV_TX_OK; + goto out; + } + } + + /* Make sure some TX hook claimed the packet */ + tx_pipe = p_info.tx_pipe; + if (!tx_pipe) { + dev_err(netcp->ndev_dev, "No TX hook claimed the packet!\n"); + ret = -ENXIO; + goto out; + } + + /* update descriptor */ + if (p_info.psdata_len) { + u32 *psdata = p_info.psdata; + + memmove(p_info.psdata, p_info.psdata + p_info.psdata_len, + p_info.psdata_len); + set_words(psdata, p_info.psdata_len, psdata); + packet_info |= + (p_info.psdata_len & KNAV_DMA_DESC_PSLEN_MASK) << + KNAV_DMA_DESC_PSLEN_SHIFT; + } + + packet_info |= KNAV_DMA_DESC_HAS_EPIB | + ((netcp->tx_compl_qid & KNAV_DMA_DESC_RETQ_MASK) << + KNAV_DMA_DESC_RETQ_SHIFT) | + ((tx_pipe->dma_psflags & KNAV_DMA_DESC_PSFLAG_MASK) << + KNAV_DMA_DESC_PSFLAG_SHIFT); + + set_words(&packet_info, 1, &desc->packet_info); + set_words((u32 *)&skb, 1, &desc->pad[0]); + + /* submit packet descriptor */ + ret = knav_pool_desc_map(netcp->tx_pool, desc, sizeof(*desc), &dma, + &dma_sz); + if (unlikely(ret)) { + dev_err(netcp->ndev_dev, "%s() failed to map desc\n", __func__); + ret = -ENOMEM; + goto out; + } + skb_tx_timestamp(skb); + knav_queue_push(tx_pipe->dma_queue, dma, dma_sz, 0); + +out: + return ret; +} + +/* Submit the packet */ +static int netcp_ndo_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ + struct netcp_intf *netcp = netdev_priv(ndev); + int subqueue = skb_get_queue_mapping(skb); + struct knav_dma_desc *desc; + int desc_count, ret = 0; + + if (unlikely(skb->len <= 0)) { + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + if (unlikely(skb->len < NETCP_MIN_PACKET_SIZE)) { + ret = skb_padto(skb, NETCP_MIN_PACKET_SIZE); + if (ret < 0) { + /* If we get here, the skb has already been dropped */ + dev_warn(netcp->ndev_dev, "padding failed (%d), packet dropped\n", + ret); + ndev->stats.tx_dropped++; + return ret; + } + skb->len = NETCP_MIN_PACKET_SIZE; + } + + desc = netcp_tx_map_skb(skb, netcp); + if (unlikely(!desc)) { + netif_stop_subqueue(ndev, subqueue); + ret = -ENOBUFS; + goto drop; + } + + ret = netcp_tx_submit_skb(netcp, skb, desc); + if (ret) + goto drop; + + ndev->trans_start = jiffies; + + /* Check Tx pool count & stop subqueue if needed */ + desc_count = knav_pool_count(netcp->tx_pool); + if (desc_count < netcp->tx_pause_threshold) { + dev_dbg(netcp->ndev_dev, "pausing tx, count(%d)\n", desc_count); + netif_stop_subqueue(ndev, subqueue); + } + return NETDEV_TX_OK; + +drop: + ndev->stats.tx_dropped++; + if (desc) + netcp_free_tx_desc_chain(netcp, desc, sizeof(*desc)); + dev_kfree_skb(skb); + return ret; +} + +int netcp_txpipe_close(struct netcp_tx_pipe *tx_pipe) +{ + if (tx_pipe->dma_channel) { + knav_dma_close_channel(tx_pipe->dma_channel); + tx_pipe->dma_channel = NULL; + } + return 0; +} +EXPORT_SYMBOL_GPL(netcp_txpipe_close); + +int netcp_txpipe_open(struct netcp_tx_pipe *tx_pipe) +{ + struct device *dev = tx_pipe->netcp_device->device; + struct knav_dma_cfg config; + int ret = 0; + u8 name[16]; + + memset(&config, 0, sizeof(config)); + config.direction = DMA_MEM_TO_DEV; + config.u.tx.filt_einfo = false; + config.u.tx.filt_pswords = false; + config.u.tx.priority = DMA_PRIO_MED_L; + + tx_pipe->dma_channel = knav_dma_open_channel(dev, + tx_pipe->dma_chan_name, &config); + if (IS_ERR_OR_NULL(tx_pipe->dma_channel)) { + dev_err(dev, "failed opening tx chan(%s)\n", + tx_pipe->dma_chan_name); + goto err; + } + + snprintf(name, sizeof(name), "tx-pipe-%s", dev_name(dev)); + tx_pipe->dma_queue = knav_queue_open(name, tx_pipe->dma_queue_id, + KNAV_QUEUE_SHARED); + if (IS_ERR(tx_pipe->dma_queue)) { + dev_err(dev, "Could not open DMA queue for channel \"%s\": %d\n", + name, ret); + ret = PTR_ERR(tx_pipe->dma_queue); + goto err; + } + + dev_dbg(dev, "opened tx pipe %s\n", name); + return 0; + +err: + if (!IS_ERR_OR_NULL(tx_pipe->dma_channel)) + knav_dma_close_channel(tx_pipe->dma_channel); + tx_pipe->dma_channel = NULL; + return ret; +} +EXPORT_SYMBOL_GPL(netcp_txpipe_open); + +int netcp_txpipe_init(struct netcp_tx_pipe *tx_pipe, + struct netcp_device *netcp_device, + const char *dma_chan_name, unsigned int dma_queue_id) +{ + memset(tx_pipe, 0, sizeof(*tx_pipe)); + tx_pipe->netcp_device = netcp_device; + tx_pipe->dma_chan_name = dma_chan_name; + tx_pipe->dma_queue_id = dma_queue_id; + return 0; +} +EXPORT_SYMBOL_GPL(netcp_txpipe_init); + +static struct netcp_addr *netcp_addr_find(struct netcp_intf *netcp, + const u8 *addr, + enum netcp_addr_type type) +{ + struct netcp_addr *naddr; + + list_for_each_entry(naddr, &netcp->addr_list, node) { + if (naddr->type != type) + continue; + if (addr && memcmp(addr, naddr->addr, ETH_ALEN)) + continue; + return naddr; + } + + return NULL; +} + +static struct netcp_addr *netcp_addr_add(struct netcp_intf *netcp, + const u8 *addr, + enum netcp_addr_type type) +{ + struct netcp_addr *naddr; + + naddr = devm_kmalloc(netcp->dev, sizeof(*naddr), GFP_ATOMIC); + if (!naddr) + return NULL; + + naddr->type = type; + naddr->flags = 0; + naddr->netcp = netcp; + if (addr) + ether_addr_copy(naddr->addr, addr); + else + memset(naddr->addr, 0, ETH_ALEN); + list_add_tail(&naddr->node, &netcp->addr_list); + + return naddr; +} + +static void netcp_addr_del(struct netcp_intf *netcp, struct netcp_addr *naddr) +{ + list_del(&naddr->node); + devm_kfree(netcp->dev, naddr); +} + +static void netcp_addr_clear_mark(struct netcp_intf *netcp) +{ + struct netcp_addr *naddr; + + list_for_each_entry(naddr, &netcp->addr_list, node) + naddr->flags = 0; +} + +static void netcp_addr_add_mark(struct netcp_intf *netcp, const u8 *addr, + enum netcp_addr_type type) +{ + struct netcp_addr *naddr; + + naddr = netcp_addr_find(netcp, addr, type); + if (naddr) { + naddr->flags |= ADDR_VALID; + return; + } + + naddr = netcp_addr_add(netcp, addr, type); + if (!WARN_ON(!naddr)) + naddr->flags |= ADDR_NEW; +} + +static void netcp_addr_sweep_del(struct netcp_intf *netcp) +{ + struct netcp_addr *naddr, *tmp; + struct netcp_intf_modpriv *priv; + struct netcp_module *module; + int error; + + list_for_each_entry_safe(naddr, tmp, &netcp->addr_list, node) { + if (naddr->flags & (ADDR_VALID | ADDR_NEW)) + 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) + continue; + error = module->del_addr(priv->module_priv, + naddr); + WARN_ON(error); + } + mutex_unlock(&netcp_modules_lock); + netcp_addr_del(netcp, naddr); + } +} + +static void netcp_addr_sweep_add(struct netcp_intf *netcp) +{ + struct netcp_addr *naddr, *tmp; + struct netcp_intf_modpriv *priv; + struct netcp_module *module; + int error; + + list_for_each_entry_safe(naddr, tmp, &netcp->addr_list, node) { + if (!(naddr->flags & ADDR_NEW)) + 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) + continue; + error = module->add_addr(priv->module_priv, naddr); + WARN_ON(error); + } + mutex_unlock(&netcp_modules_lock); + } +} + +static void netcp_set_rx_mode(struct net_device *ndev) +{ + struct netcp_intf *netcp = netdev_priv(ndev); + struct netdev_hw_addr *ndev_addr; + bool promisc; + + promisc = (ndev->flags & IFF_PROMISC || + ndev->flags & IFF_ALLMULTI || + netdev_mc_count(ndev) > NETCP_MAX_MCAST_ADDR); + + /* first clear all marks */ + netcp_addr_clear_mark(netcp); + + /* next add new entries, mark existing ones */ + netcp_addr_add_mark(netcp, ndev->broadcast, ADDR_BCAST); + for_each_dev_addr(ndev, ndev_addr) + netcp_addr_add_mark(netcp, ndev_addr->addr, ADDR_DEV); + netdev_for_each_uc_addr(ndev_addr, ndev) + netcp_addr_add_mark(netcp, ndev_addr->addr, ADDR_UCAST); + netdev_for_each_mc_addr(ndev_addr, ndev) + netcp_addr_add_mark(netcp, ndev_addr->addr, ADDR_MCAST); + + if (promisc) + netcp_addr_add_mark(netcp, NULL, ADDR_ANY); + + /* finally sweep and callout into modules */ + netcp_addr_sweep_del(netcp); + netcp_addr_sweep_add(netcp); +} + +static void netcp_free_navigator_resources(struct netcp_intf *netcp) +{ + int i; + + if (netcp->rx_channel) { + knav_dma_close_channel(netcp->rx_channel); + netcp->rx_channel = NULL; + } + + if (!IS_ERR_OR_NULL(netcp->rx_pool)) + netcp_rxpool_free(netcp); + + if (!IS_ERR_OR_NULL(netcp->rx_queue)) { + knav_queue_close(netcp->rx_queue); + netcp->rx_queue = NULL; + } + + for (i = 0; i < KNAV_DMA_FDQ_PER_CHAN && + !IS_ERR_OR_NULL(netcp->rx_fdq[i]) ; ++i) { + knav_queue_close(netcp->rx_fdq[i]); + netcp->rx_fdq[i] = NULL; + } + + if (!IS_ERR_OR_NULL(netcp->tx_compl_q)) { + knav_queue_close(netcp->tx_compl_q); + netcp->tx_compl_q = NULL; + } + + if (!IS_ERR_OR_NULL(netcp->tx_pool)) { + knav_pool_destroy(netcp->tx_pool); + netcp->tx_pool = NULL; + } +} + +static int netcp_setup_navigator_resources(struct net_device *ndev) +{ + struct netcp_intf *netcp = netdev_priv(ndev); + struct knav_queue_notify_config notify_cfg; + struct knav_dma_cfg config; + u32 last_fdq = 0; + u8 name[16]; + int ret; + int i; + + /* Create Rx/Tx descriptor pools */ + snprintf(name, sizeof(name), "rx-pool-%s", ndev->name); + netcp->rx_pool = knav_pool_create(name, netcp->rx_pool_size, + netcp->rx_pool_region_id); + if (IS_ERR_OR_NULL(netcp->rx_pool)) { + dev_err(netcp->ndev_dev, "Couldn't create rx pool\n"); + ret = PTR_ERR(netcp->rx_pool); + goto fail; + } + + snprintf(name, sizeof(name), "tx-pool-%s", ndev->name); + netcp->tx_pool = knav_pool_create(name, netcp->tx_pool_size, + netcp->tx_pool_region_id); + if (IS_ERR_OR_NULL(netcp->tx_pool)) { + dev_err(netcp->ndev_dev, "Couldn't create tx pool\n"); + ret = PTR_ERR(netcp->tx_pool); + goto fail; + } + + /* open Tx completion queue */ + snprintf(name, sizeof(name), "tx-compl-%s", ndev->name); + netcp->tx_compl_q = knav_queue_open(name, netcp->tx_compl_qid, 0); + if (IS_ERR_OR_NULL(netcp->tx_compl_q)) { + ret = PTR_ERR(netcp->tx_compl_q); + goto fail; + } + netcp->tx_compl_qid = knav_queue_get_id(netcp->tx_compl_q); + + /* Set notification for Tx completion */ + notify_cfg.fn = netcp_tx_notify; + notify_cfg.fn_arg = netcp; + ret = knav_queue_device_control(netcp->tx_compl_q, + KNAV_QUEUE_SET_NOTIFIER, + (unsigned long)¬ify_cfg); + if (ret) + goto fail; + + knav_queue_disable_notify(netcp->tx_compl_q); + + /* open Rx completion queue */ + snprintf(name, sizeof(name), "rx-compl-%s", ndev->name); + netcp->rx_queue = knav_queue_open(name, netcp->rx_queue_id, 0); + if (IS_ERR_OR_NULL(netcp->rx_queue)) { + ret = PTR_ERR(netcp->rx_queue); + goto fail; + } + netcp->rx_queue_id = knav_queue_get_id(netcp->rx_queue); + + /* Set notification for Rx completion */ + notify_cfg.fn = netcp_rx_notify; + notify_cfg.fn_arg = netcp; + ret = knav_queue_device_control(netcp->rx_queue, + KNAV_QUEUE_SET_NOTIFIER, + (unsigned long)¬ify_cfg); + if (ret) + goto fail; + + knav_queue_disable_notify(netcp->rx_queue); + + /* open Rx FDQs */ + for (i = 0; i < KNAV_DMA_FDQ_PER_CHAN && + netcp->rx_queue_depths[i] && netcp->rx_buffer_sizes[i]; ++i) { + snprintf(name, sizeof(name), "rx-fdq-%s-%d", ndev->name, i); + netcp->rx_fdq[i] = knav_queue_open(name, KNAV_QUEUE_GP, 0); + if (IS_ERR_OR_NULL(netcp->rx_fdq[i])) { + ret = PTR_ERR(netcp->rx_fdq[i]); + goto fail; + } + } + + memset(&config, 0, sizeof(config)); + config.direction = DMA_DEV_TO_MEM; + config.u.rx.einfo_present = true; + config.u.rx.psinfo_present = true; + config.u.rx.err_mode = DMA_DROP; + config.u.rx.desc_type = DMA_DESC_HOST; + config.u.rx.psinfo_at_sop = false; + config.u.rx.sop_offset = NETCP_SOP_OFFSET; + config.u.rx.dst_q = netcp->rx_queue_id; + config.u.rx.thresh = DMA_THRESH_NONE; + + for (i = 0; i < KNAV_DMA_FDQ_PER_CHAN; ++i) { + if (netcp->rx_fdq[i]) + last_fdq = knav_queue_get_id(netcp->rx_fdq[i]); + config.u.rx.fdq[i] = last_fdq; + } + + netcp->rx_channel = knav_dma_open_channel(netcp->netcp_device->device, + netcp->dma_chan_name, &config); + if (IS_ERR_OR_NULL(netcp->rx_channel)) { + dev_err(netcp->ndev_dev, "failed opening rx chan(%s\n", + netcp->dma_chan_name); + goto fail; + } + + dev_dbg(netcp->ndev_dev, "opened RX channel: %p\n", netcp->rx_channel); + return 0; + +fail: + netcp_free_navigator_resources(netcp); + return ret; +} + +/* Open the device */ +static int netcp_ndo_open(struct net_device *ndev) +{ + struct netcp_intf *netcp = netdev_priv(ndev); + struct netcp_intf_modpriv *intf_modpriv; + struct netcp_module *module; + int ret; + + netif_carrier_off(ndev); + ret = netcp_setup_navigator_resources(ndev); + if (ret) { + dev_err(netcp->ndev_dev, "Failed to setup navigator resources\n"); + goto fail; + } + + mutex_lock(&netcp_modules_lock); + for_each_module(netcp, intf_modpriv) { + module = intf_modpriv->netcp_module; + if (module->open) { + ret = module->open(intf_modpriv->module_priv, ndev); + if (ret != 0) { + dev_err(netcp->ndev_dev, "module open failed\n"); + goto fail_open; + } + } + } + mutex_unlock(&netcp_modules_lock); + + netcp_rxpool_refill(netcp); + napi_enable(&netcp->rx_napi); + napi_enable(&netcp->tx_napi); + knav_queue_enable_notify(netcp->tx_compl_q); + knav_queue_enable_notify(netcp->rx_queue); + netif_tx_wake_all_queues(ndev); + dev_dbg(netcp->ndev_dev, "netcp device %s opened\n", ndev->name); + return 0; + +fail_open: + for_each_module(netcp, intf_modpriv) { + module = intf_modpriv->netcp_module; + if (module->close) + module->close(intf_modpriv->module_priv, ndev); + } + mutex_unlock(&netcp_modules_lock); + +fail: + netcp_free_navigator_resources(netcp); + return ret; +} + +/* Close the device */ +static int netcp_ndo_stop(struct net_device *ndev) +{ + struct netcp_intf *netcp = netdev_priv(ndev); + struct netcp_intf_modpriv *intf_modpriv; + struct netcp_module *module; + int err = 0; + + netif_tx_stop_all_queues(ndev); + netif_carrier_off(ndev); + netcp_addr_clear_mark(netcp); + netcp_addr_sweep_del(netcp); + knav_queue_disable_notify(netcp->rx_queue); + knav_queue_disable_notify(netcp->tx_compl_q); + 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) { + err = module->close(intf_modpriv->module_priv, ndev); + if (err != 0) + dev_err(netcp->ndev_dev, "Close failed\n"); + } + } + mutex_unlock(&netcp_modules_lock); + + /* Recycle Rx descriptors from completion queue */ + netcp_empty_rx_queue(netcp); + + /* Recycle Tx descriptors from completion queue */ + netcp_process_tx_compl_packets(netcp, netcp->tx_pool_size); + + if (knav_pool_count(netcp->tx_pool) != netcp->tx_pool_size) + dev_err(netcp->ndev_dev, "Lost (%d) Tx descs\n", + netcp->tx_pool_size - knav_pool_count(netcp->tx_pool)); + + netcp_free_navigator_resources(netcp); + dev_dbg(netcp->ndev_dev, "netcp device %s stopped\n", ndev->name); + return 0; +} + +static int netcp_ndo_ioctl(struct net_device *ndev, + struct ifreq *req, int cmd) +{ + struct netcp_intf *netcp = netdev_priv(ndev); + struct netcp_intf_modpriv *intf_modpriv; + struct netcp_module *module; + int ret = -1, err = -EOPNOTSUPP; + + 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) + continue; + + err = module->ioctl(intf_modpriv->module_priv, req, cmd); + if ((err < 0) && (err != -EOPNOTSUPP)) { + ret = err; + goto out; + } + if (err == 0) + ret = err; + } + +out: + mutex_unlock(&netcp_modules_lock); + return (ret == 0) ? 0 : err; +} + +static int netcp_ndo_change_mtu(struct net_device *ndev, int new_mtu) +{ + struct netcp_intf *netcp = netdev_priv(ndev); + + /* MTU < 68 is an error for IPv4 traffic */ + if ((new_mtu < 68) || + (new_mtu > (NETCP_MAX_FRAME_SIZE - ETH_HLEN - ETH_FCS_LEN))) { + dev_err(netcp->ndev_dev, "Invalid mtu size = %d\n", new_mtu); + return -EINVAL; + } + + ndev->mtu = new_mtu; + return 0; +} + +static void netcp_ndo_tx_timeout(struct net_device *ndev) +{ + struct netcp_intf *netcp = netdev_priv(ndev); + unsigned int descs = knav_pool_count(netcp->tx_pool); + + dev_err(netcp->ndev_dev, "transmit timed out tx descs(%d)\n", descs); + netcp_process_tx_compl_packets(netcp, netcp->tx_pool_size); + ndev->trans_start = jiffies; + netif_tx_wake_all_queues(ndev); +} + +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; + int err = 0; + + dev_dbg(netcp->ndev_dev, "adding rx vlan id: %d\n", vid); + + mutex_lock(&netcp_modules_lock); + for_each_module(netcp, intf_modpriv) { + module = intf_modpriv->netcp_module; + if ((module->add_vid) && (vid != 0)) { + err = module->add_vid(intf_modpriv->module_priv, vid); + if (err != 0) { + dev_err(netcp->ndev_dev, "Could not add vlan id = %d\n", + vid); + break; + } + } + } + mutex_unlock(&netcp_modules_lock); + return err; +} + +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; + int err = 0; + + dev_dbg(netcp->ndev_dev, "removing rx vlan id: %d\n", vid); + + mutex_lock(&netcp_modules_lock); + for_each_module(netcp, intf_modpriv) { + module = intf_modpriv->netcp_module; + if (module->del_vid) { + err = module->del_vid(intf_modpriv->module_priv, vid); + if (err != 0) { + dev_err(netcp->ndev_dev, "Could not delete vlan id = %d\n", + vid); + break; + } + } + } + mutex_unlock(&netcp_modules_lock); + return err; +} + +static u16 netcp_select_queue(struct net_device *dev, struct sk_buff *skb, + void *accel_priv, + select_queue_fallback_t fallback) +{ + return 0; +} + +static int netcp_setup_tc(struct net_device *dev, u8 num_tc) +{ + int i; + + /* setup tc must be called under rtnl lock */ + ASSERT_RTNL(); + + /* Sanity-check the number of traffic classes requested */ + if ((dev->real_num_tx_queues <= 1) || + (dev->real_num_tx_queues < num_tc)) + return -EINVAL; + + /* Configure traffic class to queue mappings */ + if (num_tc) { + netdev_set_num_tc(dev, num_tc); + for (i = 0; i < num_tc; i++) + netdev_set_tc_queue(dev, i, 1, i); + } else { + netdev_reset_tc(dev); + } + + return 0; +} + +static const struct net_device_ops netcp_netdev_ops = { + .ndo_open = netcp_ndo_open, + .ndo_stop = netcp_ndo_stop, + .ndo_start_xmit = netcp_ndo_start_xmit, + .ndo_set_rx_mode = netcp_set_rx_mode, + .ndo_do_ioctl = netcp_ndo_ioctl, + .ndo_change_mtu = netcp_ndo_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, + .ndo_vlan_rx_add_vid = netcp_rx_add_vid, + .ndo_vlan_rx_kill_vid = netcp_rx_kill_vid, + .ndo_tx_timeout = netcp_ndo_tx_timeout, + .ndo_select_queue = netcp_select_queue, + .ndo_setup_tc = netcp_setup_tc, +}; + +static int netcp_create_interface(struct netcp_device *netcp_device, + struct device_node *node_interface) +{ + struct device *dev = netcp_device->device; + struct device_node *node = dev->of_node; + struct netcp_intf *netcp; + struct net_device *ndev; + resource_size_t size; + struct resource res; + void __iomem *efuse = NULL; + u32 efuse_mac = 0; + const void *mac_addr; + u8 efuse_mac_addr[6]; + u32 temp[2]; + int ret = 0; + + ndev = alloc_etherdev_mqs(sizeof(*netcp), 1, 1); + if (!ndev) { + dev_err(dev, "Error allocating netdev\n"); + return -ENOMEM; + } + + ndev->features |= NETIF_F_SG; + ndev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; + ndev->hw_features = ndev->features; + ndev->vlan_features |= NETIF_F_SG; + + netcp = netdev_priv(ndev); + spin_lock_init(&netcp->lock); + INIT_LIST_HEAD(&netcp->module_head); + INIT_LIST_HEAD(&netcp->txhook_list_head); + INIT_LIST_HEAD(&netcp->rxhook_list_head); + INIT_LIST_HEAD(&netcp->addr_list); + netcp->netcp_device = netcp_device; + netcp->dev = netcp_device->device; + netcp->ndev = ndev; + netcp->ndev_dev = &ndev->dev; + netcp->msg_enable = netif_msg_init(netcp_debug_level, NETCP_DEBUG); + netcp->tx_pause_threshold = MAX_SKB_FRAGS; + netcp->tx_resume_threshold = netcp->tx_pause_threshold; + netcp->node_interface = node_interface; + + ret = of_property_read_u32(node_interface, "efuse-mac", &efuse_mac); + if (efuse_mac) { + if (of_address_to_resource(node, NETCP_EFUSE_REG_INDEX, &res)) { + dev_err(dev, "could not find efuse-mac reg resource\n"); + ret = -ENODEV; + goto quit; + } + size = resource_size(&res); + + if (!devm_request_mem_region(dev, res.start, size, + dev_name(dev))) { + dev_err(dev, "could not reserve resource\n"); + ret = -ENOMEM; + goto quit; + } + + efuse = devm_ioremap_nocache(dev, res.start, size); + if (!efuse) { + dev_err(dev, "could not map resource\n"); + devm_release_mem_region(dev, res.start, size); + ret = -ENOMEM; + goto quit; + } + + emac_arch_get_mac_addr(efuse_mac_addr, efuse); + if (is_valid_ether_addr(efuse_mac_addr)) + ether_addr_copy(ndev->dev_addr, efuse_mac_addr); + else + random_ether_addr(ndev->dev_addr); + + devm_iounmap(dev, efuse); + devm_release_mem_region(dev, res.start, size); + } else { + mac_addr = of_get_mac_address(node_interface); + if (mac_addr) + ether_addr_copy(ndev->dev_addr, mac_addr); + else + random_ether_addr(ndev->dev_addr); + } + + ret = of_property_read_string(node_interface, "rx-channel", + &netcp->dma_chan_name); + if (ret < 0) { + dev_err(dev, "missing \"rx-channel\" parameter\n"); + ret = -ENODEV; + goto quit; + } + + ret = of_property_read_u32(node_interface, "rx-queue", + &netcp->rx_queue_id); + if (ret < 0) { + dev_warn(dev, "missing \"rx-queue\" parameter\n"); + netcp->rx_queue_id = KNAV_QUEUE_QPEND; + } + + ret = of_property_read_u32_array(node_interface, "rx-queue-depth", + netcp->rx_queue_depths, + KNAV_DMA_FDQ_PER_CHAN); + if (ret < 0) { + dev_err(dev, "missing \"rx-queue-depth\" parameter\n"); + netcp->rx_queue_depths[0] = 128; + } + + ret = of_property_read_u32_array(node_interface, "rx-buffer-size", + netcp->rx_buffer_sizes, + KNAV_DMA_FDQ_PER_CHAN); + if (ret) { + dev_err(dev, "missing \"rx-buffer-size\" parameter\n"); + netcp->rx_buffer_sizes[0] = 1536; + } + + ret = of_property_read_u32_array(node_interface, "rx-pool", temp, 2); + if (ret < 0) { + dev_err(dev, "missing \"rx-pool\" parameter\n"); + ret = -ENODEV; + goto quit; + } + netcp->rx_pool_size = temp[0]; + netcp->rx_pool_region_id = temp[1]; + + ret = of_property_read_u32_array(node_interface, "tx-pool", temp, 2); + if (ret < 0) { + dev_err(dev, "missing \"tx-pool\" parameter\n"); + ret = -ENODEV; + goto quit; + } + netcp->tx_pool_size = temp[0]; + netcp->tx_pool_region_id = temp[1]; + + if (netcp->tx_pool_size < MAX_SKB_FRAGS) { + dev_err(dev, "tx-pool size too small, must be atleast(%ld)\n", + MAX_SKB_FRAGS); + ret = -ENODEV; + goto quit; + } + + ret = of_property_read_u32(node_interface, "tx-completion-queue", + &netcp->tx_compl_qid); + if (ret < 0) { + dev_warn(dev, "missing \"tx-completion-queue\" parameter\n"); + netcp->tx_compl_qid = KNAV_QUEUE_QPEND; + } + + /* NAPI register */ + netif_napi_add(ndev, &netcp->rx_napi, netcp_rx_poll, NETCP_NAPI_WEIGHT); + netif_napi_add(ndev, &netcp->tx_napi, netcp_tx_poll, NETCP_NAPI_WEIGHT); + + /* Register the network device */ + ndev->dev_id = 0; + ndev->watchdog_timeo = NETCP_TX_TIMEOUT; + ndev->netdev_ops = &netcp_netdev_ops; + SET_NETDEV_DEV(ndev, dev); + + list_add_tail(&netcp->interface_list, &netcp_device->interface_head); + return 0; + +quit: + free_netdev(ndev); + return ret; +} + +static void netcp_delete_interface(struct netcp_device *netcp_device, + struct net_device *ndev) +{ + struct netcp_intf_modpriv *intf_modpriv, *tmp; + struct netcp_intf *netcp = netdev_priv(ndev); + struct netcp_module *module; + + dev_dbg(netcp_device->device, "Removing interface \"%s\"\n", + ndev->name); + + /* Notify each of the modules that the interface is going away */ + list_for_each_entry_safe(intf_modpriv, tmp, &netcp->module_head, + intf_list) { + module = intf_modpriv->netcp_module; + dev_dbg(netcp_device->device, "Releasing module \"%s\"\n", + module->name); + if (module->release) + module->release(intf_modpriv->module_priv); + list_del(&intf_modpriv->intf_list); + kfree(intf_modpriv); + } + WARN(!list_empty(&netcp->module_head), "%s interface module list is not empty!\n", + ndev->name); + + list_del(&netcp->interface_list); + + of_node_put(netcp->node_interface); + unregister_netdev(ndev); + netif_napi_del(&netcp->rx_napi); + free_netdev(ndev); +} + +static int netcp_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct netcp_intf *netcp_intf, *netcp_tmp; + struct device_node *child, *interfaces; + struct netcp_device *netcp_device; + struct device *dev = &pdev->dev; + struct netcp_module *module; + int ret; + + if (!node) { + dev_err(dev, "could not find device info\n"); + return -ENODEV; + } + + /* Allocate a new NETCP device instance */ + netcp_device = devm_kzalloc(dev, sizeof(*netcp_device), GFP_KERNEL); + if (!netcp_device) + return -ENOMEM; + + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) { + dev_err(dev, "Failed to enable NETCP power-domain\n"); + pm_runtime_disable(&pdev->dev); + return ret; + } + + /* Initialize the NETCP device instance */ + INIT_LIST_HEAD(&netcp_device->interface_head); + INIT_LIST_HEAD(&netcp_device->modpriv_head); + netcp_device->device = dev; + platform_set_drvdata(pdev, netcp_device); + + /* create interfaces */ + interfaces = of_get_child_by_name(node, "netcp-interfaces"); + if (!interfaces) { + dev_err(dev, "could not find netcp-interfaces node\n"); + ret = -ENODEV; + goto probe_quit; + } + + for_each_available_child_of_node(interfaces, child) { + ret = netcp_create_interface(netcp_device, child); + if (ret) { + dev_err(dev, "could not create interface(%s)\n", + child->name); + goto probe_quit_interface; + } + } + + /* 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: + list_for_each_entry_safe(netcp_intf, netcp_tmp, + &netcp_device->interface_head, + interface_list) { + netcp_delete_interface(netcp_device, netcp_intf->ndev); + } + +probe_quit: + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + platform_set_drvdata(pdev, NULL); + return ret; +} + +static int netcp_remove(struct platform_device *pdev) +{ + struct netcp_device *netcp_device = platform_get_drvdata(pdev); + struct netcp_inst_modpriv *inst_modpriv, *tmp; + struct netcp_module *module; + + list_for_each_entry_safe(inst_modpriv, tmp, &netcp_device->modpriv_head, + inst_list) { + module = inst_modpriv->netcp_module; + dev_dbg(&pdev->dev, "Removing module \"%s\"\n", module->name); + module->remove(netcp_device, inst_modpriv->module_priv); + list_del(&inst_modpriv->inst_list); + kfree(inst_modpriv); + } + WARN(!list_empty(&netcp_device->interface_head), "%s interface list not empty!\n", + pdev->name); + + devm_kfree(&pdev->dev, netcp_device); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct of_device_id of_match[] = { + { .compatible = "ti,netcp-1.0", }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_match); + +static struct platform_driver netcp_driver = { + .driver = { + .name = "netcp-1.0", + .owner = THIS_MODULE, + .of_match_table = of_match, + }, + .probe = netcp_probe, + .remove = netcp_remove, +}; +module_platform_driver(netcp_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TI NETCP driver for Keystone SOCs"); +MODULE_AUTHOR("Sandeep Nair <sandeep_n@ti.com"); diff --git a/drivers/net/ethernet/ti/netcp_ethss.c b/drivers/net/ethernet/ti/netcp_ethss.c new file mode 100644 index 000000000000..84f5ce525750 --- /dev/null +++ b/drivers/net/ethernet/ti/netcp_ethss.c @@ -0,0 +1,2159 @@ +/* + * Keystone GBE and XGBE subsystem code + * + * Copyright (C) 2014 Texas Instruments Incorporated + * Authors: Sandeep Nair <sandeep_n@ti.com> + * Sandeep Paulraj <s-paulraj@ti.com> + * Cyril Chemparathy <cyril@ti.com> + * Santosh Shilimkar <santosh.shilimkar@ti.com> + * Wingman Kwok <w-kwok2@ti.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 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/io.h> +#include <linux/module.h> +#include <linux/of_mdio.h> +#include <linux/of_address.h> +#include <linux/if_vlan.h> +#include <linux/ethtool.h> + +#include "cpsw_ale.h" +#include "netcp.h" + +#define NETCP_DRIVER_NAME "TI KeyStone Ethernet Driver" +#define NETCP_DRIVER_VERSION "v1.0" + +#define GBE_IDENT(reg) ((reg >> 16) & 0xffff) +#define GBE_MAJOR_VERSION(reg) (reg >> 8 & 0x7) +#define GBE_MINOR_VERSION(reg) (reg & 0xff) +#define GBE_RTL_VERSION(reg) ((reg >> 11) & 0x1f) + +/* 1G Ethernet SS defines */ +#define GBE_MODULE_NAME "netcp-gbe" +#define GBE_SS_VERSION_14 0x4ed21104 + +#define GBE13_SGMII_MODULE_OFFSET 0x100 +#define GBE13_SGMII34_MODULE_OFFSET 0x400 +#define GBE13_SWITCH_MODULE_OFFSET 0x800 +#define GBE13_HOST_PORT_OFFSET 0x834 +#define GBE13_SLAVE_PORT_OFFSET 0x860 +#define GBE13_EMAC_OFFSET 0x900 +#define GBE13_SLAVE_PORT2_OFFSET 0xa00 +#define GBE13_HW_STATS_OFFSET 0xb00 +#define GBE13_ALE_OFFSET 0xe00 +#define GBE13_HOST_PORT_NUM 0 +#define GBE13_NUM_SLAVES 4 +#define GBE13_NUM_ALE_PORTS (GBE13_NUM_SLAVES + 1) +#define GBE13_NUM_ALE_ENTRIES 1024 + +/* 10G Ethernet SS defines */ +#define XGBE_MODULE_NAME "netcp-xgbe" +#define XGBE_SS_VERSION_10 0x4ee42100 + +#define XGBE_SERDES_REG_INDEX 1 +#define XGBE10_SGMII_MODULE_OFFSET 0x100 +#define XGBE10_SWITCH_MODULE_OFFSET 0x1000 +#define XGBE10_HOST_PORT_OFFSET 0x1034 +#define XGBE10_SLAVE_PORT_OFFSET 0x1064 +#define XGBE10_EMAC_OFFSET 0x1400 +#define XGBE10_ALE_OFFSET 0x1700 +#define XGBE10_HW_STATS_OFFSET 0x1800 +#define XGBE10_HOST_PORT_NUM 0 +#define XGBE10_NUM_SLAVES 2 +#define XGBE10_NUM_ALE_PORTS (XGBE10_NUM_SLAVES + 1) +#define XGBE10_NUM_ALE_ENTRIES 1024 + +#define GBE_TIMER_INTERVAL (HZ / 2) + +/* Soft reset register values */ +#define SOFT_RESET_MASK BIT(0) +#define SOFT_RESET BIT(0) +#define DEVICE_EMACSL_RESET_POLL_COUNT 100 +#define GMACSL_RET_WARN_RESET_INCOMPLETE -2 + +#define MACSL_RX_ENABLE_CSF BIT(23) +#define MACSL_ENABLE_EXT_CTL BIT(18) +#define MACSL_XGMII_ENABLE BIT(13) +#define MACSL_XGIG_MODE BIT(8) +#define MACSL_GIG_MODE BIT(7) +#define MACSL_GMII_ENABLE BIT(5) +#define MACSL_FULLDUPLEX BIT(0) + +#define GBE_CTL_P0_ENABLE BIT(2) +#define GBE_REG_VAL_STAT_ENABLE_ALL 0xff +#define XGBE_REG_VAL_STAT_ENABLE_ALL 0xf +#define GBE_STATS_CD_SEL BIT(28) + +#define GBE_PORT_MASK(x) (BIT(x) - 1) +#define GBE_MASK_NO_PORTS 0 + +#define GBE_DEF_1G_MAC_CONTROL \ + (MACSL_GIG_MODE | MACSL_GMII_ENABLE | \ + MACSL_ENABLE_EXT_CTL | MACSL_RX_ENABLE_CSF) + +#define GBE_DEF_10G_MAC_CONTROL \ + (MACSL_XGIG_MODE | MACSL_XGMII_ENABLE | \ + MACSL_ENABLE_EXT_CTL | MACSL_RX_ENABLE_CSF) + +#define GBE_STATSA_MODULE 0 +#define GBE_STATSB_MODULE 1 +#define GBE_STATSC_MODULE 2 +#define GBE_STATSD_MODULE 3 + +#define XGBE_STATS0_MODULE 0 +#define XGBE_STATS1_MODULE 1 +#define XGBE_STATS2_MODULE 2 + +#define MAX_SLAVES GBE13_NUM_SLAVES +/* s: 0-based slave_port */ +#define SGMII_BASE(s) \ + (((s) < 2) ? gbe_dev->sgmii_port_regs : gbe_dev->sgmii_port34_regs) + +#define GBE_TX_QUEUE 648 +#define GBE_TXHOOK_ORDER 0 +#define GBE_DEFAULT_ALE_AGEOUT 30 +#define SLAVE_LINK_IS_XGMII(s) ((s)->link_interface >= XGMII_LINK_MAC_PHY) +#define NETCP_LINK_STATE_INVALID -1 + +#define GBE_SET_REG_OFS(p, rb, rn) p->rb##_ofs.rn = \ + offsetof(struct gbe##_##rb, rn) +#define XGBE_SET_REG_OFS(p, rb, rn) p->rb##_ofs.rn = \ + offsetof(struct xgbe##_##rb, rn) +#define GBE_REG_ADDR(p, rb, rn) (p->rb + p->rb##_ofs.rn) + +struct xgbe_ss_regs { + u32 id_ver; + u32 synce_count; + u32 synce_mux; + u32 control; +}; + +struct xgbe_switch_regs { + u32 id_ver; + u32 control; + u32 emcontrol; + u32 stat_port_en; + u32 ptype; + u32 soft_idle; + u32 thru_rate; + u32 gap_thresh; + u32 tx_start_wds; + u32 flow_control; + u32 cppi_thresh; +}; + +struct xgbe_port_regs { + u32 blk_cnt; + u32 port_vlan; + u32 tx_pri_map; + u32 sa_lo; + u32 sa_hi; + u32 ts_ctl; + u32 ts_seq_ltype; + u32 ts_vlan; + u32 ts_ctl_ltype2; + u32 ts_ctl2; + u32 control; +}; + +struct xgbe_host_port_regs { + u32 blk_cnt; + u32 port_vlan; + u32 tx_pri_map; + u32 src_id; + u32 rx_pri_map; + u32 rx_maxlen; +}; + +struct xgbe_emac_regs { + u32 id_ver; + u32 mac_control; + u32 mac_status; + u32 soft_reset; + u32 rx_maxlen; + u32 __reserved_0; + u32 rx_pause; + u32 tx_pause; + u32 em_control; + u32 __reserved_1; + u32 tx_gap; + u32 rsvd[4]; +}; + +struct xgbe_host_hw_stats { + u32 rx_good_frames; + u32 rx_broadcast_frames; + u32 rx_multicast_frames; + u32 __rsvd_0[3]; + u32 rx_oversized_frames; + u32 __rsvd_1; + u32 rx_undersized_frames; + u32 __rsvd_2; + u32 overrun_type4; + u32 overrun_type5; + u32 rx_bytes; + u32 tx_good_frames; + u32 tx_broadcast_frames; + u32 tx_multicast_frames; + u32 __rsvd_3[9]; + u32 tx_bytes; + u32 tx_64byte_frames; + u32 tx_65_to_127byte_frames; + u32 tx_128_to_255byte_frames; + u32 tx_256_to_511byte_frames; + u32 tx_512_to_1023byte_frames; + u32 tx_1024byte_frames; + u32 net_bytes; + u32 rx_sof_overruns; + u32 rx_mof_overruns; + u32 rx_dma_overruns; +}; + +struct xgbe_hw_stats { + u32 rx_good_frames; + u32 rx_broadcast_frames; + u32 rx_multicast_frames; + u32 rx_pause_frames; + u32 rx_crc_errors; + u32 rx_align_code_errors; + u32 rx_oversized_frames; + u32 rx_jabber_frames; + u32 rx_undersized_frames; + u32 rx_fragments; + u32 overrun_type4; + u32 overrun_type5; + u32 rx_bytes; + u32 tx_good_frames; + u32 tx_broadcast_frames; + u32 tx_multicast_frames; + u32 tx_pause_frames; + u32 tx_deferred_frames; + u32 tx_collision_frames; + u32 tx_single_coll_frames; + u32 tx_mult_coll_frames; + u32 tx_excessive_collisions; + u32 tx_late_collisions; + u32 tx_underrun; + u32 tx_carrier_sense_errors; + u32 tx_bytes; + u32 tx_64byte_frames; + u32 tx_65_to_127byte_frames; + u32 tx_128_to_255byte_frames; + u32 tx_256_to_511byte_frames; + u32 tx_512_to_1023byte_frames; + u32 tx_1024byte_frames; + u32 net_bytes; + u32 rx_sof_overruns; + u32 rx_mof_overruns; + u32 rx_dma_overruns; +}; + +#define XGBE10_NUM_STAT_ENTRIES (sizeof(struct xgbe_hw_stats)/sizeof(u32)) + +struct gbe_ss_regs { + u32 id_ver; + u32 synce_count; + u32 synce_mux; +}; + +struct gbe_ss_regs_ofs { + u16 id_ver; + u16 control; +}; + +struct gbe_switch_regs { + u32 id_ver; + u32 control; + u32 soft_reset; + u32 stat_port_en; + u32 ptype; + u32 soft_idle; + u32 thru_rate; + u32 gap_thresh; + u32 tx_start_wds; + u32 flow_control; +}; + +struct gbe_switch_regs_ofs { + u16 id_ver; + u16 control; + u16 soft_reset; + u16 emcontrol; + u16 stat_port_en; + u16 ptype; + u16 flow_control; +}; + +struct gbe_port_regs { + u32 max_blks; + u32 blk_cnt; + u32 port_vlan; + u32 tx_pri_map; + u32 sa_lo; + u32 sa_hi; + u32 ts_ctl; + u32 ts_seq_ltype; + u32 ts_vlan; + u32 ts_ctl_ltype2; + u32 ts_ctl2; +}; + +struct gbe_port_regs_ofs { + u16 port_vlan; + u16 tx_pri_map; + u16 sa_lo; + u16 sa_hi; + u16 ts_ctl; + u16 ts_seq_ltype; + u16 ts_vlan; + u16 ts_ctl_ltype2; + u16 ts_ctl2; +}; + +struct gbe_host_port_regs { + u32 src_id; + u32 port_vlan; + u32 rx_pri_map; + u32 rx_maxlen; +}; + +struct gbe_host_port_regs_ofs { + u16 port_vlan; + u16 tx_pri_map; + u16 rx_maxlen; +}; + +struct gbe_emac_regs { + u32 id_ver; + u32 mac_control; + u32 mac_status; + u32 soft_reset; + u32 rx_maxlen; + u32 __reserved_0; + u32 rx_pause; + u32 tx_pause; + u32 __reserved_1; + u32 rx_pri_map; + u32 rsvd[6]; +}; + +struct gbe_emac_regs_ofs { + u16 mac_control; + u16 soft_reset; + u16 rx_maxlen; +}; + +struct gbe_hw_stats { + u32 rx_good_frames; + u32 rx_broadcast_frames; + u32 rx_multicast_frames; + u32 rx_pause_frames; + u32 rx_crc_errors; + u32 rx_align_code_errors; + u32 rx_oversized_frames; + u32 rx_jabber_frames; + u32 rx_undersized_frames; + u32 rx_fragments; + u32 __pad_0[2]; + u32 rx_bytes; + u32 tx_good_frames; + u32 tx_broadcast_frames; + u32 tx_multicast_frames; + u32 tx_pause_frames; + u32 tx_deferred_frames; + u32 tx_collision_frames; + u32 tx_single_coll_frames; + u32 tx_mult_coll_frames; + u32 tx_excessive_collisions; + u32 tx_late_collisions; + u32 tx_underrun; + u32 tx_carrier_sense_errors; + u32 tx_bytes; + u32 tx_64byte_frames; + u32 tx_65_to_127byte_frames; + u32 tx_128_to_255byte_frames; + u32 tx_256_to_511byte_frames; + u32 tx_512_to_1023byte_frames; + u32 tx_1024byte_frames; + u32 net_bytes; + u32 rx_sof_overruns; + u32 rx_mof_overruns; + u32 rx_dma_overruns; +}; + +#define GBE13_NUM_HW_STAT_ENTRIES (sizeof(struct gbe_hw_stats)/sizeof(u32)) +#define GBE13_NUM_HW_STATS_MOD 2 +#define XGBE10_NUM_HW_STATS_MOD 3 +#define GBE_MAX_HW_STAT_MODS 3 +#define GBE_HW_STATS_REG_MAP_SZ 0x100 + +struct gbe_slave { + void __iomem *port_regs; + void __iomem *emac_regs; + struct gbe_port_regs_ofs port_regs_ofs; + struct gbe_emac_regs_ofs emac_regs_ofs; + int slave_num; /* 0 based logical number */ + int port_num; /* actual port number */ + atomic_t link_state; + bool open; + struct phy_device *phy; + u32 link_interface; + u32 mac_control; + u8 phy_port_t; + struct device_node *phy_node; + struct list_head slave_list; +}; + +struct gbe_priv { + struct device *dev; + struct netcp_device *netcp_device; + struct timer_list timer; + u32 num_slaves; + u32 ale_entries; + u32 ale_ports; + bool enable_ale; + struct netcp_tx_pipe tx_pipe; + + int host_port; + u32 rx_packet_max; + u32 ss_version; + + void __iomem *ss_regs; + void __iomem *switch_regs; + void __iomem *host_port_regs; + void __iomem *ale_reg; + void __iomem *sgmii_port_regs; + void __iomem *sgmii_port34_regs; + void __iomem *xgbe_serdes_regs; + void __iomem *hw_stats_regs[GBE_MAX_HW_STAT_MODS]; + + struct gbe_ss_regs_ofs ss_regs_ofs; + struct gbe_switch_regs_ofs switch_regs_ofs; + struct gbe_host_port_regs_ofs host_port_regs_ofs; + + struct cpsw_ale *ale; + unsigned int tx_queue_id; + const char *dma_chan_name; + + struct list_head gbe_intf_head; + struct list_head secondary_slaves; + struct net_device *dummy_ndev; + + u64 *hw_stats; + const struct netcp_ethtool_stat *et_stats; + int num_et_stats; + /* Lock for updating the hwstats */ + spinlock_t hw_stats_lock; +}; + +struct gbe_intf { + struct net_device *ndev; + struct device *dev; + struct gbe_priv *gbe_dev; + struct netcp_tx_pipe tx_pipe; + struct gbe_slave *slave; + struct list_head gbe_intf_list; + unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; +}; + +static struct netcp_module gbe_module; +static struct netcp_module xgbe_module; + +/* Statistic management */ +struct netcp_ethtool_stat { + char desc[ETH_GSTRING_LEN]; + int type; + u32 size; + int offset; +}; + +#define GBE_STATSA_INFO(field) "GBE_A:"#field, GBE_STATSA_MODULE,\ + FIELD_SIZEOF(struct gbe_hw_stats, field), \ + offsetof(struct gbe_hw_stats, field) + +#define GBE_STATSB_INFO(field) "GBE_B:"#field, GBE_STATSB_MODULE,\ + FIELD_SIZEOF(struct gbe_hw_stats, field), \ + offsetof(struct gbe_hw_stats, field) + +#define GBE_STATSC_INFO(field) "GBE_C:"#field, GBE_STATSC_MODULE,\ + FIELD_SIZEOF(struct gbe_hw_stats, field), \ + offsetof(struct gbe_hw_stats, field) + +#define GBE_STATSD_INFO(field) "GBE_D:"#field, GBE_STATSD_MODULE,\ + FIELD_SIZEOF(struct gbe_hw_stats, field), \ + offsetof(struct gbe_hw_stats, field) + +static const struct netcp_ethtool_stat gbe13_et_stats[] = { + /* GBE module A */ + {GBE_STATSA_INFO(rx_good_frames)}, + {GBE_STATSA_INFO(rx_broadcast_frames)}, + {GBE_STATSA_INFO(rx_multicast_frames)}, + {GBE_STATSA_INFO(rx_pause_frames)}, + {GBE_STATSA_INFO(rx_crc_errors)}, + {GBE_STATSA_INFO(rx_align_code_errors)}, + {GBE_STATSA_INFO(rx_oversized_frames)}, + {GBE_STATSA_INFO(rx_jabber_frames)}, + {GBE_STATSA_INFO(rx_undersized_frames)}, + {GBE_STATSA_INFO(rx_fragments)}, + {GBE_STATSA_INFO(rx_bytes)}, + {GBE_STATSA_INFO(tx_good_frames)}, + {GBE_STATSA_INFO(tx_broadcast_frames)}, + {GBE_STATSA_INFO(tx_multicast_frames)}, + {GBE_STATSA_INFO(tx_pause_frames)}, + {GBE_STATSA_INFO(tx_deferred_frames)}, + {GBE_STATSA_INFO(tx_collision_frames)}, + {GBE_STATSA_INFO(tx_single_coll_frames)}, + {GBE_STATSA_INFO(tx_mult_coll_frames)}, + {GBE_STATSA_INFO(tx_excessive_collisions)}, + {GBE_STATSA_INFO(tx_late_collisions)}, + {GBE_STATSA_INFO(tx_underrun)}, + {GBE_STATSA_INFO(tx_carrier_sense_errors)}, + {GBE_STATSA_INFO(tx_bytes)}, + {GBE_STATSA_INFO(tx_64byte_frames)}, + {GBE_STATSA_INFO(tx_65_to_127byte_frames)}, + {GBE_STATSA_INFO(tx_128_to_255byte_frames)}, + {GBE_STATSA_INFO(tx_256_to_511byte_frames)}, + {GBE_STATSA_INFO(tx_512_to_1023byte_frames)}, + {GBE_STATSA_INFO(tx_1024byte_frames)}, + {GBE_STATSA_INFO(net_bytes)}, + {GBE_STATSA_INFO(rx_sof_overruns)}, + {GBE_STATSA_INFO(rx_mof_overruns)}, + {GBE_STATSA_INFO(rx_dma_overruns)}, + /* GBE module B */ + {GBE_STATSB_INFO(rx_good_frames)}, + {GBE_STATSB_INFO(rx_broadcast_frames)}, + {GBE_STATSB_INFO(rx_multicast_frames)}, + {GBE_STATSB_INFO(rx_pause_frames)}, + {GBE_STATSB_INFO(rx_crc_errors)}, + {GBE_STATSB_INFO(rx_align_code_errors)}, + {GBE_STATSB_INFO(rx_oversized_frames)}, + {GBE_STATSB_INFO(rx_jabber_frames)}, + {GBE_STATSB_INFO(rx_undersized_frames)}, + {GBE_STATSB_INFO(rx_fragments)}, + {GBE_STATSB_INFO(rx_bytes)}, + {GBE_STATSB_INFO(tx_good_frames)}, + {GBE_STATSB_INFO(tx_broadcast_frames)}, + {GBE_STATSB_INFO(tx_multicast_frames)}, + {GBE_STATSB_INFO(tx_pause_frames)}, + {GBE_STATSB_INFO(tx_deferred_frames)}, + {GBE_STATSB_INFO(tx_collision_frames)}, + {GBE_STATSB_INFO(tx_single_coll_frames)}, + {GBE_STATSB_INFO(tx_mult_coll_frames)}, + {GBE_STATSB_INFO(tx_excessive_collisions)}, + {GBE_STATSB_INFO(tx_late_collisions)}, + {GBE_STATSB_INFO(tx_underrun)}, + {GBE_STATSB_INFO(tx_carrier_sense_errors)}, + {GBE_STATSB_INFO(tx_bytes)}, + {GBE_STATSB_INFO(tx_64byte_frames)}, + {GBE_STATSB_INFO(tx_65_to_127byte_frames)}, + {GBE_STATSB_INFO(tx_128_to_255byte_frames)}, + {GBE_STATSB_INFO(tx_256_to_511byte_frames)}, + {GBE_STATSB_INFO(tx_512_to_1023byte_frames)}, + {GBE_STATSB_INFO(tx_1024byte_frames)}, + {GBE_STATSB_INFO(net_bytes)}, + {GBE_STATSB_INFO(rx_sof_overruns)}, + {GBE_STATSB_INFO(rx_mof_overruns)}, + {GBE_STATSB_INFO(rx_dma_overruns)}, + /* GBE module C */ + {GBE_STATSC_INFO(rx_good_frames)}, + {GBE_STATSC_INFO(rx_broadcast_frames)}, + {GBE_STATSC_INFO(rx_multicast_frames)}, + {GBE_STATSC_INFO(rx_pause_frames)}, + {GBE_STATSC_INFO(rx_crc_errors)}, + {GBE_STATSC_INFO(rx_align_code_errors)}, + {GBE_STATSC_INFO(rx_oversized_frames)}, + {GBE_STATSC_INFO(rx_jabber_frames)}, + {GBE_STATSC_INFO(rx_undersized_frames)}, + {GBE_STATSC_INFO(rx_fragments)}, + {GBE_STATSC_INFO(rx_bytes)}, + {GBE_STATSC_INFO(tx_good_frames)}, + {GBE_STATSC_INFO(tx_broadcast_frames)}, + {GBE_STATSC_INFO(tx_multicast_frames)}, + {GBE_STATSC_INFO(tx_pause_frames)}, + {GBE_STATSC_INFO(tx_deferred_frames)}, + {GBE_STATSC_INFO(tx_collision_frames)}, + {GBE_STATSC_INFO(tx_single_coll_frames)}, + {GBE_STATSC_INFO(tx_mult_coll_frames)}, + {GBE_STATSC_INFO(tx_excessive_collisions)}, + {GBE_STATSC_INFO(tx_late_collisions)}, + {GBE_STATSC_INFO(tx_underrun)}, + {GBE_STATSC_INFO(tx_carrier_sense_errors)}, + {GBE_STATSC_INFO(tx_bytes)}, + {GBE_STATSC_INFO(tx_64byte_frames)}, + {GBE_STATSC_INFO(tx_65_to_127byte_frames)}, + {GBE_STATSC_INFO(tx_128_to_255byte_frames)}, + {GBE_STATSC_INFO(tx_256_to_511byte_frames)}, + {GBE_STATSC_INFO(tx_512_to_1023byte_frames)}, + {GBE_STATSC_INFO(tx_1024byte_frames)}, + {GBE_STATSC_INFO(net_bytes)}, + {GBE_STATSC_INFO(rx_sof_overruns)}, + {GBE_STATSC_INFO(rx_mof_overruns)}, + {GBE_STATSC_INFO(rx_dma_overruns)}, + /* GBE module D */ + {GBE_STATSD_INFO(rx_good_frames)}, + {GBE_STATSD_INFO(rx_broadcast_frames)}, + {GBE_STATSD_INFO(rx_multicast_frames)}, + {GBE_STATSD_INFO(rx_pause_frames)}, + {GBE_STATSD_INFO(rx_crc_errors)}, + {GBE_STATSD_INFO(rx_align_code_errors)}, + {GBE_STATSD_INFO(rx_oversized_frames)}, + {GBE_STATSD_INFO(rx_jabber_frames)}, + {GBE_STATSD_INFO(rx_undersized_frames)}, + {GBE_STATSD_INFO(rx_fragments)}, + {GBE_STATSD_INFO(rx_bytes)}, + {GBE_STATSD_INFO(tx_good_frames)}, + {GBE_STATSD_INFO(tx_broadcast_frames)}, + {GBE_STATSD_INFO(tx_multicast_frames)}, + {GBE_STATSD_INFO(tx_pause_frames)}, + {GBE_STATSD_INFO(tx_deferred_frames)}, + {GBE_STATSD_INFO(tx_collision_frames)}, + {GBE_STATSD_INFO(tx_single_coll_frames)}, + {GBE_STATSD_INFO(tx_mult_coll_frames)}, + {GBE_STATSD_INFO(tx_excessive_collisions)}, + {GBE_STATSD_INFO(tx_late_collisions)}, + {GBE_STATSD_INFO(tx_underrun)}, + {GBE_STATSD_INFO(tx_carrier_sense_errors)}, + {GBE_STATSD_INFO(tx_bytes)}, + {GBE_STATSD_INFO(tx_64byte_frames)}, + {GBE_STATSD_INFO(tx_65_to_127byte_frames)}, + {GBE_STATSD_INFO(tx_128_to_255byte_frames)}, + {GBE_STATSD_INFO(tx_256_to_511byte_frames)}, + {GBE_STATSD_INFO(tx_512_to_1023byte_frames)}, + {GBE_STATSD_INFO(tx_1024byte_frames)}, + {GBE_STATSD_INFO(net_bytes)}, + {GBE_STATSD_INFO(rx_sof_overruns)}, + {GBE_STATSD_INFO(rx_mof_overruns)}, + {GBE_STATSD_INFO(rx_dma_overruns)}, +}; + +#define XGBE_STATS0_INFO(field) "GBE_0:"#field, XGBE_STATS0_MODULE, \ + FIELD_SIZEOF(struct xgbe_hw_stats, field), \ + offsetof(struct xgbe_hw_stats, field) + +#define XGBE_STATS1_INFO(field) "GBE_1:"#field, XGBE_STATS1_MODULE, \ + FIELD_SIZEOF(struct xgbe_hw_stats, field), \ + offsetof(struct xgbe_hw_stats, field) + +#define XGBE_STATS2_INFO(field) "GBE_2:"#field, XGBE_STATS2_MODULE, \ + FIELD_SIZEOF(struct xgbe_hw_stats, field), \ + offsetof(struct xgbe_hw_stats, field) + +static const struct netcp_ethtool_stat xgbe10_et_stats[] = { + /* GBE module 0 */ + {XGBE_STATS0_INFO(rx_good_frames)}, + {XGBE_STATS0_INFO(rx_broadcast_frames)}, + {XGBE_STATS0_INFO(rx_multicast_frames)}, + {XGBE_STATS0_INFO(rx_oversized_frames)}, + {XGBE_STATS0_INFO(rx_undersized_frames)}, + {XGBE_STATS0_INFO(overrun_type4)}, + {XGBE_STATS0_INFO(overrun_type5)}, + {XGBE_STATS0_INFO(rx_bytes)}, + {XGBE_STATS0_INFO(tx_good_frames)}, + {XGBE_STATS0_INFO(tx_broadcast_frames)}, + {XGBE_STATS0_INFO(tx_multicast_frames)}, + {XGBE_STATS0_INFO(tx_bytes)}, + {XGBE_STATS0_INFO(tx_64byte_frames)}, + {XGBE_STATS0_INFO(tx_65_to_127byte_frames)}, + {XGBE_STATS0_INFO(tx_128_to_255byte_frames)}, + {XGBE_STATS0_INFO(tx_256_to_511byte_frames)}, + {XGBE_STATS0_INFO(tx_512_to_1023byte_frames)}, + {XGBE_STATS0_INFO(tx_1024byte_frames)}, + {XGBE_STATS0_INFO(net_bytes)}, + {XGBE_STATS0_INFO(rx_sof_overruns)}, + {XGBE_STATS0_INFO(rx_mof_overruns)}, + {XGBE_STATS0_INFO(rx_dma_overruns)}, + /* XGBE module 1 */ + {XGBE_STATS1_INFO(rx_good_frames)}, + {XGBE_STATS1_INFO(rx_broadcast_frames)}, + {XGBE_STATS1_INFO(rx_multicast_frames)}, + {XGBE_STATS1_INFO(rx_pause_frames)}, + {XGBE_STATS1_INFO(rx_crc_errors)}, + {XGBE_STATS1_INFO(rx_align_code_errors)}, + {XGBE_STATS1_INFO(rx_oversized_frames)}, + {XGBE_STATS1_INFO(rx_jabber_frames)}, + {XGBE_STATS1_INFO(rx_undersized_frames)}, + {XGBE_STATS1_INFO(rx_fragments)}, + {XGBE_STATS1_INFO(overrun_type4)}, + {XGBE_STATS1_INFO(overrun_type5)}, + {XGBE_STATS1_INFO(rx_bytes)}, + {XGBE_STATS1_INFO(tx_good_frames)}, + {XGBE_STATS1_INFO(tx_broadcast_frames)}, + {XGBE_STATS1_INFO(tx_multicast_frames)}, + {XGBE_STATS1_INFO(tx_pause_frames)}, + {XGBE_STATS1_INFO(tx_deferred_frames)}, + {XGBE_STATS1_INFO(tx_collision_frames)}, + {XGBE_STATS1_INFO(tx_single_coll_frames)}, + {XGBE_STATS1_INFO(tx_mult_coll_frames)}, + {XGBE_STATS1_INFO(tx_excessive_collisions)}, + {XGBE_STATS1_INFO(tx_late_collisions)}, + {XGBE_STATS1_INFO(tx_underrun)}, + {XGBE_STATS1_INFO(tx_carrier_sense_errors)}, + {XGBE_STATS1_INFO(tx_bytes)}, + {XGBE_STATS1_INFO(tx_64byte_frames)}, + {XGBE_STATS1_INFO(tx_65_to_127byte_frames)}, + {XGBE_STATS1_INFO(tx_128_to_255byte_frames)}, + {XGBE_STATS1_INFO(tx_256_to_511byte_frames)}, + {XGBE_STATS1_INFO(tx_512_to_1023byte_frames)}, + {XGBE_STATS1_INFO(tx_1024byte_frames)}, + {XGBE_STATS1_INFO(net_bytes)}, + {XGBE_STATS1_INFO(rx_sof_overruns)}, + {XGBE_STATS1_INFO(rx_mof_overruns)}, + {XGBE_STATS1_INFO(rx_dma_overruns)}, + /* XGBE module 2 */ + {XGBE_STATS2_INFO(rx_good_frames)}, + {XGBE_STATS2_INFO(rx_broadcast_frames)}, + {XGBE_STATS2_INFO(rx_multicast_frames)}, + {XGBE_STATS2_INFO(rx_pause_frames)}, + {XGBE_STATS2_INFO(rx_crc_errors)}, + {XGBE_STATS2_INFO(rx_align_code_errors)}, + {XGBE_STATS2_INFO(rx_oversized_frames)}, + {XGBE_STATS2_INFO(rx_jabber_frames)}, + {XGBE_STATS2_INFO(rx_undersized_frames)}, + {XGBE_STATS2_INFO(rx_fragments)}, + {XGBE_STATS2_INFO(overrun_type4)}, + {XGBE_STATS2_INFO(overrun_type5)}, + {XGBE_STATS2_INFO(rx_bytes)}, + {XGBE_STATS2_INFO(tx_good_frames)}, + {XGBE_STATS2_INFO(tx_broadcast_frames)}, + {XGBE_STATS2_INFO(tx_multicast_frames)}, + {XGBE_STATS2_INFO(tx_pause_frames)}, + {XGBE_STATS2_INFO(tx_deferred_frames)}, + {XGBE_STATS2_INFO(tx_collision_frames)}, + {XGBE_STATS2_INFO(tx_single_coll_frames)}, + {XGBE_STATS2_INFO(tx_mult_coll_frames)}, + {XGBE_STATS2_INFO(tx_excessive_collisions)}, + {XGBE_STATS2_INFO(tx_late_collisions)}, + {XGBE_STATS2_INFO(tx_underrun)}, + {XGBE_STATS2_INFO(tx_carrier_sense_errors)}, + {XGBE_STATS2_INFO(tx_bytes)}, + {XGBE_STATS2_INFO(tx_64byte_frames)}, + {XGBE_STATS2_INFO(tx_65_to_127byte_frames)}, + {XGBE_STATS2_INFO(tx_128_to_255byte_frames)}, + {XGBE_STATS2_INFO(tx_256_to_511byte_frames)}, + {XGBE_STATS2_INFO(tx_512_to_1023byte_frames)}, + {XGBE_STATS2_INFO(tx_1024byte_frames)}, + {XGBE_STATS2_INFO(net_bytes)}, + {XGBE_STATS2_INFO(rx_sof_overruns)}, + {XGBE_STATS2_INFO(rx_mof_overruns)}, + {XGBE_STATS2_INFO(rx_dma_overruns)}, +}; + +#define for_each_intf(i, priv) \ + list_for_each_entry((i), &(priv)->gbe_intf_head, gbe_intf_list) + +#define for_each_sec_slave(slave, priv) \ + list_for_each_entry((slave), &(priv)->secondary_slaves, slave_list) + +#define first_sec_slave(priv) \ + list_first_entry(&priv->secondary_slaves, \ + struct gbe_slave, slave_list) + +static void keystone_get_drvinfo(struct net_device *ndev, + struct ethtool_drvinfo *info) +{ + strncpy(info->driver, NETCP_DRIVER_NAME, sizeof(info->driver)); + strncpy(info->version, NETCP_DRIVER_VERSION, sizeof(info->version)); +} + +static u32 keystone_get_msglevel(struct net_device *ndev) +{ + struct netcp_intf *netcp = netdev_priv(ndev); + + return netcp->msg_enable; +} + +static void keystone_set_msglevel(struct net_device *ndev, u32 value) +{ + struct netcp_intf *netcp = netdev_priv(ndev); + + netcp->msg_enable = value; +} + +static void keystone_get_stat_strings(struct net_device *ndev, + uint32_t stringset, uint8_t *data) +{ + struct netcp_intf *netcp = netdev_priv(ndev); + struct gbe_intf *gbe_intf; + struct gbe_priv *gbe_dev; + int i; + + gbe_intf = netcp_module_get_intf_data(&gbe_module, netcp); + if (!gbe_intf) + return; + gbe_dev = gbe_intf->gbe_dev; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < gbe_dev->num_et_stats; i++) { + memcpy(data, gbe_dev->et_stats[i].desc, + ETH_GSTRING_LEN); + data += ETH_GSTRING_LEN; + } + break; + case ETH_SS_TEST: + break; + } +} + +static int keystone_get_sset_count(struct net_device *ndev, int stringset) +{ + struct netcp_intf *netcp = netdev_priv(ndev); + struct gbe_intf *gbe_intf; + struct gbe_priv *gbe_dev; + + gbe_intf = netcp_module_get_intf_data(&gbe_module, netcp); + if (!gbe_intf) + return -EINVAL; + gbe_dev = gbe_intf->gbe_dev; + + switch (stringset) { + case ETH_SS_TEST: + return 0; + case ETH_SS_STATS: + return gbe_dev->num_et_stats; + default: + return -EINVAL; + } +} + +static void gbe_update_stats(struct gbe_priv *gbe_dev, uint64_t *data) +{ + void __iomem *base = NULL; + u32 __iomem *p; + u32 tmp = 0; + int i; + + for (i = 0; i < gbe_dev->num_et_stats; i++) { + base = gbe_dev->hw_stats_regs[gbe_dev->et_stats[i].type]; + p = base + gbe_dev->et_stats[i].offset; + tmp = readl(p); + gbe_dev->hw_stats[i] = gbe_dev->hw_stats[i] + tmp; + if (data) + data[i] = gbe_dev->hw_stats[i]; + /* write-to-decrement: + * new register value = old register value - write value + */ + writel(tmp, p); + } +} + +static void gbe_update_stats_ver14(struct gbe_priv *gbe_dev, uint64_t *data) +{ + void __iomem *gbe_statsa = gbe_dev->hw_stats_regs[0]; + void __iomem *gbe_statsb = gbe_dev->hw_stats_regs[1]; + u64 *hw_stats = &gbe_dev->hw_stats[0]; + void __iomem *base = NULL; + u32 __iomem *p; + u32 tmp = 0, val, pair_size = (gbe_dev->num_et_stats / 2); + int i, j, pair; + + for (pair = 0; pair < 2; pair++) { + val = readl(GBE_REG_ADDR(gbe_dev, switch_regs, stat_port_en)); + + if (pair == 0) + val &= ~GBE_STATS_CD_SEL; + else + val |= GBE_STATS_CD_SEL; + + /* make the stat modules visible */ + writel(val, GBE_REG_ADDR(gbe_dev, switch_regs, stat_port_en)); + + for (i = 0; i < pair_size; i++) { + j = pair * pair_size + i; + switch (gbe_dev->et_stats[j].type) { + case GBE_STATSA_MODULE: + case GBE_STATSC_MODULE: + base = gbe_statsa; + break; + case GBE_STATSB_MODULE: + case GBE_STATSD_MODULE: + base = gbe_statsb; + break; + } + + p = base + gbe_dev->et_stats[j].offset; + tmp = readl(p); + hw_stats[j] += tmp; + if (data) + data[j] = hw_stats[j]; + /* write-to-decrement: + * new register value = old register value - write value + */ + writel(tmp, p); + } + } +} + +static void keystone_get_ethtool_stats(struct net_device *ndev, + struct ethtool_stats *stats, + uint64_t *data) +{ + struct netcp_intf *netcp = netdev_priv(ndev); + struct gbe_intf *gbe_intf; + struct gbe_priv *gbe_dev; + + gbe_intf = netcp_module_get_intf_data(&gbe_module, netcp); + if (!gbe_intf) + return; + + gbe_dev = gbe_intf->gbe_dev; + spin_lock_bh(&gbe_dev->hw_stats_lock); + if (gbe_dev->ss_version == GBE_SS_VERSION_14) + gbe_update_stats_ver14(gbe_dev, data); + else + gbe_update_stats(gbe_dev, data); + spin_unlock_bh(&gbe_dev->hw_stats_lock); +} + +static int keystone_get_settings(struct net_device *ndev, + struct ethtool_cmd *cmd) +{ + struct netcp_intf *netcp = netdev_priv(ndev); + struct phy_device *phy = ndev->phydev; + struct gbe_intf *gbe_intf; + int ret; + + if (!phy) + return -EINVAL; + + gbe_intf = netcp_module_get_intf_data(&gbe_module, netcp); + if (!gbe_intf) + return -EINVAL; + + if (!gbe_intf->slave) + return -EINVAL; + + ret = phy_ethtool_gset(phy, cmd); + if (!ret) + cmd->port = gbe_intf->slave->phy_port_t; + + return ret; +} + +static int keystone_set_settings(struct net_device *ndev, + struct ethtool_cmd *cmd) +{ + struct netcp_intf *netcp = netdev_priv(ndev); + struct phy_device *phy = ndev->phydev; + struct gbe_intf *gbe_intf; + u32 features = cmd->advertising & cmd->supported; + + if (!phy) + return -EINVAL; + + gbe_intf = netcp_module_get_intf_data(&gbe_module, netcp); + if (!gbe_intf) + return -EINVAL; + + if (!gbe_intf->slave) + return -EINVAL; + + if (cmd->port != gbe_intf->slave->phy_port_t) { + if ((cmd->port == PORT_TP) && !(features & ADVERTISED_TP)) + return -EINVAL; + + if ((cmd->port == PORT_AUI) && !(features & ADVERTISED_AUI)) + return -EINVAL; + + if ((cmd->port == PORT_BNC) && !(features & ADVERTISED_BNC)) + return -EINVAL; + + if ((cmd->port == PORT_MII) && !(features & ADVERTISED_MII)) + return -EINVAL; + + if ((cmd->port == PORT_FIBRE) && !(features & ADVERTISED_FIBRE)) + return -EINVAL; + } + + gbe_intf->slave->phy_port_t = cmd->port; + return phy_ethtool_sset(phy, cmd); +} + +static const struct ethtool_ops keystone_ethtool_ops = { + .get_drvinfo = keystone_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_msglevel = keystone_get_msglevel, + .set_msglevel = keystone_set_msglevel, + .get_strings = keystone_get_stat_strings, + .get_sset_count = keystone_get_sset_count, + .get_ethtool_stats = keystone_get_ethtool_stats, + .get_settings = keystone_get_settings, + .set_settings = keystone_set_settings, +}; + +#define mac_hi(mac) (((mac)[0] << 0) | ((mac)[1] << 8) | \ + ((mac)[2] << 16) | ((mac)[3] << 24)) +#define mac_lo(mac) (((mac)[4] << 0) | ((mac)[5] << 8)) + +static void gbe_set_slave_mac(struct gbe_slave *slave, + struct gbe_intf *gbe_intf) +{ + struct net_device *ndev = gbe_intf->ndev; + + writel(mac_hi(ndev->dev_addr), GBE_REG_ADDR(slave, port_regs, sa_hi)); + writel(mac_lo(ndev->dev_addr), GBE_REG_ADDR(slave, port_regs, sa_lo)); +} + +static int gbe_get_slave_port(struct gbe_priv *priv, u32 slave_num) +{ + if (priv->host_port == 0) + return slave_num + 1; + + return slave_num; +} + +static void netcp_ethss_link_state_action(struct gbe_priv *gbe_dev, + struct net_device *ndev, + struct gbe_slave *slave, + int up) +{ + struct phy_device *phy = slave->phy; + u32 mac_control = 0; + + if (up) { + mac_control = slave->mac_control; + if (phy && (phy->speed == SPEED_1000)) { + mac_control |= MACSL_GIG_MODE; + mac_control &= ~MACSL_XGIG_MODE; + } else if (phy && (phy->speed == SPEED_10000)) { + mac_control |= MACSL_XGIG_MODE; + mac_control &= ~MACSL_GIG_MODE; + } + + writel(mac_control, GBE_REG_ADDR(slave, emac_regs, + mac_control)); + + cpsw_ale_control_set(gbe_dev->ale, slave->port_num, + ALE_PORT_STATE, + ALE_PORT_STATE_FORWARD); + + if (ndev && slave->open) + netif_carrier_on(ndev); + } else { + writel(mac_control, GBE_REG_ADDR(slave, emac_regs, + mac_control)); + cpsw_ale_control_set(gbe_dev->ale, slave->port_num, + ALE_PORT_STATE, + ALE_PORT_STATE_DISABLE); + if (ndev) + netif_carrier_off(ndev); + } + + if (phy) + phy_print_status(phy); +} + +static bool gbe_phy_link_status(struct gbe_slave *slave) +{ + return !slave->phy || slave->phy->link; +} + +static void netcp_ethss_update_link_state(struct gbe_priv *gbe_dev, + struct gbe_slave *slave, + struct net_device *ndev) +{ + int sp = slave->slave_num; + int phy_link_state, sgmii_link_state = 1, link_state; + + if (!slave->open) + return; + + if (!SLAVE_LINK_IS_XGMII(slave)) + sgmii_link_state = netcp_sgmii_get_port_link(SGMII_BASE(sp), + sp); + phy_link_state = gbe_phy_link_status(slave); + link_state = phy_link_state & sgmii_link_state; + + if (atomic_xchg(&slave->link_state, link_state) != link_state) + netcp_ethss_link_state_action(gbe_dev, ndev, slave, + link_state); +} + +static void xgbe_adjust_link(struct net_device *ndev) +{ + struct netcp_intf *netcp = netdev_priv(ndev); + struct gbe_intf *gbe_intf; + + gbe_intf = netcp_module_get_intf_data(&xgbe_module, netcp); + if (!gbe_intf) + return; + + netcp_ethss_update_link_state(gbe_intf->gbe_dev, gbe_intf->slave, + ndev); +} + +static void gbe_adjust_link(struct net_device *ndev) +{ + struct netcp_intf *netcp = netdev_priv(ndev); + struct gbe_intf *gbe_intf; + + gbe_intf = netcp_module_get_intf_data(&gbe_module, netcp); + if (!gbe_intf) + return; + + netcp_ethss_update_link_state(gbe_intf->gbe_dev, gbe_intf->slave, + ndev); +} + +static void gbe_adjust_link_sec_slaves(struct net_device *ndev) +{ + struct gbe_priv *gbe_dev = netdev_priv(ndev); + struct gbe_slave *slave; + + for_each_sec_slave(slave, gbe_dev) + netcp_ethss_update_link_state(gbe_dev, slave, NULL); +} + +/* Reset EMAC + * Soft reset is set and polled until clear, or until a timeout occurs + */ +static int gbe_port_reset(struct gbe_slave *slave) +{ + u32 i, v; + + /* Set the soft reset bit */ + writel(SOFT_RESET, GBE_REG_ADDR(slave, emac_regs, soft_reset)); + + /* Wait for the bit to clear */ + for (i = 0; i < DEVICE_EMACSL_RESET_POLL_COUNT; i++) { + v = readl(GBE_REG_ADDR(slave, emac_regs, soft_reset)); + if ((v & SOFT_RESET_MASK) != SOFT_RESET) + return 0; + } + + /* Timeout on the reset */ + return GMACSL_RET_WARN_RESET_INCOMPLETE; +} + +/* Configure EMAC */ +static void gbe_port_config(struct gbe_priv *gbe_dev, struct gbe_slave *slave, + int max_rx_len) +{ + u32 xgmii_mode; + + if (max_rx_len > NETCP_MAX_FRAME_SIZE) + max_rx_len = NETCP_MAX_FRAME_SIZE; + + /* Enable correct MII mode at SS level */ + if ((gbe_dev->ss_version == XGBE_SS_VERSION_10) && + (slave->link_interface >= XGMII_LINK_MAC_PHY)) { + xgmii_mode = readl(GBE_REG_ADDR(gbe_dev, ss_regs, control)); + xgmii_mode |= (1 << slave->slave_num); + writel(xgmii_mode, GBE_REG_ADDR(gbe_dev, ss_regs, control)); + } + + writel(max_rx_len, GBE_REG_ADDR(slave, emac_regs, rx_maxlen)); + writel(slave->mac_control, GBE_REG_ADDR(slave, emac_regs, mac_control)); +} + +static void gbe_slave_stop(struct gbe_intf *intf) +{ + struct gbe_priv *gbe_dev = intf->gbe_dev; + struct gbe_slave *slave = intf->slave; + + gbe_port_reset(slave); + /* Disable forwarding */ + cpsw_ale_control_set(gbe_dev->ale, slave->port_num, + ALE_PORT_STATE, ALE_PORT_STATE_DISABLE); + cpsw_ale_del_mcast(gbe_dev->ale, intf->ndev->broadcast, + 1 << slave->port_num, 0, 0); + + if (!slave->phy) + return; + + phy_stop(slave->phy); + phy_disconnect(slave->phy); + slave->phy = NULL; +} + +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)) { + netcp_sgmii_reset(sgmii_port_regs, slave->slave_num); + netcp_sgmii_config(sgmii_port_regs, slave->slave_num, + slave->link_interface); + } +} + +static int gbe_slave_open(struct gbe_intf *gbe_intf) +{ + struct gbe_priv *priv = gbe_intf->gbe_dev; + struct gbe_slave *slave = gbe_intf->slave; + phy_interface_t phy_mode; + bool has_phy = false; + + void (*hndlr)(struct net_device *) = gbe_adjust_link; + + gbe_sgmii_config(priv, slave); + gbe_port_reset(slave); + gbe_port_config(priv, slave, priv->rx_packet_max); + gbe_set_slave_mac(slave, gbe_intf); + /* enable forwarding */ + cpsw_ale_control_set(priv->ale, slave->port_num, + ALE_PORT_STATE, ALE_PORT_STATE_FORWARD); + cpsw_ale_add_mcast(priv->ale, gbe_intf->ndev->broadcast, + 1 << slave->port_num, 0, 0, ALE_MCAST_FWD_2); + + if (slave->link_interface == SGMII_LINK_MAC_PHY) { + has_phy = true; + phy_mode = PHY_INTERFACE_MODE_SGMII; + slave->phy_port_t = PORT_MII; + } else if (slave->link_interface == XGMII_LINK_MAC_PHY) { + has_phy = true; + phy_mode = PHY_INTERFACE_MODE_NA; + slave->phy_port_t = PORT_FIBRE; + } + + if (has_phy) { + if (priv->ss_version == XGBE_SS_VERSION_10) + hndlr = xgbe_adjust_link; + + slave->phy = of_phy_connect(gbe_intf->ndev, + slave->phy_node, + hndlr, 0, + phy_mode); + if (!slave->phy) { + dev_err(priv->dev, "phy not found on slave %d\n", + slave->slave_num); + return -ENODEV; + } + dev_dbg(priv->dev, "phy found: id is: 0x%s\n", + dev_name(&slave->phy->dev)); + phy_start(slave->phy); + phy_read_status(slave->phy); + } + return 0; +} + +static void gbe_init_host_port(struct gbe_priv *priv) +{ + int bypass_en = 1; + /* Max length register */ + writel(NETCP_MAX_FRAME_SIZE, GBE_REG_ADDR(priv, host_port_regs, + rx_maxlen)); + + cpsw_ale_start(priv->ale); + + if (priv->enable_ale) + bypass_en = 0; + + cpsw_ale_control_set(priv->ale, 0, ALE_BYPASS, bypass_en); + + cpsw_ale_control_set(priv->ale, 0, ALE_NO_PORT_VLAN, 1); + + cpsw_ale_control_set(priv->ale, priv->host_port, + ALE_PORT_STATE, ALE_PORT_STATE_FORWARD); + + cpsw_ale_control_set(priv->ale, 0, + ALE_PORT_UNKNOWN_VLAN_MEMBER, + GBE_PORT_MASK(priv->ale_ports)); + + cpsw_ale_control_set(priv->ale, 0, + ALE_PORT_UNKNOWN_MCAST_FLOOD, + GBE_PORT_MASK(priv->ale_ports - 1)); + + cpsw_ale_control_set(priv->ale, 0, + ALE_PORT_UNKNOWN_REG_MCAST_FLOOD, + GBE_PORT_MASK(priv->ale_ports)); + + cpsw_ale_control_set(priv->ale, 0, + ALE_PORT_UNTAGGED_EGRESS, + GBE_PORT_MASK(priv->ale_ports)); +} + +static void gbe_add_mcast_addr(struct gbe_intf *gbe_intf, u8 *addr) +{ + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + u16 vlan_id; + + cpsw_ale_add_mcast(gbe_dev->ale, addr, + GBE_PORT_MASK(gbe_dev->ale_ports), 0, 0, + ALE_MCAST_FWD_2); + for_each_set_bit(vlan_id, gbe_intf->active_vlans, VLAN_N_VID) { + cpsw_ale_add_mcast(gbe_dev->ale, addr, + GBE_PORT_MASK(gbe_dev->ale_ports), + ALE_VLAN, vlan_id, ALE_MCAST_FWD_2); + } +} + +static void gbe_add_ucast_addr(struct gbe_intf *gbe_intf, u8 *addr) +{ + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + u16 vlan_id; + + cpsw_ale_add_ucast(gbe_dev->ale, addr, gbe_dev->host_port, 0, 0); + + for_each_set_bit(vlan_id, gbe_intf->active_vlans, VLAN_N_VID) + cpsw_ale_add_ucast(gbe_dev->ale, addr, gbe_dev->host_port, + ALE_VLAN, vlan_id); +} + +static void gbe_del_mcast_addr(struct gbe_intf *gbe_intf, u8 *addr) +{ + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + u16 vlan_id; + + cpsw_ale_del_mcast(gbe_dev->ale, addr, 0, 0, 0); + + for_each_set_bit(vlan_id, gbe_intf->active_vlans, VLAN_N_VID) { + cpsw_ale_del_mcast(gbe_dev->ale, addr, 0, ALE_VLAN, vlan_id); + } +} + +static void gbe_del_ucast_addr(struct gbe_intf *gbe_intf, u8 *addr) +{ + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + u16 vlan_id; + + cpsw_ale_del_ucast(gbe_dev->ale, addr, gbe_dev->host_port, 0, 0); + + for_each_set_bit(vlan_id, gbe_intf->active_vlans, VLAN_N_VID) { + cpsw_ale_del_ucast(gbe_dev->ale, addr, gbe_dev->host_port, + ALE_VLAN, vlan_id); + } +} + +static int gbe_add_addr(void *intf_priv, struct netcp_addr *naddr) +{ + struct gbe_intf *gbe_intf = intf_priv; + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + + dev_dbg(gbe_dev->dev, "ethss adding address %pM, type %d\n", + naddr->addr, naddr->type); + + switch (naddr->type) { + case ADDR_MCAST: + case ADDR_BCAST: + gbe_add_mcast_addr(gbe_intf, naddr->addr); + break; + case ADDR_UCAST: + case ADDR_DEV: + gbe_add_ucast_addr(gbe_intf, naddr->addr); + break; + case ADDR_ANY: + /* nothing to do for promiscuous */ + default: + break; + } + + return 0; +} + +static int gbe_del_addr(void *intf_priv, struct netcp_addr *naddr) +{ + struct gbe_intf *gbe_intf = intf_priv; + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + + dev_dbg(gbe_dev->dev, "ethss deleting address %pM, type %d\n", + naddr->addr, naddr->type); + + switch (naddr->type) { + case ADDR_MCAST: + case ADDR_BCAST: + gbe_del_mcast_addr(gbe_intf, naddr->addr); + break; + case ADDR_UCAST: + case ADDR_DEV: + gbe_del_ucast_addr(gbe_intf, naddr->addr); + break; + case ADDR_ANY: + /* nothing to do for promiscuous */ + default: + break; + } + + return 0; +} + +static int gbe_add_vid(void *intf_priv, int vid) +{ + struct gbe_intf *gbe_intf = intf_priv; + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + + set_bit(vid, gbe_intf->active_vlans); + + cpsw_ale_add_vlan(gbe_dev->ale, vid, + GBE_PORT_MASK(gbe_dev->ale_ports), + GBE_MASK_NO_PORTS, + GBE_PORT_MASK(gbe_dev->ale_ports), + GBE_PORT_MASK(gbe_dev->ale_ports - 1)); + + return 0; +} + +static int gbe_del_vid(void *intf_priv, int vid) +{ + struct gbe_intf *gbe_intf = intf_priv; + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + + cpsw_ale_del_vlan(gbe_dev->ale, vid, 0); + clear_bit(vid, gbe_intf->active_vlans); + return 0; +} + +static int gbe_ioctl(void *intf_priv, struct ifreq *req, int cmd) +{ + struct gbe_intf *gbe_intf = intf_priv; + struct phy_device *phy = gbe_intf->slave->phy; + int ret = -EOPNOTSUPP; + + if (phy) + ret = phy_mii_ioctl(phy, req, cmd); + + return ret; +} + +static void netcp_ethss_timer(unsigned long arg) +{ + struct gbe_priv *gbe_dev = (struct gbe_priv *)arg; + struct gbe_intf *gbe_intf; + struct gbe_slave *slave; + + /* Check & update SGMII link state of interfaces */ + for_each_intf(gbe_intf, gbe_dev) { + if (!gbe_intf->slave->open) + continue; + netcp_ethss_update_link_state(gbe_dev, gbe_intf->slave, + gbe_intf->ndev); + } + + /* Check & update SGMII link state of secondary ports */ + for_each_sec_slave(slave, gbe_dev) { + netcp_ethss_update_link_state(gbe_dev, slave, NULL); + } + + spin_lock_bh(&gbe_dev->hw_stats_lock); + + if (gbe_dev->ss_version == GBE_SS_VERSION_14) + gbe_update_stats_ver14(gbe_dev, NULL); + else + gbe_update_stats(gbe_dev, NULL); + + spin_unlock_bh(&gbe_dev->hw_stats_lock); + + gbe_dev->timer.expires = jiffies + GBE_TIMER_INTERVAL; + add_timer(&gbe_dev->timer); +} + +static int gbe_tx_hook(int order, void *data, struct netcp_packet *p_info) +{ + struct gbe_intf *gbe_intf = data; + + p_info->tx_pipe = &gbe_intf->tx_pipe; + return 0; +} + +static int gbe_open(void *intf_priv, struct net_device *ndev) +{ + struct gbe_intf *gbe_intf = intf_priv; + struct gbe_priv *gbe_dev = gbe_intf->gbe_dev; + struct netcp_intf *netcp = netdev_priv(ndev); + struct gbe_slave *slave = gbe_intf->slave; + int port_num = slave->port_num; + u32 reg; + int ret; + + reg = readl(GBE_REG_ADDR(gbe_dev, switch_regs, id_ver)); + dev_dbg(gbe_dev->dev, "initializing gbe version %d.%d (%d) GBE identification value 0x%x\n", + GBE_MAJOR_VERSION(reg), GBE_MINOR_VERSION(reg), + GBE_RTL_VERSION(reg), GBE_IDENT(reg)); + + if (gbe_dev->enable_ale) + gbe_intf->tx_pipe.dma_psflags = 0; + else + gbe_intf->tx_pipe.dma_psflags = port_num; + + dev_dbg(gbe_dev->dev, "opened TX channel %s: %p with psflags %d\n", + gbe_intf->tx_pipe.dma_chan_name, + gbe_intf->tx_pipe.dma_channel, + gbe_intf->tx_pipe.dma_psflags); + + gbe_slave_stop(gbe_intf); + + /* disable priority elevation and enable statistics on all ports */ + writel(0, GBE_REG_ADDR(gbe_dev, switch_regs, ptype)); + + /* Control register */ + writel(GBE_CTL_P0_ENABLE, GBE_REG_ADDR(gbe_dev, switch_regs, control)); + + /* All statistics enabled and STAT AB visible by default */ + writel(GBE_REG_VAL_STAT_ENABLE_ALL, GBE_REG_ADDR(gbe_dev, switch_regs, + stat_port_en)); + + ret = gbe_slave_open(gbe_intf); + if (ret) + goto fail; + + netcp_register_txhook(netcp, GBE_TXHOOK_ORDER, gbe_tx_hook, + gbe_intf); + + slave->open = true; + netcp_ethss_update_link_state(gbe_dev, slave, ndev); + return 0; + +fail: + gbe_slave_stop(gbe_intf); + return ret; +} + +static int gbe_close(void *intf_priv, struct net_device *ndev) +{ + struct gbe_intf *gbe_intf = intf_priv; + struct netcp_intf *netcp = netdev_priv(ndev); + + gbe_slave_stop(gbe_intf); + netcp_unregister_txhook(netcp, GBE_TXHOOK_ORDER, gbe_tx_hook, + gbe_intf); + + gbe_intf->slave->open = false; + atomic_set(&gbe_intf->slave->link_state, NETCP_LINK_STATE_INVALID); + return 0; +} + +static int init_slave(struct gbe_priv *gbe_dev, struct gbe_slave *slave, + struct device_node *node) +{ + int port_reg_num; + u32 port_reg_ofs, emac_reg_ofs; + + if (of_property_read_u32(node, "slave-port", &slave->slave_num)) { + dev_err(gbe_dev->dev, "missing slave-port parameter\n"); + return -EINVAL; + } + + if (of_property_read_u32(node, "link-interface", + &slave->link_interface)) { + dev_warn(gbe_dev->dev, + "missing link-interface value defaulting to 1G mac-phy link\n"); + slave->link_interface = SGMII_LINK_MAC_PHY; + } + + slave->open = false; + slave->phy_node = of_parse_phandle(node, "phy-handle", 0); + slave->port_num = gbe_get_slave_port(gbe_dev, slave->slave_num); + + if (slave->link_interface >= XGMII_LINK_MAC_PHY) + slave->mac_control = GBE_DEF_10G_MAC_CONTROL; + else + slave->mac_control = GBE_DEF_1G_MAC_CONTROL; + + /* Emac regs memmap are contiguous but port regs are not */ + port_reg_num = slave->slave_num; + if (gbe_dev->ss_version == GBE_SS_VERSION_14) { + if (slave->slave_num > 1) { + port_reg_ofs = GBE13_SLAVE_PORT2_OFFSET; + port_reg_num -= 2; + } else { + port_reg_ofs = GBE13_SLAVE_PORT_OFFSET; + } + } else if (gbe_dev->ss_version == XGBE_SS_VERSION_10) { + port_reg_ofs = XGBE10_SLAVE_PORT_OFFSET; + } else { + dev_err(gbe_dev->dev, "unknown ethss(0x%x)\n", + gbe_dev->ss_version); + return -EINVAL; + } + + if (gbe_dev->ss_version == GBE_SS_VERSION_14) + emac_reg_ofs = GBE13_EMAC_OFFSET; + else if (gbe_dev->ss_version == XGBE_SS_VERSION_10) + emac_reg_ofs = XGBE10_EMAC_OFFSET; + + slave->port_regs = gbe_dev->ss_regs + port_reg_ofs + + (0x30 * port_reg_num); + slave->emac_regs = gbe_dev->ss_regs + emac_reg_ofs + + (0x40 * slave->slave_num); + + if (gbe_dev->ss_version == GBE_SS_VERSION_14) { + /* Initialize slave port register offsets */ + GBE_SET_REG_OFS(slave, port_regs, port_vlan); + GBE_SET_REG_OFS(slave, port_regs, tx_pri_map); + GBE_SET_REG_OFS(slave, port_regs, sa_lo); + GBE_SET_REG_OFS(slave, port_regs, sa_hi); + GBE_SET_REG_OFS(slave, port_regs, ts_ctl); + GBE_SET_REG_OFS(slave, port_regs, ts_seq_ltype); + GBE_SET_REG_OFS(slave, port_regs, ts_vlan); + GBE_SET_REG_OFS(slave, port_regs, ts_ctl_ltype2); + GBE_SET_REG_OFS(slave, port_regs, ts_ctl2); + + /* Initialize EMAC register offsets */ + GBE_SET_REG_OFS(slave, emac_regs, mac_control); + GBE_SET_REG_OFS(slave, emac_regs, soft_reset); + GBE_SET_REG_OFS(slave, emac_regs, rx_maxlen); + + } else if (gbe_dev->ss_version == XGBE_SS_VERSION_10) { + /* Initialize slave port register offsets */ + XGBE_SET_REG_OFS(slave, port_regs, port_vlan); + XGBE_SET_REG_OFS(slave, port_regs, tx_pri_map); + XGBE_SET_REG_OFS(slave, port_regs, sa_lo); + XGBE_SET_REG_OFS(slave, port_regs, sa_hi); + XGBE_SET_REG_OFS(slave, port_regs, ts_ctl); + XGBE_SET_REG_OFS(slave, port_regs, ts_seq_ltype); + XGBE_SET_REG_OFS(slave, port_regs, ts_vlan); + XGBE_SET_REG_OFS(slave, port_regs, ts_ctl_ltype2); + XGBE_SET_REG_OFS(slave, port_regs, ts_ctl2); + + /* Initialize EMAC register offsets */ + XGBE_SET_REG_OFS(slave, emac_regs, mac_control); + XGBE_SET_REG_OFS(slave, emac_regs, soft_reset); + XGBE_SET_REG_OFS(slave, emac_regs, rx_maxlen); + } + + atomic_set(&slave->link_state, NETCP_LINK_STATE_INVALID); + return 0; +} + +static void init_secondary_ports(struct gbe_priv *gbe_dev, + struct device_node *node) +{ + struct device *dev = gbe_dev->dev; + phy_interface_t phy_mode; + struct gbe_priv **priv; + struct device_node *port; + struct gbe_slave *slave; + bool mac_phy_link = false; + + for_each_child_of_node(node, port) { + slave = devm_kzalloc(dev, sizeof(*slave), GFP_KERNEL); + if (!slave) { + dev_err(dev, + "memomry alloc failed for secondary port(%s), skipping...\n", + port->name); + continue; + } + + if (init_slave(gbe_dev, slave, port)) { + dev_err(dev, + "Failed to initialize secondary port(%s), skipping...\n", + port->name); + devm_kfree(dev, slave); + continue; + } + + gbe_sgmii_config(gbe_dev, slave); + gbe_port_reset(slave); + gbe_port_config(gbe_dev, slave, gbe_dev->rx_packet_max); + list_add_tail(&slave->slave_list, &gbe_dev->secondary_slaves); + gbe_dev->num_slaves++; + if ((slave->link_interface == SGMII_LINK_MAC_PHY) || + (slave->link_interface == XGMII_LINK_MAC_PHY)) + mac_phy_link = true; + + slave->open = true; + } + + /* of_phy_connect() is needed only for MAC-PHY interface */ + if (!mac_phy_link) + return; + + /* Allocate dummy netdev device for attaching to phy device */ + gbe_dev->dummy_ndev = alloc_netdev(sizeof(gbe_dev), "dummy", + NET_NAME_UNKNOWN, ether_setup); + if (!gbe_dev->dummy_ndev) { + dev_err(dev, + "Failed to allocate dummy netdev for secondary ports, skipping phy_connect()...\n"); + return; + } + priv = netdev_priv(gbe_dev->dummy_ndev); + *priv = gbe_dev; + + if (slave->link_interface == SGMII_LINK_MAC_PHY) { + phy_mode = PHY_INTERFACE_MODE_SGMII; + slave->phy_port_t = PORT_MII; + } else { + phy_mode = PHY_INTERFACE_MODE_NA; + slave->phy_port_t = PORT_FIBRE; + } + + for_each_sec_slave(slave, gbe_dev) { + if ((slave->link_interface != SGMII_LINK_MAC_PHY) && + (slave->link_interface != XGMII_LINK_MAC_PHY)) + continue; + slave->phy = + of_phy_connect(gbe_dev->dummy_ndev, + slave->phy_node, + gbe_adjust_link_sec_slaves, + 0, phy_mode); + if (!slave->phy) { + dev_err(dev, "phy not found for slave %d\n", + slave->slave_num); + slave->phy = NULL; + } else { + dev_dbg(dev, "phy found: id is: 0x%s\n", + dev_name(&slave->phy->dev)); + phy_start(slave->phy); + phy_read_status(slave->phy); + } + } +} + +static void free_secondary_ports(struct gbe_priv *gbe_dev) +{ + struct gbe_slave *slave; + + for (;;) { + slave = first_sec_slave(gbe_dev); + if (!slave) + break; + if (slave->phy) + phy_disconnect(slave->phy); + list_del(&slave->slave_list); + } + if (gbe_dev->dummy_ndev) + free_netdev(gbe_dev->dummy_ndev); +} + +static int set_xgbe_ethss10_priv(struct gbe_priv *gbe_dev, + struct device_node *node) +{ + struct resource res; + void __iomem *regs; + int ret, i; + + ret = of_address_to_resource(node, 0, &res); + if (ret) { + dev_err(gbe_dev->dev, "Can't translate of node(%s) address for xgbe subsystem regs\n", + node->name); + return ret; + } + + regs = devm_ioremap_resource(gbe_dev->dev, &res); + if (IS_ERR(regs)) { + dev_err(gbe_dev->dev, "Failed to map xgbe register base\n"); + return PTR_ERR(regs); + } + gbe_dev->ss_regs = regs; + + ret = of_address_to_resource(node, XGBE_SERDES_REG_INDEX, &res); + if (ret) { + dev_err(gbe_dev->dev, "Can't translate of node(%s) address for xgbe serdes regs\n", + node->name); + return ret; + } + + regs = devm_ioremap_resource(gbe_dev->dev, &res); + if (IS_ERR(regs)) { + dev_err(gbe_dev->dev, "Failed to map xgbe serdes register base\n"); + return PTR_ERR(regs); + } + gbe_dev->xgbe_serdes_regs = regs; + + gbe_dev->hw_stats = devm_kzalloc(gbe_dev->dev, + XGBE10_NUM_STAT_ENTRIES * + (XGBE10_NUM_SLAVES + 1) * sizeof(u64), + GFP_KERNEL); + if (!gbe_dev->hw_stats) { + dev_err(gbe_dev->dev, "hw_stats memory allocation failed\n"); + return -ENOMEM; + } + + gbe_dev->ss_version = XGBE_SS_VERSION_10; + gbe_dev->sgmii_port_regs = gbe_dev->ss_regs + + XGBE10_SGMII_MODULE_OFFSET; + gbe_dev->switch_regs = gbe_dev->ss_regs + XGBE10_SWITCH_MODULE_OFFSET; + gbe_dev->host_port_regs = gbe_dev->ss_regs + XGBE10_HOST_PORT_OFFSET; + + for (i = 0; i < XGBE10_NUM_HW_STATS_MOD; i++) + gbe_dev->hw_stats_regs[i] = gbe_dev->ss_regs + + XGBE10_HW_STATS_OFFSET + (GBE_HW_STATS_REG_MAP_SZ * i); + + gbe_dev->ale_reg = gbe_dev->ss_regs + XGBE10_ALE_OFFSET; + gbe_dev->ale_ports = XGBE10_NUM_ALE_PORTS; + gbe_dev->host_port = XGBE10_HOST_PORT_NUM; + gbe_dev->ale_entries = XGBE10_NUM_ALE_ENTRIES; + gbe_dev->et_stats = xgbe10_et_stats; + gbe_dev->num_et_stats = ARRAY_SIZE(xgbe10_et_stats); + + /* Subsystem registers */ + XGBE_SET_REG_OFS(gbe_dev, ss_regs, id_ver); + XGBE_SET_REG_OFS(gbe_dev, ss_regs, control); + + /* Switch module registers */ + XGBE_SET_REG_OFS(gbe_dev, switch_regs, id_ver); + XGBE_SET_REG_OFS(gbe_dev, switch_regs, control); + XGBE_SET_REG_OFS(gbe_dev, switch_regs, ptype); + XGBE_SET_REG_OFS(gbe_dev, switch_regs, stat_port_en); + XGBE_SET_REG_OFS(gbe_dev, switch_regs, flow_control); + + /* Host port registers */ + XGBE_SET_REG_OFS(gbe_dev, host_port_regs, port_vlan); + XGBE_SET_REG_OFS(gbe_dev, host_port_regs, tx_pri_map); + XGBE_SET_REG_OFS(gbe_dev, host_port_regs, rx_maxlen); + return 0; +} + +static int get_gbe_resource_version(struct gbe_priv *gbe_dev, + struct device_node *node) +{ + struct resource res; + void __iomem *regs; + int ret; + + ret = of_address_to_resource(node, 0, &res); + if (ret) { + dev_err(gbe_dev->dev, "Can't translate of node(%s) address\n", + node->name); + return ret; + } + + regs = devm_ioremap_resource(gbe_dev->dev, &res); + if (IS_ERR(regs)) { + dev_err(gbe_dev->dev, "Failed to map gbe register base\n"); + return PTR_ERR(regs); + } + gbe_dev->ss_regs = regs; + gbe_dev->ss_version = readl(gbe_dev->ss_regs); + return 0; +} + +static int set_gbe_ethss14_priv(struct gbe_priv *gbe_dev, + struct device_node *node) +{ + void __iomem *regs; + int i; + + gbe_dev->hw_stats = devm_kzalloc(gbe_dev->dev, + GBE13_NUM_HW_STAT_ENTRIES * + GBE13_NUM_SLAVES * sizeof(u64), + GFP_KERNEL); + if (!gbe_dev->hw_stats) { + dev_err(gbe_dev->dev, "hw_stats memory allocation failed\n"); + return -ENOMEM; + } + + regs = gbe_dev->ss_regs; + gbe_dev->sgmii_port_regs = regs + GBE13_SGMII_MODULE_OFFSET; + gbe_dev->sgmii_port34_regs = regs + GBE13_SGMII34_MODULE_OFFSET; + gbe_dev->switch_regs = regs + GBE13_SWITCH_MODULE_OFFSET; + gbe_dev->host_port_regs = regs + GBE13_HOST_PORT_OFFSET; + + for (i = 0; i < GBE13_NUM_HW_STATS_MOD; i++) + gbe_dev->hw_stats_regs[i] = regs + GBE13_HW_STATS_OFFSET + + (GBE_HW_STATS_REG_MAP_SZ * i); + + gbe_dev->ale_reg = regs + GBE13_ALE_OFFSET; + gbe_dev->ale_ports = GBE13_NUM_ALE_PORTS; + gbe_dev->host_port = GBE13_HOST_PORT_NUM; + gbe_dev->ale_entries = GBE13_NUM_ALE_ENTRIES; + gbe_dev->et_stats = gbe13_et_stats; + gbe_dev->num_et_stats = ARRAY_SIZE(gbe13_et_stats); + + /* Subsystem registers */ + GBE_SET_REG_OFS(gbe_dev, ss_regs, id_ver); + + /* Switch module registers */ + GBE_SET_REG_OFS(gbe_dev, switch_regs, id_ver); + GBE_SET_REG_OFS(gbe_dev, switch_regs, control); + GBE_SET_REG_OFS(gbe_dev, switch_regs, soft_reset); + GBE_SET_REG_OFS(gbe_dev, switch_regs, stat_port_en); + GBE_SET_REG_OFS(gbe_dev, switch_regs, ptype); + GBE_SET_REG_OFS(gbe_dev, switch_regs, flow_control); + + /* Host port registers */ + GBE_SET_REG_OFS(gbe_dev, host_port_regs, port_vlan); + GBE_SET_REG_OFS(gbe_dev, host_port_regs, rx_maxlen); + return 0; +} + +static int gbe_probe(struct netcp_device *netcp_device, struct device *dev, + struct device_node *node, void **inst_priv) +{ + struct device_node *interfaces, *interface; + struct device_node *secondary_ports; + struct cpsw_ale_params ale_params; + struct gbe_priv *gbe_dev; + u32 slave_num; + int ret = 0; + + if (!node) { + dev_err(dev, "device tree info unavailable\n"); + return -ENODEV; + } + + gbe_dev = devm_kzalloc(dev, sizeof(struct gbe_priv), GFP_KERNEL); + if (!gbe_dev) + return -ENOMEM; + + gbe_dev->dev = dev; + gbe_dev->netcp_device = netcp_device; + gbe_dev->rx_packet_max = NETCP_MAX_FRAME_SIZE; + + /* init the hw stats lock */ + spin_lock_init(&gbe_dev->hw_stats_lock); + + if (of_find_property(node, "enable-ale", NULL)) { + gbe_dev->enable_ale = true; + dev_info(dev, "ALE enabled\n"); + } else { + gbe_dev->enable_ale = false; + dev_dbg(dev, "ALE bypass enabled*\n"); + } + + ret = of_property_read_u32(node, "tx-queue", + &gbe_dev->tx_queue_id); + if (ret < 0) { + dev_err(dev, "missing tx_queue parameter\n"); + gbe_dev->tx_queue_id = GBE_TX_QUEUE; + } + + ret = of_property_read_string(node, "tx-channel", + &gbe_dev->dma_chan_name); + if (ret < 0) { + dev_err(dev, "missing \"tx-channel\" parameter\n"); + ret = -ENODEV; + goto quit; + } + + if (!strcmp(node->name, "gbe")) { + ret = get_gbe_resource_version(gbe_dev, node); + if (ret) + goto quit; + + ret = set_gbe_ethss14_priv(gbe_dev, node); + if (ret) + goto quit; + } else if (!strcmp(node->name, "xgbe")) { + ret = set_xgbe_ethss10_priv(gbe_dev, node); + if (ret) + goto quit; + ret = netcp_xgbe_serdes_init(gbe_dev->xgbe_serdes_regs, + gbe_dev->ss_regs); + if (ret) + goto quit; + } else { + dev_err(dev, "unknown GBE node(%s)\n", node->name); + ret = -ENODEV; + goto quit; + } + + interfaces = of_get_child_by_name(node, "interfaces"); + if (!interfaces) + dev_err(dev, "could not find interfaces\n"); + + ret = netcp_txpipe_init(&gbe_dev->tx_pipe, netcp_device, + gbe_dev->dma_chan_name, gbe_dev->tx_queue_id); + if (ret) + goto quit; + + ret = netcp_txpipe_open(&gbe_dev->tx_pipe); + if (ret) + goto quit; + + /* Create network interfaces */ + INIT_LIST_HEAD(&gbe_dev->gbe_intf_head); + for_each_child_of_node(interfaces, interface) { + ret = of_property_read_u32(interface, "slave-port", &slave_num); + if (ret) { + dev_err(dev, "missing slave-port parameter, skipping interface configuration for %s\n", + interface->name); + continue; + } + gbe_dev->num_slaves++; + } + + if (!gbe_dev->num_slaves) + dev_warn(dev, "No network interface configured\n"); + + /* Initialize Secondary slave ports */ + secondary_ports = of_get_child_by_name(node, "secondary-slave-ports"); + INIT_LIST_HEAD(&gbe_dev->secondary_slaves); + if (secondary_ports) + init_secondary_ports(gbe_dev, secondary_ports); + of_node_put(secondary_ports); + + if (!gbe_dev->num_slaves) { + dev_err(dev, "No network interface or secondary ports configured\n"); + ret = -ENODEV; + goto quit; + } + + memset(&ale_params, 0, sizeof(ale_params)); + ale_params.dev = gbe_dev->dev; + ale_params.ale_regs = gbe_dev->ale_reg; + ale_params.ale_ageout = GBE_DEFAULT_ALE_AGEOUT; + ale_params.ale_entries = gbe_dev->ale_entries; + ale_params.ale_ports = gbe_dev->ale_ports; + + gbe_dev->ale = cpsw_ale_create(&ale_params); + if (!gbe_dev->ale) { + dev_err(gbe_dev->dev, "error initializing ale engine\n"); + ret = -ENODEV; + goto quit; + } else { + dev_dbg(gbe_dev->dev, "Created a gbe ale engine\n"); + } + + /* initialize host port */ + gbe_init_host_port(gbe_dev); + + init_timer(&gbe_dev->timer); + gbe_dev->timer.data = (unsigned long)gbe_dev; + gbe_dev->timer.function = netcp_ethss_timer; + gbe_dev->timer.expires = jiffies + GBE_TIMER_INTERVAL; + add_timer(&gbe_dev->timer); + *inst_priv = gbe_dev; + return 0; + +quit: + if (gbe_dev->hw_stats) + devm_kfree(dev, gbe_dev->hw_stats); + cpsw_ale_destroy(gbe_dev->ale); + if (gbe_dev->ss_regs) + devm_iounmap(dev, gbe_dev->ss_regs); + of_node_put(interfaces); + devm_kfree(dev, gbe_dev); + return ret; +} + +static int gbe_attach(void *inst_priv, struct net_device *ndev, + struct device_node *node, void **intf_priv) +{ + struct gbe_priv *gbe_dev = inst_priv; + struct gbe_intf *gbe_intf; + int ret; + + if (!node) { + dev_err(gbe_dev->dev, "interface node not available\n"); + return -ENODEV; + } + + gbe_intf = devm_kzalloc(gbe_dev->dev, sizeof(*gbe_intf), GFP_KERNEL); + if (!gbe_intf) + return -ENOMEM; + + gbe_intf->ndev = ndev; + gbe_intf->dev = gbe_dev->dev; + gbe_intf->gbe_dev = gbe_dev; + + gbe_intf->slave = devm_kzalloc(gbe_dev->dev, + sizeof(*gbe_intf->slave), + GFP_KERNEL); + if (!gbe_intf->slave) { + ret = -ENOMEM; + goto fail; + } + + if (init_slave(gbe_dev, gbe_intf->slave, node)) { + ret = -ENODEV; + goto fail; + } + + gbe_intf->tx_pipe = gbe_dev->tx_pipe; + ndev->ethtool_ops = &keystone_ethtool_ops; + list_add_tail(&gbe_intf->gbe_intf_list, &gbe_dev->gbe_intf_head); + *intf_priv = gbe_intf; + return 0; + +fail: + if (gbe_intf->slave) + devm_kfree(gbe_dev->dev, gbe_intf->slave); + if (gbe_intf) + devm_kfree(gbe_dev->dev, gbe_intf); + return ret; +} + +static int gbe_release(void *intf_priv) +{ + struct gbe_intf *gbe_intf = intf_priv; + + gbe_intf->ndev->ethtool_ops = NULL; + list_del(&gbe_intf->gbe_intf_list); + devm_kfree(gbe_intf->dev, gbe_intf->slave); + devm_kfree(gbe_intf->dev, gbe_intf); + return 0; +} + +static int gbe_remove(struct netcp_device *netcp_device, void *inst_priv) +{ + struct gbe_priv *gbe_dev = inst_priv; + + del_timer_sync(&gbe_dev->timer); + cpsw_ale_stop(gbe_dev->ale); + cpsw_ale_destroy(gbe_dev->ale); + netcp_txpipe_close(&gbe_dev->tx_pipe); + free_secondary_ports(gbe_dev); + + if (!list_empty(&gbe_dev->gbe_intf_head)) + dev_alert(gbe_dev->dev, "unreleased ethss interfaces present\n"); + + devm_kfree(gbe_dev->dev, gbe_dev->hw_stats); + devm_iounmap(gbe_dev->dev, gbe_dev->ss_regs); + memset(gbe_dev, 0x00, sizeof(*gbe_dev)); + devm_kfree(gbe_dev->dev, gbe_dev); + return 0; +} + +static struct netcp_module gbe_module = { + .name = GBE_MODULE_NAME, + .owner = THIS_MODULE, + .primary = true, + .probe = gbe_probe, + .open = gbe_open, + .close = gbe_close, + .remove = gbe_remove, + .attach = gbe_attach, + .release = gbe_release, + .add_addr = gbe_add_addr, + .del_addr = gbe_del_addr, + .add_vid = gbe_add_vid, + .del_vid = gbe_del_vid, + .ioctl = gbe_ioctl, +}; + +static struct netcp_module xgbe_module = { + .name = XGBE_MODULE_NAME, + .owner = THIS_MODULE, + .primary = true, + .probe = gbe_probe, + .open = gbe_open, + .close = gbe_close, + .remove = gbe_remove, + .attach = gbe_attach, + .release = gbe_release, + .add_addr = gbe_add_addr, + .del_addr = gbe_del_addr, + .add_vid = gbe_add_vid, + .del_vid = gbe_del_vid, + .ioctl = gbe_ioctl, +}; + +static int __init keystone_gbe_init(void) +{ + int ret; + + ret = netcp_register_module(&gbe_module); + if (ret) + return ret; + + ret = netcp_register_module(&xgbe_module); + if (ret) + return ret; + + return 0; +} +module_init(keystone_gbe_init); + +static void __exit keystone_gbe_exit(void) +{ + netcp_unregister_module(&gbe_module); + netcp_unregister_module(&xgbe_module); +} +module_exit(keystone_gbe_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TI NETCP ETHSS driver for Keystone SOCs"); +MODULE_AUTHOR("Sandeep Nair <sandeep_n@ti.com"); diff --git a/drivers/net/ethernet/ti/netcp_sgmii.c b/drivers/net/ethernet/ti/netcp_sgmii.c new file mode 100644 index 000000000000..dbeb14266e2f --- /dev/null +++ b/drivers/net/ethernet/ti/netcp_sgmii.c @@ -0,0 +1,131 @@ +/* + * SGMI module initialisation + * + * Copyright (C) 2014 Texas Instruments Incorporated + * Authors: Sandeep Nair <sandeep_n@ti.com> + * Sandeep Paulraj <s-paulraj@ti.com> + * Wingman Kwok <w-kwok2@ti.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 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 "netcp.h" + +#define SGMII_REG_STATUS_LOCK BIT(4) +#define SGMII_REG_STATUS_LINK BIT(0) +#define SGMII_REG_STATUS_AUTONEG BIT(2) +#define SGMII_REG_CONTROL_AUTONEG BIT(0) + +#define SGMII23_OFFSET(x) ((x - 2) * 0x100) +#define SGMII_OFFSET(x) ((x <= 1) ? (x * 0x100) : (SGMII23_OFFSET(x))) + +/* SGMII registers */ +#define SGMII_SRESET_REG(x) (SGMII_OFFSET(x) + 0x004) +#define SGMII_CTL_REG(x) (SGMII_OFFSET(x) + 0x010) +#define SGMII_STATUS_REG(x) (SGMII_OFFSET(x) + 0x014) +#define SGMII_MRADV_REG(x) (SGMII_OFFSET(x) + 0x018) + +static void sgmii_write_reg(void __iomem *base, int reg, u32 val) +{ + writel(val, base + reg); +} + +static u32 sgmii_read_reg(void __iomem *base, int reg) +{ + return readl(base + reg); +} + +static void sgmii_write_reg_bit(void __iomem *base, int reg, u32 val) +{ + writel((readl(base + reg) | val), base + reg); +} + +/* port is 0 based */ +int netcp_sgmii_reset(void __iomem *sgmii_ofs, int port) +{ + /* Soft reset */ + sgmii_write_reg_bit(sgmii_ofs, SGMII_SRESET_REG(port), 0x1); + while (sgmii_read_reg(sgmii_ofs, SGMII_SRESET_REG(port)) != 0x0) + ; + return 0; +} + +int netcp_sgmii_get_port_link(void __iomem *sgmii_ofs, int port) +{ + u32 status = 0, link = 0; + + status = sgmii_read_reg(sgmii_ofs, SGMII_STATUS_REG(port)); + if ((status & SGMII_REG_STATUS_LINK) != 0) + link = 1; + return link; +} + +int netcp_sgmii_config(void __iomem *sgmii_ofs, int port, u32 interface) +{ + unsigned int i, status, mask; + u32 mr_adv_ability; + u32 control; + + switch (interface) { + case SGMII_LINK_MAC_MAC_AUTONEG: + mr_adv_ability = 0x9801; + control = 0x21; + break; + + case SGMII_LINK_MAC_PHY: + case SGMII_LINK_MAC_PHY_NO_MDIO: + mr_adv_ability = 1; + control = 1; + break; + + case SGMII_LINK_MAC_MAC_FORCED: + mr_adv_ability = 0x9801; + control = 0x20; + break; + + case SGMII_LINK_MAC_FIBER: + mr_adv_ability = 0x20; + control = 0x1; + break; + + default: + WARN_ONCE(1, "Invalid sgmii interface: %d\n", interface); + return -EINVAL; + } + + sgmii_write_reg(sgmii_ofs, SGMII_CTL_REG(port), 0); + + /* Wait for the SerDes pll to lock */ + for (i = 0; i < 1000; i++) { + usleep_range(1000, 2000); + status = sgmii_read_reg(sgmii_ofs, SGMII_STATUS_REG(port)); + if ((status & SGMII_REG_STATUS_LOCK) != 0) + break; + } + + if ((status & SGMII_REG_STATUS_LOCK) == 0) + pr_err("serdes PLL not locked\n"); + + sgmii_write_reg(sgmii_ofs, SGMII_MRADV_REG(port), mr_adv_ability); + sgmii_write_reg(sgmii_ofs, SGMII_CTL_REG(port), control); + + mask = SGMII_REG_STATUS_LINK; + if (control & SGMII_REG_CONTROL_AUTONEG) + mask |= SGMII_REG_STATUS_AUTONEG; + + for (i = 0; i < 1000; i++) { + usleep_range(200, 500); + status = sgmii_read_reg(sgmii_ofs, SGMII_STATUS_REG(port)); + if ((status & mask) == mask) + break; + } + + return 0; +} diff --git a/drivers/net/ethernet/ti/netcp_xgbepcsr.c b/drivers/net/ethernet/ti/netcp_xgbepcsr.c new file mode 100644 index 000000000000..33571acc52b6 --- /dev/null +++ b/drivers/net/ethernet/ti/netcp_xgbepcsr.c @@ -0,0 +1,501 @@ +/* + * XGE PCSR module initialisation + * + * Copyright (C) 2014 Texas Instruments Incorporated + * Authors: Sandeep Nair <sandeep_n@ti.com> + * WingMan Kwok <w-kwok2@ti.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 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 "netcp.h" + +/* XGBE registers */ +#define XGBE_CTRL_OFFSET 0x0c +#define XGBE_SGMII_1_OFFSET 0x0114 +#define XGBE_SGMII_2_OFFSET 0x0214 + +/* PCS-R registers */ +#define PCSR_CPU_CTRL_OFFSET 0x1fd0 +#define POR_EN BIT(29) + +#define reg_rmw(addr, value, mask) \ + writel(((readl(addr) & (~(mask))) | \ + (value & (mask))), (addr)) + +/* bit mask of width w at offset s */ +#define MASK_WID_SH(w, s) (((1 << w) - 1) << s) + +/* shift value v to offset s */ +#define VAL_SH(v, s) (v << s) + +#define PHY_A(serdes) 0 + +struct serdes_cfg { + u32 ofs; + u32 val; + u32 mask; +}; + +static struct serdes_cfg cfg_phyb_1p25g_156p25mhz_cmu0[] = { + {0x0000, 0x00800002, 0x00ff00ff}, + {0x0014, 0x00003838, 0x0000ffff}, + {0x0060, 0x1c44e438, 0xffffffff}, + {0x0064, 0x00c18400, 0x00ffffff}, + {0x0068, 0x17078200, 0xffffff00}, + {0x006c, 0x00000014, 0x000000ff}, + {0x0078, 0x0000c000, 0x0000ff00}, + {0x0000, 0x00000003, 0x000000ff}, +}; + +static struct serdes_cfg cfg_phyb_10p3125g_156p25mhz_cmu1[] = { + {0x0c00, 0x00030002, 0x00ff00ff}, + {0x0c14, 0x00005252, 0x0000ffff}, + {0x0c28, 0x80000000, 0xff000000}, + {0x0c2c, 0x000000f6, 0x000000ff}, + {0x0c3c, 0x04000405, 0xff00ffff}, + {0x0c40, 0xc0800000, 0xffff0000}, + {0x0c44, 0x5a202062, 0xffffffff}, + {0x0c48, 0x40040424, 0xffffffff}, + {0x0c4c, 0x00004002, 0x0000ffff}, + {0x0c50, 0x19001c00, 0xff00ff00}, + {0x0c54, 0x00002100, 0x0000ff00}, + {0x0c58, 0x00000060, 0x000000ff}, + {0x0c60, 0x80131e7c, 0xffffffff}, + {0x0c64, 0x8400cb02, 0xff00ffff}, + {0x0c68, 0x17078200, 0xffffff00}, + {0x0c6c, 0x00000016, 0x000000ff}, + {0x0c74, 0x00000400, 0x0000ff00}, + {0x0c78, 0x0000c000, 0x0000ff00}, + {0x0c00, 0x00000003, 0x000000ff}, +}; + +static struct serdes_cfg cfg_phyb_10p3125g_16bit_lane[] = { + {0x0204, 0x00000080, 0x000000ff}, + {0x0208, 0x0000920d, 0x0000ffff}, + {0x0204, 0xfc000000, 0xff000000}, + {0x0208, 0x00009104, 0x0000ffff}, + {0x0210, 0x1a000000, 0xff000000}, + {0x0214, 0x00006b58, 0x00ffffff}, + {0x0218, 0x75800084, 0xffff00ff}, + {0x022c, 0x00300000, 0x00ff0000}, + {0x0230, 0x00003800, 0x0000ff00}, + {0x024c, 0x008f0000, 0x00ff0000}, + {0x0250, 0x30000000, 0xff000000}, + {0x0260, 0x00000002, 0x000000ff}, + {0x0264, 0x00000057, 0x000000ff}, + {0x0268, 0x00575700, 0x00ffff00}, + {0x0278, 0xff000000, 0xff000000}, + {0x0280, 0x00500050, 0x00ff00ff}, + {0x0284, 0x00001f15, 0x0000ffff}, + {0x028c, 0x00006f00, 0x0000ff00}, + {0x0294, 0x00000000, 0xffffff00}, + {0x0298, 0x00002640, 0xff00ffff}, + {0x029c, 0x00000003, 0x000000ff}, + {0x02a4, 0x00000f13, 0x0000ffff}, + {0x02a8, 0x0001b600, 0x00ffff00}, + {0x0380, 0x00000030, 0x000000ff}, + {0x03c0, 0x00000200, 0x0000ff00}, + {0x03cc, 0x00000018, 0x000000ff}, + {0x03cc, 0x00000000, 0x000000ff}, +}; + +static struct serdes_cfg cfg_phyb_10p3125g_comlane[] = { + {0x0a00, 0x00000800, 0x0000ff00}, + {0x0a84, 0x00000000, 0x000000ff}, + {0x0a8c, 0x00130000, 0x00ff0000}, + {0x0a90, 0x77a00000, 0xffff0000}, + {0x0a94, 0x00007777, 0x0000ffff}, + {0x0b08, 0x000f0000, 0xffff0000}, + {0x0b0c, 0x000f0000, 0x00ffffff}, + {0x0b10, 0xbe000000, 0xff000000}, + {0x0b14, 0x000000ff, 0x000000ff}, + {0x0b18, 0x00000014, 0x000000ff}, + {0x0b5c, 0x981b0000, 0xffff0000}, + {0x0b64, 0x00001100, 0x0000ff00}, + {0x0b78, 0x00000c00, 0x0000ff00}, + {0x0abc, 0xff000000, 0xff000000}, + {0x0ac0, 0x0000008b, 0x000000ff}, +}; + +static struct serdes_cfg cfg_cm_c1_c2[] = { + {0x0208, 0x00000000, 0x00000f00}, + {0x0208, 0x00000000, 0x0000001f}, + {0x0204, 0x00000000, 0x00040000}, + {0x0208, 0x000000a0, 0x000000e0}, +}; + +static void netcp_xgbe_serdes_cmu_init(void __iomem *serdes_regs) +{ + int i; + + /* cmu0 setup */ + for (i = 0; i < ARRAY_SIZE(cfg_phyb_1p25g_156p25mhz_cmu0); i++) { + reg_rmw(serdes_regs + cfg_phyb_1p25g_156p25mhz_cmu0[i].ofs, + cfg_phyb_1p25g_156p25mhz_cmu0[i].val, + cfg_phyb_1p25g_156p25mhz_cmu0[i].mask); + } + + /* cmu1 setup */ + for (i = 0; i < ARRAY_SIZE(cfg_phyb_10p3125g_156p25mhz_cmu1); i++) { + reg_rmw(serdes_regs + cfg_phyb_10p3125g_156p25mhz_cmu1[i].ofs, + cfg_phyb_10p3125g_156p25mhz_cmu1[i].val, + cfg_phyb_10p3125g_156p25mhz_cmu1[i].mask); + } +} + +/* lane is 0 based */ +static void netcp_xgbe_serdes_lane_config( + void __iomem *serdes_regs, int lane) +{ + int i; + + /* lane setup */ + for (i = 0; i < ARRAY_SIZE(cfg_phyb_10p3125g_16bit_lane); i++) { + reg_rmw(serdes_regs + + cfg_phyb_10p3125g_16bit_lane[i].ofs + + (0x200 * lane), + cfg_phyb_10p3125g_16bit_lane[i].val, + cfg_phyb_10p3125g_16bit_lane[i].mask); + } + + /* disable auto negotiation*/ + reg_rmw(serdes_regs + (0x200 * lane) + 0x0380, + 0x00000000, 0x00000010); + + /* disable link training */ + reg_rmw(serdes_regs + (0x200 * lane) + 0x03c0, + 0x00000000, 0x00000200); +} + +static void netcp_xgbe_serdes_com_enable(void __iomem *serdes_regs) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cfg_phyb_10p3125g_comlane); i++) { + reg_rmw(serdes_regs + cfg_phyb_10p3125g_comlane[i].ofs, + cfg_phyb_10p3125g_comlane[i].val, + cfg_phyb_10p3125g_comlane[i].mask); + } +} + +static void netcp_xgbe_serdes_lane_enable( + void __iomem *serdes_regs, int lane) +{ + /* Set Lane Control Rate */ + writel(0xe0e9e038, serdes_regs + 0x1fe0 + (4 * lane)); +} + +static void netcp_xgbe_serdes_phyb_rst_clr(void __iomem *serdes_regs) +{ + reg_rmw(serdes_regs + 0x0a00, 0x0000001f, 0x000000ff); +} + +static void netcp_xgbe_serdes_pll_disable(void __iomem *serdes_regs) +{ + writel(0x88000000, serdes_regs + 0x1ff4); +} + +static void netcp_xgbe_serdes_pll_enable(void __iomem *serdes_regs) +{ + netcp_xgbe_serdes_phyb_rst_clr(serdes_regs); + writel(0xee000000, serdes_regs + 0x1ff4); +} + +static int netcp_xgbe_wait_pll_locked(void __iomem *sw_regs) +{ + unsigned long timeout; + int ret = 0; + u32 val_1, val_0; + + timeout = jiffies + msecs_to_jiffies(500); + do { + val_0 = (readl(sw_regs + XGBE_SGMII_1_OFFSET) & BIT(4)); + val_1 = (readl(sw_regs + XGBE_SGMII_2_OFFSET) & BIT(4)); + + if (val_1 && val_0) + return 0; + + if (time_after(jiffies, timeout)) { + ret = -ETIMEDOUT; + break; + } + + cpu_relax(); + } while (true); + + pr_err("XGBE serdes not locked: time out.\n"); + return ret; +} + +static void netcp_xgbe_serdes_enable_xgmii_port(void __iomem *sw_regs) +{ + writel(0x03, sw_regs + XGBE_CTRL_OFFSET); +} + +static u32 netcp_xgbe_serdes_read_tbus_val(void __iomem *serdes_regs) +{ + u32 tmp; + + if (PHY_A(serdes_regs)) { + tmp = (readl(serdes_regs + 0x0ec) >> 24) & 0x0ff; + tmp |= ((readl(serdes_regs + 0x0fc) >> 16) & 0x00f00); + } else { + tmp = (readl(serdes_regs + 0x0f8) >> 16) & 0x0fff; + } + + return tmp; +} + +static void netcp_xgbe_serdes_write_tbus_addr(void __iomem *serdes_regs, + int select, int ofs) +{ + if (PHY_A(serdes_regs)) { + reg_rmw(serdes_regs + 0x0008, ((select << 5) + ofs) << 24, + ~0x00ffffff); + return; + } + + /* For 2 lane Phy-B, lane0 is actually lane1 */ + switch (select) { + case 1: + select = 2; + break; + case 2: + select = 3; + break; + default: + return; + } + + reg_rmw(serdes_regs + 0x00fc, ((select << 8) + ofs) << 16, ~0xf800ffff); +} + +static u32 netcp_xgbe_serdes_read_select_tbus(void __iomem *serdes_regs, + int select, int ofs) +{ + /* Set tbus address */ + netcp_xgbe_serdes_write_tbus_addr(serdes_regs, select, ofs); + /* Get TBUS Value */ + return netcp_xgbe_serdes_read_tbus_val(serdes_regs); +} + +static void netcp_xgbe_serdes_reset_cdr(void __iomem *serdes_regs, + void __iomem *sig_detect_reg, int lane) +{ + u32 tmp, dlpf, tbus; + + /*Get the DLPF values */ + tmp = netcp_xgbe_serdes_read_select_tbus( + serdes_regs, lane + 1, 5); + + dlpf = tmp >> 2; + + if (dlpf < 400 || dlpf > 700) { + reg_rmw(sig_detect_reg, VAL_SH(2, 1), MASK_WID_SH(2, 1)); + mdelay(1); + reg_rmw(sig_detect_reg, VAL_SH(0, 1), MASK_WID_SH(2, 1)); + } else { + tbus = netcp_xgbe_serdes_read_select_tbus(serdes_regs, lane + + 1, 0xe); + + pr_debug("XGBE: CDR centered, DLPF: %4d,%d,%d.\n", + tmp >> 2, tmp & 3, (tbus >> 2) & 3); + } +} + +/* Call every 100 ms */ +static int netcp_xgbe_check_link_status(void __iomem *serdes_regs, + void __iomem *sw_regs, u32 lanes, + u32 *current_state, u32 *lane_down) +{ + void __iomem *pcsr_base = sw_regs + 0x0600; + void __iomem *sig_detect_reg; + u32 pcsr_rx_stat, blk_lock, blk_errs; + int loss, i, status = 1; + + for (i = 0; i < lanes; i++) { + /* Get the Loss bit */ + loss = readl(serdes_regs + 0x1fc0 + 0x20 + (i * 0x04)) & 0x1; + + /* Get Block Errors and Block Lock bits */ + pcsr_rx_stat = readl(pcsr_base + 0x0c + (i * 0x80)); + blk_lock = (pcsr_rx_stat >> 30) & 0x1; + blk_errs = (pcsr_rx_stat >> 16) & 0x0ff; + + /* Get Signal Detect Overlay Address */ + sig_detect_reg = serdes_regs + (i * 0x200) + 0x200 + 0x04; + + /* If Block errors maxed out, attempt recovery! */ + if (blk_errs == 0x0ff) + blk_lock = 0; + + switch (current_state[i]) { + case 0: + /* if good link lock the signal detect ON! */ + if (!loss && blk_lock) { + pr_debug("XGBE PCSR Linked Lane: %d\n", i); + reg_rmw(sig_detect_reg, VAL_SH(3, 1), + MASK_WID_SH(2, 1)); + current_state[i] = 1; + } else if (!blk_lock) { + /* if no lock, then reset CDR */ + pr_debug("XGBE PCSR Recover Lane: %d\n", i); + netcp_xgbe_serdes_reset_cdr(serdes_regs, + sig_detect_reg, i); + } + break; + + case 1: + if (!blk_lock) { + /* Link Lost? */ + lane_down[i] = 1; + current_state[i] = 2; + } + break; + + case 2: + if (blk_lock) + /* Nope just noise */ + current_state[i] = 1; + else { + /* Lost the block lock, reset CDR if it is + * not centered and go back to sync state + */ + netcp_xgbe_serdes_reset_cdr(serdes_regs, + sig_detect_reg, i); + current_state[i] = 0; + } + break; + + default: + pr_err("XGBE: unknown current_state[%d] %d\n", + i, current_state[i]); + break; + } + + if (blk_errs > 0) { + /* Reset the Error counts! */ + reg_rmw(pcsr_base + 0x08 + (i * 0x80), VAL_SH(0x19, 0), + MASK_WID_SH(8, 0)); + + reg_rmw(pcsr_base + 0x08 + (i * 0x80), VAL_SH(0x00, 0), + MASK_WID_SH(8, 0)); + } + + status &= (current_state[i] == 1); + } + + return status; +} + +static int netcp_xgbe_serdes_check_lane(void __iomem *serdes_regs, + void __iomem *sw_regs) +{ + u32 current_state[2] = {0, 0}; + int retries = 0, link_up; + u32 lane_down[2]; + + do { + lane_down[0] = 0; + lane_down[1] = 0; + + link_up = netcp_xgbe_check_link_status(serdes_regs, sw_regs, 2, + current_state, + lane_down); + + /* if we did not get link up then wait 100ms before calling + * it again + */ + if (link_up) + break; + + if (lane_down[0]) + pr_debug("XGBE: detected link down on lane 0\n"); + + if (lane_down[1]) + pr_debug("XGBE: detected link down on lane 1\n"); + + if (++retries > 1) { + pr_debug("XGBE: timeout waiting for serdes link up\n"); + return -ETIMEDOUT; + } + mdelay(100); + } while (!link_up); + + pr_debug("XGBE: PCSR link is up\n"); + return 0; +} + +static void netcp_xgbe_serdes_setup_cm_c1_c2(void __iomem *serdes_regs, + int lane, int cm, int c1, int c2) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cfg_cm_c1_c2); i++) { + reg_rmw(serdes_regs + cfg_cm_c1_c2[i].ofs + (0x200 * lane), + cfg_cm_c1_c2[i].val, + cfg_cm_c1_c2[i].mask); + } +} + +static void netcp_xgbe_reset_serdes(void __iomem *serdes_regs) +{ + /* Toggle the POR_EN bit in CONFIG.CPU_CTRL */ + /* enable POR_EN bit */ + reg_rmw(serdes_regs + PCSR_CPU_CTRL_OFFSET, POR_EN, POR_EN); + usleep_range(10, 100); + + /* disable POR_EN bit */ + reg_rmw(serdes_regs + PCSR_CPU_CTRL_OFFSET, 0, POR_EN); + usleep_range(10, 100); +} + +static int netcp_xgbe_serdes_config(void __iomem *serdes_regs, + void __iomem *sw_regs) +{ + u32 ret, i; + + netcp_xgbe_serdes_pll_disable(serdes_regs); + netcp_xgbe_serdes_cmu_init(serdes_regs); + + for (i = 0; i < 2; i++) + netcp_xgbe_serdes_lane_config(serdes_regs, i); + + netcp_xgbe_serdes_com_enable(serdes_regs); + /* This is EVM + RTM-BOC specific */ + for (i = 0; i < 2; i++) + netcp_xgbe_serdes_setup_cm_c1_c2(serdes_regs, i, 0, 0, 5); + + netcp_xgbe_serdes_pll_enable(serdes_regs); + for (i = 0; i < 2; i++) + netcp_xgbe_serdes_lane_enable(serdes_regs, i); + + /* SB PLL Status Poll */ + ret = netcp_xgbe_wait_pll_locked(sw_regs); + if (ret) + return ret; + + netcp_xgbe_serdes_enable_xgmii_port(sw_regs); + netcp_xgbe_serdes_check_lane(serdes_regs, sw_regs); + return ret; +} + +int netcp_xgbe_serdes_init(void __iomem *serdes_regs, void __iomem *xgbe_regs) +{ + u32 val; + + /* read COMLANE bits 4:0 */ + val = readl(serdes_regs + 0xa00); + if (val & 0x1f) { + pr_debug("XGBE: serdes already in operation - reset\n"); + netcp_xgbe_reset_serdes(serdes_regs); + } + return netcp_xgbe_serdes_config(serdes_regs, xgbe_regs); +} diff --git a/drivers/net/ethernet/ti/tlan.c b/drivers/net/ethernet/ti/tlan.c index f2ff0074aac9..691ec936e88d 100644 --- a/drivers/net/ethernet/ti/tlan.c +++ b/drivers/net/ethernet/ti/tlan.c @@ -2540,7 +2540,7 @@ static void tlan_phy_power_down(struct net_device *dev) * This is abitrary. It is intended to make sure the * transceiver settles. */ - tlan_set_timer(dev, (HZ/20), TLAN_TIMER_PHY_PUP); + tlan_set_timer(dev, msecs_to_jiffies(50), TLAN_TIMER_PHY_PUP); } @@ -2561,7 +2561,7 @@ static void tlan_phy_power_up(struct net_device *dev) * transceiver. The TLAN docs say both 50 ms and * 500 ms, so do the longer, just in case. */ - tlan_set_timer(dev, (HZ/20), TLAN_TIMER_PHY_RESET); + tlan_set_timer(dev, msecs_to_jiffies(500), TLAN_TIMER_PHY_RESET); } @@ -2593,7 +2593,7 @@ static void tlan_phy_reset(struct net_device *dev) * I don't remember why I wait this long. * I've changed this to 50ms, as it seems long enough. */ - tlan_set_timer(dev, (HZ/20), TLAN_TIMER_PHY_START_LINK); + tlan_set_timer(dev, msecs_to_jiffies(50), TLAN_TIMER_PHY_START_LINK); } @@ -2658,7 +2658,7 @@ static void tlan_phy_start_link(struct net_device *dev) data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN; tlan_dio_write16(dev->base_addr, TLAN_NET_CONFIG, data); - tlan_set_timer(dev, (40*HZ/1000), TLAN_TIMER_PHY_PDOWN); + tlan_set_timer(dev, msecs_to_jiffies(40), TLAN_TIMER_PHY_PDOWN); return; } else if (priv->phy_num == 0) { control = 0; @@ -2725,7 +2725,7 @@ static void tlan_phy_finish_auto_neg(struct net_device *dev) (priv->adapter->flags & TLAN_ADAPTER_USE_INTERN_10) && (priv->phy_num != 0)) { priv->phy_num = 0; - tlan_set_timer(dev, (400*HZ/1000), TLAN_TIMER_PHY_PDOWN); + tlan_set_timer(dev, msecs_to_jiffies(400), TLAN_TIMER_PHY_PDOWN); return; } @@ -2744,7 +2744,7 @@ static void tlan_phy_finish_auto_neg(struct net_device *dev) /* Wait for 100 ms. No reason in partiticular. */ - tlan_set_timer(dev, (HZ/10), TLAN_TIMER_FINISH_RESET); + tlan_set_timer(dev, msecs_to_jiffies(100), TLAN_TIMER_FINISH_RESET); } @@ -2796,7 +2796,7 @@ static void tlan_phy_monitor(unsigned long data) /* set to external PHY */ priv->phy_num = 1; /* restart autonegotiation */ - tlan_set_timer(dev, 4 * HZ / 10, + tlan_set_timer(dev, msecs_to_jiffies(400), TLAN_TIMER_PHY_PDOWN); return; } diff --git a/drivers/net/ethernet/via/via-rhine.c b/drivers/net/ethernet/via/via-rhine.c index a191afc23b56..17e276651601 100644 --- a/drivers/net/ethernet/via/via-rhine.c +++ b/drivers/net/ethernet/via/via-rhine.c @@ -1326,7 +1326,8 @@ static void rhine_check_media(struct net_device *dev, unsigned int init_media) struct rhine_private *rp = netdev_priv(dev); void __iomem *ioaddr = rp->base; - mii_check_media(&rp->mii_if, netif_msg_link(rp), init_media); + if (!rp->mii_if.force_media) + mii_check_media(&rp->mii_if, netif_msg_link(rp), init_media); if (rp->mii_if.full_duplex) iowrite8(ioread8(ioaddr + ChipCmd1) | Cmd1FDuplex, @@ -1781,8 +1782,8 @@ static netdev_tx_t rhine_start_tx(struct sk_buff *skb, rp->tx_ring[entry].desc_length = cpu_to_le32(TXDESC | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN)); - if (unlikely(vlan_tx_tag_present(skb))) { - u16 vid_pcp = vlan_tx_tag_get(skb); + if (unlikely(skb_vlan_tag_present(skb))) { + u16 vid_pcp = skb_vlan_tag_get(skb); /* drop CFI/DEI bit, register needs VID and PCP */ vid_pcp = (vid_pcp & VLAN_VID_MASK) | @@ -1803,7 +1804,7 @@ static netdev_tx_t rhine_start_tx(struct sk_buff *skb, /* Non-x86 Todo: explicitly flush cache lines here. */ - if (vlan_tx_tag_present(skb)) + if (skb_vlan_tag_present(skb)) /* Tx queues are bits 7-0 (first Tx queue: bit 7) */ BYTE_REG_BITS_ON(1 << 7, ioaddr + TQWake); diff --git a/drivers/net/ethernet/via/via-velocity.c b/drivers/net/ethernet/via/via-velocity.c index 282f83a63b67..c20206f83cc1 100644 --- a/drivers/net/ethernet/via/via-velocity.c +++ b/drivers/net/ethernet/via/via-velocity.c @@ -2611,8 +2611,8 @@ static netdev_tx_t velocity_xmit(struct sk_buff *skb, td_ptr->tdesc1.cmd = TCPLS_NORMAL + (tdinfo->nskb_dma + 1) * 16; - if (vlan_tx_tag_present(skb)) { - td_ptr->tdesc1.vlan = cpu_to_le16(vlan_tx_tag_get(skb)); + if (skb_vlan_tag_present(skb)) { + td_ptr->tdesc1.vlan = cpu_to_le16(skb_vlan_tag_get(skb)); td_ptr->tdesc1.TCR |= TCR0_VETAG; } diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index 9c2d91ea0af4..dbcbf0c5bcfa 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -1043,6 +1043,7 @@ static int temac_of_probe(struct platform_device *op) lp->regs = of_iomap(op->dev.of_node, 0); if (!lp->regs) { dev_err(&op->dev, "could not map temac regs.\n"); + rc = -ENOMEM; goto nodev; } @@ -1062,6 +1063,7 @@ static int temac_of_probe(struct platform_device *op) np = of_parse_phandle(op->dev.of_node, "llink-connected", 0); if (!np) { dev_err(&op->dev, "could not find DMA node\n"); + rc = -ENODEV; goto err_iounmap; } diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index c18a0c637c44..a6d2860b712c 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -1501,6 +1501,7 @@ static int axienet_of_probe(struct platform_device *op) lp->regs = of_iomap(op->dev.of_node, 0); if (!lp->regs) { dev_err(&op->dev, "could not map Axi Ethernet regs.\n"); + ret = -ENOMEM; goto nodev; } /* Setup checksum offload, but default to off if not specified */ @@ -1563,6 +1564,7 @@ static int axienet_of_probe(struct platform_device *op) np = of_parse_phandle(op->dev.of_node, "axistream-connected", 0); if (!np) { dev_err(&op->dev, "could not find DMA node\n"); + ret = -ENODEV; goto err_iounmap; } lp->dma_regs = of_iomap(np, 0); diff --git a/drivers/net/ethernet/xilinx/xilinx_emaclite.c b/drivers/net/ethernet/xilinx/xilinx_emaclite.c index 24858799c204..9d4ce388510a 100644 --- a/drivers/net/ethernet/xilinx/xilinx_emaclite.c +++ b/drivers/net/ethernet/xilinx/xilinx_emaclite.c @@ -1109,6 +1109,7 @@ static int xemaclite_of_probe(struct platform_device *ofdev) res = platform_get_resource(ofdev, IORESOURCE_IRQ, 0); if (!res) { dev_err(dev, "no IRQ found\n"); + rc = -ENXIO; goto error; } diff --git a/drivers/net/fddi/skfp/smt.c b/drivers/net/fddi/skfp/smt.c index 9edada85ed02..cd78b7cacc75 100644 --- a/drivers/net/fddi/skfp/smt.c +++ b/drivers/net/fddi/skfp/smt.c @@ -1736,18 +1736,6 @@ char *addr_to_string(struct fddi_addr *addr) } #endif -#ifdef AM29K -int smt_ifconfig(int argc, char *argv[]) -{ - if (argc >= 2 && !strcmp(argv[0],"opt_bypass") && - !strcmp(argv[1],"yes")) { - smc->mib.fddiSMTBypassPresent = 1 ; - return 0; - } - return amdfddi_config(0, argc, argv); -} -#endif - /* * return static mac index */ diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 9f49c0129a78..208eb05446ba 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -217,7 +217,7 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device) static int netvsc_init_buf(struct hv_device *device) { int ret = 0; - int t; + unsigned long t; struct netvsc_device *net_device; struct nvsp_message *init_packet; struct net_device *ndev; @@ -409,7 +409,8 @@ static int negotiate_nvsp_ver(struct hv_device *device, struct nvsp_message *init_packet, u32 nvsp_ver) { - int ret, t; + int ret; + unsigned long t; memset(init_packet, 0, sizeof(struct nvsp_message)); init_packet->hdr.msg_type = NVSP_MSG_TYPE_INIT; @@ -684,9 +685,9 @@ static u32 netvsc_get_next_send_section(struct netvsc_device *net_device) return ret_val; } -u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device, - unsigned int section_index, - struct hv_netvsc_packet *packet) +static u32 netvsc_copy_to_send_buf(struct netvsc_device *net_device, + unsigned int section_index, + struct hv_netvsc_packet *packet) { char *start = net_device->send_buf; char *dest = (start + (section_index * net_device->send_section_size)); @@ -716,7 +717,7 @@ int netvsc_send(struct hv_device *device, u64 req_id; unsigned int section_index = NETVSC_INVALID_INDEX; u32 msg_size = 0; - struct sk_buff *skb; + struct sk_buff *skb = NULL; u16 q_idx = packet->q_idx; @@ -743,8 +744,6 @@ int netvsc_send(struct hv_device *device, packet); skb = (struct sk_buff *) (unsigned long)packet->send_completion_tid; - if (skb) - dev_kfree_skb_any(skb); packet->page_buf_cnt = 0; } } @@ -810,6 +809,13 @@ int netvsc_send(struct hv_device *device, packet, ret); } + if (ret != 0) { + if (section_index != NETVSC_INVALID_INDEX) + netvsc_free_send_slot(net_device, section_index); + } else if (skb) { + dev_kfree_skb_any(skb); + } + return ret; } diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index ec0c40a8f653..7816d98bdddc 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -470,7 +470,7 @@ static int rndis_filter_query_device(struct rndis_device *dev, u32 oid, struct rndis_query_request *query; struct rndis_query_complete *query_complete; int ret = 0; - int t; + unsigned long t; if (!result) return -EINVAL; @@ -560,7 +560,8 @@ int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac) char macstr[2*ETH_ALEN+1]; u32 extlen = sizeof(struct rndis_config_parameter_info) + 2*NWADR_STRLEN + 4*ETH_ALEN; - int ret, t; + int ret; + unsigned long t; request = get_rndis_request(rdev, RNDIS_MSG_SET, RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen); @@ -623,7 +624,8 @@ cleanup: return ret; } -int rndis_filter_set_offload_params(struct hv_device *hdev, +static int +rndis_filter_set_offload_params(struct hv_device *hdev, struct ndis_offload_params *req_offloads) { struct netvsc_device *nvdev = hv_get_drvdata(hdev); @@ -634,7 +636,8 @@ int rndis_filter_set_offload_params(struct hv_device *hdev, struct ndis_offload_params *offload_params; struct rndis_set_complete *set_complete; u32 extlen = sizeof(struct ndis_offload_params); - int ret, t; + int ret; + unsigned long t; u32 vsp_version = nvdev->nvsp_version; if (vsp_version <= NVSP_PROTOCOL_VERSION_4) { @@ -697,7 +700,7 @@ u8 netvsc_hash_key[HASH_KEYLEN] = { 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa }; -int rndis_filter_set_rss_param(struct rndis_device *rdev, int num_queue) +static int rndis_filter_set_rss_param(struct rndis_device *rdev, int num_queue) { struct net_device *ndev = rdev->net_dev->ndev; struct rndis_request *request; @@ -708,7 +711,8 @@ int rndis_filter_set_rss_param(struct rndis_device *rdev, int num_queue) struct ndis_recv_scale_param *rssp; u32 *itab; u8 *keyp; - int i, t, ret; + int i, ret; + unsigned long t; request = get_rndis_request( rdev, RNDIS_MSG_SET, @@ -792,7 +796,8 @@ int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter) struct rndis_set_request *set; struct rndis_set_complete *set_complete; u32 status; - int ret, t; + int ret; + unsigned long t; struct net_device *ndev; ndev = dev->net_dev->ndev; @@ -848,7 +853,8 @@ static int rndis_filter_init_device(struct rndis_device *dev) struct rndis_initialize_request *init; struct rndis_initialize_complete *init_complete; u32 status; - int ret, t; + int ret; + unsigned long t; request = get_rndis_request(dev, RNDIS_MSG_INIT, RNDIS_MESSAGE_SIZE(struct rndis_initialize_request)); @@ -998,7 +1004,7 @@ int rndis_filter_device_add(struct hv_device *dev, struct netvsc_device_info *device_info = additional_info; struct ndis_offload_params offloads; struct nvsp_message *init_packet; - int t; + unsigned long t; struct ndis_recv_scale_cap rsscap; u32 rsscap_size = sizeof(struct ndis_recv_scale_cap); u32 mtu, size; diff --git a/drivers/net/ieee802154/at86rf230.c b/drivers/net/ieee802154/at86rf230.c index 1c0135620c62..7b051eacb7f1 100644 --- a/drivers/net/ieee802154/at86rf230.c +++ b/drivers/net/ieee802154/at86rf230.c @@ -427,7 +427,7 @@ at86rf230_reg_precious(struct device *dev, unsigned int reg) } } -static struct regmap_config at86rf230_regmap_spi_config = { +static const struct regmap_config at86rf230_regmap_spi_config = { .reg_bits = 8, .val_bits = 8, .write_flag_mask = CMD_REG | CMD_WRITE, @@ -450,7 +450,7 @@ at86rf230_async_error_recover(void *context) ieee802154_wake_queue(lp->hw); } -static void +static inline void at86rf230_async_error(struct at86rf230_local *lp, struct at86rf230_state_change *ctx, int rc) { @@ -524,7 +524,6 @@ at86rf230_async_state_assert(void *context) } } - dev_warn(&lp->spi->dev, "unexcept state change from 0x%02x to 0x%02x. Actual state: 0x%02x\n", ctx->from_state, ctx->to_state, trx_state); } @@ -655,7 +654,7 @@ at86rf230_async_state_change_start(void *context) if (ctx->irq_enable) enable_irq(lp->spi->irq); - at86rf230_async_error(lp, &lp->state, rc); + at86rf230_async_error(lp, ctx, rc); } } @@ -715,10 +714,7 @@ at86rf230_tx_complete(void *context) enable_irq(lp->spi->irq); - if (lp->max_frame_retries <= 0) - ieee802154_xmit_complete(lp->hw, skb, true); - else - ieee802154_xmit_complete(lp->hw, skb, false); + ieee802154_xmit_complete(lp->hw, skb, !lp->tx_aret); } static void @@ -753,16 +749,13 @@ at86rf230_tx_trac_check(void *context) * to STATE_FORCE_TRX_OFF then STATE_TX_ON to recover the transceiver * state to TX_ON. */ - if (trac) { + if (trac) at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF, at86rf230_tx_trac_error, true); - return; - } - - at86rf230_tx_on(context); + else + at86rf230_tx_on(context); } - static void at86rf230_tx_trac_status(void *context) { @@ -1082,7 +1075,7 @@ at86rf230_set_hw_addr_filt(struct ieee802154_hw *hw, u16 addr = le16_to_cpu(filt->short_addr); dev_vdbg(&lp->spi->dev, - "at86rf230_set_hw_addr_filt called for saddr\n"); + "at86rf230_set_hw_addr_filt called for saddr\n"); __at86rf230_write(lp, RG_SHORT_ADDR_0, addr); __at86rf230_write(lp, RG_SHORT_ADDR_1, addr >> 8); } @@ -1091,7 +1084,7 @@ at86rf230_set_hw_addr_filt(struct ieee802154_hw *hw, u16 pan = le16_to_cpu(filt->pan_id); dev_vdbg(&lp->spi->dev, - "at86rf230_set_hw_addr_filt called for pan id\n"); + "at86rf230_set_hw_addr_filt called for pan id\n"); __at86rf230_write(lp, RG_PAN_ID_0, pan); __at86rf230_write(lp, RG_PAN_ID_1, pan >> 8); } @@ -1101,14 +1094,14 @@ at86rf230_set_hw_addr_filt(struct ieee802154_hw *hw, memcpy(addr, &filt->ieee_addr, 8); dev_vdbg(&lp->spi->dev, - "at86rf230_set_hw_addr_filt called for IEEE addr\n"); + "at86rf230_set_hw_addr_filt called for IEEE addr\n"); for (i = 0; i < 8; i++) __at86rf230_write(lp, RG_IEEE_ADDR_0 + i, addr[i]); } if (changed & IEEE802154_AFILT_PANC_CHANGED) { dev_vdbg(&lp->spi->dev, - "at86rf230_set_hw_addr_filt called for panc change\n"); + "at86rf230_set_hw_addr_filt called for panc change\n"); if (filt->pan_coord) at86rf230_write_subreg(lp, SR_AACK_I_AM_COORD, 1); else @@ -1146,11 +1139,37 @@ at86rf230_set_lbt(struct ieee802154_hw *hw, bool on) } static int -at86rf230_set_cca_mode(struct ieee802154_hw *hw, u8 mode) +at86rf230_set_cca_mode(struct ieee802154_hw *hw, + const struct wpan_phy_cca *cca) { struct at86rf230_local *lp = hw->priv; + u8 val; + + /* mapping 802.15.4 to driver spec */ + switch (cca->mode) { + case NL802154_CCA_ENERGY: + val = 1; + break; + case NL802154_CCA_CARRIER: + val = 2; + break; + case NL802154_CCA_ENERGY_CARRIER: + switch (cca->opt) { + case NL802154_CCA_OPT_ENERGY_CARRIER_AND: + val = 3; + break; + case NL802154_CCA_OPT_ENERGY_CARRIER_OR: + val = 0; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } - return at86rf230_write_subreg(lp, SR_CCA_MODE, mode); + return at86rf230_write_subreg(lp, SR_CCA_MODE, val); } static int @@ -1400,7 +1419,7 @@ at86rf230_detect_device(struct at86rf230_local *lp) if (rc) return rc; - rc = __at86rf230_read(lp, RG_PART_NUM, &version); + rc = __at86rf230_read(lp, RG_VERSION_NUM, &version); if (rc) return rc; @@ -1410,11 +1429,12 @@ at86rf230_detect_device(struct at86rf230_local *lp) return -EINVAL; } - lp->hw->extra_tx_headroom = 0; lp->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AACK | IEEE802154_HW_TXPOWER | IEEE802154_HW_ARET | IEEE802154_HW_AFILT | IEEE802154_HW_PROMISCUOUS; + lp->hw->phy->cca.mode = NL802154_CCA_ENERGY; + switch (part) { case 2: chip = "at86rf230"; @@ -1429,16 +1449,12 @@ at86rf230_detect_device(struct at86rf230_local *lp) break; case 7: chip = "at86rf212"; - if (version == 1) { - lp->data = &at86rf212_data; - lp->hw->flags |= IEEE802154_HW_LBT; - lp->hw->phy->channels_supported[0] = 0x00007FF; - lp->hw->phy->channels_supported[2] = 0x00007FF; - lp->hw->phy->current_channel = 5; - lp->hw->phy->symbol_duration = 25; - } else { - rc = -ENOTSUPP; - } + lp->data = &at86rf212_data; + lp->hw->flags |= IEEE802154_HW_LBT; + lp->hw->phy->channels_supported[0] = 0x00007FF; + lp->hw->phy->channels_supported[2] = 0x00007FF; + lp->hw->phy->current_channel = 5; + lp->hw->phy->symbol_duration = 25; break; case 11: chip = "at86rf233"; @@ -1448,7 +1464,7 @@ at86rf230_detect_device(struct at86rf230_local *lp) lp->hw->phy->symbol_duration = 16; break; default: - chip = "unkown"; + chip = "unknown"; rc = -ENOTSUPP; break; } diff --git a/drivers/net/ieee802154/cc2520.c b/drivers/net/ieee802154/cc2520.c index f9df9fa86d5f..181b349b060e 100644 --- a/drivers/net/ieee802154/cc2520.c +++ b/drivers/net/ieee802154/cc2520.c @@ -19,7 +19,6 @@ #include <linux/workqueue.h> #include <linux/interrupt.h> #include <linux/skbuff.h> -#include <linux/pinctrl/consumer.h> #include <linux/of_gpio.h> #include <linux/ieee802154.h> @@ -45,9 +44,9 @@ #define CC2520_FREG_MASK 0x3F /* status byte values */ -#define CC2520_STATUS_XOSC32M_STABLE (1 << 7) -#define CC2520_STATUS_RSSI_VALID (1 << 6) -#define CC2520_STATUS_TX_UNDERFLOW (1 << 3) +#define CC2520_STATUS_XOSC32M_STABLE BIT(7) +#define CC2520_STATUS_RSSI_VALID BIT(6) +#define CC2520_STATUS_TX_UNDERFLOW BIT(3) /* IEEE-802.15.4 defined constants (2.4 GHz logical channels) */ #define CC2520_MINCHANNEL 11 @@ -513,7 +512,6 @@ err_tx: return rc; } - static int cc2520_rx(struct cc2520_private *priv) { u8 len = 0, lqi = 0, bytes = 1; @@ -551,14 +549,14 @@ cc2520_ed(struct ieee802154_hw *hw, u8 *level) u8 rssi; int ret; - ret = cc2520_read_register(priv , CC2520_RSSISTAT, &status); + ret = cc2520_read_register(priv, CC2520_RSSISTAT, &status); if (ret) return ret; if (status != RSSI_VALID) return -EINVAL; - ret = cc2520_read_register(priv , CC2520_RSSI, &rssi); + ret = cc2520_read_register(priv, CC2520_RSSI, &rssi); if (ret) return ret; @@ -652,6 +650,7 @@ static int cc2520_register(struct cc2520_private *priv) priv->hw->parent = &priv->spi->dev; priv->hw->extra_tx_headroom = 0; priv->hw->vif_data_size = sizeof(*priv); + ieee802154_random_extended_addr(&priv->hw->phy->perm_extended_addr); /* We do support only 2.4 Ghz */ priv->hw->phy->channels_supported[0] = 0x7FFF800; @@ -842,24 +841,15 @@ done: static int cc2520_probe(struct spi_device *spi) { struct cc2520_private *priv; - struct pinctrl *pinctrl; struct cc2520_platform_data *pdata; int ret; - priv = devm_kzalloc(&spi->dev, - sizeof(struct cc2520_private), GFP_KERNEL); - if (!priv) { - ret = -ENOMEM; - goto err_ret; - } + priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; spi_set_drvdata(spi, priv); - pinctrl = devm_pinctrl_get_select_default(&spi->dev); - if (IS_ERR(pinctrl)) - dev_warn(&spi->dev, - "pinctrl pins are not configured\n"); - pdata = cc2520_get_platform_data(spi); if (!pdata) { dev_err(&spi->dev, "no platform data\n"); @@ -870,10 +860,8 @@ static int cc2520_probe(struct spi_device *spi) priv->buf = devm_kzalloc(&spi->dev, SPI_COMMAND_BUFFER, GFP_KERNEL); - if (!priv->buf) { - ret = -ENOMEM; - goto err_ret; - } + if (!priv->buf) + return -ENOMEM; mutex_init(&priv->buffer_mutex); INIT_WORK(&priv->fifop_irqwork, cc2520_fifop_irqwork); @@ -947,7 +935,6 @@ static int cc2520_probe(struct spi_device *spi) if (ret) goto err_hw_init; - gpio_set_value(pdata->vreg, HIGH); usleep_range(100, 150); @@ -991,8 +978,6 @@ static int cc2520_probe(struct spi_device *spi) err_hw_init: mutex_destroy(&priv->buffer_mutex); flush_work(&priv->fifop_irqwork); - -err_ret: return ret; } diff --git a/drivers/net/ieee802154/mrf24j40.c b/drivers/net/ieee802154/mrf24j40.c index a200fa16beae..fba2dfd910f7 100644 --- a/drivers/net/ieee802154/mrf24j40.c +++ b/drivers/net/ieee802154/mrf24j40.c @@ -289,7 +289,7 @@ static int mrf24j40_read_rx_buf(struct mrf24j40 *devrec, goto out; /* Range check the RX FIFO length, accounting for the one-byte - * length field at the begining. */ + * 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; @@ -323,7 +323,7 @@ static int mrf24j40_read_rx_buf(struct mrf24j40 *devrec, #ifdef DEBUG print_hex_dump(KERN_DEBUG, "mrf24j40 rx: ", - DUMP_PREFIX_OFFSET, 16, 1, data, *len, 0); + DUMP_PREFIX_OFFSET, 16, 1, data, *len, 0); pr_debug("mrf24j40 rx: lqi: %02hhx rssi: %02hhx\n", lqi_rssi[0], lqi_rssi[1]); #endif @@ -521,7 +521,7 @@ static int mrf24j40_filter(struct ieee802154_hw *hw, */ dev_dbg(printdev(devrec), "Set Pan Coord to %s\n", - filt->pan_coord ? "on" : "off"); + filt->pan_coord ? "on" : "off"); } return 0; diff --git a/drivers/net/ipvlan/ipvlan_core.c b/drivers/net/ipvlan/ipvlan_core.c index a14d87783245..2a175006028b 100644 --- a/drivers/net/ipvlan/ipvlan_core.c +++ b/drivers/net/ipvlan/ipvlan_core.c @@ -9,7 +9,7 @@ #include "ipvlan.h" -static u32 ipvlan_jhash_secret; +static u32 ipvlan_jhash_secret __read_mostly; void ipvlan_init_secret(void) { @@ -377,9 +377,11 @@ static int ipvlan_process_v6_outbound(struct sk_buff *skb) }; dst = ip6_route_output(dev_net(dev), NULL, &fl6); - if (IS_ERR(dst)) + if (dst->error) { + ret = dst->error; + dst_release(dst); goto err; - + } skb_dst_drop(skb); skb_dst_set(skb, dst); err = ip6_local_out(skb); diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c index 58f98f4de773..58ae11a14bb6 100644 --- a/drivers/net/irda/ali-ircc.c +++ b/drivers/net/irda/ali-ircc.c @@ -1462,17 +1462,12 @@ static netdev_tx_t ali_ircc_fir_hard_xmit(struct sk_buff *skb, if (mtt) { /* Check how much time we have used already */ - do_gettimeofday(&self->now); - - diff = self->now.tv_usec - self->stamp.tv_usec; + diff = ktime_us_delta(ktime_get(), self->stamp); /* self->stamp is set from ali_ircc_dma_receive_complete() */ pr_debug("%s(), ******* diff = %d *******\n", __func__, diff); - - if (diff < 0) - diff += 1000000; - + /* Check if the mtt is larger than the time we have * already used by all the protocol processing */ @@ -1884,7 +1879,7 @@ static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self) * reduce the min turn time a bit since we will know * how much time we have used for protocol processing */ - do_gettimeofday(&self->stamp); + self->stamp = ktime_get(); skb = dev_alloc_skb(len+1); if (skb == NULL) diff --git a/drivers/net/irda/ali-ircc.h b/drivers/net/irda/ali-ircc.h index 0c8edb41bd0a..c2d9747a5108 100644 --- a/drivers/net/irda/ali-ircc.h +++ b/drivers/net/irda/ali-ircc.h @@ -22,7 +22,7 @@ #ifndef ALI_IRCC_H #define ALI_IRCC_H -#include <linux/time.h> +#include <linux/ktime.h> #include <linux/spinlock.h> #include <linux/pm.h> @@ -209,8 +209,7 @@ struct ali_ircc_cb { unsigned char rcvFramesOverflow; - struct timeval stamp; - struct timeval now; + ktime_t stamp; spinlock_t lock; /* For serializing operations */ diff --git a/drivers/net/irda/au1k_ir.c b/drivers/net/irda/au1k_ir.c index e151205281e2..44e4f386a5dc 100644 --- a/drivers/net/irda/au1k_ir.c +++ b/drivers/net/irda/au1k_ir.c @@ -24,7 +24,6 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/slab.h> -#include <linux/time.h> #include <linux/types.h> #include <linux/ioport.h> @@ -163,8 +162,6 @@ struct au1k_private { iobuff_t rx_buff; struct net_device *netdev; - struct timeval stamp; - struct timeval now; struct qos_info qos; struct irlap_cb *irlap; diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c index 48b2f9a321b7..f6c916312577 100644 --- a/drivers/net/irda/irda-usb.c +++ b/drivers/net/irda/irda-usb.c @@ -495,18 +495,12 @@ static netdev_tx_t irda_usb_hard_xmit(struct sk_buff *skb, mtt = irda_get_mtt(skb); if (mtt) { int diff; - do_gettimeofday(&self->now); - diff = self->now.tv_usec - self->stamp.tv_usec; + diff = ktime_us_delta(ktime_get(), self->stamp); #ifdef IU_USB_MIN_RTT /* Factor in USB delays -> Get rid of udelay() that * would be lost in the noise - Jean II */ diff += IU_USB_MIN_RTT; #endif /* IU_USB_MIN_RTT */ - /* If the usec counter did wraparound, the diff will - * go negative (tv_usec is a long), so we need to - * correct it by one second. Jean II */ - if (diff < 0) - diff += 1000000; /* Check if the mtt is larger than the time we have * already used by all the protocol processing @@ -869,7 +863,7 @@ static void irda_usb_receive(struct urb *urb) * reduce the min turn time a bit since we will know * how much time we have used for protocol processing */ - do_gettimeofday(&self->stamp); + self->stamp = ktime_get(); /* Check if we need to copy the data to a new skb or not. * For most frames, we use ZeroCopy and pass the already diff --git a/drivers/net/irda/irda-usb.h b/drivers/net/irda/irda-usb.h index 58ddb5214916..8ac389fa9348 100644 --- a/drivers/net/irda/irda-usb.h +++ b/drivers/net/irda/irda-usb.h @@ -29,7 +29,7 @@ * *****************************************************************************/ -#include <linux/time.h> +#include <linux/ktime.h> #include <net/irda/irda.h> #include <net/irda/irda_device.h> /* struct irlap_cb */ @@ -157,8 +157,7 @@ struct irda_usb_cb { char *speed_buff; /* Buffer for speed changes */ char *tx_buff; - struct timeval stamp; - struct timeval now; + ktime_t stamp; spinlock_t lock; /* For serializing Tx operations */ diff --git a/drivers/net/irda/kingsun-sir.c b/drivers/net/irda/kingsun-sir.c index e638893e98a9..fb5d162ec7d2 100644 --- a/drivers/net/irda/kingsun-sir.c +++ b/drivers/net/irda/kingsun-sir.c @@ -114,7 +114,6 @@ struct kingsun_cb { (usually 8) */ iobuff_t rx_buff; /* receive unwrap state machine */ - struct timeval rx_time; spinlock_t lock; int receiving; @@ -235,7 +234,6 @@ static void kingsun_rcv_irq(struct urb *urb) &kingsun->netdev->stats, &kingsun->rx_buff, bytes[i]); } - do_gettimeofday(&kingsun->rx_time); kingsun->receiving = (kingsun->rx_buff.state != OUTSIDE_FRAME) ? 1 : 0; @@ -273,7 +271,6 @@ static int kingsun_net_open(struct net_device *netdev) skb_reserve(kingsun->rx_buff.skb, 1); kingsun->rx_buff.head = kingsun->rx_buff.skb->data; - do_gettimeofday(&kingsun->rx_time); kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!kingsun->rx_urb) diff --git a/drivers/net/irda/ks959-sir.c b/drivers/net/irda/ks959-sir.c index e6b3804edacd..8e6e0edf2440 100644 --- a/drivers/net/irda/ks959-sir.c +++ b/drivers/net/irda/ks959-sir.c @@ -187,7 +187,6 @@ struct ks959_cb { __u8 *rx_buf; __u8 rx_variable_xormask; iobuff_t rx_unwrap_buff; - struct timeval rx_time; struct usb_ctrlrequest *speed_setuprequest; struct urb *speed_urb; @@ -476,7 +475,6 @@ static void ks959_rcv_irq(struct urb *urb) bytes[i]); } } - do_gettimeofday(&kingsun->rx_time); kingsun->receiving = (kingsun->rx_unwrap_buff.state != OUTSIDE_FRAME) ? 1 : 0; } @@ -514,7 +512,6 @@ static int ks959_net_open(struct net_device *netdev) skb_reserve(kingsun->rx_unwrap_buff.skb, 1); kingsun->rx_unwrap_buff.head = kingsun->rx_unwrap_buff.skb->data; - do_gettimeofday(&kingsun->rx_time); kingsun->rx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!kingsun->rx_urb) diff --git a/drivers/net/irda/mcs7780.c b/drivers/net/irda/mcs7780.c index e4d678fbeb2f..bca6a1e72d1d 100644 --- a/drivers/net/irda/mcs7780.c +++ b/drivers/net/irda/mcs7780.c @@ -722,7 +722,6 @@ static int mcs_net_open(struct net_device *netdev) skb_reserve(mcs->rx_buff.skb, 1); mcs->rx_buff.head = mcs->rx_buff.skb->data; - do_gettimeofday(&mcs->rx_time); /* * Now that everything should be initialized properly, @@ -799,7 +798,6 @@ static void mcs_receive_irq(struct urb *urb) mcs_unwrap_fir(mcs, urb->transfer_buffer, urb->actual_length); } - do_gettimeofday(&mcs->rx_time); } ret = usb_submit_urb(urb, GFP_ATOMIC); diff --git a/drivers/net/irda/mcs7780.h b/drivers/net/irda/mcs7780.h index b10689b2887c..a6e8f7dbafc9 100644 --- a/drivers/net/irda/mcs7780.h +++ b/drivers/net/irda/mcs7780.h @@ -116,7 +116,6 @@ struct mcs_cb { __u8 *fifo_status; iobuff_t rx_buff; /* receive unwrap state machine */ - struct timeval rx_time; spinlock_t lock; int receiving; diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c index e7317b104bfb..dc0dbd8dd0b5 100644 --- a/drivers/net/irda/nsc-ircc.c +++ b/drivers/net/irda/nsc-ircc.c @@ -1501,10 +1501,7 @@ static netdev_tx_t nsc_ircc_hard_xmit_fir(struct sk_buff *skb, mtt = irda_get_mtt(skb); if (mtt) { /* Check how much time we have used already */ - do_gettimeofday(&self->now); - diff = self->now.tv_usec - self->stamp.tv_usec; - if (diff < 0) - diff += 1000000; + diff = ktime_us_delta(ktime_get(), self->stamp); /* Check if the mtt is larger than the time we have * already used by all the protocol processing @@ -1867,7 +1864,7 @@ static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase) * reduce the min turn time a bit since we will know * how much time we have used for protocol processing */ - do_gettimeofday(&self->stamp); + self->stamp = ktime_get(); skb = dev_alloc_skb(len+1); if (skb == NULL) { diff --git a/drivers/net/irda/nsc-ircc.h b/drivers/net/irda/nsc-ircc.h index 32fa58211fad..7be5acb56532 100644 --- a/drivers/net/irda/nsc-ircc.h +++ b/drivers/net/irda/nsc-ircc.h @@ -28,7 +28,7 @@ #ifndef NSC_IRCC_H #define NSC_IRCC_H -#include <linux/time.h> +#include <linux/ktime.h> #include <linux/spinlock.h> #include <linux/pm.h> @@ -263,8 +263,7 @@ struct nsc_ircc_cb { __u8 ier; /* Interrupt enable register */ - struct timeval stamp; - struct timeval now; + ktime_t stamp; spinlock_t lock; /* For serializing operations */ diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c index 7b17fa2114e1..b6e44ff4e373 100644 --- a/drivers/net/irda/sa1100_ir.c +++ b/drivers/net/irda/sa1100_ir.c @@ -38,7 +38,7 @@ #include <net/irda/irda_device.h> #include <mach/hardware.h> -#include <asm/mach/irda.h> +#include <linux/platform_data/irda-sa11x0.h> static int power_level = 3; static int tx_lpm; diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c index dd1bd1060ec9..83cc48a01802 100644 --- a/drivers/net/irda/stir4200.c +++ b/drivers/net/irda/stir4200.c @@ -40,6 +40,7 @@ #include <linux/moduleparam.h> #include <linux/kernel.h> +#include <linux/ktime.h> #include <linux/types.h> #include <linux/time.h> #include <linux/skbuff.h> @@ -174,7 +175,7 @@ struct stir_cb { __u8 *fifo_status; iobuff_t rx_buff; /* receive unwrap state machine */ - struct timeval rx_time; + ktime_t rx_time; int receiving; struct urb *rx_urb; }; @@ -650,15 +651,12 @@ static int fifo_txwait(struct stir_cb *stir, int space) static void turnaround_delay(const struct stir_cb *stir, long us) { long ticks; - struct timeval now; if (us <= 0) return; - do_gettimeofday(&now); - if (now.tv_sec - stir->rx_time.tv_sec > 0) - us -= USEC_PER_SEC; - us -= now.tv_usec - stir->rx_time.tv_usec; + us -= ktime_us_delta(ktime_get(), stir->rx_time); + if (us < 10) return; @@ -823,8 +821,8 @@ static void stir_rcv_irq(struct urb *urb) pr_debug("receive %d\n", urb->actual_length); unwrap_chars(stir, urb->transfer_buffer, urb->actual_length); - - do_gettimeofday(&stir->rx_time); + + stir->rx_time = ktime_get(); } /* kernel thread is stopping receiver don't resubmit */ @@ -876,7 +874,7 @@ static int stir_net_open(struct net_device *netdev) skb_reserve(stir->rx_buff.skb, 1); stir->rx_buff.head = stir->rx_buff.skb->data; - do_gettimeofday(&stir->rx_time); + stir->rx_time = ktime_get(); stir->rx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!stir->rx_urb) diff --git a/drivers/net/irda/via-ircc.h b/drivers/net/irda/via-ircc.h index 7ce820ecc361..ac1525573398 100644 --- a/drivers/net/irda/via-ircc.h +++ b/drivers/net/irda/via-ircc.h @@ -29,7 +29,6 @@ this program; if not, see <http://www.gnu.org/licenses/>. ********************************************************************/ #ifndef via_IRCC_H #define via_IRCC_H -#include <linux/time.h> #include <linux/spinlock.h> #include <linux/pm.h> #include <linux/types.h> @@ -106,9 +105,6 @@ struct via_ircc_cb { __u8 ier; /* Interrupt enable register */ - struct timeval stamp; - struct timeval now; - spinlock_t lock; /* For serializing operations */ __u32 flags; /* Interface flags */ diff --git a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c index ac39d9f33d5f..a0849f49bbec 100644 --- a/drivers/net/irda/vlsi_ir.c +++ b/drivers/net/irda/vlsi_ir.c @@ -33,6 +33,7 @@ MODULE_LICENSE("GPL"); /********************************************************/ #include <linux/kernel.h> +#include <linux/ktime.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/pci.h> @@ -40,9 +41,9 @@ MODULE_LICENSE("GPL"); #include <linux/netdevice.h> #include <linux/skbuff.h> #include <linux/delay.h> -#include <linux/time.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <linux/math64.h> #include <linux/mutex.h> #include <asm/uaccess.h> #include <asm/byteorder.h> @@ -180,8 +181,7 @@ static void vlsi_proc_ndev(struct seq_file *seq, struct net_device *ndev) vlsi_irda_dev_t *idev = netdev_priv(ndev); u8 byte; u16 word; - unsigned delta1, delta2; - struct timeval now; + s32 sec, usec; unsigned iobase = ndev->base_addr; seq_printf(seq, "\n%s link state: %s / %s / %s / %s\n", ndev->name, @@ -277,17 +277,9 @@ static void vlsi_proc_ndev(struct seq_file *seq, struct net_device *ndev) seq_printf(seq, "\nsw-state:\n"); seq_printf(seq, "IrPHY setup: %d baud - %s encoding\n", idev->baud, (idev->mode==IFF_SIR)?"SIR":((idev->mode==IFF_MIR)?"MIR":"FIR")); - do_gettimeofday(&now); - if (now.tv_usec >= idev->last_rx.tv_usec) { - delta2 = now.tv_usec - idev->last_rx.tv_usec; - delta1 = 0; - } - else { - delta2 = 1000000 + now.tv_usec - idev->last_rx.tv_usec; - delta1 = 1; - } - seq_printf(seq, "last rx: %lu.%06u sec\n", - now.tv_sec - idev->last_rx.tv_sec - delta1, delta2); + sec = div_s64_rem(ktime_us_delta(ktime_get(), idev->last_rx), + USEC_PER_SEC, &usec); + seq_printf(seq, "last rx: %ul.%06u sec\n", sec, usec); seq_printf(seq, "RX: packets=%lu / bytes=%lu / errors=%lu / dropped=%lu", ndev->stats.rx_packets, ndev->stats.rx_bytes, ndev->stats.rx_errors, @@ -661,7 +653,7 @@ static void vlsi_rx_interrupt(struct net_device *ndev) } } - do_gettimeofday(&idev->last_rx); /* remember "now" for later mtt delay */ + idev->last_rx = ktime_get(); /* remember "now" for later mtt delay */ vlsi_fill_rx(r); @@ -858,9 +850,8 @@ static netdev_tx_t vlsi_hard_start_xmit(struct sk_buff *skb, unsigned iobase = ndev->base_addr; u8 status; u16 config; - int mtt; + int mtt, diff; int len, speed; - struct timeval now, ready; char *msg = NULL; speed = irda_get_next_speed(skb); @@ -940,21 +931,10 @@ static netdev_tx_t vlsi_hard_start_xmit(struct sk_buff *skb, spin_unlock_irqrestore(&idev->lock, flags); if ((mtt = irda_get_mtt(skb)) > 0) { - - ready.tv_usec = idev->last_rx.tv_usec + mtt; - ready.tv_sec = idev->last_rx.tv_sec; - if (ready.tv_usec >= 1000000) { - ready.tv_usec -= 1000000; - ready.tv_sec++; /* IrLAP 1.1: mtt always < 1 sec */ - } - for(;;) { - do_gettimeofday(&now); - if (now.tv_sec > ready.tv_sec || - (now.tv_sec==ready.tv_sec && now.tv_usec>=ready.tv_usec)) - break; - udelay(100); + diff = ktime_us_delta(ktime_get(), idev->last_rx); + if (mtt > diff) + udelay(mtt - diff); /* must not sleep here - called under netif_tx_lock! */ - } } /* tx buffer already owned by CPU due to pci_dma_sync_single_for_cpu() @@ -1333,7 +1313,7 @@ static int vlsi_start_hw(vlsi_irda_dev_t *idev) vlsi_fill_rx(idev->rx_ring); - do_gettimeofday(&idev->last_rx); /* first mtt may start from now on */ + idev->last_rx = ktime_get(); /* first mtt may start from now on */ outw(0, iobase+VLSI_PIO_PROMPT); /* kick hw state machine */ @@ -1520,7 +1500,7 @@ static int vlsi_open(struct net_device *ndev) if (!idev->irlap) goto errout_free_ring; - do_gettimeofday(&idev->last_rx); /* first mtt may start from now on */ + idev->last_rx = ktime_get(); /* first mtt may start from now on */ idev->new_baud = 9600; /* start with IrPHY using 9600(SIR) mode */ diff --git a/drivers/net/irda/vlsi_ir.h b/drivers/net/irda/vlsi_ir.h index f9119c6d2a09..f9db2ce4c5c6 100644 --- a/drivers/net/irda/vlsi_ir.h +++ b/drivers/net/irda/vlsi_ir.h @@ -723,7 +723,7 @@ typedef struct vlsi_irda_dev { void *virtaddr; struct vlsi_ring *tx_ring, *rx_ring; - struct timeval last_rx; + ktime_t last_rx; spinlock_t lock; struct mutex mtx; diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 612e0731142d..1df38bdae2ee 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -1471,11 +1471,17 @@ int macvlan_link_register(struct rtnl_link_ops *ops) }; EXPORT_SYMBOL_GPL(macvlan_link_register); +static struct net *macvlan_get_link_net(const struct net_device *dev) +{ + return dev_net(macvlan_dev_real_dev(dev)); +} + static struct rtnl_link_ops macvlan_link_ops = { .kind = "macvlan", .setup = macvlan_setup, .newlink = macvlan_newlink, .dellink = macvlan_dellink, + .get_link_net = macvlan_get_link_net, }; static int macvlan_device_event(struct notifier_block *unused, diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 7df221788cd4..e40fdfccc9c1 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -17,7 +17,6 @@ #include <linux/fs.h> #include <linux/uio.h> -#include <net/ipv6.h> #include <net/net_namespace.h> #include <net/rtnetlink.h> #include <net/sock.h> @@ -81,7 +80,7 @@ static struct cdev macvtap_cdev; static const struct proto_ops macvtap_socket_ops; #define TUN_OFFLOADS (NETIF_F_HW_CSUM | NETIF_F_TSO_ECN | NETIF_F_TSO | \ - NETIF_F_TSO6) + NETIF_F_TSO6 | NETIF_F_UFO) #define RX_OFFLOADS (NETIF_F_GRO | NETIF_F_LRO) #define TAP_FEATURES (NETIF_F_GSO | NETIF_F_SG) @@ -586,11 +585,7 @@ static int macvtap_skb_from_vnet_hdr(struct macvtap_queue *q, gso_type = SKB_GSO_TCPV6; break; case VIRTIO_NET_HDR_GSO_UDP: - pr_warn_once("macvtap: %s: using disabled UFO feature; please fix this program\n", - current->comm); gso_type = SKB_GSO_UDP; - if (skb->protocol == htons(ETH_P_IPV6)) - ipv6_proxy_select_ident(skb); break; default: return -EINVAL; @@ -636,6 +631,8 @@ static void macvtap_skb_to_vnet_hdr(struct macvtap_queue *q, vnet_hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; else if (sinfo->gso_type & SKB_GSO_TCPV6) vnet_hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; + else if (sinfo->gso_type & SKB_GSO_UDP) + vnet_hdr->gso_type = VIRTIO_NET_HDR_GSO_UDP; else BUG(); if (sinfo->gso_type & SKB_GSO_TCP_ECN) @@ -645,7 +642,7 @@ static void macvtap_skb_to_vnet_hdr(struct macvtap_queue *q, if (skb->ip_summed == CHECKSUM_PARTIAL) { vnet_hdr->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM; - if (vlan_tx_tag_present(skb)) + if (skb_vlan_tag_present(skb)) vnet_hdr->csum_start = cpu_to_macvtap16(q, skb_checksum_start_offset(skb) + VLAN_HLEN); else @@ -821,13 +818,13 @@ static ssize_t macvtap_put_user(struct macvtap_queue *q, total = vnet_hdr_len; total += skb->len; - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { struct { __be16 h_vlan_proto; __be16 h_vlan_TCI; } veth; veth.h_vlan_proto = skb->vlan_proto; - veth.h_vlan_TCI = htons(vlan_tx_tag_get(skb)); + veth.h_vlan_TCI = htons(skb_vlan_tag_get(skb)); vlan_offset = offsetof(struct vlan_ethhdr, h_vlan_proto); total += VLAN_HLEN; @@ -965,6 +962,9 @@ static int set_offload(struct macvtap_queue *q, unsigned long arg) if (arg & TUN_F_TSO6) feature_mask |= NETIF_F_TSO6; } + + if (arg & TUN_F_UFO) + feature_mask |= NETIF_F_UFO; } /* tun/tap driver inverts the usage for TSO offloads, where @@ -975,7 +975,7 @@ static int set_offload(struct macvtap_queue *q, unsigned long arg) * When user space turns off TSO, we turn off GSO/LRO so that * user-space will not receive TSO frames. */ - if (feature_mask & (NETIF_F_TSO | NETIF_F_TSO6)) + if (feature_mask & (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_UFO)) features |= RX_OFFLOADS; else features &= ~RX_OFFLOADS; @@ -1090,7 +1090,7 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd, case TUNSETOFFLOAD: /* let the user check for future flags */ if (arg & ~(TUN_F_CSUM | TUN_F_TSO4 | TUN_F_TSO6 | - TUN_F_TSO_ECN)) + TUN_F_TSO_ECN | TUN_F_UFO)) return -EINVAL; rtnl_lock(); diff --git a/drivers/net/mii.c b/drivers/net/mii.c index 4a99c3919037..993570b1e2ae 100644 --- a/drivers/net/mii.c +++ b/drivers/net/mii.c @@ -302,7 +302,7 @@ void mii_check_link (struct mii_if_info *mii) } /** - * mii_check_media - check the MII interface for a duplex change + * mii_check_media - check the MII interface for a carrier/speed/duplex change * @mii: the MII interface * @ok_to_print: OK to print link up/down messages * @init_media: OK to save duplex mode in @mii @@ -318,10 +318,6 @@ unsigned int mii_check_media (struct mii_if_info *mii, int advertise, lpa, media, duplex; int lpa2 = 0; - /* if forced media, go no further */ - if (mii->force_media) - return 0; /* duplex did not change */ - /* check current and old link status */ old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0; new_carrier = (unsigned int) mii_link_ok(mii); @@ -345,6 +341,12 @@ unsigned int mii_check_media (struct mii_if_info *mii, */ netif_carrier_on(mii->dev); + if (mii->force_media) { + if (ok_to_print) + netdev_info(mii->dev, "link up\n"); + return 0; /* duplex did not change */ + } + /* get MII advertise and LPA values */ if ((!init_media) && (mii->advertising)) advertise = mii->advertising; diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index a3c251b79f38..16adbc481772 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -26,7 +26,7 @@ config AMD_PHY config AMD_XGBE_PHY tristate "Driver for the AMD 10GbE (amd-xgbe) PHYs" - depends on OF && HAS_IOMEM + depends on (OF || ACPI) && HAS_IOMEM ---help--- Currently supports the AMD 10GbE PHY diff --git a/drivers/net/phy/amd-xgbe-phy.c b/drivers/net/phy/amd-xgbe-phy.c index 903dc3dc9ea7..9e3af54c9010 100644 --- a/drivers/net/phy/amd-xgbe-phy.c +++ b/drivers/net/phy/amd-xgbe-phy.c @@ -60,6 +60,7 @@ #include <linux/interrupt.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/workqueue.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> @@ -74,6 +75,9 @@ #include <linux/of_platform.h> #include <linux/of_device.h> #include <linux/uaccess.h> +#include <linux/bitops.h> +#include <linux/property.h> +#include <linux/acpi.h> MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>"); MODULE_LICENSE("Dual BSD/GPL"); @@ -84,22 +88,43 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver"); #define XGBE_PHY_MASK 0xfffffff0 #define XGBE_PHY_SPEEDSET_PROPERTY "amd,speed-set" +#define XGBE_PHY_BLWC_PROPERTY "amd,serdes-blwc" +#define XGBE_PHY_CDR_RATE_PROPERTY "amd,serdes-cdr-rate" +#define XGBE_PHY_PQ_SKEW_PROPERTY "amd,serdes-pq-skew" +#define XGBE_PHY_TX_AMP_PROPERTY "amd,serdes-tx-amp" + +#define XGBE_PHY_SPEEDS 3 +#define XGBE_PHY_SPEED_1000 0 +#define XGBE_PHY_SPEED_2500 1 +#define XGBE_PHY_SPEED_10000 2 #define XGBE_AN_INT_CMPLT 0x01 #define XGBE_AN_INC_LINK 0x02 #define XGBE_AN_PG_RCV 0x04 +#define XGBE_AN_INT_MASK 0x07 #define XNP_MCF_NULL_MESSAGE 0x001 -#define XNP_ACK_PROCESSED (1 << 12) -#define XNP_MP_FORMATTED (1 << 13) -#define XNP_NP_EXCHANGE (1 << 15) +#define XNP_ACK_PROCESSED BIT(12) +#define XNP_MP_FORMATTED BIT(13) +#define XNP_NP_EXCHANGE BIT(15) #define XGBE_PHY_RATECHANGE_COUNT 500 +#define XGBE_PHY_KR_TRAINING_START 0x01 +#define XGBE_PHY_KR_TRAINING_ENABLE 0x02 + +#define XGBE_PHY_FEC_ENABLE 0x01 +#define XGBE_PHY_FEC_FORWARD 0x02 +#define XGBE_PHY_FEC_MASK 0x03 + #ifndef MDIO_PMA_10GBR_PMD_CTRL #define MDIO_PMA_10GBR_PMD_CTRL 0x0096 #endif +#ifndef MDIO_PMA_10GBR_FEC_ABILITY +#define MDIO_PMA_10GBR_FEC_ABILITY 0x00aa +#endif + #ifndef MDIO_PMA_10GBR_FEC_CTRL #define MDIO_PMA_10GBR_FEC_CTRL 0x00ab #endif @@ -108,6 +133,10 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver"); #define MDIO_AN_XNP 0x0016 #endif +#ifndef MDIO_AN_LPX +#define MDIO_AN_LPX 0x0019 +#endif + #ifndef MDIO_AN_INTMASK #define MDIO_AN_INTMASK 0x8001 #endif @@ -116,18 +145,10 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver"); #define MDIO_AN_INT 0x8002 #endif -#ifndef MDIO_AN_KR_CTRL -#define MDIO_AN_KR_CTRL 0x8003 -#endif - #ifndef MDIO_CTRL1_SPEED1G #define MDIO_CTRL1_SPEED1G (MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100) #endif -#ifndef MDIO_KR_CTRL_PDETECT -#define MDIO_KR_CTRL_PDETECT 0x01 -#endif - /* SerDes integration register offsets */ #define SIR0_KR_RT_1 0x002c #define SIR0_STATUS 0x0040 @@ -140,10 +161,10 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver"); #define SIR0_STATUS_RX_READY_WIDTH 1 #define SIR0_STATUS_TX_READY_INDEX 8 #define SIR0_STATUS_TX_READY_WIDTH 1 +#define SIR1_SPEED_CDR_RATE_INDEX 12 +#define SIR1_SPEED_CDR_RATE_WIDTH 4 #define SIR1_SPEED_DATARATE_INDEX 4 #define SIR1_SPEED_DATARATE_WIDTH 2 -#define SIR1_SPEED_PI_SPD_SEL_INDEX 12 -#define SIR1_SPEED_PI_SPD_SEL_WIDTH 4 #define SIR1_SPEED_PLLSEL_INDEX 3 #define SIR1_SPEED_PLLSEL_WIDTH 1 #define SIR1_SPEED_RATECHANGE_INDEX 6 @@ -153,20 +174,26 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver"); #define SIR1_SPEED_WORDMODE_INDEX 0 #define SIR1_SPEED_WORDMODE_WIDTH 3 +#define SPEED_10000_BLWC 0 #define SPEED_10000_CDR 0x7 #define SPEED_10000_PLL 0x1 +#define SPEED_10000_PQ 0x1e #define SPEED_10000_RATE 0x0 #define SPEED_10000_TXAMP 0xa #define SPEED_10000_WORD 0x7 +#define SPEED_2500_BLWC 1 #define SPEED_2500_CDR 0x2 #define SPEED_2500_PLL 0x0 +#define SPEED_2500_PQ 0xa #define SPEED_2500_RATE 0x1 #define SPEED_2500_TXAMP 0xf #define SPEED_2500_WORD 0x1 +#define SPEED_1000_BLWC 1 #define SPEED_1000_CDR 0x2 #define SPEED_1000_PLL 0x0 +#define SPEED_1000_PQ 0xa #define SPEED_1000_RATE 0x3 #define SPEED_1000_TXAMP 0xf #define SPEED_1000_WORD 0x1 @@ -181,15 +208,6 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver"); #define RXTX_REG114_PQ_REG_INDEX 9 #define RXTX_REG114_PQ_REG_WIDTH 7 -#define RXTX_10000_BLWC 0 -#define RXTX_10000_PQ 0x1e - -#define RXTX_2500_BLWC 1 -#define RXTX_2500_PQ 0xa - -#define RXTX_1000_BLWC 1 -#define RXTX_1000_PQ 0xa - /* Bit setting and getting macros * The get macro will extract the current bit field value from within * the variable @@ -291,23 +309,44 @@ do { \ XRXTX_IOWRITE((_priv), _reg, reg_val); \ } while (0) +static const u32 amd_xgbe_phy_serdes_blwc[] = { + SPEED_1000_BLWC, + SPEED_2500_BLWC, + SPEED_10000_BLWC, +}; + +static const u32 amd_xgbe_phy_serdes_cdr_rate[] = { + SPEED_1000_CDR, + SPEED_2500_CDR, + SPEED_10000_CDR, +}; + +static const u32 amd_xgbe_phy_serdes_pq_skew[] = { + SPEED_1000_PQ, + SPEED_2500_PQ, + SPEED_10000_PQ, +}; + +static const u32 amd_xgbe_phy_serdes_tx_amp[] = { + SPEED_1000_TXAMP, + SPEED_2500_TXAMP, + SPEED_10000_TXAMP, +}; + enum amd_xgbe_phy_an { AMD_XGBE_AN_READY = 0, - AMD_XGBE_AN_START, - AMD_XGBE_AN_EVENT, AMD_XGBE_AN_PAGE_RECEIVED, AMD_XGBE_AN_INCOMPAT_LINK, AMD_XGBE_AN_COMPLETE, AMD_XGBE_AN_NO_LINK, - AMD_XGBE_AN_EXIT, AMD_XGBE_AN_ERROR, }; enum amd_xgbe_phy_rx { - AMD_XGBE_RX_READY = 0, - AMD_XGBE_RX_BPA, + AMD_XGBE_RX_BPA = 0, AMD_XGBE_RX_XNP, AMD_XGBE_RX_COMPLETE, + AMD_XGBE_RX_ERROR, }; enum amd_xgbe_phy_mode { @@ -316,12 +355,13 @@ enum amd_xgbe_phy_mode { }; enum amd_xgbe_phy_speedset { - AMD_XGBE_PHY_SPEEDSET_1000_10000, + AMD_XGBE_PHY_SPEEDSET_1000_10000 = 0, AMD_XGBE_PHY_SPEEDSET_2500_10000, }; struct amd_xgbe_phy_priv { struct platform_device *pdev; + struct acpi_device *adev; struct device *dev; struct phy_device *phydev; @@ -336,10 +376,24 @@ struct amd_xgbe_phy_priv { void __iomem *sir0_regs; /* SerDes integration registers (1/2) */ void __iomem *sir1_regs; /* SerDes integration registers (2/2) */ - /* Maintain link status for re-starting auto-negotiation */ - unsigned int link; + int an_irq; + char an_irq_name[IFNAMSIZ + 32]; + struct work_struct an_irq_work; + unsigned int an_irq_allocated; + unsigned int speed_set; + /* SerDes UEFI configurable settings. + * Switching between modes/speeds requires new values for some + * SerDes settings. The values can be supplied as device + * properties in array format. The first array entry is for + * 1GbE, second for 2.5GbE and third for 10GbE + */ + u32 serdes_blwc[XGBE_PHY_SPEEDS]; + u32 serdes_cdr_rate[XGBE_PHY_SPEEDS]; + u32 serdes_pq_skew[XGBE_PHY_SPEEDS]; + u32 serdes_tx_amp[XGBE_PHY_SPEEDS]; + /* Auto-negotiation state machine support */ struct mutex an_mutex; enum amd_xgbe_phy_an an_result; @@ -348,7 +402,11 @@ struct amd_xgbe_phy_priv { enum amd_xgbe_phy_rx kx_state; struct work_struct an_work; struct workqueue_struct *an_workqueue; + unsigned int an_supported; unsigned int parallel_detect; + unsigned int fec_ability; + + unsigned int lpm_ctrl; /* CTRL1 for resume */ }; static int amd_xgbe_an_enable_kr_training(struct phy_device *phydev) @@ -359,7 +417,7 @@ static int amd_xgbe_an_enable_kr_training(struct phy_device *phydev) if (ret < 0) return ret; - ret |= 0x02; + ret |= XGBE_PHY_KR_TRAINING_ENABLE; phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret); return 0; @@ -373,7 +431,7 @@ static int amd_xgbe_an_disable_kr_training(struct phy_device *phydev) if (ret < 0) return ret; - ret &= ~0x02; + ret &= ~XGBE_PHY_KR_TRAINING_ENABLE; phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret); return 0; @@ -466,12 +524,16 @@ static int amd_xgbe_phy_xgmii_mode(struct phy_device *phydev) XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_10000_RATE); XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_10000_WORD); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_10000_TXAMP); XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_10000_PLL); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_10000_CDR); - XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_10000_BLWC); - XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_10000_PQ); + XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, CDR_RATE, + priv->serdes_cdr_rate[XGBE_PHY_SPEED_10000]); + XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, + priv->serdes_tx_amp[XGBE_PHY_SPEED_10000]); + XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, + priv->serdes_blwc[XGBE_PHY_SPEED_10000]); + XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, + priv->serdes_pq_skew[XGBE_PHY_SPEED_10000]); amd_xgbe_phy_serdes_complete_ratechange(phydev); @@ -514,12 +576,16 @@ static int amd_xgbe_phy_gmii_2500_mode(struct phy_device *phydev) XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_2500_RATE); XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_2500_WORD); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_2500_TXAMP); XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_2500_PLL); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_2500_CDR); - XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_2500_BLWC); - XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_2500_PQ); + XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, CDR_RATE, + priv->serdes_cdr_rate[XGBE_PHY_SPEED_2500]); + XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, + priv->serdes_tx_amp[XGBE_PHY_SPEED_2500]); + XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, + priv->serdes_blwc[XGBE_PHY_SPEED_2500]); + XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, + priv->serdes_pq_skew[XGBE_PHY_SPEED_2500]); amd_xgbe_phy_serdes_complete_ratechange(phydev); @@ -562,12 +628,16 @@ static int amd_xgbe_phy_gmii_mode(struct phy_device *phydev) XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_1000_RATE); XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_1000_WORD); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_1000_TXAMP); XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_1000_PLL); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_1000_CDR); - XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_1000_BLWC); - XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_1000_PQ); + XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, CDR_RATE, + priv->serdes_cdr_rate[XGBE_PHY_SPEED_1000]); + XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, + priv->serdes_tx_amp[XGBE_PHY_SPEED_1000]); + XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, + priv->serdes_blwc[XGBE_PHY_SPEED_1000]); + XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, + priv->serdes_pq_skew[XGBE_PHY_SPEED_1000]); amd_xgbe_phy_serdes_complete_ratechange(phydev); @@ -635,6 +705,38 @@ static int amd_xgbe_phy_set_mode(struct phy_device *phydev, return ret; } +static int amd_xgbe_phy_set_an(struct phy_device *phydev, bool enable, + bool restart) +{ + int ret; + + ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1); + if (ret < 0) + return ret; + + ret &= ~MDIO_AN_CTRL1_ENABLE; + + if (enable) + ret |= MDIO_AN_CTRL1_ENABLE; + + if (restart) + ret |= MDIO_AN_CTRL1_RESTART; + + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, ret); + + return 0; +} + +static int amd_xgbe_phy_restart_an(struct phy_device *phydev) +{ + return amd_xgbe_phy_set_an(phydev, true, true); +} + +static int amd_xgbe_phy_disable_an(struct phy_device *phydev) +{ + return amd_xgbe_phy_set_an(phydev, false, false); +} + static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev, enum amd_xgbe_phy_rx *state) { @@ -645,7 +747,7 @@ static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev, /* If we're not in KR mode then we're done */ if (!amd_xgbe_phy_in_kr_mode(phydev)) - return AMD_XGBE_AN_EVENT; + return AMD_XGBE_AN_PAGE_RECEIVED; /* Enable/Disable FEC */ ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); @@ -660,10 +762,9 @@ static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev, if (ret < 0) return AMD_XGBE_AN_ERROR; + ret &= ~XGBE_PHY_FEC_MASK; if ((ad_reg & 0xc000) && (lp_reg & 0xc000)) - ret |= 0x01; - else - ret &= ~0x01; + ret |= priv->fec_ability; phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FEC_CTRL, ret); @@ -672,14 +773,17 @@ static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev, if (ret < 0) return AMD_XGBE_AN_ERROR; - XSIR0_IOWRITE_BITS(priv, SIR0_KR_RT_1, RESET, 1); + if (ret & XGBE_PHY_KR_TRAINING_ENABLE) { + XSIR0_IOWRITE_BITS(priv, SIR0_KR_RT_1, RESET, 1); - ret |= 0x01; - phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret); + ret |= XGBE_PHY_KR_TRAINING_START; + phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, + ret); - XSIR0_IOWRITE_BITS(priv, SIR0_KR_RT_1, RESET, 0); + XSIR0_IOWRITE_BITS(priv, SIR0_KR_RT_1, RESET, 0); + } - return AMD_XGBE_AN_EVENT; + return AMD_XGBE_AN_PAGE_RECEIVED; } static enum amd_xgbe_phy_an amd_xgbe_an_tx_xnp(struct phy_device *phydev, @@ -696,7 +800,7 @@ static enum amd_xgbe_phy_an amd_xgbe_an_tx_xnp(struct phy_device *phydev, phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP + 1, 0); phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP, msg); - return AMD_XGBE_AN_EVENT; + return AMD_XGBE_AN_PAGE_RECEIVED; } static enum amd_xgbe_phy_an amd_xgbe_an_rx_bpa(struct phy_device *phydev, @@ -735,11 +839,11 @@ static enum amd_xgbe_phy_an amd_xgbe_an_rx_xnp(struct phy_device *phydev, int ad_reg, lp_reg; /* Check Extended Next Page support */ - ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE); + ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP); if (ad_reg < 0) return AMD_XGBE_AN_ERROR; - lp_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA); + lp_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPX); if (lp_reg < 0) return AMD_XGBE_AN_ERROR; @@ -748,226 +852,255 @@ static enum amd_xgbe_phy_an amd_xgbe_an_rx_xnp(struct phy_device *phydev, amd_xgbe_an_tx_training(phydev, state); } -static enum amd_xgbe_phy_an amd_xgbe_an_start(struct phy_device *phydev) +static enum amd_xgbe_phy_an amd_xgbe_an_page_received(struct phy_device *phydev) +{ + struct amd_xgbe_phy_priv *priv = phydev->priv; + enum amd_xgbe_phy_rx *state; + int ret; + + state = amd_xgbe_phy_in_kr_mode(phydev) ? &priv->kr_state + : &priv->kx_state; + + switch (*state) { + case AMD_XGBE_RX_BPA: + ret = amd_xgbe_an_rx_bpa(phydev, state); + break; + + case AMD_XGBE_RX_XNP: + ret = amd_xgbe_an_rx_xnp(phydev, state); + break; + + default: + ret = AMD_XGBE_AN_ERROR; + } + + return ret; +} + +static enum amd_xgbe_phy_an amd_xgbe_an_incompat_link(struct phy_device *phydev) { struct amd_xgbe_phy_priv *priv = phydev->priv; int ret; /* Be sure we aren't looping trying to negotiate */ if (amd_xgbe_phy_in_kr_mode(phydev)) { - if (priv->kr_state != AMD_XGBE_RX_READY) + priv->kr_state = AMD_XGBE_RX_ERROR; + + if (!(phydev->supported & SUPPORTED_1000baseKX_Full) && + !(phydev->supported & SUPPORTED_2500baseX_Full)) + return AMD_XGBE_AN_NO_LINK; + + if (priv->kx_state != AMD_XGBE_RX_BPA) return AMD_XGBE_AN_NO_LINK; - priv->kr_state = AMD_XGBE_RX_BPA; } else { - if (priv->kx_state != AMD_XGBE_RX_READY) + priv->kx_state = AMD_XGBE_RX_ERROR; + + if (!(phydev->supported & SUPPORTED_10000baseKR_Full)) + return AMD_XGBE_AN_NO_LINK; + + if (priv->kr_state != AMD_XGBE_RX_BPA) return AMD_XGBE_AN_NO_LINK; - priv->kx_state = AMD_XGBE_RX_BPA; } - /* Set up Advertisement register 3 first */ - ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); - if (ret < 0) + ret = amd_xgbe_phy_disable_an(phydev); + if (ret) return AMD_XGBE_AN_ERROR; - if (phydev->supported & SUPPORTED_10000baseR_FEC) - ret |= 0xc000; - else - ret &= ~0xc000; - - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2, ret); - - /* Set up Advertisement register 2 next */ - ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); - if (ret < 0) + ret = amd_xgbe_phy_switch_mode(phydev); + if (ret) return AMD_XGBE_AN_ERROR; - if (phydev->supported & SUPPORTED_10000baseKR_Full) - ret |= 0x80; - else - ret &= ~0x80; - - if ((phydev->supported & SUPPORTED_1000baseKX_Full) || - (phydev->supported & SUPPORTED_2500baseX_Full)) - ret |= 0x20; - else - ret &= ~0x20; - - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1, ret); - - /* Set up Advertisement register 1 last */ - ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE); - if (ret < 0) + ret = amd_xgbe_phy_restart_an(phydev); + if (ret) return AMD_XGBE_AN_ERROR; - if (phydev->supported & SUPPORTED_Pause) - ret |= 0x400; - else - ret &= ~0x400; + return AMD_XGBE_AN_INCOMPAT_LINK; +} - if (phydev->supported & SUPPORTED_Asym_Pause) - ret |= 0x800; - else - ret &= ~0x800; +static irqreturn_t amd_xgbe_an_isr(int irq, void *data) +{ + struct amd_xgbe_phy_priv *priv = (struct amd_xgbe_phy_priv *)data; - /* We don't intend to perform XNP */ - ret &= ~XNP_NP_EXCHANGE; + /* Interrupt reason must be read and cleared outside of IRQ context */ + disable_irq_nosync(priv->an_irq); - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE, ret); + queue_work(priv->an_workqueue, &priv->an_irq_work); - /* Enable and start auto-negotiation */ - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0); + return IRQ_HANDLED; +} - ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_KR_CTRL); - if (ret < 0) - return AMD_XGBE_AN_ERROR; +static void amd_xgbe_an_irq_work(struct work_struct *work) +{ + struct amd_xgbe_phy_priv *priv = container_of(work, + struct amd_xgbe_phy_priv, + an_irq_work); - ret |= MDIO_KR_CTRL_PDETECT; - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_KR_CTRL, ret); + /* Avoid a race between enabling the IRQ and exiting the work by + * waiting for the work to finish and then queueing it + */ + flush_work(&priv->an_work); + queue_work(priv->an_workqueue, &priv->an_work); +} - ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1); - if (ret < 0) - return AMD_XGBE_AN_ERROR; +static void amd_xgbe_an_state_machine(struct work_struct *work) +{ + struct amd_xgbe_phy_priv *priv = container_of(work, + struct amd_xgbe_phy_priv, + an_work); + struct phy_device *phydev = priv->phydev; + enum amd_xgbe_phy_an cur_state = priv->an_state; + int int_reg, int_mask; - ret |= MDIO_AN_CTRL1_ENABLE; - ret |= MDIO_AN_CTRL1_RESTART; - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, ret); + mutex_lock(&priv->an_mutex); - return AMD_XGBE_AN_EVENT; -} + /* Read the interrupt */ + int_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT); + if (!int_reg) + goto out; -static enum amd_xgbe_phy_an amd_xgbe_an_event(struct phy_device *phydev) -{ - enum amd_xgbe_phy_an new_state; - int ret; +next_int: + if (int_reg < 0) { + priv->an_state = AMD_XGBE_AN_ERROR; + int_mask = XGBE_AN_INT_MASK; + } else if (int_reg & XGBE_AN_PG_RCV) { + priv->an_state = AMD_XGBE_AN_PAGE_RECEIVED; + int_mask = XGBE_AN_PG_RCV; + } else if (int_reg & XGBE_AN_INC_LINK) { + priv->an_state = AMD_XGBE_AN_INCOMPAT_LINK; + int_mask = XGBE_AN_INC_LINK; + } else if (int_reg & XGBE_AN_INT_CMPLT) { + priv->an_state = AMD_XGBE_AN_COMPLETE; + int_mask = XGBE_AN_INT_CMPLT; + } else { + priv->an_state = AMD_XGBE_AN_ERROR; + int_mask = 0; + } - ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT); - if (ret < 0) - return AMD_XGBE_AN_ERROR; + /* Clear the interrupt to be processed */ + int_reg &= ~int_mask; + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, int_reg); - new_state = AMD_XGBE_AN_EVENT; - if (ret & XGBE_AN_PG_RCV) - new_state = AMD_XGBE_AN_PAGE_RECEIVED; - else if (ret & XGBE_AN_INC_LINK) - new_state = AMD_XGBE_AN_INCOMPAT_LINK; - else if (ret & XGBE_AN_INT_CMPLT) - new_state = AMD_XGBE_AN_COMPLETE; + priv->an_result = priv->an_state; - if (new_state != AMD_XGBE_AN_EVENT) - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0); +again: + cur_state = priv->an_state; - return new_state; -} + switch (priv->an_state) { + case AMD_XGBE_AN_READY: + priv->an_supported = 0; + break; -static enum amd_xgbe_phy_an amd_xgbe_an_page_received(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - enum amd_xgbe_phy_rx *state; - int ret; + case AMD_XGBE_AN_PAGE_RECEIVED: + priv->an_state = amd_xgbe_an_page_received(phydev); + priv->an_supported++; + break; - state = amd_xgbe_phy_in_kr_mode(phydev) ? &priv->kr_state - : &priv->kx_state; + case AMD_XGBE_AN_INCOMPAT_LINK: + priv->an_supported = 0; + priv->parallel_detect = 0; + priv->an_state = amd_xgbe_an_incompat_link(phydev); + break; - switch (*state) { - case AMD_XGBE_RX_BPA: - ret = amd_xgbe_an_rx_bpa(phydev, state); + case AMD_XGBE_AN_COMPLETE: + priv->parallel_detect = priv->an_supported ? 0 : 1; + netdev_dbg(phydev->attached_dev, "%s successful\n", + priv->an_supported ? "Auto negotiation" + : "Parallel detection"); break; - case AMD_XGBE_RX_XNP: - ret = amd_xgbe_an_rx_xnp(phydev, state); + case AMD_XGBE_AN_NO_LINK: break; default: - ret = AMD_XGBE_AN_ERROR; + priv->an_state = AMD_XGBE_AN_ERROR; } - return ret; -} + if (priv->an_state == AMD_XGBE_AN_NO_LINK) { + int_reg = 0; + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0); + } else if (priv->an_state == AMD_XGBE_AN_ERROR) { + netdev_err(phydev->attached_dev, + "error during auto-negotiation, state=%u\n", + cur_state); -static enum amd_xgbe_phy_an amd_xgbe_an_incompat_link(struct phy_device *phydev) -{ - int ret; + int_reg = 0; + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0); + } - ret = amd_xgbe_phy_switch_mode(phydev); - if (ret) - return AMD_XGBE_AN_ERROR; + if (priv->an_state >= AMD_XGBE_AN_COMPLETE) { + priv->an_result = priv->an_state; + priv->an_state = AMD_XGBE_AN_READY; + priv->kr_state = AMD_XGBE_RX_BPA; + priv->kx_state = AMD_XGBE_RX_BPA; + } - return AMD_XGBE_AN_START; -} + if (cur_state != priv->an_state) + goto again; -static void amd_xgbe_an_state_machine(struct work_struct *work) -{ - struct amd_xgbe_phy_priv *priv = container_of(work, - struct amd_xgbe_phy_priv, - an_work); - struct phy_device *phydev = priv->phydev; - enum amd_xgbe_phy_an cur_state; - int sleep; - unsigned int an_supported = 0; + if (int_reg) + goto next_int; - /* Start in KX mode */ - if (amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KX)) - priv->an_state = AMD_XGBE_AN_ERROR; +out: + enable_irq(priv->an_irq); - while (1) { - mutex_lock(&priv->an_mutex); + mutex_unlock(&priv->an_mutex); +} - cur_state = priv->an_state; +static int amd_xgbe_an_init(struct phy_device *phydev) +{ + int ret; - switch (priv->an_state) { - case AMD_XGBE_AN_START: - an_supported = 0; - priv->parallel_detect = 0; - priv->an_state = amd_xgbe_an_start(phydev); - break; + /* Set up Advertisement register 3 first */ + ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); + if (ret < 0) + return ret; - case AMD_XGBE_AN_EVENT: - priv->an_state = amd_xgbe_an_event(phydev); - break; + if (phydev->supported & SUPPORTED_10000baseR_FEC) + ret |= 0xc000; + else + ret &= ~0xc000; - case AMD_XGBE_AN_PAGE_RECEIVED: - priv->an_state = amd_xgbe_an_page_received(phydev); - an_supported++; - break; + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2, ret); - case AMD_XGBE_AN_INCOMPAT_LINK: - priv->an_state = amd_xgbe_an_incompat_link(phydev); - break; + /* Set up Advertisement register 2 next */ + ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); + if (ret < 0) + return ret; - case AMD_XGBE_AN_COMPLETE: - priv->parallel_detect = an_supported ? 0 : 1; - netdev_info(phydev->attached_dev, "%s successful\n", - an_supported ? "Auto negotiation" - : "Parallel detection"); - /* fall through */ + if (phydev->supported & SUPPORTED_10000baseKR_Full) + ret |= 0x80; + else + ret &= ~0x80; - case AMD_XGBE_AN_NO_LINK: - case AMD_XGBE_AN_EXIT: - goto exit_unlock; + if ((phydev->supported & SUPPORTED_1000baseKX_Full) || + (phydev->supported & SUPPORTED_2500baseX_Full)) + ret |= 0x20; + else + ret &= ~0x20; - default: - priv->an_state = AMD_XGBE_AN_ERROR; - } + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1, ret); - if (priv->an_state == AMD_XGBE_AN_ERROR) { - netdev_err(phydev->attached_dev, - "error during auto-negotiation, state=%u\n", - cur_state); - goto exit_unlock; - } + /* Set up Advertisement register 1 last */ + ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE); + if (ret < 0) + return ret; - sleep = (priv->an_state == AMD_XGBE_AN_EVENT) ? 1 : 0; + if (phydev->supported & SUPPORTED_Pause) + ret |= 0x400; + else + ret &= ~0x400; - mutex_unlock(&priv->an_mutex); + if (phydev->supported & SUPPORTED_Asym_Pause) + ret |= 0x800; + else + ret &= ~0x800; - if (sleep) - usleep_range(20, 50); - } + /* We don't intend to perform XNP */ + ret &= ~XNP_NP_EXCHANGE; -exit_unlock: - priv->an_result = priv->an_state; - priv->an_state = AMD_XGBE_AN_READY; + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE, ret); - mutex_unlock(&priv->an_mutex); + return 0; } static int amd_xgbe_phy_soft_reset(struct phy_device *phydev) @@ -992,20 +1125,57 @@ static int amd_xgbe_phy_soft_reset(struct phy_device *phydev) if (ret & MDIO_CTRL1_RESET) return -ETIMEDOUT; - /* Make sure the XPCS and SerDes are in compatible states */ - return amd_xgbe_phy_xgmii_mode(phydev); + /* Disable auto-negotiation for now */ + ret = amd_xgbe_phy_disable_an(phydev); + if (ret < 0) + return ret; + + /* Clear auto-negotiation interrupts */ + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0); + + return 0; } static int amd_xgbe_phy_config_init(struct phy_device *phydev) { struct amd_xgbe_phy_priv *priv = phydev->priv; + struct net_device *netdev = phydev->attached_dev; + int ret; + + if (!priv->an_irq_allocated) { + /* Allocate the auto-negotiation workqueue and interrupt */ + snprintf(priv->an_irq_name, sizeof(priv->an_irq_name) - 1, + "%s-pcs", netdev_name(netdev)); + + priv->an_workqueue = + create_singlethread_workqueue(priv->an_irq_name); + if (!priv->an_workqueue) { + netdev_err(netdev, "phy workqueue creation failed\n"); + return -ENOMEM; + } + + ret = devm_request_irq(priv->dev, priv->an_irq, + amd_xgbe_an_isr, 0, priv->an_irq_name, + priv); + if (ret) { + netdev_err(netdev, "phy irq request failed\n"); + destroy_workqueue(priv->an_workqueue); + return ret; + } + + priv->an_irq_allocated = 1; + } + + ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FEC_ABILITY); + if (ret < 0) + return ret; + priv->fec_ability = ret & XGBE_PHY_FEC_MASK; /* Initialize supported features */ phydev->supported = SUPPORTED_Autoneg; phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; phydev->supported |= SUPPORTED_Backplane; - phydev->supported |= SUPPORTED_10000baseKR_Full | - SUPPORTED_10000baseR_FEC; + phydev->supported |= SUPPORTED_10000baseKR_Full; switch (priv->speed_set) { case AMD_XGBE_PHY_SPEEDSET_1000_10000: phydev->supported |= SUPPORTED_1000baseKX_Full; @@ -1014,11 +1184,33 @@ static int amd_xgbe_phy_config_init(struct phy_device *phydev) phydev->supported |= SUPPORTED_2500baseX_Full; break; } + + if (priv->fec_ability & XGBE_PHY_FEC_ENABLE) + phydev->supported |= SUPPORTED_10000baseR_FEC; + phydev->advertising = phydev->supported; - /* Turn off and clear interrupts */ - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INTMASK, 0); - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0); + /* Set initial mode - call the mode setting routines + * directly to insure we are properly configured + */ + if (phydev->supported & SUPPORTED_10000baseKR_Full) + ret = amd_xgbe_phy_xgmii_mode(phydev); + else if (phydev->supported & SUPPORTED_1000baseKX_Full) + ret = amd_xgbe_phy_gmii_mode(phydev); + else if (phydev->supported & SUPPORTED_2500baseX_Full) + ret = amd_xgbe_phy_gmii_2500_mode(phydev); + else + ret = -EINVAL; + if (ret < 0) + return ret; + + /* Set up advertisement registers based on current settings */ + ret = amd_xgbe_an_init(phydev); + if (ret) + return ret; + + /* Enable auto-negotiation interrupts */ + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INTMASK, 0x07); return 0; } @@ -1028,25 +1220,19 @@ static int amd_xgbe_phy_setup_forced(struct phy_device *phydev) int ret; /* Disable auto-negotiation */ - ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1); + ret = amd_xgbe_phy_disable_an(phydev); if (ret < 0) return ret; - ret &= ~MDIO_AN_CTRL1_ENABLE; - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, ret); - /* Validate/Set specified speed */ switch (phydev->speed) { case SPEED_10000: - ret = amd_xgbe_phy_xgmii_mode(phydev); + ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KR); break; case SPEED_2500: - ret = amd_xgbe_phy_gmii_2500_mode(phydev); - break; - case SPEED_1000: - ret = amd_xgbe_phy_gmii_mode(phydev); + ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KX); break; default: @@ -1066,10 +1252,11 @@ static int amd_xgbe_phy_setup_forced(struct phy_device *phydev) return 0; } -static int amd_xgbe_phy_config_aneg(struct phy_device *phydev) +static int __amd_xgbe_phy_config_aneg(struct phy_device *phydev) { struct amd_xgbe_phy_priv *priv = phydev->priv; u32 mmd_mask = phydev->c45_ids.devices_in_package; + int ret; if (phydev->autoneg != AUTONEG_ENABLE) return amd_xgbe_phy_setup_forced(phydev); @@ -1078,56 +1265,79 @@ static int amd_xgbe_phy_config_aneg(struct phy_device *phydev) if (!(mmd_mask & MDIO_DEVS_AN)) return -EINVAL; - /* Start/Restart the auto-negotiation state machine */ - mutex_lock(&priv->an_mutex); + /* Disable auto-negotiation interrupt */ + disable_irq(priv->an_irq); + + /* Start auto-negotiation in a supported mode */ + if (phydev->supported & SUPPORTED_10000baseKR_Full) + ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KR); + else if ((phydev->supported & SUPPORTED_1000baseKX_Full) || + (phydev->supported & SUPPORTED_2500baseX_Full)) + ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KX); + else + ret = -EINVAL; + if (ret < 0) { + enable_irq(priv->an_irq); + return ret; + } + + /* Disable and stop any in progress auto-negotiation */ + ret = amd_xgbe_phy_disable_an(phydev); + if (ret < 0) + return ret; + + /* Clear any auto-negotitation interrupts */ + phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0); + priv->an_result = AMD_XGBE_AN_READY; - priv->an_state = AMD_XGBE_AN_START; - priv->kr_state = AMD_XGBE_RX_READY; - priv->kx_state = AMD_XGBE_RX_READY; - mutex_unlock(&priv->an_mutex); + priv->an_state = AMD_XGBE_AN_READY; + priv->kr_state = AMD_XGBE_RX_BPA; + priv->kx_state = AMD_XGBE_RX_BPA; - queue_work(priv->an_workqueue, &priv->an_work); + /* Re-enable auto-negotiation interrupt */ + enable_irq(priv->an_irq); - return 0; + /* Set up advertisement registers based on current settings */ + ret = amd_xgbe_an_init(phydev); + if (ret) + return ret; + + /* Enable and start auto-negotiation */ + return amd_xgbe_phy_restart_an(phydev); } -static int amd_xgbe_phy_aneg_done(struct phy_device *phydev) +static int amd_xgbe_phy_config_aneg(struct phy_device *phydev) { struct amd_xgbe_phy_priv *priv = phydev->priv; - enum amd_xgbe_phy_an state; + int ret; mutex_lock(&priv->an_mutex); - state = priv->an_result; + + ret = __amd_xgbe_phy_config_aneg(phydev); + mutex_unlock(&priv->an_mutex); - return (state == AMD_XGBE_AN_COMPLETE); + return ret; +} + +static int amd_xgbe_phy_aneg_done(struct phy_device *phydev) +{ + struct amd_xgbe_phy_priv *priv = phydev->priv; + + return (priv->an_result == AMD_XGBE_AN_COMPLETE); } static int amd_xgbe_phy_update_link(struct phy_device *phydev) { struct amd_xgbe_phy_priv *priv = phydev->priv; - enum amd_xgbe_phy_an state; - unsigned int check_again, autoneg; int ret; /* If we're doing auto-negotiation don't report link down */ - mutex_lock(&priv->an_mutex); - state = priv->an_state; - mutex_unlock(&priv->an_mutex); - - if (state != AMD_XGBE_AN_READY) { + if (priv->an_state != AMD_XGBE_AN_READY) { phydev->link = 1; return 0; } - /* Since the device can be in the wrong mode when a link is - * (re-)established (cable connected after the interface is - * up, etc.), the link status may report no link. If there - * is no link, try switching modes and checking the status - * again if auto negotiation is enabled. - */ - check_again = (phydev->autoneg == AUTONEG_ENABLE) ? 1 : 0; -again: /* Link status is latched low, so read once to clear * and then read again to get current state */ @@ -1141,25 +1351,6 @@ again: phydev->link = (ret & MDIO_STAT1_LSTATUS) ? 1 : 0; - if (!phydev->link) { - if (check_again) { - ret = amd_xgbe_phy_switch_mode(phydev); - if (ret < 0) - return ret; - check_again = 0; - goto again; - } - } - - autoneg = (phydev->link && !priv->link) ? 1 : 0; - priv->link = phydev->link; - if (autoneg) { - /* Link is (back) up, re-start auto-negotiation */ - ret = amd_xgbe_phy_config_aneg(phydev); - if (ret < 0) - return ret; - } - return 0; } @@ -1249,6 +1440,7 @@ static int amd_xgbe_phy_read_status(struct phy_device *phydev) static int amd_xgbe_phy_suspend(struct phy_device *phydev) { + struct amd_xgbe_phy_priv *priv = phydev->priv; int ret; mutex_lock(&phydev->lock); @@ -1257,6 +1449,8 @@ static int amd_xgbe_phy_suspend(struct phy_device *phydev) if (ret < 0) goto unlock; + priv->lpm_ctrl = ret; + ret |= MDIO_CTRL1_LPOWER; phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret); @@ -1270,69 +1464,106 @@ unlock: static int amd_xgbe_phy_resume(struct phy_device *phydev) { - int ret; + struct amd_xgbe_phy_priv *priv = phydev->priv; mutex_lock(&phydev->lock); - ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1); - if (ret < 0) - goto unlock; + priv->lpm_ctrl &= ~MDIO_CTRL1_LPOWER; + phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, priv->lpm_ctrl); - ret &= ~MDIO_CTRL1_LPOWER; - phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret); + mutex_unlock(&phydev->lock); - ret = 0; + return 0; +} -unlock: - mutex_unlock(&phydev->lock); +static unsigned int amd_xgbe_phy_resource_count(struct platform_device *pdev, + unsigned int type) +{ + unsigned int count; + int i; - return ret; + for (i = 0, count = 0; i < pdev->num_resources; i++) { + struct resource *r = &pdev->resource[i]; + + if (type == resource_type(r)) + count++; + } + + return count; } static int amd_xgbe_phy_probe(struct phy_device *phydev) { struct amd_xgbe_phy_priv *priv; - struct platform_device *pdev; - struct device *dev; - char *wq_name; - const __be32 *property; - unsigned int speed_set; + struct platform_device *phy_pdev; + struct device *dev, *phy_dev; + unsigned int phy_resnum, phy_irqnum; int ret; - if (!phydev->dev.of_node) + if (!phydev->bus || !phydev->bus->parent) return -EINVAL; - pdev = of_find_device_by_node(phydev->dev.of_node); - if (!pdev) - return -EINVAL; - dev = &pdev->dev; - - wq_name = kasprintf(GFP_KERNEL, "%s-amd-xgbe-phy", phydev->bus->name); - if (!wq_name) { - ret = -ENOMEM; - goto err_pdev; - } + dev = phydev->bus->parent; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - ret = -ENOMEM; - goto err_name; - } + if (!priv) + return -ENOMEM; - priv->pdev = pdev; + priv->pdev = to_platform_device(dev); + priv->adev = ACPI_COMPANION(dev); priv->dev = dev; priv->phydev = phydev; + mutex_init(&priv->an_mutex); + INIT_WORK(&priv->an_irq_work, amd_xgbe_an_irq_work); + INIT_WORK(&priv->an_work, amd_xgbe_an_state_machine); + + if (!priv->adev || acpi_disabled) { + struct device_node *bus_node; + struct device_node *phy_node; + + bus_node = priv->dev->of_node; + phy_node = of_parse_phandle(bus_node, "phy-handle", 0); + if (!phy_node) { + dev_err(dev, "unable to parse phy-handle\n"); + ret = -EINVAL; + goto err_priv; + } + + phy_pdev = of_find_device_by_node(phy_node); + of_node_put(phy_node); + + if (!phy_pdev) { + dev_err(dev, "unable to obtain phy device\n"); + ret = -EINVAL; + goto err_priv; + } + + phy_resnum = 0; + phy_irqnum = 0; + } else { + /* In ACPI, the XGBE and PHY resources are the grouped + * together with the PHY resources at the end + */ + phy_pdev = priv->pdev; + phy_resnum = amd_xgbe_phy_resource_count(phy_pdev, + IORESOURCE_MEM) - 3; + phy_irqnum = amd_xgbe_phy_resource_count(phy_pdev, + IORESOURCE_IRQ) - 1; + } + phy_dev = &phy_pdev->dev; /* Get the device mmio areas */ - priv->rxtx_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->rxtx_res = platform_get_resource(phy_pdev, IORESOURCE_MEM, + phy_resnum++); priv->rxtx_regs = devm_ioremap_resource(dev, priv->rxtx_res); if (IS_ERR(priv->rxtx_regs)) { dev_err(dev, "rxtx ioremap failed\n"); ret = PTR_ERR(priv->rxtx_regs); - goto err_priv; + goto err_put; } - priv->sir0_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + priv->sir0_res = platform_get_resource(phy_pdev, IORESOURCE_MEM, + phy_resnum++); priv->sir0_regs = devm_ioremap_resource(dev, priv->sir0_res); if (IS_ERR(priv->sir0_regs)) { dev_err(dev, "sir0 ioremap failed\n"); @@ -1340,7 +1571,8 @@ static int amd_xgbe_phy_probe(struct phy_device *phydev) goto err_rxtx; } - priv->sir1_res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + priv->sir1_res = platform_get_resource(phy_pdev, IORESOURCE_MEM, + phy_resnum++); priv->sir1_regs = devm_ioremap_resource(dev, priv->sir1_res); if (IS_ERR(priv->sir1_regs)) { dev_err(dev, "sir1 ioremap failed\n"); @@ -1348,40 +1580,98 @@ static int amd_xgbe_phy_probe(struct phy_device *phydev) goto err_sir0; } + /* Get the auto-negotiation interrupt */ + ret = platform_get_irq(phy_pdev, phy_irqnum); + if (ret < 0) { + dev_err(dev, "platform_get_irq failed\n"); + goto err_sir1; + } + priv->an_irq = ret; + /* Get the device speed set property */ - speed_set = 0; - property = of_get_property(dev->of_node, XGBE_PHY_SPEEDSET_PROPERTY, - NULL); - if (property) - speed_set = be32_to_cpu(*property); - - switch (speed_set) { - case 0: - priv->speed_set = AMD_XGBE_PHY_SPEEDSET_1000_10000; - break; - case 1: - priv->speed_set = AMD_XGBE_PHY_SPEEDSET_2500_10000; + ret = device_property_read_u32(phy_dev, XGBE_PHY_SPEEDSET_PROPERTY, + &priv->speed_set); + if (ret) { + dev_err(dev, "invalid %s property\n", + XGBE_PHY_SPEEDSET_PROPERTY); + goto err_sir1; + } + + switch (priv->speed_set) { + case AMD_XGBE_PHY_SPEEDSET_1000_10000: + case AMD_XGBE_PHY_SPEEDSET_2500_10000: break; default: - dev_err(dev, "invalid amd,speed-set property\n"); + dev_err(dev, "invalid %s property\n", + XGBE_PHY_SPEEDSET_PROPERTY); ret = -EINVAL; goto err_sir1; } - priv->link = 1; + if (device_property_present(phy_dev, XGBE_PHY_BLWC_PROPERTY)) { + ret = device_property_read_u32_array(phy_dev, + XGBE_PHY_BLWC_PROPERTY, + priv->serdes_blwc, + XGBE_PHY_SPEEDS); + if (ret) { + dev_err(dev, "invalid %s property\n", + XGBE_PHY_BLWC_PROPERTY); + goto err_sir1; + } + } else { + memcpy(priv->serdes_blwc, amd_xgbe_phy_serdes_blwc, + sizeof(priv->serdes_blwc)); + } - mutex_init(&priv->an_mutex); - INIT_WORK(&priv->an_work, amd_xgbe_an_state_machine); - priv->an_workqueue = create_singlethread_workqueue(wq_name); - if (!priv->an_workqueue) { - ret = -ENOMEM; - goto err_sir1; + if (device_property_present(phy_dev, XGBE_PHY_CDR_RATE_PROPERTY)) { + ret = device_property_read_u32_array(phy_dev, + XGBE_PHY_CDR_RATE_PROPERTY, + priv->serdes_cdr_rate, + XGBE_PHY_SPEEDS); + if (ret) { + dev_err(dev, "invalid %s property\n", + XGBE_PHY_CDR_RATE_PROPERTY); + goto err_sir1; + } + } else { + memcpy(priv->serdes_cdr_rate, amd_xgbe_phy_serdes_cdr_rate, + sizeof(priv->serdes_cdr_rate)); + } + + if (device_property_present(phy_dev, XGBE_PHY_PQ_SKEW_PROPERTY)) { + ret = device_property_read_u32_array(phy_dev, + XGBE_PHY_PQ_SKEW_PROPERTY, + priv->serdes_pq_skew, + XGBE_PHY_SPEEDS); + if (ret) { + dev_err(dev, "invalid %s property\n", + XGBE_PHY_PQ_SKEW_PROPERTY); + goto err_sir1; + } + } else { + memcpy(priv->serdes_pq_skew, amd_xgbe_phy_serdes_pq_skew, + sizeof(priv->serdes_pq_skew)); + } + + if (device_property_present(phy_dev, XGBE_PHY_TX_AMP_PROPERTY)) { + ret = device_property_read_u32_array(phy_dev, + XGBE_PHY_TX_AMP_PROPERTY, + priv->serdes_tx_amp, + XGBE_PHY_SPEEDS); + if (ret) { + dev_err(dev, "invalid %s property\n", + XGBE_PHY_TX_AMP_PROPERTY); + goto err_sir1; + } + } else { + memcpy(priv->serdes_tx_amp, amd_xgbe_phy_serdes_tx_amp, + sizeof(priv->serdes_tx_amp)); } phydev->priv = priv; - kfree(wq_name); - of_dev_put(pdev); + if (!priv->adev || acpi_disabled) + platform_device_put(phy_pdev); return 0; @@ -1400,15 +1690,13 @@ err_rxtx: devm_release_mem_region(dev, priv->rxtx_res->start, resource_size(priv->rxtx_res)); +err_put: + if (!priv->adev || acpi_disabled) + platform_device_put(phy_pdev); + err_priv: devm_kfree(dev, priv); -err_name: - kfree(wq_name); - -err_pdev: - of_dev_put(pdev); - return ret; } @@ -1417,13 +1705,12 @@ static void amd_xgbe_phy_remove(struct phy_device *phydev) struct amd_xgbe_phy_priv *priv = phydev->priv; struct device *dev = priv->dev; - /* Stop any in process auto-negotiation */ - mutex_lock(&priv->an_mutex); - priv->an_state = AMD_XGBE_AN_EXIT; - mutex_unlock(&priv->an_mutex); + if (priv->an_irq_allocated) { + devm_free_irq(dev, priv->an_irq, priv); - flush_workqueue(priv->an_workqueue); - destroy_workqueue(priv->an_workqueue); + flush_workqueue(priv->an_workqueue); + destroy_workqueue(priv->an_workqueue); + } /* Release resources */ devm_iounmap(dev, priv->sir1_regs); diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c index 3ad0e6e16c39..a08a3c78ba97 100644 --- a/drivers/net/phy/fixed_phy.c +++ b/drivers/net/phy/fixed_phy.c @@ -168,7 +168,7 @@ int fixed_phy_set_link_update(struct phy_device *phydev, struct fixed_mdio_bus *fmb = &platform_fmb; struct fixed_phy *fp; - if (!link_update || !phydev || !phydev->bus) + if (!phydev || !phydev->bus) return -EINVAL; list_for_each_entry(fp, &fmb->phys, node) { diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 50051f271b10..095ef3fe369a 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -443,9 +443,13 @@ static bool mdio_bus_phy_may_suspend(struct phy_device *phydev) if (!drv || !phydrv->suspend) return false; - /* PHY not attached? May suspend. */ + /* PHY not attached? May suspend if the PHY has not already been + * suspended as part of a prior call to phy_disconnect() -> + * phy_detach() -> phy_suspend() because the parent netdev might be the + * MDIO bus driver and clock gated at this point. + */ if (!netdev) - return true; + return !phydev->suspended; /* Don't suspend PHY if the attched netdev parent may wakeup. * The parent may point to a PCI device, as in tg3 driver. @@ -465,7 +469,6 @@ static bool mdio_bus_phy_may_suspend(struct phy_device *phydev) static int mdio_bus_suspend(struct device *dev) { - struct phy_driver *phydrv = to_phy_driver(dev->driver); struct phy_device *phydev = to_phy_device(dev); /* We must stop the state machine manually, otherwise it stops out of @@ -479,19 +482,18 @@ static int mdio_bus_suspend(struct device *dev) if (!mdio_bus_phy_may_suspend(phydev)) return 0; - return phydrv->suspend(phydev); + return phy_suspend(phydev); } static int mdio_bus_resume(struct device *dev) { - struct phy_driver *phydrv = to_phy_driver(dev->driver); struct phy_device *phydev = to_phy_device(dev); int ret; if (!mdio_bus_phy_may_suspend(phydev)) goto no_resume; - ret = phydrv->resume(phydev); + ret = phy_resume(phydev); if (ret < 0) return ret; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 767cd110f496..cdcac6aa4260 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -439,6 +439,9 @@ int phy_start_aneg(struct phy_device *phydev) if (AUTONEG_DISABLE == phydev->autoneg) phy_sanitize_settings(phydev); + /* Invalidate LP advertising flags */ + phydev->lp_advertising = 0; + err = phydev->drv->config_aneg(phydev); if (err < 0) goto out_unlock; diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 3fc91e89f5a5..bdfe51fc3a65 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -699,6 +699,7 @@ int phy_suspend(struct phy_device *phydev) { struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver); struct ethtool_wolinfo wol = { .cmd = ETHTOOL_GWOL }; + int ret = 0; /* If the device has WOL enabled, we cannot suspend the PHY */ phy_ethtool_get_wol(phydev, &wol); @@ -706,18 +707,31 @@ int phy_suspend(struct phy_device *phydev) return -EBUSY; if (phydrv->suspend) - return phydrv->suspend(phydev); - return 0; + ret = phydrv->suspend(phydev); + + if (ret) + return ret; + + phydev->suspended = true; + + return ret; } EXPORT_SYMBOL(phy_suspend); int phy_resume(struct phy_device *phydev) { struct phy_driver *phydrv = to_phy_driver(phydev->dev.driver); + int ret = 0; if (phydrv->resume) - return phydrv->resume(phydev); - return 0; + ret = phydrv->resume(phydev); + + if (ret) + return ret; + + phydev->suspended = false; + + return ret; } EXPORT_SYMBOL(phy_resume); diff --git a/drivers/net/ppp/ppp_deflate.c b/drivers/net/ppp/ppp_deflate.c index 602c625d95d5..b5edc7f96a39 100644 --- a/drivers/net/ppp/ppp_deflate.c +++ b/drivers/net/ppp/ppp_deflate.c @@ -246,7 +246,7 @@ static int z_compress(void *arg, unsigned char *rptr, unsigned char *obuf, /* * See if we managed to reduce the size of the packet. */ - if (olen < isize) { + if (olen < isize && olen <= osize) { state->stats.comp_bytes += olen; state->stats.comp_packets++; } else { diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 93e224217e24..0e62274e884a 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -28,6 +28,7 @@ #include <net/genetlink.h> #include <net/netlink.h> #include <net/sch_generic.h> +#include <net/switchdev.h> #include <generated/utsrelease.h> #include <linux/if_team.h> @@ -176,7 +177,6 @@ static int __team_option_inst_add(struct team *team, struct team_option *option, static int __team_option_inst_add_option(struct team *team, struct team_option *option) { - struct team_port *port; int err; if (!option->per_port) { @@ -184,12 +184,6 @@ static int __team_option_inst_add_option(struct team *team, if (err) goto inst_del_option; } - - list_for_each_entry(port, &team->port_list, list) { - err = __team_option_inst_add(team, option, port); - if (err) - goto inst_del_option; - } return 0; inst_del_option: @@ -629,6 +623,7 @@ static int team_change_mode(struct team *team, const char *kind) static void team_notify_peers_work(struct work_struct *work) { struct team *team; + int val; team = container_of(work, struct team, notify_peers.dw.work); @@ -636,9 +631,14 @@ static void team_notify_peers_work(struct work_struct *work) schedule_delayed_work(&team->notify_peers.dw, 0); return; } + val = atomic_dec_if_positive(&team->notify_peers.count_pending); + if (val < 0) { + rtnl_unlock(); + return; + } call_netdevice_notifiers(NETDEV_NOTIFY_PEERS, team->dev); rtnl_unlock(); - if (!atomic_dec_and_test(&team->notify_peers.count_pending)) + if (val) schedule_delayed_work(&team->notify_peers.dw, msecs_to_jiffies(team->notify_peers.interval)); } @@ -669,6 +669,7 @@ static void team_notify_peers_fini(struct team *team) static void team_mcast_rejoin_work(struct work_struct *work) { struct team *team; + int val; team = container_of(work, struct team, mcast_rejoin.dw.work); @@ -676,9 +677,14 @@ static void team_mcast_rejoin_work(struct work_struct *work) schedule_delayed_work(&team->mcast_rejoin.dw, 0); return; } + val = atomic_dec_if_positive(&team->mcast_rejoin.count_pending); + if (val < 0) { + rtnl_unlock(); + return; + } call_netdevice_notifiers(NETDEV_RESEND_IGMP, team->dev); rtnl_unlock(); - if (!atomic_dec_and_test(&team->mcast_rejoin.count_pending)) + if (val) schedule_delayed_work(&team->mcast_rejoin.dw, msecs_to_jiffies(team->mcast_rejoin.interval)); } @@ -1920,7 +1926,7 @@ static netdev_features_t team_fix_features(struct net_device *dev, struct team *team = netdev_priv(dev); netdev_features_t mask; - mask = features; + mask = features | NETIF_F_HW_SWITCH_OFFLOAD; features &= ~NETIF_F_ONE_FOR_ALL; features |= NETIF_F_ALL_FOR_ALL; @@ -1970,6 +1976,8 @@ static const struct net_device_ops team_netdev_ops = { .ndo_del_slave = team_del_slave, .ndo_fix_features = team_fix_features, .ndo_change_carrier = team_change_carrier, + .ndo_bridge_setlink = ndo_dflt_netdev_switch_port_bridge_setlink, + .ndo_bridge_dellink = ndo_dflt_netdev_switch_port_bridge_dellink, }; /*********************** diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 8c8dc16839a7..857dca47bf80 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -65,7 +65,6 @@ #include <linux/nsproxy.h> #include <linux/virtio_net.h> #include <linux/rcupdate.h> -#include <net/ipv6.h> #include <net/net_namespace.h> #include <net/netns/generic.h> #include <net/rtnetlink.h> @@ -124,10 +123,9 @@ struct tap_filter { unsigned char addr[FLT_EXACT_COUNT][ETH_ALEN]; }; -/* DEFAULT_MAX_NUM_RSS_QUEUES were chosen to let the rx/tx queues allocated for - * the netdevice to be fit in one page. So we can make sure the success of - * memory allocation. TODO: increase the limit. */ -#define MAX_TAP_QUEUES DEFAULT_MAX_NUM_RSS_QUEUES +/* MAX_TAP_QUEUES 256 is chosen to allow rx/tx queues to be equal + * to max number of VCPUs in guest. */ +#define MAX_TAP_QUEUES 256 #define MAX_TAP_FLOWS 4096 #define TUN_FLOW_EXPIRE (3 * HZ) @@ -187,7 +185,7 @@ struct tun_struct { struct net_device *dev; netdev_features_t set_features; #define TUN_USER_FEATURES (NETIF_F_HW_CSUM|NETIF_F_TSO_ECN|NETIF_F_TSO| \ - NETIF_F_TSO6) + NETIF_F_TSO6|NETIF_F_UFO) int vnet_hdr_sz; int sndbuf; @@ -258,7 +256,6 @@ static void tun_flow_delete(struct tun_struct *tun, struct tun_flow_entry *e) { tun_debug(KERN_INFO, tun, "delete flow: hash %u index %u\n", e->rxhash, e->queue_index); - sock_rps_reset_flow_hash(e->rps_rxhash); hlist_del_rcu(&e->hash_link); kfree_rcu(e, rcu); --tun->flow_count; @@ -375,10 +372,8 @@ unlock: */ static inline void tun_flow_save_rps_rxhash(struct tun_flow_entry *e, u32 hash) { - if (unlikely(e->rps_rxhash != hash)) { - sock_rps_reset_flow_hash(e->rps_rxhash); + if (unlikely(e->rps_rxhash != hash)) e->rps_rxhash = hash; - } } /* We try to identify a flow through its rxhash first. The reason that @@ -1167,8 +1162,6 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, break; } - skb_reset_network_header(skb); - if (gso.gso_type != VIRTIO_NET_HDR_GSO_NONE) { pr_debug("GSO!\n"); switch (gso.gso_type & ~VIRTIO_NET_HDR_GSO_ECN) { @@ -1179,20 +1172,8 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; break; case VIRTIO_NET_HDR_GSO_UDP: - { - static bool warned; - - if (!warned) { - warned = true; - netdev_warn(tun->dev, - "%s: using disabled UFO feature; please fix this program\n", - current->comm); - } skb_shinfo(skb)->gso_type = SKB_GSO_UDP; - if (skb->protocol == htons(ETH_P_IPV6)) - ipv6_proxy_select_ident(skb); break; - } default: tun->dev->stats.rx_frame_errors++; kfree_skb(skb); @@ -1221,6 +1202,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, skb_shinfo(skb)->tx_flags |= SKBTX_SHARED_FRAG; } + skb_reset_network_header(skb); skb_probe_transport_header(skb, 0); rxhash = skb_get_hash(skb); @@ -1261,7 +1243,7 @@ static ssize_t tun_put_user(struct tun_struct *tun, int vlan_hlen = 0; int vnet_hdr_sz = 0; - if (vlan_tx_tag_present(skb)) + if (skb_vlan_tag_present(skb)) vlan_hlen = VLAN_HLEN; if (tun->flags & IFF_VNET_HDR) @@ -1298,6 +1280,8 @@ static ssize_t tun_put_user(struct tun_struct *tun, gso.gso_type = VIRTIO_NET_HDR_GSO_TCPV4; else if (sinfo->gso_type & SKB_GSO_TCPV6) gso.gso_type = VIRTIO_NET_HDR_GSO_TCPV6; + else if (sinfo->gso_type & SKB_GSO_UDP) + gso.gso_type = VIRTIO_NET_HDR_GSO_UDP; else { pr_err("unexpected GSO type: " "0x%x, gso_size %d, hdr_len %d\n", @@ -1338,7 +1322,7 @@ static ssize_t tun_put_user(struct tun_struct *tun, } veth; veth.h_vlan_proto = skb->vlan_proto; - veth.h_vlan_TCI = htons(vlan_tx_tag_get(skb)); + veth.h_vlan_TCI = htons(skb_vlan_tag_get(skb)); vlan_offset = offsetof(struct vlan_ethhdr, h_vlan_proto); @@ -1380,7 +1364,7 @@ static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile, skb = __skb_recv_datagram(tfile->socket.sk, noblock ? MSG_DONTWAIT : 0, &peeked, &off, &err); if (!skb) - return 0; + return err; ret = tun_put_user(tun, tfile, skb, to); if (unlikely(ret < 0)) @@ -1501,7 +1485,7 @@ static int tun_recvmsg(struct kiocb *iocb, struct socket *sock, goto out; } ret = tun_do_read(tun, tfile, &m->msg_iter, flags & MSG_DONTWAIT); - if (ret > total_len) { + if (ret > (ssize_t)total_len) { m->msg_flags |= MSG_TRUNC; ret = flags & MSG_TRUNC ? ret : total_len; } @@ -1566,6 +1550,17 @@ static DEVICE_ATTR(tun_flags, 0444, tun_show_flags, NULL); static DEVICE_ATTR(owner, 0444, tun_show_owner, NULL); static DEVICE_ATTR(group, 0444, tun_show_group, NULL); +static struct attribute *tun_dev_attrs[] = { + &dev_attr_tun_flags.attr, + &dev_attr_owner.attr, + &dev_attr_group.attr, + NULL +}; + +static const struct attribute_group tun_attr_group = { + .attrs = tun_dev_attrs +}; + static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) { struct tun_struct *tun; @@ -1646,6 +1641,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) dev_net_set(dev, net); dev->rtnl_link_ops = &tun_link_ops; dev->ifindex = tfile->ifindex; + dev->sysfs_groups[0] = &tun_attr_group; tun = netdev_priv(dev); tun->dev = dev; @@ -1681,11 +1677,6 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) err = register_netdevice(tun->dev); if (err < 0) goto err_detach; - - if (device_create_file(&tun->dev->dev, &dev_attr_tun_flags) || - device_create_file(&tun->dev->dev, &dev_attr_owner) || - device_create_file(&tun->dev->dev, &dev_attr_group)) - pr_err("Failed to create tun sysfs files\n"); } netif_carrier_on(tun->dev); @@ -1746,6 +1737,11 @@ static int set_offload(struct tun_struct *tun, unsigned long arg) features |= NETIF_F_TSO6; arg &= ~(TUN_F_TSO4|TUN_F_TSO6); } + + if (arg & TUN_F_UFO) { + features |= NETIF_F_UFO; + arg &= ~TUN_F_UFO; + } } /* This gives the user a way to test for new features in future by diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 9c5aa922a9f4..6b8efcabb816 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -58,7 +58,6 @@ #include <linux/module.h> #include <linux/ethtool.h> #include <linux/usb.h> -#include <linux/timer.h> #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/tty_flip.h> @@ -154,6 +153,7 @@ struct hso_net { struct hso_device *parent; struct net_device *net; struct rfkill *rfkill; + char name[24]; struct usb_endpoint_descriptor *in_endp; struct usb_endpoint_descriptor *out_endp; @@ -274,7 +274,6 @@ struct hso_device { u8 usb_gone; struct work_struct async_get_intf; struct work_struct async_put_intf; - struct work_struct reset_device; struct usb_device *usb; struct usb_interface *interface; @@ -340,7 +339,6 @@ static void async_put_intf(struct work_struct *data); static int hso_put_activity(struct hso_device *hso_dev); static int hso_get_activity(struct hso_device *hso_dev); static void tiocmget_intr_callback(struct urb *urb); -static void reset_device(struct work_struct *data); /*****************************************************************************/ /* Helping functions */ /*****************************************************************************/ @@ -533,6 +531,13 @@ static ssize_t hso_sysfs_show_porttype(struct device *dev, } static DEVICE_ATTR(hsotype, S_IRUGO, hso_sysfs_show_porttype, NULL); +static struct attribute *hso_serial_dev_attrs[] = { + &dev_attr_hsotype.attr, + NULL +}; + +ATTRIBUTE_GROUPS(hso_serial_dev); + static int hso_urb_to_index(struct hso_serial *serial, struct urb *urb) { int idx; @@ -696,7 +701,7 @@ static void handle_usb_error(int status, const char *function, case -ETIMEDOUT: explanation = "protocol error"; if (hso_dev) - schedule_work(&hso_dev->reset_device); + usb_queue_reset_device(hso_dev->interface); break; default: explanation = "unknown status"; @@ -1271,7 +1276,6 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp) goto err_out; D1("Opening %d", serial->minor); - kref_get(&serial->parent->ref); /* setup */ tty->driver_data = serial; @@ -1290,7 +1294,8 @@ static int hso_serial_open(struct tty_struct *tty, struct file *filp) if (result) { hso_stop_serial_device(serial->parent); serial->port.count--; - kref_put(&serial->parent->ref, hso_serial_ref_free); + } else { + kref_get(&serial->parent->ref); } } else { D1("Port was already open"); @@ -1340,8 +1345,6 @@ static void hso_serial_close(struct tty_struct *tty, struct file *filp) usb_autopm_put_interface(serial->parent->interface); mutex_unlock(&serial->parent->mutex); - - kref_put(&serial->parent->ref, hso_serial_ref_free); } /* close the requested serial port */ @@ -1392,6 +1395,16 @@ static int hso_serial_write_room(struct tty_struct *tty) return room; } +static void hso_serial_cleanup(struct tty_struct *tty) +{ + struct hso_serial *serial = tty->driver_data; + + if (!serial) + return; + + kref_put(&serial->parent->ref, hso_serial_ref_free); +} + /* setup the term */ static void hso_serial_set_termios(struct tty_struct *tty, struct ktermios *old) { @@ -2198,8 +2211,8 @@ static int hso_stop_serial_device(struct hso_device *hso_dev) for (i = 0; i < serial->num_rx_urbs; i++) { if (serial->rx_urb[i]) { - usb_kill_urb(serial->rx_urb[i]); - serial->rx_urb_filled[i] = 0; + usb_kill_urb(serial->rx_urb[i]); + serial->rx_urb_filled[i] = 0; } } serial->curr_rx_urb_idx = 0; @@ -2228,15 +2241,15 @@ static int hso_stop_serial_device(struct hso_device *hso_dev) return 0; } +static void hso_serial_tty_unregister(struct hso_serial *serial) +{ + tty_unregister_device(tty_drv, serial->minor); +} + static void hso_serial_common_free(struct hso_serial *serial) { int i; - if (serial->parent->dev) - device_remove_file(serial->parent->dev, &dev_attr_hsotype); - - tty_unregister_device(tty_drv, serial->minor); - for (i = 0; i < serial->num_rx_urbs; i++) { /* unlink and free RX URB */ usb_free_urb(serial->rx_urb[i]); @@ -2246,6 +2259,7 @@ static void hso_serial_common_free(struct hso_serial *serial) /* unlink and free TX URB */ usb_free_urb(serial->tx_urb); + kfree(serial->tx_buffer); kfree(serial->tx_data); tty_port_destroy(&serial->port); } @@ -2264,11 +2278,10 @@ static int hso_serial_common_create(struct hso_serial *serial, int num_urbs, goto exit; /* register our minor number */ - serial->parent->dev = tty_port_register_device(&serial->port, tty_drv, - minor, &serial->parent->interface->dev); + serial->parent->dev = tty_port_register_device_attr(&serial->port, + tty_drv, minor, &serial->parent->interface->dev, + serial->parent, hso_serial_dev_groups); dev = serial->parent->dev; - dev_set_drvdata(dev, serial->parent); - i = device_create_file(dev, &dev_attr_hsotype); /* fill in specific data for later use */ serial->minor = minor; @@ -2316,6 +2329,7 @@ static int hso_serial_common_create(struct hso_serial *serial, int num_urbs, return 0; exit: + hso_serial_tty_unregister(serial); hso_serial_common_free(serial); return -1; } @@ -2338,7 +2352,6 @@ static struct hso_device *hso_create_device(struct usb_interface *intf, INIT_WORK(&hso_dev->async_get_intf, async_get_intf); INIT_WORK(&hso_dev->async_put_intf, async_put_intf); - INIT_WORK(&hso_dev->reset_device, reset_device); return hso_dev; } @@ -2459,27 +2472,21 @@ static void hso_create_rfkill(struct hso_device *hso_dev, { struct hso_net *hso_net = dev2net(hso_dev); struct device *dev = &hso_net->net->dev; - char *rfkn; + static u32 rfkill_counter; - rfkn = kzalloc(20, GFP_KERNEL); - if (!rfkn) - dev_err(dev, "%s - Out of memory\n", __func__); - - snprintf(rfkn, 20, "hso-%d", - interface->altsetting->desc.bInterfaceNumber); + snprintf(hso_net->name, sizeof(hso_net->name), "hso-%d", + rfkill_counter++); - hso_net->rfkill = rfkill_alloc(rfkn, + hso_net->rfkill = rfkill_alloc(hso_net->name, &interface_to_usbdev(interface)->dev, RFKILL_TYPE_WWAN, &hso_rfkill_ops, hso_dev); if (!hso_net->rfkill) { dev_err(dev, "%s - Out of memory\n", __func__); - kfree(rfkn); return; } if (rfkill_register(hso_net->rfkill) < 0) { rfkill_destroy(hso_net->rfkill); - kfree(rfkn); hso_net->rfkill = NULL; dev_err(dev, "%s - Failed to register rfkill\n", __func__); return; @@ -2594,7 +2601,6 @@ static void hso_free_serial_device(struct hso_device *hso_dev) if (!serial) return; - set_serial_by_index(serial->minor, NULL); hso_serial_common_free(serial); @@ -2684,6 +2690,7 @@ static struct hso_device *hso_create_bulk_serial_device( return hso_dev; exit2: + hso_serial_tty_unregister(serial); hso_serial_common_free(serial); exit: hso_free_tiomget(serial); @@ -3083,26 +3090,6 @@ out: return result; } -static void reset_device(struct work_struct *data) -{ - struct hso_device *hso_dev = - container_of(data, struct hso_device, reset_device); - struct usb_device *usb = hso_dev->usb; - int result; - - if (hso_dev->usb_gone) { - D1("No reset during disconnect\n"); - } else { - result = usb_lock_device_for_reset(usb, hso_dev->interface); - if (result < 0) - D1("unable to lock device for reset: %d\n", result); - else { - usb_reset_device(usb); - usb_unlock_device(usb); - } - } -} - static void hso_serial_ref_free(struct kref *ref) { struct hso_device *hso_dev = container_of(ref, struct hso_device, ref); @@ -3112,18 +3099,22 @@ static void hso_serial_ref_free(struct kref *ref) static void hso_free_interface(struct usb_interface *interface) { - struct hso_serial *hso_dev; + struct hso_serial *serial; int i; for (i = 0; i < HSO_SERIAL_TTY_MINORS; i++) { if (serial_table[i] && (serial_table[i]->interface == interface)) { - hso_dev = dev2ser(serial_table[i]); - tty_port_tty_hangup(&hso_dev->port, false); - mutex_lock(&hso_dev->parent->mutex); - hso_dev->parent->usb_gone = 1; - mutex_unlock(&hso_dev->parent->mutex); + serial = dev2ser(serial_table[i]); + tty_port_tty_hangup(&serial->port, false); + mutex_lock(&serial->parent->mutex); + serial->parent->usb_gone = 1; + mutex_unlock(&serial->parent->mutex); + cancel_work_sync(&serial_table[i]->async_put_intf); + cancel_work_sync(&serial_table[i]->async_get_intf); + hso_serial_tty_unregister(serial); kref_put(&serial_table[i]->ref, hso_serial_ref_free); + set_serial_by_index(i, NULL); } } @@ -3215,6 +3206,7 @@ static const struct tty_operations hso_serial_ops = { .close = hso_serial_close, .write = hso_serial_write, .write_room = hso_serial_write_room, + .cleanup = hso_serial_cleanup, .ioctl = hso_serial_ioctl, .set_termios = hso_serial_set_termios, .chars_in_buffer = hso_serial_chars_in_buffer, diff --git a/drivers/net/usb/kaweth.c b/drivers/net/usb/kaweth.c index dcb6d33141e0..1e9cdca37014 100644 --- a/drivers/net/usb/kaweth.c +++ b/drivers/net/usb/kaweth.c @@ -1276,7 +1276,7 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length) awd.done = 0; urb->context = &awd; - status = usb_submit_urb(urb, GFP_NOIO); + status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { // something went wrong usb_free_urb(urb); diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index b8a82b86f909..602dc6668c3a 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -56,6 +56,8 @@ struct qmi_wwan_state { /* default ethernet address used by the modem */ static const u8 default_modem_addr[ETH_ALEN] = {0x02, 0x50, 0xf3}; +static const u8 buggy_fw_addr[ETH_ALEN] = {0x00, 0xa0, 0xc6, 0x00, 0x00, 0x00}; + /* Make up an ethernet header if the packet doesn't have one. * * A firmware bug common among several devices cause them to send raw @@ -332,10 +334,12 @@ next_desc: usb_driver_release_interface(driver, info->data); } - /* Never use the same address on both ends of the link, even - * if the buggy firmware told us to. + /* Never use the same address on both ends of the link, even if the + * buggy firmware told us to. Or, if device is assigned the well-known + * buggy firmware MAC address, replace it with a random address, */ - if (ether_addr_equal(dev->net->dev_addr, default_modem_addr)) + if (ether_addr_equal(dev->net->dev_addr, default_modem_addr) || + ether_addr_equal(dev->net->dev_addr, buggy_fw_addr)) eth_hw_addr_random(dev->net); /* make MAC addr easily distinguishable from an IP header */ diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 2d1c77e81836..5980ac6c48dd 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -27,7 +27,7 @@ #include <linux/usb/cdc.h> /* Version Information */ -#define DRIVER_VERSION "v1.07.0 (2014/10/09)" +#define DRIVER_VERSION "v1.08.0 (2015/01/13)" #define DRIVER_AUTHOR "Realtek linux nic maintainers <nic_swsd@realtek.com>" #define DRIVER_DESC "Realtek RTL8152/RTL8153 Based USB Ethernet Adapters" #define MODULENAME "r8152" @@ -448,6 +448,7 @@ enum rtl_register_content { #define RTL8152_RMS (VLAN_ETH_FRAME_LEN + VLAN_HLEN) #define RTL8153_RMS RTL8153_MAX_PACKET #define RTL8152_TX_TIMEOUT (5 * HZ) +#define RTL8152_NAPI_WEIGHT 64 /* rtl8152 flags */ enum rtl8152_flags { @@ -457,7 +458,7 @@ enum rtl8152_flags { RTL8152_LINK_CHG, SELECTIVE_SUSPEND, PHY_RESET, - SCHEDULE_TASKLET, + SCHEDULE_NAPI, }; /* Define these values to match your device */ @@ -488,16 +489,16 @@ struct rx_desc { #define RX_LEN_MASK 0x7fff __le32 opts2; -#define RD_UDP_CS (1 << 23) -#define RD_TCP_CS (1 << 22) -#define RD_IPV6_CS (1 << 20) -#define RD_IPV4_CS (1 << 19) +#define RD_UDP_CS BIT(23) +#define RD_TCP_CS BIT(22) +#define RD_IPV6_CS BIT(20) +#define RD_IPV4_CS BIT(19) __le32 opts3; -#define IPF (1 << 23) /* IP checksum fail */ -#define UDPF (1 << 22) /* UDP checksum fail */ -#define TCPF (1 << 21) /* TCP checksum fail */ -#define RX_VLAN_TAG (1 << 16) +#define IPF BIT(23) /* IP checksum fail */ +#define UDPF BIT(22) /* UDP checksum fail */ +#define TCPF BIT(21) /* TCP checksum fail */ +#define RX_VLAN_TAG BIT(16) __le32 opts4; __le32 opts5; @@ -506,24 +507,24 @@ struct rx_desc { struct tx_desc { __le32 opts1; -#define TX_FS (1 << 31) /* First segment of a packet */ -#define TX_LS (1 << 30) /* Final segment of a packet */ -#define GTSENDV4 (1 << 28) -#define GTSENDV6 (1 << 27) +#define TX_FS BIT(31) /* First segment of a packet */ +#define TX_LS BIT(30) /* Final segment of a packet */ +#define GTSENDV4 BIT(28) +#define GTSENDV6 BIT(27) #define GTTCPHO_SHIFT 18 #define GTTCPHO_MAX 0x7fU #define TX_LEN_MAX 0x3ffffU __le32 opts2; -#define UDP_CS (1 << 31) /* Calculate UDP/IP checksum */ -#define TCP_CS (1 << 30) /* Calculate TCP/IP checksum */ -#define IPV4_CS (1 << 29) /* Calculate IPv4 checksum */ -#define IPV6_CS (1 << 28) /* Calculate IPv6 checksum */ +#define UDP_CS BIT(31) /* Calculate UDP/IP checksum */ +#define TCP_CS BIT(30) /* Calculate TCP/IP checksum */ +#define IPV4_CS BIT(29) /* Calculate IPv4 checksum */ +#define IPV6_CS BIT(28) /* Calculate IPv6 checksum */ #define MSS_SHIFT 17 #define MSS_MAX 0x7ffU #define TCPHO_SHIFT 17 #define TCPHO_MAX 0x7ffU -#define TX_VLAN_TAG (1 << 16) +#define TX_VLAN_TAG BIT(16) }; struct r8152; @@ -549,14 +550,14 @@ struct tx_agg { struct r8152 { unsigned long flags; struct usb_device *udev; - struct tasklet_struct tl; + struct napi_struct napi; struct usb_interface *intf; struct net_device *netdev; struct urb *intr_urb; struct tx_agg tx_info[RTL8152_MAX_TX]; struct rx_agg rx_info[RTL8152_MAX_RX]; struct list_head rx_done, tx_free; - struct sk_buff_head tx_queue; + struct sk_buff_head tx_queue, rx_queue; spinlock_t rx_lock, tx_lock; struct delayed_work schedule; struct mii_if_info mii; @@ -580,7 +581,6 @@ struct r8152 { u16 ocp_base; u8 *intr_buff; u8 version; - u8 speed; }; enum rtl_version { @@ -833,9 +833,6 @@ static void ocp_write_word(struct r8152 *tp, u16 type, u16 index, u32 data) index &= ~3; } - generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); - - data |= __le32_to_cpu(tmp) & ~mask; tmp = __cpu_to_le32(data); generic_ocp_write(tp, index, byen, sizeof(tmp), &tmp, type); @@ -874,9 +871,6 @@ static void ocp_write_byte(struct r8152 *tp, u16 type, u16 index, u32 data) index &= ~3; } - generic_ocp_read(tp, index, sizeof(tmp), &tmp, type); - - data |= __le32_to_cpu(tmp) & ~mask; tmp = __cpu_to_le32(data); generic_ocp_write(tp, index, byen, sizeof(tmp), &tmp, type); @@ -926,12 +920,6 @@ static void sram_write(struct r8152 *tp, u16 addr, u16 data) ocp_reg_write(tp, OCP_SRAM_DATA, data); } -static u16 sram_read(struct r8152 *tp, u16 addr) -{ - ocp_reg_write(tp, OCP_SRAM_ADDR, addr); - return ocp_reg_read(tp, OCP_SRAM_DATA); -} - static int read_mii_word(struct net_device *netdev, int phy_id, int reg) { struct r8152 *tp = netdev_priv(netdev); @@ -1062,7 +1050,7 @@ static void read_bulk_callback(struct urb *urb) spin_lock(&tp->rx_lock); list_add_tail(&agg->list, &tp->rx_done); spin_unlock(&tp->rx_lock); - tasklet_schedule(&tp->tl); + napi_schedule(&tp->napi); return; case -ESHUTDOWN: set_bit(RTL8152_UNPLUG, &tp->flags); @@ -1126,7 +1114,7 @@ static void write_bulk_callback(struct urb *urb) return; if (!skb_queue_empty(&tp->tx_queue)) - tasklet_schedule(&tp->tl); + napi_schedule(&tp->napi); } static void intr_callback(struct urb *urb) @@ -1168,12 +1156,12 @@ static void intr_callback(struct urb *urb) d = urb->transfer_buffer; if (INTR_LINK & __le16_to_cpu(d[0])) { - if (!(tp->speed & LINK_STATUS)) { + if (!netif_carrier_ok(tp->netdev)) { set_bit(RTL8152_LINK_CHG, &tp->flags); schedule_delayed_work(&tp->schedule, 0); } } else { - if (tp->speed & LINK_STATUS) { + if (netif_carrier_ok(tp->netdev)) { set_bit(RTL8152_LINK_CHG, &tp->flags); schedule_delayed_work(&tp->schedule, 0); } @@ -1245,6 +1233,7 @@ static int alloc_all_mem(struct r8152 *tp) spin_lock_init(&tp->tx_lock); INIT_LIST_HEAD(&tp->tx_free); skb_queue_head_init(&tp->tx_queue); + skb_queue_head_init(&tp->rx_queue); for (i = 0; i < RTL8152_MAX_RX; i++) { buf = kmalloc_node(agg_buf_sz, GFP_KERNEL, node); @@ -1341,18 +1330,6 @@ static struct tx_agg *r8152_get_tx_agg(struct r8152 *tp) return agg; } -static inline __be16 get_protocol(struct sk_buff *skb) -{ - __be16 protocol; - - if (skb->protocol == htons(ETH_P_8021Q)) - protocol = vlan_eth_hdr(skb)->h_vlan_encapsulated_proto; - else - protocol = skb->protocol; - - return protocol; -} - /* r8152_csum_workaround() * The hw limites the value the transport offset. When the offset is out of the * range, calculate the checksum by sw. @@ -1421,10 +1398,10 @@ static int msdn_giant_send_check(struct sk_buff *skb) static inline void rtl_tx_vlan_tag(struct tx_desc *desc, struct sk_buff *skb) { - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { u32 opts2; - opts2 = TX_VLAN_TAG | swab16(vlan_tx_tag_get(skb)); + opts2 = TX_VLAN_TAG | swab16(skb_vlan_tag_get(skb)); desc->opts2 |= cpu_to_le32(opts2); } } @@ -1458,7 +1435,7 @@ static int r8152_tx_csum(struct r8152 *tp, struct tx_desc *desc, goto unavailable; } - switch (get_protocol(skb)) { + switch (vlan_get_protocol(skb)) { case htons(ETH_P_IP): opts1 |= GTSENDV4; break; @@ -1489,7 +1466,7 @@ static int r8152_tx_csum(struct r8152 *tp, struct tx_desc *desc, goto unavailable; } - switch (get_protocol(skb)) { + switch (vlan_get_protocol(skb)) { case htons(ETH_P_IP): opts2 |= IPV4_CS; ip_protocol = ip_hdr(skb)->protocol; @@ -1649,13 +1626,32 @@ return_result: return checksum; } -static void rx_bottom(struct r8152 *tp) +static int rx_bottom(struct r8152 *tp, int budget) { unsigned long flags; struct list_head *cursor, *next, rx_queue; + int ret = 0, work_done = 0; + + if (!skb_queue_empty(&tp->rx_queue)) { + while (work_done < budget) { + struct sk_buff *skb = __skb_dequeue(&tp->rx_queue); + struct net_device *netdev = tp->netdev; + struct net_device_stats *stats = &netdev->stats; + unsigned int pkt_len; + + if (!skb) + break; + + pkt_len = skb->len; + napi_gro_receive(&tp->napi, skb); + work_done++; + stats->rx_packets++; + stats->rx_bytes += pkt_len; + } + } if (list_empty(&tp->rx_done)) - return; + goto out1; INIT_LIST_HEAD(&rx_queue); spin_lock_irqsave(&tp->rx_lock, flags); @@ -1708,9 +1704,14 @@ static void rx_bottom(struct r8152 *tp) skb_put(skb, pkt_len); skb->protocol = eth_type_trans(skb, netdev); rtl_rx_vlan_tag(rx_desc, skb); - netif_receive_skb(skb); - stats->rx_packets++; - stats->rx_bytes += pkt_len; + if (work_done < budget) { + napi_gro_receive(&tp->napi, skb); + work_done++; + stats->rx_packets++; + stats->rx_bytes += pkt_len; + } else { + __skb_queue_tail(&tp->rx_queue, skb); + } find_next_rx: rx_data = rx_agg_align(rx_data + pkt_len + CRC_SIZE); @@ -1720,8 +1721,22 @@ find_next_rx: } submit: - r8152_submit_rx(tp, agg, GFP_ATOMIC); + if (!ret) { + ret = r8152_submit_rx(tp, agg, GFP_ATOMIC); + } else { + urb->actual_length = 0; + list_add_tail(&agg->list, next); + } } + + if (!list_empty(&rx_queue)) { + spin_lock_irqsave(&tp->rx_lock, flags); + list_splice_tail(&rx_queue, &tp->rx_done); + spin_unlock_irqrestore(&tp->rx_lock, flags); + } + +out1: + return work_done; } static void tx_bottom(struct r8152 *tp) @@ -1761,12 +1776,8 @@ static void tx_bottom(struct r8152 *tp) } while (res == 0); } -static void bottom_half(unsigned long data) +static void bottom_half(struct r8152 *tp) { - struct r8152 *tp; - - tp = (struct r8152 *)data; - if (test_bit(RTL8152_UNPLUG, &tp->flags)) return; @@ -1778,17 +1789,38 @@ static void bottom_half(unsigned long data) if (!netif_carrier_ok(tp->netdev)) return; - clear_bit(SCHEDULE_TASKLET, &tp->flags); + clear_bit(SCHEDULE_NAPI, &tp->flags); - rx_bottom(tp); tx_bottom(tp); } +static int r8152_poll(struct napi_struct *napi, int budget) +{ + struct r8152 *tp = container_of(napi, struct r8152, napi); + int work_done; + + work_done = rx_bottom(tp, budget); + bottom_half(tp); + + if (work_done < budget) { + napi_complete(napi); + if (!list_empty(&tp->rx_done)) + napi_schedule(napi); + } + + return work_done; +} + static int r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags) { int ret; + /* The rx would be stopped, so skip submitting */ + if (test_bit(RTL8152_UNPLUG, &tp->flags) || + !test_bit(WORK_ENABLE, &tp->flags) || !netif_carrier_ok(tp->netdev)) + return 0; + usb_fill_bulk_urb(agg->urb, tp->udev, usb_rcvbulkpipe(tp->udev, 1), agg->head, agg_buf_sz, (usb_complete_t)read_bulk_callback, agg); @@ -1805,7 +1837,11 @@ int r8152_submit_rx(struct r8152 *tp, struct rx_agg *agg, gfp_t mem_flags) spin_lock_irqsave(&tp->rx_lock, flags); list_add_tail(&agg->list, &tp->rx_done); spin_unlock_irqrestore(&tp->rx_lock, flags); - tasklet_schedule(&tp->tl); + + netif_err(tp, rx_err, tp->netdev, + "Couldn't submit rx[%p], ret = %d\n", agg, ret); + + napi_schedule(&tp->napi); } return ret; @@ -1845,7 +1881,7 @@ static void rtl8152_set_rx_mode(struct net_device *netdev) { struct r8152 *tp = netdev_priv(netdev); - if (tp->speed & LINK_STATUS) { + if (netif_carrier_ok(netdev)) { set_bit(RTL8152_SET_RX_MODE, &tp->flags); schedule_delayed_work(&tp->schedule, 0); } @@ -1897,6 +1933,22 @@ static void _rtl8152_set_rx_mode(struct net_device *netdev) netif_wake_queue(netdev); } +static netdev_features_t +rtl8152_features_check(struct sk_buff *skb, struct net_device *dev, + netdev_features_t features) +{ + u32 mss = skb_shinfo(skb)->gso_size; + int max_offset = mss ? GTTCPHO_MAX : TCPHO_MAX; + int offset = skb_transport_offset(skb); + + if ((mss || skb->ip_summed == CHECKSUM_PARTIAL) && offset > max_offset) + features &= ~(NETIF_F_ALL_CSUM | NETIF_F_GSO_MASK); + else if ((skb->len + sizeof(struct tx_desc)) > agg_buf_sz) + features &= ~NETIF_F_GSO_MASK; + + return features; +} + static netdev_tx_t rtl8152_start_xmit(struct sk_buff *skb, struct net_device *netdev) { @@ -1908,11 +1960,11 @@ static netdev_tx_t rtl8152_start_xmit(struct sk_buff *skb, if (!list_empty(&tp->tx_free)) { if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { - set_bit(SCHEDULE_TASKLET, &tp->flags); + set_bit(SCHEDULE_NAPI, &tp->flags); schedule_delayed_work(&tp->schedule, 0); } else { usb_mark_last_busy(tp->udev); - tasklet_schedule(&tp->tl); + napi_schedule(&tp->napi); } } else if (skb_queue_len(&tp->tx_queue) > tp->tx_qlen) { netif_stop_queue(netdev); @@ -1991,6 +2043,7 @@ static int rtl_start_rx(struct r8152 *tp) { int i, ret = 0; + napi_disable(&tp->napi); INIT_LIST_HEAD(&tp->rx_done); for (i = 0; i < RTL8152_MAX_RX; i++) { INIT_LIST_HEAD(&tp->rx_info[i].list); @@ -1998,6 +2051,7 @@ static int rtl_start_rx(struct r8152 *tp) if (ret) break; } + napi_enable(&tp->napi); if (ret && ++i < RTL8152_MAX_RX) { struct list_head rx_queue; @@ -2028,6 +2082,9 @@ static int rtl_stop_rx(struct r8152 *tp) for (i = 0; i < RTL8152_MAX_RX; i++) usb_kill_urb(tp->rx_info[i].urb); + while (!skb_queue_empty(&tp->rx_queue)) + dev_kfree_skb(__skb_dequeue(&tp->rx_queue)); + return 0; } @@ -2043,7 +2100,7 @@ static int rtl_enable(struct r8152 *tp) rxdy_gated_en(tp, false); - return rtl_start_rx(tp); + return 0; } static int rtl8152_enable(struct r8152 *tp) @@ -2502,24 +2559,18 @@ static void r8153_hw_phy_cfg(struct r8152 *tp) data = ocp_reg_read(tp, OCP_POWER_CFG); data |= EN_10M_PLLOFF; ocp_reg_write(tp, OCP_POWER_CFG, data); - data = sram_read(tp, SRAM_IMPEDANCE); - data &= ~RX_DRIVING_MASK; - sram_write(tp, SRAM_IMPEDANCE, data); + sram_write(tp, SRAM_IMPEDANCE, 0x0b13); ocp_data = ocp_read_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR); ocp_data |= PFM_PWM_SWITCH; ocp_write_word(tp, MCU_TYPE_PLA, PLA_PHY_PWR, ocp_data); - data = sram_read(tp, SRAM_LPF_CFG); - data |= LPF_AUTO_TUNE; - sram_write(tp, SRAM_LPF_CFG, data); + /* Enable LPF corner auto tune */ + sram_write(tp, SRAM_LPF_CFG, 0xf70f); - data = sram_read(tp, SRAM_10M_AMP1); - data |= GDAC_IB_UPALL; - sram_write(tp, SRAM_10M_AMP1, data); - data = sram_read(tp, SRAM_10M_AMP2); - data |= AMP_DN; - sram_write(tp, SRAM_10M_AMP2, data); + /* Adjust 10M Amplitude */ + sram_write(tp, SRAM_10M_AMP1, 0x00af); + sram_write(tp, SRAM_10M_AMP2, 0x0208); set_bit(PHY_RESET, &tp->flags); } @@ -2854,20 +2905,20 @@ static void set_carrier(struct r8152 *tp) speed = rtl8152_get_speed(tp); if (speed & LINK_STATUS) { - if (!(tp->speed & LINK_STATUS)) { + if (!netif_carrier_ok(netdev)) { tp->rtl_ops.enable(tp); set_bit(RTL8152_SET_RX_MODE, &tp->flags); netif_carrier_on(netdev); + rtl_start_rx(tp); } } else { - if (tp->speed & LINK_STATUS) { + if (netif_carrier_ok(netdev)) { netif_carrier_off(netdev); - tasklet_disable(&tp->tl); + napi_disable(&tp->napi); tp->rtl_ops.disable(tp); - tasklet_enable(&tp->tl); + napi_enable(&tp->napi); } } - tp->speed = speed; } static void rtl_work_func_t(struct work_struct *work) @@ -2897,10 +2948,11 @@ static void rtl_work_func_t(struct work_struct *work) if (test_bit(RTL8152_SET_RX_MODE, &tp->flags)) _rtl8152_set_rx_mode(tp->netdev); - if (test_bit(SCHEDULE_TASKLET, &tp->flags) && - (tp->speed & LINK_STATUS)) { - clear_bit(SCHEDULE_TASKLET, &tp->flags); - tasklet_schedule(&tp->tl); + /* don't schedule napi before linking */ + if (test_bit(SCHEDULE_NAPI, &tp->flags) && + netif_carrier_ok(tp->netdev)) { + clear_bit(SCHEDULE_NAPI, &tp->flags); + napi_schedule(&tp->napi); } if (test_bit(PHY_RESET, &tp->flags)) @@ -2921,8 +2973,7 @@ static int rtl8152_open(struct net_device *netdev) if (res) goto out; - /* set speed to 0 to avoid autoresume try to submit rx */ - tp->speed = 0; + netif_carrier_off(netdev); res = usb_autopm_get_interface(tp->intf); if (res < 0) { @@ -2939,7 +2990,7 @@ static int rtl8152_open(struct net_device *netdev) cancel_delayed_work_sync(&tp->schedule); /* disable the tx/rx, if the workqueue has enabled them. */ - if (tp->speed & LINK_STATUS) + if (netif_carrier_ok(netdev)) tp->rtl_ops.disable(tp); } @@ -2948,7 +2999,6 @@ static int rtl8152_open(struct net_device *netdev) rtl8152_set_speed(tp, AUTONEG_ENABLE, tp->mii.supports_gmii ? SPEED_1000 : SPEED_100, DUPLEX_FULL); - tp->speed = 0; netif_carrier_off(netdev); netif_start_queue(netdev); set_bit(WORK_ENABLE, &tp->flags); @@ -2961,7 +3011,7 @@ static int rtl8152_open(struct net_device *netdev) res); free_all_mem(tp); } else { - tasklet_enable(&tp->tl); + napi_enable(&tp->napi); } mutex_unlock(&tp->control); @@ -2977,15 +3027,16 @@ static int rtl8152_close(struct net_device *netdev) struct r8152 *tp = netdev_priv(netdev); int res = 0; - tasklet_disable(&tp->tl); + napi_disable(&tp->napi); clear_bit(WORK_ENABLE, &tp->flags); usb_kill_urb(tp->intr_urb); cancel_delayed_work_sync(&tp->schedule); netif_stop_queue(netdev); res = usb_autopm_get_interface(tp->intf); - if (res < 0) { + if (res < 0 || test_bit(RTL8152_UNPLUG, &tp->flags)) { rtl_drop_queued_tx(tp); + rtl_stop_rx(tp); } else { mutex_lock(&tp->control); @@ -3189,10 +3240,10 @@ static void r8153_init(struct r8152 *tp) ocp_data = ocp_read_byte(tp, MCU_TYPE_USB, USB_LPM_CTRL); ocp_data &= ~LPM_TIMER_MASK; - if (tp->udev->speed == USB_SPEED_SUPER) - ocp_data |= LPM_TIMER_500US; - else + if (tp->version == RTL_VER_04 && tp->udev->speed != USB_SPEED_SUPER) ocp_data |= LPM_TIMER_500MS; + else + ocp_data |= LPM_TIMER_500US; ocp_write_byte(tp, MCU_TYPE_USB, USB_LPM_CTRL, ocp_data); ocp_data = ocp_read_word(tp, MCU_TYPE_USB, USB_AFE_CTRL2); @@ -3241,7 +3292,7 @@ static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message) if (netif_running(netdev) && test_bit(WORK_ENABLE, &tp->flags)) { clear_bit(WORK_ENABLE, &tp->flags); usb_kill_urb(tp->intr_urb); - tasklet_disable(&tp->tl); + napi_disable(&tp->napi); if (test_bit(SELECTIVE_SUSPEND, &tp->flags)) { rtl_stop_rx(tp); rtl_runtime_suspend_enable(tp, true); @@ -3249,7 +3300,7 @@ static int rtl8152_suspend(struct usb_interface *intf, pm_message_t message) cancel_delayed_work_sync(&tp->schedule); tp->rtl_ops.down(tp); } - tasklet_enable(&tp->tl); + napi_enable(&tp->napi); } out1: mutex_unlock(&tp->control); @@ -3273,7 +3324,7 @@ static int rtl8152_resume(struct usb_interface *intf) rtl_runtime_suspend_enable(tp, false); clear_bit(SELECTIVE_SUSPEND, &tp->flags); set_bit(WORK_ENABLE, &tp->flags); - if (tp->speed & LINK_STATUS) + if (netif_carrier_ok(tp->netdev)) rtl_start_rx(tp); } else { tp->rtl_ops.up(tp); @@ -3281,7 +3332,6 @@ static int rtl8152_resume(struct usb_interface *intf) tp->mii.supports_gmii ? SPEED_1000 : SPEED_100, DUPLEX_FULL); - tp->speed = 0; netif_carrier_off(tp->netdev); set_bit(WORK_ENABLE, &tp->flags); } @@ -3706,6 +3756,7 @@ static const struct net_device_ops rtl8152_netdev_ops = { .ndo_set_mac_address = rtl8152_set_mac_address, .ndo_change_mtu = rtl8152_change_mtu, .ndo_validate_addr = eth_validate_addr, + .ndo_features_check = rtl8152_features_check, }; static void r8152b_get_version(struct r8152 *tp) @@ -3832,7 +3883,6 @@ static int rtl8152_probe(struct usb_interface *intf, if (ret) goto out; - tasklet_init(&tp->tl, bottom_half, (unsigned long)tp); mutex_init(&tp->control); INIT_DELAYED_WORK(&tp->schedule, rtl_work_func_t); @@ -3846,8 +3896,7 @@ static int rtl8152_probe(struct usb_interface *intf, netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_TSO | NETIF_F_FRAGLIST | NETIF_F_IPV6_CSUM | NETIF_F_TSO6 | - NETIF_F_HW_VLAN_CTAG_RX | - NETIF_F_HW_VLAN_CTAG_TX; + NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX; netdev->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST | NETIF_F_IPV6_CSUM | NETIF_F_TSO6; @@ -3868,6 +3917,7 @@ static int rtl8152_probe(struct usb_interface *intf, set_ethernet_addr(tp); usb_set_intfdata(intf, tp); + netif_napi_add(netdev, &tp->napi, r8152_poll, RTL8152_NAPI_WEIGHT); ret = register_netdev(netdev); if (ret != 0) { @@ -3881,15 +3931,13 @@ static int rtl8152_probe(struct usb_interface *intf, else device_set_wakeup_enable(&udev->dev, false); - tasklet_disable(&tp->tl); - netif_info(tp, probe, netdev, "%s\n", DRIVER_VERSION); return 0; out1: + netif_napi_del(&tp->napi); usb_set_intfdata(intf, NULL); - tasklet_kill(&tp->tl); out: free_netdev(netdev); return ret; @@ -3906,7 +3954,7 @@ static void rtl8152_disconnect(struct usb_interface *intf) if (udev->state == USB_STATE_NOTATTACHED) set_bit(RTL8152_UNPLUG, &tp->flags); - tasklet_kill(&tp->tl); + netif_napi_del(&tp->napi); unregister_netdev(tp->netdev); tp->rtl_ops.unload(tp); free_netdev(tp->netdev); diff --git a/drivers/net/usb/sr9700.c b/drivers/net/usb/sr9700.c index 99b69af14274..4a1e9c489f1f 100644 --- a/drivers/net/usb/sr9700.c +++ b/drivers/net/usb/sr9700.c @@ -77,7 +77,7 @@ static int wait_phy_eeprom_ready(struct usbnet *dev, int phy) int ret; udelay(1); - ret = sr_read_reg(dev, EPCR, &tmp); + ret = sr_read_reg(dev, SR_EPCR, &tmp); if (ret < 0) return ret; @@ -98,15 +98,15 @@ static int sr_share_read_word(struct usbnet *dev, int phy, u8 reg, mutex_lock(&dev->phy_mutex); - sr_write_reg(dev, EPAR, phy ? (reg | EPAR_PHY_ADR) : reg); - sr_write_reg(dev, EPCR, phy ? (EPCR_EPOS | EPCR_ERPRR) : EPCR_ERPRR); + sr_write_reg(dev, SR_EPAR, phy ? (reg | EPAR_PHY_ADR) : reg); + sr_write_reg(dev, SR_EPCR, phy ? (EPCR_EPOS | EPCR_ERPRR) : EPCR_ERPRR); ret = wait_phy_eeprom_ready(dev, phy); if (ret < 0) goto out_unlock; - sr_write_reg(dev, EPCR, 0x0); - ret = sr_read(dev, EPDR, 2, value); + sr_write_reg(dev, SR_EPCR, 0x0); + ret = sr_read(dev, SR_EPDR, 2, value); netdev_dbg(dev->net, "read shared %d 0x%02x returned 0x%04x, %d\n", phy, reg, *value, ret); @@ -123,19 +123,19 @@ static int sr_share_write_word(struct usbnet *dev, int phy, u8 reg, mutex_lock(&dev->phy_mutex); - ret = sr_write(dev, EPDR, 2, &value); + ret = sr_write(dev, SR_EPDR, 2, &value); if (ret < 0) goto out_unlock; - sr_write_reg(dev, EPAR, phy ? (reg | EPAR_PHY_ADR) : reg); - sr_write_reg(dev, EPCR, phy ? (EPCR_WEP | EPCR_EPOS | EPCR_ERPRW) : + sr_write_reg(dev, SR_EPAR, phy ? (reg | EPAR_PHY_ADR) : reg); + sr_write_reg(dev, SR_EPCR, phy ? (EPCR_WEP | EPCR_EPOS | EPCR_ERPRW) : (EPCR_WEP | EPCR_ERPRW)); ret = wait_phy_eeprom_ready(dev, phy); if (ret < 0) goto out_unlock; - sr_write_reg(dev, EPCR, 0x0); + sr_write_reg(dev, SR_EPCR, 0x0); out_unlock: mutex_unlock(&dev->phy_mutex); @@ -188,7 +188,7 @@ static int sr_mdio_read(struct net_device *netdev, int phy_id, int loc) if (loc == MII_BMSR) { u8 value; - sr_read_reg(dev, NSR, &value); + sr_read_reg(dev, SR_NSR, &value); if (value & NSR_LINKST) rc = 1; } @@ -228,7 +228,7 @@ static u32 sr9700_get_link(struct net_device *netdev) int rc = 0; /* Get the Link Status directly */ - sr_read_reg(dev, NSR, &value); + sr_read_reg(dev, SR_NSR, &value); if (value & NSR_LINKST) rc = 1; @@ -281,8 +281,8 @@ static void sr9700_set_multicast(struct net_device *netdev) } } - sr_write_async(dev, MAR, SR_MCAST_SIZE, hashes); - sr_write_reg_async(dev, RCR, rx_ctl); + sr_write_async(dev, SR_MAR, SR_MCAST_SIZE, hashes); + sr_write_reg_async(dev, SR_RCR, rx_ctl); } static int sr9700_set_mac_address(struct net_device *netdev, void *p) @@ -297,7 +297,7 @@ static int sr9700_set_mac_address(struct net_device *netdev, void *p) } memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); - sr_write_async(dev, PAR, 6, netdev->dev_addr); + sr_write_async(dev, SR_PAR, 6, netdev->dev_addr); return 0; } @@ -340,7 +340,7 @@ static int sr9700_bind(struct usbnet *dev, struct usb_interface *intf) mii->phy_id_mask = 0x1f; mii->reg_num_mask = 0x1f; - sr_write_reg(dev, NCR, NCR_RST); + sr_write_reg(dev, SR_NCR, NCR_RST); udelay(20); /* read MAC @@ -348,17 +348,17 @@ static int sr9700_bind(struct usbnet *dev, struct usb_interface *intf) * EEPROM automatically to PAR. In case there is no EEPROM externally, * a default MAC address is stored in PAR for making chip work properly. */ - if (sr_read(dev, PAR, ETH_ALEN, netdev->dev_addr) < 0) { + if (sr_read(dev, SR_PAR, ETH_ALEN, netdev->dev_addr) < 0) { netdev_err(netdev, "Error reading MAC address\n"); ret = -ENODEV; goto out; } /* power up and reset phy */ - sr_write_reg(dev, PRR, PRR_PHY_RST); + sr_write_reg(dev, SR_PRR, PRR_PHY_RST); /* at least 10ms, here 20ms for safe */ mdelay(20); - sr_write_reg(dev, PRR, 0); + sr_write_reg(dev, SR_PRR, 0); /* at least 1ms, here 2ms for reading right register */ udelay(2 * 1000); diff --git a/drivers/net/usb/sr9700.h b/drivers/net/usb/sr9700.h index fd687c575e74..258b030277e7 100644 --- a/drivers/net/usb/sr9700.h +++ b/drivers/net/usb/sr9700.h @@ -14,13 +14,13 @@ /* sr9700 spec. register table on Linux platform */ /* Network Control Reg */ -#define NCR 0x00 +#define SR_NCR 0x00 #define NCR_RST (1 << 0) #define NCR_LBK (3 << 1) #define NCR_FDX (1 << 3) #define NCR_WAKEEN (1 << 6) /* Network Status Reg */ -#define NSR 0x01 +#define SR_NSR 0x01 #define NSR_RXRDY (1 << 0) #define NSR_RXOV (1 << 1) #define NSR_TX1END (1 << 2) @@ -30,7 +30,7 @@ #define NSR_LINKST (1 << 6) #define NSR_SPEED (1 << 7) /* Tx Control Reg */ -#define TCR 0x02 +#define SR_TCR 0x02 #define TCR_CRC_DIS (1 << 1) #define TCR_PAD_DIS (1 << 2) #define TCR_LC_CARE (1 << 3) @@ -38,7 +38,7 @@ #define TCR_EXCECM (1 << 5) #define TCR_LF_EN (1 << 6) /* Tx Status Reg for Packet Index 1 */ -#define TSR1 0x03 +#define SR_TSR1 0x03 #define TSR1_EC (1 << 2) #define TSR1_COL (1 << 3) #define TSR1_LC (1 << 4) @@ -46,7 +46,7 @@ #define TSR1_LOC (1 << 6) #define TSR1_TLF (1 << 7) /* Tx Status Reg for Packet Index 2 */ -#define TSR2 0x04 +#define SR_TSR2 0x04 #define TSR2_EC (1 << 2) #define TSR2_COL (1 << 3) #define TSR2_LC (1 << 4) @@ -54,7 +54,7 @@ #define TSR2_LOC (1 << 6) #define TSR2_TLF (1 << 7) /* Rx Control Reg*/ -#define RCR 0x05 +#define SR_RCR 0x05 #define RCR_RXEN (1 << 0) #define RCR_PRMSC (1 << 1) #define RCR_RUNT (1 << 2) @@ -62,87 +62,87 @@ #define RCR_DIS_CRC (1 << 4) #define RCR_DIS_LONG (1 << 5) /* Rx Status Reg */ -#define RSR 0x06 +#define SR_RSR 0x06 #define RSR_AE (1 << 2) #define RSR_MF (1 << 6) #define RSR_RF (1 << 7) /* Rx Overflow Counter Reg */ -#define ROCR 0x07 +#define SR_ROCR 0x07 #define ROCR_ROC (0x7F << 0) #define ROCR_RXFU (1 << 7) /* Back Pressure Threshold Reg */ -#define BPTR 0x08 +#define SR_BPTR 0x08 #define BPTR_JPT (0x0F << 0) #define BPTR_BPHW (0x0F << 4) /* Flow Control Threshold Reg */ -#define FCTR 0x09 +#define SR_FCTR 0x09 #define FCTR_LWOT (0x0F << 0) #define FCTR_HWOT (0x0F << 4) /* rx/tx Flow Control Reg */ -#define FCR 0x0A +#define SR_FCR 0x0A #define FCR_FLCE (1 << 0) #define FCR_BKPA (1 << 4) #define FCR_TXPEN (1 << 5) #define FCR_TXPF (1 << 6) #define FCR_TXP0 (1 << 7) /* Eeprom & Phy Control Reg */ -#define EPCR 0x0B +#define SR_EPCR 0x0B #define EPCR_ERRE (1 << 0) #define EPCR_ERPRW (1 << 1) #define EPCR_ERPRR (1 << 2) #define EPCR_EPOS (1 << 3) #define EPCR_WEP (1 << 4) /* Eeprom & Phy Address Reg */ -#define EPAR 0x0C +#define SR_EPAR 0x0C #define EPAR_EROA (0x3F << 0) #define EPAR_PHY_ADR_MASK (0x03 << 6) #define EPAR_PHY_ADR (0x01 << 6) /* Eeprom & Phy Data Reg */ -#define EPDR 0x0D /* 0x0D ~ 0x0E for Data Reg Low & High */ +#define SR_EPDR 0x0D /* 0x0D ~ 0x0E for Data Reg Low & High */ /* Wakeup Control Reg */ -#define WCR 0x0F +#define SR_WCR 0x0F #define WCR_MAGICST (1 << 0) #define WCR_LINKST (1 << 2) #define WCR_MAGICEN (1 << 3) #define WCR_LINKEN (1 << 5) /* Physical Address Reg */ -#define PAR 0x10 /* 0x10 ~ 0x15 6 bytes for PAR */ +#define SR_PAR 0x10 /* 0x10 ~ 0x15 6 bytes for PAR */ /* Multicast Address Reg */ -#define MAR 0x16 /* 0x16 ~ 0x1D 8 bytes for MAR */ +#define SR_MAR 0x16 /* 0x16 ~ 0x1D 8 bytes for MAR */ /* 0x1e unused */ /* Phy Reset Reg */ -#define PRR 0x1F +#define SR_PRR 0x1F #define PRR_PHY_RST (1 << 0) /* Tx sdram Write Pointer Address Low */ -#define TWPAL 0x20 +#define SR_TWPAL 0x20 /* Tx sdram Write Pointer Address High */ -#define TWPAH 0x21 +#define SR_TWPAH 0x21 /* Tx sdram Read Pointer Address Low */ -#define TRPAL 0x22 +#define SR_TRPAL 0x22 /* Tx sdram Read Pointer Address High */ -#define TRPAH 0x23 +#define SR_TRPAH 0x23 /* Rx sdram Write Pointer Address Low */ -#define RWPAL 0x24 +#define SR_RWPAL 0x24 /* Rx sdram Write Pointer Address High */ -#define RWPAH 0x25 +#define SR_RWPAH 0x25 /* Rx sdram Read Pointer Address Low */ -#define RRPAL 0x26 +#define SR_RRPAL 0x26 /* Rx sdram Read Pointer Address High */ -#define RRPAH 0x27 +#define SR_RRPAH 0x27 /* Vendor ID register */ -#define VID 0x28 /* 0x28 ~ 0x29 2 bytes for VID */ +#define SR_VID 0x28 /* 0x28 ~ 0x29 2 bytes for VID */ /* Product ID register */ -#define PID 0x2A /* 0x2A ~ 0x2B 2 bytes for PID */ +#define SR_PID 0x2A /* 0x2A ~ 0x2B 2 bytes for PID */ /* CHIP Revision register */ -#define CHIPR 0x2C +#define SR_CHIPR 0x2C /* 0x2D --> 0xEF unused */ /* USB Device Address */ -#define USBDA 0xF0 +#define SR_USBDA 0xF0 #define USBDA_USBFA (0x7F << 0) /* RX packet Counter Reg */ -#define RXC 0xF1 +#define SR_RXC 0xF1 /* Tx packet Counter & USB Status Reg */ -#define TXC_USBS 0xF2 +#define SR_TXC_USBS 0xF2 #define TXC_USBS_TXC0 (1 << 0) #define TXC_USBS_TXC1 (1 << 1) #define TXC_USBS_TXC2 (1 << 2) @@ -150,7 +150,7 @@ #define TXC_USBS_SUSFLAG (1 << 6) #define TXC_USBS_RXFAULT (1 << 7) /* USB Control register */ -#define USBC 0xF4 +#define SR_USBC 0xF4 #define USBC_EP3NAK (1 << 4) #define USBC_EP3ACK (1 << 5) diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 3a6770a65d78..449835f4331e 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -160,20 +160,19 @@ EXPORT_SYMBOL_GPL(usbnet_get_endpoints); int usbnet_get_ethernet_addr(struct usbnet *dev, int iMACAddress) { - int tmp, i; + int tmp = -1, ret; unsigned char buf [13]; - tmp = usb_string(dev->udev, iMACAddress, buf, sizeof buf); - if (tmp != 12) { + ret = usb_string(dev->udev, iMACAddress, buf, sizeof buf); + if (ret == 12) + tmp = hex2bin(dev->net->dev_addr, buf, 6); + if (tmp < 0) { dev_dbg(&dev->udev->dev, "bad MAC string %d fetch, %d\n", iMACAddress, tmp); - if (tmp >= 0) - tmp = -EINVAL; - return tmp; + if (ret >= 0) + ret = -EINVAL; + return ret; } - for (i = tmp = 0; i < 6; i++, tmp += 2) - dev->net->dev_addr [i] = - (hex_to_bin(buf[tmp]) << 4) + hex_to_bin(buf[tmp + 1]); return 0; } EXPORT_SYMBOL_GPL(usbnet_get_ethernet_addr); diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 8ad596573d17..4cca36ebc4fb 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -469,6 +469,14 @@ static const struct nla_policy veth_policy[VETH_INFO_MAX + 1] = { [VETH_INFO_PEER] = { .len = sizeof(struct ifinfomsg) }, }; +static struct net *veth_get_link_net(const struct net_device *dev) +{ + struct veth_priv *priv = netdev_priv(dev); + struct net_device *peer = rtnl_dereference(priv->peer); + + return peer ? dev_net(peer) : dev_net(dev); +} + static struct rtnl_link_ops veth_link_ops = { .kind = DRV_NAME, .priv_size = sizeof(struct veth_priv), @@ -478,6 +486,7 @@ static struct rtnl_link_ops veth_link_ops = { .dellink = veth_dellink, .policy = veth_policy, .maxtype = VETH_INFO_MAX, + .get_link_net = veth_get_link_net, }; /* diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 5ca97713bfb3..110a2cf67244 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -490,17 +490,8 @@ static void receive_buf(struct virtnet_info *vi, struct receive_queue *rq, skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; break; case VIRTIO_NET_HDR_GSO_UDP: - { - static bool warned; - - if (!warned) { - warned = true; - netdev_warn(dev, - "host using disabled UFO feature; please fix it\n"); - } skb_shinfo(skb)->gso_type = SKB_GSO_UDP; break; - } case VIRTIO_NET_HDR_GSO_TCPV6: skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; break; @@ -888,6 +879,8 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb) hdr->hdr.gso_type = VIRTIO_NET_HDR_GSO_TCPV4; else if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) hdr->hdr.gso_type = VIRTIO_NET_HDR_GSO_TCPV6; + else if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP) + hdr->hdr.gso_type = VIRTIO_NET_HDR_GSO_UDP; else BUG(); if (skb_shinfo(skb)->gso_type & SKB_GSO_TCP_ECN) @@ -925,6 +918,9 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) /* Free up any pending old buffers before queueing new ones. */ free_old_xmit_skbs(sq); + /* timestamp packet in software */ + skb_tx_timestamp(skb); + /* Try to transmit */ err = xmit_skb(sq, skb); @@ -1376,6 +1372,7 @@ static const struct ethtool_ops virtnet_ethtool_ops = { .get_ringparam = virtnet_get_ringparam, .set_channels = virtnet_set_channels, .get_channels = virtnet_get_channels, + .get_ts_info = ethtool_op_get_ts_info, }; #define MIN_MTU 68 @@ -1748,7 +1745,7 @@ static int virtnet_probe(struct virtio_device *vdev) dev->features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST; if (virtio_has_feature(vdev, VIRTIO_NET_F_GSO)) { - dev->hw_features |= NETIF_F_TSO + dev->hw_features |= NETIF_F_TSO | NETIF_F_UFO | NETIF_F_TSO_ECN | NETIF_F_TSO6; } /* Individual feature bits: what can host handle? */ @@ -1758,9 +1755,13 @@ static int virtnet_probe(struct virtio_device *vdev) dev->hw_features |= NETIF_F_TSO6; if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_ECN)) dev->hw_features |= NETIF_F_TSO_ECN; + if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_UFO)) + dev->hw_features |= NETIF_F_UFO; + + dev->features |= NETIF_F_GSO_ROBUST; if (gso) - dev->features |= dev->hw_features & NETIF_F_ALL_TSO; + dev->features |= dev->hw_features & (NETIF_F_ALL_TSO|NETIF_F_UFO); /* (!csum && gso) case will be fixed by register_netdev() */ } if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_CSUM)) @@ -1798,7 +1799,8 @@ static int virtnet_probe(struct virtio_device *vdev) /* If we can receive ANY GSO packets, we must allocate large ones. */ if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO4) || virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO6) || - virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ECN)) + virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_ECN) || + virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_UFO)) vi->big_packets = true; if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF)) @@ -1994,9 +1996,9 @@ static struct virtio_device_id id_table[] = { static unsigned int features[] = { VIRTIO_NET_F_CSUM, VIRTIO_NET_F_GUEST_CSUM, VIRTIO_NET_F_GSO, VIRTIO_NET_F_MAC, - VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_TSO6, + VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_HOST_TSO6, VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, - VIRTIO_NET_F_GUEST_ECN, + VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_UFO, VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_STATUS, VIRTIO_NET_F_CTRL_VQ, VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_VLAN, VIRTIO_NET_F_GUEST_ANNOUNCE, VIRTIO_NET_F_MQ, diff --git a/drivers/net/vmxnet3/vmxnet3_defs.h b/drivers/net/vmxnet3/vmxnet3_defs.h index 4d84912c99ba..3718d024f638 100644 --- a/drivers/net/vmxnet3/vmxnet3_defs.h +++ b/drivers/net/vmxnet3/vmxnet3_defs.h @@ -342,6 +342,7 @@ union Vmxnet3_GenericDesc { #define VMXNET3_TX_RING_MAX_SIZE 4096 #define VMXNET3_TC_RING_MAX_SIZE 4096 #define VMXNET3_RX_RING_MAX_SIZE 4096 +#define VMXNET3_RX_RING2_MAX_SIZE 2048 #define VMXNET3_RC_RING_MAX_SIZE 8192 /* a list of reasons for queue stop */ @@ -392,7 +393,7 @@ struct Vmxnet3_DriverInfo { }; -#define VMXNET3_REV1_MAGIC 0xbabefee1 +#define VMXNET3_REV1_MAGIC 3133079265u /* * QueueDescPA must be 128 bytes aligned. It points to an array of diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index afd295348ddb..294214c15292 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -1038,9 +1038,9 @@ vmxnet3_tq_xmit(struct sk_buff *skb, struct vmxnet3_tx_queue *tq, le32_add_cpu(&tq->shared->txNumDeferred, 1); } - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { gdesc->txd.ti = 1; - gdesc->txd.tci = vlan_tx_tag_get(skb); + gdesc->txd.tci = skb_vlan_tag_get(skb); } /* finally flips the GEN bit of the SOP desc. */ @@ -2505,6 +2505,9 @@ vmxnet3_adjust_rx_ring_size(struct vmxnet3_adapter *adapter) ring0_size = min_t(u32, ring0_size, VMXNET3_RX_RING_MAX_SIZE / sz * sz); ring1_size = adapter->rx_queue[0].rx_ring[1].size; + ring1_size = (ring1_size + sz - 1) / sz * sz; + ring1_size = min_t(u32, ring1_size, VMXNET3_RX_RING2_MAX_SIZE / + sz * sz); comp_size = ring0_size + ring1_size; for (i = 0; i < adapter->num_rx_queues; i++) { @@ -2585,7 +2588,7 @@ vmxnet3_open(struct net_device *netdev) err = vmxnet3_create_queues(adapter, adapter->tx_ring_size, adapter->rx_ring_size, - VMXNET3_DEF_RX_RING_SIZE); + adapter->rx_ring2_size); if (err) goto queue_err; @@ -2964,6 +2967,7 @@ vmxnet3_probe_device(struct pci_dev *pdev, adapter->tx_ring_size = VMXNET3_DEF_TX_RING_SIZE; adapter->rx_ring_size = VMXNET3_DEF_RX_RING_SIZE; + adapter->rx_ring2_size = VMXNET3_DEF_RX_RING2_SIZE; spin_lock_init(&adapter->cmd_lock); adapter->adapter_pa = dma_map_single(&adapter->pdev->dev, adapter, @@ -3286,27 +3290,15 @@ skip_arp: static int vmxnet3_resume(struct device *device) { - int err, i = 0; + int err; unsigned long flags; struct pci_dev *pdev = to_pci_dev(device); struct net_device *netdev = pci_get_drvdata(pdev); struct vmxnet3_adapter *adapter = netdev_priv(netdev); - struct Vmxnet3_PMConf *pmConf; if (!netif_running(netdev)) return 0; - /* Destroy wake-up filters. */ - pmConf = adapter->pm_conf; - memset(pmConf, 0, sizeof(*pmConf)); - - adapter->shared->devRead.pmConfDesc.confVer = cpu_to_le32(1); - adapter->shared->devRead.pmConfDesc.confLen = cpu_to_le32(sizeof( - *pmConf)); - adapter->shared->devRead.pmConfDesc.confPA = - cpu_to_le64(adapter->pm_conf_pa); - - netif_device_attach(netdev); pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); err = pci_enable_device_mem(pdev); @@ -3315,15 +3307,31 @@ vmxnet3_resume(struct device *device) pci_enable_wake(pdev, PCI_D0, 0); + vmxnet3_alloc_intr_resources(adapter); + + /* During hibernate and suspend, device has to be reinitialized as the + * device state need not be preserved. + */ + + /* Need not check adapter state as other reset tasks cannot run during + * device resume. + */ spin_lock_irqsave(&adapter->cmd_lock, flags); VMXNET3_WRITE_BAR1_REG(adapter, VMXNET3_REG_CMD, - VMXNET3_CMD_UPDATE_PMCFG); + VMXNET3_CMD_QUIESCE_DEV); spin_unlock_irqrestore(&adapter->cmd_lock, flags); - vmxnet3_alloc_intr_resources(adapter); - vmxnet3_request_irqs(adapter); - for (i = 0; i < adapter->num_rx_queues; i++) - napi_enable(&adapter->rx_queue[i].napi); - vmxnet3_enable_all_intrs(adapter); + vmxnet3_tq_cleanup_all(adapter); + vmxnet3_rq_cleanup_all(adapter); + + vmxnet3_reset_dev(adapter); + err = vmxnet3_activate_dev(adapter); + if (err != 0) { + netdev_err(netdev, + "failed to re-activate on resume, error: %d", err); + vmxnet3_force_close(adapter); + return err; + } + netif_device_attach(netdev); return 0; } @@ -3331,6 +3339,8 @@ vmxnet3_resume(struct device *device) static const struct dev_pm_ops vmxnet3_pm_ops = { .suspend = vmxnet3_suspend, .resume = vmxnet3_resume, + .freeze = vmxnet3_suspend, + .restore = vmxnet3_resume, }; #endif diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c index b7b53329d575..4c8a944d58b4 100644 --- a/drivers/net/vmxnet3/vmxnet3_ethtool.c +++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c @@ -323,7 +323,7 @@ vmxnet3_get_ethtool_stats(struct net_device *netdev, vmxnet3_tq_driver_stats[i].offset); } - for (j = 0; j < adapter->num_tx_queues; j++) { + for (j = 0; j < adapter->num_rx_queues; j++) { base = (u8 *)&adapter->rqd_start[j].stats; *buf++ = (u64) j; for (i = 1; i < ARRAY_SIZE(vmxnet3_rq_dev_stats); i++) @@ -447,12 +447,12 @@ vmxnet3_get_ringparam(struct net_device *netdev, param->rx_max_pending = VMXNET3_RX_RING_MAX_SIZE; param->tx_max_pending = VMXNET3_TX_RING_MAX_SIZE; param->rx_mini_max_pending = 0; - param->rx_jumbo_max_pending = 0; + param->rx_jumbo_max_pending = VMXNET3_RX_RING2_MAX_SIZE; param->rx_pending = adapter->rx_ring_size; param->tx_pending = adapter->tx_ring_size; param->rx_mini_pending = 0; - param->rx_jumbo_pending = 0; + param->rx_jumbo_pending = adapter->rx_ring2_size; } @@ -461,7 +461,7 @@ vmxnet3_set_ringparam(struct net_device *netdev, struct ethtool_ringparam *param) { struct vmxnet3_adapter *adapter = netdev_priv(netdev); - u32 new_tx_ring_size, new_rx_ring_size; + u32 new_tx_ring_size, new_rx_ring_size, new_rx_ring2_size; u32 sz; int err = 0; @@ -473,6 +473,10 @@ vmxnet3_set_ringparam(struct net_device *netdev, VMXNET3_RX_RING_MAX_SIZE) return -EINVAL; + if (param->rx_jumbo_pending == 0 || + param->rx_jumbo_pending > VMXNET3_RX_RING2_MAX_SIZE) + return -EINVAL; + /* if adapter not yet initialized, do nothing */ if (adapter->rx_buf_per_pkt == 0) { netdev_err(netdev, "adapter not completely initialized, " @@ -500,8 +504,15 @@ vmxnet3_set_ringparam(struct net_device *netdev, sz) != 0) return -EINVAL; - if (new_tx_ring_size == adapter->tx_queue[0].tx_ring.size && - new_rx_ring_size == adapter->rx_queue[0].rx_ring[0].size) { + /* ring2 has to be a multiple of VMXNET3_RING_SIZE_ALIGN */ + new_rx_ring2_size = (param->rx_jumbo_pending + VMXNET3_RING_SIZE_MASK) & + ~VMXNET3_RING_SIZE_MASK; + new_rx_ring2_size = min_t(u32, new_rx_ring2_size, + VMXNET3_RX_RING2_MAX_SIZE); + + if (new_tx_ring_size == adapter->tx_ring_size && + new_rx_ring_size == adapter->rx_ring_size && + new_rx_ring2_size == adapter->rx_ring2_size) { return 0; } @@ -522,7 +533,7 @@ vmxnet3_set_ringparam(struct net_device *netdev, vmxnet3_rq_destroy_all(adapter); err = vmxnet3_create_queues(adapter, new_tx_ring_size, - new_rx_ring_size, VMXNET3_DEF_RX_RING_SIZE); + new_rx_ring_size, new_rx_ring2_size); if (err) { /* failed, most likely because of OOM, try default @@ -530,11 +541,12 @@ vmxnet3_set_ringparam(struct net_device *netdev, netdev_err(netdev, "failed to apply new sizes, " "try the default ones\n"); new_rx_ring_size = VMXNET3_DEF_RX_RING_SIZE; + new_rx_ring2_size = VMXNET3_DEF_RX_RING2_SIZE; new_tx_ring_size = VMXNET3_DEF_TX_RING_SIZE; err = vmxnet3_create_queues(adapter, new_tx_ring_size, new_rx_ring_size, - VMXNET3_DEF_RX_RING_SIZE); + new_rx_ring2_size); if (err) { netdev_err(netdev, "failed to create queues " "with default sizes. Closing it\n"); @@ -549,6 +561,7 @@ vmxnet3_set_ringparam(struct net_device *netdev, } adapter->tx_ring_size = new_tx_ring_size; adapter->rx_ring_size = new_rx_ring_size; + adapter->rx_ring2_size = new_rx_ring2_size; out: clear_bit(VMXNET3_STATE_BIT_RESETTING, &adapter->state); diff --git a/drivers/net/vmxnet3/vmxnet3_int.h b/drivers/net/vmxnet3/vmxnet3_int.h index 5f0199f6c31e..cd71c77f78f2 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.2.1.0-k" +#define VMXNET3_DRIVER_VERSION_STRING "1.3.4.0-k" /* a 32-bit int, each byte encode a verion number in VMXNET3_DRIVER_VERSION */ -#define VMXNET3_DRIVER_VERSION_NUM 0x01020100 +#define VMXNET3_DRIVER_VERSION_NUM 0x01030400 #if defined(CONFIG_PCI_MSI) /* RSS only makes sense if MSI-X is supported. */ @@ -352,6 +352,7 @@ struct vmxnet3_adapter { /* Ring sizes */ u32 tx_ring_size; u32 rx_ring_size; + u32 rx_ring2_size; struct work_struct work; @@ -384,6 +385,7 @@ struct vmxnet3_adapter { /* must be a multiple of VMXNET3_RING_SIZE_ALIGN */ #define VMXNET3_DEF_TX_RING_SIZE 512 #define VMXNET3_DEF_RX_RING_SIZE 256 +#define VMXNET3_DEF_RX_RING2_SIZE 128 #define VMXNET3_MAX_ETH_HDR_SIZE 22 #define VMXNET3_MAX_SKB_BUF_SIZE (3*1024) diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 7fbd89fbe107..0e57e862c399 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -61,12 +61,6 @@ #define FDB_AGE_DEFAULT 300 /* 5 min */ #define FDB_AGE_INTERVAL (10 * HZ) /* rescan interval */ -#define VXLAN_N_VID (1u << 24) -#define VXLAN_VID_MASK (VXLAN_N_VID - 1) -#define VXLAN_HLEN (sizeof(struct udphdr) + sizeof(struct vxlanhdr)) - -#define VXLAN_FLAGS 0x08000000 /* struct vxlanhdr.vx_flags required value. */ - /* UDP port for VXLAN traffic. * The IANA assigned port is 4789, but the Linux default is 8472 * for compatibility with early adopters. @@ -269,15 +263,20 @@ static inline struct vxlan_rdst *first_remote_rtnl(struct vxlan_fdb *fdb) return list_first_entry(&fdb->remotes, struct vxlan_rdst, list); } -/* Find VXLAN socket based on network namespace, address family and UDP port */ -static struct vxlan_sock *vxlan_find_sock(struct net *net, - sa_family_t family, __be16 port) +/* Find VXLAN socket based on network namespace, address family and UDP port + * and enabled unshareable flags. + */ +static struct vxlan_sock *vxlan_find_sock(struct net *net, sa_family_t family, + __be16 port, u32 flags) { struct vxlan_sock *vs; + flags &= VXLAN_F_RCV_FLAGS; + hlist_for_each_entry_rcu(vs, vs_head(net, port), hlist) { if (inet_sk(vs->sock->sk)->inet_sport == port && - inet_sk(vs->sock->sk)->sk.sk_family == family) + inet_sk(vs->sock->sk)->sk.sk_family == family && + vs->flags == flags) return vs; } return NULL; @@ -297,11 +296,12 @@ static struct vxlan_dev *vxlan_vs_find_vni(struct vxlan_sock *vs, u32 id) /* Look up VNI in a per net namespace table */ static struct vxlan_dev *vxlan_find_vni(struct net *net, u32 id, - sa_family_t family, __be16 port) + sa_family_t family, __be16 port, + u32 flags) { struct vxlan_sock *vs; - vs = vxlan_find_sock(net, family, port); + vs = vxlan_find_sock(net, family, port, flags); if (!vs) return NULL; @@ -340,6 +340,11 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan, ndm->ndm_flags = fdb->flags; ndm->ndm_type = RTN_UNICAST; + if (!net_eq(dev_net(vxlan->dev), vxlan->net) && + nla_put_s32(skb, NDA_LINK_NETNSID, + peernet2id(dev_net(vxlan->dev), vxlan->net))) + goto nla_put_failure; + if (send_eth && nla_put(skb, NDA_LLADDR, ETH_ALEN, &fdb->eth_addr)) goto nla_put_failure; @@ -364,7 +369,8 @@ static int vxlan_fdb_info(struct sk_buff *skb, struct vxlan_dev *vxlan, if (nla_put(skb, NDA_CACHEINFO, sizeof(ci), &ci)) goto nla_put_failure; - return nlmsg_end(skb, nlh); + nlmsg_end(skb, nlh); + return 0; nla_put_failure: nlmsg_cancel(skb, nlh); @@ -379,6 +385,7 @@ static inline size_t vxlan_nlmsg_size(void) + nla_total_size(sizeof(__be16)) /* NDA_PORT */ + nla_total_size(sizeof(__be32)) /* NDA_VNI */ + nla_total_size(sizeof(__u32)) /* NDA_IFINDEX */ + + nla_total_size(sizeof(__s32)) /* NDA_LINK_NETNSID */ + nla_total_size(sizeof(struct nda_cacheinfo)); } @@ -545,15 +552,51 @@ static int vxlan_fdb_append(struct vxlan_fdb *f, return 1; } -static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, struct sk_buff *skb) +static struct vxlanhdr *vxlan_gro_remcsum(struct sk_buff *skb, + unsigned int off, + struct vxlanhdr *vh, size_t hdrlen, + u32 data) +{ + size_t start, offset, plen; + + if (skb->remcsum_offload) + return vh; + + if (!NAPI_GRO_CB(skb)->csum_valid) + return NULL; + + start = (data & VXLAN_RCO_MASK) << VXLAN_RCO_SHIFT; + offset = start + ((data & VXLAN_RCO_UDP) ? + offsetof(struct udphdr, check) : + offsetof(struct tcphdr, check)); + + plen = hdrlen + offset + sizeof(u16); + + /* Pull checksum that will be written */ + if (skb_gro_header_hard(skb, off + plen)) { + vh = skb_gro_header_slow(skb, off + plen, off); + if (!vh) + return NULL; + } + + skb_gro_remcsum_process(skb, (void *)vh + hdrlen, start, offset); + + skb->remcsum_offload = 1; + + return vh; +} + +static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, + struct sk_buff *skb, + struct udp_offload *uoff) { struct sk_buff *p, **pp = NULL; struct vxlanhdr *vh, *vh2; - struct ethhdr *eh, *eh2; - unsigned int hlen, off_vx, off_eth; - const struct packet_offload *ptype; - __be16 type; + unsigned int hlen, off_vx; int flush = 1; + struct vxlan_sock *vs = container_of(uoff, struct vxlan_sock, + udp_offloads); + u32 flags; off_vx = skb_gro_offset(skb); hlen = off_vx + sizeof(*vh); @@ -563,15 +606,17 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, struct sk_buff if (unlikely(!vh)) goto out; } + skb_gro_pull(skb, sizeof(struct vxlanhdr)); /* pull vxlan header */ skb_gro_postpull_rcsum(skb, vh, sizeof(struct vxlanhdr)); - off_eth = skb_gro_offset(skb); - hlen = off_eth + sizeof(*eh); - eh = skb_gro_header_fast(skb, off_eth); - if (skb_gro_header_hard(skb, hlen)) { - eh = skb_gro_header_slow(skb, hlen, off_eth); - if (unlikely(!eh)) + flags = ntohl(vh->vx_flags); + + if ((flags & VXLAN_HF_RCO) && (vs->flags & VXLAN_F_REMCSUM_RX)) { + vh = vxlan_gro_remcsum(skb, off_vx, vh, sizeof(struct vxlanhdr), + ntohl(vh->vx_vni)); + + if (!vh) goto out; } @@ -582,54 +627,27 @@ static struct sk_buff **vxlan_gro_receive(struct sk_buff **head, struct sk_buff continue; vh2 = (struct vxlanhdr *)(p->data + off_vx); - eh2 = (struct ethhdr *)(p->data + off_eth); - if (vh->vx_vni != vh2->vx_vni || compare_ether_header(eh, eh2)) { + if (vh->vx_flags != vh2->vx_flags || + vh->vx_vni != vh2->vx_vni) { NAPI_GRO_CB(p)->same_flow = 0; continue; } } - type = eh->h_proto; - - rcu_read_lock(); - ptype = gro_find_receive_by_type(type); - if (ptype == NULL) { - flush = 1; - goto out_unlock; - } + pp = eth_gro_receive(head, skb); - skb_gro_pull(skb, sizeof(*eh)); /* pull inner eth header */ - skb_gro_postpull_rcsum(skb, eh, sizeof(*eh)); - pp = ptype->callbacks.gro_receive(head, skb); - -out_unlock: - rcu_read_unlock(); out: NAPI_GRO_CB(skb)->flush |= flush; return pp; } -static int vxlan_gro_complete(struct sk_buff *skb, int nhoff) +static int vxlan_gro_complete(struct sk_buff *skb, int nhoff, + struct udp_offload *uoff) { - struct ethhdr *eh; - struct packet_offload *ptype; - __be16 type; - int vxlan_len = sizeof(struct vxlanhdr) + sizeof(struct ethhdr); - int err = -ENOSYS; - udp_tunnel_gro_complete(skb, nhoff); - eh = (struct ethhdr *)(skb->data + nhoff + sizeof(struct vxlanhdr)); - type = eh->h_proto; - - rcu_read_lock(); - ptype = gro_find_complete_by_type(type); - if (ptype != NULL) - err = ptype->callbacks.gro_complete(skb, nhoff + vxlan_len); - - rcu_read_unlock(); - return err; + return eth_gro_complete(skb, nhoff + sizeof(struct vxlanhdr)); } /* Notify netdevs that UDP port started listening */ @@ -991,7 +1009,7 @@ static bool vxlan_snoop(struct net_device *dev, if (net_ratelimit()) netdev_info(dev, "%pM migrated from %pIS to %pIS\n", - src_mac, &rdst->remote_ip, &src_ip); + src_mac, &rdst->remote_ip.sa, &src_ip->sa); rdst->remote_ip = *src_ip; f->updated = jiffies; @@ -1131,33 +1149,107 @@ static void vxlan_igmp_leave(struct work_struct *work) dev_put(vxlan->dev); } +static struct vxlanhdr *vxlan_remcsum(struct sk_buff *skb, struct vxlanhdr *vh, + size_t hdrlen, u32 data) +{ + size_t start, offset, plen; + + if (skb->remcsum_offload) { + /* Already processed in GRO path */ + skb->remcsum_offload = 0; + return vh; + } + + start = (data & VXLAN_RCO_MASK) << VXLAN_RCO_SHIFT; + offset = start + ((data & VXLAN_RCO_UDP) ? + offsetof(struct udphdr, check) : + offsetof(struct tcphdr, check)); + + plen = hdrlen + offset + sizeof(u16); + + if (!pskb_may_pull(skb, plen)) + return NULL; + + vh = (struct vxlanhdr *)(udp_hdr(skb) + 1); + + skb_remcsum_process(skb, (void *)vh + hdrlen, start, offset); + + return vh; +} + /* Callback from net/ipv4/udp.c to receive packets */ static int vxlan_udp_encap_recv(struct sock *sk, struct sk_buff *skb) { struct vxlan_sock *vs; struct vxlanhdr *vxh; + u32 flags, vni; + struct vxlan_metadata md = {0}; /* Need Vxlan and inner Ethernet header to be present */ if (!pskb_may_pull(skb, VXLAN_HLEN)) goto error; - /* Return packets with reserved bits set */ vxh = (struct vxlanhdr *)(udp_hdr(skb) + 1); - if (vxh->vx_flags != htonl(VXLAN_FLAGS) || - (vxh->vx_vni & htonl(0xff))) { - netdev_dbg(skb->dev, "invalid vxlan flags=%#x vni=%#x\n", - ntohl(vxh->vx_flags), ntohl(vxh->vx_vni)); - goto error; + flags = ntohl(vxh->vx_flags); + vni = ntohl(vxh->vx_vni); + + if (flags & VXLAN_HF_VNI) { + flags &= ~VXLAN_HF_VNI; + } else { + /* VNI flag always required to be set */ + goto bad_flags; } if (iptunnel_pull_header(skb, VXLAN_HLEN, htons(ETH_P_TEB))) goto drop; + vxh = (struct vxlanhdr *)(udp_hdr(skb) + 1); vs = rcu_dereference_sk_user_data(sk); if (!vs) goto drop; - vs->rcv(vs, skb, vxh->vx_vni); + if ((flags & VXLAN_HF_RCO) && (vs->flags & VXLAN_F_REMCSUM_RX)) { + vxh = vxlan_remcsum(skb, vxh, sizeof(struct vxlanhdr), vni); + if (!vxh) + goto drop; + + flags &= ~VXLAN_HF_RCO; + vni &= VXLAN_VID_MASK; + } + + /* For backwards compatibility, only allow reserved fields to be + * used by VXLAN extensions if explicitly requested. + */ + if ((flags & VXLAN_HF_GBP) && (vs->flags & VXLAN_F_GBP)) { + struct vxlanhdr_gbp *gbp; + + gbp = (struct vxlanhdr_gbp *)vxh; + md.gbp = ntohs(gbp->policy_id); + + if (gbp->dont_learn) + md.gbp |= VXLAN_GBP_DONT_LEARN; + + if (gbp->policy_applied) + md.gbp |= VXLAN_GBP_POLICY_APPLIED; + + flags &= ~VXLAN_GBP_USED_BITS; + } + + if (flags || (vni & ~VXLAN_VID_MASK)) { + /* If there are any unprocessed flags remaining treat + * this as a malformed packet. This behavior diverges from + * VXLAN RFC (RFC7348) which stipulates that bits in reserved + * in reserved fields are to be ignored. The approach here + * maintains compatbility with previous stack code, and also + * is more robust and provides a little more security in + * adding extensions to VXLAN. + */ + + goto bad_flags; + } + + md.vni = vxh->vx_vni; + vs->rcv(vs, skb, &md); return 0; drop: @@ -1165,13 +1257,17 @@ drop: kfree_skb(skb); return 0; +bad_flags: + netdev_dbg(skb->dev, "invalid vxlan flags=%#x vni=%#x\n", + ntohl(vxh->vx_flags), ntohl(vxh->vx_vni)); + error: /* Return non vxlan pkt */ return 1; } -static void vxlan_rcv(struct vxlan_sock *vs, - struct sk_buff *skb, __be32 vx_vni) +static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb, + struct vxlan_metadata *md) { struct iphdr *oip = NULL; struct ipv6hdr *oip6 = NULL; @@ -1182,7 +1278,7 @@ static void vxlan_rcv(struct vxlan_sock *vs, int err = 0; union vxlan_addr *remote_ip; - vni = ntohl(vx_vni) >> 8; + vni = ntohl(md->vni) >> 8; /* Is this VNI defined? */ vxlan = vxlan_vs_find_vni(vs, vni); if (!vxlan) @@ -1216,6 +1312,7 @@ static void vxlan_rcv(struct vxlan_sock *vs, goto drop; skb_reset_network_header(skb); + skb->mark = md->gbp; if (oip6) err = IP6_ECN_decapsulate(oip6, skb); @@ -1565,20 +1662,54 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb) return false; } +static void vxlan_build_gbp_hdr(struct vxlanhdr *vxh, u32 vxflags, + struct vxlan_metadata *md) +{ + struct vxlanhdr_gbp *gbp; + + if (!md->gbp) + return; + + gbp = (struct vxlanhdr_gbp *)vxh; + vxh->vx_flags |= htonl(VXLAN_HF_GBP); + + if (md->gbp & VXLAN_GBP_DONT_LEARN) + gbp->dont_learn = 1; + + if (md->gbp & VXLAN_GBP_POLICY_APPLIED) + gbp->policy_applied = 1; + + gbp->policy_id = htons(md->gbp & VXLAN_GBP_ID_MASK); +} + #if IS_ENABLED(CONFIG_IPV6) -static int vxlan6_xmit_skb(struct vxlan_sock *vs, - struct dst_entry *dst, struct sk_buff *skb, +static int vxlan6_xmit_skb(struct dst_entry *dst, struct sk_buff *skb, struct net_device *dev, struct in6_addr *saddr, struct in6_addr *daddr, __u8 prio, __u8 ttl, - __be16 src_port, __be16 dst_port, __be32 vni, - bool xnet) + __be16 src_port, __be16 dst_port, + struct vxlan_metadata *md, bool xnet, u32 vxflags) { struct vxlanhdr *vxh; int min_headroom; int err; - bool udp_sum = !udp_get_no_check6_tx(vs->sock->sk); + bool udp_sum = !(vxflags & VXLAN_F_UDP_ZERO_CSUM6_TX); + int type = udp_sum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; + u16 hdrlen = sizeof(struct vxlanhdr); + + if ((vxflags & VXLAN_F_REMCSUM_TX) && + skb->ip_summed == CHECKSUM_PARTIAL) { + int csum_start = skb_checksum_start_offset(skb); + + if (csum_start <= VXLAN_MAX_REMCSUM_START && + !(csum_start & VXLAN_RCO_SHIFT_MASK) && + (skb->csum_offset == offsetof(struct udphdr, check) || + skb->csum_offset == offsetof(struct tcphdr, check))) { + udp_sum = false; + type |= SKB_GSO_TUNNEL_REMCSUM; + } + } - skb = udp_tunnel_handle_offloads(skb, udp_sum); + skb = iptunnel_handle_offloads(skb, udp_sum, type); if (IS_ERR(skb)) { err = -EINVAL; goto err; @@ -1588,7 +1719,7 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs, min_headroom = LL_RESERVED_SPACE(dst->dev) + dst->header_len + VXLAN_HLEN + sizeof(struct ipv6hdr) - + (vlan_tx_tag_present(skb) ? VLAN_HLEN : 0); + + (skb_vlan_tag_present(skb) ? VLAN_HLEN : 0); /* Need space for new headers (invalidates iph ptr) */ err = skb_cow_head(skb, min_headroom); @@ -1604,13 +1735,33 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs, } vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh)); - vxh->vx_flags = htonl(VXLAN_FLAGS); - vxh->vx_vni = vni; + vxh->vx_flags = htonl(VXLAN_HF_VNI); + vxh->vx_vni = md->vni; + + if (type & SKB_GSO_TUNNEL_REMCSUM) { + u32 data = (skb_checksum_start_offset(skb) - hdrlen) >> + VXLAN_RCO_SHIFT; + + if (skb->csum_offset == offsetof(struct udphdr, check)) + data |= VXLAN_RCO_UDP; + + vxh->vx_vni |= htonl(data); + vxh->vx_flags |= htonl(VXLAN_HF_RCO); + + if (!skb_is_gso(skb)) { + skb->ip_summed = CHECKSUM_NONE; + skb->encapsulation = 0; + } + } + + if (vxflags & VXLAN_F_GBP) + vxlan_build_gbp_hdr(vxh, vxflags, md); skb_set_inner_protocol(skb, htons(ETH_P_TEB)); - udp_tunnel6_xmit_skb(vs->sock, dst, skb, dev, saddr, daddr, prio, - ttl, src_port, dst_port); + udp_tunnel6_xmit_skb(dst, skb, dev, saddr, daddr, prio, + ttl, src_port, dst_port, + !!(vxflags & VXLAN_F_UDP_ZERO_CSUM6_TX)); return 0; err: dst_release(dst); @@ -1618,23 +1769,38 @@ err: } #endif -int vxlan_xmit_skb(struct vxlan_sock *vs, - struct rtable *rt, struct sk_buff *skb, +int vxlan_xmit_skb(struct rtable *rt, struct sk_buff *skb, __be32 src, __be32 dst, __u8 tos, __u8 ttl, __be16 df, - __be16 src_port, __be16 dst_port, __be32 vni, bool xnet) + __be16 src_port, __be16 dst_port, + struct vxlan_metadata *md, bool xnet, u32 vxflags) { struct vxlanhdr *vxh; int min_headroom; int err; - bool udp_sum = !vs->sock->sk->sk_no_check_tx; + bool udp_sum = !!(vxflags & VXLAN_F_UDP_CSUM); + int type = udp_sum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL; + u16 hdrlen = sizeof(struct vxlanhdr); + + if ((vxflags & VXLAN_F_REMCSUM_TX) && + skb->ip_summed == CHECKSUM_PARTIAL) { + int csum_start = skb_checksum_start_offset(skb); + + if (csum_start <= VXLAN_MAX_REMCSUM_START && + !(csum_start & VXLAN_RCO_SHIFT_MASK) && + (skb->csum_offset == offsetof(struct udphdr, check) || + skb->csum_offset == offsetof(struct tcphdr, check))) { + udp_sum = false; + type |= SKB_GSO_TUNNEL_REMCSUM; + } + } - skb = udp_tunnel_handle_offloads(skb, udp_sum); + skb = iptunnel_handle_offloads(skb, udp_sum, type); if (IS_ERR(skb)) return PTR_ERR(skb); min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len + VXLAN_HLEN + sizeof(struct iphdr) - + (vlan_tx_tag_present(skb) ? VLAN_HLEN : 0); + + (skb_vlan_tag_present(skb) ? VLAN_HLEN : 0); /* Need space for new headers (invalidates iph ptr) */ err = skb_cow_head(skb, min_headroom); @@ -1648,13 +1814,33 @@ int vxlan_xmit_skb(struct vxlan_sock *vs, return -ENOMEM; vxh = (struct vxlanhdr *) __skb_push(skb, sizeof(*vxh)); - vxh->vx_flags = htonl(VXLAN_FLAGS); - vxh->vx_vni = vni; + vxh->vx_flags = htonl(VXLAN_HF_VNI); + vxh->vx_vni = md->vni; + + if (type & SKB_GSO_TUNNEL_REMCSUM) { + u32 data = (skb_checksum_start_offset(skb) - hdrlen) >> + VXLAN_RCO_SHIFT; + + if (skb->csum_offset == offsetof(struct udphdr, check)) + data |= VXLAN_RCO_UDP; + + vxh->vx_vni |= htonl(data); + vxh->vx_flags |= htonl(VXLAN_HF_RCO); + + if (!skb_is_gso(skb)) { + skb->ip_summed = CHECKSUM_NONE; + skb->encapsulation = 0; + } + } + + if (vxflags & VXLAN_F_GBP) + vxlan_build_gbp_hdr(vxh, vxflags, md); skb_set_inner_protocol(skb, htons(ETH_P_TEB)); - return udp_tunnel_xmit_skb(vs->sock, rt, skb, src, dst, tos, - ttl, df, src_port, dst_port, xnet); + return udp_tunnel_xmit_skb(rt, skb, src, dst, tos, + ttl, df, src_port, dst_port, xnet, + !(vxflags & VXLAN_F_UDP_CSUM)); } EXPORT_SYMBOL_GPL(vxlan_xmit_skb); @@ -1711,6 +1897,7 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, const struct iphdr *old_iph; struct flowi4 fl4; union vxlan_addr *dst; + struct vxlan_metadata md; __be16 src_port = 0, dst_port; u32 vni; __be16 df = 0; @@ -1772,7 +1959,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, ip_rt_put(rt); dst_vxlan = vxlan_find_vni(vxlan->net, vni, - dst->sa.sa_family, dst_port); + dst->sa.sa_family, dst_port, + vxlan->flags); if (!dst_vxlan) goto tx_error; vxlan_encap_bypass(skb, vxlan, dst_vxlan); @@ -1781,12 +1969,14 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, tos = ip_tunnel_ecn_encap(tos, old_iph, skb); ttl = ttl ? : ip4_dst_hoplimit(&rt->dst); - - err = vxlan_xmit_skb(vxlan->vn_sock, rt, skb, - fl4.saddr, dst->sin.sin_addr.s_addr, - tos, ttl, df, src_port, dst_port, - htonl(vni << 8), - !net_eq(vxlan->net, dev_net(vxlan->dev))); + md.vni = htonl(vni << 8); + md.gbp = skb->mark; + + err = vxlan_xmit_skb(rt, skb, fl4.saddr, + dst->sin.sin_addr.s_addr, tos, ttl, df, + src_port, dst_port, &md, + !net_eq(vxlan->net, dev_net(vxlan->dev)), + vxlan->flags); if (err < 0) { /* skb is already freed. */ skb = NULL; @@ -1830,7 +2020,8 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, dst_release(ndst); dst_vxlan = vxlan_find_vni(vxlan->net, vni, - dst->sa.sa_family, dst_port); + dst->sa.sa_family, dst_port, + vxlan->flags); if (!dst_vxlan) goto tx_error; vxlan_encap_bypass(skb, vxlan, dst_vxlan); @@ -1838,11 +2029,13 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev, } ttl = ttl ? : ip6_dst_hoplimit(ndst); + md.vni = htonl(vni << 8); + md.gbp = skb->mark; - err = vxlan6_xmit_skb(vxlan->vn_sock, ndst, skb, - dev, &fl6.saddr, &fl6.daddr, 0, ttl, - src_port, dst_port, htonl(vni << 8), - !net_eq(vxlan->net, dev_net(vxlan->dev))); + err = vxlan6_xmit_skb(ndst, skb, dev, &fl6.saddr, &fl6.daddr, + 0, ttl, src_port, dst_port, &md, + !net_eq(vxlan->net, dev_net(vxlan->dev)), + vxlan->flags); #endif } @@ -1998,7 +2191,7 @@ static int vxlan_init(struct net_device *dev) spin_lock(&vn->sock_lock); vs = vxlan_find_sock(vxlan->net, ipv6 ? AF_INET6 : AF_INET, - vxlan->dst_port); + vxlan->dst_port, vxlan->flags); if (vs && atomic_add_unless(&vs->refcnt, 1, 0)) { /* If we have a socket with same port already, reuse it */ vxlan_vs_add_dev(vs, vxlan); @@ -2242,6 +2435,9 @@ static const struct nla_policy vxlan_policy[IFLA_VXLAN_MAX + 1] = { [IFLA_VXLAN_UDP_CSUM] = { .type = NLA_U8 }, [IFLA_VXLAN_UDP_ZERO_CSUM6_TX] = { .type = NLA_U8 }, [IFLA_VXLAN_UDP_ZERO_CSUM6_RX] = { .type = NLA_U8 }, + [IFLA_VXLAN_REMCSUM_TX] = { .type = NLA_U8 }, + [IFLA_VXLAN_REMCSUM_RX] = { .type = NLA_U8 }, + [IFLA_VXLAN_GBP] = { .type = NLA_FLAG, }, }; static int vxlan_validate(struct nlattr *tb[], struct nlattr *data[]) @@ -2311,15 +2507,11 @@ static struct socket *vxlan_create_sock(struct net *net, bool ipv6, if (ipv6) { udp_conf.family = AF_INET6; - udp_conf.use_udp6_tx_checksums = - !(flags & VXLAN_F_UDP_ZERO_CSUM6_TX); udp_conf.use_udp6_rx_checksums = !(flags & VXLAN_F_UDP_ZERO_CSUM6_RX); } else { udp_conf.family = AF_INET; udp_conf.local_ip.s_addr = INADDR_ANY; - udp_conf.use_udp_checksums = - !!(flags & VXLAN_F_UDP_CSUM); } udp_conf.local_udp_port = port; @@ -2363,6 +2555,7 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port, atomic_set(&vs->refcnt, 1); vs->rcv = rcv; vs->data = data; + vs->flags = (flags & VXLAN_F_RCV_FLAGS); /* Initialize the vxlan udp offloads structure */ vs->udp_offloads.port = port; @@ -2401,7 +2594,7 @@ struct vxlan_sock *vxlan_sock_add(struct net *net, __be16 port, return vs; spin_lock(&vn->sock_lock); - vs = vxlan_find_sock(net, ipv6 ? AF_INET6 : AF_INET, port); + vs = vxlan_find_sock(net, ipv6 ? AF_INET6 : AF_INET, port, flags); if (vs && ((vs->rcv != rcv) || !atomic_add_unless(&vs->refcnt, 1, 0))) vs = ERR_PTR(-EBUSY); @@ -2432,10 +2625,10 @@ static void vxlan_sock_work(struct work_struct *work) dev_put(vxlan->dev); } -static int vxlan_newlink(struct net *net, struct net_device *dev, +static int vxlan_newlink(struct net *src_net, struct net_device *dev, struct nlattr *tb[], struct nlattr *data[]) { - struct vxlan_net *vn = net_generic(net, vxlan_net_id); + 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; __u32 vni; @@ -2445,7 +2638,7 @@ static int vxlan_newlink(struct net *net, struct net_device *dev, if (!data[IFLA_VXLAN_ID]) return -EINVAL; - vxlan->net = dev_net(dev); + vxlan->net = src_net; vni = nla_get_u32(data[IFLA_VXLAN_ID]); dst->remote_vni = vni; @@ -2481,7 +2674,7 @@ static int vxlan_newlink(struct net *net, struct net_device *dev, if (data[IFLA_VXLAN_LINK] && (dst->remote_ifindex = nla_get_u32(data[IFLA_VXLAN_LINK]))) { struct net_device *lowerdev - = __dev_get_by_index(net, dst->remote_ifindex); + = __dev_get_by_index(src_net, dst->remote_ifindex); if (!lowerdev) { pr_info("ifindex %d does not exist\n", dst->remote_ifindex); @@ -2557,8 +2750,19 @@ static int vxlan_newlink(struct net *net, struct net_device *dev, nla_get_u8(data[IFLA_VXLAN_UDP_ZERO_CSUM6_RX])) vxlan->flags |= VXLAN_F_UDP_ZERO_CSUM6_RX; - if (vxlan_find_vni(net, vni, use_ipv6 ? AF_INET6 : AF_INET, - vxlan->dst_port)) { + if (data[IFLA_VXLAN_REMCSUM_TX] && + nla_get_u8(data[IFLA_VXLAN_REMCSUM_TX])) + vxlan->flags |= VXLAN_F_REMCSUM_TX; + + if (data[IFLA_VXLAN_REMCSUM_RX] && + nla_get_u8(data[IFLA_VXLAN_REMCSUM_RX])) + vxlan->flags |= VXLAN_F_REMCSUM_RX; + + if (data[IFLA_VXLAN_GBP]) + vxlan->flags |= VXLAN_F_GBP; + + if (vxlan_find_vni(src_net, vni, use_ipv6 ? AF_INET6 : AF_INET, + vxlan->dst_port, vxlan->flags)) { pr_info("duplicate VNI %u\n", vni); return -EEXIST; } @@ -2625,6 +2829,8 @@ static size_t vxlan_get_size(const struct net_device *dev) nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_CSUM */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_ZERO_CSUM6_TX */ nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_UDP_ZERO_CSUM6_RX */ + nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_REMCSUM_TX */ + nla_total_size(sizeof(__u8)) + /* IFLA_VXLAN_REMCSUM_RX */ 0; } @@ -2690,18 +2896,33 @@ static int vxlan_fill_info(struct sk_buff *skb, const struct net_device *dev) nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, !!(vxlan->flags & VXLAN_F_UDP_ZERO_CSUM6_TX)) || nla_put_u8(skb, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, - !!(vxlan->flags & VXLAN_F_UDP_ZERO_CSUM6_RX))) + !!(vxlan->flags & VXLAN_F_UDP_ZERO_CSUM6_RX)) || + nla_put_u8(skb, IFLA_VXLAN_REMCSUM_TX, + !!(vxlan->flags & VXLAN_F_REMCSUM_TX)) || + nla_put_u8(skb, IFLA_VXLAN_REMCSUM_RX, + !!(vxlan->flags & VXLAN_F_REMCSUM_RX))) goto nla_put_failure; if (nla_put(skb, IFLA_VXLAN_PORT_RANGE, sizeof(ports), &ports)) goto nla_put_failure; + if (vxlan->flags & VXLAN_F_GBP && + nla_put_flag(skb, IFLA_VXLAN_GBP)) + goto nla_put_failure; + return 0; nla_put_failure: return -EMSGSIZE; } +static struct net *vxlan_get_link_net(const struct net_device *dev) +{ + struct vxlan_dev *vxlan = netdev_priv(dev); + + return vxlan->net; +} + static struct rtnl_link_ops vxlan_link_ops __read_mostly = { .kind = "vxlan", .maxtype = IFLA_VXLAN_MAX, @@ -2713,6 +2934,7 @@ static struct rtnl_link_ops vxlan_link_ops __read_mostly = { .dellink = vxlan_dellink, .get_size = vxlan_get_size, .fill_info = vxlan_fill_info, + .get_link_net = vxlan_get_link_net, }; static void vxlan_handle_lowerdev_unregister(struct vxlan_net *vn, diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig index 94e234975c61..a2fdd15f285a 100644 --- a/drivers/net/wan/Kconfig +++ b/drivers/net/wan/Kconfig @@ -25,7 +25,7 @@ if WAN # There is no way to detect a comtrol sv11 - force it modular for now. config HOSTESS_SV11 tristate "Comtrol Hostess SV-11 support" - depends on ISA && m && ISA_DMA_API && INET && HDLC + depends on ISA && m && ISA_DMA_API && INET && HDLC && VIRT_TO_BUS help Driver for Comtrol Hostess SV-11 network card which operates on low speed synchronous serial links at up to @@ -37,7 +37,7 @@ config HOSTESS_SV11 # The COSA/SRP driver has not been tested as non-modular yet. config COSA tristate "COSA/SRP sync serial boards support" - depends on ISA && m && ISA_DMA_API && HDLC + depends on ISA && m && ISA_DMA_API && HDLC && VIRT_TO_BUS ---help--- Driver for COSA and SRP synchronous serial boards. @@ -87,7 +87,7 @@ config LANMEDIA # There is no way to detect a Sealevel board. Force it modular config SEALEVEL_4021 tristate "Sealevel Systems 4021 support" - depends on ISA && m && ISA_DMA_API && INET && HDLC + depends on ISA && m && ISA_DMA_API && INET && HDLC && VIRT_TO_BUS help This is a driver for the Sealevel Systems ACB 56 serial I/O adapter. diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index 17fcaabb2687..f07a61899545 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -1837,6 +1837,7 @@ static int adm8211_probe(struct pci_dev *pdev, if (!priv->map) { printk(KERN_ERR "%s (adm8211): Cannot map device memory\n", pci_name(pdev)); + err = -ENOMEM; goto err_free_dev; } diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h index ccba4fea7269..1eebe2ea3dfb 100644 --- a/drivers/net/wireless/ath/ath.h +++ b/drivers/net/wireless/ath/ath.h @@ -64,6 +64,7 @@ enum ath_op_flags { ATH_OP_HW_RESET, ATH_OP_SCANNING, ATH_OP_MULTI_CHANNEL, + ATH_OP_WOW_ENABLED, }; enum ath_bus_type { diff --git a/drivers/net/wireless/ath/ath10k/Makefile b/drivers/net/wireless/ath/ath10k/Makefile index 8b1b1adb477a..f4dbb3e93bf8 100644 --- a/drivers/net/wireless/ath/ath10k/Makefile +++ b/drivers/net/wireless/ath/ath10k/Makefile @@ -8,11 +8,15 @@ ath10k_core-y += mac.o \ htt_tx.o \ txrx.o \ wmi.o \ - bmi.o + wmi-tlv.o \ + bmi.o \ + hw.o ath10k_core-$(CONFIG_ATH10K_DEBUGFS) += spectral.o ath10k_core-$(CONFIG_NL80211_TESTMODE) += testmode.o ath10k_core-$(CONFIG_ATH10K_TRACING) += trace.o +ath10k_core-$(CONFIG_THERMAL) += thermal.o +ath10k_core-$(CONFIG_MAC80211_DEBUGFS) += debugfs_sta.o obj-$(CONFIG_ATH10K_PCI) += ath10k_pci.o ath10k_pci-y += pci.o \ diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index a156e6e48708..e508c65b6ba8 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -803,7 +803,7 @@ int ath10k_ce_disable_interrupts(struct ath10k *ar) int ce_id; for (ce_id = 0; ce_id < CE_COUNT; ce_id++) { - u32 ctrl_addr = ath10k_ce_base_address(ce_id); + u32 ctrl_addr = ath10k_ce_base_address(ar, ce_id); ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr); ath10k_ce_error_intr_disable(ar, ctrl_addr); @@ -832,7 +832,7 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar, struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; struct ath10k_ce_ring *src_ring = ce_state->src_ring; - u32 nentries, ctrl_addr = ath10k_ce_base_address(ce_id); + u32 nentries, ctrl_addr = ath10k_ce_base_address(ar, ce_id); nentries = roundup_pow_of_two(attr->src_nentries); @@ -869,7 +869,7 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar, struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); struct ath10k_ce_pipe *ce_state = &ar_pci->ce_states[ce_id]; struct ath10k_ce_ring *dest_ring = ce_state->dest_ring; - u32 nentries, ctrl_addr = ath10k_ce_base_address(ce_id); + u32 nentries, ctrl_addr = ath10k_ce_base_address(ar, ce_id); nentries = roundup_pow_of_two(attr->dest_nentries); @@ -1051,7 +1051,7 @@ int ath10k_ce_init_pipe(struct ath10k *ar, unsigned int ce_id, static void ath10k_ce_deinit_src_ring(struct ath10k *ar, unsigned int ce_id) { - u32 ctrl_addr = ath10k_ce_base_address(ce_id); + u32 ctrl_addr = ath10k_ce_base_address(ar, ce_id); ath10k_ce_src_ring_base_addr_set(ar, ctrl_addr, 0); ath10k_ce_src_ring_size_set(ar, ctrl_addr, 0); @@ -1061,7 +1061,7 @@ static void ath10k_ce_deinit_src_ring(struct ath10k *ar, unsigned int ce_id) static void ath10k_ce_deinit_dest_ring(struct ath10k *ar, unsigned int ce_id) { - u32 ctrl_addr = ath10k_ce_base_address(ce_id); + u32 ctrl_addr = ath10k_ce_base_address(ar, ce_id); ath10k_ce_dest_ring_base_addr_set(ar, ctrl_addr, 0); ath10k_ce_dest_ring_size_set(ar, ctrl_addr, 0); @@ -1093,10 +1093,12 @@ int ath10k_ce_alloc_pipe(struct ath10k *ar, int ce_id, (CE_HTT_H2T_MSG_SRC_NENTRIES - 1)); BUILD_BUG_ON(2*TARGET_10X_NUM_MSDU_DESC > (CE_HTT_H2T_MSG_SRC_NENTRIES - 1)); + BUILD_BUG_ON(2*TARGET_TLV_NUM_MSDU_DESC > + (CE_HTT_H2T_MSG_SRC_NENTRIES - 1)); ce_state->ar = ar; ce_state->id = ce_id; - ce_state->ctrl_addr = ath10k_ce_base_address(ce_id); + ce_state->ctrl_addr = ath10k_ce_base_address(ar, ce_id); ce_state->attr_flags = attr->flags; ce_state->src_sz_max = attr->src_sz_max; diff --git a/drivers/net/wireless/ath/ath10k/ce.h b/drivers/net/wireless/ath/ath10k/ce.h index 617a151e8ce4..c18647b87f71 100644 --- a/drivers/net/wireless/ath/ath10k/ce.h +++ b/drivers/net/wireless/ath/ath10k/ce.h @@ -394,7 +394,7 @@ struct ce_attr { #define DST_WATERMARK_HIGH_RESET 0 #define DST_WATERMARK_ADDRESS 0x0050 -static inline u32 ath10k_ce_base_address(unsigned int ce_id) +static inline u32 ath10k_ce_base_address(struct ath10k *ar, unsigned int ce_id) { return CE0_BASE_ADDRESS + (CE1_BASE_ADDRESS - CE0_BASE_ADDRESS) * ce_id; } diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 7762061a1944..310e12bc078a 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -17,6 +17,7 @@ #include <linux/module.h> #include <linux/firmware.h> +#include <linux/of.h> #include "core.h" #include "mac.h" @@ -27,20 +28,18 @@ #include "debug.h" #include "htt.h" #include "testmode.h" +#include "wmi-ops.h" unsigned int ath10k_debug_mask; static bool uart_print; -static unsigned int ath10k_p2p; static bool skip_otp; module_param_named(debug_mask, ath10k_debug_mask, uint, 0644); module_param(uart_print, bool, 0644); -module_param_named(p2p, ath10k_p2p, uint, 0644); module_param(skip_otp, bool, 0644); MODULE_PARM_DESC(debug_mask, "Debugging mask"); MODULE_PARM_DESC(uart_print, "Uart target debugging"); -MODULE_PARM_DESC(p2p, "Enable ath10k P2P support"); MODULE_PARM_DESC(skip_otp, "Skip otp failure for calibration in testmode"); static const struct ath10k_hw_params ath10k_hw_params_list[] = { @@ -48,11 +47,57 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = { .id = QCA988X_HW_2_0_VERSION, .name = "qca988x hw2.0", .patch_load_addr = QCA988X_HW_2_0_PATCH_LOAD_ADDR, + .uart_pin = 7, .fw = { .dir = QCA988X_HW_2_0_FW_DIR, .fw = QCA988X_HW_2_0_FW_FILE, .otp = QCA988X_HW_2_0_OTP_FILE, .board = QCA988X_HW_2_0_BOARD_DATA_FILE, + .board_size = QCA988X_BOARD_DATA_SZ, + .board_ext_size = QCA988X_BOARD_EXT_DATA_SZ, + }, + }, + { + .id = QCA6174_HW_2_1_VERSION, + .name = "qca6174 hw2.1", + .patch_load_addr = QCA6174_HW_2_1_PATCH_LOAD_ADDR, + .uart_pin = 6, + .fw = { + .dir = QCA6174_HW_2_1_FW_DIR, + .fw = QCA6174_HW_2_1_FW_FILE, + .otp = QCA6174_HW_2_1_OTP_FILE, + .board = QCA6174_HW_2_1_BOARD_DATA_FILE, + .board_size = QCA6174_BOARD_DATA_SZ, + .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, + }, + }, + { + .id = QCA6174_HW_3_0_VERSION, + .name = "qca6174 hw3.0", + .patch_load_addr = QCA6174_HW_3_0_PATCH_LOAD_ADDR, + .uart_pin = 6, + .fw = { + .dir = QCA6174_HW_3_0_FW_DIR, + .fw = QCA6174_HW_3_0_FW_FILE, + .otp = QCA6174_HW_3_0_OTP_FILE, + .board = QCA6174_HW_3_0_BOARD_DATA_FILE, + .board_size = QCA6174_BOARD_DATA_SZ, + .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, + }, + }, + { + .id = QCA6174_HW_3_2_VERSION, + .name = "qca6174 hw3.2", + .patch_load_addr = QCA6174_HW_3_0_PATCH_LOAD_ADDR, + .uart_pin = 6, + .fw = { + /* uses same binaries as hw3.0 */ + .dir = QCA6174_HW_3_0_FW_DIR, + .fw = QCA6174_HW_3_0_FW_FILE, + .otp = QCA6174_HW_3_0_OTP_FILE, + .board = QCA6174_HW_3_0_BOARD_DATA_FILE, + .board_size = QCA6174_BOARD_DATA_SZ, + .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ, }, }, }; @@ -146,8 +191,8 @@ static const struct firmware *ath10k_fetch_fw_file(struct ath10k *ar, static int ath10k_push_board_ext_data(struct ath10k *ar, const void *data, size_t data_len) { - u32 board_data_size = QCA988X_BOARD_DATA_SZ; - u32 board_ext_data_size = QCA988X_BOARD_EXT_DATA_SZ; + u32 board_data_size = ar->hw_params.fw.board_size; + u32 board_ext_data_size = ar->hw_params.fw.board_ext_size; u32 board_ext_data_addr; int ret; @@ -193,7 +238,7 @@ static int ath10k_push_board_ext_data(struct ath10k *ar, const void *data, static int ath10k_download_board_data(struct ath10k *ar, const void *data, size_t data_len) { - u32 board_data_size = QCA988X_BOARD_DATA_SZ; + u32 board_data_size = ar->hw_params.fw.board_size; u32 address; int ret; @@ -249,6 +294,63 @@ static int ath10k_download_cal_file(struct ath10k *ar) return 0; } +static int ath10k_download_cal_dt(struct ath10k *ar) +{ + struct device_node *node; + int data_len; + void *data; + int ret; + + node = ar->dev->of_node; + if (!node) + /* Device Tree is optional, don't print any warnings if + * there's no node for ath10k. + */ + return -ENOENT; + + if (!of_get_property(node, "qcom,ath10k-calibration-data", + &data_len)) { + /* The calibration data node is optional */ + return -ENOENT; + } + + if (data_len != QCA988X_CAL_DATA_LEN) { + ath10k_warn(ar, "invalid calibration data length in DT: %d\n", + data_len); + ret = -EMSGSIZE; + goto out; + } + + data = kmalloc(data_len, GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto out; + } + + ret = of_property_read_u8_array(node, "qcom,ath10k-calibration-data", + data, data_len); + if (ret) { + ath10k_warn(ar, "failed to read calibration data from DT: %d\n", + ret); + goto out_free; + } + + ret = ath10k_download_board_data(ar, data, data_len); + if (ret) { + ath10k_warn(ar, "failed to download calibration data from Device Tree: %d\n", + ret); + goto out_free; + } + + ret = 0; + +out_free: + kfree(data); + +out: + return ret; +} + static int ath10k_download_and_run_otp(struct ath10k *ar) { u32 result, address = ar->hw_params.patch_load_addr; @@ -447,7 +549,7 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) int ie_id, i, index, bit, ret; struct ath10k_fw_ie *hdr; const u8 *data; - __le32 *timestamp; + __le32 *timestamp, *version; /* first fetch the firmware file (firmware-*.bin) */ ar->firmware = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, name); @@ -562,6 +664,17 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) ar->otp_len = ie_len; break; + case ATH10K_FW_IE_WMI_OP_VERSION: + if (ie_len != sizeof(u32)) + break; + + version = (__le32 *)data; + + ar->wmi.op_version = le32_to_cpup(version); + + ath10k_dbg(ar, ATH10K_DBG_BOOT, "found fw ie wmi op version %d\n", + ar->wmi.op_version); + break; default: ath10k_warn(ar, "Unknown FW IE: %u\n", le32_to_cpu(hdr->id)); @@ -582,13 +695,6 @@ static int ath10k_core_fetch_firmware_api_n(struct ath10k *ar, const char *name) goto err; } - if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features) && - !test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - ath10k_err(ar, "feature bits corrupted: 10.2 feature requires 10.x feature to be set as well"); - ret = -EINVAL; - goto err; - } - /* now fetch the board file */ if (ar->hw_params.fw.board == NULL) { ath10k_err(ar, "board data file not defined"); @@ -624,6 +730,13 @@ static int ath10k_core_fetch_firmware_files(struct ath10k *ar) /* calibration file is optional, don't check for any errors */ ath10k_fetch_cal_file(ar); + ar->fw_api = 4; + ath10k_dbg(ar, ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api); + + ret = ath10k_core_fetch_firmware_api_n(ar, ATH10K_FW_API4_FILE); + if (ret == 0) + goto success; + ar->fw_api = 3; ath10k_dbg(ar, ATH10K_DBG_BOOT, "trying fw api %d\n", ar->fw_api); @@ -662,7 +775,17 @@ static int ath10k_download_cal_data(struct ath10k *ar) } ath10k_dbg(ar, ATH10K_DBG_BOOT, - "boot did not find a calibration file, try OTP next: %d\n", + "boot did not find a calibration file, try DT next: %d\n", + ret); + + ret = ath10k_download_cal_dt(ar); + if (ret == 0) { + ar->cal_mode = ATH10K_CAL_MODE_DT; + goto done; + } + + ath10k_dbg(ar, ATH10K_DBG_BOOT, + "boot did not find DT entry, try OTP next: %d\n", ret); ret = ath10k_download_and_run_otp(ar); @@ -696,7 +819,7 @@ static int ath10k_init_uart(struct ath10k *ar) if (!uart_print) return 0; - ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, 7); + ret = ath10k_bmi_write32(ar, hi_dbg_uart_txpin, ar->hw_params.uart_pin); if (ret) { ath10k_warn(ar, "could not enable UART prints (%d)\n", ret); return ret; @@ -764,6 +887,7 @@ static void ath10k_core_restart(struct work_struct *work) complete_all(&ar->offchan_tx_completed); complete_all(&ar->install_key_done); complete_all(&ar->vdev_setup_done); + complete_all(&ar->thermal.wmi_sync); wake_up(&ar->htt.empty_tx_wq); wake_up(&ar->wmi.tx_credits_wq); wake_up(&ar->peer_mapping_wq); @@ -799,15 +923,63 @@ static void ath10k_core_restart(struct work_struct *work) mutex_unlock(&ar->conf_mutex); } -static void ath10k_core_init_max_sta_count(struct ath10k *ar) +static int ath10k_core_init_firmware_features(struct ath10k *ar) { - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - ar->max_num_peers = TARGET_10X_NUM_PEERS; - ar->max_num_stations = TARGET_10X_NUM_STATIONS; - } else { + if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features) && + !test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { + ath10k_err(ar, "feature bits corrupted: 10.2 feature requires 10.x feature to be set as well"); + return -EINVAL; + } + + if (ar->wmi.op_version >= ATH10K_FW_WMI_OP_VERSION_MAX) { + ath10k_err(ar, "unsupported WMI OP version (max %d): %d\n", + ATH10K_FW_WMI_OP_VERSION_MAX, ar->wmi.op_version); + return -EINVAL; + } + + /* Backwards compatibility for firmwares without + * ATH10K_FW_IE_WMI_OP_VERSION. + */ + if (ar->wmi.op_version == ATH10K_FW_WMI_OP_VERSION_UNSET) { + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { + if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, + ar->fw_features)) + ar->wmi.op_version = ATH10K_FW_WMI_OP_VERSION_10_2; + else + ar->wmi.op_version = ATH10K_FW_WMI_OP_VERSION_10_1; + } else { + ar->wmi.op_version = ATH10K_FW_WMI_OP_VERSION_MAIN; + } + } + + switch (ar->wmi.op_version) { + case ATH10K_FW_WMI_OP_VERSION_MAIN: ar->max_num_peers = TARGET_NUM_PEERS; ar->max_num_stations = TARGET_NUM_STATIONS; + ar->max_num_vdevs = TARGET_NUM_VDEVS; + ar->htt.max_num_pending_tx = TARGET_NUM_MSDU_DESC; + break; + case ATH10K_FW_WMI_OP_VERSION_10_1: + case ATH10K_FW_WMI_OP_VERSION_10_2: + case ATH10K_FW_WMI_OP_VERSION_10_2_4: + ar->max_num_peers = TARGET_10X_NUM_PEERS; + ar->max_num_stations = TARGET_10X_NUM_STATIONS; + ar->max_num_vdevs = TARGET_10X_NUM_VDEVS; + ar->htt.max_num_pending_tx = TARGET_10X_NUM_MSDU_DESC; + break; + case ATH10K_FW_WMI_OP_VERSION_TLV: + ar->max_num_peers = TARGET_TLV_NUM_PEERS; + ar->max_num_stations = TARGET_TLV_NUM_STATIONS; + ar->max_num_vdevs = TARGET_TLV_NUM_VDEVS; + ar->htt.max_num_pending_tx = TARGET_TLV_NUM_MSDU_DESC; + break; + case ATH10K_FW_WMI_OP_VERSION_UNSET: + case ATH10K_FW_WMI_OP_VERSION_MAX: + WARN_ON(1); + return -EINVAL; } + + return 0; } int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode) @@ -932,6 +1104,18 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode) goto err_hif_stop; } + /* If firmware indicates Full Rx Reorder support it must be used in a + * slightly different manner. Let HTT code know. + */ + ar->htt.rx_ring.in_ord_rx = !!(test_bit(WMI_SERVICE_RX_FULL_REORDER, + ar->wmi.svc_map)); + + status = ath10k_htt_rx_ring_refill(ar); + if (status) { + ath10k_err(ar, "failed to refill htt rx ring: %d\n", status); + goto err_hif_stop; + } + /* we don't care about HTT in UTF mode */ if (mode == ATH10K_FIRMWARE_MODE_NORMAL) { status = ath10k_htt_setup(&ar->htt); @@ -945,10 +1129,7 @@ int ath10k_core_start(struct ath10k *ar, enum ath10k_firmware_mode mode) if (status) goto err_hif_stop; - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) - ar->free_vdev_map = (1LL << TARGET_10X_NUM_VDEVS) - 1; - else - ar->free_vdev_map = (1LL << TARGET_NUM_VDEVS) - 1; + ar->free_vdev_map = (1LL << ar->max_num_vdevs) - 1; INIT_LIST_HEAD(&ar->arvifs); @@ -1025,8 +1206,7 @@ static int ath10k_core_probe_fw(struct ath10k *ar) ret = ath10k_bmi_get_target_info(ar, &target_info); if (ret) { ath10k_err(ar, "could not get target info (%d)\n", ret); - ath10k_hif_power_down(ar); - return ret; + goto err_power_down; } ar->target_version = target_info.version; @@ -1035,28 +1215,28 @@ static int ath10k_core_probe_fw(struct ath10k *ar) ret = ath10k_init_hw_params(ar); if (ret) { ath10k_err(ar, "could not get hw params (%d)\n", ret); - ath10k_hif_power_down(ar); - return ret; + goto err_power_down; } ret = ath10k_core_fetch_firmware_files(ar); if (ret) { ath10k_err(ar, "could not fetch firmware files (%d)\n", ret); - ath10k_hif_power_down(ar); - return ret; + goto err_power_down; } - ath10k_core_init_max_sta_count(ar); + ret = ath10k_core_init_firmware_features(ar); + if (ret) { + ath10k_err(ar, "fatal problem with firmware features: %d\n", + ret); + goto err_free_firmware_files; + } mutex_lock(&ar->conf_mutex); ret = ath10k_core_start(ar, ATH10K_FIRMWARE_MODE_NORMAL); if (ret) { ath10k_err(ar, "could not init core (%d)\n", ret); - ath10k_core_free_firmware_files(ar); - ath10k_hif_power_down(ar); - mutex_unlock(&ar->conf_mutex); - return ret; + goto err_unlock; } ath10k_print_driver_info(ar); @@ -1066,34 +1246,17 @@ static int ath10k_core_probe_fw(struct ath10k *ar) ath10k_hif_power_down(ar); return 0; -} - -static int ath10k_core_check_chip_id(struct ath10k *ar) -{ - u32 hw_revision = MS(ar->chip_id, SOC_CHIP_ID_REV); - - ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot chip_id 0x%08x hw_revision 0x%x\n", - ar->chip_id, hw_revision); - /* Check that we are not using hw1.0 (some of them have same pci id - * as hw2.0) before doing anything else as ath10k crashes horribly - * due to missing hw1.0 workarounds. */ - switch (hw_revision) { - case QCA988X_HW_1_0_CHIP_ID_REV: - ath10k_err(ar, "ERROR: qca988x hw1.0 is not supported\n"); - return -EOPNOTSUPP; +err_unlock: + mutex_unlock(&ar->conf_mutex); - case QCA988X_HW_2_0_CHIP_ID_REV: - /* known hardware revision, continue normally */ - return 0; +err_free_firmware_files: + ath10k_core_free_firmware_files(ar); - default: - ath10k_warn(ar, "Warning: hardware revision unknown (0x%x), expect problems\n", - ar->chip_id); - return 0; - } +err_power_down: + ath10k_hif_power_down(ar); - return 0; + return ret; } static void ath10k_core_register_work(struct work_struct *work) @@ -1125,9 +1288,18 @@ static void ath10k_core_register_work(struct work_struct *work) goto err_debug_destroy; } + status = ath10k_thermal_register(ar); + if (status) { + ath10k_err(ar, "could not register thermal device: %d\n", + status); + goto err_spectral_destroy; + } + set_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags); return; +err_spectral_destroy: + ath10k_spectral_destroy(ar); err_debug_destroy: ath10k_debug_destroy(ar); err_unregister_mac: @@ -1143,16 +1315,7 @@ err: int ath10k_core_register(struct ath10k *ar, u32 chip_id) { - int status; - ar->chip_id = chip_id; - - status = ath10k_core_check_chip_id(ar); - if (status) { - ath10k_err(ar, "Unsupported chip id 0x%08x\n", ar->chip_id); - return status; - } - queue_work(ar->workqueue, &ar->register_work); return 0; @@ -1166,6 +1329,7 @@ void ath10k_core_unregister(struct ath10k *ar) if (!test_bit(ATH10K_FLAG_CORE_REGISTERED, &ar->dev_flags)) return; + ath10k_thermal_unregister(ar); /* Stop spectral before unregistering from mac80211 to remove the * relayfs debugfs file cleanly. Otherwise the parent debugfs tree * would be already be free'd recursively, leading to a double free. @@ -1187,6 +1351,7 @@ EXPORT_SYMBOL(ath10k_core_unregister); struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, enum ath10k_bus bus, + enum ath10k_hw_rev hw_rev, const struct ath10k_hif_ops *hif_ops) { struct ath10k *ar; @@ -1198,13 +1363,25 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, ar->ath_common.priv = ar; ar->ath_common.hw = ar->hw; - - ar->p2p = !!ath10k_p2p; ar->dev = dev; - + ar->hw_rev = hw_rev; ar->hif.ops = hif_ops; ar->hif.bus = bus; + switch (hw_rev) { + case ATH10K_HW_QCA988X: + ar->regs = &qca988x_regs; + break; + case ATH10K_HW_QCA6174: + ar->regs = &qca6174_regs; + break; + default: + ath10k_err(ar, "unsupported core hardware revision %d\n", + hw_rev); + ret = -ENOTSUPP; + goto err_free_mac; + } + init_completion(&ar->scan.started); init_completion(&ar->scan.completed); init_completion(&ar->scan.on_channel); @@ -1212,6 +1389,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, init_completion(&ar->install_key_done); init_completion(&ar->vdev_setup_done); + init_completion(&ar->thermal.wmi_sync); INIT_DELAYED_WORK(&ar->scan.timeout, ath10k_scan_timeout_work); diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 514c219263a5..d60e46fe6d19 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -34,6 +34,7 @@ #include "../regd.h" #include "../dfs_pattern_detector.h" #include "spectral.h" +#include "thermal.h" #define MS(_v, _f) (((_v) & _f##_MASK) >> _f##_LSB) #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) @@ -96,6 +97,11 @@ struct ath10k_skb_cb { } bcn; } __packed; +struct ath10k_skb_rxcb { + dma_addr_t paddr; + struct hlist_node hlist; +}; + static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb) { BUILD_BUG_ON(sizeof(struct ath10k_skb_cb) > @@ -103,6 +109,15 @@ static inline struct ath10k_skb_cb *ATH10K_SKB_CB(struct sk_buff *skb) return (struct ath10k_skb_cb *)&IEEE80211_SKB_CB(skb)->driver_data; } +static inline struct ath10k_skb_rxcb *ATH10K_SKB_RXCB(struct sk_buff *skb) +{ + BUILD_BUG_ON(sizeof(struct ath10k_skb_rxcb) > sizeof(skb->cb)); + return (struct ath10k_skb_rxcb *)skb->cb; +} + +#define ATH10K_RXCB_SKB(rxcb) \ + container_of((void *)rxcb, struct sk_buff, cb) + static inline u32 host_interest_item_address(u32 item_offset) { return QCA988X_HOST_INTEREST_ADDRESS + item_offset; @@ -120,6 +135,7 @@ struct ath10k_mem_chunk { }; struct ath10k_wmi { + enum ath10k_fw_wmi_op_version op_version; enum ath10k_htc_ep_id eid; struct completion service_ready; struct completion unified_ready; @@ -128,6 +144,7 @@ struct ath10k_wmi { struct wmi_cmd_map *cmd; struct wmi_vdev_param_map *vdev_param; struct wmi_pdev_param_map *pdev_param; + const struct wmi_ops *ops; u32 num_mem_chunks; struct ath10k_mem_chunk mem_chunks[WMI_MAX_MEM_REQS]; @@ -236,10 +253,21 @@ struct ath10k_sta { u32 smps; struct work_struct update_wk; + +#ifdef CONFIG_MAC80211_DEBUGFS + /* protected by conf_mutex */ + bool aggr_mode; +#endif }; #define ATH10K_VDEV_SETUP_TIMEOUT_HZ (5*HZ) +enum ath10k_beacon_state { + ATH10K_BEACON_SCHEDULED = 0, + ATH10K_BEACON_SENDING, + ATH10K_BEACON_SENT, +}; + struct ath10k_vif { struct list_head list; @@ -250,7 +278,7 @@ struct ath10k_vif { u32 dtim_period; struct sk_buff *beacon; /* protected by data_lock */ - bool beacon_sent; + enum ath10k_beacon_state beacon_state; void *beacon_buf; dma_addr_t beacon_paddr; @@ -263,10 +291,8 @@ struct ath10k_vif { u32 aid; u8 bssid[ETH_ALEN]; - struct work_struct wep_key_work; struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1]; - u8 def_wep_key_idx; - u8 def_wep_key_newidx; + s8 def_wep_key_idx; u16 tx_seq_no; @@ -293,6 +319,7 @@ struct ath10k_vif { bool use_cts_prot; int num_legacy_stations; int txpower; + struct wmi_wmm_params_all_arg wmm_params; }; struct ath10k_vif_iter { @@ -323,8 +350,10 @@ struct ath10k_debug { /* protected by conf_mutex */ u32 fw_dbglog_mask; + u32 fw_dbglog_level; u32 pktlog_filter; u32 reg_addr; + u32 nf_cal_period; u8 htt_max_amsdu; u8 htt_max_ampdu; @@ -369,7 +398,7 @@ enum ath10k_fw_features { /* wmi_mgmt_rx_hdr contains extra RSSI information */ ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX = 0, - /* firmware from 10X branch */ + /* Firmware from 10X branch. Deprecated, don't use in new code. */ ATH10K_FW_FEATURE_WMI_10X = 1, /* firmware support tx frame management over WMI, otherwise it's HTT */ @@ -378,8 +407,9 @@ enum ath10k_fw_features { /* Firmware does not support P2P */ ATH10K_FW_FEATURE_NO_P2P = 3, - /* Firmware 10.2 feature bit. The ATH10K_FW_FEATURE_WMI_10X feature bit - * is required to be set as well. + /* Firmware 10.2 feature bit. The ATH10K_FW_FEATURE_WMI_10X feature + * bit is required to be set as well. Deprecated, don't use in new + * code. */ ATH10K_FW_FEATURE_WMI_10_2 = 4, @@ -401,6 +431,7 @@ enum ath10k_dev_flags { enum ath10k_cal_mode { ATH10K_CAL_MODE_FILE, ATH10K_CAL_MODE_OTP, + ATH10K_CAL_MODE_DT, }; static inline const char *ath10k_cal_mode_str(enum ath10k_cal_mode mode) @@ -410,6 +441,8 @@ static inline const char *ath10k_cal_mode_str(enum ath10k_cal_mode mode) return "file"; case ATH10K_CAL_MODE_OTP: return "otp"; + case ATH10K_CAL_MODE_DT: + return "dt"; } return "unknown"; @@ -444,6 +477,7 @@ struct ath10k { struct device *dev; u8 mac_addr[ETH_ALEN]; + enum ath10k_hw_rev hw_rev; u32 chip_id; u32 target_version; u8 fw_version_major; @@ -459,9 +493,6 @@ struct ath10k { DECLARE_BITMAP(fw_features, ATH10K_FW_FEATURE_COUNT); - struct targetdef *targetdef; - struct hostdef *hostdef; - bool p2p; struct { @@ -471,6 +502,7 @@ struct ath10k { struct completion target_suspend; + const struct ath10k_hw_regs *regs; struct ath10k_bmi bmi; struct ath10k_wmi wmi; struct ath10k_htc htc; @@ -480,12 +512,15 @@ struct ath10k { u32 id; const char *name; u32 patch_load_addr; + int uart_pin; struct ath10k_hw_params_fw { const char *dir; const char *fw; const char *otp; const char *board; + size_t board_size; + size_t board_ext_size; } fw; } hw_params; @@ -548,7 +583,6 @@ struct ath10k { u8 cfg_tx_chainmask; u8 cfg_rx_chainmask; - struct wmi_pdev_set_wmm_params_arg wmm_params; struct completion install_key_done; struct completion vdev_setup_done; @@ -571,6 +605,7 @@ struct ath10k { int max_num_peers; int max_num_stations; + int max_num_vdevs; struct work_struct offchan_tx_work; struct sk_buff_head offchan_tx_queue; @@ -610,6 +645,7 @@ struct ath10k { /* protected by conf_mutex */ const struct firmware *utf; DECLARE_BITMAP(orig_fw_features, ATH10K_FW_FEATURE_COUNT); + enum ath10k_fw_wmi_op_version orig_wmi_op_version; /* protected by data_lock */ bool utf_monitor; @@ -622,12 +658,15 @@ struct ath10k { u32 fw_cold_reset_counter; } stats; + struct ath10k_thermal thermal; + /* must be last */ u8 drv_priv[0] __aligned(sizeof(void *)); }; struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev, enum ath10k_bus bus, + enum ath10k_hw_rev hw_rev, const struct ath10k_hif_ops *hif_ops); void ath10k_core_destroy(struct ath10k *ar); diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index a716758f14b0..d2281e5c2ffe 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -23,6 +23,7 @@ #include "core.h" #include "debug.h" #include "hif.h" +#include "wmi-ops.h" /* ms */ #define ATH10K_DEBUG_HTT_STATS_INTERVAL 1000 @@ -123,7 +124,7 @@ EXPORT_SYMBOL(ath10k_info); void ath10k_print_driver_info(struct ath10k *ar) { - ath10k_info(ar, "%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d wmi %d.%d.%d.%d cal %s max_sta %d\n", + ath10k_info(ar, "%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d wmi %d cal %s max_sta %d\n", ar->hw_params.name, ar->target_version, ar->chip_id, @@ -131,10 +132,7 @@ void ath10k_print_driver_info(struct ath10k *ar) ar->fw_api, ar->htt.target_version_major, ar->htt.target_version_minor, - ar->fw_version_major, - ar->fw_version_minor, - ar->fw_version_release, - ar->fw_version_build, + ar->wmi.op_version, ath10k_cal_mode_str(ar->cal_mode), ar->max_num_stations); ath10k_info(ar, "debug %d debugfs %d tracing %d dfs %d testmode %d\n", @@ -373,7 +371,7 @@ static int ath10k_debug_fw_stats_request(struct ath10k *ar) ret = wait_for_completion_timeout(&ar->debug.fw_stats_complete, 1*HZ); - if (ret <= 0) + if (ret == 0) return -ETIMEDOUT; spin_lock_bh(&ar->data_lock); @@ -1320,10 +1318,10 @@ static ssize_t ath10k_read_fw_dbglog(struct file *file, { struct ath10k *ar = file->private_data; unsigned int len; - char buf[32]; + char buf[64]; - len = scnprintf(buf, sizeof(buf), "0x%08x\n", - ar->debug.fw_dbglog_mask); + len = scnprintf(buf, sizeof(buf), "0x%08x %u\n", + ar->debug.fw_dbglog_mask, ar->debug.fw_dbglog_level); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } @@ -1333,19 +1331,32 @@ static ssize_t ath10k_write_fw_dbglog(struct file *file, size_t count, loff_t *ppos) { struct ath10k *ar = file->private_data; - unsigned long mask; int ret; + char buf[64]; + unsigned int log_level, mask; - ret = kstrtoul_from_user(user_buf, count, 0, &mask); - if (ret) - return ret; + simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); + + /* make sure that buf is null terminated */ + buf[sizeof(buf) - 1] = 0; + + ret = sscanf(buf, "%x %u", &mask, &log_level); + + if (!ret) + return -EINVAL; + + if (ret == 1) + /* default if user did not specify */ + log_level = ATH10K_DBGLOG_LEVEL_WARN; mutex_lock(&ar->conf_mutex); ar->debug.fw_dbglog_mask = mask; + ar->debug.fw_dbglog_level = log_level; if (ar->state == ATH10K_STATE_ON) { - ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask); + ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask, + ar->debug.fw_dbglog_level); if (ret) { ath10k_warn(ar, "dbglog cfg failed from debugfs: %d\n", ret); @@ -1607,6 +1618,73 @@ static const struct file_operations fops_cal_data = { .llseek = default_llseek, }; +static ssize_t ath10k_read_nf_cal_period(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + unsigned int len; + char buf[32]; + + len = scnprintf(buf, sizeof(buf), "%d\n", + ar->debug.nf_cal_period); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath10k_write_nf_cal_period(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + unsigned long period; + int ret; + + ret = kstrtoul_from_user(user_buf, count, 0, &period); + if (ret) + return ret; + + if (period > WMI_PDEV_PARAM_CAL_PERIOD_MAX) + return -EINVAL; + + /* there's no way to switch back to the firmware default */ + if (period == 0) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + ar->debug.nf_cal_period = period; + + if (ar->state != ATH10K_STATE_ON) { + /* firmware is not running, nothing else to do */ + ret = count; + goto exit; + } + + ret = ath10k_wmi_pdev_set_param(ar, ar->wmi.pdev_param->cal_period, + ar->debug.nf_cal_period); + if (ret) { + ath10k_warn(ar, "cal period cfg failed from debugfs: %d\n", + ret); + goto exit; + } + + ret = count; + +exit: + mutex_unlock(&ar->conf_mutex); + + return ret; +} + +static const struct file_operations fops_nf_cal_period = { + .read = ath10k_read_nf_cal_period, + .write = ath10k_write_nf_cal_period, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + int ath10k_debug_start(struct ath10k *ar) { int ret; @@ -1620,7 +1698,8 @@ int ath10k_debug_start(struct ath10k *ar) ret); if (ar->debug.fw_dbglog_mask) { - ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask); + ret = ath10k_wmi_dbglog_cfg(ar, ar->debug.fw_dbglog_mask, + ATH10K_DBGLOG_LEVEL_WARN); if (ret) /* not serious */ ath10k_warn(ar, "failed to enable dbglog during start: %d", @@ -1642,6 +1721,16 @@ int ath10k_debug_start(struct ath10k *ar) ath10k_warn(ar, "failed to disable pktlog: %d\n", ret); } + if (ar->debug.nf_cal_period) { + ret = ath10k_wmi_pdev_set_param(ar, + ar->wmi.pdev_param->cal_period, + ar->debug.nf_cal_period); + if (ret) + /* not serious */ + ath10k_warn(ar, "cal period cfg failed from debug start: %d\n", + ret); + } + return ret; } @@ -1880,6 +1969,9 @@ int ath10k_debug_register(struct ath10k *ar) debugfs_create_file("cal_data", S_IRUSR, ar->debug.debugfs_phy, ar, &fops_cal_data); + debugfs_create_file("nf_cal_period", S_IRUSR | S_IWUSR, + ar->debug.debugfs_phy, ar, &fops_nf_cal_period); + if (config_enabled(CONFIG_ATH10K_DFS_CERTIFIED)) { debugfs_create_file("dfs_simulate_radar", S_IWUSR, ar->debug.debugfs_phy, ar, diff --git a/drivers/net/wireless/ath/ath10k/debug.h b/drivers/net/wireless/ath/ath10k/debug.h index 1b87a5dbec53..a12b8323f9f1 100644 --- a/drivers/net/wireless/ath/ath10k/debug.h +++ b/drivers/net/wireless/ath/ath10k/debug.h @@ -48,6 +48,12 @@ enum ath10k_pktlog_filter { ATH10K_PKTLOG_ANY = 0x00000001f, }; +enum ath10k_dbg_aggr_mode { + ATH10K_DBG_AGGR_MODE_AUTO, + ATH10K_DBG_AGGR_MODE_MANUAL, + ATH10K_DBG_AGGR_MODE_MAX, +}; + extern unsigned int ath10k_debug_mask; __printf(2, 3) void ath10k_info(struct ath10k *ar, const char *fmt, ...); @@ -77,7 +83,6 @@ int ath10k_debug_get_et_sset_count(struct ieee80211_hw *hw, void ath10k_debug_get_et_stats(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ethtool_stats *stats, u64 *data); - #else static inline int ath10k_debug_start(struct ath10k *ar) { @@ -129,6 +134,10 @@ ath10k_debug_get_new_fw_crash_data(struct ath10k *ar) #define ath10k_debug_get_et_stats NULL #endif /* CONFIG_ATH10K_DEBUGFS */ +#ifdef CONFIG_MAC80211_DEBUGFS +void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct dentry *dir); +#endif /* CONFIG_MAC80211_DEBUGFS */ #ifdef CONFIG_ATH10K_DEBUG __printf(3, 4) void ath10k_dbg(struct ath10k *ar, diff --git a/drivers/net/wireless/ath/ath10k/debugfs_sta.c b/drivers/net/wireless/ath/ath10k/debugfs_sta.c new file mode 100644 index 000000000000..95b5c49374e0 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/debugfs_sta.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2014 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "core.h" +#include "wmi-ops.h" +#include "debug.h" + +static ssize_t ath10k_dbg_sta_read_aggr_mode(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; + struct ath10k *ar = arsta->arvif->ar; + char buf[32]; + int len = 0; + + mutex_lock(&ar->conf_mutex); + len = scnprintf(buf, sizeof(buf) - len, "aggregation mode: %s\n", + (arsta->aggr_mode == ATH10K_DBG_AGGR_MODE_AUTO) ? + "auto" : "manual"); + mutex_unlock(&ar->conf_mutex); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath10k_dbg_sta_write_aggr_mode(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; + struct ath10k *ar = arsta->arvif->ar; + u32 aggr_mode; + int ret; + + if (kstrtouint_from_user(user_buf, count, 0, &aggr_mode)) + return -EINVAL; + + if (aggr_mode >= ATH10K_DBG_AGGR_MODE_MAX) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + if ((ar->state != ATH10K_STATE_ON) || + (aggr_mode == arsta->aggr_mode)) { + ret = count; + goto out; + } + + ret = ath10k_wmi_addba_clear_resp(ar, arsta->arvif->vdev_id, sta->addr); + if (ret) { + ath10k_warn(ar, "failed to clear addba session ret: %d\n", ret); + goto out; + } + + arsta->aggr_mode = aggr_mode; +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static const struct file_operations fops_aggr_mode = { + .read = ath10k_dbg_sta_read_aggr_mode, + .write = ath10k_dbg_sta_write_aggr_mode, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath10k_dbg_sta_write_addba(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; + struct ath10k *ar = arsta->arvif->ar; + u32 tid, buf_size; + int ret; + char buf[64]; + + simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); + + /* make sure that buf is null terminated */ + buf[sizeof(buf) - 1] = '\0'; + + ret = sscanf(buf, "%u %u", &tid, &buf_size); + if (ret != 2) + return -EINVAL; + + /* Valid TID values are 0 through 15 */ + if (tid > HTT_DATA_TX_EXT_TID_MGMT - 2) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + if ((ar->state != ATH10K_STATE_ON) || + (arsta->aggr_mode != ATH10K_DBG_AGGR_MODE_MANUAL)) { + ret = count; + goto out; + } + + ret = ath10k_wmi_addba_send(ar, arsta->arvif->vdev_id, sta->addr, + tid, buf_size); + if (ret) { + ath10k_warn(ar, "failed to send addba request: vdev_id %u peer %pM tid %u buf_size %u\n", + arsta->arvif->vdev_id, sta->addr, tid, buf_size); + } + + ret = count; +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static const struct file_operations fops_addba = { + .write = ath10k_dbg_sta_write_addba, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath10k_dbg_sta_write_addba_resp(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; + struct ath10k *ar = arsta->arvif->ar; + u32 tid, status; + int ret; + char buf[64]; + + simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); + + /* make sure that buf is null terminated */ + buf[sizeof(buf) - 1] = '\0'; + + ret = sscanf(buf, "%u %u", &tid, &status); + if (ret != 2) + return -EINVAL; + + /* Valid TID values are 0 through 15 */ + if (tid > HTT_DATA_TX_EXT_TID_MGMT - 2) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + if ((ar->state != ATH10K_STATE_ON) || + (arsta->aggr_mode != ATH10K_DBG_AGGR_MODE_MANUAL)) { + ret = count; + goto out; + } + + ret = ath10k_wmi_addba_set_resp(ar, arsta->arvif->vdev_id, sta->addr, + tid, status); + if (ret) { + ath10k_warn(ar, "failed to send addba response: vdev_id %u peer %pM tid %u status%u\n", + arsta->arvif->vdev_id, sta->addr, tid, status); + } + ret = count; +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static const struct file_operations fops_addba_resp = { + .write = ath10k_dbg_sta_write_addba_resp, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath10k_dbg_sta_write_delba(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_sta *sta = file->private_data; + struct ath10k_sta *arsta = (struct ath10k_sta *)sta->drv_priv; + struct ath10k *ar = arsta->arvif->ar; + u32 tid, initiator, reason; + int ret; + char buf[64]; + + simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); + + /* make sure that buf is null terminated */ + buf[sizeof(buf) - 1] = '\0'; + + ret = sscanf(buf, "%u %u %u", &tid, &initiator, &reason); + if (ret != 3) + return -EINVAL; + + /* Valid TID values are 0 through 15 */ + if (tid > HTT_DATA_TX_EXT_TID_MGMT - 2) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + if ((ar->state != ATH10K_STATE_ON) || + (arsta->aggr_mode != ATH10K_DBG_AGGR_MODE_MANUAL)) { + ret = count; + goto out; + } + + ret = ath10k_wmi_delba_send(ar, arsta->arvif->vdev_id, sta->addr, + tid, initiator, reason); + if (ret) { + ath10k_warn(ar, "failed to send delba: vdev_id %u peer %pM tid %u initiator %u reason %u\n", + arsta->arvif->vdev_id, sta->addr, tid, initiator, + reason); + } + ret = count; +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static const struct file_operations fops_delba = { + .write = ath10k_dbg_sta_write_delba, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath10k_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct dentry *dir) +{ + debugfs_create_file("aggr_mode", S_IRUGO | S_IWUSR, dir, sta, + &fops_aggr_mode); + debugfs_create_file("addba", S_IWUSR, dir, sta, &fops_addba); + debugfs_create_file("addba_resp", S_IWUSR, dir, sta, &fops_addba_resp); + debugfs_create_file("delba", S_IWUSR, dir, sta, &fops_delba); +} diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index f1946a6be442..2fd9e180272b 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -703,11 +703,9 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc, /* wait for response */ status = wait_for_completion_timeout(&htc->ctl_resp, ATH10K_HTC_CONN_SVC_TIMEOUT_HZ); - if (status <= 0) { - if (status == 0) - status = -ETIMEDOUT; + if (status == 0) { ath10k_err(ar, "Service connect timeout: %d\n", status); - return status; + return -ETIMEDOUT; } /* we controlled the buffer creation, it's aligned */ diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c index 56cb4aceb383..4f59ab923e48 100644 --- a/drivers/net/wireless/ath/ath10k/htt.c +++ b/drivers/net/wireless/ath/ath10k/htt.c @@ -53,7 +53,6 @@ int ath10k_htt_init(struct ath10k *ar) struct ath10k_htt *htt = &ar->htt; htt->ar = ar; - htt->max_throughput_mbps = 800; /* * Prefetch enough data to satisfy target @@ -102,7 +101,7 @@ int ath10k_htt_setup(struct ath10k_htt *htt) status = wait_for_completion_timeout(&htt->target_version_received, HTT_TARGET_VERSION_TIMEOUT_HZ); - if (status <= 0) { + if (status == 0) { ath10k_warn(ar, "htt version request timed out\n"); return -ETIMEDOUT; } diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 1bd5545af903..874bf44ff7a2 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -21,6 +21,7 @@ #include <linux/bug.h> #include <linux/interrupt.h> #include <linux/dmapool.h> +#include <linux/hashtable.h> #include <net/mac80211.h> #include "htc.h" @@ -286,7 +287,19 @@ enum htt_t2h_msg_type { HTT_T2H_MSG_TYPE_RC_UPDATE_IND = 0xc, HTT_T2H_MSG_TYPE_TX_INSPECT_IND = 0xd, HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION = 0xe, + HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND = 0xf, + HTT_T2H_MSG_TYPE_RX_PN_IND = 0x10, + HTT_T2H_MSG_TYPE_RX_OFFLOAD_DELIVER_IND = 0x11, + HTT_T2H_MSG_TYPE_RX_IN_ORD_PADDR_IND = 0x12, + /* 0x13 reservd */ + HTT_T2H_MSG_TYPE_WDI_IPA_OP_RESPONSE = 0x14, + + /* FIXME: Do not depend on this event id. Numbering of this event id is + * broken across different firmware revisions and HTT version fails to + * indicate this. + */ HTT_T2H_MSG_TYPE_TEST, + /* keep this last */ HTT_T2H_NUM_MSGS }; @@ -655,6 +668,53 @@ struct htt_rx_fragment_indication { #define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_END_MASK 0x00000FC0 #define HTT_RX_FRAG_IND_INFO1_FLUSH_SEQ_NUM_END_LSB 6 +struct htt_rx_pn_ind { + __le16 peer_id; + u8 tid; + u8 seqno_start; + u8 seqno_end; + u8 pn_ie_count; + u8 reserved; + u8 pn_ies[0]; +} __packed; + +struct htt_rx_offload_msdu { + __le16 msdu_len; + __le16 peer_id; + u8 vdev_id; + u8 tid; + u8 fw_desc; + u8 payload[0]; +} __packed; + +struct htt_rx_offload_ind { + u8 reserved; + __le16 msdu_count; +} __packed; + +struct htt_rx_in_ord_msdu_desc { + __le32 msdu_paddr; + __le16 msdu_len; + u8 fw_desc; + u8 reserved; +} __packed; + +struct htt_rx_in_ord_ind { + u8 info; + __le16 peer_id; + u8 vdev_id; + u8 reserved; + __le16 msdu_count; + struct htt_rx_in_ord_msdu_desc msdu_descs[0]; +} __packed; + +#define HTT_RX_IN_ORD_IND_INFO_TID_MASK 0x0000001f +#define HTT_RX_IN_ORD_IND_INFO_TID_LSB 0 +#define HTT_RX_IN_ORD_IND_INFO_OFFLOAD_MASK 0x00000020 +#define HTT_RX_IN_ORD_IND_INFO_OFFLOAD_LSB 5 +#define HTT_RX_IN_ORD_IND_INFO_FRAG_MASK 0x00000040 +#define HTT_RX_IN_ORD_IND_INFO_FRAG_LSB 6 + /* * target -> host test message definition * @@ -1150,6 +1210,9 @@ struct htt_resp { struct htt_rx_test rx_test; struct htt_pktlog_msg pktlog_msg; struct htt_stats_conf stats_conf; + struct htt_rx_pn_ind rx_pn_ind; + struct htt_rx_offload_ind rx_offload_ind; + struct htt_rx_in_ord_ind rx_in_ord_ind; }; } __packed; @@ -1182,7 +1245,6 @@ struct ath10k_htt { struct ath10k *ar; enum ath10k_htc_ep_id eid; - int max_throughput_mbps; u8 target_version_major; u8 target_version_minor; struct completion target_version_received; @@ -1198,6 +1260,20 @@ struct ath10k_htt { * filled. */ struct sk_buff **netbufs_ring; + + /* This is used only with firmware supporting IN_ORD_IND. + * + * With Full Rx Reorder the HTT Rx Ring is more of a temporary + * buffer ring from which buffer addresses are copied by the + * firmware to MAC Rx ring. Firmware then delivers IN_ORD_IND + * pointing to specific (re-ordered) buffers. + * + * FIXME: With kernel generic hashing functions there's a lot + * of hash collisions for sk_buffs. + */ + bool in_ord_rx; + DECLARE_HASHTABLE(skb_table, 4); + /* * Ring of buffer addresses - * This ring holds the "physical" device address of the @@ -1252,12 +1328,11 @@ struct ath10k_htt { unsigned int prefetch_len; - /* Protects access to %pending_tx, %used_msdu_ids */ + /* Protects access to pending_tx, num_pending_tx */ spinlock_t tx_lock; int max_num_pending_tx; int num_pending_tx; - struct sk_buff **pending_tx; - unsigned long *used_msdu_ids; /* bitmap */ + struct idr pending_tx; wait_queue_head_t empty_tx_wq; struct dma_pool *tx_pool; @@ -1271,6 +1346,7 @@ struct ath10k_htt { struct tasklet_struct txrx_compl_task; struct sk_buff_head tx_compl_q; struct sk_buff_head rx_compl_q; + struct sk_buff_head rx_in_ord_compl_q; /* rx_status template */ struct ieee80211_rx_status rx_status; @@ -1334,6 +1410,7 @@ int ath10k_htt_tx_alloc(struct ath10k_htt *htt); void ath10k_htt_tx_free(struct ath10k_htt *htt); int ath10k_htt_rx_alloc(struct ath10k_htt *htt); +int ath10k_htt_rx_ring_refill(struct ath10k *ar); void ath10k_htt_rx_free(struct ath10k_htt *htt); void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb); @@ -1346,7 +1423,7 @@ int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt, u8 max_subfrms_amsdu); void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt); -int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt); +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 *); int ath10k_htt_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 9c782a42665e..c1da44f65a4d 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -25,8 +25,8 @@ #include <linux/log2.h> -#define HTT_RX_RING_SIZE 1024 -#define HTT_RX_RING_FILL_LEVEL 1000 +#define HTT_RX_RING_SIZE HTT_RX_RING_SIZE_MAX +#define HTT_RX_RING_FILL_LEVEL (((HTT_RX_RING_SIZE) / 2) - 1) /* when under memory pressure rx ring refill may fail and needs a retry */ #define HTT_RX_RING_REFILL_RETRY_MS 50 @@ -34,31 +34,70 @@ static int ath10k_htt_rx_get_csum_state(struct sk_buff *skb); static void ath10k_htt_txrx_compl_task(unsigned long ptr); +static struct sk_buff * +ath10k_htt_rx_find_skb_paddr(struct ath10k *ar, u32 paddr) +{ + struct ath10k_skb_rxcb *rxcb; + + hash_for_each_possible(ar->htt.rx_ring.skb_table, rxcb, hlist, paddr) + if (rxcb->paddr == paddr) + return ATH10K_RXCB_SKB(rxcb); + + WARN_ON_ONCE(1); + return NULL; +} + static void ath10k_htt_rx_ring_free(struct ath10k_htt *htt) { struct sk_buff *skb; - struct ath10k_skb_cb *cb; + struct ath10k_skb_rxcb *rxcb; + struct hlist_node *n; int i; - for (i = 0; i < htt->rx_ring.fill_cnt; i++) { - skb = htt->rx_ring.netbufs_ring[i]; - cb = ATH10K_SKB_CB(skb); - dma_unmap_single(htt->ar->dev, cb->paddr, - skb->len + skb_tailroom(skb), - DMA_FROM_DEVICE); - dev_kfree_skb_any(skb); + if (htt->rx_ring.in_ord_rx) { + hash_for_each_safe(htt->rx_ring.skb_table, i, n, rxcb, hlist) { + skb = ATH10K_RXCB_SKB(rxcb); + dma_unmap_single(htt->ar->dev, rxcb->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + hash_del(&rxcb->hlist); + dev_kfree_skb_any(skb); + } + } else { + for (i = 0; i < htt->rx_ring.size; i++) { + skb = htt->rx_ring.netbufs_ring[i]; + if (!skb) + continue; + + rxcb = ATH10K_SKB_RXCB(skb); + dma_unmap_single(htt->ar->dev, rxcb->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + dev_kfree_skb_any(skb); + } } htt->rx_ring.fill_cnt = 0; + hash_init(htt->rx_ring.skb_table); + memset(htt->rx_ring.netbufs_ring, 0, + htt->rx_ring.size * sizeof(htt->rx_ring.netbufs_ring[0])); } static int __ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num) { struct htt_rx_desc *rx_desc; + struct ath10k_skb_rxcb *rxcb; struct sk_buff *skb; dma_addr_t paddr; int ret = 0, idx; + /* The Full Rx Reorder firmware has no way of telling the host + * implicitly when it copied HTT Rx Ring buffers to MAC Rx Ring. + * To keep things simple make sure ring is always half empty. This + * guarantees there'll be no replenishment overruns possible. + */ + BUILD_BUG_ON(HTT_RX_RING_FILL_LEVEL >= HTT_RX_RING_SIZE / 2); + idx = __le32_to_cpu(*htt->rx_ring.alloc_idx.vaddr); while (num > 0) { skb = dev_alloc_skb(HTT_RX_BUF_SIZE + HTT_RX_DESC_ALIGN); @@ -86,17 +125,29 @@ static int __ath10k_htt_rx_ring_fill_n(struct ath10k_htt *htt, int num) goto fail; } - ATH10K_SKB_CB(skb)->paddr = paddr; + rxcb = ATH10K_SKB_RXCB(skb); + rxcb->paddr = paddr; htt->rx_ring.netbufs_ring[idx] = skb; htt->rx_ring.paddrs_ring[idx] = __cpu_to_le32(paddr); htt->rx_ring.fill_cnt++; + if (htt->rx_ring.in_ord_rx) { + hash_add(htt->rx_ring.skb_table, + &ATH10K_SKB_RXCB(skb)->hlist, + (u32)paddr); + } + num--; idx++; idx &= htt->rx_ring.size_mask; } fail: + /* + * Make sure the rx buffer is updated before available buffer + * index to avoid any potential rx ring corruption. + */ + mb(); *htt->rx_ring.alloc_idx.vaddr = __cpu_to_le32(idx); return ret; } @@ -153,22 +204,20 @@ static void ath10k_htt_rx_ring_refill_retry(unsigned long arg) ath10k_htt_rx_msdu_buff_replenish(htt); } -static void ath10k_htt_rx_ring_clean_up(struct ath10k_htt *htt) +int ath10k_htt_rx_ring_refill(struct ath10k *ar) { - struct sk_buff *skb; - int i; + struct ath10k_htt *htt = &ar->htt; + int ret; - for (i = 0; i < htt->rx_ring.size; i++) { - skb = htt->rx_ring.netbufs_ring[i]; - if (!skb) - continue; + spin_lock_bh(&htt->rx_ring.lock); + ret = ath10k_htt_rx_ring_fill_n(htt, (htt->rx_ring.fill_level - + htt->rx_ring.fill_cnt)); + spin_unlock_bh(&htt->rx_ring.lock); - dma_unmap_single(htt->ar->dev, ATH10K_SKB_CB(skb)->paddr, - skb->len + skb_tailroom(skb), - DMA_FROM_DEVICE); - dev_kfree_skb_any(skb); - htt->rx_ring.netbufs_ring[i] = NULL; - } + if (ret) + ath10k_htt_rx_ring_free(htt); + + return ret; } void ath10k_htt_rx_free(struct ath10k_htt *htt) @@ -179,8 +228,9 @@ void ath10k_htt_rx_free(struct ath10k_htt *htt) skb_queue_purge(&htt->tx_compl_q); skb_queue_purge(&htt->rx_compl_q); + skb_queue_purge(&htt->rx_in_ord_compl_q); - ath10k_htt_rx_ring_clean_up(htt); + ath10k_htt_rx_ring_free(htt); dma_free_coherent(htt->ar->dev, (htt->rx_ring.size * @@ -212,6 +262,7 @@ static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt) idx = htt->rx_ring.sw_rd_idx.msdu_payld; msdu = htt->rx_ring.netbufs_ring[idx]; htt->rx_ring.netbufs_ring[idx] = NULL; + htt->rx_ring.paddrs_ring[idx] = 0; idx++; idx &= htt->rx_ring.size_mask; @@ -219,7 +270,7 @@ static inline struct sk_buff *ath10k_htt_rx_netbuf_pop(struct ath10k_htt *htt) htt->rx_ring.fill_cnt--; dma_unmap_single(htt->ar->dev, - ATH10K_SKB_CB(msdu)->paddr, + ATH10K_SKB_RXCB(msdu)->paddr, msdu->len + skb_tailroom(msdu), DMA_FROM_DEVICE); ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx netbuf pop: ", @@ -379,6 +430,82 @@ static void ath10k_htt_rx_replenish_task(unsigned long ptr) ath10k_htt_rx_msdu_buff_replenish(htt); } +static struct sk_buff *ath10k_htt_rx_pop_paddr(struct ath10k_htt *htt, + u32 paddr) +{ + struct ath10k *ar = htt->ar; + struct ath10k_skb_rxcb *rxcb; + struct sk_buff *msdu; + + lockdep_assert_held(&htt->rx_ring.lock); + + msdu = ath10k_htt_rx_find_skb_paddr(ar, paddr); + if (!msdu) + return NULL; + + rxcb = ATH10K_SKB_RXCB(msdu); + hash_del(&rxcb->hlist); + htt->rx_ring.fill_cnt--; + + dma_unmap_single(htt->ar->dev, rxcb->paddr, + msdu->len + skb_tailroom(msdu), + DMA_FROM_DEVICE); + ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt rx netbuf pop: ", + msdu->data, msdu->len + skb_tailroom(msdu)); + + return msdu; +} + +static int ath10k_htt_rx_pop_paddr_list(struct ath10k_htt *htt, + struct htt_rx_in_ord_ind *ev, + struct sk_buff_head *list) +{ + struct ath10k *ar = htt->ar; + struct htt_rx_in_ord_msdu_desc *msdu_desc = ev->msdu_descs; + struct htt_rx_desc *rxd; + struct sk_buff *msdu; + int msdu_count; + bool is_offload; + u32 paddr; + + lockdep_assert_held(&htt->rx_ring.lock); + + msdu_count = __le16_to_cpu(ev->msdu_count); + is_offload = !!(ev->info & HTT_RX_IN_ORD_IND_INFO_OFFLOAD_MASK); + + while (msdu_count--) { + paddr = __le32_to_cpu(msdu_desc->msdu_paddr); + + msdu = ath10k_htt_rx_pop_paddr(htt, paddr); + if (!msdu) { + __skb_queue_purge(list); + return -ENOENT; + } + + __skb_queue_tail(list, msdu); + + if (!is_offload) { + rxd = (void *)msdu->data; + + trace_ath10k_htt_rx_desc(ar, rxd, sizeof(*rxd)); + + skb_put(msdu, sizeof(*rxd)); + skb_pull(msdu, sizeof(*rxd)); + skb_put(msdu, __le16_to_cpu(msdu_desc->msdu_len)); + + if (!(__le32_to_cpu(rxd->attention.flags) & + RX_ATTENTION_FLAGS_MSDU_DONE)) { + ath10k_warn(htt->ar, "tried to pop an incomplete frame, oops!\n"); + return -EIO; + } + } + + msdu_desc++; + } + + return 0; +} + int ath10k_htt_rx_alloc(struct ath10k_htt *htt) { struct ath10k *ar = htt->ar; @@ -424,7 +551,7 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt) htt->rx_ring.alloc_idx.vaddr = vaddr; htt->rx_ring.alloc_idx.paddr = paddr; - htt->rx_ring.sw_rd_idx.msdu_payld = 0; + htt->rx_ring.sw_rd_idx.msdu_payld = htt->rx_ring.size_mask; *htt->rx_ring.alloc_idx.vaddr = 0; /* Initialize the Rx refill retry timer */ @@ -433,14 +560,15 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt) spin_lock_init(&htt->rx_ring.lock); htt->rx_ring.fill_cnt = 0; - if (__ath10k_htt_rx_ring_fill_n(htt, htt->rx_ring.fill_level)) - goto err_fill_ring; + htt->rx_ring.sw_rd_idx.msdu_payld = 0; + hash_init(htt->rx_ring.skb_table); tasklet_init(&htt->rx_replenish_task, ath10k_htt_rx_replenish_task, (unsigned long)htt); skb_queue_head_init(&htt->tx_compl_q); skb_queue_head_init(&htt->rx_compl_q); + skb_queue_head_init(&htt->rx_in_ord_compl_q); tasklet_init(&htt->txrx_compl_task, ath10k_htt_txrx_compl_task, (unsigned long)htt); @@ -449,12 +577,6 @@ int ath10k_htt_rx_alloc(struct ath10k_htt *htt) htt->rx_ring.size, htt->rx_ring.fill_level); return 0; -err_fill_ring: - ath10k_htt_rx_ring_free(htt); - dma_free_coherent(htt->ar->dev, - sizeof(*htt->rx_ring.alloc_idx.vaddr), - htt->rx_ring.alloc_idx.vaddr, - htt->rx_ring.alloc_idx.paddr); err_dma_idx: dma_free_coherent(htt->ar->dev, (htt->rx_ring.size * @@ -691,7 +813,7 @@ static void ath10k_htt_rx_h_mactime(struct ath10k *ar, * * FIXME: Can we get/compute 64bit TSF? */ - status->mactime = __le32_to_cpu(rxd->ppdu_end.tsf_timestamp); + status->mactime = __le32_to_cpu(rxd->ppdu_end.common.tsf_timestamp); status->flag |= RX_FLAG_MACTIME_END; } @@ -1578,6 +1700,194 @@ static void ath10k_htt_rx_delba(struct ath10k *ar, struct htt_resp *resp) spin_unlock_bh(&ar->data_lock); } +static int ath10k_htt_rx_extract_amsdu(struct sk_buff_head *list, + struct sk_buff_head *amsdu) +{ + struct sk_buff *msdu; + struct htt_rx_desc *rxd; + + if (skb_queue_empty(list)) + return -ENOBUFS; + + if (WARN_ON(!skb_queue_empty(amsdu))) + return -EINVAL; + + while ((msdu = __skb_dequeue(list))) { + __skb_queue_tail(amsdu, msdu); + + rxd = (void *)msdu->data - sizeof(*rxd); + if (rxd->msdu_end.info0 & + __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU)) + break; + } + + msdu = skb_peek_tail(amsdu); + rxd = (void *)msdu->data - sizeof(*rxd); + if (!(rxd->msdu_end.info0 & + __cpu_to_le32(RX_MSDU_END_INFO0_LAST_MSDU))) { + skb_queue_splice_init(amsdu, list); + return -EAGAIN; + } + + return 0; +} + +static void ath10k_htt_rx_h_rx_offload_prot(struct ieee80211_rx_status *status, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + + if (!ieee80211_has_protected(hdr->frame_control)) + return; + + /* Offloaded frames are already decrypted but firmware insists they are + * protected in the 802.11 header. Strip the flag. Otherwise mac80211 + * will drop the frame. + */ + + hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED); + status->flag |= RX_FLAG_DECRYPTED | + RX_FLAG_IV_STRIPPED | + RX_FLAG_MMIC_STRIPPED; +} + +static void ath10k_htt_rx_h_rx_offload(struct ath10k *ar, + struct sk_buff_head *list) +{ + struct ath10k_htt *htt = &ar->htt; + struct ieee80211_rx_status *status = &htt->rx_status; + struct htt_rx_offload_msdu *rx; + struct sk_buff *msdu; + size_t offset; + + while ((msdu = __skb_dequeue(list))) { + /* Offloaded frames don't have Rx descriptor. Instead they have + * a short meta information header. + */ + + rx = (void *)msdu->data; + + skb_put(msdu, sizeof(*rx)); + skb_pull(msdu, sizeof(*rx)); + + if (skb_tailroom(msdu) < __le16_to_cpu(rx->msdu_len)) { + ath10k_warn(ar, "dropping frame: offloaded rx msdu is too long!\n"); + dev_kfree_skb_any(msdu); + continue; + } + + skb_put(msdu, __le16_to_cpu(rx->msdu_len)); + + /* Offloaded rx header length isn't multiple of 2 nor 4 so the + * actual payload is unaligned. Align the frame. Otherwise + * mac80211 complains. This shouldn't reduce performance much + * because these offloaded frames are rare. + */ + offset = 4 - ((unsigned long)msdu->data & 3); + skb_put(msdu, offset); + memmove(msdu->data + offset, msdu->data, msdu->len); + skb_pull(msdu, offset); + + /* FIXME: The frame is NWifi. Re-construct QoS Control + * if possible later. + */ + + memset(status, 0, sizeof(*status)); + status->flag |= RX_FLAG_NO_SIGNAL_VAL; + + ath10k_htt_rx_h_rx_offload_prot(status, msdu); + ath10k_htt_rx_h_channel(ar, status); + ath10k_process_rx(ar, status, msdu); + } +} + +static void ath10k_htt_rx_in_ord_ind(struct ath10k *ar, struct sk_buff *skb) +{ + struct ath10k_htt *htt = &ar->htt; + struct htt_resp *resp = (void *)skb->data; + struct ieee80211_rx_status *status = &htt->rx_status; + struct sk_buff_head list; + struct sk_buff_head amsdu; + u16 peer_id; + u16 msdu_count; + u8 vdev_id; + u8 tid; + bool offload; + bool frag; + int ret; + + lockdep_assert_held(&htt->rx_ring.lock); + + if (htt->rx_confused) + return; + + skb_pull(skb, sizeof(resp->hdr)); + skb_pull(skb, sizeof(resp->rx_in_ord_ind)); + + peer_id = __le16_to_cpu(resp->rx_in_ord_ind.peer_id); + msdu_count = __le16_to_cpu(resp->rx_in_ord_ind.msdu_count); + vdev_id = resp->rx_in_ord_ind.vdev_id; + tid = SM(resp->rx_in_ord_ind.info, HTT_RX_IN_ORD_IND_INFO_TID); + offload = !!(resp->rx_in_ord_ind.info & + HTT_RX_IN_ORD_IND_INFO_OFFLOAD_MASK); + frag = !!(resp->rx_in_ord_ind.info & HTT_RX_IN_ORD_IND_INFO_FRAG_MASK); + + ath10k_dbg(ar, ATH10K_DBG_HTT, + "htt rx in ord vdev %i peer %i tid %i offload %i frag %i msdu count %i\n", + vdev_id, peer_id, tid, offload, frag, msdu_count); + + if (skb->len < msdu_count * sizeof(*resp->rx_in_ord_ind.msdu_descs)) { + ath10k_warn(ar, "dropping invalid in order rx indication\n"); + return; + } + + /* The event can deliver more than 1 A-MSDU. Each A-MSDU is later + * extracted and processed. + */ + __skb_queue_head_init(&list); + ret = ath10k_htt_rx_pop_paddr_list(htt, &resp->rx_in_ord_ind, &list); + if (ret < 0) { + ath10k_warn(ar, "failed to pop paddr list: %d\n", ret); + htt->rx_confused = true; + return; + } + + /* Offloaded frames are very different and need to be handled + * separately. + */ + if (offload) + ath10k_htt_rx_h_rx_offload(ar, &list); + + while (!skb_queue_empty(&list)) { + __skb_queue_head_init(&amsdu); + ret = ath10k_htt_rx_extract_amsdu(&list, &amsdu); + switch (ret) { + case 0: + /* Note: The in-order indication may report interleaved + * frames from different PPDUs meaning reported rx rate + * to mac80211 isn't accurate/reliable. It's still + * better to report something than nothing though. This + * should still give an idea about rx rate to the user. + */ + ath10k_htt_rx_h_ppdu(ar, &amsdu, status); + ath10k_htt_rx_h_filter(ar, &amsdu, status); + ath10k_htt_rx_h_mpdu(ar, &amsdu, status); + ath10k_htt_rx_h_deliver(ar, &amsdu, status); + break; + case -EAGAIN: + /* fall through */ + default: + /* Should not happen. */ + ath10k_warn(ar, "failed to extract amsdu: %d\n", ret); + htt->rx_confused = true; + __skb_queue_purge(&list); + return; + } + } + + tasklet_schedule(&htt->rx_replenish_task); +} + void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) { struct ath10k_htt *htt = &ar->htt; @@ -1700,6 +2010,20 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) */ break; } + case HTT_T2H_MSG_TYPE_RX_IN_ORD_PADDR_IND: { + spin_lock_bh(&htt->rx_ring.lock); + __skb_queue_tail(&htt->rx_in_ord_compl_q, skb); + spin_unlock_bh(&htt->rx_ring.lock); + tasklet_schedule(&htt->txrx_compl_task); + return; + } + case HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND: + /* FIXME: This WMI-TLV event is overlapping with 10.2 + * CHAN_CHANGE - both being 0xF. Neither is being used in + * practice so no immediate action is necessary. Nevertheless + * HTT may need an abstraction layer like WMI has one day. + */ + break; default: ath10k_warn(ar, "htt event (%d) not handled\n", resp->hdr.msg_type); @@ -1715,6 +2039,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb) static void ath10k_htt_txrx_compl_task(unsigned long ptr) { struct ath10k_htt *htt = (struct ath10k_htt *)ptr; + struct ath10k *ar = htt->ar; struct htt_resp *resp; struct sk_buff *skb; @@ -1731,5 +2056,10 @@ static void ath10k_htt_txrx_compl_task(unsigned long ptr) ath10k_htt_rx_handler(htt, &resp->rx_ind); dev_kfree_skb_any(skb); } + + while ((skb = __skb_dequeue(&htt->rx_in_ord_compl_q))) { + ath10k_htt_rx_in_ord_ind(ar, skb); + dev_kfree_skb_any(skb); + } spin_unlock_bh(&htt->rx_ring.lock); } diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 4bc51d8a14a3..cbd2bc9e6202 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -56,21 +56,18 @@ exit: return ret; } -int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt) +int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt, struct sk_buff *skb) { struct ath10k *ar = htt->ar; - int msdu_id; + int ret; lockdep_assert_held(&htt->tx_lock); - msdu_id = find_first_zero_bit(htt->used_msdu_ids, - htt->max_num_pending_tx); - if (msdu_id == htt->max_num_pending_tx) - return -ENOBUFS; + ret = idr_alloc(&htt->pending_tx, skb, 0, 0x10000, GFP_ATOMIC); + + ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx alloc msdu_id %d\n", ret); - ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx alloc msdu_id %d\n", msdu_id); - __set_bit(msdu_id, htt->used_msdu_ids); - return msdu_id; + return ret; } void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id) @@ -79,79 +76,53 @@ void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id) lockdep_assert_held(&htt->tx_lock); - if (!test_bit(msdu_id, htt->used_msdu_ids)) - ath10k_warn(ar, "trying to free unallocated msdu_id %d\n", - msdu_id); - ath10k_dbg(ar, ATH10K_DBG_HTT, "htt tx free msdu_id %hu\n", msdu_id); - __clear_bit(msdu_id, htt->used_msdu_ids); + + idr_remove(&htt->pending_tx, msdu_id); } int ath10k_htt_tx_alloc(struct ath10k_htt *htt) { struct ath10k *ar = htt->ar; - spin_lock_init(&htt->tx_lock); - - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, htt->ar->fw_features)) - htt->max_num_pending_tx = TARGET_10X_NUM_MSDU_DESC; - else - htt->max_num_pending_tx = TARGET_NUM_MSDU_DESC; - ath10k_dbg(ar, ATH10K_DBG_BOOT, "htt tx max num pending tx %d\n", htt->max_num_pending_tx); - htt->pending_tx = kzalloc(sizeof(*htt->pending_tx) * - htt->max_num_pending_tx, GFP_KERNEL); - if (!htt->pending_tx) - return -ENOMEM; - - htt->used_msdu_ids = kzalloc(sizeof(unsigned long) * - BITS_TO_LONGS(htt->max_num_pending_tx), - GFP_KERNEL); - if (!htt->used_msdu_ids) { - kfree(htt->pending_tx); - return -ENOMEM; - } + spin_lock_init(&htt->tx_lock); + idr_init(&htt->pending_tx); htt->tx_pool = dma_pool_create("ath10k htt tx pool", htt->ar->dev, sizeof(struct ath10k_htt_txbuf), 4, 0); if (!htt->tx_pool) { - kfree(htt->used_msdu_ids); - kfree(htt->pending_tx); + idr_destroy(&htt->pending_tx); return -ENOMEM; } return 0; } -static void ath10k_htt_tx_free_pending(struct ath10k_htt *htt) +static int ath10k_htt_tx_clean_up_pending(int msdu_id, void *skb, void *ctx) { - struct ath10k *ar = htt->ar; + struct ath10k *ar = ctx; + struct ath10k_htt *htt = &ar->htt; struct htt_tx_done tx_done = {0}; - int msdu_id; - - spin_lock_bh(&htt->tx_lock); - for (msdu_id = 0; msdu_id < htt->max_num_pending_tx; msdu_id++) { - if (!test_bit(msdu_id, htt->used_msdu_ids)) - continue; - ath10k_dbg(ar, ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n", - msdu_id); + ath10k_dbg(ar, ATH10K_DBG_HTT, "force cleanup msdu_id %hu\n", msdu_id); - tx_done.discard = 1; - tx_done.msdu_id = msdu_id; + tx_done.discard = 1; + tx_done.msdu_id = msdu_id; - ath10k_txrx_tx_unref(htt, &tx_done); - } + spin_lock_bh(&htt->tx_lock); + ath10k_txrx_tx_unref(htt, &tx_done); spin_unlock_bh(&htt->tx_lock); + + return 0; } void ath10k_htt_tx_free(struct ath10k_htt *htt) { - ath10k_htt_tx_free_pending(htt); - kfree(htt->pending_tx); - kfree(htt->used_msdu_ids); + idr_for_each(&htt->pending_tx, ath10k_htt_tx_clean_up_pending, htt->ar); + idr_destroy(&htt->pending_tx); dma_pool_destroy(htt->tx_pool); } @@ -383,13 +354,12 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) len += sizeof(cmd->mgmt_tx); spin_lock_bh(&htt->tx_lock); - res = ath10k_htt_tx_alloc_msdu_id(htt); + res = ath10k_htt_tx_alloc_msdu_id(htt, msdu); if (res < 0) { spin_unlock_bh(&htt->tx_lock); goto err_tx_dec; } msdu_id = res; - htt->pending_tx[msdu_id] = msdu; spin_unlock_bh(&htt->tx_lock); txdesc = ath10k_htc_alloc_skb(ar, len); @@ -428,7 +398,6 @@ err_free_txdesc: dev_kfree_skb_any(txdesc); err_free_msdu_id: spin_lock_bh(&htt->tx_lock); - htt->pending_tx[msdu_id] = NULL; ath10k_htt_tx_free_msdu_id(htt, msdu_id); spin_unlock_bh(&htt->tx_lock); err_tx_dec: @@ -460,13 +429,12 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) goto err; spin_lock_bh(&htt->tx_lock); - res = ath10k_htt_tx_alloc_msdu_id(htt); + res = ath10k_htt_tx_alloc_msdu_id(htt, msdu); if (res < 0) { spin_unlock_bh(&htt->tx_lock); goto err_tx_dec; } msdu_id = res; - htt->pending_tx[msdu_id] = msdu; spin_unlock_bh(&htt->tx_lock); prefetch_len = min(htt->prefetch_len, msdu->len); @@ -480,10 +448,18 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) skb_cb->htt.txbuf = dma_pool_alloc(htt->tx_pool, GFP_ATOMIC, &paddr); - if (!skb_cb->htt.txbuf) + if (!skb_cb->htt.txbuf) { + res = -ENOMEM; goto err_free_msdu_id; + } skb_cb->htt.txbuf_paddr = paddr; + if ((ieee80211_is_action(hdr->frame_control) || + ieee80211_is_deauth(hdr->frame_control) || + ieee80211_is_disassoc(hdr->frame_control)) && + ieee80211_has_protected(hdr->frame_control)) + skb_put(msdu, IEEE80211_CCMP_MIC_LEN); + skb_cb->paddr = dma_map_single(dev, msdu->data, msdu->len, DMA_TO_DEVICE); res = dma_mapping_error(dev, skb_cb->paddr); @@ -539,8 +515,10 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) flags1 |= SM((u16)vdev_id, HTT_DATA_TX_DESC_FLAGS1_VDEV_ID); flags1 |= SM((u16)tid, HTT_DATA_TX_DESC_FLAGS1_EXT_TID); - flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD; - flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD; + if (msdu->ip_summed == CHECKSUM_PARTIAL) { + flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD; + flags1 |= HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD; + } /* Prevent firmware from sending up tx inspection requests. There's * nothing ath10k can do with frames requested for inspection so force @@ -598,7 +576,6 @@ err_free_txbuf: skb_cb->htt.txbuf_paddr); err_free_msdu_id: spin_lock_bh(&htt->tx_lock); - htt->pending_tx[msdu_id] = NULL; ath10k_htt_tx_free_msdu_id(htt, msdu_id); spin_unlock_bh(&htt->tx_lock); err_tx_dec: diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c new file mode 100644 index 000000000000..839a8791fb9e --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/hw.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014-2015 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/types.h> +#include "hw.h" + +const struct ath10k_hw_regs qca988x_regs = { + .rtc_state_cold_reset_mask = 0x00000400, + .rtc_soc_base_address = 0x00004000, + .rtc_wmac_base_address = 0x00005000, + .soc_core_base_address = 0x00009000, + .ce_wrapper_base_address = 0x00057000, + .ce0_base_address = 0x00057400, + .ce1_base_address = 0x00057800, + .ce2_base_address = 0x00057c00, + .ce3_base_address = 0x00058000, + .ce4_base_address = 0x00058400, + .ce5_base_address = 0x00058800, + .ce6_base_address = 0x00058c00, + .ce7_base_address = 0x00059000, + .soc_reset_control_si0_rst_mask = 0x00000001, + .soc_reset_control_ce_rst_mask = 0x00040000, + .soc_chip_id_address = 0x00ec, + .scratch_3_address = 0x0030, +}; + +const struct ath10k_hw_regs qca6174_regs = { + .rtc_state_cold_reset_mask = 0x00002000, + .rtc_soc_base_address = 0x00000800, + .rtc_wmac_base_address = 0x00001000, + .soc_core_base_address = 0x0003a000, + .ce_wrapper_base_address = 0x00034000, + .ce0_base_address = 0x00034400, + .ce1_base_address = 0x00034800, + .ce2_base_address = 0x00034c00, + .ce3_base_address = 0x00035000, + .ce4_base_address = 0x00035400, + .ce5_base_address = 0x00035800, + .ce6_base_address = 0x00035c00, + .ce7_base_address = 0x00036000, + .soc_reset_control_si0_rst_mask = 0x00000000, + .soc_reset_control_ce_rst_mask = 0x00000001, + .soc_chip_id_address = 0x000f0, + .scratch_3_address = 0x0028, +}; diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index dfedfd0e0f34..460771fcfe9e 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -34,9 +34,50 @@ #define QCA988X_HW_2_0_BOARD_DATA_FILE "board.bin" #define QCA988X_HW_2_0_PATCH_LOAD_ADDR 0x1234 +/* QCA6174 target BMI version signatures */ +#define QCA6174_HW_1_0_VERSION 0x05000000 +#define QCA6174_HW_1_1_VERSION 0x05000001 +#define QCA6174_HW_1_3_VERSION 0x05000003 +#define QCA6174_HW_2_1_VERSION 0x05010000 +#define QCA6174_HW_3_0_VERSION 0x05020000 +#define QCA6174_HW_3_2_VERSION 0x05030000 + +enum qca6174_pci_rev { + QCA6174_PCI_REV_1_1 = 0x11, + QCA6174_PCI_REV_1_3 = 0x13, + QCA6174_PCI_REV_2_0 = 0x20, + QCA6174_PCI_REV_3_0 = 0x30, +}; + +enum qca6174_chip_id_rev { + QCA6174_HW_1_0_CHIP_ID_REV = 0, + QCA6174_HW_1_1_CHIP_ID_REV = 1, + QCA6174_HW_1_3_CHIP_ID_REV = 2, + QCA6174_HW_2_1_CHIP_ID_REV = 4, + QCA6174_HW_2_2_CHIP_ID_REV = 5, + QCA6174_HW_3_0_CHIP_ID_REV = 8, + QCA6174_HW_3_1_CHIP_ID_REV = 9, + QCA6174_HW_3_2_CHIP_ID_REV = 10, +}; + +#define QCA6174_HW_2_1_FW_DIR "ath10k/QCA6174/hw2.1" +#define QCA6174_HW_2_1_FW_FILE "firmware.bin" +#define QCA6174_HW_2_1_OTP_FILE "otp.bin" +#define QCA6174_HW_2_1_BOARD_DATA_FILE "board.bin" +#define QCA6174_HW_2_1_PATCH_LOAD_ADDR 0x1234 + +#define QCA6174_HW_3_0_FW_DIR "ath10k/QCA6174/hw3.0" +#define QCA6174_HW_3_0_FW_FILE "firmware.bin" +#define QCA6174_HW_3_0_OTP_FILE "otp.bin" +#define QCA6174_HW_3_0_BOARD_DATA_FILE "board.bin" +#define QCA6174_HW_3_0_PATCH_LOAD_ADDR 0x1234 + #define ATH10K_FW_API2_FILE "firmware-2.bin" #define ATH10K_FW_API3_FILE "firmware-3.bin" +/* added support for ATH10K_FW_IE_WMI_OP_VERSION */ +#define ATH10K_FW_API4_FILE "firmware-4.bin" + #define ATH10K_FW_UTF_FILE "utf.bin" /* includes also the null byte */ @@ -58,8 +99,57 @@ enum ath10k_fw_ie_type { ATH10K_FW_IE_FEATURES = 2, ATH10K_FW_IE_FW_IMAGE = 3, ATH10K_FW_IE_OTP_IMAGE = 4, + + /* WMI "operations" interface version, 32 bit value. Supported from + * FW API 4 and above. + */ + ATH10K_FW_IE_WMI_OP_VERSION = 5, +}; + +enum ath10k_fw_wmi_op_version { + ATH10K_FW_WMI_OP_VERSION_UNSET = 0, + + ATH10K_FW_WMI_OP_VERSION_MAIN = 1, + ATH10K_FW_WMI_OP_VERSION_10_1 = 2, + ATH10K_FW_WMI_OP_VERSION_10_2 = 3, + ATH10K_FW_WMI_OP_VERSION_TLV = 4, + ATH10K_FW_WMI_OP_VERSION_10_2_4 = 5, + + /* keep last */ + ATH10K_FW_WMI_OP_VERSION_MAX, +}; + +enum ath10k_hw_rev { + ATH10K_HW_QCA988X, + ATH10K_HW_QCA6174, +}; + +struct ath10k_hw_regs { + u32 rtc_state_cold_reset_mask; + u32 rtc_soc_base_address; + u32 rtc_wmac_base_address; + u32 soc_core_base_address; + u32 ce_wrapper_base_address; + u32 ce0_base_address; + u32 ce1_base_address; + u32 ce2_base_address; + u32 ce3_base_address; + u32 ce4_base_address; + u32 ce5_base_address; + u32 ce6_base_address; + u32 ce7_base_address; + u32 soc_reset_control_si0_rst_mask; + u32 soc_reset_control_ce_rst_mask; + u32 soc_chip_id_address; + u32 scratch_3_address; }; +extern const struct ath10k_hw_regs qca988x_regs; +extern const struct ath10k_hw_regs qca6174_regs; + +#define QCA_REV_988X(ar) ((ar)->hw_rev == ATH10K_HW_QCA988X) +#define QCA_REV_6174(ar) ((ar)->hw_rev == ATH10K_HW_QCA6174) + /* Known pecularities: * - current FW doesn't support raw rx mode (last tested v599) * - current FW dumps upon raw tx mode (last tested v599) @@ -162,6 +252,18 @@ struct ath10k_pktlog_hdr { #define TARGET_10X_NUM_MSDU_DESC (1024 + 400) #define TARGET_10X_MAX_FRAG_ENTRIES 0 +/* 10.2 parameters */ +#define TARGET_10_2_DMA_BURST_SIZE 1 + +/* Target specific defines for WMI-TLV firmware */ +#define TARGET_TLV_NUM_VDEVS 3 +#define TARGET_TLV_NUM_STATIONS 32 +#define TARGET_TLV_NUM_PEERS ((TARGET_TLV_NUM_STATIONS) + \ + (TARGET_TLV_NUM_VDEVS) + \ + 2) +#define TARGET_TLV_NUM_TIDS ((TARGET_TLV_NUM_PEERS) * 2) +#define TARGET_TLV_NUM_MSDU_DESC (1024 + 32) + /* Number of Copy Engines supported */ #define CE_COUNT 8 @@ -192,7 +294,7 @@ struct ath10k_pktlog_hdr { /* as of IP3.7.1 */ #define RTC_STATE_V_ON 3 -#define RTC_STATE_COLD_RESET_MASK 0x00000400 +#define RTC_STATE_COLD_RESET_MASK ar->regs->rtc_state_cold_reset_mask #define RTC_STATE_V_LSB 0 #define RTC_STATE_V_MASK 0x00000007 #define RTC_STATE_ADDRESS 0x0000 @@ -201,12 +303,12 @@ struct ath10k_pktlog_hdr { #define PCIE_SOC_WAKE_RESET 0x00000000 #define SOC_GLOBAL_RESET_ADDRESS 0x0008 -#define RTC_SOC_BASE_ADDRESS 0x00004000 -#define RTC_WMAC_BASE_ADDRESS 0x00005000 +#define RTC_SOC_BASE_ADDRESS ar->regs->rtc_soc_base_address +#define RTC_WMAC_BASE_ADDRESS ar->regs->rtc_wmac_base_address #define MAC_COEX_BASE_ADDRESS 0x00006000 #define BT_COEX_BASE_ADDRESS 0x00007000 #define SOC_PCIE_BASE_ADDRESS 0x00008000 -#define SOC_CORE_BASE_ADDRESS 0x00009000 +#define SOC_CORE_BASE_ADDRESS ar->regs->soc_core_base_address #define WLAN_UART_BASE_ADDRESS 0x0000c000 #define WLAN_SI_BASE_ADDRESS 0x00010000 #define WLAN_GPIO_BASE_ADDRESS 0x00014000 @@ -215,23 +317,23 @@ struct ath10k_pktlog_hdr { #define EFUSE_BASE_ADDRESS 0x00030000 #define FPGA_REG_BASE_ADDRESS 0x00039000 #define WLAN_UART2_BASE_ADDRESS 0x00054c00 -#define CE_WRAPPER_BASE_ADDRESS 0x00057000 -#define CE0_BASE_ADDRESS 0x00057400 -#define CE1_BASE_ADDRESS 0x00057800 -#define CE2_BASE_ADDRESS 0x00057c00 -#define CE3_BASE_ADDRESS 0x00058000 -#define CE4_BASE_ADDRESS 0x00058400 -#define CE5_BASE_ADDRESS 0x00058800 -#define CE6_BASE_ADDRESS 0x00058c00 -#define CE7_BASE_ADDRESS 0x00059000 +#define CE_WRAPPER_BASE_ADDRESS ar->regs->ce_wrapper_base_address +#define CE0_BASE_ADDRESS ar->regs->ce0_base_address +#define CE1_BASE_ADDRESS ar->regs->ce1_base_address +#define CE2_BASE_ADDRESS ar->regs->ce2_base_address +#define CE3_BASE_ADDRESS ar->regs->ce3_base_address +#define CE4_BASE_ADDRESS ar->regs->ce4_base_address +#define CE5_BASE_ADDRESS ar->regs->ce5_base_address +#define CE6_BASE_ADDRESS ar->regs->ce6_base_address +#define CE7_BASE_ADDRESS ar->regs->ce7_base_address #define DBI_BASE_ADDRESS 0x00060000 #define WLAN_ANALOG_INTF_PCIE_BASE_ADDRESS 0x0006c000 #define PCIE_LOCAL_BASE_ADDRESS 0x00080000 #define SOC_RESET_CONTROL_ADDRESS 0x00000000 #define SOC_RESET_CONTROL_OFFSET 0x00000000 -#define SOC_RESET_CONTROL_SI0_RST_MASK 0x00000001 -#define SOC_RESET_CONTROL_CE_RST_MASK 0x00040000 +#define SOC_RESET_CONTROL_SI0_RST_MASK ar->regs->soc_reset_control_si0_rst_mask +#define SOC_RESET_CONTROL_CE_RST_MASK ar->regs->soc_reset_control_ce_rst_mask #define SOC_RESET_CONTROL_CPU_WARM_RST_MASK 0x00000040 #define SOC_CPU_CLOCK_OFFSET 0x00000020 #define SOC_CPU_CLOCK_STANDARD_LSB 0 @@ -245,7 +347,7 @@ struct ath10k_pktlog_hdr { #define SOC_LF_TIMER_CONTROL0_ADDRESS 0x00000050 #define SOC_LF_TIMER_CONTROL0_ENABLE_MASK 0x00000004 -#define SOC_CHIP_ID_ADDRESS 0x000000ec +#define SOC_CHIP_ID_ADDRESS ar->regs->soc_chip_id_address #define SOC_CHIP_ID_REV_LSB 8 #define SOC_CHIP_ID_REV_MASK 0x00000f00 @@ -301,7 +403,7 @@ struct ath10k_pktlog_hdr { #define PCIE_INTR_ENABLE_ADDRESS 0x0008 #define PCIE_INTR_CAUSE_ADDRESS 0x000c #define PCIE_INTR_CLR_ADDRESS 0x0014 -#define SCRATCH_3_ADDRESS 0x0030 +#define SCRATCH_3_ADDRESS ar->regs->scratch_3_address #define CPU_INTR_ADDRESS 0x0010 /* Firmware indications to the Host via SCRATCH_3 register. */ diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index c4005670cba2..d6d2f0f00caa 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -27,6 +27,8 @@ #include "htt.h" #include "txrx.h" #include "testmode.h" +#include "wmi.h" +#include "wmi-ops.h" /**********/ /* Crypto */ @@ -35,7 +37,7 @@ static int ath10k_send_key(struct ath10k_vif *arvif, struct ieee80211_key_conf *key, enum set_key_cmd cmd, - const u8 *macaddr) + const u8 *macaddr, bool def_idx) { struct ath10k *ar = arvif->ar; struct wmi_vdev_install_key_arg arg = { @@ -56,10 +58,7 @@ static int ath10k_send_key(struct ath10k_vif *arvif, switch (key->cipher) { case WLAN_CIPHER_SUITE_CCMP: arg.key_cipher = WMI_CIPHER_AES_CCM; - if (arvif->vdev_type == WMI_VDEV_TYPE_AP) - key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; - else - key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX; + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; break; case WLAN_CIPHER_SUITE_TKIP: arg.key_cipher = WMI_CIPHER_TKIP; @@ -73,7 +72,13 @@ static int ath10k_send_key(struct ath10k_vif *arvif, * Otherwise pairwise key must be set */ if (memcmp(macaddr, arvif->vif->addr, ETH_ALEN)) arg.key_flags = WMI_KEY_PAIRWISE; + + if (def_idx) + arg.key_flags |= WMI_KEY_TX_USAGE; break; + case WLAN_CIPHER_SUITE_AES_CMAC: + /* this one needs to be done in software */ + return 1; default: ath10k_warn(ar, "cipher %d is not supported\n", key->cipher); return -EOPNOTSUPP; @@ -90,7 +95,7 @@ static int ath10k_send_key(struct ath10k_vif *arvif, static int ath10k_install_key(struct ath10k_vif *arvif, struct ieee80211_key_conf *key, enum set_key_cmd cmd, - const u8 *macaddr) + const u8 *macaddr, bool def_idx) { struct ath10k *ar = arvif->ar; int ret; @@ -99,7 +104,7 @@ static int ath10k_install_key(struct ath10k_vif *arvif, reinit_completion(&ar->install_key_done); - ret = ath10k_send_key(arvif, key, cmd, macaddr); + ret = ath10k_send_key(arvif, key, cmd, macaddr, def_idx); if (ret) return ret; @@ -117,6 +122,7 @@ static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif, struct ath10k_peer *peer; int ret; int i; + bool def_idx; lockdep_assert_held(&ar->conf_mutex); @@ -130,9 +136,14 @@ static int ath10k_install_peer_wep_keys(struct ath10k_vif *arvif, for (i = 0; i < ARRAY_SIZE(arvif->wep_keys); i++) { if (arvif->wep_keys[i] == NULL) continue; + /* set TX_USAGE flag for default key id */ + if (arvif->def_wep_key_idx == i) + def_idx = true; + else + def_idx = false; ret = ath10k_install_key(arvif, arvif->wep_keys[i], SET_KEY, - addr); + addr, def_idx); if (ret) return ret; @@ -166,8 +177,9 @@ static int ath10k_clear_peer_keys(struct ath10k_vif *arvif, if (peer->keys[i] == NULL) continue; + /* key flags are not required to delete the key */ ret = ath10k_install_key(arvif, peer->keys[i], - DISABLE_KEY, addr); + DISABLE_KEY, addr, false); if (ret && first_errno == 0) first_errno = ret; @@ -241,8 +253,8 @@ static int ath10k_clear_vdev_key(struct ath10k_vif *arvif, if (i == ARRAY_SIZE(peer->keys)) break; - - ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr); + /* key flags are not required to delete the key */ + ret = ath10k_install_key(arvif, key, DISABLE_KEY, addr, false); if (ret && first_errno == 0) first_errno = ret; @@ -267,7 +279,10 @@ chan_to_phymode(const struct cfg80211_chan_def *chandef) case IEEE80211_BAND_2GHZ: switch (chandef->width) { case NL80211_CHAN_WIDTH_20_NOHT: - phymode = MODE_11G; + if (chandef->chan->flags & IEEE80211_CHAN_NO_OFDM) + phymode = MODE_11B; + else + phymode = MODE_11G; break; case NL80211_CHAN_WIDTH_20: phymode = MODE_11NG_HT20; @@ -519,10 +534,14 @@ void ath10k_mac_vif_beacon_free(struct ath10k_vif *arvif) dma_unmap_single(ar->dev, ATH10K_SKB_CB(arvif->beacon)->paddr, arvif->beacon->len, DMA_TO_DEVICE); + if (WARN_ON(arvif->beacon_state != ATH10K_BEACON_SCHEDULED && + arvif->beacon_state != ATH10K_BEACON_SENT)) + return; + dev_kfree_skb_any(arvif->beacon); arvif->beacon = NULL; - arvif->beacon_sent = false; + arvif->beacon_state = ATH10K_BEACON_SCHEDULED; } static void ath10k_mac_vif_beacon_cleanup(struct ath10k_vif *arvif) @@ -962,6 +981,143 @@ static int ath10k_vdev_stop(struct ath10k_vif *arvif) return ret; } +static int ath10k_mac_setup_bcn_p2p_ie(struct ath10k_vif *arvif, + struct sk_buff *bcn) +{ + struct ath10k *ar = arvif->ar; + struct ieee80211_mgmt *mgmt; + const u8 *p2p_ie; + int ret; + + if (arvif->vdev_type != WMI_VDEV_TYPE_AP) + return 0; + + if (arvif->vdev_subtype != WMI_VDEV_SUBTYPE_P2P_GO) + return 0; + + mgmt = (void *)bcn->data; + p2p_ie = cfg80211_find_vendor_ie(WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P, + mgmt->u.beacon.variable, + bcn->len - (mgmt->u.beacon.variable - + bcn->data)); + if (!p2p_ie) + return -ENOENT; + + ret = ath10k_wmi_p2p_go_bcn_ie(ar, arvif->vdev_id, p2p_ie); + if (ret) { + ath10k_warn(ar, "failed to submit p2p go bcn ie for vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + return 0; +} + +static int ath10k_mac_remove_vendor_ie(struct sk_buff *skb, unsigned int oui, + u8 oui_type, size_t ie_offset) +{ + size_t len; + const u8 *next; + const u8 *end; + u8 *ie; + + if (WARN_ON(skb->len < ie_offset)) + return -EINVAL; + + ie = (u8 *)cfg80211_find_vendor_ie(oui, oui_type, + skb->data + ie_offset, + skb->len - ie_offset); + if (!ie) + return -ENOENT; + + len = ie[1] + 2; + end = skb->data + skb->len; + next = ie + len; + + if (WARN_ON(next > end)) + return -EINVAL; + + memmove(ie, next, end - next); + skb_trim(skb, skb->len - len); + + return 0; +} + +static int ath10k_mac_setup_bcn_tmpl(struct ath10k_vif *arvif) +{ + struct ath10k *ar = arvif->ar; + struct ieee80211_hw *hw = ar->hw; + struct ieee80211_vif *vif = arvif->vif; + struct ieee80211_mutable_offsets offs = {}; + struct sk_buff *bcn; + int ret; + + if (!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map)) + return 0; + + bcn = ieee80211_beacon_get_template(hw, vif, &offs); + if (!bcn) { + ath10k_warn(ar, "failed to get beacon template from mac80211\n"); + return -EPERM; + } + + ret = ath10k_mac_setup_bcn_p2p_ie(arvif, bcn); + if (ret) { + ath10k_warn(ar, "failed to setup p2p go bcn ie: %d\n", ret); + kfree_skb(bcn); + return ret; + } + + /* P2P IE is inserted by firmware automatically (as configured above) + * so remove it from the base beacon template to avoid duplicate P2P + * IEs in beacon frames. + */ + ath10k_mac_remove_vendor_ie(bcn, WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P, + offsetof(struct ieee80211_mgmt, + u.beacon.variable)); + + ret = ath10k_wmi_bcn_tmpl(ar, arvif->vdev_id, offs.tim_offset, bcn, 0, + 0, NULL, 0); + kfree_skb(bcn); + + if (ret) { + ath10k_warn(ar, "failed to submit beacon template command: %d\n", + ret); + return ret; + } + + return 0; +} + +static int ath10k_mac_setup_prb_tmpl(struct ath10k_vif *arvif) +{ + struct ath10k *ar = arvif->ar; + struct ieee80211_hw *hw = ar->hw; + struct ieee80211_vif *vif = arvif->vif; + struct sk_buff *prb; + int ret; + + if (!test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map)) + return 0; + + prb = ieee80211_proberesp_get(hw, vif); + if (!prb) { + ath10k_warn(ar, "failed to get probe resp template from mac80211\n"); + return -EPERM; + } + + ret = ath10k_wmi_prb_tmpl(ar, arvif->vdev_id, prb); + kfree_skb(prb); + + if (ret) { + ath10k_warn(ar, "failed to submit probe resp template command: %d\n", + ret); + return ret; + } + + return 0; +} + static void ath10k_control_beaconing(struct ath10k_vif *arvif, struct ieee80211_bss_conf *info) { @@ -1046,28 +1202,85 @@ static void ath10k_control_ibss(struct ath10k_vif *arvif, arvif->vdev_id, ret); } -/* - * Review this when mac80211 gains per-interface powersave support. - */ +static int ath10k_mac_vif_recalc_ps_wake_threshold(struct ath10k_vif *arvif) +{ + struct ath10k *ar = arvif->ar; + u32 param; + u32 value; + int ret; + + lockdep_assert_held(&arvif->ar->conf_mutex); + + if (arvif->u.sta.uapsd) + value = WMI_STA_PS_TX_WAKE_THRESHOLD_NEVER; + else + value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS; + + param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD; + ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, value); + if (ret) { + ath10k_warn(ar, "failed to submit ps wake threshold %u on vdev %i: %d\n", + value, arvif->vdev_id, ret); + return ret; + } + + return 0; +} + +static int ath10k_mac_vif_recalc_ps_poll_count(struct ath10k_vif *arvif) +{ + struct ath10k *ar = arvif->ar; + u32 param; + u32 value; + int ret; + + lockdep_assert_held(&arvif->ar->conf_mutex); + + if (arvif->u.sta.uapsd) + value = WMI_STA_PS_PSPOLL_COUNT_UAPSD; + else + value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX; + + param = WMI_STA_PS_PARAM_PSPOLL_COUNT; + ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, + param, value); + if (ret) { + ath10k_warn(ar, "failed to submit ps poll count %u on vdev %i: %d\n", + value, arvif->vdev_id, ret); + return ret; + } + + return 0; +} + static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif) { struct ath10k *ar = arvif->ar; + struct ieee80211_vif *vif = arvif->vif; struct ieee80211_conf *conf = &ar->hw->conf; enum wmi_sta_powersave_param param; enum wmi_sta_ps_mode psmode; int ret; + int ps_timeout; lockdep_assert_held(&arvif->ar->conf_mutex); if (arvif->vif->type != NL80211_IFTYPE_STATION) return 0; - if (conf->flags & IEEE80211_CONF_PS) { + if (vif->bss_conf.ps) { psmode = WMI_STA_PS_MODE_ENABLED; param = WMI_STA_PS_PARAM_INACTIVITY_TIME; + ps_timeout = conf->dynamic_ps_timeout; + if (ps_timeout == 0) { + /* Firmware doesn't like 0 */ + ps_timeout = ieee80211_tu_to_usec( + vif->bss_conf.beacon_int) / 1000; + } + ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, param, - conf->dynamic_ps_timeout); + ps_timeout); if (ret) { ath10k_warn(ar, "failed to set inactivity time for vdev %d: %i\n", arvif->vdev_id, ret); @@ -1090,6 +1303,38 @@ static int ath10k_mac_vif_setup_ps(struct ath10k_vif *arvif) return 0; } +static int ath10k_mac_vif_disable_keepalive(struct ath10k_vif *arvif) +{ + struct ath10k *ar = arvif->ar; + struct wmi_sta_keepalive_arg arg = {}; + int ret; + + lockdep_assert_held(&arvif->ar->conf_mutex); + + if (arvif->vdev_type != WMI_VDEV_TYPE_STA) + return 0; + + if (!test_bit(WMI_SERVICE_STA_KEEP_ALIVE, ar->wmi.svc_map)) + return 0; + + /* Some firmware revisions have a bug and ignore the `enabled` field. + * Instead use the interval to disable the keepalive. + */ + arg.vdev_id = arvif->vdev_id; + arg.enabled = 1; + arg.method = WMI_STA_KEEPALIVE_METHOD_NULL_FRAME; + arg.interval = WMI_STA_KEEPALIVE_INTERVAL_DISABLE; + + ret = ath10k_wmi_sta_keepalive(ar, &arg); + if (ret) { + ath10k_warn(ar, "failed to submit keepalive on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + return 0; +} + /**********************/ /* Station management */ /**********************/ @@ -1358,6 +1603,10 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar, return; arg->peer_flags |= WMI_PEER_VHT; + + if (ar->hw->conf.chandef.chan->band == IEEE80211_BAND_2GHZ) + arg->peer_flags |= WMI_PEER_VHT_2G; + arg->peer_vht_caps = vht_cap->cap; ampdu_factor = (vht_cap->cap & @@ -1409,9 +1658,22 @@ static void ath10k_peer_assoc_h_qos(struct ath10k *ar, if (vif->bss_conf.qos) arg->peer_flags |= WMI_PEER_QOS; break; + case WMI_VDEV_TYPE_IBSS: + if (sta->wme) + arg->peer_flags |= WMI_PEER_QOS; + break; default: break; } + + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac peer %pM qos %d\n", + sta->addr, !!(arg->peer_flags & WMI_PEER_QOS)); +} + +static bool ath10k_mac_sta_has_11g_rates(struct ieee80211_sta *sta) +{ + /* First 4 rates in ath10k_rates are CCK (11b) rates. */ + return sta->supp_rates[IEEE80211_BAND_2GHZ] >> 4; } static void ath10k_peer_assoc_h_phymode(struct ath10k *ar, @@ -1423,13 +1685,20 @@ static void ath10k_peer_assoc_h_phymode(struct ath10k *ar, switch (ar->hw->conf.chandef.chan->band) { case IEEE80211_BAND_2GHZ: - if (sta->ht_cap.ht_supported) { + if (sta->vht_cap.vht_supported) { + if (sta->bandwidth == IEEE80211_STA_RX_BW_40) + phymode = MODE_11AC_VHT40; + else + phymode = MODE_11AC_VHT20; + } else if (sta->ht_cap.ht_supported) { if (sta->bandwidth == IEEE80211_STA_RX_BW_40) phymode = MODE_11NG_HT40; else phymode = MODE_11NG_HT20; - } else { + } else if (ath10k_mac_sta_has_11g_rates(sta)) { phymode = MODE_11G; + } else { + phymode = MODE_11B; } break; @@ -1603,7 +1872,8 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw, ath10k_warn(ar, "faield to down vdev %i: %d\n", arvif->vdev_id, ret); - arvif->def_wep_key_idx = 0; + arvif->def_wep_key_idx = -1; + arvif->is_up = false; } @@ -1662,11 +1932,14 @@ static int ath10k_station_assoc(struct ath10k *ar, } } - ret = ath10k_install_peer_wep_keys(arvif, sta->addr); - if (ret) { - ath10k_warn(ar, "failed to install peer wep keys for vdev %i: %d\n", - arvif->vdev_id, ret); - return ret; + /* Plumb cached keys only for static WEP */ + if (arvif->def_wep_key_idx != -1) { + ret = ath10k_install_peer_wep_keys(arvif, sta->addr); + if (ret) { + ath10k_warn(ar, "failed to install peer wep keys for vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } } } @@ -1931,75 +2204,13 @@ static void ath10k_tx_h_nwifi(struct ieee80211_hw *hw, struct sk_buff *skb) * used only for CQM purposes (e.g. hostapd station keepalive ping) so * it is safe to downgrade to NullFunc. */ + hdr = (void *)skb->data; if (ieee80211_is_qos_nullfunc(hdr->frame_control)) { hdr->frame_control &= ~__cpu_to_le16(IEEE80211_STYPE_QOS_DATA); cb->htt.tid = HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST; } } -static void ath10k_tx_wep_key_work(struct work_struct *work) -{ - struct ath10k_vif *arvif = container_of(work, struct ath10k_vif, - wep_key_work); - struct ath10k *ar = arvif->ar; - int ret, keyidx = arvif->def_wep_key_newidx; - - mutex_lock(&arvif->ar->conf_mutex); - - if (arvif->ar->state != ATH10K_STATE_ON) - goto unlock; - - if (arvif->def_wep_key_idx == keyidx) - goto unlock; - - ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n", - arvif->vdev_id, keyidx); - - ret = ath10k_wmi_vdev_set_param(arvif->ar, - arvif->vdev_id, - arvif->ar->wmi.vdev_param->def_keyid, - keyidx); - if (ret) { - ath10k_warn(ar, "failed to update wep key index for vdev %d: %d\n", - arvif->vdev_id, - ret); - goto unlock; - } - - arvif->def_wep_key_idx = keyidx; - -unlock: - mutex_unlock(&arvif->ar->conf_mutex); -} - -static void ath10k_tx_h_update_wep_key(struct ieee80211_vif *vif, - struct ieee80211_key_conf *key, - struct sk_buff *skb) -{ - struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); - struct ath10k *ar = arvif->ar; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - - if (!ieee80211_has_protected(hdr->frame_control)) - return; - - if (!key) - return; - - if (key->cipher != WLAN_CIPHER_SUITE_WEP40 && - key->cipher != WLAN_CIPHER_SUITE_WEP104) - return; - - if (key->keyidx == arvif->def_wep_key_idx) - return; - - /* FIXME: Most likely a few frames will be TXed with an old key. Simply - * queueing frames until key index is updated is not an option because - * sk_buff may need more processing to be done, e.g. offchannel */ - arvif->def_wep_key_newidx = key->keyidx; - ieee80211_queue_work(ar->hw, &arvif->wep_key_work); -} - static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct ieee80211_vif *vif, struct sk_buff *skb) @@ -2151,7 +2362,7 @@ void ath10k_offchan_tx_work(struct work_struct *work) ret = wait_for_completion_timeout(&ar->offchan_tx_completed, 3 * HZ); - if (ret <= 0) + if (ret == 0) ath10k_warn(ar, "timed out waiting for offchannel skb %p\n", skb); @@ -2213,6 +2424,7 @@ void __ath10k_scan_finish(struct ath10k *ar) case ATH10K_SCAN_RUNNING: if (ar->scan.is_roc) ieee80211_remain_on_channel_expired(ar->hw); + /* fall through */ case ATH10K_SCAN_ABORTING: if (!ar->scan.is_roc) ieee80211_scan_completed(ar->hw, @@ -2359,7 +2571,6 @@ static void ath10k_tx(struct ieee80211_hw *hw, struct ath10k *ar = hw->priv; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_vif *vif = info->control.vif; - struct ieee80211_key_conf *key = info->control.hw_key; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; /* We should disable CCK RATE due to P2P */ @@ -2373,7 +2584,6 @@ static void ath10k_tx(struct ieee80211_hw *hw, /* it makes no sense to process injected frames like that */ if (vif && vif->type != NL80211_IFTYPE_MONITOR) { ath10k_tx_h_nwifi(hw, skb); - ath10k_tx_h_update_wep_key(vif, key, skb); ath10k_tx_h_add_p2p_noa_ie(ar, vif, skb); ath10k_tx_h_seq_no(vif, skb); } @@ -2871,6 +3081,8 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, int bit; u32 vdev_param; + vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; + mutex_lock(&ar->conf_mutex); memset(arvif, 0, sizeof(*arvif)); @@ -2878,7 +3090,6 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, arvif->ar = ar; arvif->vif = vif; - INIT_WORK(&arvif->wep_key_work, ath10k_tx_wep_key_work); INIT_LIST_HEAD(&arvif->list); if (ar->free_vdev_map == 0) { @@ -2894,10 +3105,11 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, arvif->vdev_id = bit; arvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE; - if (ar->p2p) - arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE; - switch (vif->type) { + case NL80211_IFTYPE_P2P_DEVICE: + arvif->vdev_type = WMI_VDEV_TYPE_STA; + arvif->vdev_subtype = WMI_VDEV_SUBTYPE_P2P_DEVICE; + break; case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_STATION: arvif->vdev_type = WMI_VDEV_TYPE_STA; @@ -2966,15 +3178,18 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, ar->free_vdev_map &= ~(1LL << arvif->vdev_id); list_add(&arvif->list, &ar->arvifs); - vdev_param = ar->wmi.vdev_param->def_keyid; - ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param, - arvif->def_wep_key_idx); + /* It makes no sense to have firmware do keepalives. mac80211 already + * takes care of this with idle connection polling. + */ + ret = ath10k_mac_vif_disable_keepalive(arvif); if (ret) { - ath10k_warn(ar, "failed to set vdev %i default key id: %d\n", + ath10k_warn(ar, "failed to disable keepalive on vdev %i: %d\n", arvif->vdev_id, ret); goto err_vdev_delete; } + arvif->def_wep_key_idx = -1; + vdev_param = ar->wmi.vdev_param->tx_encap_type; ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param, ATH10K_HW_TXRX_NATIVE_WIFI); @@ -3026,22 +3241,16 @@ static int ath10k_add_interface(struct ieee80211_hw *hw, goto err_peer_delete; } - param = WMI_STA_PS_PARAM_TX_WAKE_THRESHOLD; - value = WMI_STA_PS_TX_WAKE_THRESHOLD_ALWAYS; - ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, - param, value); + ret = ath10k_mac_vif_recalc_ps_wake_threshold(arvif); if (ret) { - ath10k_warn(ar, "failed to set vdev %i TX wake thresh: %d\n", + ath10k_warn(ar, "failed to recalc ps wake threshold on vdev %i: %d\n", arvif->vdev_id, ret); goto err_peer_delete; } - param = WMI_STA_PS_PARAM_PSPOLL_COUNT; - value = WMI_STA_PS_PSPOLL_COUNT_NO_MAX; - ret = ath10k_wmi_set_sta_ps_param(ar, arvif->vdev_id, - param, value); + ret = ath10k_mac_vif_recalc_ps_poll_count(arvif); if (ret) { - ath10k_warn(ar, "failed to set vdev %i PSPOLL count: %d\n", + ath10k_warn(ar, "failed to recalc ps poll count on vdev %i: %d\n", arvif->vdev_id, ret); goto err_peer_delete; } @@ -3099,8 +3308,6 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw, struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); int ret; - cancel_work_sync(&arvif->wep_key_work); - mutex_lock(&ar->conf_mutex); spin_lock_bh(&ar->data_lock); @@ -3211,9 +3418,21 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, if (ret) ath10k_warn(ar, "failed to set beacon mode for vdev %d: %i\n", arvif->vdev_id, ret); + + ret = ath10k_mac_setup_bcn_tmpl(arvif); + if (ret) + ath10k_warn(ar, "failed to update beacon template: %d\n", + ret); } - if (changed & BSS_CHANGED_BEACON_INFO) { + if (changed & BSS_CHANGED_AP_PROBE_RESP) { + ret = ath10k_mac_setup_prb_tmpl(arvif); + if (ret) + ath10k_warn(ar, "failed to setup probe resp template on vdev %i: %d\n", + arvif->vdev_id, ret); + } + + if (changed & (BSS_CHANGED_BEACON_INFO | BSS_CHANGED_BEACON)) { arvif->dtim_period = info->dtim_period; ath10k_dbg(ar, ATH10K_DBG_MAC, @@ -3314,6 +3533,13 @@ static void ath10k_bss_info_changed(struct ieee80211_hw *hw, ath10k_warn(ar, "failed to recalc tx power: %d\n", ret); } + if (changed & BSS_CHANGED_PS) { + ret = ath10k_mac_vif_setup_ps(arvif); + if (ret) + ath10k_warn(ar, "failed to setup ps on vdev %i: %d\n", + arvif->vdev_id, ret); + } + mutex_unlock(&ar->conf_mutex); } @@ -3453,6 +3679,7 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, const u8 *peer_addr; bool is_wep = key->cipher == WLAN_CIPHER_SUITE_WEP40 || key->cipher == WLAN_CIPHER_SUITE_WEP104; + bool def_idx = false; int ret = 0; if (key->keyidx > WMI_MAX_KEY_INDEX) @@ -3498,7 +3725,14 @@ static int ath10k_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ath10k_clear_vdev_key(arvif, key); } - ret = ath10k_install_key(arvif, key, cmd, peer_addr); + /* set TX_USAGE flag for all the keys incase of dot1x-WEP. For + * static WEP, do not set this flag for the keys whose key id + * is greater than default key id. + */ + if (arvif->def_wep_key_idx == -1) + def_idx = true; + + ret = ath10k_install_key(arvif, key, cmd, peer_addr, def_idx); if (ret) { ath10k_warn(ar, "failed to install key for vdev %i peer %pM: %d\n", arvif->vdev_id, peer_addr, ret); @@ -3523,6 +3757,39 @@ exit: return ret; } +static void ath10k_set_default_unicast_key(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + int keyidx) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + int ret; + + mutex_lock(&arvif->ar->conf_mutex); + + if (arvif->ar->state != ATH10K_STATE_ON) + goto unlock; + + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n", + arvif->vdev_id, keyidx); + + ret = ath10k_wmi_vdev_set_param(arvif->ar, + arvif->vdev_id, + arvif->ar->wmi.vdev_param->def_keyid, + keyidx); + + if (ret) { + ath10k_warn(ar, "failed to update wep key index for vdev %d: %d\n", + arvif->vdev_id, + ret); + goto unlock; + } + + arvif->def_wep_key_idx = keyidx; +unlock: + mutex_unlock(&arvif->ar->conf_mutex); +} + static void ath10k_sta_rc_update_wk(struct work_struct *wk) { struct ath10k *ar; @@ -3583,8 +3850,9 @@ static void ath10k_sta_rc_update_wk(struct work_struct *wk) sta->addr, smps, err); } - if (changed & IEEE80211_RC_SUPP_RATES_CHANGED) { - ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM supp rates\n", + if (changed & IEEE80211_RC_SUPP_RATES_CHANGED || + changed & IEEE80211_RC_NSS_CHANGED) { + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac update sta %pM supp rates/nss\n", sta->addr); err = ath10k_station_assoc(ar, arvif->vif, sta, true); @@ -3757,6 +4025,8 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif, u16 ac, bool enable) { struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + struct wmi_sta_uapsd_auto_trig_arg arg = {}; + u32 prio = 0, acc = 0; u32 value = 0; int ret = 0; @@ -3769,18 +4039,26 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif, case IEEE80211_AC_VO: value = WMI_STA_PS_UAPSD_AC3_DELIVERY_EN | WMI_STA_PS_UAPSD_AC3_TRIGGER_EN; + prio = 7; + acc = 3; break; case IEEE80211_AC_VI: value = WMI_STA_PS_UAPSD_AC2_DELIVERY_EN | WMI_STA_PS_UAPSD_AC2_TRIGGER_EN; + prio = 5; + acc = 2; break; case IEEE80211_AC_BE: value = WMI_STA_PS_UAPSD_AC1_DELIVERY_EN | WMI_STA_PS_UAPSD_AC1_TRIGGER_EN; + prio = 2; + acc = 1; break; case IEEE80211_AC_BK: value = WMI_STA_PS_UAPSD_AC0_DELIVERY_EN | WMI_STA_PS_UAPSD_AC0_TRIGGER_EN; + prio = 0; + acc = 0; break; } @@ -3808,6 +4086,43 @@ static int ath10k_conf_tx_uapsd(struct ath10k *ar, struct ieee80211_vif *vif, if (ret) ath10k_warn(ar, "failed to set rx wake param: %d\n", ret); + ret = ath10k_mac_vif_recalc_ps_wake_threshold(arvif); + if (ret) { + ath10k_warn(ar, "failed to recalc ps wake threshold on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + ret = ath10k_mac_vif_recalc_ps_poll_count(arvif); + if (ret) { + ath10k_warn(ar, "failed to recalc ps poll count on vdev %i: %d\n", + arvif->vdev_id, ret); + return ret; + } + + if (test_bit(WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, ar->wmi.svc_map) || + test_bit(WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, ar->wmi.svc_map)) { + /* Only userspace can make an educated decision when to send + * trigger frame. The following effectively disables u-UAPSD + * autotrigger in firmware (which is enabled by default + * provided the autotrigger service is available). + */ + + arg.wmm_ac = acc; + arg.user_priority = prio; + arg.service_interval = 0; + arg.suspend_interval = WMI_STA_UAPSD_MAX_INTERVAL_MSEC; + arg.delay_interval = WMI_STA_UAPSD_MAX_INTERVAL_MSEC; + + ret = ath10k_wmi_vdev_sta_uapsd(ar, arvif->vdev_id, + arvif->bssid, &arg, 1); + if (ret) { + ath10k_warn(ar, "failed to set uapsd auto trigger %d\n", + ret); + return ret; + } + } + exit: return ret; } @@ -3817,6 +4132,7 @@ static int ath10k_conf_tx(struct ieee80211_hw *hw, const struct ieee80211_tx_queue_params *params) { struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); struct wmi_wmm_params_arg *p = NULL; int ret; @@ -3824,16 +4140,16 @@ static int ath10k_conf_tx(struct ieee80211_hw *hw, switch (ac) { case IEEE80211_AC_VO: - p = &ar->wmm_params.ac_vo; + p = &arvif->wmm_params.ac_vo; break; case IEEE80211_AC_VI: - p = &ar->wmm_params.ac_vi; + p = &arvif->wmm_params.ac_vi; break; case IEEE80211_AC_BE: - p = &ar->wmm_params.ac_be; + p = &arvif->wmm_params.ac_be; break; case IEEE80211_AC_BK: - p = &ar->wmm_params.ac_bk; + p = &arvif->wmm_params.ac_bk; break; } @@ -3853,11 +4169,23 @@ static int ath10k_conf_tx(struct ieee80211_hw *hw, */ p->txop = params->txop * 32; - /* FIXME: FW accepts wmm params per hw, not per vif */ - ret = ath10k_wmi_pdev_set_wmm_params(ar, &ar->wmm_params); - if (ret) { - ath10k_warn(ar, "failed to set wmm params: %d\n", ret); - goto exit; + if (ar->wmi.ops->gen_vdev_wmm_conf) { + ret = ath10k_wmi_vdev_wmm_conf(ar, arvif->vdev_id, + &arvif->wmm_params); + if (ret) { + ath10k_warn(ar, "failed to set vdev wmm params on vdev %i: %d\n", + arvif->vdev_id, ret); + goto exit; + } + } else { + /* This won't work well with multi-interface cases but it's + * better than nothing. + */ + ret = ath10k_wmi_pdev_set_wmm_params(ar, &arvif->wmm_params); + if (ret) { + ath10k_warn(ar, "failed to set wmm params: %d\n", ret); + goto exit; + } } ret = ath10k_conf_tx_uapsd(ar, vif, ac, params->uapsd); @@ -3989,29 +4317,6 @@ static int ath10k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) return ret; } -static int ath10k_set_frag_threshold(struct ieee80211_hw *hw, u32 value) -{ - struct ath10k *ar = hw->priv; - struct ath10k_vif *arvif; - int ret = 0; - - mutex_lock(&ar->conf_mutex); - list_for_each_entry(arvif, &ar->arvifs, list) { - ath10k_dbg(ar, ATH10K_DBG_MAC, "mac vdev %d fragmentation threshold %d\n", - arvif->vdev_id, value); - - ret = ath10k_mac_set_frag(arvif, value); - if (ret) { - ath10k_warn(ar, "failed to set fragmentation threshold for vdev %d: %d\n", - arvif->vdev_id, ret); - break; - } - } - mutex_unlock(&ar->conf_mutex); - - return ret; -} - static void ath10k_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop) { @@ -4650,12 +4955,12 @@ static const struct ieee80211_ops ath10k_ops = { .hw_scan = ath10k_hw_scan, .cancel_hw_scan = ath10k_cancel_hw_scan, .set_key = ath10k_set_key, + .set_default_unicast_key = ath10k_set_default_unicast_key, .sta_state = ath10k_sta_state, .conf_tx = ath10k_conf_tx, .remain_on_channel = ath10k_remain_on_channel, .cancel_remain_on_channel = ath10k_cancel_remain_on_channel, .set_rts_threshold = ath10k_set_rts_threshold, - .set_frag_threshold = ath10k_set_frag_threshold, .flush = ath10k_flush, .tx_last_beacon = ath10k_tx_last_beacon, .set_antenna = ath10k_set_antenna, @@ -4676,6 +4981,9 @@ static const struct ieee80211_ops ath10k_ops = { .suspend = ath10k_suspend, .resume = ath10k_resume, #endif +#ifdef CONFIG_MAC80211_DEBUGFS + .sta_add_debugfs = ath10k_sta_add_debugfs, +#endif }; #define RATETAB_ENT(_rate, _rateid, _flags) { \ @@ -4746,6 +5054,9 @@ static const struct ieee80211_channel ath10k_5ghz_channels[] = { CHAN5G(165, 5825, 0), }; +/* Note: Be careful if you re-order these. There is code which depends on this + * ordering. + */ static struct ieee80211_rate ath10k_rates[] = { /* CCK */ RATETAB_ENT(10, 0x82, 0), @@ -4799,6 +5110,10 @@ static const struct ieee80211_iface_limit ath10k_if_limits[] = { .types = BIT(NL80211_IFTYPE_P2P_GO) }, { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_DEVICE) + }, + { .max = 7, .types = BIT(NL80211_IFTYPE_AP) }, @@ -4956,6 +5271,13 @@ struct ath10k_vif *ath10k_get_arvif(struct ath10k *ar, u32 vdev_id) int ath10k_mac_register(struct ath10k *ar) { + static const u32 cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + WLAN_CIPHER_SUITE_AES_CMAC, + }; struct ieee80211_supported_band *band; struct ieee80211_sta_vht_cap vht_cap; struct ieee80211_sta_ht_cap ht_cap; @@ -4985,7 +5307,8 @@ int ath10k_mac_register(struct ath10k *ar) band->bitrates = ath10k_g_rates; band->ht_cap = ht_cap; - /* vht is not supported in 2.4 GHz */ + /* Enable the VHT support at 2.4 GHz */ + band->vht_cap = vht_cap; ar->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = band; } @@ -5018,18 +5341,19 @@ int ath10k_mac_register(struct ath10k *ar) if (!test_bit(ATH10K_FW_FEATURE_NO_P2P, ar->fw_features)) ar->hw->wiphy->interface_modes |= + BIT(NL80211_IFTYPE_P2P_DEVICE) | BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO); ar->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | - IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_MFP_CAPABLE | IEEE80211_HW_REPORTS_TX_ACK_STATUS | IEEE80211_HW_HAS_RATE_CONTROL | IEEE80211_HW_AP_LINK_PS | - IEEE80211_HW_SPECTRUM_MGMT; + IEEE80211_HW_SPECTRUM_MGMT | + IEEE80211_HW_SW_CRYPTO_CONTROL; ar->hw->wiphy->features |= NL80211_FEATURE_STATIC_SMPS; @@ -5049,6 +5373,19 @@ int ath10k_mac_register(struct ath10k *ar) ar->hw->max_listen_interval = ATH10K_MAX_HW_LISTEN_INTERVAL; + if (test_bit(WMI_SERVICE_BEACON_OFFLOAD, ar->wmi.svc_map)) { + ar->hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; + + /* Firmware delivers WPS/P2P Probe Requests frames to driver so + * that userspace (e.g. wpa_supplicant/hostapd) can generate + * correct Probe Responses. This is more of a hack advert.. + */ + ar->hw->wiphy->probe_resp_offload |= + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; + } + ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; ar->hw->wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; ar->hw->wiphy->max_remain_on_channel_duration = 5000; @@ -5062,16 +5399,26 @@ int ath10k_mac_register(struct ath10k *ar) */ ar->hw->queues = 4; - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - ar->hw->wiphy->iface_combinations = ath10k_10x_if_comb; - ar->hw->wiphy->n_iface_combinations = - ARRAY_SIZE(ath10k_10x_if_comb); - } else { + switch (ar->wmi.op_version) { + case ATH10K_FW_WMI_OP_VERSION_MAIN: + case ATH10K_FW_WMI_OP_VERSION_TLV: ar->hw->wiphy->iface_combinations = ath10k_if_comb; ar->hw->wiphy->n_iface_combinations = ARRAY_SIZE(ath10k_if_comb); - ar->hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC); + break; + case ATH10K_FW_WMI_OP_VERSION_10_1: + case ATH10K_FW_WMI_OP_VERSION_10_2: + case ATH10K_FW_WMI_OP_VERSION_10_2_4: + ar->hw->wiphy->iface_combinations = ath10k_10x_if_comb; + ar->hw->wiphy->n_iface_combinations = + ARRAY_SIZE(ath10k_10x_if_comb); + break; + case ATH10K_FW_WMI_OP_VERSION_UNSET: + case ATH10K_FW_WMI_OP_VERSION_MAX: + WARN_ON(1); + ret = -EINVAL; + goto err_free; } ar->hw->netdev_features = NETIF_F_HW_CSUM; @@ -5093,6 +5440,9 @@ int ath10k_mac_register(struct ath10k *ar) goto err_free; } + ar->hw->wiphy->cipher_suites = cipher_suites; + ar->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); + ret = ieee80211_register_hw(ar->hw); if (ret) { ath10k_err(ar, "failed to register ieee80211: %d\n", ret); diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index 7abb8367119a..e6972b09333e 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -58,12 +58,27 @@ MODULE_PARM_DESC(reset_mode, "0: auto, 1: warm only (default: 0)"); #define ATH10K_PCI_NUM_WARM_RESET_ATTEMPTS 3 #define QCA988X_2_0_DEVICE_ID (0x003c) +#define QCA6174_2_1_DEVICE_ID (0x003e) static const struct pci_device_id ath10k_pci_id_table[] = { { PCI_VDEVICE(ATHEROS, QCA988X_2_0_DEVICE_ID) }, /* PCI-E QCA988X V2 */ + { PCI_VDEVICE(ATHEROS, QCA6174_2_1_DEVICE_ID) }, /* PCI-E QCA6174 V2.1 */ {0} }; +static const struct ath10k_pci_supp_chip ath10k_pci_supp_chips[] = { + /* QCA988X pre 2.0 chips are not supported because they need some nasty + * hacks. ath10k doesn't have them and these devices crash horribly + * because of that. + */ + { QCA988X_2_0_DEVICE_ID, QCA988X_HW_2_0_CHIP_ID_REV }, + { QCA6174_2_1_DEVICE_ID, QCA6174_HW_2_1_CHIP_ID_REV }, + { QCA6174_2_1_DEVICE_ID, QCA6174_HW_2_2_CHIP_ID_REV }, + { QCA6174_2_1_DEVICE_ID, QCA6174_HW_3_0_CHIP_ID_REV }, + { QCA6174_2_1_DEVICE_ID, QCA6174_HW_3_1_CHIP_ID_REV }, + { QCA6174_2_1_DEVICE_ID, QCA6174_HW_3_2_CHIP_ID_REV }, +}; + static void ath10k_pci_buffer_cleanup(struct ath10k *ar); static int ath10k_pci_cold_reset(struct ath10k *ar); static int ath10k_pci_warm_reset(struct ath10k *ar); @@ -395,7 +410,7 @@ static int __ath10k_pci_rx_post_buf(struct ath10k_pci_pipe *pipe) return -EIO; } - ATH10K_SKB_CB(skb)->paddr = paddr; + ATH10K_SKB_RXCB(skb)->paddr = paddr; ret = __ath10k_ce_rx_post_buf(ce_pipe, skb, paddr); if (ret) { @@ -864,7 +879,7 @@ static void ath10k_pci_ce_recv_data(struct ath10k_ce_pipe *ce_state) &flags) == 0) { skb = transfer_context; max_nbytes = skb->len + skb_tailroom(skb); - dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr, + dma_unmap_single(ar->dev, ATH10K_SKB_RXCB(skb)->paddr, max_nbytes, DMA_FROM_DEVICE); if (unlikely(max_nbytes < nbytes)) { @@ -1230,7 +1245,7 @@ static void ath10k_pci_rx_pipe_cleanup(struct ath10k_pci_pipe *pci_pipe) ce_ring->per_transfer_context[i] = NULL; - dma_unmap_single(ar->dev, ATH10K_SKB_CB(skb)->paddr, + dma_unmap_single(ar->dev, ATH10K_SKB_RXCB(skb)->paddr, skb->len + skb_tailroom(skb), DMA_FROM_DEVICE); dev_kfree_skb_any(skb); @@ -1498,6 +1513,35 @@ static int ath10k_pci_wake_target_cpu(struct ath10k *ar) return 0; } +static int ath10k_pci_get_num_banks(struct ath10k *ar) +{ + struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); + + switch (ar_pci->pdev->device) { + case QCA988X_2_0_DEVICE_ID: + return 1; + case QCA6174_2_1_DEVICE_ID: + switch (MS(ar->chip_id, SOC_CHIP_ID_REV)) { + case QCA6174_HW_1_0_CHIP_ID_REV: + case QCA6174_HW_1_1_CHIP_ID_REV: + return 3; + case QCA6174_HW_1_3_CHIP_ID_REV: + return 2; + case QCA6174_HW_2_1_CHIP_ID_REV: + case QCA6174_HW_2_2_CHIP_ID_REV: + return 6; + case QCA6174_HW_3_0_CHIP_ID_REV: + case QCA6174_HW_3_1_CHIP_ID_REV: + case QCA6174_HW_3_2_CHIP_ID_REV: + return 9; + } + break; + } + + ath10k_warn(ar, "unknown number of banks, assuming 1\n"); + return 1; +} + static int ath10k_pci_init_config(struct ath10k *ar) { u32 interconnect_targ_addr; @@ -1608,7 +1652,8 @@ static int ath10k_pci_init_config(struct ath10k *ar) /* first bank is switched to IRAM */ ealloc_value |= ((HI_EARLY_ALLOC_MAGIC << HI_EARLY_ALLOC_MAGIC_SHIFT) & HI_EARLY_ALLOC_MAGIC_MASK); - ealloc_value |= ((1 << HI_EARLY_ALLOC_IRAM_BANKS_SHIFT) & + ealloc_value |= ((ath10k_pci_get_num_banks(ar) << + HI_EARLY_ALLOC_IRAM_BANKS_SHIFT) & HI_EARLY_ALLOC_IRAM_BANKS_MASK); ret = ath10k_pci_diag_write32(ar, ealloc_targ_addr, ealloc_value); @@ -1804,12 +1849,12 @@ static int ath10k_pci_warm_reset(struct ath10k *ar) return 0; } -static int ath10k_pci_chip_reset(struct ath10k *ar) +static int ath10k_pci_qca988x_chip_reset(struct ath10k *ar) { int i, ret; u32 val; - ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot chip reset\n"); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot 988x chip reset\n"); /* Some hardware revisions (e.g. CUS223v2) has issues with cold reset. * It is thus preferred to use warm reset which is safer but may not be @@ -1873,11 +1918,53 @@ static int ath10k_pci_chip_reset(struct ath10k *ar) return ret; } - ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot chip reset complete (cold)\n"); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot qca988x chip reset complete (cold)\n"); + + return 0; +} + +static int ath10k_pci_qca6174_chip_reset(struct ath10k *ar) +{ + int ret; + + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot qca6174 chip reset\n"); + + /* FIXME: QCA6174 requires cold + warm reset to work. */ + + ret = ath10k_pci_cold_reset(ar); + if (ret) { + ath10k_warn(ar, "failed to cold reset: %d\n", ret); + return ret; + } + + ret = ath10k_pci_wait_for_target_init(ar); + if (ret) { + ath10k_warn(ar, "failed to wait for target after cold reset: %d\n", + ret); + return ret; + } + + ret = ath10k_pci_warm_reset(ar); + if (ret) { + ath10k_warn(ar, "failed to warm reset: %d\n", ret); + return ret; + } + + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot qca6174 chip reset complete (cold)\n"); return 0; } +static int ath10k_pci_chip_reset(struct ath10k *ar) +{ + if (QCA_REV_988X(ar)) + return ath10k_pci_qca988x_chip_reset(ar); + else if (QCA_REV_6174(ar)) + return ath10k_pci_qca6174_chip_reset(ar); + else + return -ENOTSUPP; +} + static int ath10k_pci_hif_power_up(struct ath10k *ar) { int ret; @@ -1902,6 +1989,12 @@ static int ath10k_pci_hif_power_up(struct ath10k *ar) */ ret = ath10k_pci_chip_reset(ar); if (ret) { + if (ath10k_pci_has_fw_crashed(ar)) { + ath10k_warn(ar, "firmware crashed during chip reset\n"); + ath10k_pci_fw_crashed_clear(ar); + ath10k_pci_fw_crashed_dump(ar); + } + ath10k_err(ar, "failed to reset chip: %d\n", ret); goto err_sleep; } @@ -2033,6 +2126,7 @@ static void ath10k_msi_err_tasklet(unsigned long data) return; } + ath10k_pci_irq_disable(ar); ath10k_pci_fw_crashed_clear(ar); ath10k_pci_fw_crashed_dump(ar); } @@ -2102,6 +2196,7 @@ static void ath10k_pci_tasklet(unsigned long data) struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); if (ath10k_pci_has_fw_crashed(ar)) { + ath10k_pci_irq_disable(ar); ath10k_pci_fw_crashed_clear(ar); ath10k_pci_fw_crashed_dump(ar); return; @@ -2344,8 +2439,6 @@ static int ath10k_pci_wait_for_target_init(struct ath10k *ar) if (val & FW_IND_EVENT_PENDING) { ath10k_warn(ar, "device has crashed during init\n"); - ath10k_pci_fw_crashed_clear(ar); - ath10k_pci_fw_crashed_dump(ar); return -ECOMM; } @@ -2476,17 +2569,46 @@ static void ath10k_pci_release(struct ath10k *ar) pci_disable_device(pdev); } +static bool ath10k_pci_chip_is_supported(u32 dev_id, u32 chip_id) +{ + const struct ath10k_pci_supp_chip *supp_chip; + int i; + u32 rev_id = MS(chip_id, SOC_CHIP_ID_REV); + + for (i = 0; i < ARRAY_SIZE(ath10k_pci_supp_chips); i++) { + supp_chip = &ath10k_pci_supp_chips[i]; + + if (supp_chip->dev_id == dev_id && + supp_chip->rev_id == rev_id) + return true; + } + + return false; +} + static int ath10k_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_dev) { int ret = 0; struct ath10k *ar; struct ath10k_pci *ar_pci; + enum ath10k_hw_rev hw_rev; u32 chip_id; - ar = ath10k_core_create(sizeof(*ar_pci), &pdev->dev, - ATH10K_BUS_PCI, - &ath10k_pci_hif_ops); + switch (pci_dev->device) { + case QCA988X_2_0_DEVICE_ID: + hw_rev = ATH10K_HW_QCA988X; + break; + case QCA6174_2_1_DEVICE_ID: + hw_rev = ATH10K_HW_QCA6174; + break; + default: + WARN_ON(1); + return -ENOTSUPP; + } + + ar = ath10k_core_create(sizeof(*ar_pci), &pdev->dev, ATH10K_BUS_PCI, + hw_rev, &ath10k_pci_hif_ops); if (!ar) { dev_err(&pdev->dev, "failed to allocate core\n"); return -ENOMEM; @@ -2515,12 +2637,6 @@ static int ath10k_pci_probe(struct pci_dev *pdev, goto err_release; } - chip_id = ath10k_pci_soc_read32(ar, SOC_CHIP_ID_ADDRESS); - if (chip_id == 0xffffffff) { - ath10k_err(ar, "failed to get chip id\n"); - goto err_sleep; - } - ret = ath10k_pci_alloc_pipes(ar); if (ret) { ath10k_err(ar, "failed to allocate copy engine pipes: %d\n", @@ -2547,6 +2663,24 @@ static int ath10k_pci_probe(struct pci_dev *pdev, goto err_deinit_irq; } + ret = ath10k_pci_chip_reset(ar); + if (ret) { + ath10k_err(ar, "failed to reset chip: %d\n", ret); + goto err_free_irq; + } + + chip_id = ath10k_pci_soc_read32(ar, SOC_CHIP_ID_ADDRESS); + if (chip_id == 0xffffffff) { + ath10k_err(ar, "failed to get chip id\n"); + goto err_free_irq; + } + + if (!ath10k_pci_chip_is_supported(pdev->device, chip_id)) { + ath10k_err(ar, "device %04x with chip_id %08x isn't supported\n", + pdev->device, chip_id); + goto err_sleep; + } + ath10k_pci_sleep(ar); ret = ath10k_core_register(ar, chip_id); diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index cf36511c7f4d..bddf54320160 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -152,6 +152,11 @@ struct ath10k_pci_pipe { struct tasklet_struct intr; }; +struct ath10k_pci_supp_chip { + u32 dev_id; + u32 rev_id; +}; + struct ath10k_pci { struct pci_dev *pdev; struct device *dev; @@ -189,7 +194,7 @@ static inline struct ath10k_pci *ath10k_pci_priv(struct ath10k *ar) #define ATH10K_PCI_RX_POST_RETRY_MS 50 #define ATH_PCI_RESET_WAIT_MAX 10 /* ms */ -#define PCIE_WAKE_TIMEOUT 5000 /* 5ms */ +#define PCIE_WAKE_TIMEOUT 10000 /* 10ms */ #define BAR_NUM 0 diff --git a/drivers/net/wireless/ath/ath10k/rx_desc.h b/drivers/net/wireless/ath/ath10k/rx_desc.h index e1ffdd57a18c..e9cc7787bf5f 100644 --- a/drivers/net/wireless/ath/ath10k/rx_desc.h +++ b/drivers/net/wireless/ath/ath10k/rx_desc.h @@ -850,7 +850,7 @@ struct rx_ppdu_start { #define RX_PPDU_END_INFO1_PPDU_DONE (1 << 15) -struct rx_ppdu_end { +struct rx_ppdu_end_common { __le32 evm_p0; __le32 evm_p1; __le32 evm_p2; @@ -873,10 +873,33 @@ struct rx_ppdu_end { u8 phy_err_code; __le16 flags; /* %RX_PPDU_END_FLAGS_ */ __le32 info0; /* %RX_PPDU_END_INFO0_ */ +} __packed; + +struct rx_ppdu_end_qca988x { __le16 bb_length; __le16 info1; /* %RX_PPDU_END_INFO1_ */ } __packed; +#define RX_PPDU_END_RTT_CORRELATION_VALUE_MASK 0x00ffffff +#define RX_PPDU_END_RTT_CORRELATION_VALUE_LSB 0 +#define RX_PPDU_END_RTT_UNUSED_MASK 0x7f000000 +#define RX_PPDU_END_RTT_UNUSED_LSB 24 +#define RX_PPDU_END_RTT_NORMAL_MODE BIT(31) + +struct rx_ppdu_end_qca6174 { + __le32 rtt; /* %RX_PPDU_END_RTT_ */ + __le16 bb_length; + __le16 info1; /* %RX_PPDU_END_INFO1_ */ +} __packed; + +struct rx_ppdu_end { + struct rx_ppdu_end_common common; + union { + struct rx_ppdu_end_qca988x qca988x; + struct rx_ppdu_end_qca6174 qca6174; + } __packed; +} __packed; + /* * evm_p0 * EVM for pilot 0. Contain EVM for streams: 0, 1, 2 and 3. diff --git a/drivers/net/wireless/ath/ath10k/spectral.c b/drivers/net/wireless/ath/ath10k/spectral.c index 63ce61fcdac8..d22addf6118b 100644 --- a/drivers/net/wireless/ath/ath10k/spectral.c +++ b/drivers/net/wireless/ath/ath10k/spectral.c @@ -17,6 +17,7 @@ #include <linux/relay.h> #include "core.h" #include "debug.h" +#include "wmi-ops.h" static void send_fft_sample(struct ath10k *ar, const struct fft_sample_tlv *fft_sample_tlv) diff --git a/drivers/net/wireless/ath/ath10k/targaddrs.h b/drivers/net/wireless/ath/ath10k/targaddrs.h index 9d0ae30f9ff1..a417aae52623 100644 --- a/drivers/net/wireless/ath/ath10k/targaddrs.h +++ b/drivers/net/wireless/ath/ath10k/targaddrs.h @@ -18,6 +18,8 @@ #ifndef __TARGADDRS_H__ #define __TARGADDRS_H__ +#include "hw.h" + /* * xxx_HOST_INTEREST_ADDRESS is the address in Target RAM of the * host_interest structure. It must match the address of the _host_interest @@ -445,4 +447,7 @@ Fw Mode/SubMode Mask #define QCA988X_BOARD_DATA_SZ 7168 #define QCA988X_BOARD_EXT_DATA_SZ 0 +#define QCA6174_BOARD_DATA_SZ 8192 +#define QCA6174_BOARD_EXT_DATA_SZ 0 + #endif /* __TARGADDRS_H__ */ diff --git a/drivers/net/wireless/ath/ath10k/testmode.c b/drivers/net/wireless/ath/ath10k/testmode.c index 483db9cb8c96..b084f88da102 100644 --- a/drivers/net/wireless/ath/ath10k/testmode.c +++ b/drivers/net/wireless/ath/ath10k/testmode.c @@ -187,13 +187,14 @@ static int ath10k_tm_cmd_utf_start(struct ath10k *ar, struct nlattr *tb[]) memcpy(ar->testmode.orig_fw_features, ar->fw_features, sizeof(ar->fw_features)); + ar->testmode.orig_wmi_op_version = ar->wmi.op_version; /* utf.bin firmware image does not advertise firmware features. Do * an ugly hack where we force the firmware features so that wmi.c * will use the correct WMI interface. */ memset(ar->fw_features, 0, sizeof(ar->fw_features)); - __set_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features); + ar->wmi.op_version = ATH10K_FW_WMI_OP_VERSION_10_1; ret = ath10k_hif_power_up(ar); if (ret) { @@ -224,6 +225,7 @@ err_fw_features: /* return the original firmware features */ memcpy(ar->fw_features, ar->testmode.orig_fw_features, sizeof(ar->fw_features)); + ar->wmi.op_version = ar->testmode.orig_wmi_op_version; release_firmware(ar->testmode.utf); ar->testmode.utf = NULL; @@ -250,6 +252,7 @@ static void __ath10k_tm_cmd_utf_stop(struct ath10k *ar) /* return the original firmware features */ memcpy(ar->fw_features, ar->testmode.orig_fw_features, sizeof(ar->fw_features)); + ar->wmi.op_version = ar->testmode.orig_wmi_op_version; release_firmware(ar->testmode.utf); ar->testmode.utf = NULL; diff --git a/drivers/net/wireless/ath/ath10k/thermal.c b/drivers/net/wireless/ath/ath10k/thermal.c new file mode 100644 index 000000000000..aede750809fe --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/thermal.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2014 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/device.h> +#include <linux/sysfs.h> +#include <linux/thermal.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include "core.h" +#include "debug.h" +#include "wmi-ops.h" + +static int ath10k_thermal_get_active_vifs(struct ath10k *ar, + enum wmi_vdev_type type) +{ + struct ath10k_vif *arvif; + int count = 0; + + lockdep_assert_held(&ar->conf_mutex); + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (!arvif->is_started) + continue; + + if (!arvif->is_up) + continue; + + if (arvif->vdev_type != type) + continue; + + count++; + } + return count; +} + +static int ath10k_thermal_get_max_dutycycle(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = ATH10K_QUIET_DUTY_CYCLE_MAX; + + return 0; +} + +static int ath10k_thermal_get_cur_dutycycle(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct ath10k *ar = cdev->devdata; + + mutex_lock(&ar->conf_mutex); + *state = ar->thermal.duty_cycle; + mutex_unlock(&ar->conf_mutex); + + return 0; +} + +static int ath10k_thermal_set_cur_dutycycle(struct thermal_cooling_device *cdev, + unsigned long duty_cycle) +{ + struct ath10k *ar = cdev->devdata; + u32 period, duration, enabled; + int num_bss, ret = 0; + + mutex_lock(&ar->conf_mutex); + if (ar->state != ATH10K_STATE_ON) { + ret = -ENETDOWN; + goto out; + } + + if (duty_cycle > ATH10K_QUIET_DUTY_CYCLE_MAX) { + ath10k_warn(ar, "duty cycle %ld is exceeding the limit %d\n", + duty_cycle, ATH10K_QUIET_DUTY_CYCLE_MAX); + ret = -EINVAL; + goto out; + } + /* TODO: Right now, thermal mitigation is handled only for single/multi + * vif AP mode. Since quiet param is not validated in STA mode, it needs + * to be investigated further to handle multi STA and multi-vif (AP+STA) + * mode properly. + */ + num_bss = ath10k_thermal_get_active_vifs(ar, WMI_VDEV_TYPE_AP); + if (!num_bss) { + ath10k_warn(ar, "no active AP interfaces\n"); + ret = -ENETDOWN; + goto out; + } + period = max(ATH10K_QUIET_PERIOD_MIN, + (ATH10K_QUIET_PERIOD_DEFAULT / num_bss)); + duration = (period * duty_cycle) / 100; + enabled = duration ? 1 : 0; + + ret = ath10k_wmi_pdev_set_quiet_mode(ar, period, duration, + ATH10K_QUIET_START_OFFSET, + enabled); + if (ret) { + ath10k_warn(ar, "failed to set quiet mode period %u duarion %u enabled %u ret %d\n", + period, duration, enabled, ret); + goto out; + } + ar->thermal.duty_cycle = duty_cycle; +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +static struct thermal_cooling_device_ops ath10k_thermal_ops = { + .get_max_state = ath10k_thermal_get_max_dutycycle, + .get_cur_state = ath10k_thermal_get_cur_dutycycle, + .set_cur_state = ath10k_thermal_set_cur_dutycycle, +}; + +static ssize_t ath10k_thermal_show_temp(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ath10k *ar = dev_get_drvdata(dev); + int ret, temperature; + + mutex_lock(&ar->conf_mutex); + + /* Can't get temperature when the card is off */ + if (ar->state != ATH10K_STATE_ON) { + ret = -ENETDOWN; + goto out; + } + + reinit_completion(&ar->thermal.wmi_sync); + ret = ath10k_wmi_pdev_get_temperature(ar); + if (ret) { + ath10k_warn(ar, "failed to read temperature %d\n", ret); + goto out; + } + + if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) { + ret = -ESHUTDOWN; + goto out; + } + + ret = wait_for_completion_timeout(&ar->thermal.wmi_sync, + ATH10K_THERMAL_SYNC_TIMEOUT_HZ); + if (ret == 0) { + ath10k_warn(ar, "failed to synchronize thermal read\n"); + ret = -ETIMEDOUT; + goto out; + } + + spin_lock_bh(&ar->data_lock); + temperature = ar->thermal.temperature; + spin_unlock_bh(&ar->data_lock); + + /* display in millidegree celcius */ + ret = snprintf(buf, PAGE_SIZE, "%d\n", temperature * 1000); +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + +void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature) +{ + spin_lock_bh(&ar->data_lock); + ar->thermal.temperature = temperature; + spin_unlock_bh(&ar->data_lock); + complete(&ar->thermal.wmi_sync); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ath10k_thermal_show_temp, + NULL, 0); + +static struct attribute *ath10k_hwmon_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(ath10k_hwmon); + +int ath10k_thermal_register(struct ath10k *ar) +{ + struct thermal_cooling_device *cdev; + struct device *hwmon_dev; + int ret; + + cdev = thermal_cooling_device_register("ath10k_thermal", ar, + &ath10k_thermal_ops); + + if (IS_ERR(cdev)) { + ath10k_err(ar, "failed to setup thermal device result: %ld\n", + PTR_ERR(cdev)); + return -EINVAL; + } + + ret = sysfs_create_link(&ar->dev->kobj, &cdev->device.kobj, + "cooling_device"); + if (ret) { + ath10k_err(ar, "failed to create thermal symlink\n"); + goto err_cooling_destroy; + } + + ar->thermal.cdev = cdev; + + /* Do not register hwmon device when temperature reading is not + * supported by firmware + */ + if (ar->wmi.op_version != ATH10K_FW_WMI_OP_VERSION_10_2_4) + return 0; + + /* Avoid linking error on devm_hwmon_device_register_with_groups, I + * guess linux/hwmon.h is missing proper stubs. */ + if (!config_enabled(CONFIG_HWMON)) + return 0; + + hwmon_dev = devm_hwmon_device_register_with_groups(ar->dev, + "ath10k_hwmon", ar, + ath10k_hwmon_groups); + if (IS_ERR(hwmon_dev)) { + ath10k_err(ar, "failed to register hwmon device: %ld\n", + PTR_ERR(hwmon_dev)); + ret = -EINVAL; + goto err_remove_link; + } + return 0; + +err_remove_link: + sysfs_remove_link(&ar->dev->kobj, "thermal_sensor"); +err_cooling_destroy: + thermal_cooling_device_unregister(cdev); + return ret; +} + +void ath10k_thermal_unregister(struct ath10k *ar) +{ + thermal_cooling_device_unregister(ar->thermal.cdev); + sysfs_remove_link(&ar->dev->kobj, "cooling_device"); +} diff --git a/drivers/net/wireless/ath/ath10k/thermal.h b/drivers/net/wireless/ath/ath10k/thermal.h new file mode 100644 index 000000000000..bccc17ae0fde --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/thermal.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2014 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef _THERMAL_ +#define _THERMAL_ + +#define ATH10K_QUIET_PERIOD_DEFAULT 100 +#define ATH10K_QUIET_PERIOD_MIN 25 +#define ATH10K_QUIET_START_OFFSET 10 +#define ATH10K_QUIET_DUTY_CYCLE_MAX 70 +#define ATH10K_HWMON_NAME_LEN 15 +#define ATH10K_THERMAL_SYNC_TIMEOUT_HZ (5*HZ) + +struct ath10k_thermal { + struct thermal_cooling_device *cdev; + struct completion wmi_sync; + + /* protected by conf_mutex */ + u32 duty_cycle; + /* temperature value in Celcius degree + * protected by data_lock + */ + int temperature; +}; + +#ifdef CONFIG_THERMAL +int ath10k_thermal_register(struct ath10k *ar); +void ath10k_thermal_unregister(struct ath10k *ar); +void ath10k_thermal_event_temperature(struct ath10k *ar, int temperature); +#else +static inline int ath10k_thermal_register(struct ath10k *ar) +{ + return 0; +} + +static inline void ath10k_thermal_unregister(struct ath10k *ar) +{ +} + +static inline void ath10k_thermal_event_temperature(struct ath10k *ar, + int temperature) +{ +} + +#endif +#endif /* _THERMAL_ */ diff --git a/drivers/net/wireless/ath/ath10k/trace.h b/drivers/net/wireless/ath/ath10k/trace.h index b289378b6e3e..5407887380ab 100644 --- a/drivers/net/wireless/ath/ath10k/trace.h +++ b/drivers/net/wireless/ath/ath10k/trace.h @@ -453,6 +453,74 @@ TRACE_EVENT(ath10k_htt_rx_desc, ) ); +TRACE_EVENT(ath10k_wmi_diag_container, + TP_PROTO(struct ath10k *ar, + u8 type, + u32 timestamp, + u32 code, + u16 len, + const void *data), + + TP_ARGS(ar, type, timestamp, code, len, data), + + TP_STRUCT__entry( + __string(device, dev_name(ar->dev)) + __string(driver, dev_driver_string(ar->dev)) + __field(u8, type) + __field(u32, timestamp) + __field(u32, code) + __field(u16, len) + __dynamic_array(u8, data, len) + ), + + TP_fast_assign( + __assign_str(device, dev_name(ar->dev)); + __assign_str(driver, dev_driver_string(ar->dev)); + __entry->type = type; + __entry->timestamp = timestamp; + __entry->code = code; + __entry->len = len; + memcpy(__get_dynamic_array(data), data, len); + ), + + TP_printk( + "%s %s diag container type %hhu timestamp %u code %u len %d", + __get_str(driver), + __get_str(device), + __entry->type, + __entry->timestamp, + __entry->code, + __entry->len + ) +); + +TRACE_EVENT(ath10k_wmi_diag, + TP_PROTO(struct ath10k *ar, const void *data, size_t len), + + TP_ARGS(ar, data, len), + + TP_STRUCT__entry( + __string(device, dev_name(ar->dev)) + __string(driver, dev_driver_string(ar->dev)) + __field(u16, len) + __dynamic_array(u8, data, len) + ), + + TP_fast_assign( + __assign_str(device, dev_name(ar->dev)); + __assign_str(driver, dev_driver_string(ar->dev)); + __entry->len = len; + memcpy(__get_dynamic_array(data), data, len); + ), + + TP_printk( + "%s %s tlv diag len %d", + __get_str(driver), + __get_str(device), + __entry->len + ) +); + #endif /* _TRACE_H_ || TRACE_HEADER_MULTI_READ*/ /* we don't want to use include/trace/events */ diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index 7579de8e7a8c..3f00cec8aef5 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -64,7 +64,13 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, return; } - msdu = htt->pending_tx[tx_done->msdu_id]; + msdu = idr_find(&htt->pending_tx, tx_done->msdu_id); + if (!msdu) { + ath10k_warn(ar, "received tx completion for invalid msdu_id: %d\n", + tx_done->msdu_id); + return; + } + skb_cb = ATH10K_SKB_CB(msdu); dma_unmap_single(dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); @@ -95,7 +101,6 @@ void ath10k_txrx_tx_unref(struct ath10k_htt *htt, /* we do not own the msdu anymore */ exit: - htt->pending_tx[tx_done->msdu_id] = NULL; ath10k_htt_tx_free_msdu_id(htt, tx_done->msdu_id); __ath10k_htt_tx_dec_pending(htt); if (htt->num_pending_tx == 0) diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h new file mode 100644 index 000000000000..04dc4b9db04e --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h @@ -0,0 +1,1064 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2014 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WMI_OPS_H_ +#define _WMI_OPS_H_ + +struct ath10k; +struct sk_buff; + +struct wmi_ops { + void (*rx)(struct ath10k *ar, struct sk_buff *skb); + void (*map_svc)(const __le32 *in, unsigned long *out, size_t len); + + int (*pull_scan)(struct ath10k *ar, struct sk_buff *skb, + struct wmi_scan_ev_arg *arg); + int (*pull_mgmt_rx)(struct ath10k *ar, struct sk_buff *skb, + struct wmi_mgmt_rx_ev_arg *arg); + int (*pull_ch_info)(struct ath10k *ar, struct sk_buff *skb, + struct wmi_ch_info_ev_arg *arg); + int (*pull_vdev_start)(struct ath10k *ar, struct sk_buff *skb, + struct wmi_vdev_start_ev_arg *arg); + int (*pull_peer_kick)(struct ath10k *ar, struct sk_buff *skb, + struct wmi_peer_kick_ev_arg *arg); + int (*pull_swba)(struct ath10k *ar, struct sk_buff *skb, + struct wmi_swba_ev_arg *arg); + int (*pull_phyerr)(struct ath10k *ar, struct sk_buff *skb, + struct wmi_phyerr_ev_arg *arg); + int (*pull_svc_rdy)(struct ath10k *ar, struct sk_buff *skb, + struct wmi_svc_rdy_ev_arg *arg); + int (*pull_rdy)(struct ath10k *ar, struct sk_buff *skb, + struct wmi_rdy_ev_arg *arg); + int (*pull_fw_stats)(struct ath10k *ar, struct sk_buff *skb, + struct ath10k_fw_stats *stats); + + struct sk_buff *(*gen_pdev_suspend)(struct ath10k *ar, u32 suspend_opt); + struct sk_buff *(*gen_pdev_resume)(struct ath10k *ar); + struct sk_buff *(*gen_pdev_set_rd)(struct ath10k *ar, u16 rd, u16 rd2g, + u16 rd5g, u16 ctl2g, u16 ctl5g, + enum wmi_dfs_region dfs_reg); + struct sk_buff *(*gen_pdev_set_param)(struct ath10k *ar, u32 id, + u32 value); + struct sk_buff *(*gen_init)(struct ath10k *ar); + struct sk_buff *(*gen_start_scan)(struct ath10k *ar, + const struct wmi_start_scan_arg *arg); + struct sk_buff *(*gen_stop_scan)(struct ath10k *ar, + const struct wmi_stop_scan_arg *arg); + struct sk_buff *(*gen_vdev_create)(struct ath10k *ar, u32 vdev_id, + enum wmi_vdev_type type, + enum wmi_vdev_subtype subtype, + const u8 macaddr[ETH_ALEN]); + struct sk_buff *(*gen_vdev_delete)(struct ath10k *ar, u32 vdev_id); + struct sk_buff *(*gen_vdev_start)(struct ath10k *ar, + const struct wmi_vdev_start_request_arg *arg, + bool restart); + struct sk_buff *(*gen_vdev_stop)(struct ath10k *ar, u32 vdev_id); + struct sk_buff *(*gen_vdev_up)(struct ath10k *ar, u32 vdev_id, u32 aid, + const u8 *bssid); + struct sk_buff *(*gen_vdev_down)(struct ath10k *ar, u32 vdev_id); + struct sk_buff *(*gen_vdev_set_param)(struct ath10k *ar, u32 vdev_id, + u32 param_id, u32 param_value); + struct sk_buff *(*gen_vdev_install_key)(struct ath10k *ar, + const struct wmi_vdev_install_key_arg *arg); + struct sk_buff *(*gen_vdev_spectral_conf)(struct ath10k *ar, + const struct wmi_vdev_spectral_conf_arg *arg); + struct sk_buff *(*gen_vdev_spectral_enable)(struct ath10k *ar, u32 vdev_id, + u32 trigger, u32 enable); + struct sk_buff *(*gen_vdev_wmm_conf)(struct ath10k *ar, u32 vdev_id, + const struct wmi_wmm_params_all_arg *arg); + struct sk_buff *(*gen_peer_create)(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]); + struct sk_buff *(*gen_peer_delete)(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]); + struct sk_buff *(*gen_peer_flush)(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN], + u32 tid_bitmap); + struct sk_buff *(*gen_peer_set_param)(struct ath10k *ar, u32 vdev_id, + const u8 *peer_addr, + enum wmi_peer_param param_id, + u32 param_value); + struct sk_buff *(*gen_peer_assoc)(struct ath10k *ar, + const struct wmi_peer_assoc_complete_arg *arg); + struct sk_buff *(*gen_set_psmode)(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_ps_mode psmode); + struct sk_buff *(*gen_set_sta_ps)(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_powersave_param param_id, + u32 value); + struct sk_buff *(*gen_set_ap_ps)(struct ath10k *ar, u32 vdev_id, + const u8 *mac, + enum wmi_ap_ps_peer_param param_id, + u32 value); + struct sk_buff *(*gen_scan_chan_list)(struct ath10k *ar, + const struct wmi_scan_chan_list_arg *arg); + struct sk_buff *(*gen_beacon_dma)(struct ath10k *ar, u32 vdev_id, + const void *bcn, size_t bcn_len, + u32 bcn_paddr, bool dtim_zero, + bool deliver_cab); + struct sk_buff *(*gen_pdev_set_wmm)(struct ath10k *ar, + const struct wmi_wmm_params_all_arg *arg); + struct sk_buff *(*gen_request_stats)(struct ath10k *ar, + enum wmi_stats_id stats_id); + struct sk_buff *(*gen_force_fw_hang)(struct ath10k *ar, + enum wmi_force_fw_hang_type type, + u32 delay_ms); + struct sk_buff *(*gen_mgmt_tx)(struct ath10k *ar, struct sk_buff *skb); + struct sk_buff *(*gen_dbglog_cfg)(struct ath10k *ar, u32 module_enable, + u32 log_level); + struct sk_buff *(*gen_pktlog_enable)(struct ath10k *ar, u32 filter); + struct sk_buff *(*gen_pktlog_disable)(struct ath10k *ar); + struct sk_buff *(*gen_pdev_set_quiet_mode)(struct ath10k *ar, + u32 period, u32 duration, + u32 next_offset, + u32 enabled); + struct sk_buff *(*gen_pdev_get_temperature)(struct ath10k *ar); + struct sk_buff *(*gen_addba_clear_resp)(struct ath10k *ar, u32 vdev_id, + const u8 *mac); + struct sk_buff *(*gen_addba_send)(struct ath10k *ar, u32 vdev_id, + const u8 *mac, u32 tid, u32 buf_size); + struct sk_buff *(*gen_addba_set_resp)(struct ath10k *ar, u32 vdev_id, + const u8 *mac, u32 tid, + u32 status); + struct sk_buff *(*gen_delba_send)(struct ath10k *ar, u32 vdev_id, + const u8 *mac, u32 tid, u32 initiator, + u32 reason); + struct sk_buff *(*gen_bcn_tmpl)(struct ath10k *ar, u32 vdev_id, + u32 tim_ie_offset, struct sk_buff *bcn, + u32 prb_caps, u32 prb_erp, + void *prb_ies, size_t prb_ies_len); + struct sk_buff *(*gen_prb_tmpl)(struct ath10k *ar, u32 vdev_id, + struct sk_buff *bcn); + struct sk_buff *(*gen_p2p_go_bcn_ie)(struct ath10k *ar, u32 vdev_id, + const u8 *p2p_ie); + struct sk_buff *(*gen_vdev_sta_uapsd)(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN], + const struct wmi_sta_uapsd_auto_trig_arg *args, + u32 num_ac); + struct sk_buff *(*gen_sta_keepalive)(struct ath10k *ar, + const struct wmi_sta_keepalive_arg *arg); +}; + +int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id); + +static inline int +ath10k_wmi_rx(struct ath10k *ar, struct sk_buff *skb) +{ + if (WARN_ON_ONCE(!ar->wmi.ops->rx)) + return -EOPNOTSUPP; + + ar->wmi.ops->rx(ar, skb); + return 0; +} + +static inline int +ath10k_wmi_map_svc(struct ath10k *ar, const __le32 *in, unsigned long *out, + size_t len) +{ + if (!ar->wmi.ops->map_svc) + return -EOPNOTSUPP; + + ar->wmi.ops->map_svc(in, out, len); + return 0; +} + +static inline int +ath10k_wmi_pull_scan(struct ath10k *ar, struct sk_buff *skb, + struct wmi_scan_ev_arg *arg) +{ + if (!ar->wmi.ops->pull_scan) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_scan(ar, skb, arg); +} + +static inline int +ath10k_wmi_pull_mgmt_rx(struct ath10k *ar, struct sk_buff *skb, + struct wmi_mgmt_rx_ev_arg *arg) +{ + if (!ar->wmi.ops->pull_mgmt_rx) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_mgmt_rx(ar, skb, arg); +} + +static inline int +ath10k_wmi_pull_ch_info(struct ath10k *ar, struct sk_buff *skb, + struct wmi_ch_info_ev_arg *arg) +{ + if (!ar->wmi.ops->pull_ch_info) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_ch_info(ar, skb, arg); +} + +static inline int +ath10k_wmi_pull_vdev_start(struct ath10k *ar, struct sk_buff *skb, + struct wmi_vdev_start_ev_arg *arg) +{ + if (!ar->wmi.ops->pull_vdev_start) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_vdev_start(ar, skb, arg); +} + +static inline int +ath10k_wmi_pull_peer_kick(struct ath10k *ar, struct sk_buff *skb, + struct wmi_peer_kick_ev_arg *arg) +{ + if (!ar->wmi.ops->pull_peer_kick) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_peer_kick(ar, skb, arg); +} + +static inline int +ath10k_wmi_pull_swba(struct ath10k *ar, struct sk_buff *skb, + struct wmi_swba_ev_arg *arg) +{ + if (!ar->wmi.ops->pull_swba) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_swba(ar, skb, arg); +} + +static inline int +ath10k_wmi_pull_phyerr(struct ath10k *ar, struct sk_buff *skb, + struct wmi_phyerr_ev_arg *arg) +{ + if (!ar->wmi.ops->pull_phyerr) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_phyerr(ar, skb, arg); +} + +static inline int +ath10k_wmi_pull_svc_rdy(struct ath10k *ar, struct sk_buff *skb, + struct wmi_svc_rdy_ev_arg *arg) +{ + if (!ar->wmi.ops->pull_svc_rdy) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_svc_rdy(ar, skb, arg); +} + +static inline int +ath10k_wmi_pull_rdy(struct ath10k *ar, struct sk_buff *skb, + struct wmi_rdy_ev_arg *arg) +{ + if (!ar->wmi.ops->pull_rdy) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_rdy(ar, skb, arg); +} + +static inline int +ath10k_wmi_pull_fw_stats(struct ath10k *ar, struct sk_buff *skb, + struct ath10k_fw_stats *stats) +{ + if (!ar->wmi.ops->pull_fw_stats) + return -EOPNOTSUPP; + + return ar->wmi.ops->pull_fw_stats(ar, skb, stats); +} + +static inline int +ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(msdu); + struct sk_buff *skb; + int ret; + + if (!ar->wmi.ops->gen_mgmt_tx) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_mgmt_tx(ar, msdu); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + ret = ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->mgmt_tx_cmdid); + if (ret) + return ret; + + /* FIXME There's no ACK event for Management Tx. This probably + * shouldn't be called here either. */ + info->flags |= IEEE80211_TX_STAT_ACK; + ieee80211_tx_status_irqsafe(ar->hw, msdu); + + return 0; +} + +static inline int +ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g, u16 rd5g, + u16 ctl2g, u16 ctl5g, + enum wmi_dfs_region dfs_reg) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pdev_set_rd) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pdev_set_rd(ar, rd, rd2g, rd5g, ctl2g, ctl5g, + dfs_reg); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_set_regdomain_cmdid); +} + +static inline int +ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pdev_suspend) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pdev_suspend(ar, suspend_opt); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_suspend_cmdid); +} + +static inline int +ath10k_wmi_pdev_resume_target(struct ath10k *ar) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pdev_resume) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pdev_resume(ar); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_resume_cmdid); +} + +static inline int +ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pdev_set_param) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pdev_set_param(ar, id, value); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_set_param_cmdid); +} + +static inline int +ath10k_wmi_cmd_init(struct ath10k *ar) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_init) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_init(ar); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->init_cmdid); +} + +static inline int +ath10k_wmi_start_scan(struct ath10k *ar, + const struct wmi_start_scan_arg *arg) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_start_scan) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_start_scan(ar, arg); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->start_scan_cmdid); +} + +static inline int +ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_stop_scan) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_stop_scan(ar, arg); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->stop_scan_cmdid); +} + +static inline int +ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id, + enum wmi_vdev_type type, + enum wmi_vdev_subtype subtype, + const u8 macaddr[ETH_ALEN]) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_vdev_create) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_vdev_create(ar, vdev_id, type, subtype, macaddr); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_create_cmdid); +} + +static inline int +ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_vdev_delete) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_vdev_delete(ar, vdev_id); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_delete_cmdid); +} + +static inline int +ath10k_wmi_vdev_start(struct ath10k *ar, + const struct wmi_vdev_start_request_arg *arg) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_vdev_start) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_vdev_start(ar, arg, false); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->vdev_start_request_cmdid); +} + +static inline int +ath10k_wmi_vdev_restart(struct ath10k *ar, + const struct wmi_vdev_start_request_arg *arg) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_vdev_start) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_vdev_start(ar, arg, true); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->vdev_restart_request_cmdid); +} + +static inline int +ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_vdev_stop) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_vdev_stop(ar, vdev_id); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_stop_cmdid); +} + +static inline int +ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_vdev_up) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_vdev_up(ar, vdev_id, aid, bssid); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_up_cmdid); +} + +static inline int +ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_vdev_down) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_vdev_down(ar, vdev_id); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_down_cmdid); +} + +static inline int +ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, u32 param_id, + u32 param_value) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_vdev_set_param) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_vdev_set_param(ar, vdev_id, param_id, + param_value); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_set_param_cmdid); +} + +static inline int +ath10k_wmi_vdev_install_key(struct ath10k *ar, + const struct wmi_vdev_install_key_arg *arg) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_vdev_install_key) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_vdev_install_key(ar, arg); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->vdev_install_key_cmdid); +} + +static inline int +ath10k_wmi_vdev_spectral_conf(struct ath10k *ar, + const struct wmi_vdev_spectral_conf_arg *arg) +{ + struct sk_buff *skb; + u32 cmd_id; + + skb = ar->wmi.ops->gen_vdev_spectral_conf(ar, arg); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + cmd_id = ar->wmi.cmd->vdev_spectral_scan_configure_cmdid; + return ath10k_wmi_cmd_send(ar, skb, cmd_id); +} + +static inline int +ath10k_wmi_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id, u32 trigger, + u32 enable) +{ + struct sk_buff *skb; + u32 cmd_id; + + skb = ar->wmi.ops->gen_vdev_spectral_enable(ar, vdev_id, trigger, + enable); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + cmd_id = ar->wmi.cmd->vdev_spectral_scan_enable_cmdid; + return ath10k_wmi_cmd_send(ar, skb, cmd_id); +} + +static inline int +ath10k_wmi_vdev_sta_uapsd(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN], + const struct wmi_sta_uapsd_auto_trig_arg *args, + u32 num_ac) +{ + struct sk_buff *skb; + u32 cmd_id; + + if (!ar->wmi.ops->gen_vdev_sta_uapsd) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_vdev_sta_uapsd(ar, vdev_id, peer_addr, args, + num_ac); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + cmd_id = ar->wmi.cmd->sta_uapsd_auto_trig_cmdid; + return ath10k_wmi_cmd_send(ar, skb, cmd_id); +} + +static inline int +ath10k_wmi_vdev_wmm_conf(struct ath10k *ar, u32 vdev_id, + const struct wmi_wmm_params_all_arg *arg) +{ + struct sk_buff *skb; + u32 cmd_id; + + skb = ar->wmi.ops->gen_vdev_wmm_conf(ar, vdev_id, arg); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + cmd_id = ar->wmi.cmd->vdev_set_wmm_params_cmdid; + return ath10k_wmi_cmd_send(ar, skb, cmd_id); +} + +static inline int +ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_peer_create) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_peer_create(ar, vdev_id, peer_addr); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_create_cmdid); +} + +static inline int +ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_peer_delete) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_peer_delete(ar, vdev_id, peer_addr); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_delete_cmdid); +} + +static inline int +ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN], u32 tid_bitmap) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_peer_flush) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_peer_flush(ar, vdev_id, peer_addr, tid_bitmap); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_flush_tids_cmdid); +} + +static inline int +ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, const u8 *peer_addr, + enum wmi_peer_param param_id, u32 param_value) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_peer_set_param) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_peer_set_param(ar, vdev_id, peer_addr, param_id, + param_value); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_set_param_cmdid); +} + +static inline int +ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_ps_mode psmode) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_set_psmode) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_set_psmode(ar, vdev_id, psmode); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->sta_powersave_mode_cmdid); +} + +static inline int +ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_powersave_param param_id, u32 value) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_set_sta_ps) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_set_sta_ps(ar, vdev_id, param_id, value); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->sta_powersave_param_cmdid); +} + +static inline int +ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, + enum wmi_ap_ps_peer_param param_id, u32 value) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_set_ap_ps) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_set_ap_ps(ar, vdev_id, mac, param_id, value); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->ap_ps_peer_param_cmdid); +} + +static inline int +ath10k_wmi_scan_chan_list(struct ath10k *ar, + const struct wmi_scan_chan_list_arg *arg) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_scan_chan_list) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_scan_chan_list(ar, arg); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->scan_chan_list_cmdid); +} + +static inline int +ath10k_wmi_peer_assoc(struct ath10k *ar, + const struct wmi_peer_assoc_complete_arg *arg) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_peer_assoc) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_peer_assoc(ar, arg); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_assoc_cmdid); +} + +static inline int +ath10k_wmi_beacon_send_ref_nowait(struct ath10k *ar, u32 vdev_id, + const void *bcn, size_t bcn_len, + u32 bcn_paddr, bool dtim_zero, + bool deliver_cab) +{ + struct sk_buff *skb; + int ret; + + if (!ar->wmi.ops->gen_beacon_dma) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_beacon_dma(ar, vdev_id, bcn, bcn_len, bcn_paddr, + dtim_zero, deliver_cab); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + ret = ath10k_wmi_cmd_send_nowait(ar, skb, + ar->wmi.cmd->pdev_send_bcn_cmdid); + if (ret) { + dev_kfree_skb(skb); + return ret; + } + + return 0; +} + +static inline int +ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, + const struct wmi_wmm_params_all_arg *arg) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pdev_set_wmm) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pdev_set_wmm(ar, arg); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_set_wmm_params_cmdid); +} + +static inline int +ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_request_stats) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_request_stats(ar, stats_id); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->request_stats_cmdid); +} + +static inline int +ath10k_wmi_force_fw_hang(struct ath10k *ar, + enum wmi_force_fw_hang_type type, u32 delay_ms) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_force_fw_hang) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_force_fw_hang(ar, type, delay_ms); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid); +} + +static inline int +ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable, u32 log_level) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_dbglog_cfg) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_dbglog_cfg(ar, module_enable, log_level); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->dbglog_cfg_cmdid); +} + +static inline int +ath10k_wmi_pdev_pktlog_enable(struct ath10k *ar, u32 filter) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pktlog_enable) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pktlog_enable(ar, filter); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_pktlog_enable_cmdid); +} + +static inline int +ath10k_wmi_pdev_pktlog_disable(struct ath10k *ar) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pktlog_disable) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pktlog_disable(ar); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_pktlog_disable_cmdid); +} + +static inline int +ath10k_wmi_pdev_set_quiet_mode(struct ath10k *ar, u32 period, u32 duration, + u32 next_offset, u32 enabled) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pdev_set_quiet_mode) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pdev_set_quiet_mode(ar, period, duration, + next_offset, enabled); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_set_quiet_mode_cmdid); +} + +static inline int +ath10k_wmi_pdev_get_temperature(struct ath10k *ar) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_pdev_get_temperature) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_pdev_get_temperature(ar); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->pdev_get_temperature_cmdid); +} + +static inline int +ath10k_wmi_addba_clear_resp(struct ath10k *ar, u32 vdev_id, const u8 *mac) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_addba_clear_resp) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_addba_clear_resp(ar, vdev_id, mac); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->addba_clear_resp_cmdid); +} + +static inline int +ath10k_wmi_addba_send(struct ath10k *ar, u32 vdev_id, const u8 *mac, + u32 tid, u32 buf_size) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_addba_send) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_addba_send(ar, vdev_id, mac, tid, buf_size); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->addba_send_cmdid); +} + +static inline int +ath10k_wmi_addba_set_resp(struct ath10k *ar, u32 vdev_id, const u8 *mac, + u32 tid, u32 status) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_addba_set_resp) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_addba_set_resp(ar, vdev_id, mac, tid, status); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->addba_set_resp_cmdid); +} + +static inline int +ath10k_wmi_delba_send(struct ath10k *ar, u32 vdev_id, const u8 *mac, + u32 tid, u32 initiator, u32 reason) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_delba_send) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_delba_send(ar, vdev_id, mac, tid, initiator, + reason); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, + ar->wmi.cmd->delba_send_cmdid); +} + +static inline int +ath10k_wmi_bcn_tmpl(struct ath10k *ar, u32 vdev_id, u32 tim_ie_offset, + struct sk_buff *bcn, u32 prb_caps, u32 prb_erp, + void *prb_ies, size_t prb_ies_len) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_bcn_tmpl) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_bcn_tmpl(ar, vdev_id, tim_ie_offset, bcn, + prb_caps, prb_erp, prb_ies, + prb_ies_len); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->bcn_tmpl_cmdid); +} + +static inline int +ath10k_wmi_prb_tmpl(struct ath10k *ar, u32 vdev_id, struct sk_buff *prb) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_prb_tmpl) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_prb_tmpl(ar, vdev_id, prb); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->prb_tmpl_cmdid); +} + +static inline int +ath10k_wmi_p2p_go_bcn_ie(struct ath10k *ar, u32 vdev_id, const u8 *p2p_ie) +{ + struct sk_buff *skb; + + if (!ar->wmi.ops->gen_p2p_go_bcn_ie) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_p2p_go_bcn_ie(ar, vdev_id, p2p_ie); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->p2p_go_set_beacon_ie); +} + +static inline int +ath10k_wmi_sta_keepalive(struct ath10k *ar, + const struct wmi_sta_keepalive_arg *arg) +{ + struct sk_buff *skb; + u32 cmd_id; + + if (!ar->wmi.ops->gen_sta_keepalive) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_sta_keepalive(ar, arg); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + cmd_id = ar->wmi.cmd->sta_keepalive_cmd; + return ath10k_wmi_cmd_send(ar, skb, cmd_id); +} + +#endif diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c new file mode 100644 index 000000000000..71614ba1b145 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -0,0 +1,2696 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2014 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include "core.h" +#include "debug.h" +#include "hw.h" +#include "wmi.h" +#include "wmi-ops.h" +#include "wmi-tlv.h" + +/***************/ +/* TLV helpers */ +/**************/ + +struct wmi_tlv_policy { + size_t min_len; +}; + +static const struct wmi_tlv_policy wmi_tlv_policies[] = { + [WMI_TLV_TAG_ARRAY_BYTE] + = { .min_len = sizeof(u8) }, + [WMI_TLV_TAG_ARRAY_UINT32] + = { .min_len = sizeof(u32) }, + [WMI_TLV_TAG_STRUCT_SCAN_EVENT] + = { .min_len = sizeof(struct wmi_scan_event) }, + [WMI_TLV_TAG_STRUCT_MGMT_RX_HDR] + = { .min_len = sizeof(struct wmi_tlv_mgmt_rx_ev) }, + [WMI_TLV_TAG_STRUCT_CHAN_INFO_EVENT] + = { .min_len = sizeof(struct wmi_chan_info_event) }, + [WMI_TLV_TAG_STRUCT_VDEV_START_RESPONSE_EVENT] + = { .min_len = sizeof(struct wmi_vdev_start_response_event) }, + [WMI_TLV_TAG_STRUCT_PEER_STA_KICKOUT_EVENT] + = { .min_len = sizeof(struct wmi_peer_sta_kickout_event) }, + [WMI_TLV_TAG_STRUCT_HOST_SWBA_EVENT] + = { .min_len = sizeof(struct wmi_host_swba_event) }, + [WMI_TLV_TAG_STRUCT_TIM_INFO] + = { .min_len = sizeof(struct wmi_tim_info) }, + [WMI_TLV_TAG_STRUCT_P2P_NOA_INFO] + = { .min_len = sizeof(struct wmi_p2p_noa_info) }, + [WMI_TLV_TAG_STRUCT_SERVICE_READY_EVENT] + = { .min_len = sizeof(struct wmi_tlv_svc_rdy_ev) }, + [WMI_TLV_TAG_STRUCT_HAL_REG_CAPABILITIES] + = { .min_len = sizeof(struct hal_reg_capabilities) }, + [WMI_TLV_TAG_STRUCT_WLAN_HOST_MEM_REQ] + = { .min_len = sizeof(struct wlan_host_mem_req) }, + [WMI_TLV_TAG_STRUCT_READY_EVENT] + = { .min_len = sizeof(struct wmi_tlv_rdy_ev) }, + [WMI_TLV_TAG_STRUCT_OFFLOAD_BCN_TX_STATUS_EVENT] + = { .min_len = sizeof(struct wmi_tlv_bcn_tx_status_ev) }, + [WMI_TLV_TAG_STRUCT_DIAG_DATA_CONTAINER_EVENT] + = { .min_len = sizeof(struct wmi_tlv_diag_data_ev) }, +}; + +static int +ath10k_wmi_tlv_iter(struct ath10k *ar, const void *ptr, size_t len, + int (*iter)(struct ath10k *ar, u16 tag, u16 len, + const void *ptr, void *data), + void *data) +{ + const void *begin = ptr; + const struct wmi_tlv *tlv; + u16 tlv_tag, tlv_len; + int ret; + + while (len > 0) { + if (len < sizeof(*tlv)) { + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi tlv parse failure at byte %zd (%zu bytes left, %zu expected)\n", + ptr - begin, len, sizeof(*tlv)); + return -EINVAL; + } + + tlv = ptr; + tlv_tag = __le16_to_cpu(tlv->tag); + tlv_len = __le16_to_cpu(tlv->len); + ptr += sizeof(*tlv); + len -= sizeof(*tlv); + + if (tlv_len > len) { + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi tlv parse failure of tag %hhu at byte %zd (%zu bytes left, %hhu expected)\n", + tlv_tag, ptr - begin, len, tlv_len); + return -EINVAL; + } + + if (tlv_tag < ARRAY_SIZE(wmi_tlv_policies) && + wmi_tlv_policies[tlv_tag].min_len && + wmi_tlv_policies[tlv_tag].min_len > tlv_len) { + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi tlv parse failure of tag %hhu at byte %zd (%hhu bytes is less than min length %zu)\n", + tlv_tag, ptr - begin, tlv_len, + wmi_tlv_policies[tlv_tag].min_len); + return -EINVAL; + } + + ret = iter(ar, tlv_tag, tlv_len, ptr, data); + if (ret) + return ret; + + ptr += tlv_len; + len -= tlv_len; + } + + return 0; +} + +static int ath10k_wmi_tlv_iter_parse(struct ath10k *ar, u16 tag, u16 len, + const void *ptr, void *data) +{ + const void **tb = data; + + if (tag < WMI_TLV_TAG_MAX) + tb[tag] = ptr; + + return 0; +} + +static int ath10k_wmi_tlv_parse(struct ath10k *ar, const void **tb, + const void *ptr, size_t len) +{ + return ath10k_wmi_tlv_iter(ar, ptr, len, ath10k_wmi_tlv_iter_parse, + (void *)tb); +} + +static const void ** +ath10k_wmi_tlv_parse_alloc(struct ath10k *ar, const void *ptr, + size_t len, gfp_t gfp) +{ + const void **tb; + int ret; + + tb = kzalloc(sizeof(*tb) * WMI_TLV_TAG_MAX, gfp); + if (!tb) + return ERR_PTR(-ENOMEM); + + ret = ath10k_wmi_tlv_parse(ar, tb, ptr, len); + if (ret) { + kfree(tb); + return ERR_PTR(ret); + } + + return tb; +} + +static u16 ath10k_wmi_tlv_len(const void *ptr) +{ + return __le16_to_cpu((((const struct wmi_tlv *)ptr) - 1)->len); +} + +/**************/ +/* TLV events */ +/**************/ +static int ath10k_wmi_tlv_event_bcn_tx_status(struct ath10k *ar, + struct sk_buff *skb) +{ + const void **tb; + const struct wmi_tlv_bcn_tx_status_ev *ev; + u32 vdev_id, tx_status; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_OFFLOAD_BCN_TX_STATUS_EVENT]; + if (!ev) { + kfree(tb); + return -EPROTO; + } + + tx_status = __le32_to_cpu(ev->tx_status); + vdev_id = __le32_to_cpu(ev->vdev_id); + + switch (tx_status) { + case WMI_TLV_BCN_TX_STATUS_OK: + break; + case WMI_TLV_BCN_TX_STATUS_XRETRY: + case WMI_TLV_BCN_TX_STATUS_DROP: + case WMI_TLV_BCN_TX_STATUS_FILTERED: + /* FIXME: It's probably worth telling mac80211 to stop the + * interface as it is crippled. + */ + ath10k_warn(ar, "received bcn tmpl tx status on vdev %i: %d", + vdev_id, tx_status); + break; + } + + kfree(tb); + return 0; +} + +static int ath10k_wmi_tlv_event_diag_data(struct ath10k *ar, + struct sk_buff *skb) +{ + const void **tb; + const struct wmi_tlv_diag_data_ev *ev; + const struct wmi_tlv_diag_item *item; + const void *data; + int ret, num_items, len; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_DIAG_DATA_CONTAINER_EVENT]; + data = tb[WMI_TLV_TAG_ARRAY_BYTE]; + if (!ev || !data) { + kfree(tb); + return -EPROTO; + } + + num_items = __le32_to_cpu(ev->num_items); + len = ath10k_wmi_tlv_len(data); + + while (num_items--) { + if (len == 0) + break; + if (len < sizeof(*item)) { + ath10k_warn(ar, "failed to parse diag data: can't fit item header\n"); + break; + } + + item = data; + + if (len < sizeof(*item) + __le16_to_cpu(item->len)) { + ath10k_warn(ar, "failed to parse diag data: item is too long\n"); + break; + } + + trace_ath10k_wmi_diag_container(ar, + item->type, + __le32_to_cpu(item->timestamp), + __le32_to_cpu(item->code), + __le16_to_cpu(item->len), + item->payload); + + len -= sizeof(*item); + len -= roundup(__le16_to_cpu(item->len), 4); + + data += sizeof(*item); + data += roundup(__le16_to_cpu(item->len), 4); + } + + if (num_items != -1 || len != 0) + ath10k_warn(ar, "failed to parse diag data event: num_items %d len %d\n", + num_items, len); + + kfree(tb); + return 0; +} + +static int ath10k_wmi_tlv_event_diag(struct ath10k *ar, + struct sk_buff *skb) +{ + const void **tb; + const void *data; + int ret, len; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + data = tb[WMI_TLV_TAG_ARRAY_BYTE]; + if (!data) { + kfree(tb); + return -EPROTO; + } + len = ath10k_wmi_tlv_len(data); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv diag event len %d\n", len); + trace_ath10k_wmi_diag(ar, data, len); + + kfree(tb); + return 0; +} + +/***********/ +/* TLV ops */ +/***********/ + +static void ath10k_wmi_tlv_op_rx(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_cmd_hdr *cmd_hdr; + enum wmi_tlv_event_id id; + + cmd_hdr = (struct wmi_cmd_hdr *)skb->data; + id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID); + + if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL) + return; + + trace_ath10k_wmi_event(ar, id, skb->data, skb->len); + + switch (id) { + case WMI_TLV_MGMT_RX_EVENTID: + ath10k_wmi_event_mgmt_rx(ar, skb); + /* mgmt_rx() owns the skb now! */ + return; + case WMI_TLV_SCAN_EVENTID: + ath10k_wmi_event_scan(ar, skb); + break; + case WMI_TLV_CHAN_INFO_EVENTID: + ath10k_wmi_event_chan_info(ar, skb); + break; + case WMI_TLV_ECHO_EVENTID: + ath10k_wmi_event_echo(ar, skb); + break; + case WMI_TLV_DEBUG_MESG_EVENTID: + ath10k_wmi_event_debug_mesg(ar, skb); + break; + case WMI_TLV_UPDATE_STATS_EVENTID: + ath10k_wmi_event_update_stats(ar, skb); + break; + case WMI_TLV_VDEV_START_RESP_EVENTID: + ath10k_wmi_event_vdev_start_resp(ar, skb); + break; + case WMI_TLV_VDEV_STOPPED_EVENTID: + ath10k_wmi_event_vdev_stopped(ar, skb); + break; + case WMI_TLV_PEER_STA_KICKOUT_EVENTID: + ath10k_wmi_event_peer_sta_kickout(ar, skb); + break; + case WMI_TLV_HOST_SWBA_EVENTID: + ath10k_wmi_event_host_swba(ar, skb); + break; + case WMI_TLV_TBTTOFFSET_UPDATE_EVENTID: + ath10k_wmi_event_tbttoffset_update(ar, skb); + break; + case WMI_TLV_PHYERR_EVENTID: + ath10k_wmi_event_phyerr(ar, skb); + break; + case WMI_TLV_ROAM_EVENTID: + ath10k_wmi_event_roam(ar, skb); + break; + case WMI_TLV_PROFILE_MATCH: + ath10k_wmi_event_profile_match(ar, skb); + break; + case WMI_TLV_DEBUG_PRINT_EVENTID: + ath10k_wmi_event_debug_print(ar, skb); + break; + case WMI_TLV_PDEV_QVIT_EVENTID: + ath10k_wmi_event_pdev_qvit(ar, skb); + break; + case WMI_TLV_WLAN_PROFILE_DATA_EVENTID: + ath10k_wmi_event_wlan_profile_data(ar, skb); + break; + case WMI_TLV_RTT_MEASUREMENT_REPORT_EVENTID: + ath10k_wmi_event_rtt_measurement_report(ar, skb); + break; + case WMI_TLV_TSF_MEASUREMENT_REPORT_EVENTID: + ath10k_wmi_event_tsf_measurement_report(ar, skb); + break; + case WMI_TLV_RTT_ERROR_REPORT_EVENTID: + ath10k_wmi_event_rtt_error_report(ar, skb); + break; + case WMI_TLV_WOW_WAKEUP_HOST_EVENTID: + ath10k_wmi_event_wow_wakeup_host(ar, skb); + break; + case WMI_TLV_DCS_INTERFERENCE_EVENTID: + ath10k_wmi_event_dcs_interference(ar, skb); + break; + case WMI_TLV_PDEV_TPC_CONFIG_EVENTID: + ath10k_wmi_event_pdev_tpc_config(ar, skb); + break; + case WMI_TLV_PDEV_FTM_INTG_EVENTID: + ath10k_wmi_event_pdev_ftm_intg(ar, skb); + break; + case WMI_TLV_GTK_OFFLOAD_STATUS_EVENTID: + ath10k_wmi_event_gtk_offload_status(ar, skb); + break; + case WMI_TLV_GTK_REKEY_FAIL_EVENTID: + ath10k_wmi_event_gtk_rekey_fail(ar, skb); + break; + case WMI_TLV_TX_DELBA_COMPLETE_EVENTID: + ath10k_wmi_event_delba_complete(ar, skb); + break; + case WMI_TLV_TX_ADDBA_COMPLETE_EVENTID: + ath10k_wmi_event_addba_complete(ar, skb); + break; + case WMI_TLV_VDEV_INSTALL_KEY_COMPLETE_EVENTID: + ath10k_wmi_event_vdev_install_key_complete(ar, skb); + break; + case WMI_TLV_SERVICE_READY_EVENTID: + ath10k_wmi_event_service_ready(ar, skb); + break; + case WMI_TLV_READY_EVENTID: + ath10k_wmi_event_ready(ar, skb); + break; + case WMI_TLV_OFFLOAD_BCN_TX_STATUS_EVENTID: + ath10k_wmi_tlv_event_bcn_tx_status(ar, skb); + break; + case WMI_TLV_DIAG_DATA_CONTAINER_EVENTID: + ath10k_wmi_tlv_event_diag_data(ar, skb); + break; + case WMI_TLV_DIAG_EVENTID: + ath10k_wmi_tlv_event_diag(ar, skb); + break; + default: + ath10k_warn(ar, "Unknown eventid: %d\n", id); + break; + } + + dev_kfree_skb(skb); +} + +static int ath10k_wmi_tlv_op_pull_scan_ev(struct ath10k *ar, + struct sk_buff *skb, + struct wmi_scan_ev_arg *arg) +{ + const void **tb; + const struct wmi_scan_event *ev; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_SCAN_EVENT]; + if (!ev) { + kfree(tb); + return -EPROTO; + } + + arg->event_type = ev->event_type; + arg->reason = ev->reason; + arg->channel_freq = ev->channel_freq; + arg->scan_req_id = ev->scan_req_id; + arg->scan_id = ev->scan_id; + arg->vdev_id = ev->vdev_id; + + kfree(tb); + return 0; +} + +static int ath10k_wmi_tlv_op_pull_mgmt_rx_ev(struct ath10k *ar, + struct sk_buff *skb, + struct wmi_mgmt_rx_ev_arg *arg) +{ + const void **tb; + const struct wmi_tlv_mgmt_rx_ev *ev; + const u8 *frame; + u32 msdu_len; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_MGMT_RX_HDR]; + frame = tb[WMI_TLV_TAG_ARRAY_BYTE]; + + if (!ev || !frame) { + kfree(tb); + return -EPROTO; + } + + arg->channel = ev->channel; + arg->buf_len = ev->buf_len; + arg->status = ev->status; + arg->snr = ev->snr; + arg->phy_mode = ev->phy_mode; + arg->rate = ev->rate; + + msdu_len = __le32_to_cpu(arg->buf_len); + + if (skb->len < (frame - skb->data) + msdu_len) { + kfree(tb); + return -EPROTO; + } + + /* shift the sk_buff to point to `frame` */ + skb_trim(skb, 0); + skb_put(skb, frame - skb->data); + skb_pull(skb, frame - skb->data); + skb_put(skb, msdu_len); + + kfree(tb); + return 0; +} + +static int ath10k_wmi_tlv_op_pull_ch_info_ev(struct ath10k *ar, + struct sk_buff *skb, + struct wmi_ch_info_ev_arg *arg) +{ + const void **tb; + const struct wmi_chan_info_event *ev; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_CHAN_INFO_EVENT]; + if (!ev) { + kfree(tb); + return -EPROTO; + } + + arg->err_code = ev->err_code; + arg->freq = ev->freq; + arg->cmd_flags = ev->cmd_flags; + arg->noise_floor = ev->noise_floor; + arg->rx_clear_count = ev->rx_clear_count; + arg->cycle_count = ev->cycle_count; + + kfree(tb); + return 0; +} + +static int +ath10k_wmi_tlv_op_pull_vdev_start_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_vdev_start_ev_arg *arg) +{ + const void **tb; + const struct wmi_vdev_start_response_event *ev; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_VDEV_START_RESPONSE_EVENT]; + if (!ev) { + kfree(tb); + return -EPROTO; + } + + skb_pull(skb, sizeof(*ev)); + arg->vdev_id = ev->vdev_id; + arg->req_id = ev->req_id; + arg->resp_type = ev->resp_type; + arg->status = ev->status; + + kfree(tb); + return 0; +} + +static int ath10k_wmi_tlv_op_pull_peer_kick_ev(struct ath10k *ar, + struct sk_buff *skb, + struct wmi_peer_kick_ev_arg *arg) +{ + const void **tb; + const struct wmi_peer_sta_kickout_event *ev; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_PEER_STA_KICKOUT_EVENT]; + if (!ev) { + kfree(tb); + return -EPROTO; + } + + arg->mac_addr = ev->peer_macaddr.addr; + + kfree(tb); + return 0; +} + +struct wmi_tlv_swba_parse { + const struct wmi_host_swba_event *ev; + bool tim_done; + bool noa_done; + size_t n_tim; + size_t n_noa; + struct wmi_swba_ev_arg *arg; +}; + +static int ath10k_wmi_tlv_swba_tim_parse(struct ath10k *ar, u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_swba_parse *swba = data; + + if (tag != WMI_TLV_TAG_STRUCT_TIM_INFO) + return -EPROTO; + + if (swba->n_tim >= ARRAY_SIZE(swba->arg->tim_info)) + return -ENOBUFS; + + swba->arg->tim_info[swba->n_tim++] = ptr; + return 0; +} + +static int ath10k_wmi_tlv_swba_noa_parse(struct ath10k *ar, u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_swba_parse *swba = data; + + if (tag != WMI_TLV_TAG_STRUCT_P2P_NOA_INFO) + return -EPROTO; + + if (swba->n_noa >= ARRAY_SIZE(swba->arg->noa_info)) + return -ENOBUFS; + + swba->arg->noa_info[swba->n_noa++] = ptr; + return 0; +} + +static int ath10k_wmi_tlv_swba_parse(struct ath10k *ar, u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_swba_parse *swba = data; + int ret; + + switch (tag) { + case WMI_TLV_TAG_STRUCT_HOST_SWBA_EVENT: + swba->ev = ptr; + break; + case WMI_TLV_TAG_ARRAY_STRUCT: + if (!swba->tim_done) { + swba->tim_done = true; + ret = ath10k_wmi_tlv_iter(ar, ptr, len, + ath10k_wmi_tlv_swba_tim_parse, + swba); + if (ret) + return ret; + } else if (!swba->noa_done) { + swba->noa_done = true; + ret = ath10k_wmi_tlv_iter(ar, ptr, len, + ath10k_wmi_tlv_swba_noa_parse, + swba); + if (ret) + return ret; + } + break; + default: + break; + } + return 0; +} + +static int ath10k_wmi_tlv_op_pull_swba_ev(struct ath10k *ar, + struct sk_buff *skb, + struct wmi_swba_ev_arg *arg) +{ + struct wmi_tlv_swba_parse swba = { .arg = arg }; + u32 map; + size_t n_vdevs; + int ret; + + ret = ath10k_wmi_tlv_iter(ar, skb->data, skb->len, + ath10k_wmi_tlv_swba_parse, &swba); + if (ret) { + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + if (!swba.ev) + return -EPROTO; + + arg->vdev_map = swba.ev->vdev_map; + + for (map = __le32_to_cpu(arg->vdev_map), n_vdevs = 0; map; map >>= 1) + if (map & BIT(0)) + n_vdevs++; + + if (n_vdevs != swba.n_tim || + n_vdevs != swba.n_noa) + return -EPROTO; + + return 0; +} + +static int ath10k_wmi_tlv_op_pull_phyerr_ev(struct ath10k *ar, + struct sk_buff *skb, + struct wmi_phyerr_ev_arg *arg) +{ + const void **tb; + const struct wmi_tlv_phyerr_ev *ev; + const void *phyerrs; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_COMB_PHYERR_RX_HDR]; + phyerrs = tb[WMI_TLV_TAG_ARRAY_BYTE]; + + if (!ev || !phyerrs) { + kfree(tb); + return -EPROTO; + } + + arg->num_phyerrs = ev->num_phyerrs; + arg->tsf_l32 = ev->tsf_l32; + arg->tsf_u32 = ev->tsf_u32; + arg->buf_len = ev->buf_len; + arg->phyerrs = phyerrs; + + kfree(tb); + return 0; +} + +#define WMI_TLV_ABI_VER_NS0 0x5F414351 +#define WMI_TLV_ABI_VER_NS1 0x00004C4D +#define WMI_TLV_ABI_VER_NS2 0x00000000 +#define WMI_TLV_ABI_VER_NS3 0x00000000 + +#define WMI_TLV_ABI_VER0_MAJOR 1 +#define WMI_TLV_ABI_VER0_MINOR 0 +#define WMI_TLV_ABI_VER0 ((((WMI_TLV_ABI_VER0_MAJOR) << 24) & 0xFF000000) | \ + (((WMI_TLV_ABI_VER0_MINOR) << 0) & 0x00FFFFFF)) +#define WMI_TLV_ABI_VER1 53 + +static int +ath10k_wmi_tlv_parse_mem_reqs(struct ath10k *ar, u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_svc_rdy_ev_arg *arg = data; + int i; + + if (tag != WMI_TLV_TAG_STRUCT_WLAN_HOST_MEM_REQ) + return -EPROTO; + + for (i = 0; i < ARRAY_SIZE(arg->mem_reqs); i++) { + if (!arg->mem_reqs[i]) { + arg->mem_reqs[i] = ptr; + return 0; + } + } + + return -ENOMEM; +} + +static int ath10k_wmi_tlv_op_pull_svc_rdy_ev(struct ath10k *ar, + struct sk_buff *skb, + struct wmi_svc_rdy_ev_arg *arg) +{ + const void **tb; + const struct hal_reg_capabilities *reg; + const struct wmi_tlv_svc_rdy_ev *ev; + const __le32 *svc_bmap; + const struct wlan_host_mem_req *mem_reqs; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_SERVICE_READY_EVENT]; + reg = tb[WMI_TLV_TAG_STRUCT_HAL_REG_CAPABILITIES]; + svc_bmap = tb[WMI_TLV_TAG_ARRAY_UINT32]; + mem_reqs = tb[WMI_TLV_TAG_ARRAY_STRUCT]; + + if (!ev || !reg || !svc_bmap || !mem_reqs) { + kfree(tb); + return -EPROTO; + } + + /* This is an internal ABI compatibility check for WMI TLV so check it + * here instead of the generic WMI code. + */ + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi tlv abi 0x%08x ?= 0x%08x, 0x%08x ?= 0x%08x, 0x%08x ?= 0x%08x, 0x%08x ?= 0x%08x, 0x%08x ?= 0x%08x\n", + __le32_to_cpu(ev->abi.abi_ver0), WMI_TLV_ABI_VER0, + __le32_to_cpu(ev->abi.abi_ver_ns0), WMI_TLV_ABI_VER_NS0, + __le32_to_cpu(ev->abi.abi_ver_ns1), WMI_TLV_ABI_VER_NS1, + __le32_to_cpu(ev->abi.abi_ver_ns2), WMI_TLV_ABI_VER_NS2, + __le32_to_cpu(ev->abi.abi_ver_ns3), WMI_TLV_ABI_VER_NS3); + + if (__le32_to_cpu(ev->abi.abi_ver0) != WMI_TLV_ABI_VER0 || + __le32_to_cpu(ev->abi.abi_ver_ns0) != WMI_TLV_ABI_VER_NS0 || + __le32_to_cpu(ev->abi.abi_ver_ns1) != WMI_TLV_ABI_VER_NS1 || + __le32_to_cpu(ev->abi.abi_ver_ns2) != WMI_TLV_ABI_VER_NS2 || + __le32_to_cpu(ev->abi.abi_ver_ns3) != WMI_TLV_ABI_VER_NS3) { + kfree(tb); + return -ENOTSUPP; + } + + arg->min_tx_power = ev->hw_min_tx_power; + arg->max_tx_power = ev->hw_max_tx_power; + arg->ht_cap = ev->ht_cap_info; + arg->vht_cap = ev->vht_cap_info; + arg->sw_ver0 = ev->abi.abi_ver0; + arg->sw_ver1 = ev->abi.abi_ver1; + arg->fw_build = ev->fw_build_vers; + arg->phy_capab = ev->phy_capability; + arg->num_rf_chains = ev->num_rf_chains; + arg->eeprom_rd = reg->eeprom_rd; + arg->num_mem_reqs = ev->num_mem_reqs; + arg->service_map = svc_bmap; + arg->service_map_len = ath10k_wmi_tlv_len(svc_bmap); + + ret = ath10k_wmi_tlv_iter(ar, mem_reqs, ath10k_wmi_tlv_len(mem_reqs), + ath10k_wmi_tlv_parse_mem_reqs, arg); + if (ret) { + kfree(tb); + ath10k_warn(ar, "failed to parse mem_reqs tlv: %d\n", ret); + return ret; + } + + kfree(tb); + return 0; +} + +static int ath10k_wmi_tlv_op_pull_rdy_ev(struct ath10k *ar, + struct sk_buff *skb, + struct wmi_rdy_ev_arg *arg) +{ + const void **tb; + const struct wmi_tlv_rdy_ev *ev; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_READY_EVENT]; + if (!ev) { + kfree(tb); + return -EPROTO; + } + + arg->sw_version = ev->abi.abi_ver0; + arg->abi_version = ev->abi.abi_ver1; + arg->status = ev->status; + arg->mac_addr = ev->mac_addr.addr; + + kfree(tb); + return 0; +} + +static int ath10k_wmi_tlv_op_pull_fw_stats(struct ath10k *ar, + struct sk_buff *skb, + struct ath10k_fw_stats *stats) +{ + const void **tb; + const struct wmi_stats_event *ev; + const void *data; + u32 num_pdev_stats, num_vdev_stats, num_peer_stats; + size_t data_len; + int ret; + + tb = ath10k_wmi_tlv_parse_alloc(ar, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath10k_warn(ar, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TLV_TAG_STRUCT_STATS_EVENT]; + data = tb[WMI_TLV_TAG_ARRAY_BYTE]; + + if (!ev || !data) { + kfree(tb); + return -EPROTO; + } + + data_len = ath10k_wmi_tlv_len(data); + num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats); + num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats); + num_peer_stats = __le32_to_cpu(ev->num_peer_stats); + + WARN_ON(1); /* FIXME: not implemented yet */ + + kfree(tb); + return 0; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_pdev_suspend(struct ath10k *ar, u32 opt) +{ + struct wmi_tlv_pdev_suspend *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_SUSPEND_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->opt = __cpu_to_le32(opt); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev suspend\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_pdev_resume(struct ath10k *ar) +{ + struct wmi_tlv_resume_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_RESUME_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->reserved = __cpu_to_le32(0); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev resume\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_pdev_set_rd(struct ath10k *ar, + u16 rd, u16 rd2g, u16 rd5g, + u16 ctl2g, u16 ctl5g, + enum wmi_dfs_region dfs_reg) +{ + struct wmi_tlv_pdev_set_rd_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_SET_REGDOMAIN_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->regd = __cpu_to_le32(rd); + cmd->regd_2ghz = __cpu_to_le32(rd2g); + cmd->regd_5ghz = __cpu_to_le32(rd5g); + cmd->conform_limit_2ghz = __cpu_to_le32(rd2g); + cmd->conform_limit_5ghz = __cpu_to_le32(rd5g); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev set rd\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_pdev_set_param(struct ath10k *ar, u32 param_id, + u32 param_value) +{ + struct wmi_tlv_pdev_set_param_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_SET_PARAM_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->param_id = __cpu_to_le32(param_id); + cmd->param_value = __cpu_to_le32(param_value); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev set param\n"); + return skb; +} + +static struct sk_buff *ath10k_wmi_tlv_op_gen_init(struct ath10k *ar) +{ + struct sk_buff *skb; + struct wmi_tlv *tlv; + struct wmi_tlv_init_cmd *cmd; + struct wmi_tlv_resource_config *cfg; + struct wmi_host_mem_chunks *chunks; + size_t len, chunks_len; + void *ptr; + + chunks_len = ar->wmi.num_mem_chunks * sizeof(struct host_memory_chunk); + len = (sizeof(*tlv) + sizeof(*cmd)) + + (sizeof(*tlv) + sizeof(*cfg)) + + (sizeof(*tlv) + chunks_len); + + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = skb->data; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_INIT_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_RESOURCE_CONFIG); + tlv->len = __cpu_to_le16(sizeof(*cfg)); + cfg = (void *)tlv->value; + ptr += sizeof(*tlv); + ptr += sizeof(*cfg); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT); + tlv->len = __cpu_to_le16(chunks_len); + chunks = (void *)tlv->value; + + ptr += sizeof(*tlv); + ptr += chunks_len; + + cmd->abi.abi_ver0 = __cpu_to_le32(WMI_TLV_ABI_VER0); + cmd->abi.abi_ver1 = __cpu_to_le32(WMI_TLV_ABI_VER1); + cmd->abi.abi_ver_ns0 = __cpu_to_le32(WMI_TLV_ABI_VER_NS0); + cmd->abi.abi_ver_ns1 = __cpu_to_le32(WMI_TLV_ABI_VER_NS1); + cmd->abi.abi_ver_ns2 = __cpu_to_le32(WMI_TLV_ABI_VER_NS2); + cmd->abi.abi_ver_ns3 = __cpu_to_le32(WMI_TLV_ABI_VER_NS3); + cmd->num_host_mem_chunks = __cpu_to_le32(ar->wmi.num_mem_chunks); + + cfg->num_vdevs = __cpu_to_le32(TARGET_TLV_NUM_VDEVS); + cfg->num_peers = __cpu_to_le32(TARGET_TLV_NUM_PEERS); + + if (test_bit(WMI_SERVICE_RX_FULL_REORDER, ar->wmi.svc_map)) { + cfg->num_offload_peers = __cpu_to_le32(3); + cfg->num_offload_reorder_bufs = __cpu_to_le32(3); + } else { + cfg->num_offload_peers = __cpu_to_le32(0); + cfg->num_offload_reorder_bufs = __cpu_to_le32(0); + } + + cfg->num_peer_keys = __cpu_to_le32(2); + cfg->num_tids = __cpu_to_le32(TARGET_TLV_NUM_TIDS); + cfg->ast_skid_limit = __cpu_to_le32(0x10); + cfg->tx_chain_mask = __cpu_to_le32(0x7); + cfg->rx_chain_mask = __cpu_to_le32(0x7); + cfg->rx_timeout_pri[0] = __cpu_to_le32(0x64); + cfg->rx_timeout_pri[1] = __cpu_to_le32(0x64); + cfg->rx_timeout_pri[2] = __cpu_to_le32(0x64); + cfg->rx_timeout_pri[3] = __cpu_to_le32(0x28); + cfg->rx_decap_mode = __cpu_to_le32(1); + cfg->scan_max_pending_reqs = __cpu_to_le32(4); + cfg->bmiss_offload_max_vdev = __cpu_to_le32(3); + cfg->roam_offload_max_vdev = __cpu_to_le32(3); + cfg->roam_offload_max_ap_profiles = __cpu_to_le32(8); + cfg->num_mcast_groups = __cpu_to_le32(0); + cfg->num_mcast_table_elems = __cpu_to_le32(0); + cfg->mcast2ucast_mode = __cpu_to_le32(0); + cfg->tx_dbg_log_size = __cpu_to_le32(0x400); + cfg->num_wds_entries = __cpu_to_le32(0x20); + cfg->dma_burst_size = __cpu_to_le32(0); + cfg->mac_aggr_delim = __cpu_to_le32(0); + cfg->rx_skip_defrag_timeout_dup_detection_check = __cpu_to_le32(0); + cfg->vow_config = __cpu_to_le32(0); + cfg->gtk_offload_max_vdev = __cpu_to_le32(2); + cfg->num_msdu_desc = __cpu_to_le32(TARGET_TLV_NUM_MSDU_DESC); + cfg->max_frag_entries = __cpu_to_le32(2); + cfg->num_tdls_vdevs = __cpu_to_le32(1); + cfg->num_tdls_conn_table_entries = __cpu_to_le32(0x20); + cfg->beacon_tx_offload_max_vdev = __cpu_to_le32(2); + cfg->num_multicast_filter_entries = __cpu_to_le32(5); + cfg->num_wow_filters = __cpu_to_le32(0x16); + cfg->num_keep_alive_pattern = __cpu_to_le32(6); + cfg->keep_alive_pattern_size = __cpu_to_le32(0); + cfg->max_tdls_concurrent_sleep_sta = __cpu_to_le32(1); + cfg->max_tdls_concurrent_buffer_sta = __cpu_to_le32(1); + + ath10k_wmi_put_host_mem_chunks(ar, chunks); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv init\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_start_scan(struct ath10k *ar, + const struct wmi_start_scan_arg *arg) +{ + struct wmi_tlv_start_scan_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + size_t len, chan_len, ssid_len, bssid_len, ie_len; + __le32 *chans; + struct wmi_ssid *ssids; + struct wmi_mac_addr *addrs; + void *ptr; + int i, ret; + + ret = ath10k_wmi_start_scan_verify(arg); + if (ret) + return ERR_PTR(ret); + + chan_len = arg->n_channels * sizeof(__le32); + ssid_len = arg->n_ssids * sizeof(struct wmi_ssid); + bssid_len = arg->n_bssids * sizeof(struct wmi_mac_addr); + ie_len = roundup(arg->ie_len, 4); + len = (sizeof(*tlv) + sizeof(*cmd)) + + (arg->n_channels ? sizeof(*tlv) + chan_len : 0) + + (arg->n_ssids ? sizeof(*tlv) + ssid_len : 0) + + (arg->n_bssids ? sizeof(*tlv) + bssid_len : 0) + + (arg->ie_len ? sizeof(*tlv) + ie_len : 0); + + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_START_SCAN_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + + ath10k_wmi_put_start_scan_common(&cmd->common, arg); + cmd->burst_duration_ms = __cpu_to_le32(0); + cmd->num_channels = __cpu_to_le32(arg->n_channels); + cmd->num_ssids = __cpu_to_le32(arg->n_ssids); + cmd->num_bssids = __cpu_to_le32(arg->n_bssids); + cmd->ie_len = __cpu_to_le32(arg->ie_len); + cmd->num_probes = __cpu_to_le32(3); + + /* FIXME: There are some scan flag inconsistencies across firmwares, + * e.g. WMI-TLV inverts the logic behind the following flag. + */ + cmd->common.scan_ctrl_flags ^= __cpu_to_le32(WMI_SCAN_FILTER_PROBE_REQ); + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_UINT32); + tlv->len = __cpu_to_le16(chan_len); + chans = (void *)tlv->value; + for (i = 0; i < arg->n_channels; i++) + chans[i] = __cpu_to_le32(arg->channels[i]); + + ptr += sizeof(*tlv); + ptr += chan_len; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_FIXED_STRUCT); + tlv->len = __cpu_to_le16(ssid_len); + ssids = (void *)tlv->value; + for (i = 0; i < arg->n_ssids; i++) { + ssids[i].ssid_len = __cpu_to_le32(arg->ssids[i].len); + memcpy(ssids[i].ssid, arg->ssids[i].ssid, arg->ssids[i].len); + } + + ptr += sizeof(*tlv); + ptr += ssid_len; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_FIXED_STRUCT); + tlv->len = __cpu_to_le16(bssid_len); + addrs = (void *)tlv->value; + for (i = 0; i < arg->n_bssids; i++) + ether_addr_copy(addrs[i].addr, arg->bssids[i].bssid); + + ptr += sizeof(*tlv); + ptr += bssid_len; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE); + tlv->len = __cpu_to_le16(ie_len); + memcpy(tlv->value, arg->ie, arg->ie_len); + + ptr += sizeof(*tlv); + ptr += ie_len; + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv start scan\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_stop_scan(struct ath10k *ar, + const struct wmi_stop_scan_arg *arg) +{ + struct wmi_stop_scan_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + u32 scan_id; + u32 req_id; + + if (arg->req_id > 0xFFF) + return ERR_PTR(-EINVAL); + if (arg->req_type == WMI_SCAN_STOP_ONE && arg->u.scan_id > 0xFFF) + return ERR_PTR(-EINVAL); + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + scan_id = arg->u.scan_id; + scan_id |= WMI_HOST_SCAN_REQ_ID_PREFIX; + + req_id = arg->req_id; + req_id |= WMI_HOST_SCAN_REQUESTOR_ID_PREFIX; + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STOP_SCAN_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->req_type = __cpu_to_le32(arg->req_type); + cmd->vdev_id = __cpu_to_le32(arg->u.vdev_id); + cmd->scan_id = __cpu_to_le32(scan_id); + cmd->scan_req_id = __cpu_to_le32(req_id); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv stop scan\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_vdev_create(struct ath10k *ar, + u32 vdev_id, + enum wmi_vdev_type vdev_type, + enum wmi_vdev_subtype vdev_subtype, + const u8 mac_addr[ETH_ALEN]) +{ + struct wmi_vdev_create_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_CREATE_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->vdev_type = __cpu_to_le32(vdev_type); + cmd->vdev_subtype = __cpu_to_le32(vdev_subtype); + ether_addr_copy(cmd->vdev_macaddr.addr, mac_addr); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev create\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_vdev_delete(struct ath10k *ar, u32 vdev_id) +{ + struct wmi_vdev_delete_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_DELETE_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev delete\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_vdev_start(struct ath10k *ar, + const struct wmi_vdev_start_request_arg *arg, + bool restart) +{ + struct wmi_tlv_vdev_start_cmd *cmd; + struct wmi_channel *ch; + struct wmi_p2p_noa_descriptor *noa; + struct wmi_tlv *tlv; + struct sk_buff *skb; + size_t len; + void *ptr; + u32 flags = 0; + + if (WARN_ON(arg->ssid && arg->ssid_len == 0)) + return ERR_PTR(-EINVAL); + if (WARN_ON(arg->hidden_ssid && !arg->ssid)) + return ERR_PTR(-EINVAL); + if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid))) + return ERR_PTR(-EINVAL); + + len = (sizeof(*tlv) + sizeof(*cmd)) + + (sizeof(*tlv) + sizeof(*ch)) + + (sizeof(*tlv) + 0); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + if (arg->hidden_ssid) + flags |= WMI_VDEV_START_HIDDEN_SSID; + if (arg->pmf_enabled) + flags |= WMI_VDEV_START_PMF_ENABLED; + + ptr = (void *)skb->data; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_START_REQUEST_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(arg->vdev_id); + cmd->bcn_intval = __cpu_to_le32(arg->bcn_intval); + cmd->dtim_period = __cpu_to_le32(arg->dtim_period); + cmd->flags = __cpu_to_le32(flags); + cmd->bcn_tx_rate = __cpu_to_le32(arg->bcn_tx_rate); + cmd->bcn_tx_power = __cpu_to_le32(arg->bcn_tx_power); + cmd->disable_hw_ack = __cpu_to_le32(arg->disable_hw_ack); + + if (arg->ssid) { + cmd->ssid.ssid_len = __cpu_to_le32(arg->ssid_len); + memcpy(cmd->ssid.ssid, arg->ssid, arg->ssid_len); + } + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_CHANNEL); + tlv->len = __cpu_to_le16(sizeof(*ch)); + ch = (void *)tlv->value; + ath10k_wmi_put_wmi_channel(ch, &arg->channel); + + ptr += sizeof(*tlv); + ptr += sizeof(*ch); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT); + tlv->len = 0; + noa = (void *)tlv->value; + + /* Note: This is a nested TLV containing: + * [wmi_tlv][wmi_p2p_noa_descriptor][wmi_tlv].. + */ + + ptr += sizeof(*tlv); + ptr += 0; + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev start\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_vdev_stop(struct ath10k *ar, u32 vdev_id) +{ + struct wmi_vdev_stop_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_STOP_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev stop\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, + const u8 *bssid) + +{ + struct wmi_vdev_up_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_UP_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->vdev_assoc_id = __cpu_to_le32(aid); + ether_addr_copy(cmd->vdev_bssid.addr, bssid); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev up\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_vdev_down(struct ath10k *ar, u32 vdev_id) +{ + struct wmi_vdev_down_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_DOWN_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev down\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_vdev_set_param(struct ath10k *ar, u32 vdev_id, + u32 param_id, u32 param_value) +{ + struct wmi_vdev_set_param_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_SET_PARAM_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->param_id = __cpu_to_le32(param_id); + cmd->param_value = __cpu_to_le32(param_value); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev set param\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_vdev_install_key(struct ath10k *ar, + const struct wmi_vdev_install_key_arg *arg) +{ + struct wmi_vdev_install_key_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + size_t len; + void *ptr; + + if (arg->key_cipher == WMI_CIPHER_NONE && arg->key_data != NULL) + return ERR_PTR(-EINVAL); + if (arg->key_cipher != WMI_CIPHER_NONE && arg->key_data == NULL) + return ERR_PTR(-EINVAL); + + len = sizeof(*tlv) + sizeof(*cmd) + + sizeof(*tlv) + roundup(arg->key_len, sizeof(__le32)); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_INSTALL_KEY_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(arg->vdev_id); + cmd->key_idx = __cpu_to_le32(arg->key_idx); + cmd->key_flags = __cpu_to_le32(arg->key_flags); + cmd->key_cipher = __cpu_to_le32(arg->key_cipher); + cmd->key_len = __cpu_to_le32(arg->key_len); + cmd->key_txmic_len = __cpu_to_le32(arg->key_txmic_len); + cmd->key_rxmic_len = __cpu_to_le32(arg->key_rxmic_len); + + if (arg->macaddr) + ether_addr_copy(cmd->peer_macaddr.addr, arg->macaddr); + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE); + tlv->len = __cpu_to_le16(roundup(arg->key_len, sizeof(__le32))); + if (arg->key_data) + memcpy(tlv->value, arg->key_data, arg->key_len); + + ptr += sizeof(*tlv); + ptr += roundup(arg->key_len, sizeof(__le32)); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev install key\n"); + return skb; +} + +static void *ath10k_wmi_tlv_put_uapsd_ac(struct ath10k *ar, void *ptr, + const struct wmi_sta_uapsd_auto_trig_arg *arg) +{ + struct wmi_sta_uapsd_auto_trig_param *ac; + struct wmi_tlv *tlv; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_UAPSD_AUTO_TRIG_PARAM); + tlv->len = __cpu_to_le16(sizeof(*ac)); + ac = (void *)tlv->value; + + ac->wmm_ac = __cpu_to_le32(arg->wmm_ac); + ac->user_priority = __cpu_to_le32(arg->user_priority); + ac->service_interval = __cpu_to_le32(arg->service_interval); + ac->suspend_interval = __cpu_to_le32(arg->suspend_interval); + ac->delay_interval = __cpu_to_le32(arg->delay_interval); + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi tlv vdev sta uapsd auto trigger ac %d prio %d svc int %d susp int %d delay int %d\n", + ac->wmm_ac, ac->user_priority, ac->service_interval, + ac->suspend_interval, ac->delay_interval); + + return ptr + sizeof(*tlv) + sizeof(*ac); +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_vdev_sta_uapsd(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN], + const struct wmi_sta_uapsd_auto_trig_arg *args, + u32 num_ac) +{ + struct wmi_sta_uapsd_auto_trig_cmd_fixed_param *cmd; + struct wmi_sta_uapsd_auto_trig_param *ac; + struct wmi_tlv *tlv; + struct sk_buff *skb; + size_t len; + size_t ac_tlv_len; + void *ptr; + int i; + + ac_tlv_len = num_ac * (sizeof(*tlv) + sizeof(*ac)); + len = sizeof(*tlv) + sizeof(*cmd) + + sizeof(*tlv) + ac_tlv_len; + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_UAPSD_AUTO_TRIG_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->num_ac = __cpu_to_le32(num_ac); + ether_addr_copy(cmd->peer_macaddr.addr, peer_addr); + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT); + tlv->len = __cpu_to_le16(ac_tlv_len); + ac = (void *)tlv->value; + + ptr += sizeof(*tlv); + for (i = 0; i < num_ac; i++) + ptr = ath10k_wmi_tlv_put_uapsd_ac(ar, ptr, &args[i]); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev sta uapsd auto trigger\n"); + return skb; +} + +static void *ath10k_wmi_tlv_put_wmm(void *ptr, + const struct wmi_wmm_params_arg *arg) +{ + struct wmi_wmm_params *wmm; + struct wmi_tlv *tlv; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_WMM_PARAMS); + tlv->len = __cpu_to_le16(sizeof(*wmm)); + wmm = (void *)tlv->value; + ath10k_wmi_set_wmm_param(wmm, arg); + + return ptr + sizeof(*tlv) + sizeof(*wmm); +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_vdev_wmm_conf(struct ath10k *ar, u32 vdev_id, + const struct wmi_wmm_params_all_arg *arg) +{ + struct wmi_tlv_vdev_set_wmm_cmd *cmd; + struct wmi_wmm_params *wmm; + struct wmi_tlv *tlv; + struct sk_buff *skb; + size_t len; + void *ptr; + + len = (sizeof(*tlv) + sizeof(*cmd)) + + (4 * (sizeof(*tlv) + sizeof(*wmm))); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VDEV_SET_WMM_PARAMS_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_be); + ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_bk); + ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_vi); + ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_vo); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv vdev wmm conf\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_sta_keepalive(struct ath10k *ar, + const struct wmi_sta_keepalive_arg *arg) +{ + struct wmi_tlv_sta_keepalive_cmd *cmd; + struct wmi_sta_keepalive_arp_resp *arp; + struct sk_buff *skb; + struct wmi_tlv *tlv; + void *ptr; + size_t len; + + len = sizeof(*tlv) + sizeof(*cmd) + + sizeof(*tlv) + sizeof(*arp); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_KEEPALIVE_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(arg->vdev_id); + cmd->enabled = __cpu_to_le32(arg->enabled); + cmd->method = __cpu_to_le32(arg->method); + cmd->interval = __cpu_to_le32(arg->interval); + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_KEEPALVE_ARP_RESPONSE); + tlv->len = __cpu_to_le16(sizeof(*arp)); + arp = (void *)tlv->value; + + arp->src_ip4_addr = arg->src_ip4_addr; + arp->dest_ip4_addr = arg->dest_ip4_addr; + ether_addr_copy(arp->dest_mac_addr.addr, arg->dest_mac_addr); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv sta keepalive vdev %d enabled %d method %d inverval %d\n", + arg->vdev_id, arg->enabled, arg->method, arg->interval); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_peer_create(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]) +{ + struct wmi_tlv_peer_create_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PEER_CREATE_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->peer_type = __cpu_to_le32(WMI_TLV_PEER_TYPE_DEFAULT); /* FIXME */ + ether_addr_copy(cmd->peer_addr.addr, peer_addr); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer create\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_peer_delete(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]) +{ + struct wmi_peer_delete_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PEER_DELETE_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + ether_addr_copy(cmd->peer_macaddr.addr, peer_addr); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer delete\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_peer_flush(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN], u32 tid_bitmap) +{ + struct wmi_peer_flush_tids_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PEER_FLUSH_TIDS_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->peer_tid_bitmap = __cpu_to_le32(tid_bitmap); + ether_addr_copy(cmd->peer_macaddr.addr, peer_addr); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer flush\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_peer_set_param(struct ath10k *ar, u32 vdev_id, + const u8 *peer_addr, + enum wmi_peer_param param_id, + u32 param_value) +{ + struct wmi_peer_set_param_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PEER_SET_PARAM_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->param_id = __cpu_to_le32(param_id); + cmd->param_value = __cpu_to_le32(param_value); + ether_addr_copy(cmd->peer_macaddr.addr, peer_addr); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer set param\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_peer_assoc(struct ath10k *ar, + const struct wmi_peer_assoc_complete_arg *arg) +{ + struct wmi_tlv_peer_assoc_cmd *cmd; + struct wmi_vht_rate_set *vht_rate; + struct wmi_tlv *tlv; + struct sk_buff *skb; + size_t len, legacy_rate_len, ht_rate_len; + void *ptr; + + if (arg->peer_mpdu_density > 16) + return ERR_PTR(-EINVAL); + if (arg->peer_legacy_rates.num_rates > MAX_SUPPORTED_RATES) + return ERR_PTR(-EINVAL); + if (arg->peer_ht_rates.num_rates > MAX_SUPPORTED_RATES) + return ERR_PTR(-EINVAL); + + legacy_rate_len = roundup(arg->peer_legacy_rates.num_rates, + sizeof(__le32)); + ht_rate_len = roundup(arg->peer_ht_rates.num_rates, sizeof(__le32)); + len = (sizeof(*tlv) + sizeof(*cmd)) + + (sizeof(*tlv) + legacy_rate_len) + + (sizeof(*tlv) + ht_rate_len) + + (sizeof(*tlv) + sizeof(*vht_rate)); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PEER_ASSOC_COMPLETE_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + + cmd->vdev_id = __cpu_to_le32(arg->vdev_id); + cmd->new_assoc = __cpu_to_le32(arg->peer_reassoc ? 0 : 1); + cmd->assoc_id = __cpu_to_le32(arg->peer_aid); + cmd->flags = __cpu_to_le32(arg->peer_flags); + cmd->caps = __cpu_to_le32(arg->peer_caps); + cmd->listen_intval = __cpu_to_le32(arg->peer_listen_intval); + cmd->ht_caps = __cpu_to_le32(arg->peer_ht_caps); + cmd->max_mpdu = __cpu_to_le32(arg->peer_max_mpdu); + cmd->mpdu_density = __cpu_to_le32(arg->peer_mpdu_density); + cmd->rate_caps = __cpu_to_le32(arg->peer_rate_caps); + cmd->nss = __cpu_to_le32(arg->peer_num_spatial_streams); + cmd->vht_caps = __cpu_to_le32(arg->peer_vht_caps); + cmd->phy_mode = __cpu_to_le32(arg->peer_phymode); + cmd->num_legacy_rates = __cpu_to_le32(arg->peer_legacy_rates.num_rates); + cmd->num_ht_rates = __cpu_to_le32(arg->peer_ht_rates.num_rates); + ether_addr_copy(cmd->mac_addr.addr, arg->addr); + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE); + tlv->len = __cpu_to_le16(legacy_rate_len); + memcpy(tlv->value, arg->peer_legacy_rates.rates, + arg->peer_legacy_rates.num_rates); + + ptr += sizeof(*tlv); + ptr += legacy_rate_len; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE); + tlv->len = __cpu_to_le16(ht_rate_len); + memcpy(tlv->value, arg->peer_ht_rates.rates, + arg->peer_ht_rates.num_rates); + + ptr += sizeof(*tlv); + ptr += ht_rate_len; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_VHT_RATE_SET); + tlv->len = __cpu_to_le16(sizeof(*vht_rate)); + vht_rate = (void *)tlv->value; + + vht_rate->rx_max_rate = __cpu_to_le32(arg->peer_vht_rates.rx_max_rate); + vht_rate->rx_mcs_set = __cpu_to_le32(arg->peer_vht_rates.rx_mcs_set); + vht_rate->tx_max_rate = __cpu_to_le32(arg->peer_vht_rates.tx_max_rate); + vht_rate->tx_mcs_set = __cpu_to_le32(arg->peer_vht_rates.tx_mcs_set); + + ptr += sizeof(*tlv); + ptr += sizeof(*vht_rate); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv peer assoc\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_set_psmode(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_ps_mode psmode) +{ + struct wmi_sta_powersave_mode_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_POWERSAVE_MODE_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->sta_ps_mode = __cpu_to_le32(psmode); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv set psmode\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_set_sta_ps(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_powersave_param param_id, + u32 param_value) +{ + struct wmi_sta_powersave_param_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_STA_POWERSAVE_PARAM_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->param_id = __cpu_to_le32(param_id); + cmd->param_value = __cpu_to_le32(param_value); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv set sta ps\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_set_ap_ps(struct ath10k *ar, u32 vdev_id, const u8 *mac, + enum wmi_ap_ps_peer_param param_id, u32 value) +{ + struct wmi_ap_ps_peer_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + if (!mac) + return ERR_PTR(-EINVAL); + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_AP_PS_PEER_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->param_id = __cpu_to_le32(param_id); + cmd->param_value = __cpu_to_le32(value); + ether_addr_copy(cmd->peer_macaddr.addr, mac); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv ap ps param\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_scan_chan_list(struct ath10k *ar, + const struct wmi_scan_chan_list_arg *arg) +{ + struct wmi_tlv_scan_chan_list_cmd *cmd; + struct wmi_channel *ci; + struct wmi_channel_arg *ch; + struct wmi_tlv *tlv; + struct sk_buff *skb; + size_t chans_len, len; + int i; + void *ptr, *chans; + + chans_len = arg->n_channels * (sizeof(*tlv) + sizeof(*ci)); + len = (sizeof(*tlv) + sizeof(*cmd)) + + (sizeof(*tlv) + chans_len); + + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_SCAN_CHAN_LIST_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->num_scan_chans = __cpu_to_le32(arg->n_channels); + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_STRUCT); + tlv->len = __cpu_to_le16(chans_len); + chans = (void *)tlv->value; + + for (i = 0; i < arg->n_channels; i++) { + ch = &arg->channels[i]; + + tlv = chans; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_CHANNEL); + tlv->len = __cpu_to_le16(sizeof(*ci)); + ci = (void *)tlv->value; + + ath10k_wmi_put_wmi_channel(ci, ch); + + chans += sizeof(*tlv); + chans += sizeof(*ci); + } + + ptr += sizeof(*tlv); + ptr += chans_len; + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv scan chan list\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_beacon_dma(struct ath10k *ar, u32 vdev_id, + const void *bcn, size_t bcn_len, + u32 bcn_paddr, bool dtim_zero, + bool deliver_cab) + +{ + struct wmi_bcn_tx_ref_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + struct ieee80211_hdr *hdr; + u16 fc; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + hdr = (struct ieee80211_hdr *)bcn; + fc = le16_to_cpu(hdr->frame_control); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_BCN_SEND_FROM_HOST_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->data_len = __cpu_to_le32(bcn_len); + cmd->data_ptr = __cpu_to_le32(bcn_paddr); + cmd->msdu_id = 0; + cmd->frame_control = __cpu_to_le32(fc); + cmd->flags = 0; + + if (dtim_zero) + cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DTIM_ZERO); + + if (deliver_cab) + cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DELIVER_CAB); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv beacon dma\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_pdev_set_wmm(struct ath10k *ar, + const struct wmi_wmm_params_all_arg *arg) +{ + struct wmi_tlv_pdev_set_wmm_cmd *cmd; + struct wmi_wmm_params *wmm; + struct wmi_tlv *tlv; + struct sk_buff *skb; + size_t len; + void *ptr; + + len = (sizeof(*tlv) + sizeof(*cmd)) + + (4 * (sizeof(*tlv) + sizeof(*wmm))); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_SET_WMM_PARAMS_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + + /* nothing to set here */ + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_be); + ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_bk); + ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_vi); + ptr = ath10k_wmi_tlv_put_wmm(ptr, &arg->ac_vo); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pdev set wmm\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_request_stats(struct ath10k *ar, + enum wmi_stats_id stats_id) +{ + struct wmi_request_stats_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_REQUEST_STATS_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->stats_id = __cpu_to_le32(stats_id); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv request stats\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_force_fw_hang(struct ath10k *ar, + enum wmi_force_fw_hang_type type, + u32 delay_ms) +{ + struct wmi_force_fw_hang_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*tlv) + sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_FORCE_FW_HANG_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->type = __cpu_to_le32(type); + cmd->delay_ms = __cpu_to_le32(delay_ms); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv force fw hang\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_dbglog_cfg(struct ath10k *ar, u32 module_enable, + u32 log_level) { + struct wmi_tlv_dbglog_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + size_t len, bmap_len; + u32 value; + void *ptr; + + if (module_enable) { + value = WMI_TLV_DBGLOG_LOG_LEVEL_VALUE( + module_enable, + WMI_TLV_DBGLOG_LOG_LEVEL_VERBOSE); + } else { + value = WMI_TLV_DBGLOG_LOG_LEVEL_VALUE( + WMI_TLV_DBGLOG_ALL_MODULES, + WMI_TLV_DBGLOG_LOG_LEVEL_WARN); + } + + bmap_len = 0; + len = sizeof(*tlv) + sizeof(*cmd) + sizeof(*tlv) + bmap_len; + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_DEBUG_LOG_CONFIG_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->param = __cpu_to_le32(WMI_TLV_DBGLOG_PARAM_LOG_LEVEL); + cmd->value = __cpu_to_le32(value); + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_UINT32); + tlv->len = __cpu_to_le16(bmap_len); + + /* nothing to do here */ + + ptr += sizeof(*tlv); + ptr += sizeof(bmap_len); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv dbglog value 0x%08x\n", value); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_pktlog_enable(struct ath10k *ar, u32 filter) +{ + struct wmi_tlv_pktlog_enable *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + void *ptr; + size_t len; + + len = sizeof(*tlv) + sizeof(*cmd); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_PKTLOG_ENABLE_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->filter = __cpu_to_le32(filter); + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pktlog enable filter 0x%08x\n", + filter); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_pktlog_disable(struct ath10k *ar) +{ + struct wmi_tlv_pktlog_disable *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + void *ptr; + size_t len; + + len = sizeof(*tlv) + sizeof(*cmd); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PDEV_PKTLOG_DISABLE_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv pktlog disable\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_bcn_tmpl(struct ath10k *ar, u32 vdev_id, + u32 tim_ie_offset, struct sk_buff *bcn, + u32 prb_caps, u32 prb_erp, void *prb_ies, + size_t prb_ies_len) +{ + struct wmi_tlv_bcn_tmpl_cmd *cmd; + struct wmi_tlv_bcn_prb_info *info; + struct wmi_tlv *tlv; + struct sk_buff *skb; + void *ptr; + size_t len; + + if (WARN_ON(prb_ies_len > 0 && !prb_ies)) + return ERR_PTR(-EINVAL); + + len = sizeof(*tlv) + sizeof(*cmd) + + sizeof(*tlv) + sizeof(*info) + prb_ies_len + + sizeof(*tlv) + roundup(bcn->len, 4); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_BCN_TMPL_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->tim_ie_offset = __cpu_to_le32(tim_ie_offset); + cmd->buf_len = __cpu_to_le32(bcn->len); + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + /* FIXME: prb_ies_len should be probably aligned to 4byte boundary but + * then it is then impossible to pass original ie len. + * This chunk is not used yet so if setting probe resp template yields + * problems with beaconing or crashes firmware look here. + */ + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_BCN_PRB_INFO); + tlv->len = __cpu_to_le16(sizeof(*info) + prb_ies_len); + info = (void *)tlv->value; + info->caps = __cpu_to_le32(prb_caps); + info->erp = __cpu_to_le32(prb_erp); + memcpy(info->ies, prb_ies, prb_ies_len); + + ptr += sizeof(*tlv); + ptr += sizeof(*info); + ptr += prb_ies_len; + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE); + tlv->len = __cpu_to_le16(roundup(bcn->len, 4)); + memcpy(tlv->value, bcn->data, bcn->len); + + /* FIXME: Adjust TSF? */ + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv bcn tmpl vdev_id %i\n", + vdev_id); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_prb_tmpl(struct ath10k *ar, u32 vdev_id, + struct sk_buff *prb) +{ + struct wmi_tlv_prb_tmpl_cmd *cmd; + struct wmi_tlv_bcn_prb_info *info; + struct wmi_tlv *tlv; + struct sk_buff *skb; + void *ptr; + size_t len; + + len = sizeof(*tlv) + sizeof(*cmd) + + sizeof(*tlv) + sizeof(*info) + + sizeof(*tlv) + roundup(prb->len, 4); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_PRB_TMPL_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->buf_len = __cpu_to_le32(prb->len); + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_BCN_PRB_INFO); + tlv->len = __cpu_to_le16(sizeof(*info)); + info = (void *)tlv->value; + info->caps = 0; + info->erp = 0; + + ptr += sizeof(*tlv); + ptr += sizeof(*info); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE); + tlv->len = __cpu_to_le16(roundup(prb->len, 4)); + memcpy(tlv->value, prb->data, prb->len); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv prb tmpl vdev_id %i\n", + vdev_id); + return skb; +} + +static struct sk_buff * +ath10k_wmi_tlv_op_gen_p2p_go_bcn_ie(struct ath10k *ar, u32 vdev_id, + const u8 *p2p_ie) +{ + struct wmi_tlv_p2p_go_bcn_ie *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + void *ptr; + size_t len; + + len = sizeof(*tlv) + sizeof(*cmd) + + sizeof(*tlv) + roundup(p2p_ie[1] + 2, 4); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ptr = (void *)skb->data; + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_P2P_GO_SET_BEACON_IE); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->ie_len = __cpu_to_le32(p2p_ie[1] + 2); + + ptr += sizeof(*tlv); + ptr += sizeof(*cmd); + + tlv = ptr; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_ARRAY_BYTE); + tlv->len = __cpu_to_le16(roundup(p2p_ie[1] + 2, 4)); + memcpy(tlv->value, p2p_ie, p2p_ie[1] + 2); + + ptr += sizeof(*tlv); + ptr += roundup(p2p_ie[1] + 2, 4); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi tlv p2p go bcn ie for vdev %i\n", + vdev_id); + return skb; +} + +/****************/ +/* TLV mappings */ +/****************/ + +static struct wmi_cmd_map wmi_tlv_cmd_map = { + .init_cmdid = WMI_TLV_INIT_CMDID, + .start_scan_cmdid = WMI_TLV_START_SCAN_CMDID, + .stop_scan_cmdid = WMI_TLV_STOP_SCAN_CMDID, + .scan_chan_list_cmdid = WMI_TLV_SCAN_CHAN_LIST_CMDID, + .scan_sch_prio_tbl_cmdid = WMI_TLV_SCAN_SCH_PRIO_TBL_CMDID, + .pdev_set_regdomain_cmdid = WMI_TLV_PDEV_SET_REGDOMAIN_CMDID, + .pdev_set_channel_cmdid = WMI_TLV_PDEV_SET_CHANNEL_CMDID, + .pdev_set_param_cmdid = WMI_TLV_PDEV_SET_PARAM_CMDID, + .pdev_pktlog_enable_cmdid = WMI_TLV_PDEV_PKTLOG_ENABLE_CMDID, + .pdev_pktlog_disable_cmdid = WMI_TLV_PDEV_PKTLOG_DISABLE_CMDID, + .pdev_set_wmm_params_cmdid = WMI_TLV_PDEV_SET_WMM_PARAMS_CMDID, + .pdev_set_ht_cap_ie_cmdid = WMI_TLV_PDEV_SET_HT_CAP_IE_CMDID, + .pdev_set_vht_cap_ie_cmdid = WMI_TLV_PDEV_SET_VHT_CAP_IE_CMDID, + .pdev_set_dscp_tid_map_cmdid = WMI_TLV_PDEV_SET_DSCP_TID_MAP_CMDID, + .pdev_set_quiet_mode_cmdid = WMI_TLV_PDEV_SET_QUIET_MODE_CMDID, + .pdev_green_ap_ps_enable_cmdid = WMI_TLV_PDEV_GREEN_AP_PS_ENABLE_CMDID, + .pdev_get_tpc_config_cmdid = WMI_TLV_PDEV_GET_TPC_CONFIG_CMDID, + .pdev_set_base_macaddr_cmdid = WMI_TLV_PDEV_SET_BASE_MACADDR_CMDID, + .vdev_create_cmdid = WMI_TLV_VDEV_CREATE_CMDID, + .vdev_delete_cmdid = WMI_TLV_VDEV_DELETE_CMDID, + .vdev_start_request_cmdid = WMI_TLV_VDEV_START_REQUEST_CMDID, + .vdev_restart_request_cmdid = WMI_TLV_VDEV_RESTART_REQUEST_CMDID, + .vdev_up_cmdid = WMI_TLV_VDEV_UP_CMDID, + .vdev_stop_cmdid = WMI_TLV_VDEV_STOP_CMDID, + .vdev_down_cmdid = WMI_TLV_VDEV_DOWN_CMDID, + .vdev_set_param_cmdid = WMI_TLV_VDEV_SET_PARAM_CMDID, + .vdev_install_key_cmdid = WMI_TLV_VDEV_INSTALL_KEY_CMDID, + .peer_create_cmdid = WMI_TLV_PEER_CREATE_CMDID, + .peer_delete_cmdid = WMI_TLV_PEER_DELETE_CMDID, + .peer_flush_tids_cmdid = WMI_TLV_PEER_FLUSH_TIDS_CMDID, + .peer_set_param_cmdid = WMI_TLV_PEER_SET_PARAM_CMDID, + .peer_assoc_cmdid = WMI_TLV_PEER_ASSOC_CMDID, + .peer_add_wds_entry_cmdid = WMI_TLV_PEER_ADD_WDS_ENTRY_CMDID, + .peer_remove_wds_entry_cmdid = WMI_TLV_PEER_REMOVE_WDS_ENTRY_CMDID, + .peer_mcast_group_cmdid = WMI_TLV_PEER_MCAST_GROUP_CMDID, + .bcn_tx_cmdid = WMI_TLV_BCN_TX_CMDID, + .pdev_send_bcn_cmdid = WMI_TLV_PDEV_SEND_BCN_CMDID, + .bcn_tmpl_cmdid = WMI_TLV_BCN_TMPL_CMDID, + .bcn_filter_rx_cmdid = WMI_TLV_BCN_FILTER_RX_CMDID, + .prb_req_filter_rx_cmdid = WMI_TLV_PRB_REQ_FILTER_RX_CMDID, + .mgmt_tx_cmdid = WMI_TLV_MGMT_TX_CMDID, + .prb_tmpl_cmdid = WMI_TLV_PRB_TMPL_CMDID, + .addba_clear_resp_cmdid = WMI_TLV_ADDBA_CLEAR_RESP_CMDID, + .addba_send_cmdid = WMI_TLV_ADDBA_SEND_CMDID, + .addba_status_cmdid = WMI_TLV_ADDBA_STATUS_CMDID, + .delba_send_cmdid = WMI_TLV_DELBA_SEND_CMDID, + .addba_set_resp_cmdid = WMI_TLV_ADDBA_SET_RESP_CMDID, + .send_singleamsdu_cmdid = WMI_TLV_SEND_SINGLEAMSDU_CMDID, + .sta_powersave_mode_cmdid = WMI_TLV_STA_POWERSAVE_MODE_CMDID, + .sta_powersave_param_cmdid = WMI_TLV_STA_POWERSAVE_PARAM_CMDID, + .sta_mimo_ps_mode_cmdid = WMI_TLV_STA_MIMO_PS_MODE_CMDID, + .pdev_dfs_enable_cmdid = WMI_TLV_PDEV_DFS_ENABLE_CMDID, + .pdev_dfs_disable_cmdid = WMI_TLV_PDEV_DFS_DISABLE_CMDID, + .roam_scan_mode = WMI_TLV_ROAM_SCAN_MODE, + .roam_scan_rssi_threshold = WMI_TLV_ROAM_SCAN_RSSI_THRESHOLD, + .roam_scan_period = WMI_TLV_ROAM_SCAN_PERIOD, + .roam_scan_rssi_change_threshold = + WMI_TLV_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + .roam_ap_profile = WMI_TLV_ROAM_AP_PROFILE, + .ofl_scan_add_ap_profile = WMI_TLV_ROAM_AP_PROFILE, + .ofl_scan_remove_ap_profile = WMI_TLV_OFL_SCAN_REMOVE_AP_PROFILE, + .ofl_scan_period = WMI_TLV_OFL_SCAN_PERIOD, + .p2p_dev_set_device_info = WMI_TLV_P2P_DEV_SET_DEVICE_INFO, + .p2p_dev_set_discoverability = WMI_TLV_P2P_DEV_SET_DISCOVERABILITY, + .p2p_go_set_beacon_ie = WMI_TLV_P2P_GO_SET_BEACON_IE, + .p2p_go_set_probe_resp_ie = WMI_TLV_P2P_GO_SET_PROBE_RESP_IE, + .p2p_set_vendor_ie_data_cmdid = WMI_TLV_P2P_SET_VENDOR_IE_DATA_CMDID, + .ap_ps_peer_param_cmdid = WMI_TLV_AP_PS_PEER_PARAM_CMDID, + .ap_ps_peer_uapsd_coex_cmdid = WMI_TLV_AP_PS_PEER_UAPSD_COEX_CMDID, + .peer_rate_retry_sched_cmdid = WMI_TLV_PEER_RATE_RETRY_SCHED_CMDID, + .wlan_profile_trigger_cmdid = WMI_TLV_WLAN_PROFILE_TRIGGER_CMDID, + .wlan_profile_set_hist_intvl_cmdid = + WMI_TLV_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + .wlan_profile_get_profile_data_cmdid = + WMI_TLV_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + .wlan_profile_enable_profile_id_cmdid = + WMI_TLV_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + .wlan_profile_list_profile_id_cmdid = + WMI_TLV_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + .pdev_suspend_cmdid = WMI_TLV_PDEV_SUSPEND_CMDID, + .pdev_resume_cmdid = WMI_TLV_PDEV_RESUME_CMDID, + .add_bcn_filter_cmdid = WMI_TLV_ADD_BCN_FILTER_CMDID, + .rmv_bcn_filter_cmdid = WMI_TLV_RMV_BCN_FILTER_CMDID, + .wow_add_wake_pattern_cmdid = WMI_TLV_WOW_ADD_WAKE_PATTERN_CMDID, + .wow_del_wake_pattern_cmdid = WMI_TLV_WOW_DEL_WAKE_PATTERN_CMDID, + .wow_enable_disable_wake_event_cmdid = + WMI_TLV_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + .wow_enable_cmdid = WMI_TLV_WOW_ENABLE_CMDID, + .wow_hostwakeup_from_sleep_cmdid = + WMI_TLV_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + .rtt_measreq_cmdid = WMI_TLV_RTT_MEASREQ_CMDID, + .rtt_tsf_cmdid = WMI_TLV_RTT_TSF_CMDID, + .vdev_spectral_scan_configure_cmdid = WMI_TLV_SPECTRAL_SCAN_CONF_CMDID, + .vdev_spectral_scan_enable_cmdid = WMI_TLV_SPECTRAL_SCAN_ENABLE_CMDID, + .request_stats_cmdid = WMI_TLV_REQUEST_STATS_CMDID, + .set_arp_ns_offload_cmdid = WMI_TLV_SET_ARP_NS_OFFLOAD_CMDID, + .network_list_offload_config_cmdid = + WMI_TLV_NETWORK_LIST_OFFLOAD_CONFIG_CMDID, + .gtk_offload_cmdid = WMI_TLV_GTK_OFFLOAD_CMDID, + .csa_offload_enable_cmdid = WMI_TLV_CSA_OFFLOAD_ENABLE_CMDID, + .csa_offload_chanswitch_cmdid = WMI_TLV_CSA_OFFLOAD_CHANSWITCH_CMDID, + .chatter_set_mode_cmdid = WMI_TLV_CHATTER_SET_MODE_CMDID, + .peer_tid_addba_cmdid = WMI_TLV_PEER_TID_ADDBA_CMDID, + .peer_tid_delba_cmdid = WMI_TLV_PEER_TID_DELBA_CMDID, + .sta_dtim_ps_method_cmdid = WMI_TLV_STA_DTIM_PS_METHOD_CMDID, + .sta_uapsd_auto_trig_cmdid = WMI_TLV_STA_UAPSD_AUTO_TRIG_CMDID, + .sta_keepalive_cmd = WMI_TLV_STA_KEEPALIVE_CMDID, + .echo_cmdid = WMI_TLV_ECHO_CMDID, + .pdev_utf_cmdid = WMI_TLV_PDEV_UTF_CMDID, + .dbglog_cfg_cmdid = WMI_TLV_DBGLOG_CFG_CMDID, + .pdev_qvit_cmdid = WMI_TLV_PDEV_QVIT_CMDID, + .pdev_ftm_intg_cmdid = WMI_TLV_PDEV_FTM_INTG_CMDID, + .vdev_set_keepalive_cmdid = WMI_TLV_VDEV_SET_KEEPALIVE_CMDID, + .vdev_get_keepalive_cmdid = WMI_TLV_VDEV_GET_KEEPALIVE_CMDID, + .force_fw_hang_cmdid = WMI_TLV_FORCE_FW_HANG_CMDID, + .gpio_config_cmdid = WMI_TLV_GPIO_CONFIG_CMDID, + .gpio_output_cmdid = WMI_TLV_GPIO_OUTPUT_CMDID, + .pdev_get_temperature_cmdid = WMI_TLV_CMD_UNSUPPORTED, + .vdev_set_wmm_params_cmdid = WMI_TLV_VDEV_SET_WMM_PARAMS_CMDID, +}; + +static struct wmi_pdev_param_map wmi_tlv_pdev_param_map = { + .tx_chain_mask = WMI_TLV_PDEV_PARAM_TX_CHAIN_MASK, + .rx_chain_mask = WMI_TLV_PDEV_PARAM_RX_CHAIN_MASK, + .txpower_limit2g = WMI_TLV_PDEV_PARAM_TXPOWER_LIMIT2G, + .txpower_limit5g = WMI_TLV_PDEV_PARAM_TXPOWER_LIMIT5G, + .txpower_scale = WMI_TLV_PDEV_PARAM_TXPOWER_SCALE, + .beacon_gen_mode = WMI_TLV_PDEV_PARAM_BEACON_GEN_MODE, + .beacon_tx_mode = WMI_TLV_PDEV_PARAM_BEACON_TX_MODE, + .resmgr_offchan_mode = WMI_TLV_PDEV_PARAM_RESMGR_OFFCHAN_MODE, + .protection_mode = WMI_TLV_PDEV_PARAM_PROTECTION_MODE, + .dynamic_bw = WMI_TLV_PDEV_PARAM_DYNAMIC_BW, + .non_agg_sw_retry_th = WMI_TLV_PDEV_PARAM_NON_AGG_SW_RETRY_TH, + .agg_sw_retry_th = WMI_TLV_PDEV_PARAM_AGG_SW_RETRY_TH, + .sta_kickout_th = WMI_TLV_PDEV_PARAM_STA_KICKOUT_TH, + .ac_aggrsize_scaling = WMI_TLV_PDEV_PARAM_AC_AGGRSIZE_SCALING, + .ltr_enable = WMI_TLV_PDEV_PARAM_LTR_ENABLE, + .ltr_ac_latency_be = WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_BE, + .ltr_ac_latency_bk = WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_BK, + .ltr_ac_latency_vi = WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_VI, + .ltr_ac_latency_vo = WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_VO, + .ltr_ac_latency_timeout = WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT, + .ltr_sleep_override = WMI_TLV_PDEV_PARAM_LTR_SLEEP_OVERRIDE, + .ltr_rx_override = WMI_TLV_PDEV_PARAM_LTR_RX_OVERRIDE, + .ltr_tx_activity_timeout = WMI_TLV_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT, + .l1ss_enable = WMI_TLV_PDEV_PARAM_L1SS_ENABLE, + .dsleep_enable = WMI_TLV_PDEV_PARAM_DSLEEP_ENABLE, + .pcielp_txbuf_flush = WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_FLUSH, + .pcielp_txbuf_watermark = WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_TMO_EN, + .pcielp_txbuf_tmo_en = WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_TMO_EN, + .pcielp_txbuf_tmo_value = WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE, + .pdev_stats_update_period = WMI_TLV_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD, + .vdev_stats_update_period = WMI_TLV_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD, + .peer_stats_update_period = WMI_TLV_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD, + .bcnflt_stats_update_period = + WMI_TLV_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, + .pmf_qos = WMI_TLV_PDEV_PARAM_PMF_QOS, + .arp_ac_override = WMI_TLV_PDEV_PARAM_ARP_AC_OVERRIDE, + .dcs = WMI_TLV_PDEV_PARAM_DCS, + .ani_enable = WMI_TLV_PDEV_PARAM_ANI_ENABLE, + .ani_poll_period = WMI_TLV_PDEV_PARAM_ANI_POLL_PERIOD, + .ani_listen_period = WMI_TLV_PDEV_PARAM_ANI_LISTEN_PERIOD, + .ani_ofdm_level = WMI_TLV_PDEV_PARAM_ANI_OFDM_LEVEL, + .ani_cck_level = WMI_TLV_PDEV_PARAM_ANI_CCK_LEVEL, + .dyntxchain = WMI_TLV_PDEV_PARAM_DYNTXCHAIN, + .proxy_sta = WMI_TLV_PDEV_PARAM_PROXY_STA, + .idle_ps_config = WMI_TLV_PDEV_PARAM_IDLE_PS_CONFIG, + .power_gating_sleep = WMI_TLV_PDEV_PARAM_POWER_GATING_SLEEP, + .fast_channel_reset = WMI_TLV_PDEV_PARAM_UNSUPPORTED, + .burst_dur = WMI_TLV_PDEV_PARAM_BURST_DUR, + .burst_enable = WMI_TLV_PDEV_PARAM_BURST_ENABLE, + .cal_period = WMI_PDEV_PARAM_UNSUPPORTED, +}; + +static struct wmi_vdev_param_map wmi_tlv_vdev_param_map = { + .rts_threshold = WMI_TLV_VDEV_PARAM_RTS_THRESHOLD, + .fragmentation_threshold = WMI_TLV_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + .beacon_interval = WMI_TLV_VDEV_PARAM_BEACON_INTERVAL, + .listen_interval = WMI_TLV_VDEV_PARAM_LISTEN_INTERVAL, + .multicast_rate = WMI_TLV_VDEV_PARAM_MULTICAST_RATE, + .mgmt_tx_rate = WMI_TLV_VDEV_PARAM_MGMT_TX_RATE, + .slot_time = WMI_TLV_VDEV_PARAM_SLOT_TIME, + .preamble = WMI_TLV_VDEV_PARAM_PREAMBLE, + .swba_time = WMI_TLV_VDEV_PARAM_SWBA_TIME, + .wmi_vdev_stats_update_period = WMI_TLV_VDEV_STATS_UPDATE_PERIOD, + .wmi_vdev_pwrsave_ageout_time = WMI_TLV_VDEV_PWRSAVE_AGEOUT_TIME, + .wmi_vdev_host_swba_interval = WMI_TLV_VDEV_HOST_SWBA_INTERVAL, + .dtim_period = WMI_TLV_VDEV_PARAM_DTIM_PERIOD, + .wmi_vdev_oc_scheduler_air_time_limit = + WMI_TLV_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT, + .wds = WMI_TLV_VDEV_PARAM_WDS, + .atim_window = WMI_TLV_VDEV_PARAM_ATIM_WINDOW, + .bmiss_count_max = WMI_TLV_VDEV_PARAM_BMISS_COUNT_MAX, + .bmiss_first_bcnt = WMI_TLV_VDEV_PARAM_BMISS_FIRST_BCNT, + .bmiss_final_bcnt = WMI_TLV_VDEV_PARAM_BMISS_FINAL_BCNT, + .feature_wmm = WMI_TLV_VDEV_PARAM_FEATURE_WMM, + .chwidth = WMI_TLV_VDEV_PARAM_CHWIDTH, + .chextoffset = WMI_TLV_VDEV_PARAM_CHEXTOFFSET, + .disable_htprotection = WMI_TLV_VDEV_PARAM_DISABLE_HTPROTECTION, + .sta_quickkickout = WMI_TLV_VDEV_PARAM_STA_QUICKKICKOUT, + .mgmt_rate = WMI_TLV_VDEV_PARAM_MGMT_RATE, + .protection_mode = WMI_TLV_VDEV_PARAM_PROTECTION_MODE, + .fixed_rate = WMI_TLV_VDEV_PARAM_FIXED_RATE, + .sgi = WMI_TLV_VDEV_PARAM_SGI, + .ldpc = WMI_TLV_VDEV_PARAM_LDPC, + .tx_stbc = WMI_TLV_VDEV_PARAM_TX_STBC, + .rx_stbc = WMI_TLV_VDEV_PARAM_RX_STBC, + .intra_bss_fwd = WMI_TLV_VDEV_PARAM_INTRA_BSS_FWD, + .def_keyid = WMI_TLV_VDEV_PARAM_DEF_KEYID, + .nss = WMI_TLV_VDEV_PARAM_NSS, + .bcast_data_rate = WMI_TLV_VDEV_PARAM_BCAST_DATA_RATE, + .mcast_data_rate = WMI_TLV_VDEV_PARAM_MCAST_DATA_RATE, + .mcast_indicate = WMI_TLV_VDEV_PARAM_MCAST_INDICATE, + .dhcp_indicate = WMI_TLV_VDEV_PARAM_DHCP_INDICATE, + .unknown_dest_indicate = WMI_TLV_VDEV_PARAM_UNKNOWN_DEST_INDICATE, + .ap_keepalive_min_idle_inactive_time_secs = + WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + .ap_keepalive_max_idle_inactive_time_secs = + WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + .ap_keepalive_max_unresponsive_time_secs = + WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + .ap_enable_nawds = WMI_TLV_VDEV_PARAM_AP_ENABLE_NAWDS, + .mcast2ucast_set = WMI_TLV_VDEV_PARAM_UNSUPPORTED, + .enable_rtscts = WMI_TLV_VDEV_PARAM_ENABLE_RTSCTS, + .txbf = WMI_TLV_VDEV_PARAM_TXBF, + .packet_powersave = WMI_TLV_VDEV_PARAM_PACKET_POWERSAVE, + .drop_unencry = WMI_TLV_VDEV_PARAM_DROP_UNENCRY, + .tx_encap_type = WMI_TLV_VDEV_PARAM_TX_ENCAP_TYPE, + .ap_detect_out_of_sync_sleeping_sta_time_secs = + WMI_TLV_VDEV_PARAM_UNSUPPORTED, +}; + +static const struct wmi_ops wmi_tlv_ops = { + .rx = ath10k_wmi_tlv_op_rx, + .map_svc = wmi_tlv_svc_map, + + .pull_scan = ath10k_wmi_tlv_op_pull_scan_ev, + .pull_mgmt_rx = ath10k_wmi_tlv_op_pull_mgmt_rx_ev, + .pull_ch_info = ath10k_wmi_tlv_op_pull_ch_info_ev, + .pull_vdev_start = ath10k_wmi_tlv_op_pull_vdev_start_ev, + .pull_peer_kick = ath10k_wmi_tlv_op_pull_peer_kick_ev, + .pull_swba = ath10k_wmi_tlv_op_pull_swba_ev, + .pull_phyerr = ath10k_wmi_tlv_op_pull_phyerr_ev, + .pull_svc_rdy = ath10k_wmi_tlv_op_pull_svc_rdy_ev, + .pull_rdy = ath10k_wmi_tlv_op_pull_rdy_ev, + .pull_fw_stats = ath10k_wmi_tlv_op_pull_fw_stats, + + .gen_pdev_suspend = ath10k_wmi_tlv_op_gen_pdev_suspend, + .gen_pdev_resume = ath10k_wmi_tlv_op_gen_pdev_resume, + .gen_pdev_set_rd = ath10k_wmi_tlv_op_gen_pdev_set_rd, + .gen_pdev_set_param = ath10k_wmi_tlv_op_gen_pdev_set_param, + .gen_init = ath10k_wmi_tlv_op_gen_init, + .gen_start_scan = ath10k_wmi_tlv_op_gen_start_scan, + .gen_stop_scan = ath10k_wmi_tlv_op_gen_stop_scan, + .gen_vdev_create = ath10k_wmi_tlv_op_gen_vdev_create, + .gen_vdev_delete = ath10k_wmi_tlv_op_gen_vdev_delete, + .gen_vdev_start = ath10k_wmi_tlv_op_gen_vdev_start, + .gen_vdev_stop = ath10k_wmi_tlv_op_gen_vdev_stop, + .gen_vdev_up = ath10k_wmi_tlv_op_gen_vdev_up, + .gen_vdev_down = ath10k_wmi_tlv_op_gen_vdev_down, + .gen_vdev_set_param = ath10k_wmi_tlv_op_gen_vdev_set_param, + .gen_vdev_install_key = ath10k_wmi_tlv_op_gen_vdev_install_key, + .gen_vdev_wmm_conf = ath10k_wmi_tlv_op_gen_vdev_wmm_conf, + .gen_peer_create = ath10k_wmi_tlv_op_gen_peer_create, + .gen_peer_delete = ath10k_wmi_tlv_op_gen_peer_delete, + .gen_peer_flush = ath10k_wmi_tlv_op_gen_peer_flush, + .gen_peer_set_param = ath10k_wmi_tlv_op_gen_peer_set_param, + .gen_peer_assoc = ath10k_wmi_tlv_op_gen_peer_assoc, + .gen_set_psmode = ath10k_wmi_tlv_op_gen_set_psmode, + .gen_set_sta_ps = ath10k_wmi_tlv_op_gen_set_sta_ps, + .gen_set_ap_ps = ath10k_wmi_tlv_op_gen_set_ap_ps, + .gen_scan_chan_list = ath10k_wmi_tlv_op_gen_scan_chan_list, + .gen_beacon_dma = ath10k_wmi_tlv_op_gen_beacon_dma, + .gen_pdev_set_wmm = ath10k_wmi_tlv_op_gen_pdev_set_wmm, + .gen_request_stats = ath10k_wmi_tlv_op_gen_request_stats, + .gen_force_fw_hang = ath10k_wmi_tlv_op_gen_force_fw_hang, + /* .gen_mgmt_tx = not implemented; HTT is used */ + .gen_dbglog_cfg = ath10k_wmi_tlv_op_gen_dbglog_cfg, + .gen_pktlog_enable = ath10k_wmi_tlv_op_gen_pktlog_enable, + .gen_pktlog_disable = ath10k_wmi_tlv_op_gen_pktlog_disable, + /* .gen_pdev_set_quiet_mode not implemented */ + /* .gen_pdev_get_temperature not implemented */ + /* .gen_addba_clear_resp not implemented */ + /* .gen_addba_send not implemented */ + /* .gen_addba_set_resp not implemented */ + /* .gen_delba_send not implemented */ + .gen_bcn_tmpl = ath10k_wmi_tlv_op_gen_bcn_tmpl, + .gen_prb_tmpl = ath10k_wmi_tlv_op_gen_prb_tmpl, + .gen_p2p_go_bcn_ie = ath10k_wmi_tlv_op_gen_p2p_go_bcn_ie, + .gen_vdev_sta_uapsd = ath10k_wmi_tlv_op_gen_vdev_sta_uapsd, + .gen_sta_keepalive = ath10k_wmi_tlv_op_gen_sta_keepalive, +}; + +/************/ +/* TLV init */ +/************/ + +void ath10k_wmi_tlv_attach(struct ath10k *ar) +{ + ar->wmi.cmd = &wmi_tlv_cmd_map; + ar->wmi.vdev_param = &wmi_tlv_vdev_param_map; + ar->wmi.pdev_param = &wmi_tlv_pdev_param_map; + ar->wmi.ops = &wmi_tlv_ops; +} diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h new file mode 100644 index 000000000000..de68fe76eae6 --- /dev/null +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h @@ -0,0 +1,1444 @@ +/* + * Copyright (c) 2005-2011 Atheros Communications Inc. + * Copyright (c) 2011-2014 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef _WMI_TLV_H +#define _WMI_TLV_H + +#define WMI_TLV_CMD(grp_id) (((grp_id) << 12) | 0x1) +#define WMI_TLV_EV(grp_id) (((grp_id) << 12) | 0x1) +#define WMI_TLV_CMD_UNSUPPORTED 0 +#define WMI_TLV_PDEV_PARAM_UNSUPPORTED 0 +#define WMI_TLV_VDEV_PARAM_UNSUPPORTED 0 + +enum wmi_tlv_grp_id { + WMI_TLV_GRP_START = 0x3, + WMI_TLV_GRP_SCAN = WMI_TLV_GRP_START, + WMI_TLV_GRP_PDEV, + WMI_TLV_GRP_VDEV, + WMI_TLV_GRP_PEER, + WMI_TLV_GRP_MGMT, + WMI_TLV_GRP_BA_NEG, + WMI_TLV_GRP_STA_PS, + WMI_TLV_GRP_DFS, + WMI_TLV_GRP_ROAM, + WMI_TLV_GRP_OFL_SCAN, + WMI_TLV_GRP_P2P, + WMI_TLV_GRP_AP_PS, + WMI_TLV_GRP_RATECTL, + WMI_TLV_GRP_PROFILE, + WMI_TLV_GRP_SUSPEND, + WMI_TLV_GRP_BCN_FILTER, + WMI_TLV_GRP_WOW, + WMI_TLV_GRP_RTT, + WMI_TLV_GRP_SPECTRAL, + WMI_TLV_GRP_STATS, + WMI_TLV_GRP_ARP_NS_OFL, + WMI_TLV_GRP_NLO_OFL, + WMI_TLV_GRP_GTK_OFL, + WMI_TLV_GRP_CSA_OFL, + WMI_TLV_GRP_CHATTER, + WMI_TLV_GRP_TID_ADDBA, + WMI_TLV_GRP_MISC, + WMI_TLV_GRP_GPIO, + WMI_TLV_GRP_FWTEST, + WMI_TLV_GRP_TDLS, + WMI_TLV_GRP_RESMGR, + WMI_TLV_GRP_STA_SMPS, + WMI_TLV_GRP_WLAN_HB, + WMI_TLV_GRP_RMC, + WMI_TLV_GRP_MHF_OFL, + WMI_TLV_GRP_LOCATION_SCAN, + WMI_TLV_GRP_OEM, + WMI_TLV_GRP_NAN, + WMI_TLV_GRP_COEX, + WMI_TLV_GRP_OBSS_OFL, + WMI_TLV_GRP_LPI, + WMI_TLV_GRP_EXTSCAN, + WMI_TLV_GRP_DHCP_OFL, + WMI_TLV_GRP_IPA, + WMI_TLV_GRP_MDNS_OFL, + WMI_TLV_GRP_SAP_OFL, +}; + +enum wmi_tlv_cmd_id { + WMI_TLV_INIT_CMDID = 0x1, + WMI_TLV_START_SCAN_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_SCAN), + WMI_TLV_STOP_SCAN_CMDID, + WMI_TLV_SCAN_CHAN_LIST_CMDID, + WMI_TLV_SCAN_SCH_PRIO_TBL_CMDID, + WMI_TLV_SCAN_UPDATE_REQUEST_CMDID, + WMI_TLV_SCAN_PROB_REQ_OUI_CMDID, + WMI_TLV_PDEV_SET_REGDOMAIN_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_PDEV), + WMI_TLV_PDEV_SET_CHANNEL_CMDID, + WMI_TLV_PDEV_SET_PARAM_CMDID, + WMI_TLV_PDEV_PKTLOG_ENABLE_CMDID, + WMI_TLV_PDEV_PKTLOG_DISABLE_CMDID, + WMI_TLV_PDEV_SET_WMM_PARAMS_CMDID, + WMI_TLV_PDEV_SET_HT_CAP_IE_CMDID, + WMI_TLV_PDEV_SET_VHT_CAP_IE_CMDID, + WMI_TLV_PDEV_SET_DSCP_TID_MAP_CMDID, + WMI_TLV_PDEV_SET_QUIET_MODE_CMDID, + WMI_TLV_PDEV_GREEN_AP_PS_ENABLE_CMDID, + WMI_TLV_PDEV_GET_TPC_CONFIG_CMDID, + WMI_TLV_PDEV_SET_BASE_MACADDR_CMDID, + WMI_TLV_PDEV_DUMP_CMDID, + WMI_TLV_PDEV_SET_LED_CONFIG_CMDID, + WMI_TLV_PDEV_GET_TEMPERATURE_CMDID, + WMI_TLV_PDEV_SET_LED_FLASHING_CMDID, + WMI_TLV_VDEV_CREATE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_VDEV), + WMI_TLV_VDEV_DELETE_CMDID, + WMI_TLV_VDEV_START_REQUEST_CMDID, + WMI_TLV_VDEV_RESTART_REQUEST_CMDID, + WMI_TLV_VDEV_UP_CMDID, + WMI_TLV_VDEV_STOP_CMDID, + WMI_TLV_VDEV_DOWN_CMDID, + WMI_TLV_VDEV_SET_PARAM_CMDID, + WMI_TLV_VDEV_INSTALL_KEY_CMDID, + WMI_TLV_VDEV_WNM_SLEEPMODE_CMDID, + WMI_TLV_VDEV_WMM_ADDTS_CMDID, + WMI_TLV_VDEV_WMM_DELTS_CMDID, + WMI_TLV_VDEV_SET_WMM_PARAMS_CMDID, + WMI_TLV_VDEV_SET_GTX_PARAMS_CMDID, + WMI_TLV_VDEV_IPSEC_NATKEEPALIVE_FILTER_CMDID, + WMI_TLV_VDEV_PLMREQ_START_CMDID, + WMI_TLV_VDEV_PLMREQ_STOP_CMDID, + WMI_TLV_PEER_CREATE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_PEER), + WMI_TLV_PEER_DELETE_CMDID, + WMI_TLV_PEER_FLUSH_TIDS_CMDID, + WMI_TLV_PEER_SET_PARAM_CMDID, + WMI_TLV_PEER_ASSOC_CMDID, + WMI_TLV_PEER_ADD_WDS_ENTRY_CMDID, + WMI_TLV_PEER_REMOVE_WDS_ENTRY_CMDID, + WMI_TLV_PEER_MCAST_GROUP_CMDID, + WMI_TLV_PEER_INFO_REQ_CMDID, + WMI_TLV_PEER_GET_ESTIMATED_LINKSPEED_CMDID, + WMI_TLV_BCN_TX_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_MGMT), + WMI_TLV_PDEV_SEND_BCN_CMDID, + WMI_TLV_BCN_TMPL_CMDID, + WMI_TLV_BCN_FILTER_RX_CMDID, + WMI_TLV_PRB_REQ_FILTER_RX_CMDID, + WMI_TLV_MGMT_TX_CMDID, + WMI_TLV_PRB_TMPL_CMDID, + WMI_TLV_ADDBA_CLEAR_RESP_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_BA_NEG), + WMI_TLV_ADDBA_SEND_CMDID, + WMI_TLV_ADDBA_STATUS_CMDID, + WMI_TLV_DELBA_SEND_CMDID, + WMI_TLV_ADDBA_SET_RESP_CMDID, + WMI_TLV_SEND_SINGLEAMSDU_CMDID, + WMI_TLV_STA_POWERSAVE_MODE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_STA_PS), + WMI_TLV_STA_POWERSAVE_PARAM_CMDID, + WMI_TLV_STA_MIMO_PS_MODE_CMDID, + WMI_TLV_PDEV_DFS_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_DFS), + WMI_TLV_PDEV_DFS_DISABLE_CMDID, + WMI_TLV_DFS_PHYERR_FILTER_ENA_CMDID, + WMI_TLV_DFS_PHYERR_FILTER_DIS_CMDID, + WMI_TLV_ROAM_SCAN_MODE = WMI_TLV_CMD(WMI_TLV_GRP_ROAM), + WMI_TLV_ROAM_SCAN_RSSI_THRESHOLD, + WMI_TLV_ROAM_SCAN_PERIOD, + WMI_TLV_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + WMI_TLV_ROAM_AP_PROFILE, + WMI_TLV_ROAM_CHAN_LIST, + WMI_TLV_ROAM_SCAN_CMD, + WMI_TLV_ROAM_SYNCH_COMPLETE, + WMI_TLV_ROAM_SET_RIC_REQUEST_CMDID, + WMI_TLV_ROAM_INVOKE_CMDID, + WMI_TLV_OFL_SCAN_ADD_AP_PROFILE = WMI_TLV_CMD(WMI_TLV_GRP_OFL_SCAN), + WMI_TLV_OFL_SCAN_REMOVE_AP_PROFILE, + WMI_TLV_OFL_SCAN_PERIOD, + WMI_TLV_P2P_DEV_SET_DEVICE_INFO = WMI_TLV_CMD(WMI_TLV_GRP_P2P), + WMI_TLV_P2P_DEV_SET_DISCOVERABILITY, + WMI_TLV_P2P_GO_SET_BEACON_IE, + WMI_TLV_P2P_GO_SET_PROBE_RESP_IE, + WMI_TLV_P2P_SET_VENDOR_IE_DATA_CMDID, + WMI_TLV_P2P_DISC_OFFLOAD_CONFIG_CMDID, + WMI_TLV_P2P_DISC_OFFLOAD_APPIE_CMDID, + WMI_TLV_P2P_DISC_OFFLOAD_PATTERN_CMDID, + WMI_TLV_P2P_SET_OPPPS_PARAM_CMDID, + WMI_TLV_AP_PS_PEER_PARAM_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_AP_PS), + WMI_TLV_AP_PS_PEER_UAPSD_COEX_CMDID, + WMI_TLV_PEER_RATE_RETRY_SCHED_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_RATECTL), + WMI_TLV_WLAN_PROFILE_TRIGGER_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_PROFILE), + WMI_TLV_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + WMI_TLV_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + WMI_TLV_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + WMI_TLV_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + WMI_TLV_PDEV_SUSPEND_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_SUSPEND), + WMI_TLV_PDEV_RESUME_CMDID, + WMI_TLV_ADD_BCN_FILTER_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_BCN_FILTER), + WMI_TLV_RMV_BCN_FILTER_CMDID, + WMI_TLV_WOW_ADD_WAKE_PATTERN_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_WOW), + WMI_TLV_WOW_DEL_WAKE_PATTERN_CMDID, + WMI_TLV_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + WMI_TLV_WOW_ENABLE_CMDID, + WMI_TLV_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + WMI_TLV_WOW_ACER_IOAC_ADD_KEEPALIVE_CMDID, + WMI_TLV_WOW_ACER_IOAC_DEL_KEEPALIVE_CMDID, + WMI_TLV_WOW_ACER_IOAC_ADD_WAKE_PATTERN_CMDID, + WMI_TLV_WOW_ACER_IOAC_DEL_WAKE_PATTERN_CMDID, + WMI_TLV_D0_WOW_ENABLE_DISABLE_CMDID, + WMI_TLV_EXTWOW_ENABLE_CMDID, + WMI_TLV_EXTWOW_SET_APP_TYPE1_PARAMS_CMDID, + WMI_TLV_EXTWOW_SET_APP_TYPE2_PARAMS_CMDID, + WMI_TLV_RTT_MEASREQ_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_RTT), + WMI_TLV_RTT_TSF_CMDID, + WMI_TLV_SPECTRAL_SCAN_CONF_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_SPECTRAL), + WMI_TLV_SPECTRAL_SCAN_ENABLE_CMDID, + WMI_TLV_REQUEST_STATS_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_STATS), + WMI_TLV_MCC_SCHED_TRAFFIC_STATS_CMDID, + WMI_TLV_REQUEST_STATS_EXT_CMDID, + WMI_TLV_REQUEST_LINK_STATS_CMDID, + WMI_TLV_START_LINK_STATS_CMDID, + WMI_TLV_CLEAR_LINK_STATS_CMDID, + WMI_TLV_SET_ARP_NS_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_ARP_NS_OFL), + WMI_TLV_ADD_PROACTIVE_ARP_RSP_PATTERN_CMDID, + WMI_TLV_DEL_PROACTIVE_ARP_RSP_PATTERN_CMDID, + WMI_TLV_NETWORK_LIST_OFFLOAD_CONFIG_CMDID = + WMI_TLV_CMD(WMI_TLV_GRP_NLO_OFL), + WMI_TLV_APFIND_CMDID, + WMI_TLV_GTK_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_GTK_OFL), + WMI_TLV_CSA_OFFLOAD_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_CSA_OFL), + WMI_TLV_CSA_OFFLOAD_CHANSWITCH_CMDID, + WMI_TLV_CHATTER_SET_MODE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_CHATTER), + WMI_TLV_CHATTER_ADD_COALESCING_FILTER_CMDID, + WMI_TLV_CHATTER_DELETE_COALESCING_FILTER_CMDID, + WMI_TLV_CHATTER_COALESCING_QUERY_CMDID, + WMI_TLV_PEER_TID_ADDBA_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_TID_ADDBA), + WMI_TLV_PEER_TID_DELBA_CMDID, + WMI_TLV_STA_DTIM_PS_METHOD_CMDID, + WMI_TLV_STA_UAPSD_AUTO_TRIG_CMDID, + WMI_TLV_STA_KEEPALIVE_CMDID, + WMI_TLV_BA_REQ_SSN_CMDID, + WMI_TLV_ECHO_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_MISC), + WMI_TLV_PDEV_UTF_CMDID, + WMI_TLV_DBGLOG_CFG_CMDID, + WMI_TLV_PDEV_QVIT_CMDID, + WMI_TLV_PDEV_FTM_INTG_CMDID, + WMI_TLV_VDEV_SET_KEEPALIVE_CMDID, + WMI_TLV_VDEV_GET_KEEPALIVE_CMDID, + WMI_TLV_FORCE_FW_HANG_CMDID, + WMI_TLV_SET_MCASTBCAST_FILTER_CMDID, + WMI_TLV_THERMAL_MGMT_CMDID, + WMI_TLV_HOST_AUTO_SHUTDOWN_CFG_CMDID, + WMI_TLV_TPC_CHAINMASK_CONFIG_CMDID, + WMI_TLV_SET_ANTENNA_DIVERSITY_CMDID, + WMI_TLV_GPIO_CONFIG_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_GPIO), + WMI_TLV_GPIO_OUTPUT_CMDID, + WMI_TLV_TXBF_CMDID, + WMI_TLV_FWTEST_VDEV_MCC_SET_TBTT_MODE_CMDID = + WMI_TLV_CMD(WMI_TLV_GRP_FWTEST), + WMI_TLV_FWTEST_P2P_SET_NOA_PARAM_CMDID, + WMI_TLV_UNIT_TEST_CMDID, + WMI_TLV_TDLS_SET_STATE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_TDLS), + WMI_TLV_TDLS_PEER_UPDATE_CMDID, + WMI_TLV_TDLS_SET_OFFCHAN_MODE_CMDID, + WMI_TLV_RESMGR_ADAPTIVE_OCS_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_RESMGR), + WMI_TLV_RESMGR_SET_CHAN_TIME_QUOTA_CMDID, + WMI_TLV_RESMGR_SET_CHAN_LATENCY_CMDID, + WMI_TLV_STA_SMPS_FORCE_MODE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_STA_SMPS), + WMI_TLV_STA_SMPS_PARAM_CMDID, + WMI_TLV_HB_SET_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_WLAN_HB), + WMI_TLV_HB_SET_TCP_PARAMS_CMDID, + WMI_TLV_HB_SET_TCP_PKT_FILTER_CMDID, + WMI_TLV_HB_SET_UDP_PARAMS_CMDID, + WMI_TLV_HB_SET_UDP_PKT_FILTER_CMDID, + WMI_TLV_RMC_SET_MODE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_RMC), + WMI_TLV_RMC_SET_ACTION_PERIOD_CMDID, + WMI_TLV_RMC_CONFIG_CMDID, + WMI_TLV_MHF_OFFLOAD_SET_MODE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_MHF_OFL), + WMI_TLV_MHF_OFFLOAD_PLUMB_ROUTING_TBL_CMDID, + WMI_TLV_BATCH_SCAN_ENABLE_CMDID = + WMI_TLV_CMD(WMI_TLV_GRP_LOCATION_SCAN), + WMI_TLV_BATCH_SCAN_DISABLE_CMDID, + WMI_TLV_BATCH_SCAN_TRIGGER_RESULT_CMDID, + WMI_TLV_OEM_REQ_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_OEM), + WMI_TLV_NAN_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_NAN), + WMI_TLV_MODEM_POWER_STATE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_COEX), + WMI_TLV_CHAN_AVOID_UPDATE_CMDID, + WMI_TLV_OBSS_SCAN_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_OBSS_OFL), + WMI_TLV_OBSS_SCAN_DISABLE_CMDID, + WMI_TLV_LPI_MGMT_SNOOPING_CONFIG_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_LPI), + WMI_TLV_LPI_START_SCAN_CMDID, + WMI_TLV_LPI_STOP_SCAN_CMDID, + WMI_TLV_EXTSCAN_START_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_EXTSCAN), + WMI_TLV_EXTSCAN_STOP_CMDID, + WMI_TLV_EXTSCAN_CONFIGURE_WLAN_CHANGE_MONITOR_CMDID, + WMI_TLV_EXTSCAN_CONFIGURE_HOTLIST_MONITOR_CMDID, + WMI_TLV_EXTSCAN_GET_CACHED_RESULTS_CMDID, + WMI_TLV_EXTSCAN_GET_WLAN_CHANGE_RESULTS_CMDID, + WMI_TLV_EXTSCAN_SET_CAPABILITIES_CMDID, + WMI_TLV_EXTSCAN_GET_CAPABILITIES_CMDID, + WMI_TLV_SET_DHCP_SERVER_OFFLOAD_CMDID = + WMI_TLV_CMD(WMI_TLV_GRP_DHCP_OFL), + WMI_TLV_IPA_OFFLOAD_ENABLE_DISABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_IPA), + WMI_TLV_MDNS_OFFLOAD_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_MDNS_OFL), + WMI_TLV_MDNS_SET_FQDN_CMDID, + WMI_TLV_MDNS_SET_RESPONSE_CMDID, + WMI_TLV_MDNS_GET_STATS_CMDID, + WMI_TLV_SAP_OFL_ENABLE_CMDID = WMI_TLV_CMD(WMI_TLV_GRP_SAP_OFL), +}; + +enum wmi_tlv_event_id { + WMI_TLV_SERVICE_READY_EVENTID = 0x1, + WMI_TLV_READY_EVENTID, + WMI_TLV_SCAN_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_SCAN), + WMI_TLV_PDEV_TPC_CONFIG_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_PDEV), + WMI_TLV_CHAN_INFO_EVENTID, + WMI_TLV_PHYERR_EVENTID, + WMI_TLV_PDEV_DUMP_EVENTID, + WMI_TLV_TX_PAUSE_EVENTID, + WMI_TLV_DFS_RADAR_EVENTID, + WMI_TLV_PDEV_L1SS_TRACK_EVENTID, + WMI_TLV_PDEV_TEMPERATURE_EVENTID, + WMI_TLV_VDEV_START_RESP_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_VDEV), + WMI_TLV_VDEV_STOPPED_EVENTID, + WMI_TLV_VDEV_INSTALL_KEY_COMPLETE_EVENTID, + WMI_TLV_VDEV_MCC_BCN_INTERVAL_CHANGE_REQ_EVENTID, + WMI_TLV_PEER_STA_KICKOUT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_PEER), + WMI_TLV_PEER_INFO_EVENTID, + WMI_TLV_PEER_TX_FAIL_CNT_THR_EVENTID, + WMI_TLV_PEER_ESTIMATED_LINKSPEED_EVENTID, + WMI_TLV_PEER_STATE_EVENTID, + WMI_TLV_MGMT_RX_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_MGMT), + WMI_TLV_HOST_SWBA_EVENTID, + WMI_TLV_TBTTOFFSET_UPDATE_EVENTID, + WMI_TLV_OFFLOAD_BCN_TX_STATUS_EVENTID, + WMI_TLV_OFFLOAD_PROB_RESP_TX_STATUS_EVENTID, + WMI_TLV_TX_DELBA_COMPLETE_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_BA_NEG), + WMI_TLV_TX_ADDBA_COMPLETE_EVENTID, + WMI_TLV_BA_RSP_SSN_EVENTID, + WMI_TLV_AGGR_STATE_TRIG_EVENTID, + WMI_TLV_ROAM_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_ROAM), + WMI_TLV_PROFILE_MATCH, + WMI_TLV_ROAM_SYNCH_EVENTID, + WMI_TLV_P2P_DISC_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_P2P), + WMI_TLV_P2P_NOA_EVENTID, + WMI_TLV_PDEV_RESUME_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_SUSPEND), + WMI_TLV_WOW_WAKEUP_HOST_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_WOW), + WMI_TLV_D0_WOW_DISABLE_ACK_EVENTID, + WMI_TLV_RTT_MEASUREMENT_REPORT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_RTT), + WMI_TLV_TSF_MEASUREMENT_REPORT_EVENTID, + WMI_TLV_RTT_ERROR_REPORT_EVENTID, + WMI_TLV_STATS_EXT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_STATS), + WMI_TLV_IFACE_LINK_STATS_EVENTID, + WMI_TLV_PEER_LINK_STATS_EVENTID, + WMI_TLV_RADIO_LINK_STATS_EVENTID, + WMI_TLV_NLO_MATCH_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_NLO_OFL), + WMI_TLV_NLO_SCAN_COMPLETE_EVENTID, + WMI_TLV_APFIND_EVENTID, + WMI_TLV_GTK_OFFLOAD_STATUS_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_GTK_OFL), + WMI_TLV_GTK_REKEY_FAIL_EVENTID, + WMI_TLV_CSA_HANDLING_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_CSA_OFL), + WMI_TLV_CHATTER_PC_QUERY_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_CHATTER), + WMI_TLV_ECHO_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_MISC), + WMI_TLV_PDEV_UTF_EVENTID, + WMI_TLV_DEBUG_MESG_EVENTID, + WMI_TLV_UPDATE_STATS_EVENTID, + WMI_TLV_DEBUG_PRINT_EVENTID, + WMI_TLV_DCS_INTERFERENCE_EVENTID, + WMI_TLV_PDEV_QVIT_EVENTID, + WMI_TLV_WLAN_PROFILE_DATA_EVENTID, + WMI_TLV_PDEV_FTM_INTG_EVENTID, + WMI_TLV_WLAN_FREQ_AVOID_EVENTID, + WMI_TLV_VDEV_GET_KEEPALIVE_EVENTID, + WMI_TLV_THERMAL_MGMT_EVENTID, + WMI_TLV_DIAG_DATA_CONTAINER_EVENTID, + WMI_TLV_HOST_AUTO_SHUTDOWN_EVENTID, + WMI_TLV_UPDATE_WHAL_MIB_STATS_EVENTID, + WMI_TLV_UPDATE_VDEV_RATE_STATS_EVENTID, + WMI_TLV_DIAG_EVENTID, + WMI_TLV_GPIO_INPUT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_GPIO), + WMI_TLV_UPLOADH_EVENTID, + WMI_TLV_CAPTUREH_EVENTID, + WMI_TLV_RFKILL_STATE_CHANGE_EVENTID, + WMI_TLV_TDLS_PEER_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_TDLS), + WMI_TLV_BATCH_SCAN_ENABLED_EVENTID = + WMI_TLV_EV(WMI_TLV_GRP_LOCATION_SCAN), + WMI_TLV_BATCH_SCAN_RESULT_EVENTID, + WMI_TLV_OEM_CAPABILITY_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_OEM), + WMI_TLV_OEM_MEASUREMENT_REPORT_EVENTID, + WMI_TLV_OEM_ERROR_REPORT_EVENTID, + WMI_TLV_NAN_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_NAN), + WMI_TLV_LPI_RESULT_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_LPI), + WMI_TLV_LPI_STATUS_EVENTID, + WMI_TLV_LPI_HANDOFF_EVENTID, + WMI_TLV_EXTSCAN_START_STOP_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_EXTSCAN), + WMI_TLV_EXTSCAN_OPERATION_EVENTID, + WMI_TLV_EXTSCAN_TABLE_USAGE_EVENTID, + WMI_TLV_EXTSCAN_CACHED_RESULTS_EVENTID, + WMI_TLV_EXTSCAN_WLAN_CHANGE_RESULTS_EVENTID, + WMI_TLV_EXTSCAN_HOTLIST_MATCH_EVENTID, + WMI_TLV_EXTSCAN_CAPABILITIES_EVENTID, + WMI_TLV_MDNS_STATS_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_MDNS_OFL), + WMI_TLV_SAP_OFL_ADD_STA_EVENTID = WMI_TLV_EV(WMI_TLV_GRP_SAP_OFL), + WMI_TLV_SAP_OFL_DEL_STA_EVENTID, +}; + +enum wmi_tlv_pdev_param { + WMI_TLV_PDEV_PARAM_TX_CHAIN_MASK = 0x1, + WMI_TLV_PDEV_PARAM_RX_CHAIN_MASK, + WMI_TLV_PDEV_PARAM_TXPOWER_LIMIT2G, + WMI_TLV_PDEV_PARAM_TXPOWER_LIMIT5G, + WMI_TLV_PDEV_PARAM_TXPOWER_SCALE, + WMI_TLV_PDEV_PARAM_BEACON_GEN_MODE, + WMI_TLV_PDEV_PARAM_BEACON_TX_MODE, + WMI_TLV_PDEV_PARAM_RESMGR_OFFCHAN_MODE, + WMI_TLV_PDEV_PARAM_PROTECTION_MODE, + WMI_TLV_PDEV_PARAM_DYNAMIC_BW, + WMI_TLV_PDEV_PARAM_NON_AGG_SW_RETRY_TH, + WMI_TLV_PDEV_PARAM_AGG_SW_RETRY_TH, + WMI_TLV_PDEV_PARAM_STA_KICKOUT_TH, + WMI_TLV_PDEV_PARAM_AC_AGGRSIZE_SCALING, + WMI_TLV_PDEV_PARAM_LTR_ENABLE, + WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_BE, + WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_BK, + WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_VI, + WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_VO, + WMI_TLV_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT, + WMI_TLV_PDEV_PARAM_LTR_SLEEP_OVERRIDE, + WMI_TLV_PDEV_PARAM_LTR_RX_OVERRIDE, + WMI_TLV_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT, + WMI_TLV_PDEV_PARAM_L1SS_ENABLE, + WMI_TLV_PDEV_PARAM_DSLEEP_ENABLE, + WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_FLUSH, + WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_WATERMARK, + WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_TMO_EN, + WMI_TLV_PDEV_PARAM_PCIELP_TXBUF_TMO_VALUE, + WMI_TLV_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD, + WMI_TLV_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD, + WMI_TLV_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD, + WMI_TLV_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, + WMI_TLV_PDEV_PARAM_PMF_QOS, + WMI_TLV_PDEV_PARAM_ARP_AC_OVERRIDE, + WMI_TLV_PDEV_PARAM_DCS, + WMI_TLV_PDEV_PARAM_ANI_ENABLE, + WMI_TLV_PDEV_PARAM_ANI_POLL_PERIOD, + WMI_TLV_PDEV_PARAM_ANI_LISTEN_PERIOD, + WMI_TLV_PDEV_PARAM_ANI_OFDM_LEVEL, + WMI_TLV_PDEV_PARAM_ANI_CCK_LEVEL, + WMI_TLV_PDEV_PARAM_DYNTXCHAIN, + WMI_TLV_PDEV_PARAM_PROXY_STA, + WMI_TLV_PDEV_PARAM_IDLE_PS_CONFIG, + WMI_TLV_PDEV_PARAM_POWER_GATING_SLEEP, + WMI_TLV_PDEV_PARAM_RFKILL_ENABLE, + WMI_TLV_PDEV_PARAM_BURST_DUR, + WMI_TLV_PDEV_PARAM_BURST_ENABLE, + WMI_TLV_PDEV_PARAM_HW_RFKILL_CONFIG, + WMI_TLV_PDEV_PARAM_LOW_POWER_RF_ENABLE, + WMI_TLV_PDEV_PARAM_L1SS_TRACK, + WMI_TLV_PDEV_PARAM_HYST_EN, + WMI_TLV_PDEV_PARAM_POWER_COLLAPSE_ENABLE, + WMI_TLV_PDEV_PARAM_LED_SYS_STATE, + WMI_TLV_PDEV_PARAM_LED_ENABLE, + WMI_TLV_PDEV_PARAM_AUDIO_OVER_WLAN_LATENCY, + WMI_TLV_PDEV_PARAM_AUDIO_OVER_WLAN_ENABLE, + WMI_TLV_PDEV_PARAM_WHAL_MIB_STATS_UPDATE_ENABLE, + WMI_TLV_PDEV_PARAM_VDEV_RATE_STATS_UPDATE_PERIOD, + WMI_TLV_PDEV_PARAM_TXPOWER_REASON_NONE, + WMI_TLV_PDEV_PARAM_TXPOWER_REASON_SAR, + WMI_TLV_PDEV_PARAM_TXPOWER_REASON_MAX, +}; + +enum wmi_tlv_vdev_param { + WMI_TLV_VDEV_PARAM_RTS_THRESHOLD = 0x1, + WMI_TLV_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + WMI_TLV_VDEV_PARAM_BEACON_INTERVAL, + WMI_TLV_VDEV_PARAM_LISTEN_INTERVAL, + WMI_TLV_VDEV_PARAM_MULTICAST_RATE, + WMI_TLV_VDEV_PARAM_MGMT_TX_RATE, + WMI_TLV_VDEV_PARAM_SLOT_TIME, + WMI_TLV_VDEV_PARAM_PREAMBLE, + WMI_TLV_VDEV_PARAM_SWBA_TIME, + WMI_TLV_VDEV_STATS_UPDATE_PERIOD, + WMI_TLV_VDEV_PWRSAVE_AGEOUT_TIME, + WMI_TLV_VDEV_HOST_SWBA_INTERVAL, + WMI_TLV_VDEV_PARAM_DTIM_PERIOD, + WMI_TLV_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT, + WMI_TLV_VDEV_PARAM_WDS, + WMI_TLV_VDEV_PARAM_ATIM_WINDOW, + WMI_TLV_VDEV_PARAM_BMISS_COUNT_MAX, + WMI_TLV_VDEV_PARAM_BMISS_FIRST_BCNT, + WMI_TLV_VDEV_PARAM_BMISS_FINAL_BCNT, + WMI_TLV_VDEV_PARAM_FEATURE_WMM, + WMI_TLV_VDEV_PARAM_CHWIDTH, + WMI_TLV_VDEV_PARAM_CHEXTOFFSET, + WMI_TLV_VDEV_PARAM_DISABLE_HTPROTECTION, + WMI_TLV_VDEV_PARAM_STA_QUICKKICKOUT, + WMI_TLV_VDEV_PARAM_MGMT_RATE, + WMI_TLV_VDEV_PARAM_PROTECTION_MODE, + WMI_TLV_VDEV_PARAM_FIXED_RATE, + WMI_TLV_VDEV_PARAM_SGI, + WMI_TLV_VDEV_PARAM_LDPC, + WMI_TLV_VDEV_PARAM_TX_STBC, + WMI_TLV_VDEV_PARAM_RX_STBC, + WMI_TLV_VDEV_PARAM_INTRA_BSS_FWD, + WMI_TLV_VDEV_PARAM_DEF_KEYID, + WMI_TLV_VDEV_PARAM_NSS, + WMI_TLV_VDEV_PARAM_BCAST_DATA_RATE, + WMI_TLV_VDEV_PARAM_MCAST_DATA_RATE, + WMI_TLV_VDEV_PARAM_MCAST_INDICATE, + WMI_TLV_VDEV_PARAM_DHCP_INDICATE, + WMI_TLV_VDEV_PARAM_UNKNOWN_DEST_INDICATE, + WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + WMI_TLV_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + WMI_TLV_VDEV_PARAM_AP_ENABLE_NAWDS, + WMI_TLV_VDEV_PARAM_ENABLE_RTSCTS, + WMI_TLV_VDEV_PARAM_TXBF, + WMI_TLV_VDEV_PARAM_PACKET_POWERSAVE, + WMI_TLV_VDEV_PARAM_DROP_UNENCRY, + WMI_TLV_VDEV_PARAM_TX_ENCAP_TYPE, + WMI_TLV_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS, + WMI_TLV_VDEV_PARAM_EARLY_RX_ADJUST_ENABLE, + WMI_TLV_VDEV_PARAM_EARLY_RX_TGT_BMISS_NUM, + WMI_TLV_VDEV_PARAM_EARLY_RX_BMISS_SAMPLE_CYCLE, + WMI_TLV_VDEV_PARAM_EARLY_RX_SLOP_STEP, + WMI_TLV_VDEV_PARAM_EARLY_RX_INIT_SLOP, + WMI_TLV_VDEV_PARAM_EARLY_RX_ADJUST_PAUSE, + WMI_TLV_VDEV_PARAM_TX_PWRLIMIT, + WMI_TLV_VDEV_PARAM_SNR_NUM_FOR_CAL, + WMI_TLV_VDEV_PARAM_ROAM_FW_OFFLOAD, + WMI_TLV_VDEV_PARAM_ENABLE_RMC, + WMI_TLV_VDEV_PARAM_IBSS_MAX_BCN_LOST_MS, + WMI_TLV_VDEV_PARAM_MAX_RATE, + WMI_TLV_VDEV_PARAM_EARLY_RX_DRIFT_SAMPLE, + WMI_TLV_VDEV_PARAM_SET_IBSS_TX_FAIL_CNT_THR, + WMI_TLV_VDEV_PARAM_EBT_RESYNC_TIMEOUT, + WMI_TLV_VDEV_PARAM_AGGR_TRIG_EVENT_ENABLE, + WMI_TLV_VDEV_PARAM_IS_IBSS_POWER_SAVE_ALLOWED, + WMI_TLV_VDEV_PARAM_IS_POWER_COLLAPSE_ALLOWED, + WMI_TLV_VDEV_PARAM_IS_AWAKE_ON_TXRX_ENABLED, + WMI_TLV_VDEV_PARAM_INACTIVITY_CNT, + WMI_TLV_VDEV_PARAM_TXSP_END_INACTIVITY_TIME_MS, + WMI_TLV_VDEV_PARAM_DTIM_POLICY, + WMI_TLV_VDEV_PARAM_IBSS_PS_WARMUP_TIME_SECS, + WMI_TLV_VDEV_PARAM_IBSS_PS_1RX_CHAIN_IN_ATIM_WINDOW_ENABLE, +}; + +enum wmi_tlv_tag { + WMI_TLV_TAG_LAST_RESERVED = 15, + + WMI_TLV_TAG_FIRST_ARRAY_ENUM, + WMI_TLV_TAG_ARRAY_UINT32 = WMI_TLV_TAG_FIRST_ARRAY_ENUM, + WMI_TLV_TAG_ARRAY_BYTE, + WMI_TLV_TAG_ARRAY_STRUCT, + WMI_TLV_TAG_ARRAY_FIXED_STRUCT, + WMI_TLV_TAG_LAST_ARRAY_ENUM = 31, + + WMI_TLV_TAG_STRUCT_SERVICE_READY_EVENT, + WMI_TLV_TAG_STRUCT_HAL_REG_CAPABILITIES, + WMI_TLV_TAG_STRUCT_WLAN_HOST_MEM_REQ, + WMI_TLV_TAG_STRUCT_READY_EVENT, + WMI_TLV_TAG_STRUCT_SCAN_EVENT, + WMI_TLV_TAG_STRUCT_PDEV_TPC_CONFIG_EVENT, + WMI_TLV_TAG_STRUCT_CHAN_INFO_EVENT, + WMI_TLV_TAG_STRUCT_COMB_PHYERR_RX_HDR, + WMI_TLV_TAG_STRUCT_VDEV_START_RESPONSE_EVENT, + WMI_TLV_TAG_STRUCT_VDEV_STOPPED_EVENT, + WMI_TLV_TAG_STRUCT_VDEV_INSTALL_KEY_COMPLETE_EVENT, + WMI_TLV_TAG_STRUCT_PEER_STA_KICKOUT_EVENT, + WMI_TLV_TAG_STRUCT_MGMT_RX_HDR, + WMI_TLV_TAG_STRUCT_TBTT_OFFSET_EVENT, + WMI_TLV_TAG_STRUCT_TX_DELBA_COMPLETE_EVENT, + WMI_TLV_TAG_STRUCT_TX_ADDBA_COMPLETE_EVENT, + WMI_TLV_TAG_STRUCT_ROAM_EVENT, + WMI_TLV_TAG_STRUCT_WOW_EVENT_INFO, + WMI_TLV_TAG_STRUCT_WOW_EVENT_INFO_SECTION_BITMAP, + WMI_TLV_TAG_STRUCT_RTT_EVENT_HEADER, + WMI_TLV_TAG_STRUCT_RTT_ERROR_REPORT_EVENT, + WMI_TLV_TAG_STRUCT_RTT_MEAS_EVENT, + WMI_TLV_TAG_STRUCT_ECHO_EVENT, + WMI_TLV_TAG_STRUCT_FTM_INTG_EVENT, + WMI_TLV_TAG_STRUCT_VDEV_GET_KEEPALIVE_EVENT, + WMI_TLV_TAG_STRUCT_GPIO_INPUT_EVENT, + WMI_TLV_TAG_STRUCT_CSA_EVENT, + WMI_TLV_TAG_STRUCT_GTK_OFFLOAD_STATUS_EVENT, + WMI_TLV_TAG_STRUCT_IGTK_INFO, + WMI_TLV_TAG_STRUCT_DCS_INTERFERENCE_EVENT, + WMI_TLV_TAG_STRUCT_ATH_DCS_CW_INT, + WMI_TLV_TAG_STRUCT_ATH_DCS_WLAN_INT_STAT, + WMI_TLV_TAG_STRUCT_WLAN_PROFILE_CTX_T, + WMI_TLV_TAG_STRUCT_WLAN_PROFILE_T, + WMI_TLV_TAG_STRUCT_PDEV_QVIT_EVENT, + WMI_TLV_TAG_STRUCT_HOST_SWBA_EVENT, + WMI_TLV_TAG_STRUCT_TIM_INFO, + WMI_TLV_TAG_STRUCT_P2P_NOA_INFO, + WMI_TLV_TAG_STRUCT_STATS_EVENT, + WMI_TLV_TAG_STRUCT_AVOID_FREQ_RANGES_EVENT, + WMI_TLV_TAG_STRUCT_AVOID_FREQ_RANGE_DESC, + WMI_TLV_TAG_STRUCT_GTK_REKEY_FAIL_EVENT, + WMI_TLV_TAG_STRUCT_INIT_CMD, + WMI_TLV_TAG_STRUCT_RESOURCE_CONFIG, + WMI_TLV_TAG_STRUCT_WLAN_HOST_MEMORY_CHUNK, + WMI_TLV_TAG_STRUCT_START_SCAN_CMD, + WMI_TLV_TAG_STRUCT_STOP_SCAN_CMD, + WMI_TLV_TAG_STRUCT_SCAN_CHAN_LIST_CMD, + WMI_TLV_TAG_STRUCT_CHANNEL, + WMI_TLV_TAG_STRUCT_PDEV_SET_REGDOMAIN_CMD, + WMI_TLV_TAG_STRUCT_PDEV_SET_PARAM_CMD, + WMI_TLV_TAG_STRUCT_PDEV_SET_WMM_PARAMS_CMD, + WMI_TLV_TAG_STRUCT_WMM_PARAMS, + WMI_TLV_TAG_STRUCT_PDEV_SET_QUIET_CMD, + WMI_TLV_TAG_STRUCT_VDEV_CREATE_CMD, + WMI_TLV_TAG_STRUCT_VDEV_DELETE_CMD, + WMI_TLV_TAG_STRUCT_VDEV_START_REQUEST_CMD, + WMI_TLV_TAG_STRUCT_P2P_NOA_DESCRIPTOR, + WMI_TLV_TAG_STRUCT_P2P_GO_SET_BEACON_IE, + WMI_TLV_TAG_STRUCT_GTK_OFFLOAD_CMD, + WMI_TLV_TAG_STRUCT_VDEV_UP_CMD, + WMI_TLV_TAG_STRUCT_VDEV_STOP_CMD, + WMI_TLV_TAG_STRUCT_VDEV_DOWN_CMD, + WMI_TLV_TAG_STRUCT_VDEV_SET_PARAM_CMD, + WMI_TLV_TAG_STRUCT_VDEV_INSTALL_KEY_CMD, + WMI_TLV_TAG_STRUCT_PEER_CREATE_CMD, + WMI_TLV_TAG_STRUCT_PEER_DELETE_CMD, + WMI_TLV_TAG_STRUCT_PEER_FLUSH_TIDS_CMD, + WMI_TLV_TAG_STRUCT_PEER_SET_PARAM_CMD, + WMI_TLV_TAG_STRUCT_PEER_ASSOC_COMPLETE_CMD, + WMI_TLV_TAG_STRUCT_VHT_RATE_SET, + WMI_TLV_TAG_STRUCT_BCN_TMPL_CMD, + WMI_TLV_TAG_STRUCT_PRB_TMPL_CMD, + WMI_TLV_TAG_STRUCT_BCN_PRB_INFO, + WMI_TLV_TAG_STRUCT_PEER_TID_ADDBA_CMD, + WMI_TLV_TAG_STRUCT_PEER_TID_DELBA_CMD, + WMI_TLV_TAG_STRUCT_STA_POWERSAVE_MODE_CMD, + WMI_TLV_TAG_STRUCT_STA_POWERSAVE_PARAM_CMD, + WMI_TLV_TAG_STRUCT_STA_DTIM_PS_METHOD_CMD, + WMI_TLV_TAG_STRUCT_ROAM_SCAN_MODE, + WMI_TLV_TAG_STRUCT_ROAM_SCAN_RSSI_THRESHOLD, + WMI_TLV_TAG_STRUCT_ROAM_SCAN_PERIOD, + WMI_TLV_TAG_STRUCT_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + WMI_TLV_TAG_STRUCT_PDEV_SUSPEND_CMD, + WMI_TLV_TAG_STRUCT_PDEV_RESUME_CMD, + WMI_TLV_TAG_STRUCT_ADD_BCN_FILTER_CMD, + WMI_TLV_TAG_STRUCT_RMV_BCN_FILTER_CMD, + WMI_TLV_TAG_STRUCT_WOW_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_WOW_HOSTWAKEUP_FROM_SLEEP_CMD, + WMI_TLV_TAG_STRUCT_STA_UAPSD_AUTO_TRIG_CMD, + WMI_TLV_TAG_STRUCT_STA_UAPSD_AUTO_TRIG_PARAM, + WMI_TLV_TAG_STRUCT_SET_ARP_NS_OFFLOAD_CMD, + WMI_TLV_TAG_STRUCT_ARP_OFFLOAD_TUPLE, + WMI_TLV_TAG_STRUCT_NS_OFFLOAD_TUPLE, + WMI_TLV_TAG_STRUCT_FTM_INTG_CMD, + WMI_TLV_TAG_STRUCT_STA_KEEPALIVE_CMD, + WMI_TLV_TAG_STRUCT_STA_KEEPALVE_ARP_RESPONSE, + WMI_TLV_TAG_STRUCT_P2P_SET_VENDOR_IE_DATA_CMD, + WMI_TLV_TAG_STRUCT_AP_PS_PEER_CMD, + WMI_TLV_TAG_STRUCT_PEER_RATE_RETRY_SCHED_CMD, + WMI_TLV_TAG_STRUCT_WLAN_PROFILE_TRIGGER_CMD, + WMI_TLV_TAG_STRUCT_WLAN_PROFILE_SET_HIST_INTVL_CMD, + WMI_TLV_TAG_STRUCT_WLAN_PROFILE_GET_PROF_DATA_CMD, + WMI_TLV_TAG_STRUCT_WLAN_PROFILE_ENABLE_PROFILE_ID_CMD, + WMI_TLV_TAG_STRUCT_WOW_DEL_PATTERN_CMD, + WMI_TLV_TAG_STRUCT_WOW_ADD_DEL_EVT_CMD, + WMI_TLV_TAG_STRUCT_RTT_MEASREQ_HEAD, + WMI_TLV_TAG_STRUCT_RTT_MEASREQ_BODY, + WMI_TLV_TAG_STRUCT_RTT_TSF_CMD, + WMI_TLV_TAG_STRUCT_VDEV_SPECTRAL_CONFIGURE_CMD, + WMI_TLV_TAG_STRUCT_VDEV_SPECTRAL_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_REQUEST_STATS_CMD, + WMI_TLV_TAG_STRUCT_NLO_CONFIG_CMD, + WMI_TLV_TAG_STRUCT_NLO_CONFIGURED_PARAMETERS, + WMI_TLV_TAG_STRUCT_CSA_OFFLOAD_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_CSA_OFFLOAD_CHANSWITCH_CMD, + WMI_TLV_TAG_STRUCT_CHATTER_SET_MODE_CMD, + WMI_TLV_TAG_STRUCT_ECHO_CMD, + WMI_TLV_TAG_STRUCT_VDEV_SET_KEEPALIVE_CMD, + WMI_TLV_TAG_STRUCT_VDEV_GET_KEEPALIVE_CMD, + WMI_TLV_TAG_STRUCT_FORCE_FW_HANG_CMD, + WMI_TLV_TAG_STRUCT_GPIO_CONFIG_CMD, + WMI_TLV_TAG_STRUCT_GPIO_OUTPUT_CMD, + WMI_TLV_TAG_STRUCT_PEER_ADD_WDS_ENTRY_CMD, + WMI_TLV_TAG_STRUCT_PEER_REMOVE_WDS_ENTRY_CMD, + WMI_TLV_TAG_STRUCT_BCN_TX_HDR, + WMI_TLV_TAG_STRUCT_BCN_SEND_FROM_HOST_CMD, + WMI_TLV_TAG_STRUCT_MGMT_TX_HDR, + WMI_TLV_TAG_STRUCT_ADDBA_CLEAR_RESP_CMD, + WMI_TLV_TAG_STRUCT_ADDBA_SEND_CMD, + WMI_TLV_TAG_STRUCT_DELBA_SEND_CMD, + WMI_TLV_TAG_STRUCT_ADDBA_SETRESPONSE_CMD, + WMI_TLV_TAG_STRUCT_SEND_SINGLEAMSDU_CMD, + WMI_TLV_TAG_STRUCT_PDEV_PKTLOG_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_PDEV_PKTLOG_DISABLE_CMD, + WMI_TLV_TAG_STRUCT_PDEV_SET_HT_IE_CMD, + WMI_TLV_TAG_STRUCT_PDEV_SET_VHT_IE_CMD, + WMI_TLV_TAG_STRUCT_PDEV_SET_DSCP_TID_MAP_CMD, + WMI_TLV_TAG_STRUCT_PDEV_GREEN_AP_PS_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_PDEV_GET_TPC_CONFIG_CMD, + WMI_TLV_TAG_STRUCT_PDEV_SET_BASE_MACADDR_CMD, + WMI_TLV_TAG_STRUCT_PEER_MCAST_GROUP_CMD, + WMI_TLV_TAG_STRUCT_ROAM_AP_PROFILE, + WMI_TLV_TAG_STRUCT_AP_PROFILE, + WMI_TLV_TAG_STRUCT_SCAN_SCH_PRIORITY_TABLE_CMD, + WMI_TLV_TAG_STRUCT_PDEV_DFS_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_PDEV_DFS_DISABLE_CMD, + WMI_TLV_TAG_STRUCT_WOW_ADD_PATTERN_CMD, + WMI_TLV_TAG_STRUCT_WOW_BITMAP_PATTERN_T, + WMI_TLV_TAG_STRUCT_WOW_IPV4_SYNC_PATTERN_T, + WMI_TLV_TAG_STRUCT_WOW_IPV6_SYNC_PATTERN_T, + WMI_TLV_TAG_STRUCT_WOW_MAGIC_PATTERN_CMD, + WMI_TLV_TAG_STRUCT_SCAN_UPDATE_REQUEST_CMD, + WMI_TLV_TAG_STRUCT_CHATTER_PKT_COALESCING_FILTER, + WMI_TLV_TAG_STRUCT_CHATTER_COALESCING_ADD_FILTER_CMD, + WMI_TLV_TAG_STRUCT_CHATTER_COALESCING_DELETE_FILTER_CMD, + WMI_TLV_TAG_STRUCT_CHATTER_COALESCING_QUERY_CMD, + WMI_TLV_TAG_STRUCT_TXBF_CMD, + WMI_TLV_TAG_STRUCT_DEBUG_LOG_CONFIG_CMD, + WMI_TLV_TAG_STRUCT_NLO_EVENT, + WMI_TLV_TAG_STRUCT_CHATTER_QUERY_REPLY_EVENT, + WMI_TLV_TAG_STRUCT_UPLOAD_H_HDR, + WMI_TLV_TAG_STRUCT_CAPTURE_H_EVENT_HDR, + WMI_TLV_TAG_STRUCT_VDEV_WNM_SLEEPMODE_CMD, + WMI_TLV_TAG_STRUCT_VDEV_IPSEC_NATKEEPALIVE_FILTER_CMD, + WMI_TLV_TAG_STRUCT_VDEV_WMM_ADDTS_CMD, + WMI_TLV_TAG_STRUCT_VDEV_WMM_DELTS_CMD, + WMI_TLV_TAG_STRUCT_VDEV_SET_WMM_PARAMS_CMD, + WMI_TLV_TAG_STRUCT_TDLS_SET_STATE_CMD, + WMI_TLV_TAG_STRUCT_TDLS_PEER_UPDATE_CMD, + WMI_TLV_TAG_STRUCT_TDLS_PEER_EVENT, + WMI_TLV_TAG_STRUCT_TDLS_PEER_CAPABILITIES, + WMI_TLV_TAG_STRUCT_VDEV_MCC_SET_TBTT_MODE_CMD, + WMI_TLV_TAG_STRUCT_ROAM_CHAN_LIST, + WMI_TLV_TAG_STRUCT_VDEV_MCC_BCN_INTVL_CHANGE_EVENT, + WMI_TLV_TAG_STRUCT_RESMGR_ADAPTIVE_OCS_CMD, + WMI_TLV_TAG_STRUCT_RESMGR_SET_CHAN_TIME_QUOTA_CMD, + WMI_TLV_TAG_STRUCT_RESMGR_SET_CHAN_LATENCY_CMD, + WMI_TLV_TAG_STRUCT_BA_REQ_SSN_CMD, + WMI_TLV_TAG_STRUCT_BA_RSP_SSN_EVENT, + WMI_TLV_TAG_STRUCT_STA_SMPS_FORCE_MODE_CMD, + WMI_TLV_TAG_STRUCT_SET_MCASTBCAST_FILTER_CMD, + WMI_TLV_TAG_STRUCT_P2P_SET_OPPPS_CMD, + WMI_TLV_TAG_STRUCT_P2P_SET_NOA_CMD, + WMI_TLV_TAG_STRUCT_BA_REQ_SSN_CMD_SUB_STRUCT_PARAM, + WMI_TLV_TAG_STRUCT_BA_REQ_SSN_EVENT_SUB_STRUCT_PARAM, + WMI_TLV_TAG_STRUCT_STA_SMPS_PARAM_CMD, + WMI_TLV_TAG_STRUCT_VDEV_SET_GTX_PARAMS_CMD, + WMI_TLV_TAG_STRUCT_MCC_SCHED_TRAFFIC_STATS_CMD, + WMI_TLV_TAG_STRUCT_MCC_SCHED_STA_TRAFFIC_STATS, + WMI_TLV_TAG_STRUCT_OFFLOAD_BCN_TX_STATUS_EVENT, + WMI_TLV_TAG_STRUCT_P2P_NOA_EVENT, + WMI_TLV_TAG_STRUCT_HB_SET_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_HB_SET_TCP_PARAMS_CMD, + WMI_TLV_TAG_STRUCT_HB_SET_TCP_PKT_FILTER_CMD, + WMI_TLV_TAG_STRUCT_HB_SET_UDP_PARAMS_CMD, + WMI_TLV_TAG_STRUCT_HB_SET_UDP_PKT_FILTER_CMD, + WMI_TLV_TAG_STRUCT_HB_IND_EVENT, + WMI_TLV_TAG_STRUCT_TX_PAUSE_EVENT, + WMI_TLV_TAG_STRUCT_RFKILL_EVENT, + WMI_TLV_TAG_STRUCT_DFS_RADAR_EVENT, + WMI_TLV_TAG_STRUCT_DFS_PHYERR_FILTER_ENA_CMD, + WMI_TLV_TAG_STRUCT_DFS_PHYERR_FILTER_DIS_CMD, + WMI_TLV_TAG_STRUCT_BATCH_SCAN_RESULT_SCAN_LIST, + WMI_TLV_TAG_STRUCT_BATCH_SCAN_RESULT_NETWORK_INFO, + WMI_TLV_TAG_STRUCT_BATCH_SCAN_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_BATCH_SCAN_DISABLE_CMD, + WMI_TLV_TAG_STRUCT_BATCH_SCAN_TRIGGER_RESULT_CMD, + WMI_TLV_TAG_STRUCT_BATCH_SCAN_ENABLED_EVENT, + WMI_TLV_TAG_STRUCT_BATCH_SCAN_RESULT_EVENT, + WMI_TLV_TAG_STRUCT_VDEV_PLMREQ_START_CMD, + WMI_TLV_TAG_STRUCT_VDEV_PLMREQ_STOP_CMD, + WMI_TLV_TAG_STRUCT_THERMAL_MGMT_CMD, + WMI_TLV_TAG_STRUCT_THERMAL_MGMT_EVENT, + WMI_TLV_TAG_STRUCT_PEER_INFO_REQ_CMD, + WMI_TLV_TAG_STRUCT_PEER_INFO_EVENT, + WMI_TLV_TAG_STRUCT_PEER_INFO, + WMI_TLV_TAG_STRUCT_PEER_TX_FAIL_CNT_THR_EVENT, + WMI_TLV_TAG_STRUCT_RMC_SET_MODE_CMD, + WMI_TLV_TAG_STRUCT_RMC_SET_ACTION_PERIOD_CMD, + WMI_TLV_TAG_STRUCT_RMC_CONFIG_CMD, + WMI_TLV_TAG_STRUCT_MHF_OFFLOAD_SET_MODE_CMD, + WMI_TLV_TAG_STRUCT_MHF_OFFLOAD_PLUMB_ROUTING_TABLE_CMD, + WMI_TLV_TAG_STRUCT_ADD_PROACTIVE_ARP_RSP_PATTERN_CMD, + WMI_TLV_TAG_STRUCT_DEL_PROACTIVE_ARP_RSP_PATTERN_CMD, + WMI_TLV_TAG_STRUCT_NAN_CMD_PARAM, + WMI_TLV_TAG_STRUCT_NAN_EVENT_HDR, + WMI_TLV_TAG_STRUCT_PDEV_L1SS_TRACK_EVENT, + WMI_TLV_TAG_STRUCT_DIAG_DATA_CONTAINER_EVENT, + WMI_TLV_TAG_STRUCT_MODEM_POWER_STATE_CMD_PARAM, + WMI_TLV_TAG_STRUCT_PEER_GET_ESTIMATED_LINKSPEED_CMD, + WMI_TLV_TAG_STRUCT_PEER_ESTIMATED_LINKSPEED_EVENT, + WMI_TLV_TAG_STRUCT_AGGR_STATE_TRIG_EVENT, + WMI_TLV_TAG_STRUCT_MHF_OFFLOAD_ROUTING_TABLE_ENTRY, + WMI_TLV_TAG_STRUCT_ROAM_SCAN_CMD, + WMI_TLV_TAG_STRUCT_REQ_STATS_EXT_CMD, + WMI_TLV_TAG_STRUCT_STATS_EXT_EVENT, + WMI_TLV_TAG_STRUCT_OBSS_SCAN_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_OBSS_SCAN_DISABLE_CMD, + WMI_TLV_TAG_STRUCT_OFFLOAD_PRB_RSP_TX_STATUS_EVENT, + WMI_TLV_TAG_STRUCT_PDEV_SET_LED_CONFIG_CMD, + WMI_TLV_TAG_STRUCT_HOST_AUTO_SHUTDOWN_CFG_CMD, + WMI_TLV_TAG_STRUCT_HOST_AUTO_SHUTDOWN_EVENT, + WMI_TLV_TAG_STRUCT_UPDATE_WHAL_MIB_STATS_EVENT, + WMI_TLV_TAG_STRUCT_CHAN_AVOID_UPDATE_CMD_PARAM, + WMI_TLV_TAG_STRUCT_WOW_ACER_IOAC_PKT_PATTERN_T, + WMI_TLV_TAG_STRUCT_WOW_ACER_IOAC_TMR_PATTERN_T, + WMI_TLV_TAG_STRUCT_WOW_IOAC_ADD_KEEPALIVE_CMD, + WMI_TLV_TAG_STRUCT_WOW_IOAC_DEL_KEEPALIVE_CMD, + WMI_TLV_TAG_STRUCT_WOW_IOAC_KEEPALIVE_T, + WMI_TLV_TAG_STRUCT_WOW_ACER_IOAC_ADD_PATTERN_CMD, + WMI_TLV_TAG_STRUCT_WOW_ACER_IOAC_DEL_PATTERN_CMD, + WMI_TLV_TAG_STRUCT_START_LINK_STATS_CMD, + WMI_TLV_TAG_STRUCT_CLEAR_LINK_STATS_CMD, + WMI_TLV_TAG_STRUCT_REQUEST_LINK_STATS_CMD, + WMI_TLV_TAG_STRUCT_IFACE_LINK_STATS_EVENT, + WMI_TLV_TAG_STRUCT_RADIO_LINK_STATS_EVENT, + WMI_TLV_TAG_STRUCT_PEER_STATS_EVENT, + WMI_TLV_TAG_STRUCT_CHANNEL_STATS, + WMI_TLV_TAG_STRUCT_RADIO_LINK_STATS, + WMI_TLV_TAG_STRUCT_RATE_STATS, + WMI_TLV_TAG_STRUCT_PEER_LINK_STATS, + WMI_TLV_TAG_STRUCT_WMM_AC_STATS, + WMI_TLV_TAG_STRUCT_IFACE_LINK_STATS, + WMI_TLV_TAG_STRUCT_LPI_MGMT_SNOOPING_CONFIG_CMD, + WMI_TLV_TAG_STRUCT_LPI_START_SCAN_CMD, + WMI_TLV_TAG_STRUCT_LPI_STOP_SCAN_CMD, + WMI_TLV_TAG_STRUCT_LPI_RESULT_EVENT, + WMI_TLV_TAG_STRUCT_PEER_STATE_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_BUCKET_CMD, + WMI_TLV_TAG_STRUCT_EXTSCAN_BUCKET_CHANNEL_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_START_CMD, + WMI_TLV_TAG_STRUCT_EXTSCAN_STOP_CMD, + WMI_TLV_TAG_STRUCT_EXTSCAN_CONFIGURE_WLAN_CHANGE_MONITOR_CMD, + WMI_TLV_TAG_STRUCT_EXTSCAN_WLAN_CHANGE_BSSID_PARAM_CMD, + WMI_TLV_TAG_STRUCT_EXTSCAN_CONFIGURE_HOTLIST_MONITOR_CMD, + WMI_TLV_TAG_STRUCT_EXTSCAN_GET_CACHED_RESULTS_CMD, + WMI_TLV_TAG_STRUCT_EXTSCAN_GET_WLAN_CHANGE_RESULTS_CMD, + WMI_TLV_TAG_STRUCT_EXTSCAN_SET_CAPABILITIES_CMD, + WMI_TLV_TAG_STRUCT_EXTSCAN_GET_CAPABILITIES_CMD, + WMI_TLV_TAG_STRUCT_EXTSCAN_OPERATION_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_START_STOP_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_TABLE_USAGE_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_WLAN_DESCRIPTOR_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_RSSI_INFO_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_CACHED_RESULTS_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_WLAN_CHANGE_RESULTS_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_WLAN_CHANGE_RESULT_BSSID_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_HOTLIST_MATCH_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_CAPABILITIES_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_CACHE_CAPABILITIES_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_WLAN_CHANGE_MONITOR_CAPABILITIES_EVENT, + WMI_TLV_TAG_STRUCT_EXTSCAN_HOTLIST_MONITOR_CAPABILITIES_EVENT, + WMI_TLV_TAG_STRUCT_D0_WOW_ENABLE_DISABLE_CMD, + WMI_TLV_TAG_STRUCT_D0_WOW_DISABLE_ACK_EVENT, + WMI_TLV_TAG_STRUCT_UNIT_TEST_CMD, + WMI_TLV_TAG_STRUCT_ROAM_OFFLOAD_TLV_PARAM, + WMI_TLV_TAG_STRUCT_ROAM_11I_OFFLOAD_TLV_PARAM, + WMI_TLV_TAG_STRUCT_ROAM_11R_OFFLOAD_TLV_PARAM, + WMI_TLV_TAG_STRUCT_ROAM_ESE_OFFLOAD_TLV_PARAM, + WMI_TLV_TAG_STRUCT_ROAM_SYNCH_EVENT, + WMI_TLV_TAG_STRUCT_ROAM_SYNCH_COMPLETE, + WMI_TLV_TAG_STRUCT_EXTWOW_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_EXTWOW_SET_APP_TYPE1_PARAMS_CMD, + WMI_TLV_TAG_STRUCT_EXTWOW_SET_APP_TYPE2_PARAMS_CMD, + WMI_TLV_TAG_STRUCT_LPI_STATUS_EVENT, + WMI_TLV_TAG_STRUCT_LPI_HANDOFF_EVENT, + WMI_TLV_TAG_STRUCT_VDEV_RATE_STATS_EVENT, + WMI_TLV_TAG_STRUCT_VDEV_RATE_HT_INFO, + WMI_TLV_TAG_STRUCT_RIC_REQUEST, + WMI_TLV_TAG_STRUCT_PDEV_GET_TEMPERATURE_CMD, + WMI_TLV_TAG_STRUCT_PDEV_TEMPERATURE_EVENT, + WMI_TLV_TAG_STRUCT_SET_DHCP_SERVER_OFFLOAD_CMD, + WMI_TLV_TAG_STRUCT_TPC_CHAINMASK_CONFIG_CMD, + WMI_TLV_TAG_STRUCT_RIC_TSPEC, + WMI_TLV_TAG_STRUCT_TPC_CHAINMASK_CONFIG, + WMI_TLV_TAG_STRUCT_IPA_OFFLOAD_CMD, + WMI_TLV_TAG_STRUCT_SCAN_PROB_REQ_OUI_CMD, + WMI_TLV_TAG_STRUCT_KEY_MATERIAL, + WMI_TLV_TAG_STRUCT_TDLS_SET_OFFCHAN_MODE_CMD, + WMI_TLV_TAG_STRUCT_SET_LED_FLASHING_CMD, + WMI_TLV_TAG_STRUCT_MDNS_OFFLOAD_CMD, + WMI_TLV_TAG_STRUCT_MDNS_SET_FQDN_CMD, + WMI_TLV_TAG_STRUCT_MDNS_SET_RESP_CMD, + WMI_TLV_TAG_STRUCT_MDNS_GET_STATS_CMD, + WMI_TLV_TAG_STRUCT_MDNS_STATS_EVENT, + WMI_TLV_TAG_STRUCT_ROAM_INVOKE_CMD, + WMI_TLV_TAG_STRUCT_PDEV_RESUME_EVENT, + WMI_TLV_TAG_STRUCT_PDEV_SET_ANTENNA_DIVERSITY_CMD, + WMI_TLV_TAG_STRUCT_SAP_OFL_ENABLE_CMD, + WMI_TLV_TAG_STRUCT_SAP_OFL_ADD_STA_EVENT, + WMI_TLV_TAG_STRUCT_SAP_OFL_DEL_STA_EVENT, + WMI_TLV_TAG_STRUCT_APFIND_CMD_PARAM, + WMI_TLV_TAG_STRUCT_APFIND_EVENT_HDR, + + WMI_TLV_TAG_MAX +}; + +enum wmi_tlv_service { + WMI_TLV_SERVICE_BEACON_OFFLOAD = 0, + WMI_TLV_SERVICE_SCAN_OFFLOAD, + WMI_TLV_SERVICE_ROAM_SCAN_OFFLOAD, + WMI_TLV_SERVICE_BCN_MISS_OFFLOAD, + WMI_TLV_SERVICE_STA_PWRSAVE, + WMI_TLV_SERVICE_STA_ADVANCED_PWRSAVE, + WMI_TLV_SERVICE_AP_UAPSD, + WMI_TLV_SERVICE_AP_DFS, + WMI_TLV_SERVICE_11AC, + WMI_TLV_SERVICE_BLOCKACK, + WMI_TLV_SERVICE_PHYERR, + WMI_TLV_SERVICE_BCN_FILTER, + WMI_TLV_SERVICE_RTT, + WMI_TLV_SERVICE_WOW, + WMI_TLV_SERVICE_RATECTRL_CACHE, + WMI_TLV_SERVICE_IRAM_TIDS, + WMI_TLV_SERVICE_ARPNS_OFFLOAD, + WMI_TLV_SERVICE_NLO, + WMI_TLV_SERVICE_GTK_OFFLOAD, + WMI_TLV_SERVICE_SCAN_SCH, + WMI_TLV_SERVICE_CSA_OFFLOAD, + WMI_TLV_SERVICE_CHATTER, + WMI_TLV_SERVICE_COEX_FREQAVOID, + WMI_TLV_SERVICE_PACKET_POWER_SAVE, + WMI_TLV_SERVICE_FORCE_FW_HANG, + WMI_TLV_SERVICE_GPIO, + WMI_TLV_SERVICE_STA_DTIM_PS_MODULATED_DTIM, + WMI_TLV_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, + WMI_TLV_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, + WMI_TLV_SERVICE_STA_KEEP_ALIVE, + WMI_TLV_SERVICE_TX_ENCAP, + WMI_TLV_SERVICE_AP_PS_DETECT_OUT_OF_SYNC, + WMI_TLV_SERVICE_EARLY_RX, + WMI_TLV_SERVICE_STA_SMPS, + WMI_TLV_SERVICE_FWTEST, + WMI_TLV_SERVICE_STA_WMMAC, + WMI_TLV_SERVICE_TDLS, + WMI_TLV_SERVICE_BURST, + WMI_TLV_SERVICE_MCC_BCN_INTERVAL_CHANGE, + WMI_TLV_SERVICE_ADAPTIVE_OCS, + WMI_TLV_SERVICE_BA_SSN_SUPPORT, + WMI_TLV_SERVICE_FILTER_IPSEC_NATKEEPALIVE, + WMI_TLV_SERVICE_WLAN_HB, + WMI_TLV_SERVICE_LTE_ANT_SHARE_SUPPORT, + WMI_TLV_SERVICE_BATCH_SCAN, + WMI_TLV_SERVICE_QPOWER, + WMI_TLV_SERVICE_PLMREQ, + WMI_TLV_SERVICE_THERMAL_MGMT, + WMI_TLV_SERVICE_RMC, + WMI_TLV_SERVICE_MHF_OFFLOAD, + WMI_TLV_SERVICE_COEX_SAR, + WMI_TLV_SERVICE_BCN_TXRATE_OVERRIDE, + WMI_TLV_SERVICE_NAN, + WMI_TLV_SERVICE_L1SS_STAT, + WMI_TLV_SERVICE_ESTIMATE_LINKSPEED, + WMI_TLV_SERVICE_OBSS_SCAN, + WMI_TLV_SERVICE_TDLS_OFFCHAN, + WMI_TLV_SERVICE_TDLS_UAPSD_BUFFER_STA, + WMI_TLV_SERVICE_TDLS_UAPSD_SLEEP_STA, + WMI_TLV_SERVICE_IBSS_PWRSAVE, + WMI_TLV_SERVICE_LPASS, + WMI_TLV_SERVICE_EXTSCAN, + WMI_TLV_SERVICE_D0WOW, + WMI_TLV_SERVICE_HSOFFLOAD, + WMI_TLV_SERVICE_ROAM_HO_OFFLOAD, + WMI_TLV_SERVICE_RX_FULL_REORDER, + WMI_TLV_SERVICE_DHCP_OFFLOAD, + WMI_TLV_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT, + WMI_TLV_SERVICE_MDNS_OFFLOAD, + WMI_TLV_SERVICE_SAP_AUTH_OFFLOAD, +}; + +#define WMI_SERVICE_IS_ENABLED(wmi_svc_bmap, svc_id, len) \ + ((svc_id) < (len) && \ + __le32_to_cpu((wmi_svc_bmap)[(svc_id)/(sizeof(u32))]) & \ + BIT((svc_id)%(sizeof(u32)))) + +#define SVCMAP(x, y, len) \ + do { \ + if (WMI_SERVICE_IS_ENABLED((in), (x), (len))) \ + __set_bit(y, out); \ + } while (0) + +static inline void +wmi_tlv_svc_map(const __le32 *in, unsigned long *out, size_t len) +{ + SVCMAP(WMI_TLV_SERVICE_BEACON_OFFLOAD, + WMI_SERVICE_BEACON_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_SCAN_OFFLOAD, + WMI_SERVICE_SCAN_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_ROAM_SCAN_OFFLOAD, + WMI_SERVICE_ROAM_SCAN_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_BCN_MISS_OFFLOAD, + WMI_SERVICE_BCN_MISS_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_STA_PWRSAVE, + WMI_SERVICE_STA_PWRSAVE, len); + SVCMAP(WMI_TLV_SERVICE_STA_ADVANCED_PWRSAVE, + WMI_SERVICE_STA_ADVANCED_PWRSAVE, len); + SVCMAP(WMI_TLV_SERVICE_AP_UAPSD, + WMI_SERVICE_AP_UAPSD, len); + SVCMAP(WMI_TLV_SERVICE_AP_DFS, + WMI_SERVICE_AP_DFS, len); + SVCMAP(WMI_TLV_SERVICE_11AC, + WMI_SERVICE_11AC, len); + SVCMAP(WMI_TLV_SERVICE_BLOCKACK, + WMI_SERVICE_BLOCKACK, len); + SVCMAP(WMI_TLV_SERVICE_PHYERR, + WMI_SERVICE_PHYERR, len); + SVCMAP(WMI_TLV_SERVICE_BCN_FILTER, + WMI_SERVICE_BCN_FILTER, len); + SVCMAP(WMI_TLV_SERVICE_RTT, + WMI_SERVICE_RTT, len); + SVCMAP(WMI_TLV_SERVICE_WOW, + WMI_SERVICE_WOW, len); + SVCMAP(WMI_TLV_SERVICE_RATECTRL_CACHE, + WMI_SERVICE_RATECTRL_CACHE, len); + SVCMAP(WMI_TLV_SERVICE_IRAM_TIDS, + WMI_SERVICE_IRAM_TIDS, len); + SVCMAP(WMI_TLV_SERVICE_ARPNS_OFFLOAD, + WMI_SERVICE_ARPNS_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_NLO, + WMI_SERVICE_NLO, len); + SVCMAP(WMI_TLV_SERVICE_GTK_OFFLOAD, + WMI_SERVICE_GTK_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_SCAN_SCH, + WMI_SERVICE_SCAN_SCH, len); + SVCMAP(WMI_TLV_SERVICE_CSA_OFFLOAD, + WMI_SERVICE_CSA_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_CHATTER, + WMI_SERVICE_CHATTER, len); + SVCMAP(WMI_TLV_SERVICE_COEX_FREQAVOID, + WMI_SERVICE_COEX_FREQAVOID, len); + SVCMAP(WMI_TLV_SERVICE_PACKET_POWER_SAVE, + WMI_SERVICE_PACKET_POWER_SAVE, len); + SVCMAP(WMI_TLV_SERVICE_FORCE_FW_HANG, + WMI_SERVICE_FORCE_FW_HANG, len); + SVCMAP(WMI_TLV_SERVICE_GPIO, + WMI_SERVICE_GPIO, len); + SVCMAP(WMI_TLV_SERVICE_STA_DTIM_PS_MODULATED_DTIM, + WMI_SERVICE_STA_DTIM_PS_MODULATED_DTIM, len); + SVCMAP(WMI_TLV_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, + WMI_SERVICE_STA_UAPSD_BASIC_AUTO_TRIG, len); + SVCMAP(WMI_TLV_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, + WMI_SERVICE_STA_UAPSD_VAR_AUTO_TRIG, len); + SVCMAP(WMI_TLV_SERVICE_STA_KEEP_ALIVE, + WMI_SERVICE_STA_KEEP_ALIVE, len); + SVCMAP(WMI_TLV_SERVICE_TX_ENCAP, + WMI_SERVICE_TX_ENCAP, len); + SVCMAP(WMI_TLV_SERVICE_AP_PS_DETECT_OUT_OF_SYNC, + WMI_SERVICE_AP_PS_DETECT_OUT_OF_SYNC, len); + SVCMAP(WMI_TLV_SERVICE_EARLY_RX, + WMI_SERVICE_EARLY_RX, len); + SVCMAP(WMI_TLV_SERVICE_STA_SMPS, + WMI_SERVICE_STA_SMPS, len); + SVCMAP(WMI_TLV_SERVICE_FWTEST, + WMI_SERVICE_FWTEST, len); + SVCMAP(WMI_TLV_SERVICE_STA_WMMAC, + WMI_SERVICE_STA_WMMAC, len); + SVCMAP(WMI_TLV_SERVICE_TDLS, + WMI_SERVICE_TDLS, len); + SVCMAP(WMI_TLV_SERVICE_BURST, + WMI_SERVICE_BURST, len); + SVCMAP(WMI_TLV_SERVICE_MCC_BCN_INTERVAL_CHANGE, + WMI_SERVICE_MCC_BCN_INTERVAL_CHANGE, len); + SVCMAP(WMI_TLV_SERVICE_ADAPTIVE_OCS, + WMI_SERVICE_ADAPTIVE_OCS, len); + SVCMAP(WMI_TLV_SERVICE_BA_SSN_SUPPORT, + WMI_SERVICE_BA_SSN_SUPPORT, len); + SVCMAP(WMI_TLV_SERVICE_FILTER_IPSEC_NATKEEPALIVE, + WMI_SERVICE_FILTER_IPSEC_NATKEEPALIVE, len); + SVCMAP(WMI_TLV_SERVICE_WLAN_HB, + WMI_SERVICE_WLAN_HB, len); + SVCMAP(WMI_TLV_SERVICE_LTE_ANT_SHARE_SUPPORT, + WMI_SERVICE_LTE_ANT_SHARE_SUPPORT, len); + SVCMAP(WMI_TLV_SERVICE_BATCH_SCAN, + WMI_SERVICE_BATCH_SCAN, len); + SVCMAP(WMI_TLV_SERVICE_QPOWER, + WMI_SERVICE_QPOWER, len); + SVCMAP(WMI_TLV_SERVICE_PLMREQ, + WMI_SERVICE_PLMREQ, len); + SVCMAP(WMI_TLV_SERVICE_THERMAL_MGMT, + WMI_SERVICE_THERMAL_MGMT, len); + SVCMAP(WMI_TLV_SERVICE_RMC, + WMI_SERVICE_RMC, len); + SVCMAP(WMI_TLV_SERVICE_MHF_OFFLOAD, + WMI_SERVICE_MHF_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_COEX_SAR, + WMI_SERVICE_COEX_SAR, len); + SVCMAP(WMI_TLV_SERVICE_BCN_TXRATE_OVERRIDE, + WMI_SERVICE_BCN_TXRATE_OVERRIDE, len); + SVCMAP(WMI_TLV_SERVICE_NAN, + WMI_SERVICE_NAN, len); + SVCMAP(WMI_TLV_SERVICE_L1SS_STAT, + WMI_SERVICE_L1SS_STAT, len); + SVCMAP(WMI_TLV_SERVICE_ESTIMATE_LINKSPEED, + WMI_SERVICE_ESTIMATE_LINKSPEED, len); + SVCMAP(WMI_TLV_SERVICE_OBSS_SCAN, + WMI_SERVICE_OBSS_SCAN, len); + SVCMAP(WMI_TLV_SERVICE_TDLS_OFFCHAN, + WMI_SERVICE_TDLS_OFFCHAN, len); + SVCMAP(WMI_TLV_SERVICE_TDLS_UAPSD_BUFFER_STA, + WMI_SERVICE_TDLS_UAPSD_BUFFER_STA, len); + SVCMAP(WMI_TLV_SERVICE_TDLS_UAPSD_SLEEP_STA, + WMI_SERVICE_TDLS_UAPSD_SLEEP_STA, len); + SVCMAP(WMI_TLV_SERVICE_IBSS_PWRSAVE, + WMI_SERVICE_IBSS_PWRSAVE, len); + SVCMAP(WMI_TLV_SERVICE_LPASS, + WMI_SERVICE_LPASS, len); + SVCMAP(WMI_TLV_SERVICE_EXTSCAN, + WMI_SERVICE_EXTSCAN, len); + SVCMAP(WMI_TLV_SERVICE_D0WOW, + WMI_SERVICE_D0WOW, len); + SVCMAP(WMI_TLV_SERVICE_HSOFFLOAD, + WMI_SERVICE_HSOFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_ROAM_HO_OFFLOAD, + WMI_SERVICE_ROAM_HO_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_RX_FULL_REORDER, + WMI_SERVICE_RX_FULL_REORDER, len); + SVCMAP(WMI_TLV_SERVICE_DHCP_OFFLOAD, + WMI_SERVICE_DHCP_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT, + WMI_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT, len); + SVCMAP(WMI_TLV_SERVICE_MDNS_OFFLOAD, + WMI_SERVICE_MDNS_OFFLOAD, len); + SVCMAP(WMI_TLV_SERVICE_SAP_AUTH_OFFLOAD, + WMI_SERVICE_SAP_AUTH_OFFLOAD, len); +} + +#undef SVCMAP + +struct wmi_tlv { + __le16 len; + __le16 tag; + u8 value[0]; +} __packed; + +#define WMI_TLV_MGMT_RX_NUM_RSSI 4 + +struct wmi_tlv_mgmt_rx_ev { + __le32 channel; + __le32 snr; + __le32 rate; + __le32 phy_mode; + __le32 buf_len; + __le32 status; + __le32 rssi[WMI_TLV_MGMT_RX_NUM_RSSI]; +} __packed; + +struct wmi_tlv_abi_version { + __le32 abi_ver0; + __le32 abi_ver1; + __le32 abi_ver_ns0; + __le32 abi_ver_ns1; + __le32 abi_ver_ns2; + __le32 abi_ver_ns3; +} __packed; + +enum wmi_tlv_hw_bd_id { + WMI_TLV_HW_BD_LEGACY = 0, + WMI_TLV_HW_BD_QCA6174 = 1, + WMI_TLV_HW_BD_QCA2582 = 2, +}; + +struct wmi_tlv_hw_bd_info { + u8 rev; + u8 project_id; + u8 custom_id; + u8 reference_design_id; +} __packed; + +struct wmi_tlv_svc_rdy_ev { + __le32 fw_build_vers; + struct wmi_tlv_abi_version abi; + __le32 phy_capability; + __le32 max_frag_entry; + __le32 num_rf_chains; + __le32 ht_cap_info; + __le32 vht_cap_info; + __le32 vht_supp_mcs; + __le32 hw_min_tx_power; + __le32 hw_max_tx_power; + __le32 sys_cap_info; + __le32 min_pkt_size_enable; + __le32 max_bcn_ie_size; + __le32 num_mem_reqs; + __le32 max_num_scan_chans; + __le32 hw_bd_id; /* 0 means hw_bd_info is invalid */ + struct wmi_tlv_hw_bd_info hw_bd_info[5]; +} __packed; + +struct wmi_tlv_rdy_ev { + struct wmi_tlv_abi_version abi; + struct wmi_mac_addr mac_addr; + __le32 status; +} __packed; + +struct wmi_tlv_resource_config { + __le32 num_vdevs; + __le32 num_peers; + __le32 num_offload_peers; + __le32 num_offload_reorder_bufs; + __le32 num_peer_keys; + __le32 num_tids; + __le32 ast_skid_limit; + __le32 tx_chain_mask; + __le32 rx_chain_mask; + __le32 rx_timeout_pri[4]; + __le32 rx_decap_mode; + __le32 scan_max_pending_reqs; + __le32 bmiss_offload_max_vdev; + __le32 roam_offload_max_vdev; + __le32 roam_offload_max_ap_profiles; + __le32 num_mcast_groups; + __le32 num_mcast_table_elems; + __le32 mcast2ucast_mode; + __le32 tx_dbg_log_size; + __le32 num_wds_entries; + __le32 dma_burst_size; + __le32 mac_aggr_delim; + __le32 rx_skip_defrag_timeout_dup_detection_check; + __le32 vow_config; + __le32 gtk_offload_max_vdev; + __le32 num_msdu_desc; + __le32 max_frag_entries; + __le32 num_tdls_vdevs; + __le32 num_tdls_conn_table_entries; + __le32 beacon_tx_offload_max_vdev; + __le32 num_multicast_filter_entries; + __le32 num_wow_filters; + __le32 num_keep_alive_pattern; + __le32 keep_alive_pattern_size; + __le32 max_tdls_concurrent_sleep_sta; + __le32 max_tdls_concurrent_buffer_sta; +} __packed; + +struct wmi_tlv_init_cmd { + struct wmi_tlv_abi_version abi; + __le32 num_host_mem_chunks; +} __packed; + +struct wmi_tlv_pdev_set_param_cmd { + __le32 pdev_id; /* not used yet */ + __le32 param_id; + __le32 param_value; +} __packed; + +struct wmi_tlv_pdev_set_rd_cmd { + __le32 pdev_id; /* not used yet */ + __le32 regd; + __le32 regd_2ghz; + __le32 regd_5ghz; + __le32 conform_limit_2ghz; + __le32 conform_limit_5ghz; +} __packed; + +struct wmi_tlv_scan_chan_list_cmd { + __le32 num_scan_chans; +} __packed; + +struct wmi_tlv_start_scan_cmd { + struct wmi_start_scan_common common; + __le32 burst_duration_ms; + __le32 num_channels; + __le32 num_bssids; + __le32 num_ssids; + __le32 ie_len; + __le32 num_probes; +} __packed; + +struct wmi_tlv_vdev_start_cmd { + __le32 vdev_id; + __le32 requestor_id; + __le32 bcn_intval; + __le32 dtim_period; + __le32 flags; + struct wmi_ssid ssid; + __le32 bcn_tx_rate; + __le32 bcn_tx_power; + __le32 num_noa_descr; + __le32 disable_hw_ack; +} __packed; + +enum { + WMI_TLV_PEER_TYPE_DEFAULT = 0, /* generic / non-BSS / self-peer */ + WMI_TLV_PEER_TYPE_BSS = 1, + WMI_TLV_PEER_TYPE_TDLS = 2, + WMI_TLV_PEER_TYPE_HOST_MAX = 127, + WMI_TLV_PEER_TYPE_ROAMOFFLOAD_TMP = 128, +}; + +struct wmi_tlv_peer_create_cmd { + __le32 vdev_id; + struct wmi_mac_addr peer_addr; + __le32 peer_type; +} __packed; + +struct wmi_tlv_peer_assoc_cmd { + struct wmi_mac_addr mac_addr; + __le32 vdev_id; + __le32 new_assoc; + __le32 assoc_id; + __le32 flags; + __le32 caps; + __le32 listen_intval; + __le32 ht_caps; + __le32 max_mpdu; + __le32 mpdu_density; + __le32 rate_caps; + __le32 nss; + __le32 vht_caps; + __le32 phy_mode; + __le32 ht_info[2]; + __le32 num_legacy_rates; + __le32 num_ht_rates; +} __packed; + +struct wmi_tlv_pdev_suspend { + __le32 pdev_id; /* not used yet */ + __le32 opt; +} __packed; + +struct wmi_tlv_pdev_set_wmm_cmd { + __le32 pdev_id; /* not used yet */ + __le32 dg_type; /* no idea.. */ +} __packed; + +struct wmi_tlv_vdev_set_wmm_cmd { + __le32 vdev_id; +} __packed; + +struct wmi_tlv_phyerr_ev { + __le32 num_phyerrs; + __le32 tsf_l32; + __le32 tsf_u32; + __le32 buf_len; +} __packed; + +enum wmi_tlv_dbglog_param { + WMI_TLV_DBGLOG_PARAM_LOG_LEVEL = 1, + WMI_TLV_DBGLOG_PARAM_VDEV_ENABLE, + WMI_TLV_DBGLOG_PARAM_VDEV_DISABLE, + WMI_TLV_DBGLOG_PARAM_VDEV_ENABLE_BITMAP, + WMI_TLV_DBGLOG_PARAM_VDEV_DISABLE_BITMAP, +}; + +enum wmi_tlv_dbglog_log_level { + WMI_TLV_DBGLOG_LOG_LEVEL_VERBOSE = 0, + WMI_TLV_DBGLOG_LOG_LEVEL_INFO, + WMI_TLV_DBGLOG_LOG_LEVEL_INFO_LVL_1, + WMI_TLV_DBGLOG_LOG_LEVEL_INFO_LVL_2, + WMI_TLV_DBGLOG_LOG_LEVEL_WARN, + WMI_TLV_DBGLOG_LOG_LEVEL_ERR, +}; + +#define WMI_TLV_DBGLOG_BITMAP_MAX_IDS 512 +#define WMI_TLV_DBGLOG_BITMAP_MAX_WORDS (WMI_TLV_DBGLOG_BITMAP_MAX_IDS / \ + sizeof(__le32)) +#define WMI_TLV_DBGLOG_ALL_MODULES 0xffff +#define WMI_TLV_DBGLOG_LOG_LEVEL_VALUE(module_id, log_level) \ + (((module_id << 16) & 0xffff0000) | \ + ((log_level << 0) & 0x000000ff)) + +struct wmi_tlv_dbglog_cmd { + __le32 param; + __le32 value; +} __packed; + +struct wmi_tlv_resume_cmd { + __le32 reserved; +} __packed; + +struct wmi_tlv_req_stats_cmd { + __le32 stats_id; /* wmi_stats_id */ + __le32 vdev_id; + struct wmi_mac_addr peer_macaddr; +} __packed; + +struct wmi_tlv_vdev_stats { + __le32 vdev_id; + __le32 beacon_snr; + __le32 data_snr; + __le32 num_tx_frames[4]; /* per-AC */ + __le32 num_rx_frames; + __le32 num_tx_frames_retries[4]; + __le32 num_tx_frames_failures[4]; + __le32 num_rts_fail; + __le32 num_rts_success; + __le32 num_rx_err; + __le32 num_rx_discard; + __le32 num_tx_not_acked; + __le32 tx_rate_history[10]; + __le32 beacon_rssi_history[10]; +} __packed; + +struct wmi_tlv_pktlog_enable { + __le32 reserved; + __le32 filter; +} __packed; + +struct wmi_tlv_pktlog_disable { + __le32 reserved; +} __packed; + +enum wmi_tlv_bcn_tx_status { + WMI_TLV_BCN_TX_STATUS_OK, + WMI_TLV_BCN_TX_STATUS_XRETRY, + WMI_TLV_BCN_TX_STATUS_DROP, + WMI_TLV_BCN_TX_STATUS_FILTERED, +}; + +struct wmi_tlv_bcn_tx_status_ev { + __le32 vdev_id; + __le32 tx_status; +} __packed; + +struct wmi_tlv_bcn_prb_info { + __le32 caps; + __le32 erp; + u8 ies[0]; +} __packed; + +struct wmi_tlv_bcn_tmpl_cmd { + __le32 vdev_id; + __le32 tim_ie_offset; + __le32 buf_len; +} __packed; + +struct wmi_tlv_prb_tmpl_cmd { + __le32 vdev_id; + __le32 buf_len; +} __packed; + +struct wmi_tlv_p2p_go_bcn_ie { + __le32 vdev_id; + __le32 ie_len; +} __packed; + +enum wmi_tlv_diag_item_type { + WMI_TLV_DIAG_ITEM_TYPE_FW_EVENT, + WMI_TLV_DIAG_ITEM_TYPE_FW_LOG, + WMI_TLV_DIAG_ITEM_TYPE_FW_DEBUG_MSG, +}; + +struct wmi_tlv_diag_item { + u8 type; + u8 reserved; + __le16 len; + __le32 timestamp; + __le32 code; + u8 payload[0]; +} __packed; + +struct wmi_tlv_diag_data_ev { + __le32 num_items; +} __packed; + +struct wmi_tlv_sta_keepalive_cmd { + __le32 vdev_id; + __le32 enabled; + __le32 method; /* WMI_STA_KEEPALIVE_METHOD_ */ + __le32 interval; /* in seconds */ +} __packed; + +void ath10k_wmi_tlv_attach(struct ath10k *ar); + +#endif diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index c0f3e4d09263..aeea1c793943 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -22,8 +22,10 @@ #include "htc.h" #include "debug.h" #include "wmi.h" +#include "wmi-tlv.h" #include "mac.h" #include "testmode.h" +#include "wmi-ops.h" /* MAIN WMI cmd track */ static struct wmi_cmd_map wmi_cmd_map = { @@ -143,6 +145,7 @@ static struct wmi_cmd_map wmi_cmd_map = { .force_fw_hang_cmdid = WMI_FORCE_FW_HANG_CMDID, .gpio_config_cmdid = WMI_GPIO_CONFIG_CMDID, .gpio_output_cmdid = WMI_GPIO_OUTPUT_CMDID, + .pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED, }; /* 10.X WMI cmd track */ @@ -265,6 +268,129 @@ static struct wmi_cmd_map wmi_10x_cmd_map = { .force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED, .gpio_config_cmdid = WMI_10X_GPIO_CONFIG_CMDID, .gpio_output_cmdid = WMI_10X_GPIO_OUTPUT_CMDID, + .pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED, +}; + +/* 10.2.4 WMI cmd track */ +static struct wmi_cmd_map wmi_10_2_4_cmd_map = { + .init_cmdid = WMI_10_2_INIT_CMDID, + .start_scan_cmdid = WMI_10_2_START_SCAN_CMDID, + .stop_scan_cmdid = WMI_10_2_STOP_SCAN_CMDID, + .scan_chan_list_cmdid = WMI_10_2_SCAN_CHAN_LIST_CMDID, + .scan_sch_prio_tbl_cmdid = WMI_CMD_UNSUPPORTED, + .pdev_set_regdomain_cmdid = WMI_10_2_PDEV_SET_REGDOMAIN_CMDID, + .pdev_set_channel_cmdid = WMI_10_2_PDEV_SET_CHANNEL_CMDID, + .pdev_set_param_cmdid = WMI_10_2_PDEV_SET_PARAM_CMDID, + .pdev_pktlog_enable_cmdid = WMI_10_2_PDEV_PKTLOG_ENABLE_CMDID, + .pdev_pktlog_disable_cmdid = WMI_10_2_PDEV_PKTLOG_DISABLE_CMDID, + .pdev_set_wmm_params_cmdid = WMI_10_2_PDEV_SET_WMM_PARAMS_CMDID, + .pdev_set_ht_cap_ie_cmdid = WMI_10_2_PDEV_SET_HT_CAP_IE_CMDID, + .pdev_set_vht_cap_ie_cmdid = WMI_10_2_PDEV_SET_VHT_CAP_IE_CMDID, + .pdev_set_quiet_mode_cmdid = WMI_10_2_PDEV_SET_QUIET_MODE_CMDID, + .pdev_green_ap_ps_enable_cmdid = WMI_10_2_PDEV_GREEN_AP_PS_ENABLE_CMDID, + .pdev_get_tpc_config_cmdid = WMI_10_2_PDEV_GET_TPC_CONFIG_CMDID, + .pdev_set_base_macaddr_cmdid = WMI_10_2_PDEV_SET_BASE_MACADDR_CMDID, + .vdev_create_cmdid = WMI_10_2_VDEV_CREATE_CMDID, + .vdev_delete_cmdid = WMI_10_2_VDEV_DELETE_CMDID, + .vdev_start_request_cmdid = WMI_10_2_VDEV_START_REQUEST_CMDID, + .vdev_restart_request_cmdid = WMI_10_2_VDEV_RESTART_REQUEST_CMDID, + .vdev_up_cmdid = WMI_10_2_VDEV_UP_CMDID, + .vdev_stop_cmdid = WMI_10_2_VDEV_STOP_CMDID, + .vdev_down_cmdid = WMI_10_2_VDEV_DOWN_CMDID, + .vdev_set_param_cmdid = WMI_10_2_VDEV_SET_PARAM_CMDID, + .vdev_install_key_cmdid = WMI_10_2_VDEV_INSTALL_KEY_CMDID, + .peer_create_cmdid = WMI_10_2_PEER_CREATE_CMDID, + .peer_delete_cmdid = WMI_10_2_PEER_DELETE_CMDID, + .peer_flush_tids_cmdid = WMI_10_2_PEER_FLUSH_TIDS_CMDID, + .peer_set_param_cmdid = WMI_10_2_PEER_SET_PARAM_CMDID, + .peer_assoc_cmdid = WMI_10_2_PEER_ASSOC_CMDID, + .peer_add_wds_entry_cmdid = WMI_10_2_PEER_ADD_WDS_ENTRY_CMDID, + .peer_remove_wds_entry_cmdid = WMI_10_2_PEER_REMOVE_WDS_ENTRY_CMDID, + .peer_mcast_group_cmdid = WMI_10_2_PEER_MCAST_GROUP_CMDID, + .bcn_tx_cmdid = WMI_10_2_BCN_TX_CMDID, + .pdev_send_bcn_cmdid = WMI_10_2_PDEV_SEND_BCN_CMDID, + .bcn_tmpl_cmdid = WMI_CMD_UNSUPPORTED, + .bcn_filter_rx_cmdid = WMI_10_2_BCN_FILTER_RX_CMDID, + .prb_req_filter_rx_cmdid = WMI_10_2_PRB_REQ_FILTER_RX_CMDID, + .mgmt_tx_cmdid = WMI_10_2_MGMT_TX_CMDID, + .prb_tmpl_cmdid = WMI_CMD_UNSUPPORTED, + .addba_clear_resp_cmdid = WMI_10_2_ADDBA_CLEAR_RESP_CMDID, + .addba_send_cmdid = WMI_10_2_ADDBA_SEND_CMDID, + .addba_status_cmdid = WMI_10_2_ADDBA_STATUS_CMDID, + .delba_send_cmdid = WMI_10_2_DELBA_SEND_CMDID, + .addba_set_resp_cmdid = WMI_10_2_ADDBA_SET_RESP_CMDID, + .send_singleamsdu_cmdid = WMI_10_2_SEND_SINGLEAMSDU_CMDID, + .sta_powersave_mode_cmdid = WMI_10_2_STA_POWERSAVE_MODE_CMDID, + .sta_powersave_param_cmdid = WMI_10_2_STA_POWERSAVE_PARAM_CMDID, + .sta_mimo_ps_mode_cmdid = WMI_10_2_STA_MIMO_PS_MODE_CMDID, + .pdev_dfs_enable_cmdid = WMI_10_2_PDEV_DFS_ENABLE_CMDID, + .pdev_dfs_disable_cmdid = WMI_10_2_PDEV_DFS_DISABLE_CMDID, + .roam_scan_mode = WMI_10_2_ROAM_SCAN_MODE, + .roam_scan_rssi_threshold = WMI_10_2_ROAM_SCAN_RSSI_THRESHOLD, + .roam_scan_period = WMI_10_2_ROAM_SCAN_PERIOD, + .roam_scan_rssi_change_threshold = + WMI_10_2_ROAM_SCAN_RSSI_CHANGE_THRESHOLD, + .roam_ap_profile = WMI_10_2_ROAM_AP_PROFILE, + .ofl_scan_add_ap_profile = WMI_10_2_OFL_SCAN_ADD_AP_PROFILE, + .ofl_scan_remove_ap_profile = WMI_10_2_OFL_SCAN_REMOVE_AP_PROFILE, + .ofl_scan_period = WMI_10_2_OFL_SCAN_PERIOD, + .p2p_dev_set_device_info = WMI_10_2_P2P_DEV_SET_DEVICE_INFO, + .p2p_dev_set_discoverability = WMI_10_2_P2P_DEV_SET_DISCOVERABILITY, + .p2p_go_set_beacon_ie = WMI_10_2_P2P_GO_SET_BEACON_IE, + .p2p_go_set_probe_resp_ie = WMI_10_2_P2P_GO_SET_PROBE_RESP_IE, + .p2p_set_vendor_ie_data_cmdid = WMI_CMD_UNSUPPORTED, + .ap_ps_peer_param_cmdid = WMI_10_2_AP_PS_PEER_PARAM_CMDID, + .ap_ps_peer_uapsd_coex_cmdid = WMI_CMD_UNSUPPORTED, + .peer_rate_retry_sched_cmdid = WMI_10_2_PEER_RATE_RETRY_SCHED_CMDID, + .wlan_profile_trigger_cmdid = WMI_10_2_WLAN_PROFILE_TRIGGER_CMDID, + .wlan_profile_set_hist_intvl_cmdid = + WMI_10_2_WLAN_PROFILE_SET_HIST_INTVL_CMDID, + .wlan_profile_get_profile_data_cmdid = + WMI_10_2_WLAN_PROFILE_GET_PROFILE_DATA_CMDID, + .wlan_profile_enable_profile_id_cmdid = + WMI_10_2_WLAN_PROFILE_ENABLE_PROFILE_ID_CMDID, + .wlan_profile_list_profile_id_cmdid = + WMI_10_2_WLAN_PROFILE_LIST_PROFILE_ID_CMDID, + .pdev_suspend_cmdid = WMI_10_2_PDEV_SUSPEND_CMDID, + .pdev_resume_cmdid = WMI_10_2_PDEV_RESUME_CMDID, + .add_bcn_filter_cmdid = WMI_10_2_ADD_BCN_FILTER_CMDID, + .rmv_bcn_filter_cmdid = WMI_10_2_RMV_BCN_FILTER_CMDID, + .wow_add_wake_pattern_cmdid = WMI_10_2_WOW_ADD_WAKE_PATTERN_CMDID, + .wow_del_wake_pattern_cmdid = WMI_10_2_WOW_DEL_WAKE_PATTERN_CMDID, + .wow_enable_disable_wake_event_cmdid = + WMI_10_2_WOW_ENABLE_DISABLE_WAKE_EVENT_CMDID, + .wow_enable_cmdid = WMI_10_2_WOW_ENABLE_CMDID, + .wow_hostwakeup_from_sleep_cmdid = + WMI_10_2_WOW_HOSTWAKEUP_FROM_SLEEP_CMDID, + .rtt_measreq_cmdid = WMI_10_2_RTT_MEASREQ_CMDID, + .rtt_tsf_cmdid = WMI_10_2_RTT_TSF_CMDID, + .vdev_spectral_scan_configure_cmdid = + WMI_10_2_VDEV_SPECTRAL_SCAN_CONFIGURE_CMDID, + .vdev_spectral_scan_enable_cmdid = + WMI_10_2_VDEV_SPECTRAL_SCAN_ENABLE_CMDID, + .request_stats_cmdid = WMI_10_2_REQUEST_STATS_CMDID, + .set_arp_ns_offload_cmdid = WMI_CMD_UNSUPPORTED, + .network_list_offload_config_cmdid = WMI_CMD_UNSUPPORTED, + .gtk_offload_cmdid = WMI_CMD_UNSUPPORTED, + .csa_offload_enable_cmdid = WMI_CMD_UNSUPPORTED, + .csa_offload_chanswitch_cmdid = WMI_CMD_UNSUPPORTED, + .chatter_set_mode_cmdid = WMI_CMD_UNSUPPORTED, + .peer_tid_addba_cmdid = WMI_CMD_UNSUPPORTED, + .peer_tid_delba_cmdid = WMI_CMD_UNSUPPORTED, + .sta_dtim_ps_method_cmdid = WMI_CMD_UNSUPPORTED, + .sta_uapsd_auto_trig_cmdid = WMI_CMD_UNSUPPORTED, + .sta_keepalive_cmd = WMI_CMD_UNSUPPORTED, + .echo_cmdid = WMI_10_2_ECHO_CMDID, + .pdev_utf_cmdid = WMI_10_2_PDEV_UTF_CMDID, + .dbglog_cfg_cmdid = WMI_10_2_DBGLOG_CFG_CMDID, + .pdev_qvit_cmdid = WMI_10_2_PDEV_QVIT_CMDID, + .pdev_ftm_intg_cmdid = WMI_CMD_UNSUPPORTED, + .vdev_set_keepalive_cmdid = WMI_CMD_UNSUPPORTED, + .vdev_get_keepalive_cmdid = WMI_CMD_UNSUPPORTED, + .force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED, + .gpio_config_cmdid = WMI_10_2_GPIO_CONFIG_CMDID, + .gpio_output_cmdid = WMI_10_2_GPIO_OUTPUT_CMDID, + .pdev_get_temperature_cmdid = WMI_10_2_PDEV_GET_TEMPERATURE_CMDID, }; /* MAIN WMI VDEV param map */ @@ -385,6 +511,64 @@ static struct wmi_vdev_param_map wmi_10x_vdev_param_map = { WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS, }; +static struct wmi_vdev_param_map wmi_10_2_4_vdev_param_map = { + .rts_threshold = WMI_10X_VDEV_PARAM_RTS_THRESHOLD, + .fragmentation_threshold = WMI_10X_VDEV_PARAM_FRAGMENTATION_THRESHOLD, + .beacon_interval = WMI_10X_VDEV_PARAM_BEACON_INTERVAL, + .listen_interval = WMI_10X_VDEV_PARAM_LISTEN_INTERVAL, + .multicast_rate = WMI_10X_VDEV_PARAM_MULTICAST_RATE, + .mgmt_tx_rate = WMI_10X_VDEV_PARAM_MGMT_TX_RATE, + .slot_time = WMI_10X_VDEV_PARAM_SLOT_TIME, + .preamble = WMI_10X_VDEV_PARAM_PREAMBLE, + .swba_time = WMI_10X_VDEV_PARAM_SWBA_TIME, + .wmi_vdev_stats_update_period = WMI_10X_VDEV_STATS_UPDATE_PERIOD, + .wmi_vdev_pwrsave_ageout_time = WMI_10X_VDEV_PWRSAVE_AGEOUT_TIME, + .wmi_vdev_host_swba_interval = WMI_10X_VDEV_HOST_SWBA_INTERVAL, + .dtim_period = WMI_10X_VDEV_PARAM_DTIM_PERIOD, + .wmi_vdev_oc_scheduler_air_time_limit = + WMI_10X_VDEV_OC_SCHEDULER_AIR_TIME_LIMIT, + .wds = WMI_10X_VDEV_PARAM_WDS, + .atim_window = WMI_10X_VDEV_PARAM_ATIM_WINDOW, + .bmiss_count_max = WMI_10X_VDEV_PARAM_BMISS_COUNT_MAX, + .bmiss_first_bcnt = WMI_VDEV_PARAM_UNSUPPORTED, + .bmiss_final_bcnt = WMI_VDEV_PARAM_UNSUPPORTED, + .feature_wmm = WMI_10X_VDEV_PARAM_FEATURE_WMM, + .chwidth = WMI_10X_VDEV_PARAM_CHWIDTH, + .chextoffset = WMI_10X_VDEV_PARAM_CHEXTOFFSET, + .disable_htprotection = WMI_10X_VDEV_PARAM_DISABLE_HTPROTECTION, + .sta_quickkickout = WMI_10X_VDEV_PARAM_STA_QUICKKICKOUT, + .mgmt_rate = WMI_10X_VDEV_PARAM_MGMT_RATE, + .protection_mode = WMI_10X_VDEV_PARAM_PROTECTION_MODE, + .fixed_rate = WMI_10X_VDEV_PARAM_FIXED_RATE, + .sgi = WMI_10X_VDEV_PARAM_SGI, + .ldpc = WMI_10X_VDEV_PARAM_LDPC, + .tx_stbc = WMI_10X_VDEV_PARAM_TX_STBC, + .rx_stbc = WMI_10X_VDEV_PARAM_RX_STBC, + .intra_bss_fwd = WMI_10X_VDEV_PARAM_INTRA_BSS_FWD, + .def_keyid = WMI_10X_VDEV_PARAM_DEF_KEYID, + .nss = WMI_10X_VDEV_PARAM_NSS, + .bcast_data_rate = WMI_10X_VDEV_PARAM_BCAST_DATA_RATE, + .mcast_data_rate = WMI_10X_VDEV_PARAM_MCAST_DATA_RATE, + .mcast_indicate = WMI_10X_VDEV_PARAM_MCAST_INDICATE, + .dhcp_indicate = WMI_10X_VDEV_PARAM_DHCP_INDICATE, + .unknown_dest_indicate = WMI_10X_VDEV_PARAM_UNKNOWN_DEST_INDICATE, + .ap_keepalive_min_idle_inactive_time_secs = + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MIN_IDLE_INACTIVE_TIME_SECS, + .ap_keepalive_max_idle_inactive_time_secs = + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_IDLE_INACTIVE_TIME_SECS, + .ap_keepalive_max_unresponsive_time_secs = + WMI_10X_VDEV_PARAM_AP_KEEPALIVE_MAX_UNRESPONSIVE_TIME_SECS, + .ap_enable_nawds = WMI_10X_VDEV_PARAM_AP_ENABLE_NAWDS, + .mcast2ucast_set = WMI_10X_VDEV_PARAM_MCAST2UCAST_SET, + .enable_rtscts = WMI_10X_VDEV_PARAM_ENABLE_RTSCTS, + .txbf = WMI_VDEV_PARAM_UNSUPPORTED, + .packet_powersave = WMI_VDEV_PARAM_UNSUPPORTED, + .drop_unencry = WMI_VDEV_PARAM_UNSUPPORTED, + .tx_encap_type = WMI_VDEV_PARAM_UNSUPPORTED, + .ap_detect_out_of_sync_sleeping_sta_time_secs = + WMI_10X_VDEV_PARAM_AP_DETECT_OUT_OF_SYNC_SLEEPING_STA_TIME_SECS, +}; + static struct wmi_pdev_param_map wmi_pdev_param_map = { .tx_chain_mask = WMI_PDEV_PARAM_TX_CHAIN_MASK, .rx_chain_mask = WMI_PDEV_PARAM_RX_CHAIN_MASK, @@ -434,6 +618,7 @@ static struct wmi_pdev_param_map wmi_pdev_param_map = { .fast_channel_reset = WMI_PDEV_PARAM_UNSUPPORTED, .burst_dur = WMI_PDEV_PARAM_UNSUPPORTED, .burst_enable = WMI_PDEV_PARAM_UNSUPPORTED, + .cal_period = WMI_PDEV_PARAM_UNSUPPORTED, }; static struct wmi_pdev_param_map wmi_10x_pdev_param_map = { @@ -486,6 +671,60 @@ static struct wmi_pdev_param_map wmi_10x_pdev_param_map = { .fast_channel_reset = WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET, .burst_dur = WMI_10X_PDEV_PARAM_BURST_DUR, .burst_enable = WMI_10X_PDEV_PARAM_BURST_ENABLE, + .cal_period = WMI_10X_PDEV_PARAM_CAL_PERIOD, +}; + +static struct wmi_pdev_param_map wmi_10_2_4_pdev_param_map = { + .tx_chain_mask = WMI_10X_PDEV_PARAM_TX_CHAIN_MASK, + .rx_chain_mask = WMI_10X_PDEV_PARAM_RX_CHAIN_MASK, + .txpower_limit2g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT2G, + .txpower_limit5g = WMI_10X_PDEV_PARAM_TXPOWER_LIMIT5G, + .txpower_scale = WMI_10X_PDEV_PARAM_TXPOWER_SCALE, + .beacon_gen_mode = WMI_10X_PDEV_PARAM_BEACON_GEN_MODE, + .beacon_tx_mode = WMI_10X_PDEV_PARAM_BEACON_TX_MODE, + .resmgr_offchan_mode = WMI_10X_PDEV_PARAM_RESMGR_OFFCHAN_MODE, + .protection_mode = WMI_10X_PDEV_PARAM_PROTECTION_MODE, + .dynamic_bw = WMI_10X_PDEV_PARAM_DYNAMIC_BW, + .non_agg_sw_retry_th = WMI_10X_PDEV_PARAM_NON_AGG_SW_RETRY_TH, + .agg_sw_retry_th = WMI_10X_PDEV_PARAM_AGG_SW_RETRY_TH, + .sta_kickout_th = WMI_10X_PDEV_PARAM_STA_KICKOUT_TH, + .ac_aggrsize_scaling = WMI_10X_PDEV_PARAM_AC_AGGRSIZE_SCALING, + .ltr_enable = WMI_10X_PDEV_PARAM_LTR_ENABLE, + .ltr_ac_latency_be = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BE, + .ltr_ac_latency_bk = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_BK, + .ltr_ac_latency_vi = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VI, + .ltr_ac_latency_vo = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_VO, + .ltr_ac_latency_timeout = WMI_10X_PDEV_PARAM_LTR_AC_LATENCY_TIMEOUT, + .ltr_sleep_override = WMI_10X_PDEV_PARAM_LTR_SLEEP_OVERRIDE, + .ltr_rx_override = WMI_10X_PDEV_PARAM_LTR_RX_OVERRIDE, + .ltr_tx_activity_timeout = WMI_10X_PDEV_PARAM_LTR_TX_ACTIVITY_TIMEOUT, + .l1ss_enable = WMI_10X_PDEV_PARAM_L1SS_ENABLE, + .dsleep_enable = WMI_10X_PDEV_PARAM_DSLEEP_ENABLE, + .pcielp_txbuf_flush = WMI_PDEV_PARAM_UNSUPPORTED, + .pcielp_txbuf_watermark = WMI_PDEV_PARAM_UNSUPPORTED, + .pcielp_txbuf_tmo_en = WMI_PDEV_PARAM_UNSUPPORTED, + .pcielp_txbuf_tmo_value = WMI_PDEV_PARAM_UNSUPPORTED, + .pdev_stats_update_period = WMI_10X_PDEV_PARAM_PDEV_STATS_UPDATE_PERIOD, + .vdev_stats_update_period = WMI_10X_PDEV_PARAM_VDEV_STATS_UPDATE_PERIOD, + .peer_stats_update_period = WMI_10X_PDEV_PARAM_PEER_STATS_UPDATE_PERIOD, + .bcnflt_stats_update_period = + WMI_10X_PDEV_PARAM_BCNFLT_STATS_UPDATE_PERIOD, + .pmf_qos = WMI_10X_PDEV_PARAM_PMF_QOS, + .arp_ac_override = WMI_10X_PDEV_PARAM_ARPDHCP_AC_OVERRIDE, + .dcs = WMI_10X_PDEV_PARAM_DCS, + .ani_enable = WMI_10X_PDEV_PARAM_ANI_ENABLE, + .ani_poll_period = WMI_10X_PDEV_PARAM_ANI_POLL_PERIOD, + .ani_listen_period = WMI_10X_PDEV_PARAM_ANI_LISTEN_PERIOD, + .ani_ofdm_level = WMI_10X_PDEV_PARAM_ANI_OFDM_LEVEL, + .ani_cck_level = WMI_10X_PDEV_PARAM_ANI_CCK_LEVEL, + .dyntxchain = WMI_10X_PDEV_PARAM_DYNTXCHAIN, + .proxy_sta = WMI_PDEV_PARAM_UNSUPPORTED, + .idle_ps_config = WMI_PDEV_PARAM_UNSUPPORTED, + .power_gating_sleep = WMI_PDEV_PARAM_UNSUPPORTED, + .fast_channel_reset = WMI_10X_PDEV_PARAM_FAST_CHANNEL_RESET, + .burst_dur = WMI_10X_PDEV_PARAM_BURST_DUR, + .burst_enable = WMI_10X_PDEV_PARAM_BURST_ENABLE, + .cal_period = WMI_10X_PDEV_PARAM_CAL_PERIOD, }; /* firmware 10.2 specific mappings */ @@ -607,11 +846,11 @@ static struct wmi_cmd_map wmi_10_2_cmd_map = { .force_fw_hang_cmdid = WMI_CMD_UNSUPPORTED, .gpio_config_cmdid = WMI_10_2_GPIO_CONFIG_CMDID, .gpio_output_cmdid = WMI_10_2_GPIO_OUTPUT_CMDID, + .pdev_get_temperature_cmdid = WMI_CMD_UNSUPPORTED, }; -static void -ath10k_wmi_put_wmi_channel(struct wmi_channel *ch, - const struct wmi_channel_arg *arg) +void ath10k_wmi_put_wmi_channel(struct wmi_channel *ch, + const struct wmi_channel_arg *arg) { u32 flags = 0; @@ -685,8 +924,8 @@ static void ath10k_wmi_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb) dev_kfree_skb(skb); } -static int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb, - u32 cmd_id) +int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb, + u32 cmd_id) { struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb); struct wmi_cmd_hdr *cmd_hdr; @@ -717,23 +956,45 @@ err_pull: static void ath10k_wmi_tx_beacon_nowait(struct ath10k_vif *arvif) { + struct ath10k *ar = arvif->ar; + struct ath10k_skb_cb *cb; + struct sk_buff *bcn; int ret; - lockdep_assert_held(&arvif->ar->data_lock); + spin_lock_bh(&ar->data_lock); - if (arvif->beacon == NULL) - return; + bcn = arvif->beacon; - if (arvif->beacon_sent) - return; + if (!bcn) + goto unlock; - ret = ath10k_wmi_beacon_send_ref_nowait(arvif); - if (ret) - return; + cb = ATH10K_SKB_CB(bcn); + + switch (arvif->beacon_state) { + case ATH10K_BEACON_SENDING: + case ATH10K_BEACON_SENT: + break; + case ATH10K_BEACON_SCHEDULED: + arvif->beacon_state = ATH10K_BEACON_SENDING; + spin_unlock_bh(&ar->data_lock); + + ret = ath10k_wmi_beacon_send_ref_nowait(arvif->ar, + arvif->vdev_id, + bcn->data, bcn->len, + cb->paddr, + cb->bcn.dtim_zero, + cb->bcn.deliver_cab); + + spin_lock_bh(&ar->data_lock); + + if (ret == 0) + arvif->beacon_state = ATH10K_BEACON_SENT; + else + arvif->beacon_state = ATH10K_BEACON_SCHEDULED; + } - /* We need to retain the arvif->beacon reference for DMA unmapping and - * freeing the skbuff later. */ - arvif->beacon_sent = true; +unlock: + spin_unlock_bh(&ar->data_lock); } static void ath10k_wmi_tx_beacons_iter(void *data, u8 *mac, @@ -746,12 +1007,10 @@ static void ath10k_wmi_tx_beacons_iter(void *data, u8 *mac, static void ath10k_wmi_tx_beacons_nowait(struct ath10k *ar) { - spin_lock_bh(&ar->data_lock); ieee80211_iterate_active_interfaces_atomic(ar->hw, IEEE80211_IFACE_ITER_NORMAL, ath10k_wmi_tx_beacons_iter, NULL); - spin_unlock_bh(&ar->data_lock); } static void ath10k_wmi_op_ep_tx_credits(struct ath10k *ar) @@ -792,24 +1051,23 @@ int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id) return ret; } -int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb) +static struct sk_buff * +ath10k_wmi_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu) { - int ret = 0; struct wmi_mgmt_tx_cmd *cmd; struct ieee80211_hdr *hdr; - struct sk_buff *wmi_skb; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct sk_buff *skb; int len; - u32 buf_len = skb->len; + u32 buf_len = msdu->len; u16 fc; - hdr = (struct ieee80211_hdr *)skb->data; + hdr = (struct ieee80211_hdr *)msdu->data; fc = le16_to_cpu(hdr->frame_control); if (WARN_ON_ONCE(!ieee80211_is_mgmt(hdr->frame_control))) - return -EINVAL; + return ERR_PTR(-EINVAL); - len = sizeof(cmd->hdr) + skb->len; + len = sizeof(cmd->hdr) + msdu->len; if ((ieee80211_is_action(hdr->frame_control) || ieee80211_is_deauth(hdr->frame_control) || @@ -821,36 +1079,27 @@ int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb) len = round_up(len, 4); - wmi_skb = ath10k_wmi_alloc_skb(ar, len); - if (!wmi_skb) - return -ENOMEM; + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); - cmd = (struct wmi_mgmt_tx_cmd *)wmi_skb->data; + cmd = (struct wmi_mgmt_tx_cmd *)skb->data; - cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(skb)->vdev_id); + cmd->hdr.vdev_id = __cpu_to_le32(ATH10K_SKB_CB(msdu)->vdev_id); cmd->hdr.tx_rate = 0; cmd->hdr.tx_power = 0; cmd->hdr.buf_len = __cpu_to_le32(buf_len); ether_addr_copy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr)); - memcpy(cmd->buf, skb->data, skb->len); + memcpy(cmd->buf, msdu->data, msdu->len); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt tx skb %p len %d ftype %02x stype %02x\n", - wmi_skb, wmi_skb->len, fc & IEEE80211_FCTL_FTYPE, + msdu, skb->len, fc & IEEE80211_FCTL_FTYPE, fc & IEEE80211_FCTL_STYPE); trace_ath10k_tx_hdr(ar, skb->data, skb->len); trace_ath10k_tx_payload(ar, skb->data, skb->len); - /* Send the management frame buffer to the target */ - ret = ath10k_wmi_cmd_send(ar, wmi_skb, ar->wmi.cmd->mgmt_tx_cmdid); - if (ret) - return ret; - - /* TODO: report tx status to mac80211 - temporary just ACK */ - info->flags |= IEEE80211_TX_STAT_ACK; - ieee80211_tx_status_irqsafe(ar->hw, skb); - - return ret; + return skb; } static void ath10k_wmi_event_scan_started(struct ath10k *ar) @@ -977,22 +1226,48 @@ ath10k_wmi_event_scan_type_str(enum wmi_scan_event_type type, } } -static int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb) +static int ath10k_wmi_op_pull_scan_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_scan_ev_arg *arg) { - struct wmi_scan_event *event = (struct wmi_scan_event *)skb->data; + struct wmi_scan_event *ev = (void *)skb->data; + + if (skb->len < sizeof(*ev)) + return -EPROTO; + + skb_pull(skb, sizeof(*ev)); + arg->event_type = ev->event_type; + arg->reason = ev->reason; + arg->channel_freq = ev->channel_freq; + arg->scan_req_id = ev->scan_req_id; + arg->scan_id = ev->scan_id; + arg->vdev_id = ev->vdev_id; + + return 0; +} + +int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_scan_ev_arg arg = {}; enum wmi_scan_event_type event_type; enum wmi_scan_completion_reason reason; u32 freq; u32 req_id; u32 scan_id; u32 vdev_id; + int ret; + + ret = ath10k_wmi_pull_scan(ar, skb, &arg); + if (ret) { + ath10k_warn(ar, "failed to parse scan event: %d\n", ret); + return ret; + } - event_type = __le32_to_cpu(event->event_type); - reason = __le32_to_cpu(event->reason); - freq = __le32_to_cpu(event->channel_freq); - req_id = __le32_to_cpu(event->scan_req_id); - scan_id = __le32_to_cpu(event->scan_id); - vdev_id = __le32_to_cpu(event->vdev_id); + event_type = __le32_to_cpu(arg.event_type); + reason = __le32_to_cpu(arg.reason); + freq = __le32_to_cpu(arg.channel_freq); + req_id = __le32_to_cpu(arg.scan_req_id); + scan_id = __le32_to_cpu(arg.scan_id); + vdev_id = __le32_to_cpu(arg.vdev_id); spin_lock_bh(&ar->data_lock); @@ -1147,11 +1422,51 @@ static void ath10k_wmi_handle_wep_reauth(struct ath10k *ar, } } -static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) +static int ath10k_wmi_op_pull_mgmt_rx_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_mgmt_rx_ev_arg *arg) { struct wmi_mgmt_rx_event_v1 *ev_v1; struct wmi_mgmt_rx_event_v2 *ev_v2; struct wmi_mgmt_rx_hdr_v1 *ev_hdr; + size_t pull_len; + u32 msdu_len; + + if (test_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features)) { + ev_v2 = (struct wmi_mgmt_rx_event_v2 *)skb->data; + ev_hdr = &ev_v2->hdr.v1; + pull_len = sizeof(*ev_v2); + } else { + ev_v1 = (struct wmi_mgmt_rx_event_v1 *)skb->data; + ev_hdr = &ev_v1->hdr; + pull_len = sizeof(*ev_v1); + } + + if (skb->len < pull_len) + return -EPROTO; + + skb_pull(skb, pull_len); + arg->channel = ev_hdr->channel; + arg->buf_len = ev_hdr->buf_len; + arg->status = ev_hdr->status; + arg->snr = ev_hdr->snr; + arg->phy_mode = ev_hdr->phy_mode; + arg->rate = ev_hdr->rate; + + msdu_len = __le32_to_cpu(arg->buf_len); + if (skb->len < msdu_len) + return -EPROTO; + + /* the WMI buffer might've ended up being padded to 4 bytes due to HTC + * trailer with credit update. Trim the excess garbage. + */ + skb_trim(skb, msdu_len); + + return 0; +} + +int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_mgmt_rx_ev_arg arg = {}; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr; u32 rx_status; @@ -1161,24 +1476,20 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) u32 rate; u32 buf_len; u16 fc; - int pull_len; + int ret; - if (test_bit(ATH10K_FW_FEATURE_EXT_WMI_MGMT_RX, ar->fw_features)) { - ev_v2 = (struct wmi_mgmt_rx_event_v2 *)skb->data; - ev_hdr = &ev_v2->hdr.v1; - pull_len = sizeof(*ev_v2); - } else { - ev_v1 = (struct wmi_mgmt_rx_event_v1 *)skb->data; - ev_hdr = &ev_v1->hdr; - pull_len = sizeof(*ev_v1); + ret = ath10k_wmi_pull_mgmt_rx(ar, skb, &arg); + if (ret) { + ath10k_warn(ar, "failed to parse mgmt rx event: %d\n", ret); + return ret; } - channel = __le32_to_cpu(ev_hdr->channel); - buf_len = __le32_to_cpu(ev_hdr->buf_len); - rx_status = __le32_to_cpu(ev_hdr->status); - snr = __le32_to_cpu(ev_hdr->snr); - phy_mode = __le32_to_cpu(ev_hdr->phy_mode); - rate = __le32_to_cpu(ev_hdr->rate); + channel = __le32_to_cpu(arg.channel); + buf_len = __le32_to_cpu(arg.buf_len); + rx_status = __le32_to_cpu(arg.status); + snr = __le32_to_cpu(arg.snr); + phy_mode = __le32_to_cpu(arg.phy_mode); + rate = __le32_to_cpu(arg.rate); memset(status, 0, sizeof(*status)); @@ -1232,8 +1543,6 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) status->signal = snr + ATH10K_DEFAULT_NOISE_FLOOR; status->rate_idx = get_rate_idx(rate, status->band); - skb_pull(skb, pull_len); - hdr = (struct ieee80211_hdr *)skb->data; fc = le16_to_cpu(hdr->frame_control); @@ -1266,12 +1575,6 @@ static int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) status->freq, status->band, status->signal, status->rate_idx); - /* - * packets from HTC come aligned to 4byte boundaries - * because they can originally come in along with a trailer - */ - skb_trim(skb, buf_len); - ieee80211_rx(ar->hw, skb); return 0; } @@ -1295,21 +1598,44 @@ exit: return idx; } -static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb) +static int ath10k_wmi_op_pull_ch_info_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_ch_info_ev_arg *arg) { - struct wmi_chan_info_event *ev; + struct wmi_chan_info_event *ev = (void *)skb->data; + + if (skb->len < sizeof(*ev)) + return -EPROTO; + + skb_pull(skb, sizeof(*ev)); + arg->err_code = ev->err_code; + arg->freq = ev->freq; + arg->cmd_flags = ev->cmd_flags; + arg->noise_floor = ev->noise_floor; + arg->rx_clear_count = ev->rx_clear_count; + arg->cycle_count = ev->cycle_count; + + return 0; +} + +void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_ch_info_ev_arg arg = {}; struct survey_info *survey; u32 err_code, freq, cmd_flags, noise_floor, rx_clear_count, cycle_count; - int idx; + int idx, ret; - ev = (struct wmi_chan_info_event *)skb->data; + ret = ath10k_wmi_pull_ch_info(ar, skb, &arg); + if (ret) { + ath10k_warn(ar, "failed to parse chan info event: %d\n", ret); + return; + } - err_code = __le32_to_cpu(ev->err_code); - freq = __le32_to_cpu(ev->freq); - cmd_flags = __le32_to_cpu(ev->cmd_flags); - noise_floor = __le32_to_cpu(ev->noise_floor); - rx_clear_count = __le32_to_cpu(ev->rx_clear_count); - cycle_count = __le32_to_cpu(ev->cycle_count); + err_code = __le32_to_cpu(arg.err_code); + freq = __le32_to_cpu(arg.freq); + cmd_flags = __le32_to_cpu(arg.cmd_flags); + noise_floor = __le32_to_cpu(arg.noise_floor); + rx_clear_count = __le32_to_cpu(arg.rx_clear_count); + cycle_count = __le32_to_cpu(arg.cycle_count); ath10k_dbg(ar, ATH10K_DBG_WMI, "chan info err_code %d freq %d cmd_flags %d noise_floor %d rx_clear_count %d cycle_count %d\n", @@ -1344,11 +1670,11 @@ static void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb) rx_clear_count -= ar->survey_last_rx_clear_count; survey = &ar->survey[idx]; - survey->channel_time = WMI_CHAN_INFO_MSEC(cycle_count); - survey->channel_time_rx = WMI_CHAN_INFO_MSEC(rx_clear_count); + survey->time = WMI_CHAN_INFO_MSEC(cycle_count); + survey->time_rx = WMI_CHAN_INFO_MSEC(rx_clear_count); survey->noise = noise_floor; - survey->filled = SURVEY_INFO_CHANNEL_TIME | - SURVEY_INFO_CHANNEL_TIME_RX | + survey->filled = SURVEY_INFO_TIME | + SURVEY_INFO_TIME_RX | SURVEY_INFO_NOISE_DBM; } @@ -1359,12 +1685,12 @@ exit: spin_unlock_bh(&ar->data_lock); } -static void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb) +void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_ECHO_EVENTID\n"); } -static int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb) +int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event debug mesg len %d\n", skb->len); @@ -1374,12 +1700,9 @@ static int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb) return 0; } -static void ath10k_wmi_pull_pdev_stats(const struct wmi_pdev_stats *src, - struct ath10k_fw_stats_pdev *dst) +void ath10k_wmi_pull_pdev_stats_base(const struct wmi_pdev_stats_base *src, + struct ath10k_fw_stats_pdev *dst) { - const struct wal_dbg_tx_stats *tx = &src->wal.tx; - const struct wal_dbg_rx_stats *rx = &src->wal.rx; - dst->ch_noise_floor = __le32_to_cpu(src->chan_nf); dst->tx_frame_count = __le32_to_cpu(src->tx_frame_count); dst->rx_frame_count = __le32_to_cpu(src->rx_frame_count); @@ -1387,57 +1710,76 @@ static void ath10k_wmi_pull_pdev_stats(const struct wmi_pdev_stats *src, dst->cycle_count = __le32_to_cpu(src->cycle_count); dst->phy_err_count = __le32_to_cpu(src->phy_err_count); dst->chan_tx_power = __le32_to_cpu(src->chan_tx_pwr); +} - dst->comp_queued = __le32_to_cpu(tx->comp_queued); - dst->comp_delivered = __le32_to_cpu(tx->comp_delivered); - dst->msdu_enqued = __le32_to_cpu(tx->msdu_enqued); - dst->mpdu_enqued = __le32_to_cpu(tx->mpdu_enqued); - dst->wmm_drop = __le32_to_cpu(tx->wmm_drop); - dst->local_enqued = __le32_to_cpu(tx->local_enqued); - dst->local_freed = __le32_to_cpu(tx->local_freed); - dst->hw_queued = __le32_to_cpu(tx->hw_queued); - dst->hw_reaped = __le32_to_cpu(tx->hw_reaped); - dst->underrun = __le32_to_cpu(tx->underrun); - dst->tx_abort = __le32_to_cpu(tx->tx_abort); - dst->mpdus_requed = __le32_to_cpu(tx->mpdus_requed); - dst->tx_ko = __le32_to_cpu(tx->tx_ko); - dst->data_rc = __le32_to_cpu(tx->data_rc); - dst->self_triggers = __le32_to_cpu(tx->self_triggers); - dst->sw_retry_failure = __le32_to_cpu(tx->sw_retry_failure); - dst->illgl_rate_phy_err = __le32_to_cpu(tx->illgl_rate_phy_err); - dst->pdev_cont_xretry = __le32_to_cpu(tx->pdev_cont_xretry); - dst->pdev_tx_timeout = __le32_to_cpu(tx->pdev_tx_timeout); - dst->pdev_resets = __le32_to_cpu(tx->pdev_resets); - dst->phy_underrun = __le32_to_cpu(tx->phy_underrun); - dst->txop_ovf = __le32_to_cpu(tx->txop_ovf); - - dst->mid_ppdu_route_change = __le32_to_cpu(rx->mid_ppdu_route_change); - dst->status_rcvd = __le32_to_cpu(rx->status_rcvd); - dst->r0_frags = __le32_to_cpu(rx->r0_frags); - dst->r1_frags = __le32_to_cpu(rx->r1_frags); - dst->r2_frags = __le32_to_cpu(rx->r2_frags); - dst->r3_frags = __le32_to_cpu(rx->r3_frags); - dst->htt_msdus = __le32_to_cpu(rx->htt_msdus); - dst->htt_mpdus = __le32_to_cpu(rx->htt_mpdus); - dst->loc_msdus = __le32_to_cpu(rx->loc_msdus); - dst->loc_mpdus = __le32_to_cpu(rx->loc_mpdus); - dst->oversize_amsdu = __le32_to_cpu(rx->oversize_amsdu); - dst->phy_errs = __le32_to_cpu(rx->phy_errs); - dst->phy_err_drop = __le32_to_cpu(rx->phy_err_drop); - dst->mpdu_errs = __le32_to_cpu(rx->mpdu_errs); -} - -static void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src, - struct ath10k_fw_stats_peer *dst) +void ath10k_wmi_pull_pdev_stats_tx(const struct wmi_pdev_stats_tx *src, + struct ath10k_fw_stats_pdev *dst) +{ + dst->comp_queued = __le32_to_cpu(src->comp_queued); + dst->comp_delivered = __le32_to_cpu(src->comp_delivered); + dst->msdu_enqued = __le32_to_cpu(src->msdu_enqued); + dst->mpdu_enqued = __le32_to_cpu(src->mpdu_enqued); + dst->wmm_drop = __le32_to_cpu(src->wmm_drop); + dst->local_enqued = __le32_to_cpu(src->local_enqued); + dst->local_freed = __le32_to_cpu(src->local_freed); + dst->hw_queued = __le32_to_cpu(src->hw_queued); + dst->hw_reaped = __le32_to_cpu(src->hw_reaped); + dst->underrun = __le32_to_cpu(src->underrun); + dst->tx_abort = __le32_to_cpu(src->tx_abort); + dst->mpdus_requed = __le32_to_cpu(src->mpdus_requed); + dst->tx_ko = __le32_to_cpu(src->tx_ko); + dst->data_rc = __le32_to_cpu(src->data_rc); + dst->self_triggers = __le32_to_cpu(src->self_triggers); + dst->sw_retry_failure = __le32_to_cpu(src->sw_retry_failure); + dst->illgl_rate_phy_err = __le32_to_cpu(src->illgl_rate_phy_err); + dst->pdev_cont_xretry = __le32_to_cpu(src->pdev_cont_xretry); + dst->pdev_tx_timeout = __le32_to_cpu(src->pdev_tx_timeout); + dst->pdev_resets = __le32_to_cpu(src->pdev_resets); + dst->phy_underrun = __le32_to_cpu(src->phy_underrun); + dst->txop_ovf = __le32_to_cpu(src->txop_ovf); +} + +void ath10k_wmi_pull_pdev_stats_rx(const struct wmi_pdev_stats_rx *src, + struct ath10k_fw_stats_pdev *dst) +{ + dst->mid_ppdu_route_change = __le32_to_cpu(src->mid_ppdu_route_change); + dst->status_rcvd = __le32_to_cpu(src->status_rcvd); + dst->r0_frags = __le32_to_cpu(src->r0_frags); + dst->r1_frags = __le32_to_cpu(src->r1_frags); + dst->r2_frags = __le32_to_cpu(src->r2_frags); + dst->r3_frags = __le32_to_cpu(src->r3_frags); + dst->htt_msdus = __le32_to_cpu(src->htt_msdus); + dst->htt_mpdus = __le32_to_cpu(src->htt_mpdus); + dst->loc_msdus = __le32_to_cpu(src->loc_msdus); + dst->loc_mpdus = __le32_to_cpu(src->loc_mpdus); + dst->oversize_amsdu = __le32_to_cpu(src->oversize_amsdu); + dst->phy_errs = __le32_to_cpu(src->phy_errs); + dst->phy_err_drop = __le32_to_cpu(src->phy_err_drop); + dst->mpdu_errs = __le32_to_cpu(src->mpdu_errs); +} + +void ath10k_wmi_pull_pdev_stats_extra(const struct wmi_pdev_stats_extra *src, + struct ath10k_fw_stats_pdev *dst) +{ + dst->ack_rx_bad = __le32_to_cpu(src->ack_rx_bad); + dst->rts_bad = __le32_to_cpu(src->rts_bad); + dst->rts_good = __le32_to_cpu(src->rts_good); + dst->fcs_bad = __le32_to_cpu(src->fcs_bad); + dst->no_beacons = __le32_to_cpu(src->no_beacons); + dst->mib_int_count = __le32_to_cpu(src->mib_int_count); +} + +void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src, + struct ath10k_fw_stats_peer *dst) { ether_addr_copy(dst->peer_macaddr, src->peer_macaddr.addr); dst->peer_rssi = __le32_to_cpu(src->peer_rssi); dst->peer_tx_rate = __le32_to_cpu(src->peer_tx_rate); } -static int ath10k_wmi_main_pull_fw_stats(struct ath10k *ar, - struct sk_buff *skb, - struct ath10k_fw_stats *stats) +static int ath10k_wmi_main_op_pull_fw_stats(struct ath10k *ar, + struct sk_buff *skb, + struct ath10k_fw_stats *stats) { const struct wmi_stats_event *ev = (void *)skb->data; u32 num_pdev_stats, num_vdev_stats, num_peer_stats; @@ -1462,7 +1804,10 @@ static int ath10k_wmi_main_pull_fw_stats(struct ath10k *ar, if (!dst) continue; - ath10k_wmi_pull_pdev_stats(src, dst); + ath10k_wmi_pull_pdev_stats_base(&src->base, dst); + ath10k_wmi_pull_pdev_stats_tx(&src->tx, dst); + ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst); + list_add_tail(&dst->list, &stats->pdevs); } @@ -1487,9 +1832,9 @@ static int ath10k_wmi_main_pull_fw_stats(struct ath10k *ar, return 0; } -static int ath10k_wmi_10x_pull_fw_stats(struct ath10k *ar, - struct sk_buff *skb, - struct ath10k_fw_stats *stats) +static int ath10k_wmi_10x_op_pull_fw_stats(struct ath10k *ar, + struct sk_buff *skb, + struct ath10k_fw_stats *stats) { const struct wmi_stats_event *ev = (void *)skb->data; u32 num_pdev_stats, num_vdev_stats, num_peer_stats; @@ -1514,14 +1859,10 @@ static int ath10k_wmi_10x_pull_fw_stats(struct ath10k *ar, if (!dst) continue; - ath10k_wmi_pull_pdev_stats(&src->old, dst); - - dst->ack_rx_bad = __le32_to_cpu(src->ack_rx_bad); - dst->rts_bad = __le32_to_cpu(src->rts_bad); - dst->rts_good = __le32_to_cpu(src->rts_good); - dst->fcs_bad = __le32_to_cpu(src->fcs_bad); - dst->no_beacons = __le32_to_cpu(src->no_beacons); - dst->mib_int_count = __le32_to_cpu(src->mib_int_count); + ath10k_wmi_pull_pdev_stats_base(&src->base, dst); + ath10k_wmi_pull_pdev_stats_tx(&src->tx, dst); + ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst); + ath10k_wmi_pull_pdev_stats_extra(&src->extra, dst); list_add_tail(&dst->list, &stats->pdevs); } @@ -1550,61 +1891,250 @@ static int ath10k_wmi_10x_pull_fw_stats(struct ath10k *ar, return 0; } -int ath10k_wmi_pull_fw_stats(struct ath10k *ar, struct sk_buff *skb, - struct ath10k_fw_stats *stats) +static int ath10k_wmi_10_2_op_pull_fw_stats(struct ath10k *ar, + struct sk_buff *skb, + struct ath10k_fw_stats *stats) { - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) - return ath10k_wmi_10x_pull_fw_stats(ar, skb, stats); - else - return ath10k_wmi_main_pull_fw_stats(ar, skb, stats); + const struct wmi_10_2_stats_event *ev = (void *)skb->data; + u32 num_pdev_stats; + u32 num_pdev_ext_stats; + u32 num_vdev_stats; + u32 num_peer_stats; + int i; + + if (!skb_pull(skb, sizeof(*ev))) + return -EPROTO; + + num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats); + num_pdev_ext_stats = __le32_to_cpu(ev->num_pdev_ext_stats); + num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats); + num_peer_stats = __le32_to_cpu(ev->num_peer_stats); + + for (i = 0; i < num_pdev_stats; i++) { + const struct wmi_10_2_pdev_stats *src; + struct ath10k_fw_stats_pdev *dst; + + src = (void *)skb->data; + if (!skb_pull(skb, sizeof(*src))) + return -EPROTO; + + dst = kzalloc(sizeof(*dst), GFP_ATOMIC); + if (!dst) + continue; + + ath10k_wmi_pull_pdev_stats_base(&src->base, dst); + ath10k_wmi_pull_pdev_stats_tx(&src->tx, dst); + ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst); + ath10k_wmi_pull_pdev_stats_extra(&src->extra, dst); + /* FIXME: expose 10.2 specific values */ + + list_add_tail(&dst->list, &stats->pdevs); + } + + for (i = 0; i < num_pdev_ext_stats; i++) { + const struct wmi_10_2_pdev_ext_stats *src; + + src = (void *)skb->data; + if (!skb_pull(skb, sizeof(*src))) + return -EPROTO; + + /* FIXME: expose values to userspace + * + * Note: Even though this loop seems to do nothing it is + * required to parse following sub-structures properly. + */ + } + + /* fw doesn't implement vdev stats */ + + for (i = 0; i < num_peer_stats; i++) { + const struct wmi_10_2_peer_stats *src; + struct ath10k_fw_stats_peer *dst; + + src = (void *)skb->data; + if (!skb_pull(skb, sizeof(*src))) + return -EPROTO; + + dst = kzalloc(sizeof(*dst), GFP_ATOMIC); + if (!dst) + continue; + + ath10k_wmi_pull_peer_stats(&src->old, dst); + + dst->peer_rx_rate = __le32_to_cpu(src->peer_rx_rate); + /* FIXME: expose 10.2 specific values */ + + list_add_tail(&dst->list, &stats->peers); + } + + return 0; +} + +static int ath10k_wmi_10_2_4_op_pull_fw_stats(struct ath10k *ar, + struct sk_buff *skb, + struct ath10k_fw_stats *stats) +{ + const struct wmi_10_2_stats_event *ev = (void *)skb->data; + u32 num_pdev_stats; + u32 num_pdev_ext_stats; + u32 num_vdev_stats; + u32 num_peer_stats; + int i; + + if (!skb_pull(skb, sizeof(*ev))) + return -EPROTO; + + num_pdev_stats = __le32_to_cpu(ev->num_pdev_stats); + num_pdev_ext_stats = __le32_to_cpu(ev->num_pdev_ext_stats); + num_vdev_stats = __le32_to_cpu(ev->num_vdev_stats); + num_peer_stats = __le32_to_cpu(ev->num_peer_stats); + + for (i = 0; i < num_pdev_stats; i++) { + const struct wmi_10_2_pdev_stats *src; + struct ath10k_fw_stats_pdev *dst; + + src = (void *)skb->data; + if (!skb_pull(skb, sizeof(*src))) + return -EPROTO; + + dst = kzalloc(sizeof(*dst), GFP_ATOMIC); + if (!dst) + continue; + + ath10k_wmi_pull_pdev_stats_base(&src->base, dst); + ath10k_wmi_pull_pdev_stats_tx(&src->tx, dst); + ath10k_wmi_pull_pdev_stats_rx(&src->rx, dst); + ath10k_wmi_pull_pdev_stats_extra(&src->extra, dst); + /* FIXME: expose 10.2 specific values */ + + list_add_tail(&dst->list, &stats->pdevs); + } + + for (i = 0; i < num_pdev_ext_stats; i++) { + const struct wmi_10_2_pdev_ext_stats *src; + + src = (void *)skb->data; + if (!skb_pull(skb, sizeof(*src))) + return -EPROTO; + + /* FIXME: expose values to userspace + * + * Note: Even though this loop seems to do nothing it is + * required to parse following sub-structures properly. + */ + } + + /* fw doesn't implement vdev stats */ + + for (i = 0; i < num_peer_stats; i++) { + const struct wmi_10_2_4_peer_stats *src; + struct ath10k_fw_stats_peer *dst; + + src = (void *)skb->data; + if (!skb_pull(skb, sizeof(*src))) + return -EPROTO; + + dst = kzalloc(sizeof(*dst), GFP_ATOMIC); + if (!dst) + continue; + + ath10k_wmi_pull_peer_stats(&src->common.old, dst); + + dst->peer_rx_rate = __le32_to_cpu(src->common.peer_rx_rate); + /* FIXME: expose 10.2 specific values */ + + list_add_tail(&dst->list, &stats->peers); + } + + return 0; } -static void ath10k_wmi_event_update_stats(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_update_stats(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_UPDATE_STATS_EVENTID\n"); ath10k_debug_fw_stats_process(ar, skb); } -static void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, - struct sk_buff *skb) +static int +ath10k_wmi_op_pull_vdev_start_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_vdev_start_ev_arg *arg) +{ + struct wmi_vdev_start_response_event *ev = (void *)skb->data; + + if (skb->len < sizeof(*ev)) + return -EPROTO; + + skb_pull(skb, sizeof(*ev)); + arg->vdev_id = ev->vdev_id; + arg->req_id = ev->req_id; + arg->resp_type = ev->resp_type; + arg->status = ev->status; + + return 0; +} + +void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, struct sk_buff *skb) { - struct wmi_vdev_start_response_event *ev; + struct wmi_vdev_start_ev_arg arg = {}; + int ret; ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_START_RESP_EVENTID\n"); - ev = (struct wmi_vdev_start_response_event *)skb->data; + ret = ath10k_wmi_pull_vdev_start(ar, skb, &arg); + if (ret) { + ath10k_warn(ar, "failed to parse vdev start event: %d\n", ret); + return; + } - if (WARN_ON(__le32_to_cpu(ev->status))) + if (WARN_ON(__le32_to_cpu(arg.status))) return; complete(&ar->vdev_setup_done); } -static void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_STOPPED_EVENTID\n"); complete(&ar->vdev_setup_done); } -static void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar, - struct sk_buff *skb) +static int +ath10k_wmi_op_pull_peer_kick_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_peer_kick_ev_arg *arg) { - struct wmi_peer_sta_kickout_event *ev; + struct wmi_peer_sta_kickout_event *ev = (void *)skb->data; + + if (skb->len < sizeof(*ev)) + return -EPROTO; + + skb_pull(skb, sizeof(*ev)); + arg->mac_addr = ev->peer_macaddr.addr; + + return 0; +} + +void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_peer_kick_ev_arg arg = {}; struct ieee80211_sta *sta; + int ret; - ev = (struct wmi_peer_sta_kickout_event *)skb->data; + ret = ath10k_wmi_pull_peer_kick(ar, skb, &arg); + if (ret) { + ath10k_warn(ar, "failed to parse peer kickout event: %d\n", + ret); + return; + } ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event peer sta kickout %pM\n", - ev->peer_macaddr.addr); + arg.mac_addr); rcu_read_lock(); - sta = ieee80211_find_sta_by_ifaddr(ar->hw, ev->peer_macaddr.addr, NULL); + sta = ieee80211_find_sta_by_ifaddr(ar->hw, arg.mac_addr, NULL); if (!sta) { ath10k_warn(ar, "Spurious quick kickout for STA %pM\n", - ev->peer_macaddr.addr); + arg.mac_addr); goto exit; } @@ -1641,7 +2171,7 @@ exit: static void ath10k_wmi_update_tim(struct ath10k *ar, struct ath10k_vif *arvif, struct sk_buff *bcn, - struct wmi_bcn_info *bcn_info) + const struct wmi_tim_info *tim_info) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)bcn->data; struct ieee80211_tim_ie *tim; @@ -1652,14 +2182,14 @@ static void ath10k_wmi_update_tim(struct ath10k *ar, /* if next SWBA has no tim_changed the tim_bitmap is garbage. * we must copy the bitmap upon change and reuse it later */ - if (__le32_to_cpu(bcn_info->tim_info.tim_changed)) { + if (__le32_to_cpu(tim_info->tim_changed)) { int i; BUILD_BUG_ON(sizeof(arvif->u.ap.tim_bitmap) != - sizeof(bcn_info->tim_info.tim_bitmap)); + sizeof(tim_info->tim_bitmap)); for (i = 0; i < sizeof(arvif->u.ap.tim_bitmap); i++) { - t = bcn_info->tim_info.tim_bitmap[i / 4]; + t = tim_info->tim_bitmap[i / 4]; v = __le32_to_cpu(t); arvif->u.ap.tim_bitmap[i] = (v >> ((i % 4) * 8)) & 0xFF; } @@ -1711,13 +2241,13 @@ static void ath10k_wmi_update_tim(struct ath10k *ar, return; } - tim->bitmap_ctrl = !!__le32_to_cpu(bcn_info->tim_info.tim_mcast); + tim->bitmap_ctrl = !!__le32_to_cpu(tim_info->tim_mcast); memcpy(tim->virtual_map, arvif->u.ap.tim_bitmap, pvm_len); if (tim->dtim_count == 0) { ATH10K_SKB_CB(bcn)->bcn.dtim_zero = true; - if (__le32_to_cpu(bcn_info->tim_info.tim_mcast) == 1) + if (__le32_to_cpu(tim_info->tim_mcast) == 1) ATH10K_SKB_CB(bcn)->bcn.deliver_cab = true; } @@ -1727,7 +2257,7 @@ static void ath10k_wmi_update_tim(struct ath10k *ar, } static void ath10k_p2p_fill_noa_ie(u8 *data, u32 len, - struct wmi_p2p_noa_info *noa) + const struct wmi_p2p_noa_info *noa) { struct ieee80211_p2p_noa_attr *noa_attr; u8 ctwindow_oppps = noa->ctwindow_oppps; @@ -1769,7 +2299,7 @@ static void ath10k_p2p_fill_noa_ie(u8 *data, u32 len, *noa_attr_len = __cpu_to_le16(attr_len); } -static u32 ath10k_p2p_calc_noa_ie_len(struct wmi_p2p_noa_info *noa) +static u32 ath10k_p2p_calc_noa_ie_len(const struct wmi_p2p_noa_info *noa) { u32 len = 0; u8 noa_descriptors = noa->num_descriptors; @@ -1789,9 +2319,8 @@ static u32 ath10k_p2p_calc_noa_ie_len(struct wmi_p2p_noa_info *noa) static void ath10k_wmi_update_noa(struct ath10k *ar, struct ath10k_vif *arvif, struct sk_buff *bcn, - struct wmi_bcn_info *bcn_info) + const struct wmi_p2p_noa_info *noa) { - struct wmi_p2p_noa_info *noa = &bcn_info->p2p_noa_info; u8 *new_data, *old_data = arvif->u.ap.noa_data; u32 new_len; @@ -1832,22 +2361,59 @@ cleanup: kfree(old_data); } -static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) +static int ath10k_wmi_op_pull_swba_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_swba_ev_arg *arg) { - struct wmi_host_swba_event *ev; + struct wmi_host_swba_event *ev = (void *)skb->data; + u32 map; + size_t i; + + if (skb->len < sizeof(*ev)) + return -EPROTO; + + skb_pull(skb, sizeof(*ev)); + arg->vdev_map = ev->vdev_map; + + for (i = 0, map = __le32_to_cpu(ev->vdev_map); map; map >>= 1) { + if (!(map & BIT(0))) + continue; + + /* If this happens there were some changes in firmware and + * ath10k should update the max size of tim_info array. + */ + if (WARN_ON_ONCE(i == ARRAY_SIZE(arg->tim_info))) + break; + + arg->tim_info[i] = &ev->bcn_info[i].tim_info; + arg->noa_info[i] = &ev->bcn_info[i].p2p_noa_info; + i++; + } + + return 0; +} + +void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_swba_ev_arg arg = {}; u32 map; int i = -1; - struct wmi_bcn_info *bcn_info; + const struct wmi_tim_info *tim_info; + const struct wmi_p2p_noa_info *noa_info; struct ath10k_vif *arvif; struct sk_buff *bcn; dma_addr_t paddr; int ret, vdev_id = 0; - ev = (struct wmi_host_swba_event *)skb->data; - map = __le32_to_cpu(ev->vdev_map); + ret = ath10k_wmi_pull_swba(ar, skb, &arg); + if (ret) { + ath10k_warn(ar, "failed to parse swba event: %d\n", ret); + return; + } + + map = __le32_to_cpu(arg.vdev_map); ath10k_dbg(ar, ATH10K_DBG_MGMT, "mgmt swba vdev_map 0x%x\n", - ev->vdev_map); + map); for (; map; map >>= 1, vdev_id++) { if (!(map & 0x1)) @@ -1860,19 +2426,20 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) break; } - bcn_info = &ev->bcn_info[i]; + tim_info = arg.tim_info[i]; + noa_info = arg.noa_info[i]; ath10k_dbg(ar, ATH10K_DBG_MGMT, "mgmt event bcn_info %d tim_len %d mcast %d changed %d num_ps_pending %d bitmap 0x%08x%08x%08x%08x\n", i, - __le32_to_cpu(bcn_info->tim_info.tim_len), - __le32_to_cpu(bcn_info->tim_info.tim_mcast), - __le32_to_cpu(bcn_info->tim_info.tim_changed), - __le32_to_cpu(bcn_info->tim_info.tim_num_ps_pending), - __le32_to_cpu(bcn_info->tim_info.tim_bitmap[3]), - __le32_to_cpu(bcn_info->tim_info.tim_bitmap[2]), - __le32_to_cpu(bcn_info->tim_info.tim_bitmap[1]), - __le32_to_cpu(bcn_info->tim_info.tim_bitmap[0])); + __le32_to_cpu(tim_info->tim_len), + __le32_to_cpu(tim_info->tim_mcast), + __le32_to_cpu(tim_info->tim_changed), + __le32_to_cpu(tim_info->tim_num_ps_pending), + __le32_to_cpu(tim_info->tim_bitmap[3]), + __le32_to_cpu(tim_info->tim_bitmap[2]), + __le32_to_cpu(tim_info->tim_bitmap[1]), + __le32_to_cpu(tim_info->tim_bitmap[0])); arvif = ath10k_get_arvif(ar, vdev_id); if (arvif == NULL) { @@ -1899,15 +2466,25 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) } ath10k_tx_h_seq_no(arvif->vif, bcn); - ath10k_wmi_update_tim(ar, arvif, bcn, bcn_info); - ath10k_wmi_update_noa(ar, arvif, bcn, bcn_info); + ath10k_wmi_update_tim(ar, arvif, bcn, tim_info); + ath10k_wmi_update_noa(ar, arvif, bcn, noa_info); spin_lock_bh(&ar->data_lock); if (arvif->beacon) { - if (!arvif->beacon_sent) - ath10k_warn(ar, "SWBA overrun on vdev %d\n", + switch (arvif->beacon_state) { + case ATH10K_BEACON_SENT: + break; + case ATH10K_BEACON_SCHEDULED: + ath10k_warn(ar, "SWBA overrun on vdev %d, skipped old beacon\n", + arvif->vdev_id); + break; + case ATH10K_BEACON_SENDING: + ath10k_warn(ar, "SWBA overrun on vdev %d, skipped new beacon\n", arvif->vdev_id); + dev_kfree_skb(bcn); + goto skip; + } ath10k_mac_vif_beacon_free(arvif); } @@ -1935,19 +2512,19 @@ static void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb) } arvif->beacon = bcn; - arvif->beacon_sent = false; + arvif->beacon_state = ATH10K_BEACON_SCHEDULED; trace_ath10k_tx_hdr(ar, bcn->data, bcn->len); trace_ath10k_tx_payload(ar, bcn->data, bcn->len); - ath10k_wmi_tx_beacon_nowait(arvif); skip: spin_unlock_bh(&ar->data_lock); } + + ath10k_wmi_tx_beacons_nowait(ar); } -static void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TBTTOFFSET_UPDATE_EVENTID\n"); } @@ -2068,9 +2645,9 @@ static int ath10k_dfs_fft_report(struct ath10k *ar, return 0; } -static void ath10k_wmi_event_dfs(struct ath10k *ar, - const struct wmi_phyerr *phyerr, - u64 tsf) +void ath10k_wmi_event_dfs(struct ath10k *ar, + const struct wmi_phyerr *phyerr, + u64 tsf) { int buf_len, tlv_len, res, i = 0; const struct phyerr_tlv *tlv; @@ -2133,10 +2710,9 @@ static void ath10k_wmi_event_dfs(struct ath10k *ar, } } -static void -ath10k_wmi_event_spectral_scan(struct ath10k *ar, - const struct wmi_phyerr *phyerr, - u64 tsf) +void ath10k_wmi_event_spectral_scan(struct ath10k *ar, + const struct wmi_phyerr *phyerr, + u64 tsf) { int buf_len, tlv_len, res, i = 0; struct phyerr_tlv *tlv; @@ -2188,37 +2764,53 @@ ath10k_wmi_event_spectral_scan(struct ath10k *ar, } } -static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb) +static int ath10k_wmi_op_pull_phyerr_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_phyerr_ev_arg *arg) { - const struct wmi_phyerr_event *ev; + struct wmi_phyerr_event *ev = (void *)skb->data; + + if (skb->len < sizeof(*ev)) + return -EPROTO; + + arg->num_phyerrs = ev->num_phyerrs; + arg->tsf_l32 = ev->tsf_l32; + arg->tsf_u32 = ev->tsf_u32; + arg->buf_len = __cpu_to_le32(skb->len - sizeof(*ev)); + arg->phyerrs = ev->phyerrs; + + return 0; +} + +void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_phyerr_ev_arg arg = {}; const struct wmi_phyerr *phyerr; u32 count, i, buf_len, phy_err_code; u64 tsf; - int left_len = skb->len; + int left_len, ret; ATH10K_DFS_STAT_INC(ar, phy_errors); - /* Check if combined event available */ - if (left_len < sizeof(*ev)) { - ath10k_warn(ar, "wmi phyerr combined event wrong len\n"); + ret = ath10k_wmi_pull_phyerr(ar, skb, &arg); + if (ret) { + ath10k_warn(ar, "failed to parse phyerr event: %d\n", ret); return; } - left_len -= sizeof(*ev); + left_len = __le32_to_cpu(arg.buf_len); /* Check number of included events */ - ev = (const struct wmi_phyerr_event *)skb->data; - count = __le32_to_cpu(ev->num_phyerrs); + count = __le32_to_cpu(arg.num_phyerrs); - tsf = __le32_to_cpu(ev->tsf_u32); + tsf = __le32_to_cpu(arg.tsf_u32); tsf <<= 32; - tsf |= __le32_to_cpu(ev->tsf_l32); + tsf |= __le32_to_cpu(arg.tsf_l32); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi event phyerr count %d tsf64 0x%llX\n", count, tsf); - phyerr = ev->phyerrs; + phyerr = arg.phyerrs; for (i = 0; i < count; i++) { /* Check if we can read event header */ if (left_len < sizeof(*phyerr)) { @@ -2258,19 +2850,17 @@ static void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb) } } -static void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb) +void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_ROAM_EVENTID\n"); } -static void ath10k_wmi_event_profile_match(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_profile_match(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PROFILE_MATCH\n"); } -static void ath10k_wmi_event_debug_print(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_debug_print(struct ath10k *ar, struct sk_buff *skb) { char buf[101], c; int i; @@ -2303,103 +2893,90 @@ static void ath10k_wmi_event_debug_print(struct ath10k *ar, ath10k_dbg(ar, ATH10K_DBG_WMI_PRINT, "wmi print '%s'\n", buf); } -static void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb) +void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_QVIT_EVENTID\n"); } -static void ath10k_wmi_event_wlan_profile_data(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_wlan_profile_data(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_WLAN_PROFILE_DATA_EVENTID\n"); } -static void ath10k_wmi_event_rtt_measurement_report(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_rtt_measurement_report(struct ath10k *ar, + struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_RTT_MEASUREMENT_REPORT_EVENTID\n"); } -static void ath10k_wmi_event_tsf_measurement_report(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_tsf_measurement_report(struct ath10k *ar, + struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TSF_MEASUREMENT_REPORT_EVENTID\n"); } -static void ath10k_wmi_event_rtt_error_report(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_rtt_error_report(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_RTT_ERROR_REPORT_EVENTID\n"); } -static void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_WOW_WAKEUP_HOST_EVENTID\n"); } -static void ath10k_wmi_event_dcs_interference(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_dcs_interference(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_DCS_INTERFERENCE_EVENTID\n"); } -static void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_TPC_CONFIG_EVENTID\n"); } -static void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_PDEV_FTM_INTG_EVENTID\n"); } -static void ath10k_wmi_event_gtk_offload_status(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_gtk_offload_status(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_GTK_OFFLOAD_STATUS_EVENTID\n"); } -static void ath10k_wmi_event_gtk_rekey_fail(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_gtk_rekey_fail(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_GTK_REKEY_FAIL_EVENTID\n"); } -static void ath10k_wmi_event_delba_complete(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_delba_complete(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TX_DELBA_COMPLETE_EVENTID\n"); } -static void ath10k_wmi_event_addba_complete(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_addba_complete(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_TX_ADDBA_COMPLETE_EVENTID\n"); } -static void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar, + struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_INSTALL_KEY_COMPLETE_EVENTID\n"); } -static void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_INST_RSSI_STATS_EVENTID\n"); } -static void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_STANDBY_REQ_EVENTID\n"); } -static void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, struct sk_buff *skb) { ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI_VDEV_RESUME_REQ_EVENTID\n"); } @@ -2435,8 +3012,9 @@ static int ath10k_wmi_alloc_host_mem(struct ath10k *ar, u32 req_id, return 0; } -static int ath10k_wmi_main_pull_svc_rdy_ev(struct sk_buff *skb, - struct wmi_svc_rdy_ev_arg *arg) +static int +ath10k_wmi_main_op_pull_svc_rdy_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_svc_rdy_ev_arg *arg) { struct wmi_service_ready_event *ev; size_t i, n; @@ -2471,8 +3049,9 @@ static int ath10k_wmi_main_pull_svc_rdy_ev(struct sk_buff *skb, return 0; } -static int ath10k_wmi_10x_pull_svc_rdy_ev(struct sk_buff *skb, - struct wmi_svc_rdy_ev_arg *arg) +static int +ath10k_wmi_10x_op_pull_svc_rdy_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_svc_rdy_ev_arg *arg) { struct wmi_10x_service_ready_event *ev; int i, n; @@ -2506,30 +3085,22 @@ static int ath10k_wmi_10x_pull_svc_rdy_ev(struct sk_buff *skb, return 0; } -static void ath10k_wmi_event_service_ready(struct ath10k *ar, - struct sk_buff *skb) +void ath10k_wmi_event_service_ready(struct ath10k *ar, struct sk_buff *skb) { struct wmi_svc_rdy_ev_arg arg = {}; u32 num_units, req_id, unit_size, num_mem_reqs, num_unit_info, i; int ret; - memset(&ar->wmi.svc_map, 0, sizeof(ar->wmi.svc_map)); - - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - ret = ath10k_wmi_10x_pull_svc_rdy_ev(skb, &arg); - wmi_10x_svc_map(arg.service_map, ar->wmi.svc_map, - arg.service_map_len); - } else { - ret = ath10k_wmi_main_pull_svc_rdy_ev(skb, &arg); - wmi_main_svc_map(arg.service_map, ar->wmi.svc_map, - arg.service_map_len); - } - + ret = ath10k_wmi_pull_svc_rdy(ar, skb, &arg); if (ret) { ath10k_warn(ar, "failed to parse service ready: %d\n", ret); return; } + memset(&ar->wmi.svc_map, 0, sizeof(ar->wmi.svc_map)); + ath10k_wmi_map_svc(ar, arg.service_map, ar->wmi.svc_map, + arg.service_map_len); + ar->hw_min_tx_power = __le32_to_cpu(arg.min_tx_power); ar->hw_max_tx_power = __le32_to_cpu(arg.max_tx_power); ar->ht_cap_info = __le32_to_cpu(arg.ht_cap); @@ -2607,13 +3178,14 @@ static void ath10k_wmi_event_service_ready(struct ath10k *ar, } 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 phy_capab 0x%08x num_rf_chains 0x%08x eeprom_rd 0x%08x num_mem_reqs 0x%08x\n", + "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), __le32_to_cpu(arg.max_tx_power), __le32_to_cpu(arg.ht_cap), __le32_to_cpu(arg.vht_cap), __le32_to_cpu(arg.sw_ver0), __le32_to_cpu(arg.sw_ver1), + __le32_to_cpu(arg.fw_build), __le32_to_cpu(arg.phy_capab), __le32_to_cpu(arg.num_rf_chains), __le32_to_cpu(arg.eeprom_rd), @@ -2622,27 +3194,59 @@ static void ath10k_wmi_event_service_ready(struct ath10k *ar, complete(&ar->wmi.service_ready); } -static int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb) +static int ath10k_wmi_op_pull_rdy_ev(struct ath10k *ar, struct sk_buff *skb, + struct wmi_rdy_ev_arg *arg) { - struct wmi_ready_event *ev = (struct wmi_ready_event *)skb->data; + struct wmi_ready_event *ev = (void *)skb->data; - if (WARN_ON(skb->len < sizeof(*ev))) - return -EINVAL; + if (skb->len < sizeof(*ev)) + return -EPROTO; - ether_addr_copy(ar->mac_addr, ev->mac_addr.addr); + skb_pull(skb, sizeof(*ev)); + arg->sw_version = ev->sw_version; + arg->abi_version = ev->abi_version; + arg->status = ev->status; + arg->mac_addr = ev->mac_addr.addr; + + return 0; +} + +int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb) +{ + struct wmi_rdy_ev_arg arg = {}; + int ret; + + ret = ath10k_wmi_pull_rdy(ar, skb, &arg); + if (ret) { + ath10k_warn(ar, "failed to parse ready event: %d\n", ret); + return ret; + } ath10k_dbg(ar, ATH10K_DBG_WMI, - "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d skb->len %i ev-sz %zu\n", - __le32_to_cpu(ev->sw_version), - __le32_to_cpu(ev->abi_version), - ev->mac_addr.addr, - __le32_to_cpu(ev->status), skb->len, sizeof(*ev)); + "wmi event ready sw_version %u abi_version %u mac_addr %pM status %d\n", + __le32_to_cpu(arg.sw_version), + __le32_to_cpu(arg.abi_version), + arg.mac_addr, + __le32_to_cpu(arg.status)); + ether_addr_copy(ar->mac_addr, arg.mac_addr); complete(&ar->wmi.unified_ready); return 0; } -static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb) +static int ath10k_wmi_event_temperature(struct ath10k *ar, struct sk_buff *skb) +{ + const struct wmi_pdev_temperature_event *ev; + + ev = (struct wmi_pdev_temperature_event *)skb->data; + if (WARN_ON(skb->len < sizeof(*ev))) + return -EPROTO; + + ath10k_thermal_event_temperature(ar, __le32_to_cpu(ev->temperature)); + return 0; +} + +static void ath10k_wmi_op_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; enum wmi_event_id id; @@ -2758,7 +3362,7 @@ static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb) dev_kfree_skb(skb); } -static void ath10k_wmi_10x_process_rx(struct ath10k *ar, struct sk_buff *skb) +static void ath10k_wmi_10_1_op_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; enum wmi_10x_event_id id; @@ -2882,7 +3486,7 @@ out: dev_kfree_skb(skb); } -static void ath10k_wmi_10_2_process_rx(struct ath10k *ar, struct sk_buff *skb) +static void ath10k_wmi_10_2_op_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; enum wmi_10_2_event_id id; @@ -2981,6 +3585,9 @@ static void ath10k_wmi_10_2_process_rx(struct ath10k *ar, struct sk_buff *skb) case WMI_10_2_READY_EVENTID: ath10k_wmi_event_ready(ar, skb); break; + case WMI_10_2_PDEV_TEMPERATURE_EVENTID: + ath10k_wmi_event_temperature(ar, skb); + break; case WMI_10_2_RTT_KEEPALIVE_EVENTID: case WMI_10_2_GPIO_INPUT_EVENTID: case WMI_10_2_PEER_RATECODE_LIST_EVENTID: @@ -3001,14 +3608,11 @@ static void ath10k_wmi_10_2_process_rx(struct ath10k *ar, struct sk_buff *skb) static void ath10k_wmi_process_rx(struct ath10k *ar, struct sk_buff *skb) { - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features)) - ath10k_wmi_10_2_process_rx(ar, skb); - else - ath10k_wmi_10x_process_rx(ar, skb); - } else { - ath10k_wmi_main_process_rx(ar, skb); - } + int ret; + + ret = ath10k_wmi_rx(ar, skb); + if (ret) + ath10k_warn(ar, "failed to process wmi rx: %d\n", ret); } int ath10k_wmi_connect(struct ath10k *ar) @@ -3039,16 +3643,17 @@ int ath10k_wmi_connect(struct ath10k *ar) return 0; } -static int ath10k_wmi_main_pdev_set_regdomain(struct ath10k *ar, u16 rd, - u16 rd2g, u16 rd5g, u16 ctl2g, - u16 ctl5g) +static struct sk_buff * +ath10k_wmi_op_gen_pdev_set_rd(struct ath10k *ar, u16 rd, u16 rd2g, u16 rd5g, + u16 ctl2g, u16 ctl5g, + enum wmi_dfs_region dfs_reg) { struct wmi_pdev_set_regdomain_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_pdev_set_regdomain_cmd *)skb->data; cmd->reg_domain = __cpu_to_le32(rd); @@ -3060,22 +3665,20 @@ static int ath10k_wmi_main_pdev_set_regdomain(struct ath10k *ar, u16 rd, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x\n", rd, rd2g, rd5g, ctl2g, ctl5g); - - return ath10k_wmi_cmd_send(ar, skb, - ar->wmi.cmd->pdev_set_regdomain_cmdid); + return skb; } -static int ath10k_wmi_10x_pdev_set_regdomain(struct ath10k *ar, u16 rd, - u16 rd2g, u16 rd5g, - u16 ctl2g, u16 ctl5g, - enum wmi_dfs_region dfs_reg) +static struct sk_buff * +ath10k_wmi_10x_op_gen_pdev_set_rd(struct ath10k *ar, u16 rd, u16 rd2g, u16 + rd5g, u16 ctl2g, u16 ctl5g, + enum wmi_dfs_region dfs_reg) { struct wmi_pdev_set_regdomain_cmd_10x *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_pdev_set_regdomain_cmd_10x *)skb->data; cmd->reg_domain = __cpu_to_le32(rd); @@ -3088,50 +3691,39 @@ static int ath10k_wmi_10x_pdev_set_regdomain(struct ath10k *ar, u16 rd, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev regdomain rd %x rd2g %x rd5g %x ctl2g %x ctl5g %x dfs_region %x\n", rd, rd2g, rd5g, ctl2g, ctl5g, dfs_reg); - - return ath10k_wmi_cmd_send(ar, skb, - ar->wmi.cmd->pdev_set_regdomain_cmdid); -} - -int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g, - u16 rd5g, u16 ctl2g, u16 ctl5g, - enum wmi_dfs_region dfs_reg) -{ - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) - return ath10k_wmi_10x_pdev_set_regdomain(ar, rd, rd2g, rd5g, - ctl2g, ctl5g, dfs_reg); - else - return ath10k_wmi_main_pdev_set_regdomain(ar, rd, rd2g, rd5g, - ctl2g, ctl5g); + return skb; } -int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt) +static struct sk_buff * +ath10k_wmi_op_gen_pdev_suspend(struct ath10k *ar, u32 suspend_opt) { struct wmi_pdev_suspend_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_pdev_suspend_cmd *)skb->data; cmd->suspend_opt = __cpu_to_le32(suspend_opt); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_suspend_cmdid); + return skb; } -int ath10k_wmi_pdev_resume_target(struct ath10k *ar) +static struct sk_buff * +ath10k_wmi_op_gen_pdev_resume(struct ath10k *ar) { struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, 0); - if (skb == NULL) - return -ENOMEM; + if (!skb) + return ERR_PTR(-ENOMEM); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_resume_cmdid); + return skb; } -int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value) +static struct sk_buff * +ath10k_wmi_op_gen_pdev_set_param(struct ath10k *ar, u32 id, u32 value) { struct wmi_pdev_set_param_cmd *cmd; struct sk_buff *skb; @@ -3139,12 +3731,12 @@ int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value) if (id == WMI_PDEV_PARAM_UNSUPPORTED) { ath10k_warn(ar, "pdev param %d not supported by firmware\n", id); - return -EOPNOTSUPP; + return ERR_PTR(-EOPNOTSUPP); } skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_pdev_set_param_cmd *)skb->data; cmd->param_id = __cpu_to_le32(id); @@ -3152,11 +3744,11 @@ int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value) ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev set param %d value %d\n", id, value); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->pdev_set_param_cmdid); + return skb; } -static void ath10k_wmi_put_host_mem_chunks(struct ath10k *ar, - struct wmi_host_mem_chunks *chunks) +void ath10k_wmi_put_host_mem_chunks(struct ath10k *ar, + struct wmi_host_mem_chunks *chunks) { struct host_memory_chunk *chunk; int i; @@ -3177,7 +3769,7 @@ static void ath10k_wmi_put_host_mem_chunks(struct ath10k *ar, } } -static int ath10k_wmi_main_cmd_init(struct ath10k *ar) +static struct sk_buff *ath10k_wmi_op_gen_init(struct ath10k *ar) { struct wmi_init_cmd *cmd; struct sk_buff *buf; @@ -3240,7 +3832,7 @@ static int ath10k_wmi_main_cmd_init(struct ath10k *ar) buf = ath10k_wmi_alloc_skb(ar, len); if (!buf) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_init_cmd *)buf->data; @@ -3248,10 +3840,10 @@ static int ath10k_wmi_main_cmd_init(struct ath10k *ar) ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init\n"); - return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid); + return buf; } -static int ath10k_wmi_10x_cmd_init(struct ath10k *ar) +static struct sk_buff *ath10k_wmi_10_1_op_gen_init(struct ath10k *ar) { struct wmi_init_cmd_10x *cmd; struct sk_buff *buf; @@ -3306,7 +3898,7 @@ static int ath10k_wmi_10x_cmd_init(struct ath10k *ar) buf = ath10k_wmi_alloc_skb(ar, len); if (!buf) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_init_cmd_10x *)buf->data; @@ -3314,15 +3906,15 @@ static int ath10k_wmi_10x_cmd_init(struct ath10k *ar) ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init 10x\n"); - return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid); + return buf; } -static int ath10k_wmi_10_2_cmd_init(struct ath10k *ar) +static struct sk_buff *ath10k_wmi_10_2_op_gen_init(struct ath10k *ar) { struct wmi_init_cmd_10_2 *cmd; struct sk_buff *buf; struct wmi_resource_config_10x config = {}; - u32 len, val; + u32 len, val, features; config.num_vdevs = __cpu_to_le32(TARGET_10X_NUM_VDEVS); config.num_peers = __cpu_to_le32(TARGET_10X_NUM_PEERS); @@ -3356,7 +3948,7 @@ static int ath10k_wmi_10_2_cmd_init(struct ath10k *ar) config.mcast2ucast_mode = __cpu_to_le32(TARGET_10X_MCAST2UCAST_MODE); config.tx_dbg_log_size = __cpu_to_le32(TARGET_10X_TX_DBG_LOG_SIZE); config.num_wds_entries = __cpu_to_le32(TARGET_10X_NUM_WDS_ENTRIES); - config.dma_burst_size = __cpu_to_le32(TARGET_10X_DMA_BURST_SIZE); + config.dma_burst_size = __cpu_to_le32(TARGET_10_2_DMA_BURST_SIZE); config.mac_aggr_delim = __cpu_to_le32(TARGET_10X_MAC_AGGR_DELIM); val = TARGET_10X_RX_SKIP_DEFRAG_TIMEOUT_DUP_DETECTION_CHECK; @@ -3372,34 +3964,21 @@ static int ath10k_wmi_10_2_cmd_init(struct ath10k *ar) buf = ath10k_wmi_alloc_skb(ar, len); if (!buf) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_init_cmd_10_2 *)buf->data; + features = WMI_10_2_RX_BATCH_MODE; + cmd->resource_config.feature_mask = __cpu_to_le32(features); + memcpy(&cmd->resource_config.common, &config, sizeof(config)); ath10k_wmi_put_host_mem_chunks(ar, &cmd->mem_chunks); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi init 10.2\n"); - return ath10k_wmi_cmd_send(ar, buf, ar->wmi.cmd->init_cmdid); + return buf; } -int ath10k_wmi_cmd_init(struct ath10k *ar) -{ - int ret; - - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features)) - ret = ath10k_wmi_10_2_cmd_init(ar); - else - ret = ath10k_wmi_10x_cmd_init(ar); - } else { - ret = ath10k_wmi_main_cmd_init(ar); - } - - return ret; -} - -static int ath10k_wmi_start_scan_verify(const struct wmi_start_scan_arg *arg) +int ath10k_wmi_start_scan_verify(const struct wmi_start_scan_arg *arg) { if (arg->ie_len && !arg->ie) return -EINVAL; @@ -3450,9 +4029,8 @@ ath10k_wmi_start_scan_tlvs_len(const struct wmi_start_scan_arg *arg) return len; } -static void -ath10k_wmi_put_start_scan_common(struct wmi_start_scan_common *cmn, - const struct wmi_start_scan_arg *arg) +void ath10k_wmi_put_start_scan_common(struct wmi_start_scan_common *cmn, + const struct wmi_start_scan_arg *arg) { u32 scan_id; u32 scan_req_id; @@ -3546,46 +4124,60 @@ ath10k_wmi_put_start_scan_tlvs(struct wmi_start_scan_tlvs *tlvs, } } -int ath10k_wmi_start_scan(struct ath10k *ar, - const struct wmi_start_scan_arg *arg) +static struct sk_buff * +ath10k_wmi_op_gen_start_scan(struct ath10k *ar, + const struct wmi_start_scan_arg *arg) { + struct wmi_start_scan_cmd *cmd; struct sk_buff *skb; size_t len; int ret; ret = ath10k_wmi_start_scan_verify(arg); if (ret) - return ret; - - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) - len = sizeof(struct wmi_10x_start_scan_cmd) + - ath10k_wmi_start_scan_tlvs_len(arg); - else - len = sizeof(struct wmi_start_scan_cmd) + - ath10k_wmi_start_scan_tlvs_len(arg); + return ERR_PTR(ret); + len = sizeof(*cmd) + ath10k_wmi_start_scan_tlvs_len(arg); skb = ath10k_wmi_alloc_skb(ar, len); if (!skb) - return -ENOMEM; - - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - struct wmi_10x_start_scan_cmd *cmd; + return ERR_PTR(-ENOMEM); - cmd = (struct wmi_10x_start_scan_cmd *)skb->data; - ath10k_wmi_put_start_scan_common(&cmd->common, arg); - ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg); - } else { - struct wmi_start_scan_cmd *cmd; + cmd = (struct wmi_start_scan_cmd *)skb->data; - cmd = (struct wmi_start_scan_cmd *)skb->data; - cmd->burst_duration_ms = __cpu_to_le32(0); + ath10k_wmi_put_start_scan_common(&cmd->common, arg); + ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg); - ath10k_wmi_put_start_scan_common(&cmd->common, arg); - ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg); - } + cmd->burst_duration_ms = __cpu_to_le32(0); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi start scan\n"); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->start_scan_cmdid); + return skb; +} + +static struct sk_buff * +ath10k_wmi_10x_op_gen_start_scan(struct ath10k *ar, + const struct wmi_start_scan_arg *arg) +{ + struct wmi_10x_start_scan_cmd *cmd; + struct sk_buff *skb; + size_t len; + int ret; + + ret = ath10k_wmi_start_scan_verify(arg); + if (ret) + return ERR_PTR(ret); + + len = sizeof(*cmd) + ath10k_wmi_start_scan_tlvs_len(arg); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + cmd = (struct wmi_10x_start_scan_cmd *)skb->data; + + ath10k_wmi_put_start_scan_common(&cmd->common, arg); + ath10k_wmi_put_start_scan_tlvs(&cmd->tlvs, arg); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi 10x start scan\n"); + return skb; } void ath10k_wmi_start_scan_init(struct ath10k *ar, @@ -3614,7 +4206,9 @@ void ath10k_wmi_start_scan_init(struct ath10k *ar, arg->bssids[0].bssid = "\xFF\xFF\xFF\xFF\xFF\xFF"; } -int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg) +static struct sk_buff * +ath10k_wmi_op_gen_stop_scan(struct ath10k *ar, + const struct wmi_stop_scan_arg *arg) { struct wmi_stop_scan_cmd *cmd; struct sk_buff *skb; @@ -3622,13 +4216,13 @@ int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg) u32 req_id; if (arg->req_id > 0xFFF) - return -EINVAL; + return ERR_PTR(-EINVAL); if (arg->req_type == WMI_SCAN_STOP_ONE && arg->u.scan_id > 0xFFF) - return -EINVAL; + return ERR_PTR(-EINVAL); skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); scan_id = arg->u.scan_id; scan_id |= WMI_HOST_SCAN_REQ_ID_PREFIX; @@ -3645,20 +4239,21 @@ int ath10k_wmi_stop_scan(struct ath10k *ar, const struct wmi_stop_scan_arg *arg) ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi stop scan reqid %d req_type %d vdev/scan_id %d\n", arg->req_id, arg->req_type, arg->u.scan_id); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->stop_scan_cmdid); + return skb; } -int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id, - enum wmi_vdev_type type, - enum wmi_vdev_subtype subtype, - const u8 macaddr[ETH_ALEN]) +static struct sk_buff * +ath10k_wmi_op_gen_vdev_create(struct ath10k *ar, u32 vdev_id, + enum wmi_vdev_type type, + enum wmi_vdev_subtype subtype, + const u8 macaddr[ETH_ALEN]) { struct wmi_vdev_create_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_vdev_create_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); @@ -3669,58 +4264,52 @@ int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI vdev create: id %d type %d subtype %d macaddr %pM\n", vdev_id, type, subtype, macaddr); - - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_create_cmdid); + return skb; } -int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id) +static struct sk_buff * +ath10k_wmi_op_gen_vdev_delete(struct ath10k *ar, u32 vdev_id) { struct wmi_vdev_delete_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_vdev_delete_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); ath10k_dbg(ar, ATH10K_DBG_WMI, "WMI vdev delete id %d\n", vdev_id); - - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_delete_cmdid); + return skb; } -static int -ath10k_wmi_vdev_start_restart(struct ath10k *ar, - const struct wmi_vdev_start_request_arg *arg, - u32 cmd_id) +static struct sk_buff * +ath10k_wmi_op_gen_vdev_start(struct ath10k *ar, + const struct wmi_vdev_start_request_arg *arg, + bool restart) { struct wmi_vdev_start_request_cmd *cmd; struct sk_buff *skb; const char *cmdname; u32 flags = 0; - if (cmd_id != ar->wmi.cmd->vdev_start_request_cmdid && - cmd_id != ar->wmi.cmd->vdev_restart_request_cmdid) - return -EINVAL; if (WARN_ON(arg->ssid && arg->ssid_len == 0)) - return -EINVAL; + return ERR_PTR(-EINVAL); if (WARN_ON(arg->hidden_ssid && !arg->ssid)) - return -EINVAL; + return ERR_PTR(-EINVAL); if (WARN_ON(arg->ssid_len > sizeof(cmd->ssid.ssid))) - return -EINVAL; + return ERR_PTR(-EINVAL); - if (cmd_id == ar->wmi.cmd->vdev_start_request_cmdid) - cmdname = "start"; - else if (cmd_id == ar->wmi.cmd->vdev_restart_request_cmdid) + if (restart) cmdname = "restart"; else - return -EINVAL; /* should not happen, we already check cmd_id */ + cmdname = "start"; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); if (arg->hidden_ssid) flags |= WMI_VDEV_START_HIDDEN_SSID; @@ -3749,50 +4338,36 @@ ath10k_wmi_vdev_start_restart(struct ath10k *ar, flags, arg->channel.freq, arg->channel.mode, cmd->chan.flags, arg->channel.max_power); - return ath10k_wmi_cmd_send(ar, skb, cmd_id); -} - -int ath10k_wmi_vdev_start(struct ath10k *ar, - const struct wmi_vdev_start_request_arg *arg) -{ - u32 cmd_id = ar->wmi.cmd->vdev_start_request_cmdid; - - return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id); -} - -int ath10k_wmi_vdev_restart(struct ath10k *ar, - const struct wmi_vdev_start_request_arg *arg) -{ - u32 cmd_id = ar->wmi.cmd->vdev_restart_request_cmdid; - - return ath10k_wmi_vdev_start_restart(ar, arg, cmd_id); + return skb; } -int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id) +static struct sk_buff * +ath10k_wmi_op_gen_vdev_stop(struct ath10k *ar, u32 vdev_id) { struct wmi_vdev_stop_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_vdev_stop_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi vdev stop id 0x%x\n", vdev_id); - - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_stop_cmdid); + return skb; } -int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid) +static struct sk_buff * +ath10k_wmi_op_gen_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, + const u8 *bssid) { struct wmi_vdev_up_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_vdev_up_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); @@ -3802,30 +4377,30 @@ int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, const u8 *bssid) ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt vdev up id 0x%x assoc id %d bssid %pM\n", vdev_id, aid, bssid); - - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_up_cmdid); + return skb; } -int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id) +static struct sk_buff * +ath10k_wmi_op_gen_vdev_down(struct ath10k *ar, u32 vdev_id) { struct wmi_vdev_down_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_vdev_down_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt vdev down id 0x%x\n", vdev_id); - - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_down_cmdid); + return skb; } -int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, - u32 param_id, u32 param_value) +static struct sk_buff * +ath10k_wmi_op_gen_vdev_set_param(struct ath10k *ar, u32 vdev_id, + u32 param_id, u32 param_value) { struct wmi_vdev_set_param_cmd *cmd; struct sk_buff *skb; @@ -3834,12 +4409,12 @@ int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ar, ATH10K_DBG_WMI, "vdev param %d not supported by firmware\n", param_id); - return -EOPNOTSUPP; + return ERR_PTR(-EOPNOTSUPP); } skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_vdev_set_param_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); @@ -3849,24 +4424,24 @@ int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi vdev id 0x%x set param %d value %d\n", vdev_id, param_id, param_value); - - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->vdev_set_param_cmdid); + return skb; } -int ath10k_wmi_vdev_install_key(struct ath10k *ar, - const struct wmi_vdev_install_key_arg *arg) +static struct sk_buff * +ath10k_wmi_op_gen_vdev_install_key(struct ath10k *ar, + const struct wmi_vdev_install_key_arg *arg) { struct wmi_vdev_install_key_cmd *cmd; struct sk_buff *skb; if (arg->key_cipher == WMI_CIPHER_NONE && arg->key_data != NULL) - return -EINVAL; + return ERR_PTR(-EINVAL); if (arg->key_cipher != WMI_CIPHER_NONE && arg->key_data == NULL) - return -EINVAL; + return ERR_PTR(-EINVAL); skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd) + arg->key_len); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_vdev_install_key_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(arg->vdev_id); @@ -3885,20 +4460,19 @@ int ath10k_wmi_vdev_install_key(struct ath10k *ar, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi vdev install key idx %d cipher %d len %d\n", arg->key_idx, arg->key_cipher, arg->key_len); - return ath10k_wmi_cmd_send(ar, skb, - ar->wmi.cmd->vdev_install_key_cmdid); + return skb; } -int ath10k_wmi_vdev_spectral_conf(struct ath10k *ar, - const struct wmi_vdev_spectral_conf_arg *arg) +static struct sk_buff * +ath10k_wmi_op_gen_vdev_spectral_conf(struct ath10k *ar, + const struct wmi_vdev_spectral_conf_arg *arg) { struct wmi_vdev_spectral_conf_cmd *cmd; struct sk_buff *skb; - u32 cmdid; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_vdev_spectral_conf_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(arg->vdev_id); @@ -3921,39 +4495,38 @@ int ath10k_wmi_vdev_spectral_conf(struct ath10k *ar, cmd->scan_dbm_adj = __cpu_to_le32(arg->scan_dbm_adj); cmd->scan_chn_mask = __cpu_to_le32(arg->scan_chn_mask); - cmdid = ar->wmi.cmd->vdev_spectral_scan_configure_cmdid; - return ath10k_wmi_cmd_send(ar, skb, cmdid); + return skb; } -int ath10k_wmi_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id, u32 trigger, - u32 enable) +static struct sk_buff * +ath10k_wmi_op_gen_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id, + u32 trigger, u32 enable) { struct wmi_vdev_spectral_enable_cmd *cmd; struct sk_buff *skb; - u32 cmdid; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_vdev_spectral_enable_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); cmd->trigger_cmd = __cpu_to_le32(trigger); cmd->enable_cmd = __cpu_to_le32(enable); - cmdid = ar->wmi.cmd->vdev_spectral_scan_enable_cmdid; - return ath10k_wmi_cmd_send(ar, skb, cmdid); + return skb; } -int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, - const u8 peer_addr[ETH_ALEN]) +static struct sk_buff * +ath10k_wmi_op_gen_peer_create(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]) { struct wmi_peer_create_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_peer_create_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); @@ -3962,18 +4535,19 @@ int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi peer create vdev_id %d peer_addr %pM\n", vdev_id, peer_addr); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_create_cmdid); + return skb; } -int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, - const u8 peer_addr[ETH_ALEN]) +static struct sk_buff * +ath10k_wmi_op_gen_peer_delete(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN]) { struct wmi_peer_delete_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_peer_delete_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); @@ -3982,18 +4556,19 @@ int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi peer delete vdev_id %d peer_addr %pM\n", vdev_id, peer_addr); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_delete_cmdid); + return skb; } -int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id, - const u8 peer_addr[ETH_ALEN], u32 tid_bitmap) +static struct sk_buff * +ath10k_wmi_op_gen_peer_flush(struct ath10k *ar, u32 vdev_id, + const u8 peer_addr[ETH_ALEN], u32 tid_bitmap) { struct wmi_peer_flush_tids_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_peer_flush_tids_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); @@ -4003,19 +4578,21 @@ int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi peer flush vdev_id %d peer_addr %pM tids %08x\n", vdev_id, peer_addr, tid_bitmap); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_flush_tids_cmdid); + return skb; } -int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, - const u8 *peer_addr, enum wmi_peer_param param_id, - u32 param_value) +static struct sk_buff * +ath10k_wmi_op_gen_peer_set_param(struct ath10k *ar, u32 vdev_id, + const u8 *peer_addr, + enum wmi_peer_param param_id, + u32 param_value) { struct wmi_peer_set_param_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_peer_set_param_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); @@ -4026,19 +4603,19 @@ int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi vdev %d peer 0x%pM set param %d value %d\n", vdev_id, peer_addr, param_id, param_value); - - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_set_param_cmdid); + return skb; } -int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id, - enum wmi_sta_ps_mode psmode) +static struct sk_buff * +ath10k_wmi_op_gen_set_psmode(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_ps_mode psmode) { struct wmi_sta_powersave_mode_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_sta_powersave_mode_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); @@ -4047,21 +4624,20 @@ int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi set powersave id 0x%x mode %d\n", vdev_id, psmode); - - return ath10k_wmi_cmd_send(ar, skb, - ar->wmi.cmd->sta_powersave_mode_cmdid); + return skb; } -int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id, - enum wmi_sta_powersave_param param_id, - u32 value) +static struct sk_buff * +ath10k_wmi_op_gen_set_sta_ps(struct ath10k *ar, u32 vdev_id, + enum wmi_sta_powersave_param param_id, + u32 value) { struct wmi_sta_powersave_param_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_sta_powersave_param_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); @@ -4071,22 +4647,22 @@ int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi sta ps param vdev_id 0x%x param %d value %d\n", vdev_id, param_id, value); - return ath10k_wmi_cmd_send(ar, skb, - ar->wmi.cmd->sta_powersave_param_cmdid); + return skb; } -int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, - enum wmi_ap_ps_peer_param param_id, u32 value) +static struct sk_buff * +ath10k_wmi_op_gen_set_ap_ps(struct ath10k *ar, u32 vdev_id, const u8 *mac, + enum wmi_ap_ps_peer_param param_id, u32 value) { struct wmi_ap_ps_peer_cmd *cmd; struct sk_buff *skb; if (!mac) - return -EINVAL; + return ERR_PTR(-EINVAL); skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_ap_ps_peer_cmd *)skb->data; cmd->vdev_id = __cpu_to_le32(vdev_id); @@ -4097,13 +4673,12 @@ int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi ap ps param vdev_id 0x%X param %d value %d mac_addr %pM\n", vdev_id, param_id, value, mac); - - return ath10k_wmi_cmd_send(ar, skb, - ar->wmi.cmd->ap_ps_peer_param_cmdid); + return skb; } -int ath10k_wmi_scan_chan_list(struct ath10k *ar, - const struct wmi_scan_chan_list_arg *arg) +static struct sk_buff * +ath10k_wmi_op_gen_scan_chan_list(struct ath10k *ar, + const struct wmi_scan_chan_list_arg *arg) { struct wmi_scan_chan_list_cmd *cmd; struct sk_buff *skb; @@ -4116,7 +4691,7 @@ int ath10k_wmi_scan_chan_list(struct ath10k *ar, skb = ath10k_wmi_alloc_skb(ar, len); if (!skb) - return -EINVAL; + return ERR_PTR(-EINVAL); cmd = (struct wmi_scan_chan_list_cmd *)skb->data; cmd->num_scan_chans = __cpu_to_le32(arg->n_channels); @@ -4128,7 +4703,7 @@ int ath10k_wmi_scan_chan_list(struct ath10k *ar, ath10k_wmi_put_wmi_channel(ci, ch); } - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->scan_chan_list_cmdid); + return skb; } static void @@ -4209,12 +4784,9 @@ ath10k_wmi_peer_assoc_fill_10_2(struct ath10k *ar, void *buf, cmd->info0 = __cpu_to_le32(info0); } -int ath10k_wmi_peer_assoc(struct ath10k *ar, - const struct wmi_peer_assoc_complete_arg *arg) +static int +ath10k_wmi_peer_assoc_check_arg(const struct wmi_peer_assoc_complete_arg *arg) { - struct sk_buff *skb; - int len; - if (arg->peer_mpdu_density > 16) return -EINVAL; if (arg->peer_legacy_rates.num_rates > MAX_SUPPORTED_RATES) @@ -4222,79 +4794,135 @@ int ath10k_wmi_peer_assoc(struct ath10k *ar, if (arg->peer_ht_rates.num_rates > MAX_SUPPORTED_RATES) return -EINVAL; - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features)) - len = sizeof(struct wmi_10_2_peer_assoc_complete_cmd); - else - len = sizeof(struct wmi_10_1_peer_assoc_complete_cmd); - } else { - len = sizeof(struct wmi_main_peer_assoc_complete_cmd); - } + return 0; +} + +static struct sk_buff * +ath10k_wmi_op_gen_peer_assoc(struct ath10k *ar, + const struct wmi_peer_assoc_complete_arg *arg) +{ + size_t len = sizeof(struct wmi_main_peer_assoc_complete_cmd); + struct sk_buff *skb; + int ret; + + ret = ath10k_wmi_peer_assoc_check_arg(arg); + if (ret) + return ERR_PTR(ret); skb = ath10k_wmi_alloc_skb(ar, len); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features)) - ath10k_wmi_peer_assoc_fill_10_2(ar, skb->data, arg); - else - ath10k_wmi_peer_assoc_fill_10_1(ar, skb->data, arg); - } else { - ath10k_wmi_peer_assoc_fill_main(ar, skb->data, arg); - } + ath10k_wmi_peer_assoc_fill_main(ar, skb->data, arg); + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi peer assoc vdev %d addr %pM (%s)\n", + arg->vdev_id, arg->addr, + arg->peer_reassoc ? "reassociate" : "new"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_10_1_op_gen_peer_assoc(struct ath10k *ar, + const struct wmi_peer_assoc_complete_arg *arg) +{ + size_t len = sizeof(struct wmi_10_1_peer_assoc_complete_cmd); + struct sk_buff *skb; + int ret; + + ret = ath10k_wmi_peer_assoc_check_arg(arg); + if (ret) + return ERR_PTR(ret); + + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ath10k_wmi_peer_assoc_fill_10_1(ar, skb->data, arg); + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi peer assoc vdev %d addr %pM (%s)\n", + arg->vdev_id, arg->addr, + arg->peer_reassoc ? "reassociate" : "new"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_10_2_op_gen_peer_assoc(struct ath10k *ar, + const struct wmi_peer_assoc_complete_arg *arg) +{ + size_t len = sizeof(struct wmi_10_2_peer_assoc_complete_cmd); + struct sk_buff *skb; + int ret; + + ret = ath10k_wmi_peer_assoc_check_arg(arg); + if (ret) + return ERR_PTR(ret); + + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + ath10k_wmi_peer_assoc_fill_10_2(ar, skb->data, arg); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi peer assoc vdev %d addr %pM (%s)\n", arg->vdev_id, arg->addr, arg->peer_reassoc ? "reassociate" : "new"); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->peer_assoc_cmdid); + return skb; +} + +static struct sk_buff * +ath10k_wmi_10_2_op_gen_pdev_get_temperature(struct ath10k *ar) +{ + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, 0); + if (!skb) + return ERR_PTR(-ENOMEM); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev get temperature\n"); + return skb; } /* This function assumes the beacon is already DMA mapped */ -int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif) +static struct sk_buff * +ath10k_wmi_op_gen_beacon_dma(struct ath10k *ar, u32 vdev_id, const void *bcn, + size_t bcn_len, u32 bcn_paddr, bool dtim_zero, + bool deliver_cab) { struct wmi_bcn_tx_ref_cmd *cmd; struct sk_buff *skb; - struct sk_buff *beacon = arvif->beacon; - struct ath10k *ar = arvif->ar; struct ieee80211_hdr *hdr; - int ret; u16 fc; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); - hdr = (struct ieee80211_hdr *)beacon->data; + hdr = (struct ieee80211_hdr *)bcn; fc = le16_to_cpu(hdr->frame_control); cmd = (struct wmi_bcn_tx_ref_cmd *)skb->data; - cmd->vdev_id = __cpu_to_le32(arvif->vdev_id); - cmd->data_len = __cpu_to_le32(beacon->len); - cmd->data_ptr = __cpu_to_le32(ATH10K_SKB_CB(beacon)->paddr); + cmd->vdev_id = __cpu_to_le32(vdev_id); + cmd->data_len = __cpu_to_le32(bcn_len); + cmd->data_ptr = __cpu_to_le32(bcn_paddr); cmd->msdu_id = 0; cmd->frame_control = __cpu_to_le32(fc); cmd->flags = 0; cmd->antenna_mask = __cpu_to_le32(WMI_BCN_TX_REF_DEF_ANTENNA); - if (ATH10K_SKB_CB(beacon)->bcn.dtim_zero) + if (dtim_zero) cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DTIM_ZERO); - if (ATH10K_SKB_CB(beacon)->bcn.deliver_cab) + if (deliver_cab) cmd->flags |= __cpu_to_le32(WMI_BCN_TX_REF_FLAG_DELIVER_CAB); - ret = ath10k_wmi_cmd_send_nowait(ar, skb, - ar->wmi.cmd->pdev_send_bcn_cmdid); - - if (ret) - dev_kfree_skb(skb); - - return ret; + return skb; } -static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params, - const struct wmi_wmm_params_arg *arg) +void ath10k_wmi_set_wmm_param(struct wmi_wmm_params *params, + const struct wmi_wmm_params_arg *arg) { params->cwmin = __cpu_to_le32(arg->cwmin); params->cwmax = __cpu_to_le32(arg->cwmax); @@ -4304,52 +4932,54 @@ static void ath10k_wmi_pdev_set_wmm_param(struct wmi_wmm_params *params, params->no_ack = __cpu_to_le32(arg->no_ack); } -int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, - const struct wmi_pdev_set_wmm_params_arg *arg) +static struct sk_buff * +ath10k_wmi_op_gen_pdev_set_wmm(struct ath10k *ar, + const struct wmi_wmm_params_all_arg *arg) { struct wmi_pdev_set_wmm_params *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_pdev_set_wmm_params *)skb->data; - ath10k_wmi_pdev_set_wmm_param(&cmd->ac_be, &arg->ac_be); - ath10k_wmi_pdev_set_wmm_param(&cmd->ac_bk, &arg->ac_bk); - ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vi, &arg->ac_vi); - ath10k_wmi_pdev_set_wmm_param(&cmd->ac_vo, &arg->ac_vo); + ath10k_wmi_set_wmm_param(&cmd->ac_be, &arg->ac_be); + ath10k_wmi_set_wmm_param(&cmd->ac_bk, &arg->ac_bk); + ath10k_wmi_set_wmm_param(&cmd->ac_vi, &arg->ac_vi); + ath10k_wmi_set_wmm_param(&cmd->ac_vo, &arg->ac_vo); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi pdev set wmm params\n"); - return ath10k_wmi_cmd_send(ar, skb, - ar->wmi.cmd->pdev_set_wmm_params_cmdid); + return skb; } -int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id) +static struct sk_buff * +ath10k_wmi_op_gen_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id) { struct wmi_request_stats_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_request_stats_cmd *)skb->data; cmd->stats_id = __cpu_to_le32(stats_id); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi request stats %d\n", (int)stats_id); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->request_stats_cmdid); + return skb; } -int ath10k_wmi_force_fw_hang(struct ath10k *ar, - enum wmi_force_fw_hang_type type, u32 delay_ms) +static struct sk_buff * +ath10k_wmi_op_gen_force_fw_hang(struct ath10k *ar, + enum wmi_force_fw_hang_type type, u32 delay_ms) { struct wmi_force_fw_hang_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_force_fw_hang_cmd *)skb->data; cmd->type = __cpu_to_le32(type); @@ -4357,10 +4987,12 @@ int ath10k_wmi_force_fw_hang(struct ath10k *ar, ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi force fw hang %d delay %d\n", type, delay_ms); - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->force_fw_hang_cmdid); + return skb; } -int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable) +static struct sk_buff * +ath10k_wmi_op_gen_dbglog_cfg(struct ath10k *ar, u32 module_enable, + u32 log_level) { struct wmi_dbglog_cfg_cmd *cmd; struct sk_buff *skb; @@ -4368,12 +5000,12 @@ int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable) skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); cmd = (struct wmi_dbglog_cfg_cmd *)skb->data; if (module_enable) { - cfg = SM(ATH10K_DBGLOG_LEVEL_VERBOSE, + cfg = SM(log_level, ATH10K_DBGLOG_CFG_LOG_LVL); } else { /* set back defaults, all modules with WARN level */ @@ -4393,57 +5025,449 @@ int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable) __le32_to_cpu(cmd->module_valid), __le32_to_cpu(cmd->config_enable), __le32_to_cpu(cmd->config_valid)); - - return ath10k_wmi_cmd_send(ar, skb, ar->wmi.cmd->dbglog_cfg_cmdid); + return skb; } -int ath10k_wmi_pdev_pktlog_enable(struct ath10k *ar, u32 ev_bitmap) +static struct sk_buff * +ath10k_wmi_op_gen_pktlog_enable(struct ath10k *ar, u32 ev_bitmap) { struct wmi_pdev_pktlog_enable_cmd *cmd; struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); ev_bitmap &= ATH10K_PKTLOG_ANY; - ath10k_dbg(ar, ATH10K_DBG_WMI, - "wmi enable pktlog filter:%x\n", ev_bitmap); cmd = (struct wmi_pdev_pktlog_enable_cmd *)skb->data; cmd->ev_bitmap = __cpu_to_le32(ev_bitmap); - return ath10k_wmi_cmd_send(ar, skb, - ar->wmi.cmd->pdev_pktlog_enable_cmdid); + + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi enable pktlog filter 0x%08x\n", + ev_bitmap); + return skb; } -int ath10k_wmi_pdev_pktlog_disable(struct ath10k *ar) +static struct sk_buff * +ath10k_wmi_op_gen_pktlog_disable(struct ath10k *ar) { struct sk_buff *skb; skb = ath10k_wmi_alloc_skb(ar, 0); if (!skb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi disable pktlog\n"); + return skb; +} + +static struct sk_buff * +ath10k_wmi_op_gen_pdev_set_quiet_mode(struct ath10k *ar, u32 period, + u32 duration, u32 next_offset, + u32 enabled) +{ + struct wmi_pdev_set_quiet_cmd *cmd; + struct sk_buff *skb; + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + cmd = (struct wmi_pdev_set_quiet_cmd *)skb->data; + cmd->period = __cpu_to_le32(period); + cmd->duration = __cpu_to_le32(duration); + cmd->next_start = __cpu_to_le32(next_offset); + cmd->enabled = __cpu_to_le32(enabled); - return ath10k_wmi_cmd_send(ar, skb, - ar->wmi.cmd->pdev_pktlog_disable_cmdid); + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi quiet param: period %u duration %u enabled %d\n", + period, duration, enabled); + return skb; } -int ath10k_wmi_attach(struct ath10k *ar) +static struct sk_buff * +ath10k_wmi_op_gen_addba_clear_resp(struct ath10k *ar, u32 vdev_id, + const u8 *mac) { - if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) { - if (test_bit(ATH10K_FW_FEATURE_WMI_10_2, ar->fw_features)) - ar->wmi.cmd = &wmi_10_2_cmd_map; - else - ar->wmi.cmd = &wmi_10x_cmd_map; + struct wmi_addba_clear_resp_cmd *cmd; + struct sk_buff *skb; + + if (!mac) + return ERR_PTR(-EINVAL); + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + cmd = (struct wmi_addba_clear_resp_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + ether_addr_copy(cmd->peer_macaddr.addr, mac); + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi addba clear resp vdev_id 0x%X mac_addr %pM\n", + vdev_id, mac); + return skb; +} + +static struct sk_buff * +ath10k_wmi_op_gen_addba_send(struct ath10k *ar, u32 vdev_id, const u8 *mac, + u32 tid, u32 buf_size) +{ + struct wmi_addba_send_cmd *cmd; + struct sk_buff *skb; + + if (!mac) + return ERR_PTR(-EINVAL); + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + cmd = (struct wmi_addba_send_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + ether_addr_copy(cmd->peer_macaddr.addr, mac); + cmd->tid = __cpu_to_le32(tid); + cmd->buffersize = __cpu_to_le32(buf_size); + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi addba send vdev_id 0x%X mac_addr %pM tid %u bufsize %u\n", + vdev_id, mac, tid, buf_size); + return skb; +} + +static struct sk_buff * +ath10k_wmi_op_gen_addba_set_resp(struct ath10k *ar, u32 vdev_id, const u8 *mac, + u32 tid, u32 status) +{ + struct wmi_addba_setresponse_cmd *cmd; + struct sk_buff *skb; + + if (!mac) + return ERR_PTR(-EINVAL); + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + cmd = (struct wmi_addba_setresponse_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + ether_addr_copy(cmd->peer_macaddr.addr, mac); + cmd->tid = __cpu_to_le32(tid); + cmd->statuscode = __cpu_to_le32(status); + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi addba set resp vdev_id 0x%X mac_addr %pM tid %u status %u\n", + vdev_id, mac, tid, status); + return skb; +} + +static struct sk_buff * +ath10k_wmi_op_gen_delba_send(struct ath10k *ar, u32 vdev_id, const u8 *mac, + u32 tid, u32 initiator, u32 reason) +{ + struct wmi_delba_send_cmd *cmd; + struct sk_buff *skb; + + if (!mac) + return ERR_PTR(-EINVAL); + + skb = ath10k_wmi_alloc_skb(ar, sizeof(*cmd)); + if (!skb) + return ERR_PTR(-ENOMEM); + + cmd = (struct wmi_delba_send_cmd *)skb->data; + cmd->vdev_id = __cpu_to_le32(vdev_id); + ether_addr_copy(cmd->peer_macaddr.addr, mac); + cmd->tid = __cpu_to_le32(tid); + cmd->initiator = __cpu_to_le32(initiator); + cmd->reasoncode = __cpu_to_le32(reason); + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi delba send vdev_id 0x%X mac_addr %pM tid %u initiator %u reason %u\n", + vdev_id, mac, tid, initiator, reason); + return skb; +} + +static const struct wmi_ops wmi_ops = { + .rx = ath10k_wmi_op_rx, + .map_svc = wmi_main_svc_map, + + .pull_scan = ath10k_wmi_op_pull_scan_ev, + .pull_mgmt_rx = ath10k_wmi_op_pull_mgmt_rx_ev, + .pull_ch_info = ath10k_wmi_op_pull_ch_info_ev, + .pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev, + .pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev, + .pull_swba = ath10k_wmi_op_pull_swba_ev, + .pull_phyerr = ath10k_wmi_op_pull_phyerr_ev, + .pull_svc_rdy = ath10k_wmi_main_op_pull_svc_rdy_ev, + .pull_rdy = ath10k_wmi_op_pull_rdy_ev, + .pull_fw_stats = ath10k_wmi_main_op_pull_fw_stats, + + .gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend, + .gen_pdev_resume = ath10k_wmi_op_gen_pdev_resume, + .gen_pdev_set_rd = ath10k_wmi_op_gen_pdev_set_rd, + .gen_pdev_set_param = ath10k_wmi_op_gen_pdev_set_param, + .gen_init = ath10k_wmi_op_gen_init, + .gen_start_scan = ath10k_wmi_op_gen_start_scan, + .gen_stop_scan = ath10k_wmi_op_gen_stop_scan, + .gen_vdev_create = ath10k_wmi_op_gen_vdev_create, + .gen_vdev_delete = ath10k_wmi_op_gen_vdev_delete, + .gen_vdev_start = ath10k_wmi_op_gen_vdev_start, + .gen_vdev_stop = ath10k_wmi_op_gen_vdev_stop, + .gen_vdev_up = ath10k_wmi_op_gen_vdev_up, + .gen_vdev_down = ath10k_wmi_op_gen_vdev_down, + .gen_vdev_set_param = ath10k_wmi_op_gen_vdev_set_param, + .gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key, + .gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf, + .gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable, + /* .gen_vdev_wmm_conf not implemented */ + .gen_peer_create = ath10k_wmi_op_gen_peer_create, + .gen_peer_delete = ath10k_wmi_op_gen_peer_delete, + .gen_peer_flush = ath10k_wmi_op_gen_peer_flush, + .gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param, + .gen_peer_assoc = ath10k_wmi_op_gen_peer_assoc, + .gen_set_psmode = ath10k_wmi_op_gen_set_psmode, + .gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps, + .gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps, + .gen_scan_chan_list = ath10k_wmi_op_gen_scan_chan_list, + .gen_beacon_dma = ath10k_wmi_op_gen_beacon_dma, + .gen_pdev_set_wmm = ath10k_wmi_op_gen_pdev_set_wmm, + .gen_request_stats = ath10k_wmi_op_gen_request_stats, + .gen_force_fw_hang = ath10k_wmi_op_gen_force_fw_hang, + .gen_mgmt_tx = ath10k_wmi_op_gen_mgmt_tx, + .gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg, + .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable, + .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable, + .gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode, + /* .gen_pdev_get_temperature not implemented */ + .gen_addba_clear_resp = ath10k_wmi_op_gen_addba_clear_resp, + .gen_addba_send = ath10k_wmi_op_gen_addba_send, + .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp, + .gen_delba_send = ath10k_wmi_op_gen_delba_send, + /* .gen_bcn_tmpl not implemented */ + /* .gen_prb_tmpl not implemented */ + /* .gen_p2p_go_bcn_ie not implemented */ +}; + +static const struct wmi_ops wmi_10_1_ops = { + .rx = ath10k_wmi_10_1_op_rx, + .map_svc = wmi_10x_svc_map, + .pull_svc_rdy = ath10k_wmi_10x_op_pull_svc_rdy_ev, + .pull_fw_stats = ath10k_wmi_10x_op_pull_fw_stats, + .gen_init = ath10k_wmi_10_1_op_gen_init, + .gen_pdev_set_rd = ath10k_wmi_10x_op_gen_pdev_set_rd, + .gen_start_scan = ath10k_wmi_10x_op_gen_start_scan, + .gen_peer_assoc = ath10k_wmi_10_1_op_gen_peer_assoc, + /* .gen_pdev_get_temperature not implemented */ + + /* shared with main branch */ + .pull_scan = ath10k_wmi_op_pull_scan_ev, + .pull_mgmt_rx = ath10k_wmi_op_pull_mgmt_rx_ev, + .pull_ch_info = ath10k_wmi_op_pull_ch_info_ev, + .pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev, + .pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev, + .pull_swba = ath10k_wmi_op_pull_swba_ev, + .pull_phyerr = ath10k_wmi_op_pull_phyerr_ev, + .pull_rdy = ath10k_wmi_op_pull_rdy_ev, + + .gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend, + .gen_pdev_resume = ath10k_wmi_op_gen_pdev_resume, + .gen_pdev_set_param = ath10k_wmi_op_gen_pdev_set_param, + .gen_stop_scan = ath10k_wmi_op_gen_stop_scan, + .gen_vdev_create = ath10k_wmi_op_gen_vdev_create, + .gen_vdev_delete = ath10k_wmi_op_gen_vdev_delete, + .gen_vdev_start = ath10k_wmi_op_gen_vdev_start, + .gen_vdev_stop = ath10k_wmi_op_gen_vdev_stop, + .gen_vdev_up = ath10k_wmi_op_gen_vdev_up, + .gen_vdev_down = ath10k_wmi_op_gen_vdev_down, + .gen_vdev_set_param = ath10k_wmi_op_gen_vdev_set_param, + .gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key, + .gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf, + .gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable, + /* .gen_vdev_wmm_conf not implemented */ + .gen_peer_create = ath10k_wmi_op_gen_peer_create, + .gen_peer_delete = ath10k_wmi_op_gen_peer_delete, + .gen_peer_flush = ath10k_wmi_op_gen_peer_flush, + .gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param, + .gen_set_psmode = ath10k_wmi_op_gen_set_psmode, + .gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps, + .gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps, + .gen_scan_chan_list = ath10k_wmi_op_gen_scan_chan_list, + .gen_beacon_dma = ath10k_wmi_op_gen_beacon_dma, + .gen_pdev_set_wmm = ath10k_wmi_op_gen_pdev_set_wmm, + .gen_request_stats = ath10k_wmi_op_gen_request_stats, + .gen_force_fw_hang = ath10k_wmi_op_gen_force_fw_hang, + .gen_mgmt_tx = ath10k_wmi_op_gen_mgmt_tx, + .gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg, + .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable, + .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable, + .gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode, + .gen_addba_clear_resp = ath10k_wmi_op_gen_addba_clear_resp, + .gen_addba_send = ath10k_wmi_op_gen_addba_send, + .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp, + .gen_delba_send = ath10k_wmi_op_gen_delba_send, + /* .gen_bcn_tmpl not implemented */ + /* .gen_prb_tmpl not implemented */ + /* .gen_p2p_go_bcn_ie not implemented */ +}; + +static const struct wmi_ops wmi_10_2_ops = { + .rx = ath10k_wmi_10_2_op_rx, + .pull_fw_stats = ath10k_wmi_10_2_op_pull_fw_stats, + .gen_init = ath10k_wmi_10_2_op_gen_init, + .gen_peer_assoc = ath10k_wmi_10_2_op_gen_peer_assoc, + /* .gen_pdev_get_temperature not implemented */ + + /* shared with 10.1 */ + .map_svc = wmi_10x_svc_map, + .pull_svc_rdy = ath10k_wmi_10x_op_pull_svc_rdy_ev, + .gen_pdev_set_rd = ath10k_wmi_10x_op_gen_pdev_set_rd, + .gen_start_scan = ath10k_wmi_10x_op_gen_start_scan, + + .pull_scan = ath10k_wmi_op_pull_scan_ev, + .pull_mgmt_rx = ath10k_wmi_op_pull_mgmt_rx_ev, + .pull_ch_info = ath10k_wmi_op_pull_ch_info_ev, + .pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev, + .pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev, + .pull_swba = ath10k_wmi_op_pull_swba_ev, + .pull_phyerr = ath10k_wmi_op_pull_phyerr_ev, + .pull_rdy = ath10k_wmi_op_pull_rdy_ev, + + .gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend, + .gen_pdev_resume = ath10k_wmi_op_gen_pdev_resume, + .gen_pdev_set_param = ath10k_wmi_op_gen_pdev_set_param, + .gen_stop_scan = ath10k_wmi_op_gen_stop_scan, + .gen_vdev_create = ath10k_wmi_op_gen_vdev_create, + .gen_vdev_delete = ath10k_wmi_op_gen_vdev_delete, + .gen_vdev_start = ath10k_wmi_op_gen_vdev_start, + .gen_vdev_stop = ath10k_wmi_op_gen_vdev_stop, + .gen_vdev_up = ath10k_wmi_op_gen_vdev_up, + .gen_vdev_down = ath10k_wmi_op_gen_vdev_down, + .gen_vdev_set_param = ath10k_wmi_op_gen_vdev_set_param, + .gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key, + .gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf, + .gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable, + /* .gen_vdev_wmm_conf not implemented */ + .gen_peer_create = ath10k_wmi_op_gen_peer_create, + .gen_peer_delete = ath10k_wmi_op_gen_peer_delete, + .gen_peer_flush = ath10k_wmi_op_gen_peer_flush, + .gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param, + .gen_set_psmode = ath10k_wmi_op_gen_set_psmode, + .gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps, + .gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps, + .gen_scan_chan_list = ath10k_wmi_op_gen_scan_chan_list, + .gen_beacon_dma = ath10k_wmi_op_gen_beacon_dma, + .gen_pdev_set_wmm = ath10k_wmi_op_gen_pdev_set_wmm, + .gen_request_stats = ath10k_wmi_op_gen_request_stats, + .gen_force_fw_hang = ath10k_wmi_op_gen_force_fw_hang, + .gen_mgmt_tx = ath10k_wmi_op_gen_mgmt_tx, + .gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg, + .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable, + .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable, + .gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode, + .gen_addba_clear_resp = ath10k_wmi_op_gen_addba_clear_resp, + .gen_addba_send = ath10k_wmi_op_gen_addba_send, + .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp, + .gen_delba_send = ath10k_wmi_op_gen_delba_send, +}; +static const struct wmi_ops wmi_10_2_4_ops = { + .rx = ath10k_wmi_10_2_op_rx, + .pull_fw_stats = ath10k_wmi_10_2_4_op_pull_fw_stats, + .gen_init = ath10k_wmi_10_2_op_gen_init, + .gen_peer_assoc = ath10k_wmi_10_2_op_gen_peer_assoc, + .gen_pdev_get_temperature = ath10k_wmi_10_2_op_gen_pdev_get_temperature, + + /* shared with 10.1 */ + .map_svc = wmi_10x_svc_map, + .pull_svc_rdy = ath10k_wmi_10x_op_pull_svc_rdy_ev, + .gen_pdev_set_rd = ath10k_wmi_10x_op_gen_pdev_set_rd, + .gen_start_scan = ath10k_wmi_10x_op_gen_start_scan, + + .pull_scan = ath10k_wmi_op_pull_scan_ev, + .pull_mgmt_rx = ath10k_wmi_op_pull_mgmt_rx_ev, + .pull_ch_info = ath10k_wmi_op_pull_ch_info_ev, + .pull_vdev_start = ath10k_wmi_op_pull_vdev_start_ev, + .pull_peer_kick = ath10k_wmi_op_pull_peer_kick_ev, + .pull_swba = ath10k_wmi_op_pull_swba_ev, + .pull_phyerr = ath10k_wmi_op_pull_phyerr_ev, + .pull_rdy = ath10k_wmi_op_pull_rdy_ev, + + .gen_pdev_suspend = ath10k_wmi_op_gen_pdev_suspend, + .gen_pdev_resume = ath10k_wmi_op_gen_pdev_resume, + .gen_pdev_set_param = ath10k_wmi_op_gen_pdev_set_param, + .gen_stop_scan = ath10k_wmi_op_gen_stop_scan, + .gen_vdev_create = ath10k_wmi_op_gen_vdev_create, + .gen_vdev_delete = ath10k_wmi_op_gen_vdev_delete, + .gen_vdev_start = ath10k_wmi_op_gen_vdev_start, + .gen_vdev_stop = ath10k_wmi_op_gen_vdev_stop, + .gen_vdev_up = ath10k_wmi_op_gen_vdev_up, + .gen_vdev_down = ath10k_wmi_op_gen_vdev_down, + .gen_vdev_set_param = ath10k_wmi_op_gen_vdev_set_param, + .gen_vdev_install_key = ath10k_wmi_op_gen_vdev_install_key, + .gen_vdev_spectral_conf = ath10k_wmi_op_gen_vdev_spectral_conf, + .gen_vdev_spectral_enable = ath10k_wmi_op_gen_vdev_spectral_enable, + .gen_peer_create = ath10k_wmi_op_gen_peer_create, + .gen_peer_delete = ath10k_wmi_op_gen_peer_delete, + .gen_peer_flush = ath10k_wmi_op_gen_peer_flush, + .gen_peer_set_param = ath10k_wmi_op_gen_peer_set_param, + .gen_set_psmode = ath10k_wmi_op_gen_set_psmode, + .gen_set_sta_ps = ath10k_wmi_op_gen_set_sta_ps, + .gen_set_ap_ps = ath10k_wmi_op_gen_set_ap_ps, + .gen_scan_chan_list = ath10k_wmi_op_gen_scan_chan_list, + .gen_beacon_dma = ath10k_wmi_op_gen_beacon_dma, + .gen_pdev_set_wmm = ath10k_wmi_op_gen_pdev_set_wmm, + .gen_request_stats = ath10k_wmi_op_gen_request_stats, + .gen_force_fw_hang = ath10k_wmi_op_gen_force_fw_hang, + .gen_mgmt_tx = ath10k_wmi_op_gen_mgmt_tx, + .gen_dbglog_cfg = ath10k_wmi_op_gen_dbglog_cfg, + .gen_pktlog_enable = ath10k_wmi_op_gen_pktlog_enable, + .gen_pktlog_disable = ath10k_wmi_op_gen_pktlog_disable, + .gen_pdev_set_quiet_mode = ath10k_wmi_op_gen_pdev_set_quiet_mode, + .gen_addba_clear_resp = ath10k_wmi_op_gen_addba_clear_resp, + .gen_addba_send = ath10k_wmi_op_gen_addba_send, + .gen_addba_set_resp = ath10k_wmi_op_gen_addba_set_resp, + .gen_delba_send = ath10k_wmi_op_gen_delba_send, + /* .gen_bcn_tmpl not implemented */ + /* .gen_prb_tmpl not implemented */ + /* .gen_p2p_go_bcn_ie not implemented */ +}; + +int ath10k_wmi_attach(struct ath10k *ar) +{ + switch (ar->wmi.op_version) { + case ATH10K_FW_WMI_OP_VERSION_10_2_4: + ar->wmi.cmd = &wmi_10_2_4_cmd_map; + ar->wmi.ops = &wmi_10_2_4_ops; + ar->wmi.vdev_param = &wmi_10_2_4_vdev_param_map; + ar->wmi.pdev_param = &wmi_10_2_4_pdev_param_map; + break; + case ATH10K_FW_WMI_OP_VERSION_10_2: + ar->wmi.cmd = &wmi_10_2_cmd_map; + ar->wmi.ops = &wmi_10_2_ops; ar->wmi.vdev_param = &wmi_10x_vdev_param_map; ar->wmi.pdev_param = &wmi_10x_pdev_param_map; - } else { + break; + case ATH10K_FW_WMI_OP_VERSION_10_1: + ar->wmi.cmd = &wmi_10x_cmd_map; + ar->wmi.ops = &wmi_10_1_ops; + ar->wmi.vdev_param = &wmi_10x_vdev_param_map; + ar->wmi.pdev_param = &wmi_10x_pdev_param_map; + break; + case ATH10K_FW_WMI_OP_VERSION_MAIN: ar->wmi.cmd = &wmi_cmd_map; + ar->wmi.ops = &wmi_ops; ar->wmi.vdev_param = &wmi_vdev_param_map; ar->wmi.pdev_param = &wmi_pdev_param_map; + break; + case ATH10K_FW_WMI_OP_VERSION_TLV: + ath10k_wmi_tlv_attach(ar); + break; + case ATH10K_FW_WMI_OP_VERSION_UNSET: + case ATH10K_FW_WMI_OP_VERSION_MAX: + ath10k_err(ar, "unsupported WMI op version: %d\n", + ar->wmi.op_version); + return -EINVAL; } init_completion(&ar->wmi.service_ready); diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 21391929d318..20ce3603e64b 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -109,6 +109,45 @@ enum wmi_service { WMI_SERVICE_BURST, WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT, WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT, + WMI_SERVICE_ROAM_SCAN_OFFLOAD, + WMI_SERVICE_AP_PS_DETECT_OUT_OF_SYNC, + WMI_SERVICE_EARLY_RX, + WMI_SERVICE_STA_SMPS, + WMI_SERVICE_FWTEST, + WMI_SERVICE_STA_WMMAC, + WMI_SERVICE_TDLS, + WMI_SERVICE_MCC_BCN_INTERVAL_CHANGE, + WMI_SERVICE_ADAPTIVE_OCS, + WMI_SERVICE_BA_SSN_SUPPORT, + WMI_SERVICE_FILTER_IPSEC_NATKEEPALIVE, + WMI_SERVICE_WLAN_HB, + WMI_SERVICE_LTE_ANT_SHARE_SUPPORT, + WMI_SERVICE_BATCH_SCAN, + WMI_SERVICE_QPOWER, + WMI_SERVICE_PLMREQ, + WMI_SERVICE_THERMAL_MGMT, + WMI_SERVICE_RMC, + WMI_SERVICE_MHF_OFFLOAD, + WMI_SERVICE_COEX_SAR, + WMI_SERVICE_BCN_TXRATE_OVERRIDE, + WMI_SERVICE_NAN, + WMI_SERVICE_L1SS_STAT, + WMI_SERVICE_ESTIMATE_LINKSPEED, + WMI_SERVICE_OBSS_SCAN, + WMI_SERVICE_TDLS_OFFCHAN, + WMI_SERVICE_TDLS_UAPSD_BUFFER_STA, + WMI_SERVICE_TDLS_UAPSD_SLEEP_STA, + WMI_SERVICE_IBSS_PWRSAVE, + WMI_SERVICE_LPASS, + WMI_SERVICE_EXTSCAN, + WMI_SERVICE_D0WOW, + WMI_SERVICE_HSOFFLOAD, + WMI_SERVICE_ROAM_HO_OFFLOAD, + WMI_SERVICE_RX_FULL_REORDER, + WMI_SERVICE_DHCP_OFFLOAD, + WMI_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT, + WMI_SERVICE_MDNS_OFFLOAD, + WMI_SERVICE_SAP_AUTH_OFFLOAD, /* keep last */ WMI_SERVICE_MAX, @@ -215,6 +254,45 @@ static inline char *wmi_service_name(int service_id) SVCSTR(WMI_SERVICE_BURST); SVCSTR(WMI_SERVICE_SMART_ANTENNA_SW_SUPPORT); SVCSTR(WMI_SERVICE_SMART_ANTENNA_HW_SUPPORT); + SVCSTR(WMI_SERVICE_ROAM_SCAN_OFFLOAD); + SVCSTR(WMI_SERVICE_AP_PS_DETECT_OUT_OF_SYNC); + SVCSTR(WMI_SERVICE_EARLY_RX); + SVCSTR(WMI_SERVICE_STA_SMPS); + SVCSTR(WMI_SERVICE_FWTEST); + SVCSTR(WMI_SERVICE_STA_WMMAC); + SVCSTR(WMI_SERVICE_TDLS); + SVCSTR(WMI_SERVICE_MCC_BCN_INTERVAL_CHANGE); + SVCSTR(WMI_SERVICE_ADAPTIVE_OCS); + SVCSTR(WMI_SERVICE_BA_SSN_SUPPORT); + SVCSTR(WMI_SERVICE_FILTER_IPSEC_NATKEEPALIVE); + SVCSTR(WMI_SERVICE_WLAN_HB); + SVCSTR(WMI_SERVICE_LTE_ANT_SHARE_SUPPORT); + SVCSTR(WMI_SERVICE_BATCH_SCAN); + SVCSTR(WMI_SERVICE_QPOWER); + SVCSTR(WMI_SERVICE_PLMREQ); + SVCSTR(WMI_SERVICE_THERMAL_MGMT); + SVCSTR(WMI_SERVICE_RMC); + SVCSTR(WMI_SERVICE_MHF_OFFLOAD); + SVCSTR(WMI_SERVICE_COEX_SAR); + SVCSTR(WMI_SERVICE_BCN_TXRATE_OVERRIDE); + SVCSTR(WMI_SERVICE_NAN); + SVCSTR(WMI_SERVICE_L1SS_STAT); + SVCSTR(WMI_SERVICE_ESTIMATE_LINKSPEED); + SVCSTR(WMI_SERVICE_OBSS_SCAN); + SVCSTR(WMI_SERVICE_TDLS_OFFCHAN); + SVCSTR(WMI_SERVICE_TDLS_UAPSD_BUFFER_STA); + SVCSTR(WMI_SERVICE_TDLS_UAPSD_SLEEP_STA); + SVCSTR(WMI_SERVICE_IBSS_PWRSAVE); + SVCSTR(WMI_SERVICE_LPASS); + SVCSTR(WMI_SERVICE_EXTSCAN); + SVCSTR(WMI_SERVICE_D0WOW); + SVCSTR(WMI_SERVICE_HSOFFLOAD); + SVCSTR(WMI_SERVICE_ROAM_HO_OFFLOAD); + SVCSTR(WMI_SERVICE_RX_FULL_REORDER); + SVCSTR(WMI_SERVICE_DHCP_OFFLOAD); + SVCSTR(WMI_SERVICE_STA_RX_IPA_OFFLOAD_SUPPORT); + SVCSTR(WMI_SERVICE_MDNS_OFFLOAD); + SVCSTR(WMI_SERVICE_SAP_AUTH_OFFLOAD); default: return NULL; } @@ -472,6 +550,8 @@ struct wmi_cmd_map { u32 force_fw_hang_cmdid; u32 gpio_config_cmdid; u32 gpio_output_cmdid; + u32 pdev_get_temperature_cmdid; + u32 vdev_set_wmm_params_cmdid; }; /* @@ -1076,6 +1156,11 @@ enum wmi_10_2_cmd_id { WMI_10_2_PDEV_SET_MIMOGAIN_TABLE_CMDID, WMI_10_2_PDEV_RATEPWR_TABLE_CMDID, WMI_10_2_PDEV_RATEPWR_CHAINMSK_TABLE_CMDID, + WMI_10_2_PDEV_GET_INFO, + WMI_10_2_VDEV_GET_INFO, + WMI_10_2_VDEV_ATF_REQUEST_CMDID, + WMI_10_2_PEER_ATF_REQUEST_CMDID, + WMI_10_2_PDEV_GET_TEMPERATURE_CMDID, WMI_10_2_PDEV_UTF_CMDID = WMI_10_2_END_CMDID - 1, }; @@ -1117,6 +1202,8 @@ enum wmi_10_2_event_id { WMI_10_2_MCAST_BUF_RELEASE_EVENTID, WMI_10_2_MCAST_LIST_AGEOUT_EVENTID, WMI_10_2_WDS_PEER_EVENTID, + WMI_10_2_PEER_STA_PS_STATECHG_EVENTID, + WMI_10_2_PDEV_TEMPERATURE_EVENTID, WMI_10_2_PDEV_UTF_EVENTID = WMI_10_2_END_EVENTID - 1, }; @@ -1862,6 +1949,11 @@ struct wmi_resource_config_10x { __le32 max_frag_entries; } __packed; +enum wmi_10_2_feature_mask { + WMI_10_2_RX_BATCH_MODE = BIT(0), + WMI_10_2_ATF_CONFIG = BIT(1), +}; + struct wmi_resource_config_10_2 { struct wmi_resource_config_10x common; __le32 max_peer_ext_stats; @@ -1870,7 +1962,7 @@ struct wmi_resource_config_10_2 { __le32 be_min_free; __le32 vi_min_free; __le32 vo_min_free; - __le32 rx_batchmode; /* 0-disable, 1-enable */ + __le32 feature_mask; } __packed; #define NUM_UNITS_IS_NUM_VDEVS 0x1 @@ -2505,6 +2597,7 @@ struct wmi_pdev_param_map { u32 fast_channel_reset; u32 burst_dur; u32 burst_enable; + u32 cal_period; }; #define WMI_PDEV_PARAM_UNSUPPORTED 0 @@ -2715,6 +2808,9 @@ enum wmi_10x_pdev_param { WMI_10X_PDEV_PARAM_SET_MCAST2UCAST_MODE, WMI_10X_PDEV_PARAM_SET_MCAST2UCAST_BUFFER, WMI_10X_PDEV_PARAM_REMOVE_MCAST2UCAST_BUFFER, + WMI_10X_PDEV_PARAM_PEER_STA_PS_STATECHG_ENABLE, + WMI_10X_PDEV_PARAM_RTS_FIXED_RATE, + WMI_10X_PDEV_PARAM_CAL_PERIOD }; struct wmi_pdev_set_param_cmd { @@ -2722,6 +2818,9 @@ struct wmi_pdev_set_param_cmd { __le32 param_value; } __packed; +/* valid period is 1 ~ 60000ms, unit in millisecond */ +#define WMI_PDEV_PARAM_CAL_PERIOD_MAX 60000 + struct wmi_pdev_get_tpc_config_cmd { /* parameter */ __le32 param; @@ -2841,14 +2940,14 @@ struct wmi_wmm_params_arg { u32 no_ack; }; -struct wmi_pdev_set_wmm_params_arg { +struct wmi_wmm_params_all_arg { struct wmi_wmm_params_arg ac_be; struct wmi_wmm_params_arg ac_bk; struct wmi_wmm_params_arg ac_vi; struct wmi_wmm_params_arg ac_vo; }; -struct wal_dbg_tx_stats { +struct wmi_pdev_stats_tx { /* Num HTT cookies queued to dispatch list */ __le32 comp_queued; @@ -2918,7 +3017,7 @@ struct wal_dbg_tx_stats { __le32 txop_ovf; } __packed; -struct wal_dbg_rx_stats { +struct wmi_pdev_stats_rx { /* Cnts any change in ring routing mid-ppdu */ __le32 mid_ppdu_route_change; @@ -2952,17 +3051,11 @@ struct wal_dbg_rx_stats { __le32 mpdu_errs; } __packed; -struct wal_dbg_peer_stats { +struct wmi_pdev_stats_peer { /* REMOVE THIS ONCE REAL PEER STAT COUNTERS ARE ADDED */ __le32 dummy; } __packed; -struct wal_dbg_stats { - struct wal_dbg_tx_stats tx; - struct wal_dbg_rx_stats rx; - struct wal_dbg_peer_stats peer; -} __packed; - enum wmi_stats_id { WMI_REQUEST_PEER_STAT = 0x01, WMI_REQUEST_AP_STAT = 0x02 @@ -3029,23 +3122,38 @@ struct wmi_stats_event { u8 data[0]; } __packed; +struct wmi_10_2_stats_event { + __le32 stats_id; /* %WMI_REQUEST_ */ + __le32 num_pdev_stats; + __le32 num_pdev_ext_stats; + __le32 num_vdev_stats; + __le32 num_peer_stats; + __le32 num_bcnflt_stats; + u8 data[0]; +} __packed; + /* * PDEV statistics * TODO: add all PDEV stats here */ +struct wmi_pdev_stats_base { + __le32 chan_nf; + __le32 tx_frame_count; + __le32 rx_frame_count; + __le32 rx_clear_count; + __le32 cycle_count; + __le32 phy_err_count; + __le32 chan_tx_pwr; +} __packed; + struct wmi_pdev_stats { - __le32 chan_nf; /* Channel noise floor */ - __le32 tx_frame_count; /* TX frame count */ - __le32 rx_frame_count; /* RX frame count */ - __le32 rx_clear_count; /* rx clear count */ - __le32 cycle_count; /* cycle count */ - __le32 phy_err_count; /* Phy error count */ - __le32 chan_tx_pwr; /* channel tx power */ - struct wal_dbg_stats wal; /* WAL dbg stats */ + struct wmi_pdev_stats_base base; + struct wmi_pdev_stats_tx tx; + struct wmi_pdev_stats_rx rx; + struct wmi_pdev_stats_peer peer; } __packed; -struct wmi_10x_pdev_stats { - struct wmi_pdev_stats old; +struct wmi_pdev_stats_extra { __le32 ack_rx_bad; __le32 rts_bad; __le32 rts_good; @@ -3054,6 +3162,30 @@ struct wmi_10x_pdev_stats { __le32 mib_int_count; } __packed; +struct wmi_10x_pdev_stats { + struct wmi_pdev_stats_base base; + struct wmi_pdev_stats_tx tx; + struct wmi_pdev_stats_rx rx; + struct wmi_pdev_stats_peer peer; + struct wmi_pdev_stats_extra extra; +} __packed; + +struct wmi_pdev_stats_mem { + __le32 dram_free; + __le32 iram_free; +} __packed; + +struct wmi_10_2_pdev_stats { + struct wmi_pdev_stats_base base; + struct wmi_pdev_stats_tx tx; + __le32 mc_drop; + struct wmi_pdev_stats_rx rx; + __le32 pdev_rx_timeout; + struct wmi_pdev_stats_mem mem; + struct wmi_pdev_stats_peer peer; + struct wmi_pdev_stats_extra extra; +} __packed; + /* * VDEV statistics * TODO: add all VDEV stats here @@ -3077,6 +3209,32 @@ struct wmi_10x_peer_stats { __le32 peer_rx_rate; } __packed; +struct wmi_10_2_peer_stats { + struct wmi_peer_stats old; + __le32 peer_rx_rate; + __le32 current_per; + __le32 retries; + __le32 tx_rate_count; + __le32 max_4ms_frame_len; + __le32 total_sub_frames; + __le32 tx_bytes; + __le32 num_pkt_loss_overflow[4]; + __le32 num_pkt_loss_excess_retry[4]; +} __packed; + +struct wmi_10_2_4_peer_stats { + struct wmi_10_2_peer_stats common; + __le32 unknown_value; /* FIXME: what is this word? */ +} __packed; + +struct wmi_10_2_pdev_ext_stats { + __le32 rx_rssi_comb; + __le32 rx_rssi[4]; + __le32 rx_mcs[10]; + __le32 tx_mcs[10]; + __le32 ack_rssi; +} __packed; + struct wmi_vdev_create_cmd { __le32 vdev_id; __le32 vdev_type; @@ -3930,6 +4088,13 @@ enum wmi_sta_ps_param_pspoll_count { * Values greater than 0 indicate the maximum numer of PS-Poll frames * FW will send before waking up. */ + + /* When u-APSD is enabled the firmware will be very reluctant to exit + * STA PS. This could result in very poor Rx performance with STA doing + * PS-Poll for each and every buffered frame. This value is a bit + * arbitrary. + */ + WMI_STA_PS_PSPOLL_COUNT_UAPSD = 3, }; /* @@ -3955,6 +4120,30 @@ enum wmi_sta_ps_param_uapsd { WMI_STA_PS_UAPSD_AC3_TRIGGER_EN = (1 << 7), }; +#define WMI_STA_UAPSD_MAX_INTERVAL_MSEC UINT_MAX + +struct wmi_sta_uapsd_auto_trig_param { + __le32 wmm_ac; + __le32 user_priority; + __le32 service_interval; + __le32 suspend_interval; + __le32 delay_interval; +}; + +struct wmi_sta_uapsd_auto_trig_cmd_fixed_param { + __le32 vdev_id; + struct wmi_mac_addr peer_macaddr; + __le32 num_ac; +}; + +struct wmi_sta_uapsd_auto_trig_arg { + u32 wmm_ac; + u32 user_priority; + u32 service_interval; + u32 suspend_interval; + u32 delay_interval; +}; + enum wmi_sta_powersave_param { /* * Controls how frames are retrievd from AP while STA is sleeping @@ -4120,7 +4309,7 @@ struct wmi_bcn_info { struct wmi_host_swba_event { __le32 vdev_map; - struct wmi_bcn_info bcn_info[1]; + struct wmi_bcn_info bcn_info[0]; } __packed; #define WMI_MAX_AP_VDEV 16 @@ -4325,7 +4514,7 @@ struct wmi_peer_set_q_empty_callback_cmd { #define WMI_PEER_SPATIAL_MUX 0x00200000 #define WMI_PEER_VHT 0x02000000 #define WMI_PEER_80MHZ 0x04000000 -#define WMI_PEER_PMF 0x08000000 +#define WMI_PEER_VHT_2G 0x08000000 /* * Peer rate capabilities. @@ -4476,6 +4665,11 @@ enum wmi_sta_keepalive_method { WMI_STA_KEEPALIVE_METHOD_UNSOLICITATED_ARP_RESPONSE = 2, }; +#define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0 + +/* Firmware crashes if keepalive interval exceeds this limit */ +#define WMI_STA_KEEPALIVE_INTERVAL_MAX_SECONDS 0xffff + /* note: ip4 addresses are in network byte order, i.e. big endian */ struct wmi_sta_keepalive_arp_resp { __be32 src_ip4_addr; @@ -4491,6 +4685,16 @@ struct wmi_sta_keepalive_cmd { struct wmi_sta_keepalive_arp_resp arp_resp; } __packed; +struct wmi_sta_keepalive_arg { + u32 vdev_id; + u32 enabled; + u32 method; + u32 interval; + __be32 src_ip4_addr; + __be32 dest_ip4_addr; + const u8 dest_mac_addr[ETH_ALEN]; +}; + enum wmi_force_fw_hang_type { WMI_FORCE_FW_HANG_ASSERT = 1, WMI_FORCE_FW_HANG_NO_DETECT, @@ -4567,6 +4771,58 @@ struct wmi_dbglog_cfg_cmd { #define WMI_MAX_MEM_REQS 16 +struct wmi_scan_ev_arg { + __le32 event_type; /* %WMI_SCAN_EVENT_ */ + __le32 reason; /* %WMI_SCAN_REASON_ */ + __le32 channel_freq; /* only valid for WMI_SCAN_EVENT_FOREIGN_CHANNEL */ + __le32 scan_req_id; + __le32 scan_id; + __le32 vdev_id; +}; + +struct wmi_mgmt_rx_ev_arg { + __le32 channel; + __le32 snr; + __le32 rate; + __le32 phy_mode; + __le32 buf_len; + __le32 status; /* %WMI_RX_STATUS_ */ +}; + +struct wmi_ch_info_ev_arg { + __le32 err_code; + __le32 freq; + __le32 cmd_flags; + __le32 noise_floor; + __le32 rx_clear_count; + __le32 cycle_count; +}; + +struct wmi_vdev_start_ev_arg { + __le32 vdev_id; + __le32 req_id; + __le32 resp_type; /* %WMI_VDEV_RESP_ */ + __le32 status; +}; + +struct wmi_peer_kick_ev_arg { + const u8 *mac_addr; +}; + +struct wmi_swba_ev_arg { + __le32 vdev_map; + const struct wmi_tim_info *tim_info[WMI_MAX_AP_VDEV]; + const struct wmi_p2p_noa_info *noa_info[WMI_MAX_AP_VDEV]; +}; + +struct wmi_phyerr_ev_arg { + __le32 num_phyerrs; + __le32 tsf_l32; + __le32 tsf_u32; + __le32 buf_len; + const struct wmi_phyerr *phyerrs; +}; + struct wmi_svc_rdy_ev_arg { __le32 min_tx_power; __le32 max_tx_power; @@ -4574,6 +4830,7 @@ struct wmi_svc_rdy_ev_arg { __le32 vht_cap; __le32 sw_ver0; __le32 sw_ver1; + __le32 fw_build; __le32 phy_capab; __le32 num_rf_chains; __le32 eeprom_rd; @@ -4583,83 +4840,99 @@ struct wmi_svc_rdy_ev_arg { const struct wlan_host_mem_req *mem_reqs[WMI_MAX_MEM_REQS]; }; +struct wmi_rdy_ev_arg { + __le32 sw_version; + __le32 abi_version; + __le32 status; + const u8 *mac_addr; +}; + +struct wmi_pdev_temperature_event { + /* temperature value in Celcius degree */ + __le32 temperature; +} __packed; + struct ath10k; struct ath10k_vif; -struct ath10k_fw_stats; +struct ath10k_fw_stats_pdev; +struct ath10k_fw_stats_peer; int ath10k_wmi_attach(struct ath10k *ar); void ath10k_wmi_detach(struct ath10k *ar); int ath10k_wmi_wait_for_service_ready(struct ath10k *ar); int ath10k_wmi_wait_for_unified_ready(struct ath10k *ar); +struct sk_buff *ath10k_wmi_alloc_skb(struct ath10k *ar, u32 len); int ath10k_wmi_connect(struct ath10k *ar); struct sk_buff *ath10k_wmi_alloc_skb(struct ath10k *ar, u32 len); int ath10k_wmi_cmd_send(struct ath10k *ar, struct sk_buff *skb, u32 cmd_id); - -int ath10k_wmi_pdev_suspend_target(struct ath10k *ar, u32 suspend_opt); -int ath10k_wmi_pdev_resume_target(struct ath10k *ar); -int ath10k_wmi_pdev_set_regdomain(struct ath10k *ar, u16 rd, u16 rd2g, - u16 rd5g, u16 ctl2g, u16 ctl5g, - enum wmi_dfs_region dfs_reg); -int ath10k_wmi_pdev_set_param(struct ath10k *ar, u32 id, u32 value); -int ath10k_wmi_cmd_init(struct ath10k *ar); -int ath10k_wmi_start_scan(struct ath10k *ar, const struct wmi_start_scan_arg *); +int ath10k_wmi_cmd_send_nowait(struct ath10k *ar, struct sk_buff *skb, + u32 cmd_id); void ath10k_wmi_start_scan_init(struct ath10k *ar, struct wmi_start_scan_arg *); -int ath10k_wmi_stop_scan(struct ath10k *ar, - const struct wmi_stop_scan_arg *arg); -int ath10k_wmi_vdev_create(struct ath10k *ar, u32 vdev_id, - enum wmi_vdev_type type, - enum wmi_vdev_subtype subtype, - const u8 macaddr[ETH_ALEN]); -int ath10k_wmi_vdev_delete(struct ath10k *ar, u32 vdev_id); -int ath10k_wmi_vdev_start(struct ath10k *ar, - const struct wmi_vdev_start_request_arg *); -int ath10k_wmi_vdev_restart(struct ath10k *ar, - const struct wmi_vdev_start_request_arg *); -int ath10k_wmi_vdev_stop(struct ath10k *ar, u32 vdev_id); -int ath10k_wmi_vdev_up(struct ath10k *ar, u32 vdev_id, u32 aid, - const u8 *bssid); -int ath10k_wmi_vdev_down(struct ath10k *ar, u32 vdev_id); -int ath10k_wmi_vdev_set_param(struct ath10k *ar, u32 vdev_id, - u32 param_id, u32 param_value); -int ath10k_wmi_vdev_install_key(struct ath10k *ar, - const struct wmi_vdev_install_key_arg *arg); -int ath10k_wmi_vdev_spectral_conf(struct ath10k *ar, - const struct wmi_vdev_spectral_conf_arg *arg); -int ath10k_wmi_vdev_spectral_enable(struct ath10k *ar, u32 vdev_id, u32 trigger, - u32 enable); -int ath10k_wmi_peer_create(struct ath10k *ar, u32 vdev_id, - const u8 peer_addr[ETH_ALEN]); -int ath10k_wmi_peer_delete(struct ath10k *ar, u32 vdev_id, - const u8 peer_addr[ETH_ALEN]); -int ath10k_wmi_peer_flush(struct ath10k *ar, u32 vdev_id, - const u8 peer_addr[ETH_ALEN], u32 tid_bitmap); -int ath10k_wmi_peer_set_param(struct ath10k *ar, u32 vdev_id, - const u8 *peer_addr, - enum wmi_peer_param param_id, u32 param_value); -int ath10k_wmi_peer_assoc(struct ath10k *ar, - const struct wmi_peer_assoc_complete_arg *arg); -int ath10k_wmi_set_psmode(struct ath10k *ar, u32 vdev_id, - enum wmi_sta_ps_mode psmode); -int ath10k_wmi_set_sta_ps_param(struct ath10k *ar, u32 vdev_id, - enum wmi_sta_powersave_param param_id, - u32 value); -int ath10k_wmi_set_ap_ps_param(struct ath10k *ar, u32 vdev_id, const u8 *mac, - enum wmi_ap_ps_peer_param param_id, u32 value); -int ath10k_wmi_scan_chan_list(struct ath10k *ar, - const struct wmi_scan_chan_list_arg *arg); -int ath10k_wmi_beacon_send_ref_nowait(struct ath10k_vif *arvif); -int ath10k_wmi_pdev_set_wmm_params(struct ath10k *ar, - const struct wmi_pdev_set_wmm_params_arg *arg); -int ath10k_wmi_request_stats(struct ath10k *ar, enum wmi_stats_id stats_id); -int ath10k_wmi_force_fw_hang(struct ath10k *ar, - enum wmi_force_fw_hang_type type, u32 delay_ms); -int ath10k_wmi_mgmt_tx(struct ath10k *ar, struct sk_buff *skb); -int ath10k_wmi_dbglog_cfg(struct ath10k *ar, u32 module_enable); -int ath10k_wmi_pull_fw_stats(struct ath10k *ar, struct sk_buff *skb, - struct ath10k_fw_stats *stats); -int ath10k_wmi_pdev_pktlog_enable(struct ath10k *ar, u32 ev_list); -int ath10k_wmi_pdev_pktlog_disable(struct ath10k *ar); + +void ath10k_wmi_pull_pdev_stats_base(const struct wmi_pdev_stats_base *src, + struct ath10k_fw_stats_pdev *dst); +void ath10k_wmi_pull_pdev_stats_tx(const struct wmi_pdev_stats_tx *src, + struct ath10k_fw_stats_pdev *dst); +void ath10k_wmi_pull_pdev_stats_rx(const struct wmi_pdev_stats_rx *src, + struct ath10k_fw_stats_pdev *dst); +void ath10k_wmi_pull_pdev_stats_extra(const struct wmi_pdev_stats_extra *src, + struct ath10k_fw_stats_pdev *dst); +void ath10k_wmi_pull_peer_stats(const struct wmi_peer_stats *src, + struct ath10k_fw_stats_peer *dst); +void ath10k_wmi_put_host_mem_chunks(struct ath10k *ar, + struct wmi_host_mem_chunks *chunks); +void ath10k_wmi_put_start_scan_common(struct wmi_start_scan_common *cmn, + const struct wmi_start_scan_arg *arg); +void ath10k_wmi_set_wmm_param(struct wmi_wmm_params *params, + const struct wmi_wmm_params_arg *arg); +void ath10k_wmi_put_wmi_channel(struct wmi_channel *ch, + const struct wmi_channel_arg *arg); +int ath10k_wmi_start_scan_verify(const struct wmi_start_scan_arg *arg); + +int ath10k_wmi_event_scan(struct ath10k *ar, struct sk_buff *skb); +int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_chan_info(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_echo(struct ath10k *ar, struct sk_buff *skb); +int ath10k_wmi_event_debug_mesg(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_update_stats(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_vdev_start_resp(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_vdev_stopped(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_peer_sta_kickout(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_host_swba(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_tbttoffset_update(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_dfs(struct ath10k *ar, + const struct wmi_phyerr *phyerr, u64 tsf); +void ath10k_wmi_event_spectral_scan(struct ath10k *ar, + const struct wmi_phyerr *phyerr, + u64 tsf); +void ath10k_wmi_event_phyerr(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_roam(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_profile_match(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_debug_print(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_pdev_qvit(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_wlan_profile_data(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_rtt_measurement_report(struct ath10k *ar, + struct sk_buff *skb); +void ath10k_wmi_event_tsf_measurement_report(struct ath10k *ar, + struct sk_buff *skb); +void ath10k_wmi_event_rtt_error_report(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_wow_wakeup_host(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_dcs_interference(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_pdev_tpc_config(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_pdev_ftm_intg(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_gtk_offload_status(struct ath10k *ar, + struct sk_buff *skb); +void ath10k_wmi_event_gtk_rekey_fail(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_delba_complete(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_addba_complete(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_vdev_install_key_complete(struct ath10k *ar, + struct sk_buff *skb); +void ath10k_wmi_event_inst_rssi_stats(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_vdev_standby_req(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_vdev_resume_req(struct ath10k *ar, struct sk_buff *skb); +void ath10k_wmi_event_service_ready(struct ath10k *ar, struct sk_buff *skb); +int ath10k_wmi_event_ready(struct ath10k *ar, struct sk_buff *skb); #endif /* _WMI_H_ */ diff --git a/drivers/net/wireless/ath/ath5k/ahb.c b/drivers/net/wireless/ath/ath5k/ahb.c index 8f387cf67340..2ca88b593e4c 100644 --- a/drivers/net/wireless/ath/ath5k/ahb.c +++ b/drivers/net/wireless/ath/ath5k/ahb.c @@ -227,7 +227,6 @@ static struct platform_driver ath_ahb_driver = { .remove = ath_ahb_remove, .driver = { .name = "ar231x-wmac", - .owner = THIS_MODULE, }, }; diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index 19eab2a69ad5..3b4a6463d87a 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -672,10 +672,10 @@ ath5k_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey) spin_lock_bh(&common->cc_lock); ath_hw_cycle_counters_update(common); if (cc->cycles > 0) { - ah->survey.channel_time += cc->cycles / div; - ah->survey.channel_time_busy += cc->rx_busy / div; - ah->survey.channel_time_rx += cc->rx_frame / div; - ah->survey.channel_time_tx += cc->tx_frame / div; + ah->survey.time += cc->cycles / div; + ah->survey.time_busy += cc->rx_busy / div; + ah->survey.time_rx += cc->rx_frame / div; + ah->survey.time_tx += cc->tx_frame / div; } memset(cc, 0, sizeof(*cc)); spin_unlock_bh(&common->cc_lock); @@ -686,10 +686,10 @@ ath5k_get_survey(struct ieee80211_hw *hw, int idx, struct survey_info *survey) survey->noise = ah->ah_noise_floor; survey->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_IN_USE | - SURVEY_INFO_CHANNEL_TIME | - SURVEY_INFO_CHANNEL_TIME_BUSY | - SURVEY_INFO_CHANNEL_TIME_RX | - SURVEY_INFO_CHANNEL_TIME_TX; + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_TX; return 0; } diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index c60d36aa13e2..bf29da5e90da 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -912,6 +912,7 @@ ath5k_hw_set_opmode(struct ath5k_hw *ah, enum nl80211_iftype op_mode) pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE | (ah->ah_version == AR5K_AR5210 ? AR5K_STA_ID1_PWR_SV : 0); + /* fall through */ case NL80211_IFTYPE_MONITOR: pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE | (ah->ah_version == AR5K_AR5210 ? diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c index a3399c4f13a9..b9b651ea9851 100644 --- a/drivers/net/wireless/ath/ath5k/reset.c +++ b/drivers/net/wireless/ath/ath5k/reset.c @@ -478,7 +478,7 @@ ath5k_hw_wisoc_reset(struct ath5k_hw *ah, u32 flags) regval = ioread32(reg); iowrite32(regval | val, reg); regval = ioread32(reg); - usleep_range(100, 150); + udelay(100); /* NB: should be atomic */ /* Bring BB/MAC out of reset */ iowrite32(regval & ~val, reg); diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 7a5337877a0c..85da63a67faf 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -1799,20 +1799,20 @@ static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev, if (vif->target_stats.rx_byte) { sinfo->rx_bytes = vif->target_stats.rx_byte; - sinfo->filled |= STATION_INFO_RX_BYTES64; + sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES64); sinfo->rx_packets = vif->target_stats.rx_pkt; - sinfo->filled |= STATION_INFO_RX_PACKETS; + sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS); } if (vif->target_stats.tx_byte) { sinfo->tx_bytes = vif->target_stats.tx_byte; - sinfo->filled |= STATION_INFO_TX_BYTES64; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES64); sinfo->tx_packets = vif->target_stats.tx_pkt; - sinfo->filled |= STATION_INFO_TX_PACKETS; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS); } sinfo->signal = vif->target_stats.cs_rssi; - sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); rate = vif->target_stats.tx_ucast_rate; @@ -1827,6 +1827,7 @@ static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev, } sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; + sinfo->txrate.bw = RATE_INFO_BW_20; } else if (is_rate_ht40(rate, &mcs, &sgi)) { if (sgi) { sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; @@ -1835,7 +1836,7 @@ static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev, sinfo->txrate.mcs = mcs; } - sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + sinfo->txrate.bw = RATE_INFO_BW_40; sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS; } else { ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, @@ -1844,12 +1845,12 @@ static int ath6kl_get_station(struct wiphy *wiphy, struct net_device *dev, return 0; } - sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); if (test_bit(CONNECTED, &vif->flags) && test_bit(DTIM_PERIOD_AVAIL, &vif->flags) && vif->nw_type == INFRA_NETWORK) { - sinfo->filled |= STATION_INFO_BSS_PARAM; + sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM); sinfo->bss_param.flags = 0; sinfo->bss_param.dtim_period = vif->assoc_bss_dtim_period; sinfo->bss_param.beacon_interval = vif->assoc_bss_beacon_int; diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 933aef025698..b42ba46b5030 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -488,7 +488,6 @@ void ath6kl_connect_ap_mode_sta(struct ath6kl_vif *vif, u16 aid, u8 *mac_addr, sinfo.assoc_req_ies = ies; sinfo.assoc_req_ies_len = ies_len; - sinfo.filled |= STATION_INFO_ASSOC_REQ_IES; cfg80211_new_sta(vif->ndev, mac_addr, &sinfo, GFP_KERNEL); diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c index e000c4c27881..bd4a1a655f42 100644 --- a/drivers/net/wireless/ath/ath9k/ahb.c +++ b/drivers/net/wireless/ath/ath9k/ahb.c @@ -43,6 +43,10 @@ static const struct platform_device_id ath9k_platform_id_table[] = { .name = "qca953x_wmac", .driver_data = AR9300_DEVID_AR953X, }, + { + .name = "qca956x_wmac", + .driver_data = AR9300_DEVID_QCA956X, + }, {}, }; diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c index ba502a2d199b..ca01d17d130f 100644 --- a/drivers/net/wireless/ath/ath9k/ani.c +++ b/drivers/net/wireless/ath/ath9k/ani.c @@ -259,7 +259,8 @@ static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel, entry_cck->fir_step_level); /* Skip MRC CCK for pre AR9003 families */ - if (!AR_SREV_9300_20_OR_LATER(ah) || AR_SREV_9485(ah) || AR_SREV_9565(ah)) + if (!AR_SREV_9300_20_OR_LATER(ah) || AR_SREV_9485(ah) || + AR_SREV_9565(ah) || AR_SREV_9561(ah)) return; if (aniState->mrcCCK != entry_cck->mrc_cck_on) diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c index 5829074208fa..f273427fdd29 100644 --- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c @@ -22,6 +22,21 @@ /* All code below is for AR5008, AR9001, AR9002 */ +#define AR5008_OFDM_RATES 8 +#define AR5008_HT_SS_RATES 8 +#define AR5008_HT_DS_RATES 8 + +#define AR5008_HT20_SHIFT 16 +#define AR5008_HT40_SHIFT 24 + +#define AR5008_11NA_OFDM_SHIFT 0 +#define AR5008_11NA_HT_SS_SHIFT 8 +#define AR5008_11NA_HT_DS_SHIFT 16 + +#define AR5008_11NG_OFDM_SHIFT 4 +#define AR5008_11NG_HT_SS_SHIFT 12 +#define AR5008_11NG_HT_DS_SHIFT 20 + static const int firstep_table[] = /* level: 0 1 2 3 4 5 6 7 8 */ { -4, -2, 0, 2, 4, 6, 8, 10, 12 }; /* lvl 0-8, default 2 */ @@ -1235,6 +1250,71 @@ static void ar5008_hw_set_radar_conf(struct ath_hw *ah) conf->radar_inband = 8; } +static void ar5008_hw_init_txpower_cck(struct ath_hw *ah, int16_t *rate_array) +{ +#define CCK_DELTA(x) ((OLC_FOR_AR9280_20_LATER) ? max((x) - 2, 0) : (x)) + ah->tx_power[0] = CCK_DELTA(rate_array[rate1l]); + ah->tx_power[1] = CCK_DELTA(min(rate_array[rate2l], + rate_array[rate2s])); + ah->tx_power[2] = CCK_DELTA(min(rate_array[rate5_5l], + rate_array[rate5_5s])); + ah->tx_power[3] = CCK_DELTA(min(rate_array[rate11l], + rate_array[rate11s])); +#undef CCK_DELTA +} + +static void ar5008_hw_init_txpower_ofdm(struct ath_hw *ah, int16_t *rate_array, + int offset) +{ + int i, idx = 0; + + for (i = offset; i < offset + AR5008_OFDM_RATES; i++) { + ah->tx_power[i] = rate_array[idx]; + idx++; + } +} + +static void ar5008_hw_init_txpower_ht(struct ath_hw *ah, int16_t *rate_array, + int ss_offset, int ds_offset, + bool is_40, int ht40_delta) +{ + int i, mcs_idx = (is_40) ? AR5008_HT40_SHIFT : AR5008_HT20_SHIFT; + + for (i = ss_offset; i < ss_offset + AR5008_HT_SS_RATES; i++) { + ah->tx_power[i] = rate_array[mcs_idx] + ht40_delta; + mcs_idx++; + } + memcpy(&ah->tx_power[ds_offset], &ah->tx_power[ss_offset], + AR5008_HT_SS_RATES); +} + +void ar5008_hw_init_rate_txpower(struct ath_hw *ah, int16_t *rate_array, + struct ath9k_channel *chan, int ht40_delta) +{ + if (IS_CHAN_5GHZ(chan)) { + ar5008_hw_init_txpower_ofdm(ah, rate_array, + AR5008_11NA_OFDM_SHIFT); + if (IS_CHAN_HT20(chan) || IS_CHAN_HT40(chan)) { + ar5008_hw_init_txpower_ht(ah, rate_array, + AR5008_11NA_HT_SS_SHIFT, + AR5008_11NA_HT_DS_SHIFT, + IS_CHAN_HT40(chan), + ht40_delta); + } + } else { + ar5008_hw_init_txpower_cck(ah, rate_array); + ar5008_hw_init_txpower_ofdm(ah, rate_array, + AR5008_11NG_OFDM_SHIFT); + if (IS_CHAN_HT20(chan) || IS_CHAN_HT40(chan)) { + ar5008_hw_init_txpower_ht(ah, rate_array, + AR5008_11NG_HT_SS_SHIFT, + AR5008_11NG_HT_DS_SHIFT, + IS_CHAN_HT40(chan), + ht40_delta); + } + } +} + int ar5008_hw_attach_phy_ops(struct ath_hw *ah) { struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 06ab71db6e80..174442beb952 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -1203,24 +1203,41 @@ static void ar9003_hw_tx_iq_cal_reload(struct ath_hw *ah) static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g) { int offset[8] = {0}, total = 0, test; - int agc_out, i; + int agc_out, i, peak_detect_threshold; + if (AR_SREV_9550(ah) || AR_SREV_9531(ah)) + peak_detect_threshold = 8; + else + peak_detect_threshold = 0; + + /* + * Turn off LNA/SW. + */ REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), AR_PHY_65NM_RXRF_GAINSTAGES_RX_OVERRIDE, 0x1); REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), AR_PHY_65NM_RXRF_GAINSTAGES_LNAON_CALDC, 0x0); - if (is_2g) - REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), - AR_PHY_65NM_RXRF_GAINSTAGES_LNA2G_GAIN_OVR, 0x0); - else - REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), - AR_PHY_65NM_RXRF_GAINSTAGES_LNA5G_GAIN_OVR, 0x0); + if (AR_SREV_9003_PCOEM(ah) || AR_SREV_9330_11(ah)) { + if (is_2g) + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), + AR_PHY_65NM_RXRF_GAINSTAGES_LNA2G_GAIN_OVR, 0x0); + else + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), + AR_PHY_65NM_RXRF_GAINSTAGES_LNA5G_GAIN_OVR, 0x0); + } + + /* + * Turn off RXON. + */ REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain), AR_PHY_65NM_RXTX2_RXON_OVR, 0x1); REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain), AR_PHY_65NM_RXTX2_RXON, 0x0); + /* + * Turn on AGC for cal. + */ REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE, 0x1); REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), @@ -1228,16 +1245,19 @@ 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_AGC_CAL_OVR, 0x1); - if (AR_SREV_9330_11(ah)) { + if (AR_SREV_9330_11(ah)) REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, 0x0); - } else { + + if (AR_SREV_9003_PCOEM(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) { if (is_2g) REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), - AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR, 0x0); + AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR, + peak_detect_threshold); else REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), - AR_PHY_65NM_RXRF_AGC_AGC5G_DBDAC_OVR, 0x0); + AR_PHY_65NM_RXRF_AGC_AGC5G_DBDAC_OVR, + peak_detect_threshold); } for (i = 6; i > 0; i--) { @@ -1266,10 +1286,19 @@ 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_AGC5G_CALDAC_OVR, total); + /* + * Turn on LNA. + */ REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), AR_PHY_65NM_RXRF_GAINSTAGES_RX_OVERRIDE, 0); + /* + * Turn off RXON. + */ REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain), AR_PHY_65NM_RXTX2_RXON_OVR, 0); + /* + * Turn off peak detect calibration. + */ REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR, 0); } @@ -1611,8 +1640,14 @@ 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)) - ar9003_hw_manual_peak_cal(ah, 0, IS_CHAN_2GHZ(chan)); + if (AR_SREV_9330_11(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah)) { + for (i = 0; i < AR9300_MAX_CHAINS; i++) { + if (!(ah->rxchainmask & (1 << i))) + continue; + ar9003_hw_manual_peak_cal(ah, i, + IS_CHAN_2GHZ(chan)); + } + } /* * For non-AR9550 chips, we just trigger AGC calibration diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c index 08225a0067c2..8b4561e8ce1a 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c @@ -3536,7 +3536,7 @@ static void ar9003_hw_xpa_bias_level_apply(struct ath_hw *ah, bool is2ghz) int bias = ar9003_modal_header(ah, is2ghz)->xpaBiasLvl; if (AR_SREV_9485(ah) || AR_SREV_9330(ah) || AR_SREV_9340(ah) || - AR_SREV_9531(ah)) + AR_SREV_9531(ah) || AR_SREV_9561(ah)) REG_RMW_FIELD(ah, AR_CH0_TOP2, AR_CH0_TOP2_XPABIASLVL, bias); else if (AR_SREV_9462(ah) || AR_SREV_9550(ah) || AR_SREV_9565(ah)) REG_RMW_FIELD(ah, AR_CH0_TOP, AR_CH0_TOP_XPABIASLVL, bias); @@ -3599,7 +3599,7 @@ static void ar9003_hw_ant_ctrl_apply(struct ath_hw *ah, bool is2ghz) if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM, AR_SWITCH_TABLE_COM_AR9462_ALL, value); - } else if (AR_SREV_9550(ah) || AR_SREV_9531(ah)) { + } else if (AR_SREV_9550(ah) || AR_SREV_9531(ah) || AR_SREV_9561(ah)) { REG_RMW_FIELD(ah, AR_PHY_SWITCH_COM, AR_SWITCH_TABLE_COM_AR9550_ALL, value); } else @@ -3929,9 +3929,13 @@ void ar9003_hw_internal_regulator_apply(struct ath_hw *ah) REG_WRITE(ah, AR_PHY_PMU2, reg_pmu_set); if (!is_pmu_set(ah, AR_PHY_PMU2, reg_pmu_set)) return; - } else if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { + } else if (AR_SREV_9462(ah) || AR_SREV_9565(ah) || + AR_SREV_9561(ah)) { reg_val = le32_to_cpu(pBase->swreg); REG_WRITE(ah, AR_PHY_PMU1, reg_val); + + if (AR_SREV_9561(ah)) + REG_WRITE(ah, AR_PHY_PMU2, 0x10200000); } else { /* Internal regulator is ON. Write swreg register. */ reg_val = le32_to_cpu(pBase->swreg); @@ -4034,7 +4038,8 @@ static void ar9003_hw_xpa_timing_control_apply(struct ath_hw *ah, bool is2ghz) if (!AR_SREV_9300(ah) && !AR_SREV_9340(ah) && !AR_SREV_9580(ah) && - !AR_SREV_9531(ah)) + !AR_SREV_9531(ah) && + !AR_SREV_9561(ah)) return; xpa_ctl = ar9003_modal_header(ah, is2ghz)->txFrameToXpaOn; @@ -4812,7 +4817,7 @@ static void ar9003_hw_power_control_override(struct ath_hw *ah, } tempslope: - if (AR_SREV_9550(ah) || AR_SREV_9531(ah)) { + if (AR_SREV_9550(ah) || AR_SREV_9531(ah) || AR_SREV_9561(ah)) { u8 txmask = (eep->baseEepHeader.txrxMask & 0xf0) >> 4; /* diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index 06ad2172030e..4335ccbe7d7e 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -29,6 +29,7 @@ #include "ar9565_1p0_initvals.h" #include "ar9565_1p1_initvals.h" #include "ar953x_initvals.h" +#include "ar956x_initvals.h" /* General hardware code for the AR9003 hadware family */ @@ -358,6 +359,40 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) INIT_INI_ARRAY(&ah->iniModesFastClock, qca953x_1p0_modes_fast_clock); + } else if (AR_SREV_9561(ah)) { + INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], + qca956x_1p0_mac_core); + INIT_INI_ARRAY(&ah->iniMac[ATH_INI_POST], + qca956x_1p0_mac_postamble); + + INIT_INI_ARRAY(&ah->iniBB[ATH_INI_CORE], + qca956x_1p0_baseband_core); + INIT_INI_ARRAY(&ah->iniBB[ATH_INI_POST], + qca956x_1p0_baseband_postamble); + + INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_CORE], + qca956x_1p0_radio_core); + INIT_INI_ARRAY(&ah->iniRadio[ATH_INI_POST], + qca956x_1p0_radio_postamble); + + INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_PRE], + qca956x_1p0_soc_preamble); + INIT_INI_ARRAY(&ah->iniSOC[ATH_INI_POST], + qca956x_1p0_soc_postamble); + + INIT_INI_ARRAY(&ah->iniModesRxGain, + qca956x_1p0_common_wo_xlna_rx_gain_table); + INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds, + qca956x_1p0_common_wo_xlna_rx_gain_bounds); + INIT_INI_ARRAY(&ah->iniModesTxGain, + qca956x_1p0_modes_no_xpa_tx_gain_table); + + INIT_INI_ARRAY(&ah->ini_dfs, + qca956x_1p0_baseband_postamble_dfs_channel); + INIT_INI_ARRAY(&ah->iniCckfirJapan2484, + qca956x_1p0_baseband_core_txfir_coeff_japan_2484); + INIT_INI_ARRAY(&ah->iniModesFastClock, + qca956x_1p0_modes_fast_clock); } else if (AR_SREV_9580(ah)) { /* mac */ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], @@ -544,6 +579,9 @@ static void ar9003_tx_gain_table_mode0(struct ath_hw *ah) else if (AR_SREV_9531_20(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, qca953x_2p0_modes_xpa_tx_gain_table); + else if (AR_SREV_9561(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + qca956x_1p0_modes_xpa_tx_gain_table); else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9580_1p0_lowest_ob_db_tx_gain_table); @@ -594,7 +632,10 @@ static void ar9003_tx_gain_table_mode1(struct ath_hw *ah) else INIT_INI_ARRAY(&ah->iniModesTxGain, qca953x_1p0_modes_no_xpa_tx_gain_table); - } else if (AR_SREV_9462_21(ah)) + } else if (AR_SREV_9561(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + qca956x_1p0_modes_no_xpa_tx_gain_table); + else if (AR_SREV_9462_21(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9462_2p1_modes_high_ob_db_tx_gain); else if (AR_SREV_9462_20(ah)) @@ -628,6 +669,9 @@ static void ar9003_tx_gain_table_mode2(struct ath_hw *ah) else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9580_1p0_low_ob_db_tx_gain_table); + else if (AR_SREV_9561(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + qca956x_1p0_modes_no_xpa_low_ob_db_tx_gain_table); else if (AR_SREV_9565_11(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9565_1p1_modes_low_ob_db_tx_gain_table); @@ -699,6 +743,9 @@ static void ar9003_tx_gain_table_mode5(struct ath_hw *ah) else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9580_1p0_type5_tx_gain_table); + else if (AR_SREV_9561(ah)) + INIT_INI_ARRAY(&ah->iniModesTxGain, + qca956x_1p0_modes_no_xpa_green_tx_gain_table); else if (AR_SREV_9300_22(ah)) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9300Modes_type5_tx_gain_table_2p2); @@ -770,6 +817,13 @@ static void ar9003_rx_gain_table_mode0(struct ath_hw *ah) qca953x_1p0_common_rx_gain_table); INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds, qca953x_1p0_common_rx_gain_bounds); + } else if (AR_SREV_9561(ah)) { + INIT_INI_ARRAY(&ah->iniModesRxGain, + qca956x_1p0_common_rx_gain_table); + INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds, + qca956x_1p0_common_rx_gain_bounds); + INIT_INI_ARRAY(&ah->ini_modes_rxgain_5g_xlna, + qca956x_1p0_xlna_only); } else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9580_1p0_rx_gain_table); @@ -825,6 +879,11 @@ static void ar9003_rx_gain_table_mode1(struct ath_hw *ah) qca953x_2p0_common_wo_xlna_rx_gain_table); INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds, qca953x_2p0_common_wo_xlna_rx_gain_bounds); + } else if (AR_SREV_9561(ah)) { + INIT_INI_ARRAY(&ah->iniModesRxGain, + qca956x_1p0_common_wo_xlna_rx_gain_table); + INIT_INI_ARRAY(&ah->ini_modes_rx_gain_bounds, + qca956x_1p0_common_wo_xlna_rx_gain_bounds); } else if (AR_SREV_9580(ah)) INIT_INI_ARRAY(&ah->iniModesRxGain, ar9580_1p0_wo_xlna_rx_gain_table); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index ae6cde273414..1ad66b76749b 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -183,7 +183,8 @@ static int ar9003_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) } else { channelSel = CHANSEL_2G(freq) >> 1; } - } else if (AR_SREV_9550(ah) || AR_SREV_9531(ah)) { + } else if (AR_SREV_9550(ah) || AR_SREV_9531(ah) || + AR_SREV_9561(ah)) { if (ah->is_clk_25mhz) div = 75; else @@ -198,7 +199,8 @@ static int ar9003_hw_set_channel(struct ath_hw *ah, struct ath9k_channel *chan) /* Set to 2G mode */ bMode = 1; } else { - if ((AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) && + if ((AR_SREV_9340(ah) || AR_SREV_9550(ah) || + AR_SREV_9531(ah) || AR_SREV_9561(ah)) && ah->is_clk_25mhz) { channelSel = freq / 75; chan_frac = ((freq % 75) * 0x20000) / 75; @@ -265,7 +267,7 @@ static void ar9003_hw_spur_mitigate_mrc_cck(struct ath_hw *ah, */ if (AR_SREV_9485(ah) || AR_SREV_9340(ah) || AR_SREV_9330(ah) || - AR_SREV_9550(ah)) { + AR_SREV_9550(ah) || AR_SREV_9561(ah)) { if (spur_fbin_ptr[0] == 0) /* No spur */ return; max_spur_cnts = 5; @@ -292,7 +294,7 @@ static void ar9003_hw_spur_mitigate_mrc_cck(struct ath_hw *ah, negative = 0; if (AR_SREV_9485(ah) || AR_SREV_9340(ah) || AR_SREV_9330(ah) || - AR_SREV_9550(ah)) + AR_SREV_9550(ah) || AR_SREV_9561(ah)) cur_bb_spur = ath9k_hw_fbin2freq(spur_fbin_ptr[i], IS_CHAN_2GHZ(chan)); else @@ -641,8 +643,10 @@ static void ar9003_hw_set_channel_regs(struct ath_hw *ah, (REG_READ(ah, AR_PHY_GEN_CTRL) & AR_PHY_GC_ENABLE_DAC_FIFO); /* Enable 11n HT, 20 MHz */ - phymode = AR_PHY_GC_HT_EN | AR_PHY_GC_SINGLE_HT_LTF1 | - AR_PHY_GC_SHORT_GI_40 | enableDacFifo; + phymode = AR_PHY_GC_HT_EN | AR_PHY_GC_SHORT_GI_40 | enableDacFifo; + + if (!AR_SREV_9561(ah)) + phymode |= AR_PHY_GC_SINGLE_HT_LTF1; /* Configure baseband for dynamic 20/40 operation */ if (IS_CHAN_HT40(chan)) { @@ -745,7 +749,8 @@ static void ar9003_hw_override_ini(struct ath_hw *ah) else ah->enabled_cals &= ~TX_CL_CAL; - if (AR_SREV_9340(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah)) { + if (AR_SREV_9340(ah) || AR_SREV_9531(ah) || AR_SREV_9550(ah) || + AR_SREV_9561(ah)) { if (ah->is_clk_25mhz) { REG_WRITE(ah, AR_RTC_DERIVED_CLK, 0x17c << 1); REG_WRITE(ah, AR_SLP32_MODE, 0x0010f3d7); @@ -812,6 +817,19 @@ static int ar9550_hw_get_modes_txgain_index(struct ath_hw *ah, return ret; } +static int ar9561_hw_get_modes_txgain_index(struct ath_hw *ah, + struct ath9k_channel *chan) +{ + if (IS_CHAN_2GHZ(chan)) { + if (IS_CHAN_HT40(chan)) + return 1; + else + return 2; + } + + return 0; +} + static void ar9003_doubler_fix(struct ath_hw *ah) { if (AR_SREV_9300(ah) || AR_SREV_9580(ah) || AR_SREV_9550(ah)) { @@ -911,21 +929,29 @@ static int ar9003_hw_process_ini(struct ath_hw *ah, REG_WRITE_ARRAY(&ah->ini_modes_rxgain_5g_xlna, modesIndex, regWrites); } + + if (AR_SREV_9561(ah) && (ar9003_hw_get_rx_gain_idx(ah) == 0)) + REG_WRITE_ARRAY(&ah->ini_modes_rxgain_5g_xlna, + modesIndex, regWrites); } - if (AR_SREV_9550(ah)) + if (AR_SREV_9550(ah) || AR_SREV_9561(ah)) REG_WRITE_ARRAY(&ah->ini_modes_rx_gain_bounds, modesIndex, regWrites); /* * TXGAIN initvals. */ - if (AR_SREV_9550(ah) || AR_SREV_9531(ah)) { + if (AR_SREV_9550(ah) || AR_SREV_9531(ah) || AR_SREV_9561(ah)) { int modes_txgain_index = 1; if (AR_SREV_9550(ah)) modes_txgain_index = ar9550_hw_get_modes_txgain_index(ah, chan); + if (AR_SREV_9561(ah)) + modes_txgain_index = + ar9561_hw_get_modes_txgain_index(ah, chan); + if (modes_txgain_index < 0) return -EINVAL; @@ -1989,7 +2015,8 @@ void ar9003_hw_attach_phy_ops(struct ath_hw *ah) priv_ops->rf_set_freq = ar9003_hw_set_channel; priv_ops->spur_mitigate_freq = ar9003_hw_spur_mitigate; - if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) + if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah) || + AR_SREV_9561(ah)) priv_ops->compute_pll_control = ar9003_hw_compute_pll_control_soc; else priv_ops->compute_pll_control = ar9003_hw_compute_pll_control; diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h index fd090b1f2d0f..c311b2bfdb00 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h @@ -454,7 +454,7 @@ #define AR_PHY_GEN_CTRL (AR_SM_BASE + 0x4) #define AR_PHY_MODE (AR_SM_BASE + 0x8) #define AR_PHY_ACTIVE (AR_SM_BASE + 0xc) -#define AR_PHY_SPUR_MASK_A (AR_SM_BASE + 0x20) +#define AR_PHY_SPUR_MASK_A (AR_SM_BASE + (AR_SREV_9561(ah) ? 0x18 : 0x20)) #define AR_PHY_SPUR_MASK_B (AR_SM_BASE + 0x24) #define AR_PHY_SPECTRAL_SCAN (AR_SM_BASE + 0x28) #define AR_PHY_RADAR_BW_FILTER (AR_SM_BASE + 0x2c) @@ -506,7 +506,7 @@ #define AR_PHY_TEST_CHAIN_SEL 0xC0000000 #define AR_PHY_TEST_CHAIN_SEL_S 30 -#define AR_PHY_TEST_CTL_STATUS (AR_SM_BASE + 0x164) +#define AR_PHY_TEST_CTL_STATUS (AR_SM_BASE + (AR_SREV_9561(ah) ? 0x160 : 0x164)) #define AR_PHY_TEST_CTL_TSTDAC_EN 0x1 #define AR_PHY_TEST_CTL_TSTDAC_EN_S 0 #define AR_PHY_TEST_CTL_TX_OBS_SEL 0x1C @@ -525,7 +525,7 @@ #define AR_PHY_CHAN_STATUS (AR_SM_BASE + 0x16c) -#define AR_PHY_CHAN_INFO_MEMORY (AR_SM_BASE + 0x170) +#define AR_PHY_CHAN_INFO_MEMORY (AR_SM_BASE + (AR_SREV_9561(ah) ? 0x16c : 0x170)) #define AR_PHY_CHAN_INFO_MEMORY_CHANINFOMEM_S2_READ 0x00000008 #define AR_PHY_CHAN_INFO_MEMORY_CHANINFOMEM_S2_READ_S 3 @@ -536,7 +536,7 @@ #define AR_PHY_SCRAMBLER_SEED (AR_SM_BASE + 0x190) #define AR_PHY_CCK_TX_CTRL (AR_SM_BASE + 0x194) -#define AR_PHY_HEAVYCLIP_CTL (AR_SM_BASE + 0x1a4) +#define AR_PHY_HEAVYCLIP_CTL (AR_SM_BASE + (AR_SREV_9561(ah) ? 0x198 : 0x1a4)) #define AR_PHY_HEAVYCLIP_20 (AR_SM_BASE + 0x1a8) #define AR_PHY_HEAVYCLIP_40 (AR_SM_BASE + 0x1ac) #define AR_PHY_ILLEGAL_TXRATE (AR_SM_BASE + 0x1b0) @@ -726,21 +726,24 @@ #define AR_CH0_TOP2 (AR_SREV_9300(ah) ? 0x1628c : \ (AR_SREV_9462(ah) ? 0x16290 : 0x16284)) -#define AR_CH0_TOP2_XPABIASLVL 0xf000 +#define AR_CH0_TOP2_XPABIASLVL (AR_SREV_9561(ah) ? 0x1e00 : 0xf000) #define AR_CH0_TOP2_XPABIASLVL_S 12 #define AR_CH0_XTAL (AR_SREV_9300(ah) ? 0x16294 : \ - ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x16298 : 0x16290)) + ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x16298 : \ + (AR_SREV_9561(ah) ? 0x162c0 : 0x16290))) #define AR_CH0_XTAL_CAPINDAC 0x7f000000 #define AR_CH0_XTAL_CAPINDAC_S 24 #define AR_CH0_XTAL_CAPOUTDAC 0x00fe0000 #define AR_CH0_XTAL_CAPOUTDAC_S 17 -#define AR_PHY_PMU1 ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x16340 : 0x16c40) +#define AR_PHY_PMU1 ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x16340 : \ + (AR_SREV_9561(ah) ? 0x16cc0 : 0x16c40)) #define AR_PHY_PMU1_PWD 0x1 #define AR_PHY_PMU1_PWD_S 0 -#define AR_PHY_PMU2 ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x16344 : 0x16c44) +#define AR_PHY_PMU2 ((AR_SREV_9462(ah) || AR_SREV_9565(ah)) ? 0x16344 : \ + (AR_SREV_9561(ah) ? 0x16cc4 : 0x16c44)) #define AR_PHY_PMU2_PGM 0x00200000 #define AR_PHY_PMU2_PGM_S 21 diff --git a/drivers/net/wireless/ath/ath9k/ar9003_wow.c b/drivers/net/wireless/ath/ath9k/ar9003_wow.c index 81c88dd606dc..86bfc9604dca 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_wow.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_wow.c @@ -17,23 +17,9 @@ #include <linux/export.h> #include "ath9k.h" #include "reg.h" +#include "reg_wow.h" #include "hw-ops.h" -const char *ath9k_hw_wow_event_to_string(u32 wow_event) -{ - if (wow_event & AH_WOW_MAGIC_PATTERN_EN) - return "Magic pattern"; - if (wow_event & AH_WOW_USER_PATTERN_EN) - return "User pattern"; - if (wow_event & AH_WOW_LINK_CHANGE) - return "Link change"; - if (wow_event & AH_WOW_BEACON_MISS) - return "Beacon miss"; - - return "unknown reason"; -} -EXPORT_SYMBOL(ath9k_hw_wow_event_to_string); - static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); @@ -49,6 +35,15 @@ static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah) return; } + if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { + if (!REG_READ(ah, AR_MAC_PCU_GEN_TIMER_TSF_SEL)) + REG_CLR_BIT(ah, AR_DIRECT_CONNECT, AR_DC_TSF2_ENABLE); + } else if (AR_SREV_9485(ah)){ + if (!(REG_READ(ah, AR_NDP2_TIMER_MODE) & + AR_GEN_TIMERS2_MODE_ENABLE_MASK)) + REG_CLR_BIT(ah, AR_DIRECT_CONNECT, AR_DC_TSF2_ENABLE); + } + REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT); } @@ -67,11 +62,15 @@ static void ath9k_wow_create_keep_alive_pattern(struct ath_hw *ah) /* set the transmit buffer */ ctl[0] = (KAL_FRAME_LEN | (MAX_RATE_POWER << 16)); ctl[1] = 0; - ctl[3] = 0xb; /* OFDM_6M hardware value for this rate */ ctl[4] = 0; ctl[7] = (ah->txchainmask) << 2; ctl[2] = 0xf << 16; /* tx_tries 0 */ + if (IS_CHAN_2GHZ(ah->curchan)) + ctl[3] = 0x1b; /* CCK_1M */ + else + ctl[3] = 0xb; /* OFDM_6M */ + for (i = 0; i < KAL_NUM_DESC_WORDS; i++) REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]); @@ -103,21 +102,22 @@ static void ath9k_wow_create_keep_alive_pattern(struct ath_hw *ah) } -void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern, - u8 *user_mask, int pattern_count, - int pattern_len) +int ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern, + u8 *user_mask, int pattern_count, + int pattern_len) { int i; u32 pattern_val, mask_val; u32 set, clr; - /* FIXME: should check count by querying the hardware capability */ - if (pattern_count >= MAX_NUM_PATTERN) - return; + if (pattern_count >= ah->wow.max_patterns) + return -ENOSPC; - REG_SET_BIT(ah, AR_WOW_PATTERN, BIT(pattern_count)); + if (pattern_count < MAX_NUM_PATTERN_LEGACY) + REG_SET_BIT(ah, AR_WOW_PATTERN, BIT(pattern_count)); + else + REG_SET_BIT(ah, AR_MAC_PCU_WOW4, BIT(pattern_count - 8)); - /* set the registers for pattern */ for (i = 0; i < MAX_PATTERN_SIZE; i += 4) { memcpy(&pattern_val, user_pattern, 4); REG_WRITE(ah, (AR_WOW_TB_PATTERN(pattern_count) + i), @@ -125,49 +125,42 @@ void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern, user_pattern += 4; } - /* set the registers for mask */ for (i = 0; i < MAX_PATTERN_MASK_SIZE; i += 4) { memcpy(&mask_val, user_mask, 4); REG_WRITE(ah, (AR_WOW_TB_MASK(pattern_count) + i), mask_val); user_mask += 4; } - /* set the pattern length to be matched - * - * AR_WOW_LENGTH1_REG1 - * bit 31:24 pattern 0 length - * bit 23:16 pattern 1 length - * bit 15:8 pattern 2 length - * bit 7:0 pattern 3 length - * - * AR_WOW_LENGTH1_REG2 - * bit 31:24 pattern 4 length - * bit 23:16 pattern 5 length - * bit 15:8 pattern 6 length - * bit 7:0 pattern 7 length - * - * the below logic writes out the new - * pattern length for the corresponding - * pattern_count, while masking out the - * other fields - */ - - ah->wow_event_mask |= BIT(pattern_count + AR_WOW_PAT_FOUND_SHIFT); + if (pattern_count < MAX_NUM_PATTERN_LEGACY) + ah->wow.wow_event_mask |= + BIT(pattern_count + AR_WOW_PAT_FOUND_SHIFT); + else + ah->wow.wow_event_mask2 |= + BIT((pattern_count - 8) + AR_WOW_PAT_FOUND_SHIFT); if (pattern_count < 4) { - /* Pattern 0-3 uses AR_WOW_LENGTH1 register */ set = (pattern_len & AR_WOW_LENGTH_MAX) << AR_WOW_LEN1_SHIFT(pattern_count); clr = AR_WOW_LENGTH1_MASK(pattern_count); REG_RMW(ah, AR_WOW_LENGTH1, set, clr); - } else { - /* Pattern 4-7 uses AR_WOW_LENGTH2 register */ + } else if (pattern_count < 8) { set = (pattern_len & AR_WOW_LENGTH_MAX) << AR_WOW_LEN2_SHIFT(pattern_count); clr = AR_WOW_LENGTH2_MASK(pattern_count); REG_RMW(ah, AR_WOW_LENGTH2, set, clr); + } else if (pattern_count < 12) { + set = (pattern_len & AR_WOW_LENGTH_MAX) << + AR_WOW_LEN3_SHIFT(pattern_count); + clr = AR_WOW_LENGTH3_MASK(pattern_count); + REG_RMW(ah, AR_WOW_LENGTH3, set, clr); + } else if (pattern_count < MAX_NUM_PATTERN) { + set = (pattern_len & AR_WOW_LENGTH_MAX) << + AR_WOW_LEN4_SHIFT(pattern_count); + clr = AR_WOW_LENGTH4_MASK(pattern_count); + REG_RMW(ah, AR_WOW_LENGTH4, set, clr); } + return 0; } EXPORT_SYMBOL(ath9k_hw_wow_apply_pattern); @@ -189,7 +182,7 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah) * register. This mask will clean it up. */ - val &= ah->wow_event_mask; + val &= ah->wow.wow_event_mask; if (val) { if (val & AR_WOW_MAGIC_PAT_FOUND) @@ -233,190 +226,192 @@ u32 ath9k_hw_wow_wakeup(struct ath_hw *ah) if (ah->is_pciexpress) ath9k_hw_configpcipowersave(ah, false); - ah->wow_event_mask = 0; + ah->wow.wow_event_mask = 0; return wow_status; } EXPORT_SYMBOL(ath9k_hw_wow_wakeup); -void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable) +static void ath9k_hw_wow_set_arwr_reg(struct ath_hw *ah) { - u32 wow_event_mask; - u32 set, clr; + u32 wa_reg; - /* - * wow_event_mask is a mask to the AR_WOW_PATTERN register to - * indicate which WoW events we have enabled. The WoW events - * are from the 'pattern_enable' in this function and - * 'pattern_count' of ath9k_hw_wow_apply_pattern() - */ - wow_event_mask = ah->wow_event_mask; + if (!ah->is_pciexpress) + return; /* - * Untie Power-on-Reset from the PCI-E-Reset. When we are in - * WOW sleep, we do want the Reset from the PCI-E to disturb - * our hw state + * We need to untie the internal POR (power-on-reset) + * to the external PCI-E reset. We also need to tie + * the PCI-E Phy reset to the PCI-E reset. */ - if (ah->is_pciexpress) { - /* - * we need to untie the internal POR (power-on-reset) - * to the external PCI-E reset. We also need to tie - * the PCI-E Phy reset to the PCI-E reset. - */ - set = AR_WA_RESET_EN | AR_WA_POR_SHORT; - clr = AR_WA_UNTIE_RESET_EN | AR_WA_D3_L1_DISABLE; - REG_RMW(ah, AR_WA, set, clr); - } + wa_reg = REG_READ(ah, AR_WA); + wa_reg &= ~AR_WA_UNTIE_RESET_EN; + wa_reg |= AR_WA_RESET_EN; + wa_reg |= AR_WA_POR_SHORT; - /* - * set the power states appropriately and enable PME - */ - set = AR_PMCTRL_HOST_PME_EN | AR_PMCTRL_PWR_PM_CTRL_ENA | - AR_PMCTRL_AUX_PWR_DET | AR_PMCTRL_WOW_PME_CLR; + REG_WRITE(ah, AR_WA, wa_reg); +} + +void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable) +{ + u32 wow_event_mask; + u32 keep_alive, magic_pattern, host_pm_ctrl; + + wow_event_mask = ah->wow.wow_event_mask; /* - * set and clear WOW_PME_CLEAR registers for the chip + * AR_PMCTRL_HOST_PME_EN - Override PME enable in configuration + * space and allow MAC to generate WoW anyway. + * + * AR_PMCTRL_PWR_PM_CTRL_ENA - ??? + * + * AR_PMCTRL_AUX_PWR_DET - PCI core SYS_AUX_PWR_DET signal, + * needs to be set for WoW in PCI mode. + * + * AR_PMCTRL_WOW_PME_CLR - WoW Clear Signal going to the MAC. + * + * Set the power states appropriately and enable PME. + * + * Set and clear WOW_PME_CLEAR for the chip * to generate next wow signal. */ - REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set); - clr = AR_PMCTRL_WOW_PME_CLR; - REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr); + REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PMCTRL_HOST_PME_EN | + AR_PMCTRL_PWR_PM_CTRL_ENA | + AR_PMCTRL_AUX_PWR_DET | + AR_PMCTRL_WOW_PME_CLR); + REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, AR_PMCTRL_WOW_PME_CLR); /* - * Setup for: - * - beacon misses - * - magic pattern - * - keep alive timeout - * - pattern matching + * Random Backoff. + * + * 31:28 in AR_WOW_PATTERN : Indicates the number of bits used in the + * contention window. For value N, + * the random backoff will be selected between + * 0 and (2 ^ N) - 1. */ + REG_SET_BIT(ah, AR_WOW_PATTERN, + AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF)); /* - * Program default values for pattern backoff, aifs/slot/KAL count, - * beacon miss timeout, KAL timeout, etc. + * AIFS time, Slot time, Keep Alive count. + */ + REG_SET_BIT(ah, AR_WOW_COUNT, AR_WOW_AIFS_CNT(AR_WOW_CNT_AIFS_CNT) | + AR_WOW_SLOT_CNT(AR_WOW_CNT_SLOT_CNT) | + AR_WOW_KEEP_ALIVE_CNT(AR_WOW_CNT_KA_CNT)); + /* + * Beacon timeout. */ - set = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF); - REG_SET_BIT(ah, AR_WOW_PATTERN, set); - - set = AR_WOW_AIFS_CNT(AR_WOW_CNT_AIFS_CNT) | - AR_WOW_SLOT_CNT(AR_WOW_CNT_SLOT_CNT) | - AR_WOW_KEEP_ALIVE_CNT(AR_WOW_CNT_KA_CNT); - REG_SET_BIT(ah, AR_WOW_COUNT, set); - if (pattern_enable & AH_WOW_BEACON_MISS) - set = AR_WOW_BEACON_TIMO; - /* We are not using beacon miss, program a large value */ + REG_WRITE(ah, AR_WOW_BCN_TIMO, AR_WOW_BEACON_TIMO); else - set = AR_WOW_BEACON_TIMO_MAX; - - REG_WRITE(ah, AR_WOW_BCN_TIMO, set); + REG_WRITE(ah, AR_WOW_BCN_TIMO, AR_WOW_BEACON_TIMO_MAX); /* - * Keep alive timo in ms except AR9280 + * Keep alive timeout in ms. */ if (!pattern_enable) - set = AR_WOW_KEEP_ALIVE_NEVER; + REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO, AR_WOW_KEEP_ALIVE_NEVER); else - set = KAL_TIMEOUT * 32; - - REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO, set); + REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO, KAL_TIMEOUT * 32); /* - * Keep alive delay in us. based on 'power on clock', - * therefore in usec + * Keep alive delay in us. */ - set = KAL_DELAY * 1000; - REG_WRITE(ah, AR_WOW_KEEP_ALIVE_DELAY, set); + REG_WRITE(ah, AR_WOW_KEEP_ALIVE_DELAY, KAL_DELAY * 1000); /* - * Create keep alive pattern to respond to beacons + * Create keep alive pattern to respond to beacons. */ ath9k_wow_create_keep_alive_pattern(ah); /* - * Configure MAC WoW Registers + * Configure keep alive register. */ - set = 0; + keep_alive = REG_READ(ah, AR_WOW_KEEP_ALIVE); + /* Send keep alive timeouts anyway */ - clr = AR_WOW_KEEP_ALIVE_AUTO_DIS; + keep_alive &= ~AR_WOW_KEEP_ALIVE_AUTO_DIS; - if (pattern_enable & AH_WOW_LINK_CHANGE) + if (pattern_enable & AH_WOW_LINK_CHANGE) { + keep_alive &= ~AR_WOW_KEEP_ALIVE_FAIL_DIS; wow_event_mask |= AR_WOW_KEEP_ALIVE_FAIL; - else - set = AR_WOW_KEEP_ALIVE_FAIL_DIS; + } else { + keep_alive |= AR_WOW_KEEP_ALIVE_FAIL_DIS; + } - set = AR_WOW_KEEP_ALIVE_FAIL_DIS; - REG_RMW(ah, AR_WOW_KEEP_ALIVE, set, clr); + REG_WRITE(ah, AR_WOW_KEEP_ALIVE, keep_alive); /* - * we are relying on a bmiss failure. ensure we have - * enough threshold to prevent false positives + * We are relying on a bmiss failure, ensure we have + * enough threshold to prevent false positives. */ REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR, AR_WOW_BMISSTHRESHOLD); - set = 0; - clr = 0; - if (pattern_enable & AH_WOW_BEACON_MISS) { - set = AR_WOW_BEACON_FAIL_EN; wow_event_mask |= AR_WOW_BEACON_FAIL; + REG_SET_BIT(ah, AR_WOW_BCN_EN, AR_WOW_BEACON_FAIL_EN); } else { - clr = AR_WOW_BEACON_FAIL_EN; + REG_CLR_BIT(ah, AR_WOW_BCN_EN, AR_WOW_BEACON_FAIL_EN); } - REG_RMW(ah, AR_WOW_BCN_EN, set, clr); - - set = 0; - clr = 0; /* - * Enable the magic packet registers + * Enable the magic packet registers. */ + magic_pattern = REG_READ(ah, AR_WOW_PATTERN); + magic_pattern |= AR_WOW_MAC_INTR_EN; + if (pattern_enable & AH_WOW_MAGIC_PATTERN_EN) { - set = AR_WOW_MAGIC_EN; + magic_pattern |= AR_WOW_MAGIC_EN; wow_event_mask |= AR_WOW_MAGIC_PAT_FOUND; } else { - clr = AR_WOW_MAGIC_EN; + magic_pattern &= ~AR_WOW_MAGIC_EN; } - set |= AR_WOW_MAC_INTR_EN; - REG_RMW(ah, AR_WOW_PATTERN, set, clr); + REG_WRITE(ah, AR_WOW_PATTERN, magic_pattern); + + /* + * Enable pattern matching for packets which are less + * than 256 bytes. + */ REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B, AR_WOW_PATTERN_SUPPORTED); /* - * Set the power states appropriately and enable PME + * Set the power states appropriately and enable PME. */ - clr = 0; - set = AR_PMCTRL_PWR_STATE_D1D3 | AR_PMCTRL_HOST_PME_EN | - AR_PMCTRL_PWR_PM_CTRL_ENA; + host_pm_ctrl = REG_READ(ah, AR_PCIE_PM_CTRL); + host_pm_ctrl |= AR_PMCTRL_PWR_STATE_D1D3 | + AR_PMCTRL_HOST_PME_EN | + AR_PMCTRL_PWR_PM_CTRL_ENA; + host_pm_ctrl &= ~AR_PCIE_PM_CTRL_ENA; - clr = AR_PCIE_PM_CTRL_ENA; - REG_RMW(ah, AR_PCIE_PM_CTRL, set, clr); + if (AR_SREV_9462(ah)) { + /* + * This is needed to prevent the chip waking up + * the host within 3-4 seconds with certain + * platform/BIOS. + */ + host_pm_ctrl &= ~AR_PMCTRL_PWR_STATE_D1D3; + host_pm_ctrl |= AR_PMCTRL_PWR_STATE_D1D3_REAL; + } + + REG_WRITE(ah, AR_PCIE_PM_CTRL, host_pm_ctrl); /* - * this is needed to prevent the chip waking up - * the host within 3-4 seconds with certain - * platform/BIOS. The fix is to enable - * D1 & D3 to match original definition and - * also match the OTP value. Anyway this - * is more related to SW WOW. + * Enable sequence number generation when asleep. */ - clr = AR_PMCTRL_PWR_STATE_D1D3; - REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr); + REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PRESERVE_SEQNUM); - set = AR_PMCTRL_PWR_STATE_D1D3_REAL; - REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set); + /* To bring down WOW power low margin */ + REG_SET_BIT(ah, AR_PCIE_PHY_REG3, BIT(13)); - REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PRESERVE_SEQNUM); + ath9k_hw_wow_set_arwr_reg(ah); - /* to bring down WOW power low margin */ - set = BIT(13); - REG_SET_BIT(ah, AR_PCIE_PHY_REG3, set); /* HW WoW */ - clr = BIT(5); - REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, clr); + REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, BIT(5)); ath9k_hw_set_powermode_wow_sleep(ah); - ah->wow_event_mask = wow_event_mask; + ah->wow.wow_event_mask = wow_event_mask; } EXPORT_SYMBOL(ath9k_hw_wow_enable); diff --git a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h index 159cc6fd2362..6fc0d07e5ec6 100644 --- a/drivers/net/wireless/ath/ath9k/ar953x_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar953x_initvals.h @@ -358,7 +358,7 @@ static const u32 qca953x_1p0_baseband_postamble[][5] = { {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, - {0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcfa10822, 0xcfa10822}, + {0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcfa10820, 0xcfa10820}, {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, @@ -378,7 +378,7 @@ static const u32 qca953x_1p0_baseband_postamble[][5] = { {0x0000a284, 0x00000000, 0x00000000, 0x00000010, 0x00000010}, {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110}, {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222}, - {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, + {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00058d18, 0x00058d18}, {0x0000a2cc, 0x18c50033, 0x18c43433, 0x18c41033, 0x18c44c33}, {0x0000a2d0, 0x00041982, 0x00041982, 0x00041982, 0x00041982}, {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b}, diff --git a/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h index fd6a84ccd49e..148562addd38 100644 --- a/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar955x_1p0_initvals.h @@ -63,7 +63,7 @@ static const u32 ar955x_1p0_baseband_postamble[][5] = { {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, - {0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcfa10822, 0xcfa10822}, + {0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcfa10820, 0xcfa10820}, {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, @@ -83,7 +83,7 @@ static const u32 ar955x_1p0_baseband_postamble[][5] = { {0x0000a284, 0x00000000, 0x00000000, 0x00000010, 0x00000010}, {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110}, {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222}, - {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, + {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00058d18, 0x00058d18}, {0x0000a2cc, 0x18c50033, 0x18c43433, 0x18c41033, 0x18c44c33}, {0x0000a2d0, 0x00041982, 0x00041982, 0x00041982, 0x00041982}, {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b}, diff --git a/drivers/net/wireless/ath/ath9k/ar956x_initvals.h b/drivers/net/wireless/ath/ath9k/ar956x_initvals.h new file mode 100644 index 000000000000..c3a47eaaf0c0 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/ar956x_initvals.h @@ -0,0 +1,1046 @@ +/* + * Copyright (c) 2010-2011 Atheros Communications Inc. + * Copyright (c) 2011-2012 Qualcomm Atheros Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef INITVALS_956X_H +#define INITVALS_956X_H + +#define qca956x_1p0_mac_core ar955x_1p0_mac_core + +#define qca956x_1p0_mac_postamble ar9331_1p1_mac_postamble + +#define qca956x_1p0_soc_preamble ar955x_1p0_soc_preamble + +#define qca956x_1p0_soc_postamble ar9300_2p2_soc_postamble + +#define qca956x_1p0_common_wo_xlna_rx_gain_table ar9300Common_wo_xlna_rx_gain_table_2p2 + +#define qca956x_1p0_baseband_postamble_dfs_channel ar9300_2p2_baseband_postamble_dfs_channel + +#define qca956x_1p0_common_wo_xlna_rx_gain_bounds ar955x_1p0_common_wo_xlna_rx_gain_bounds + +#define qca956x_1p0_common_rx_gain_bounds ar955x_1p0_common_rx_gain_bounds + +#define qca956x_1p0_modes_fast_clock ar9462_2p0_modes_fast_clock + +static const u32 qca956x_1p0_baseband_core[][2] = { + /* Addr allmodes */ + {0x00009800, 0xafe68e30}, + {0x00009804, 0xfd14e000}, + {0x00009808, 0x9c0a9f6b}, + {0x0000980c, 0x04900000}, + {0x00009814, 0x0280c00a}, + {0x00009818, 0x00000000}, + {0x0000981c, 0x00020028}, + {0x00009834, 0x6400a190}, + {0x00009838, 0x0108ecff}, + {0x0000983c, 0x14000600}, + {0x00009880, 0x201fff00}, + {0x00009884, 0x00001042}, + {0x000098a4, 0x00200400}, + {0x000098b0, 0x32840cbf}, + {0x000098bc, 0x00000002}, + {0x000098d0, 0x004b6a8e}, + {0x000098d4, 0x00000820}, + {0x000098dc, 0x00000000}, + {0x000098f0, 0x00000000}, + {0x000098f4, 0x00000000}, + {0x00009c04, 0xff55ff55}, + {0x00009c08, 0x0320ff55}, + {0x00009c0c, 0x00000000}, + {0x00009c10, 0x00000000}, + {0x00009c14, 0x00046384}, + {0x00009c18, 0x05b6b440}, + {0x00009c1c, 0x00b6b440}, + {0x00009d00, 0xc080a333}, + {0x00009d04, 0x40206c10}, + {0x00009d08, 0x009c4060}, + {0x00009d0c, 0x9883800a}, + {0x00009d10, 0x01834061}, + {0x00009d14, 0x00c0040b}, + {0x00009d18, 0x00000000}, + {0x00009e08, 0x0038230c}, + {0x00009e24, 0x990bb514}, + {0x00009e28, 0x0c6f0000}, + {0x00009e30, 0x06336f77}, + {0x00009e34, 0x6af6532f}, + {0x00009e38, 0x0cc80c00}, + {0x00009e40, 0x0d261820}, + {0x00009e4c, 0x00001004}, + {0x00009e50, 0x00ff03f1}, + {0x00009fc0, 0x813e4789}, + {0x00009fc4, 0x0001efb5}, + {0x00009fcc, 0x40000014}, + {0x00009fd0, 0x02993b93}, + {0x0000a20c, 0x00000000}, + {0x0000a218, 0x00000000}, + {0x0000a21c, 0x00000000}, + {0x0000a228, 0x10002310}, + {0x0000a23c, 0x00000000}, + {0x0000a244, 0x0c000000}, + {0x0000a248, 0x00000140}, + {0x0000a2a0, 0x00000007}, + {0x0000a2c0, 0x00000007}, + {0x0000a2c8, 0x00000000}, + {0x0000a2d4, 0x00000000}, + {0x0000a2ec, 0x00000000}, + {0x0000a2f0, 0x00000000}, + {0x0000a2f4, 0x00000000}, + {0x0000a2f8, 0x00000000}, + {0x0000a344, 0x00000000}, + {0x0000a34c, 0x00000000}, + {0x0000a350, 0x0000a000}, + {0x0000a360, 0x00000000}, + {0x0000a36c, 0x00000000}, + {0x0000a384, 0x00000001}, + {0x0000a388, 0x00000444}, + {0x0000a38c, 0x00000000}, + {0x0000a390, 0x210d0401}, + {0x0000a394, 0xab9a7144}, + {0x0000a398, 0x00000201}, + {0x0000a39c, 0x42424848}, + {0x0000a3a0, 0x3c466478}, + {0x0000a3a4, 0x3a363600}, + {0x0000a3a8, 0x0000003a}, + {0x0000a3ac, 0x00000000}, + {0x0000a3b0, 0x009011fe}, + {0x0000a3b4, 0x00000034}, + {0x0000a3b8, 0x00b3ec0a}, + {0x0000a3bc, 0x00000036}, + {0x0000a3c0, 0x20202020}, + {0x0000a3c4, 0x22222220}, + {0x0000a3c8, 0x20200020}, + {0x0000a3cc, 0x20202020}, + {0x0000a3d0, 0x20202020}, + {0x0000a3d4, 0x20202020}, + {0x0000a3d8, 0x20202020}, + {0x0000a3dc, 0x20202020}, + {0x0000a3e0, 0x20202020}, + {0x0000a3e4, 0x20202020}, + {0x0000a3e8, 0x20202020}, + {0x0000a3ec, 0x20202020}, + {0x0000a3f0, 0x00000000}, + {0x0000a3f4, 0x00000000}, + {0x0000a3f8, 0x0c9bd380}, + {0x0000a3fc, 0x000f0f01}, + {0x0000a400, 0x8fa91f01}, + {0x0000a404, 0x00000000}, + {0x0000a408, 0x0e79e5c6}, + {0x0000a40c, 0x00820820}, + {0x0000a414, 0x1ce739ce}, + {0x0000a418, 0x2d0019ce}, + {0x0000a41c, 0x1ce739ce}, + {0x0000a420, 0x000001ce}, + {0x0000a424, 0x1ce739ce}, + {0x0000a428, 0x000001ce}, + {0x0000a42c, 0x1ce739ce}, + {0x0000a430, 0x1ce739ce}, + {0x0000a434, 0x00000000}, + {0x0000a438, 0x00001801}, + {0x0000a43c, 0x00100000}, + {0x0000a444, 0x00000000}, + {0x0000a448, 0x05000080}, + {0x0000a44c, 0x00000001}, + {0x0000a450, 0x00010000}, + {0x0000a454, 0x05000000}, + {0x0000a458, 0x00000000}, + {0x0000a644, 0xbfad9fee}, + {0x0000a648, 0x0048660d}, + {0x0000a64c, 0x00003c37}, + {0x0000a670, 0x03020100}, + {0x0000a674, 0x21200504}, + {0x0000a678, 0x61602322}, + {0x0000a67c, 0x65646362}, + {0x0000a680, 0x6b6a6968}, + {0x0000a684, 0xe2706d6c}, + {0x0000a688, 0x000000e3}, + {0x0000a690, 0x00000838}, + {0x0000a7cc, 0x00000000}, + {0x0000a7d0, 0x00000000}, + {0x0000a7d4, 0x00000004}, + {0x0000a7dc, 0x00000000}, + {0x0000a8d0, 0x004b6a8e}, + {0x0000a8d4, 0x00000820}, + {0x0000a8dc, 0x00000000}, + {0x0000a8f0, 0x00000000}, + {0x0000a8f4, 0x00000000}, + {0x0000b2d0, 0x00000080}, + {0x0000b2d4, 0x00000000}, + {0x0000b2ec, 0x00000000}, + {0x0000b2f0, 0x00000000}, + {0x0000b2f4, 0x00000000}, + {0x0000b2f8, 0x00000000}, + {0x0000b408, 0x0e79e5c0}, + {0x0000b40c, 0x00820820}, + {0x0000b420, 0x00000000}, + {0x0000b8d0, 0x004b6a8e}, + {0x0000b8d4, 0x00000820}, + {0x0000b8dc, 0x00000000}, + {0x0000b8f0, 0x00000000}, + {0x0000b8f4, 0x00000000}, + {0x0000c2d0, 0x00000080}, + {0x0000c2d4, 0x00000000}, + {0x0000c2ec, 0x00000000}, + {0x0000c2f0, 0x00000000}, + {0x0000c2f4, 0x00000000}, + {0x0000c2f8, 0x00000000}, + {0x0000c408, 0x0e79e5c0}, + {0x0000c40c, 0x00820820}, + {0x0000c420, 0x00000000}, +}; + +static const u32 qca956x_1p0_baseband_postamble[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00009810, 0xd00a8005, 0xd00a8005, 0xd00a8011, 0xd00a8011}, + {0x00009820, 0x206a022e, 0x206a022e, 0x206a01ae, 0x206a01ae}, + {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac621f1, 0x5ac621f1}, + {0x00009828, 0x06903081, 0x06903081, 0x07d43881, 0x07d43881}, + {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, + {0x00009830, 0x0000059c, 0x0000059c, 0x0000119c, 0x0000119c}, + {0x00009c00, 0x000000c4, 0x000000c4, 0x000000c4, 0x000000c4}, + {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x037216a0}, + {0x00009e04, 0x001c2020, 0x001c2020, 0x001c2020, 0x001c2020}, + {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000de, 0x6c4000de}, + {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec84d2e, 0x7ec86d2e}, + {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x337d605e, 0x337d5d5e}, + {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, + {0x00009e20, 0x000003b5, 0x000003b5, 0x000003a6, 0x000003a6}, + {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, + {0x00009e3c, 0xcfa10820, 0xcfa10820, 0xcf946222, 0xcf946222}, + {0x00009e44, 0xfe321e27, 0xfe321e27, 0xfe291e27, 0xfe291e27}, + {0x00009e48, 0x5030201a, 0x5030201a, 0x50302012, 0x50302012}, + {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, + {0x0000a204, 0x005c0ec0, 0x005c0ec4, 0x045c0cc4, 0x045c0cc0}, + {0x0000a208, 0x00000104, 0x00000104, 0x00000004, 0x00000004}, + {0x0000a22c, 0x07e26a2f, 0x07e26a2f, 0x01026a2f, 0x01026a2f}, + {0x0000a230, 0x0000400a, 0x00004014, 0x00004016, 0x0000400b}, + {0x0000a234, 0x00000fff, 0x10000fff, 0x10000fff, 0x00000fff}, + {0x0000a238, 0xffb01018, 0xffb01018, 0xffb01018, 0xffb01018}, + {0x0000a250, 0x00000000, 0x00000000, 0x00000210, 0x00000108}, + {0x0000a254, 0x000007d0, 0x00000fa0, 0x00001130, 0x00000898}, + {0x0000a258, 0x02020002, 0x02020002, 0x02020002, 0x02020002}, + {0x0000a25c, 0x01000e0e, 0x01000e0e, 0x01010e0e, 0x01010e0e}, + {0x0000a260, 0x0a021501, 0x0a021501, 0x3a021501, 0x3a021501}, + {0x0000a264, 0x00000e0e, 0x00000e0e, 0x01000e0e, 0x01000e0e}, + {0x0000a280, 0x00000007, 0x00000007, 0x0000000b, 0x0000000b}, + {0x0000a284, 0x00000000, 0x00000000, 0x00000010, 0x00000010}, + {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110}, + {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222}, + {0x0000a2c4, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18}, + {0x0000a2cc, 0x18c50033, 0x18c43433, 0x18c41033, 0x18c44c33}, + {0x0000a2d0, 0x00041982, 0x00041982, 0x00041982, 0x00041982}, + {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b}, + {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, + {0x0000ae04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000}, + {0x0000ae18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000ae1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, + {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001a6, 0x000001a6}, + {0x0000b284, 0x00000000, 0x00000000, 0x00000010, 0x00000010}, + {0x0000b830, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, + {0x0000be04, 0x001c0000, 0x001c0000, 0x001c0000, 0x001c0000}, + {0x0000be18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000be1c, 0x0000019c, 0x0000019c, 0x0000019c, 0x0000019c}, + {0x0000be20, 0x000001b5, 0x000001b5, 0x000001a6, 0x000001a6}, + {0x0000c284, 0x00000000, 0x00000000, 0x00000010, 0x00000010}, +}; + +static const u32 qca956x_1p0_radio_core[][2] = { + /* Addr allmodes */ + {0x00016000, 0x36db6db6}, + {0x00016004, 0x6db6db40}, + {0x00016008, 0x73f00000}, + {0x0001600c, 0x00000000}, + {0x00016040, 0x3f80fff8}, + {0x0001604c, 0x000f0278}, + {0x00016050, 0x8036db6c}, + {0x00016054, 0x6db60000}, + {0x00016080, 0x00080000}, + {0x00016084, 0x0e48048c}, + {0x00016088, 0x14214514}, + {0x0001608c, 0x119f080a}, + {0x00016090, 0x24926490}, + {0x00016094, 0x00000000}, + {0x000160a0, 0xc2108ffe}, + {0x000160a4, 0x812fc370}, + {0x000160a8, 0x423c8000}, + {0x000160b4, 0x92480000}, + {0x000160c0, 0x006db6d8}, + {0x000160c4, 0x24b6db6c}, + {0x000160c8, 0x6db6db6c}, + {0x000160cc, 0x6db6fb7c}, + {0x000160d0, 0x6db6da44}, + {0x00016100, 0x07ff8001}, + {0x00016108, 0x00080010}, + {0x00016144, 0x01884080}, + {0x00016148, 0x00008058}, + {0x00016288, 0x001c6000}, + {0x0001628c, 0x50000000}, + {0x000162c0, 0x4b962100}, + {0x000162c4, 0x00000480}, + {0x000162c8, 0x04000144}, + {0x00016380, 0x00000000}, + {0x00016384, 0x00000000}, + {0x00016388, 0x00800700}, + {0x0001638c, 0x00800700}, + {0x00016390, 0x00800700}, + {0x00016394, 0x00000000}, + {0x00016398, 0x00000000}, + {0x0001639c, 0x00000000}, + {0x000163a0, 0x00000001}, + {0x000163a4, 0x00000001}, + {0x000163a8, 0x00000000}, + {0x000163ac, 0x00000000}, + {0x000163b0, 0x00000000}, + {0x000163b4, 0x00000000}, + {0x000163b8, 0x00000000}, + {0x000163bc, 0x00000000}, + {0x000163c0, 0x000000a0}, + {0x000163c4, 0x000c0000}, + {0x000163c8, 0x14021402}, + {0x000163cc, 0x00001402}, + {0x000163d0, 0x00000000}, + {0x000163d4, 0x00000000}, + {0x00016400, 0x36db6db6}, + {0x00016404, 0x6db6db40}, + {0x00016408, 0x73f00000}, + {0x0001640c, 0x00000000}, + {0x00016440, 0x3f80fff8}, + {0x0001644c, 0x000f0278}, + {0x00016450, 0x8036db6c}, + {0x00016454, 0x6db60000}, + {0x00016500, 0x07ff8001}, + {0x00016508, 0x00080010}, + {0x00016544, 0x01884080}, + {0x00016548, 0x00008058}, + {0x00016780, 0x00000000}, + {0x00016784, 0x00000000}, + {0x00016788, 0x00800700}, + {0x0001678c, 0x00800700}, + {0x00016790, 0x00800700}, + {0x00016794, 0x00000000}, + {0x00016798, 0x00000000}, + {0x0001679c, 0x00000000}, + {0x000167a0, 0x00000001}, + {0x000167a4, 0x00000001}, + {0x000167a8, 0x00000000}, + {0x000167ac, 0x00000000}, + {0x000167b0, 0x00000000}, + {0x000167b4, 0x00000000}, + {0x000167b8, 0x00000000}, + {0x000167bc, 0x00000000}, + {0x000167c0, 0x000000a0}, + {0x000167c4, 0x000c0000}, + {0x000167c8, 0x14021402}, + {0x000167cc, 0x00001402}, + {0x000167d0, 0x00000000}, + {0x000167d4, 0x00000000}, + {0x00016800, 0x36db6db6}, + {0x00016804, 0x6db6db40}, + {0x00016808, 0x73f00000}, + {0x0001680c, 0x00000000}, + {0x00016840, 0x3f80fff8}, + {0x0001684c, 0x000f0278}, + {0x00016850, 0x8036db6c}, + {0x00016854, 0x6db60000}, + {0x00016900, 0x07ff8001}, + {0x00016908, 0x00080010}, + {0x00016944, 0x01884080}, + {0x00016948, 0x00008058}, + {0x00016b80, 0x00000000}, + {0x00016b84, 0x00000000}, + {0x00016b88, 0x00800700}, + {0x00016b8c, 0x00800700}, + {0x00016b90, 0x00800700}, + {0x00016b94, 0x00000000}, + {0x00016b98, 0x00000000}, + {0x00016b9c, 0x00000000}, + {0x00016ba0, 0x00000001}, + {0x00016ba4, 0x00000001}, + {0x00016ba8, 0x00000000}, + {0x00016bac, 0x00000000}, + {0x00016bb0, 0x00000000}, + {0x00016bb4, 0x00000000}, + {0x00016bb8, 0x00000000}, + {0x00016bbc, 0x00000000}, + {0x00016bc0, 0x000000a0}, + {0x00016bc4, 0x000c0000}, + {0x00016bc8, 0x14021402}, + {0x00016bcc, 0x00001402}, + {0x00016bd0, 0x00000000}, + {0x00016bd4, 0x00000000}, +}; + +static const u32 qca956x_1p0_radio_postamble[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00016098, 0xd2dd5554, 0xd2dd5554, 0xc4128f5c, 0xc4128f5c}, + {0x0001609c, 0x0a566f3a, 0x0a566f3a, 0x0fd08f25, 0x0fd08f25}, + {0x000160ac, 0xa4647c00, 0xa4647c00, 0x24646800, 0x24646800}, + {0x000160b0, 0x01885f52, 0x01885f52, 0x00fe7f46, 0x00fe7f46}, + {0x00016104, 0xb7a00000, 0xb7a00000, 0xfff80001, 0xfff80001}, + {0x0001610c, 0xc0000000, 0xc0000000, 0x00000000, 0x00000000}, + {0x00016140, 0x10804008, 0x10804008, 0x50804000, 0x50804000}, + {0x00016504, 0xb7a00000, 0xb7a00000, 0xfff80001, 0xfff80001}, + {0x0001650c, 0xc0000000, 0xc0000000, 0x00000000, 0x00000000}, + {0x00016540, 0x10804008, 0x10804008, 0x50804000, 0x50804000}, + {0x00016904, 0xb7a00000, 0xb7a00000, 0xfff80001, 0xfff80001}, + {0x0001690c, 0xc0000000, 0xc0000000, 0x00000000, 0x00000000}, + {0x00016940, 0x10804008, 0x10804008, 0x50804000, 0x50804000}, +}; + +static const u32 qca956x_1p0_baseband_core_txfir_coeff_japan_2484[][2] = { + /* Addr allmodes */ + {0x0000a38c, 0x00000000}, + {0x0000a390, 0x6f7f0301}, + {0x0000a394, 0xca9228ee}, +}; + +static const u32 qca956x_1p0_modes_no_xpa_tx_gain_table[][3] = { + /* Addr 5G 2G */ + {0x0000a2dc, 0xffa9ac94, 0xffa9ac94}, + {0x0000a2e0, 0xff323118, 0xff323118}, + {0x0000a2e4, 0xff3ffe00, 0xff3ffe00}, + {0x0000a2e8, 0xffc00000, 0xffc00000}, + {0x0000a39c, 0x42424242, 0x42424242}, + {0x0000a3a4, 0x3a3e3e00, 0x3a3e3e00}, + {0x0000a3b0, 0x00a01404, 0x00a01404}, + {0x0000a3b4, 0x00000034, 0x00000034}, + {0x0000a3b8, 0x00800408, 0x00800408}, + {0x0000a3bc, 0x00000036, 0x00000036}, + {0x0000a410, 0x000050dc, 0x000050dc}, + {0x0000a500, 0x09000040, 0x09000040}, + {0x0000a504, 0x0b000041, 0x0b000041}, + {0x0000a508, 0x0d000042, 0x0d000042}, + {0x0000a50c, 0x11000044, 0x11000044}, + {0x0000a510, 0x15000046, 0x15000046}, + {0x0000a514, 0x1d000440, 0x1d000440}, + {0x0000a518, 0x1f000441, 0x1f000441}, + {0x0000a51c, 0x23000443, 0x23000443}, + {0x0000a520, 0x25000444, 0x25000444}, + {0x0000a524, 0x280004e0, 0x280004e0}, + {0x0000a528, 0x2c0004e2, 0x2c0004e2}, + {0x0000a52c, 0x2e0004e3, 0x2e0004e3}, + {0x0000a530, 0x300004e4, 0x300004e4}, + {0x0000a534, 0x340004e6, 0x340004e6}, + {0x0000a538, 0x37000ce0, 0x37000ce0}, + {0x0000a53c, 0x3b000ce2, 0x3b000ce2}, + {0x0000a540, 0x3d000ce3, 0x3d000ce3}, + {0x0000a544, 0x3f000ce4, 0x3f000ce4}, + {0x0000a548, 0x45001ee0, 0x45001ee0}, + {0x0000a54c, 0x49001ee2, 0x49001ee2}, + {0x0000a550, 0x4d001ee4, 0x4d001ee4}, + {0x0000a554, 0x51001ee6, 0x51001ee6}, + {0x0000a558, 0x55001eea, 0x55001eea}, + {0x0000a55c, 0x59001eec, 0x59001eec}, + {0x0000a560, 0x5d001ef0, 0x5d001ef0}, + {0x0000a564, 0x5f001ef1, 0x5f001ef1}, + {0x0000a568, 0x60001ef2, 0x60001ef2}, + {0x0000a56c, 0x61001ef3, 0x61001ef3}, + {0x0000a570, 0x62001ef4, 0x62001ef4}, + {0x0000a574, 0x63001ef5, 0x63001ef5}, + {0x0000a578, 0x64001ffc, 0x64001ffc}, + {0x0000a57c, 0x64001ffc, 0x64001ffc}, + {0x0000a600, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000}, + {0x0000a608, 0x00000000, 0x00000000}, + {0x0000a60c, 0x00000000, 0x00000000}, + {0x0000a610, 0x00804000, 0x00804000}, + {0x0000a614, 0x00804201, 0x00804201}, + {0x0000a618, 0x00804201, 0x00804201}, + {0x0000a61c, 0x00804201, 0x00804201}, + {0x0000a620, 0x00804201, 0x00804201}, + {0x0000a624, 0x00804201, 0x00804201}, + {0x0000a628, 0x00804201, 0x00804201}, + {0x0000a62c, 0x02808a02, 0x02808a02}, + {0x0000a630, 0x0340cd03, 0x0340cd03}, + {0x0000a634, 0x0340cd03, 0x0340cd03}, + {0x0000a638, 0x0340cd03, 0x0340cd03}, + {0x0000a63c, 0x05011404, 0x05011404}, + {0x0000b2dc, 0xffa9ac94, 0xffa9ac94}, + {0x0000b2e0, 0xff323118, 0xff323118}, + {0x0000b2e4, 0xff3ffe00, 0xff3ffe00}, + {0x0000b2e8, 0xffc00000, 0xffc00000}, + {0x0000c2dc, 0xffa9ac94, 0xffa9ac94}, + {0x0000c2e0, 0xff323118, 0xff323118}, + {0x0000c2e4, 0xff3ffe00, 0xff3ffe00}, + {0x0000c2e8, 0xffc00000, 0xffc00000}, + {0x00016044, 0x049242db, 0x049242db}, + {0x00016048, 0x64925a70, 0x64925a70}, + {0x00016148, 0x00008050, 0x00008050}, + {0x00016280, 0x41110005, 0x41110005}, + {0x00016284, 0x453a6000, 0x453a6000}, + {0x00016444, 0x049242db, 0x049242db}, + {0x00016448, 0x6c925a70, 0x6c925a70}, + {0x00016548, 0x00008050, 0x00008050}, + {0x00016844, 0x049242db, 0x049242db}, + {0x00016848, 0x6c925a70, 0x6c925a70}, + {0x00016948, 0x00008050, 0x00008050}, +}; + +static const u32 qca956x_1p0_modes_xpa_tx_gain_table[][3] = { + /* Addr 5G 2G */ + {0x0000a2dc, 0xcc69ac94, 0xcc69ac94}, + {0x0000a2e0, 0xf0b23118, 0xf0b23118}, + {0x0000a2e4, 0xffffc000, 0xffffc000}, + {0x0000a2e8, 0xc0000000, 0xc0000000}, + {0x0000a410, 0x000050d2, 0x000050d2}, + {0x0000a500, 0x0a000040, 0x0a000040}, + {0x0000a504, 0x0c000041, 0x0c000041}, + {0x0000a508, 0x0e000042, 0x0e000042}, + {0x0000a50c, 0x12000044, 0x12000044}, + {0x0000a510, 0x16000046, 0x16000046}, + {0x0000a514, 0x1d000440, 0x1d000440}, + {0x0000a518, 0x1f000441, 0x1f000441}, + {0x0000a51c, 0x23000443, 0x23000443}, + {0x0000a520, 0x25000444, 0x25000444}, + {0x0000a524, 0x29000a40, 0x29000a40}, + {0x0000a528, 0x2d000a42, 0x2d000a42}, + {0x0000a52c, 0x2f000a43, 0x2f000a43}, + {0x0000a530, 0x31000a44, 0x31000a44}, + {0x0000a534, 0x35000a46, 0x35000a46}, + {0x0000a538, 0x38000ce0, 0x38000ce0}, + {0x0000a53c, 0x3c000ce2, 0x3c000ce2}, + {0x0000a540, 0x3e000ce3, 0x3e000ce3}, + {0x0000a544, 0x40000ce4, 0x40000ce4}, + {0x0000a548, 0x46001ee0, 0x46001ee0}, + {0x0000a54c, 0x4a001ee2, 0x4a001ee2}, + {0x0000a550, 0x4e001ee4, 0x4e001ee4}, + {0x0000a554, 0x52001ee6, 0x52001ee6}, + {0x0000a558, 0x56001eea, 0x56001eea}, + {0x0000a55c, 0x5a001eec, 0x5a001eec}, + {0x0000a560, 0x5e001ef0, 0x5e001ef0}, + {0x0000a564, 0x60001ef1, 0x60001ef1}, + {0x0000a568, 0x61001ef2, 0x61001ef2}, + {0x0000a56c, 0x62001ef3, 0x62001ef3}, + {0x0000a570, 0x63001ef4, 0x63001ef4}, + {0x0000a574, 0x64001ef5, 0x64001ef5}, + {0x0000a578, 0x65001ffc, 0x65001ffc}, + {0x0000a57c, 0x65001ffc, 0x65001ffc}, + {0x0000a600, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000}, + {0x0000a608, 0x00000000, 0x00000000}, + {0x0000a60c, 0x00000000, 0x00000000}, + {0x0000a610, 0x00000000, 0x00000000}, + {0x0000a614, 0x00000000, 0x00000000}, + {0x0000a618, 0x00000000, 0x00000000}, + {0x0000a61c, 0x00804201, 0x00804201}, + {0x0000a620, 0x00804201, 0x00804201}, + {0x0000a624, 0x00804201, 0x00804201}, + {0x0000a628, 0x00804201, 0x00804201}, + {0x0000a62c, 0x02808a02, 0x02808a02}, + {0x0000a630, 0x0340cd03, 0x0340cd03}, + {0x0000a634, 0x0340cd03, 0x0340cd03}, + {0x0000a638, 0x0340cd03, 0x0340cd03}, + {0x0000a63c, 0x05011404, 0x05011404}, + {0x0000b2dc, 0xcc69ac94, 0xcc69ac94}, + {0x0000b2e0, 0xf0b23118, 0xf0b23118}, + {0x0000b2e4, 0xffffc000, 0xffffc000}, + {0x0000b2e8, 0xc0000000, 0xc0000000}, + {0x0000c2dc, 0xcc69ac94, 0xcc69ac94}, + {0x0000c2e0, 0xf0b23118, 0xf0b23118}, + {0x0000c2e4, 0xffffc000, 0xffffc000}, + {0x0000c2e8, 0xc0000000, 0xc0000000}, + {0x00016044, 0x012492db, 0x012492db}, + {0x00016048, 0x6c927a70, 0x6c927a70}, + {0x00016050, 0x8036d36c, 0x8036d36c}, + {0x00016280, 0x41110005, 0x41110005}, + {0x00016284, 0x453a7e00, 0x453a7e00}, + {0x00016444, 0x012492db, 0x012492db}, + {0x00016448, 0x6c927a70, 0x6c927a70}, + {0x00016450, 0x8036d36c, 0x8036d36c}, + {0x00016844, 0x012492db, 0x012492db}, + {0x00016848, 0x6c927a70, 0x6c927a70}, + {0x00016850, 0x8036d36c, 0x8036d36c}, +}; + +static const u32 qca956x_1p0_modes_no_xpa_low_ob_db_tx_gain_table[][3] = { + /* Addr 5G 2G */ + {0x0000a2dc, 0xffa9ac94, 0xffa9ac94}, + {0x0000a2e0, 0xff323118, 0xff323118}, + {0x0000a2e4, 0xff3ffe00, 0xff3ffe00}, + {0x0000a2e8, 0xffc00000, 0xffc00000}, + {0x0000a39c, 0x42424242, 0x42424242}, + {0x0000a3a4, 0x3a3e3e00, 0x3a3e3e00}, + {0x0000a3b0, 0x00a01404, 0x00a01404}, + {0x0000a3b4, 0x00000034, 0x00000034}, + {0x0000a3b8, 0x00800408, 0x00800408}, + {0x0000a3bc, 0x00000036, 0x00000036}, + {0x0000a410, 0x000050dc, 0x000050dc}, + {0x0000a414, 0x16b739ce, 0x16b739ce}, + {0x0000a418, 0x2d00198b, 0x2d00198b}, + {0x0000a41c, 0x16b5adce, 0x16b5adce}, + {0x0000a420, 0x0000014a, 0x0000014a}, + {0x0000a424, 0x14a525cc, 0x14a525cc}, + {0x0000a428, 0x0000012a, 0x0000012a}, + {0x0000a42c, 0x14a5294a, 0x14a5294a}, + {0x0000a430, 0x1294a929, 0x1294a929}, + {0x0000a500, 0x09000040, 0x09000040}, + {0x0000a504, 0x0b000041, 0x0b000041}, + {0x0000a508, 0x0d000042, 0x0d000042}, + {0x0000a50c, 0x11000044, 0x11000044}, + {0x0000a510, 0x15000046, 0x15000046}, + {0x0000a514, 0x1d000440, 0x1d000440}, + {0x0000a518, 0x1f000441, 0x1f000441}, + {0x0000a51c, 0x23000443, 0x23000443}, + {0x0000a520, 0x25000444, 0x25000444}, + {0x0000a524, 0x280004e0, 0x280004e0}, + {0x0000a528, 0x2c0004e2, 0x2c0004e2}, + {0x0000a52c, 0x2e0004e3, 0x2e0004e3}, + {0x0000a530, 0x300004e4, 0x300004e4}, + {0x0000a534, 0x340004e6, 0x340004e6}, + {0x0000a538, 0x37000ce0, 0x37000ce0}, + {0x0000a53c, 0x3b000ce2, 0x3b000ce2}, + {0x0000a540, 0x3d000ce3, 0x3d000ce3}, + {0x0000a544, 0x3f000ce4, 0x3f000ce4}, + {0x0000a548, 0x45001ee0, 0x45001ee0}, + {0x0000a54c, 0x49001ee2, 0x49001ee2}, + {0x0000a550, 0x4d001ee4, 0x4d001ee4}, + {0x0000a554, 0x51001ee6, 0x51001ee6}, + {0x0000a558, 0x55001eea, 0x55001eea}, + {0x0000a55c, 0x59001eec, 0x59001eec}, + {0x0000a560, 0x5d001ef0, 0x5d001ef0}, + {0x0000a564, 0x5f001ef1, 0x5f001ef1}, + {0x0000a568, 0x60001ef2, 0x60001ef2}, + {0x0000a56c, 0x61001ef3, 0x61001ef3}, + {0x0000a570, 0x62001ef4, 0x62001ef4}, + {0x0000a574, 0x63001ef5, 0x63001ef5}, + {0x0000a578, 0x64001ffc, 0x64001ffc}, + {0x0000a57c, 0x64001ffc, 0x64001ffc}, + {0x0000a600, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000}, + {0x0000a608, 0x00000000, 0x00000000}, + {0x0000a60c, 0x00000000, 0x00000000}, + {0x0000a610, 0x00804000, 0x00804000}, + {0x0000a614, 0x00804201, 0x00804201}, + {0x0000a618, 0x00804201, 0x00804201}, + {0x0000a61c, 0x00804201, 0x00804201}, + {0x0000a620, 0x00804201, 0x00804201}, + {0x0000a624, 0x00804201, 0x00804201}, + {0x0000a628, 0x00804201, 0x00804201}, + {0x0000a62c, 0x02808a02, 0x02808a02}, + {0x0000a630, 0x0340cd03, 0x0340cd03}, + {0x0000a634, 0x0340cd03, 0x0340cd03}, + {0x0000a638, 0x0340cd03, 0x0340cd03}, + {0x0000a63c, 0x05011404, 0x05011404}, + {0x0000b2dc, 0xffa9ac94, 0xffa9ac94}, + {0x0000b2e0, 0xff323118, 0xff323118}, + {0x0000b2e4, 0xff3ffe00, 0xff3ffe00}, + {0x0000b2e8, 0xffc00000, 0xffc00000}, + {0x0000c2dc, 0xffa9ac94, 0xffa9ac94}, + {0x0000c2e0, 0xff323118, 0xff323118}, + {0x0000c2e4, 0xff3ffe00, 0xff3ffe00}, + {0x0000c2e8, 0xffc00000, 0xffc00000}, + {0x00016044, 0x046e42db, 0x046e42db}, + {0x00016048, 0x64925a70, 0x64925a70}, + {0x00016148, 0x00008050, 0x00008050}, + {0x00016280, 0x41110005, 0x41110005}, + {0x00016284, 0x453a6000, 0x453a6000}, + {0x00016444, 0x046e42db, 0x046e42db}, + {0x00016448, 0x6c925a70, 0x6c925a70}, + {0x00016548, 0x00008050, 0x00008050}, + {0x00016844, 0x046e42db, 0x046e42db}, + {0x00016848, 0x6c925a70, 0x6c925a70}, + {0x00016948, 0x00008050, 0x00008050}, +}; + +static const u32 qca956x_1p0_modes_no_xpa_green_tx_gain_table[][3] = { + /* Addr 5G 2G */ + {0x000098bc, 0x00000001, 0x00000001}, + {0x0000a2dc, 0xd3555284, 0xd3555284}, + {0x0000a2e0, 0x1c666318, 0x1c666318}, + {0x0000a2e4, 0xe07bbc00, 0xe07bbc00}, + {0x0000a2e8, 0xff800000, 0xff800000}, + {0x0000a3a4, 0x3a3e3e00, 0x3a3e3e00}, + {0x0000a410, 0x000050dc, 0x000050dc}, + {0x0000a500, 0x02000040, 0x02000040}, + {0x0000a504, 0x04000041, 0x04000041}, + {0x0000a508, 0x06000042, 0x06000042}, + {0x0000a50c, 0x0a000044, 0x0a000044}, + {0x0000a510, 0x0c000045, 0x0c000045}, + {0x0000a514, 0x13000440, 0x13000440}, + {0x0000a518, 0x15000441, 0x15000441}, + {0x0000a51c, 0x19000443, 0x19000443}, + {0x0000a520, 0x1b000444, 0x1b000444}, + {0x0000a524, 0x1e0004e0, 0x1e0004e0}, + {0x0000a528, 0x220004e2, 0x220004e2}, + {0x0000a52c, 0x240004e3, 0x240004e3}, + {0x0000a530, 0x260004e4, 0x260004e4}, + {0x0000a534, 0x2a0004e6, 0x2a0004e6}, + {0x0000a538, 0x32000ce0, 0x32000ce0}, + {0x0000a53c, 0x36000ce2, 0x36000ce2}, + {0x0000a540, 0x3a000ce4, 0x3a000ce4}, + {0x0000a544, 0x3e000ce6, 0x3e000ce6}, + {0x0000a548, 0x45001ee0, 0x45001ee0}, + {0x0000a54c, 0x49001ee2, 0x49001ee2}, + {0x0000a550, 0x4d001ee4, 0x4d001ee4}, + {0x0000a554, 0x51001ee6, 0x51001ee6}, + {0x0000a558, 0x55001eea, 0x55001eea}, + {0x0000a55c, 0x59001eec, 0x59001eec}, + {0x0000a560, 0x5d001ef0, 0x5d001ef0}, + {0x0000a564, 0x5f001ef1, 0x5f001ef1}, + {0x0000a568, 0x60001ef2, 0x60001ef2}, + {0x0000a56c, 0x61001ef3, 0x61001ef3}, + {0x0000a570, 0x62001ef4, 0x62001ef4}, + {0x0000a574, 0x63001ff5, 0x63001ff5}, + {0x0000a578, 0x64001ffc, 0x64001ffc}, + {0x0000a57c, 0x64001ffc, 0x64001ffc}, + {0x0000a600, 0x00000000, 0x00000000}, + {0x0000a604, 0x00000000, 0x00000000}, + {0x0000a608, 0x00000000, 0x00000000}, + {0x0000a60c, 0x00000000, 0x00000000}, + {0x0000a610, 0x00804000, 0x00804000}, + {0x0000a614, 0x00804201, 0x00804201}, + {0x0000a618, 0x00804201, 0x00804201}, + {0x0000a61c, 0x00804201, 0x00804201}, + {0x0000a620, 0x00804201, 0x00804201}, + {0x0000a624, 0x00804201, 0x00804201}, + {0x0000a628, 0x00804201, 0x00804201}, + {0x0000a62c, 0x02808a02, 0x02808a02}, + {0x0000a630, 0x0340cd03, 0x0340cd03}, + {0x0000a634, 0x0340cd03, 0x0340cd03}, + {0x0000a638, 0x0340cd03, 0x0340cd03}, + {0x0000a63c, 0x05011404, 0x05011404}, + {0x0000b2dc, 0xd3555284, 0xd3555284}, + {0x0000b2e0, 0x1c666318, 0x1c666318}, + {0x0000b2e4, 0xe07bbc00, 0xe07bbc00}, + {0x0000b2e8, 0xff800000, 0xff800000}, + {0x0000c2dc, 0xd3555284, 0xd3555284}, + {0x0000c2e0, 0x1c666318, 0x1c666318}, + {0x0000c2e4, 0xe07bbc00, 0xe07bbc00}, + {0x0000c2e8, 0xff800000, 0xff800000}, + {0x00016044, 0x849242db, 0x849242db}, + {0x00016048, 0x64925a70, 0x64925a70}, + {0x00016280, 0x41110005, 0x41110005}, + {0x00016284, 0x453a6000, 0x453a6000}, + {0x00016444, 0x849242db, 0x849242db}, + {0x00016448, 0x6c925a70, 0x6c925a70}, + {0x00016844, 0x849242db, 0x849242db}, + {0x00016848, 0x6c925a70, 0x6c925a70}, + {0x0000a7f0, 0x800002cc, 0x800002cc}, + {0x0000a7f4, 0x00000018, 0x00000018}, + {0x0000a7f4, 0x00000018, 0x00000018}, + {0x0000a7f4, 0x00000018, 0x00000018}, + {0x0000a7f4, 0x00000018, 0x00000018}, + {0x0000a7f4, 0x00000018, 0x00000018}, + {0x0000a7f4, 0x00000018, 0x00000018}, + {0x0000a7f4, 0x00000018, 0x00000018}, + {0x0000a7f4, 0x00000018, 0x00000018}, + {0x0000a7f4, 0x00000018, 0x00000018}, + {0x0000a7f4, 0x00000018, 0x00000018}, + {0x0000a7f4, 0x00000018, 0x00000018}, + {0x0000a7f4, 0x00000018, 0x00000018}, + {0x0000a7f4, 0x00000018, 0x00000018}, + {0x0000a7f4, 0x00000018, 0x00000018}, + {0x0000a7f4, 0x00000028, 0x00000028}, + {0x0000a7f4, 0x00000028, 0x00000028}, + {0x0000a7f4, 0x00000028, 0x00000028}, + {0x0000a7f4, 0x00000028, 0x00000028}, + {0x0000a7f4, 0x00000048, 0x00000048}, + {0x0000a7f4, 0x00000048, 0x00000048}, + {0x0000a7f4, 0x00000048, 0x00000048}, + {0x0000a7f4, 0x00000048, 0x00000048}, + {0x0000a7f4, 0x00000048, 0x00000048}, + {0x0000a7f4, 0x00000048, 0x00000048}, + {0x0000a7f4, 0x00000048, 0x00000048}, + {0x0000a7f4, 0x00000048, 0x00000048}, + {0x0000a7f4, 0x00000048, 0x00000048}, + {0x0000a7f4, 0x00000048, 0x00000048}, + {0x0000a7f4, 0x00000048, 0x00000048}, + {0x0000a7f4, 0x00000048, 0x00000048}, + {0x0000a7f4, 0x00000048, 0x00000048}, + {0x0000a7f4, 0x00000048, 0x00000048}, +}; + +static const u32 qca956x_1p0_common_rx_gain_table[][2] = { + /* Addr allmodes */ + {0x0000a000, 0x00010000}, + {0x0000a004, 0x00030002}, + {0x0000a008, 0x00050004}, + {0x0000a00c, 0x00810080}, + {0x0000a010, 0x00830082}, + {0x0000a014, 0x01810180}, + {0x0000a018, 0x01830182}, + {0x0000a01c, 0x01850184}, + {0x0000a020, 0x01890188}, + {0x0000a024, 0x018b018a}, + {0x0000a028, 0x018d018c}, + {0x0000a02c, 0x01910190}, + {0x0000a030, 0x01930192}, + {0x0000a034, 0x01950194}, + {0x0000a038, 0x038a0196}, + {0x0000a03c, 0x038c038b}, + {0x0000a040, 0x0390038d}, + {0x0000a044, 0x03920391}, + {0x0000a048, 0x03940393}, + {0x0000a04c, 0x03960395}, + {0x0000a050, 0x00000000}, + {0x0000a054, 0x00000000}, + {0x0000a058, 0x00000000}, + {0x0000a05c, 0x00000000}, + {0x0000a060, 0x00000000}, + {0x0000a064, 0x00000000}, + {0x0000a068, 0x00000000}, + {0x0000a06c, 0x00000000}, + {0x0000a070, 0x00000000}, + {0x0000a074, 0x00000000}, + {0x0000a078, 0x00000000}, + {0x0000a07c, 0x00000000}, + {0x0000a080, 0x22222222}, + {0x0000a084, 0x1d1d1d1d}, + {0x0000a088, 0x1d1d1d1d}, + {0x0000a08c, 0x1d1d1d1d}, + {0x0000a090, 0x17171717}, + {0x0000a094, 0x11111717}, + {0x0000a098, 0x00030311}, + {0x0000a09c, 0x00000000}, + {0x0000a0a0, 0x00000000}, + {0x0000a0a4, 0x00000000}, + {0x0000a0a8, 0x00000000}, + {0x0000a0ac, 0x00000000}, + {0x0000a0b0, 0x00000000}, + {0x0000a0b4, 0x00000000}, + {0x0000a0b8, 0x00000000}, + {0x0000a0bc, 0x00000000}, + {0x0000a0c0, 0x001f0000}, + {0x0000a0c4, 0x01000101}, + {0x0000a0c8, 0x011e011f}, + {0x0000a0cc, 0x011c011d}, + {0x0000a0d0, 0x02030204}, + {0x0000a0d4, 0x02010202}, + {0x0000a0d8, 0x021f0200}, + {0x0000a0dc, 0x0302021e}, + {0x0000a0e0, 0x03000301}, + {0x0000a0e4, 0x031e031f}, + {0x0000a0e8, 0x0402031d}, + {0x0000a0ec, 0x04000401}, + {0x0000a0f0, 0x041e041f}, + {0x0000a0f4, 0x0502041d}, + {0x0000a0f8, 0x05000501}, + {0x0000a0fc, 0x051e051f}, + {0x0000a100, 0x06010602}, + {0x0000a104, 0x061f0600}, + {0x0000a108, 0x061d061e}, + {0x0000a10c, 0x07020703}, + {0x0000a110, 0x07000701}, + {0x0000a114, 0x00000000}, + {0x0000a118, 0x00000000}, + {0x0000a11c, 0x00000000}, + {0x0000a120, 0x00000000}, + {0x0000a124, 0x00000000}, + {0x0000a128, 0x00000000}, + {0x0000a12c, 0x00000000}, + {0x0000a130, 0x00000000}, + {0x0000a134, 0x00000000}, + {0x0000a138, 0x00000000}, + {0x0000a13c, 0x00000000}, + {0x0000a140, 0x001f0000}, + {0x0000a144, 0x01000101}, + {0x0000a148, 0x011e011f}, + {0x0000a14c, 0x011c011d}, + {0x0000a150, 0x02030204}, + {0x0000a154, 0x02010202}, + {0x0000a158, 0x021f0200}, + {0x0000a15c, 0x0302021e}, + {0x0000a160, 0x03000301}, + {0x0000a164, 0x031e031f}, + {0x0000a168, 0x0402031d}, + {0x0000a16c, 0x04000401}, + {0x0000a170, 0x041e041f}, + {0x0000a174, 0x0502041d}, + {0x0000a178, 0x05000501}, + {0x0000a17c, 0x051e051f}, + {0x0000a180, 0x06010602}, + {0x0000a184, 0x061f0600}, + {0x0000a188, 0x061d061e}, + {0x0000a18c, 0x07020703}, + {0x0000a190, 0x07000701}, + {0x0000a194, 0x00000000}, + {0x0000a198, 0x00000000}, + {0x0000a19c, 0x00000000}, + {0x0000a1a0, 0x00000000}, + {0x0000a1a4, 0x00000000}, + {0x0000a1a8, 0x00000000}, + {0x0000a1ac, 0x00000000}, + {0x0000a1b0, 0x00000000}, + {0x0000a1b4, 0x00000000}, + {0x0000a1b8, 0x00000000}, + {0x0000a1bc, 0x00000000}, + {0x0000a1c0, 0x00000000}, + {0x0000a1c4, 0x00000000}, + {0x0000a1c8, 0x00000000}, + {0x0000a1cc, 0x00000000}, + {0x0000a1d0, 0x00000000}, + {0x0000a1d4, 0x00000000}, + {0x0000a1d8, 0x00000000}, + {0x0000a1dc, 0x00000000}, + {0x0000a1e0, 0x00000000}, + {0x0000a1e4, 0x00000000}, + {0x0000a1e8, 0x00000000}, + {0x0000a1ec, 0x00000000}, + {0x0000a1f0, 0x00000396}, + {0x0000a1f4, 0x00000396}, + {0x0000a1f8, 0x00000396}, + {0x0000a1fc, 0x00000196}, + {0x0000b000, 0x00010000}, + {0x0000b004, 0x00030002}, + {0x0000b008, 0x00050004}, + {0x0000b00c, 0x00810080}, + {0x0000b010, 0x00830082}, + {0x0000b014, 0x01810180}, + {0x0000b018, 0x01830182}, + {0x0000b01c, 0x01850184}, + {0x0000b020, 0x02810280}, + {0x0000b024, 0x02830282}, + {0x0000b028, 0x02850284}, + {0x0000b02c, 0x02890288}, + {0x0000b030, 0x028b028a}, + {0x0000b034, 0x0388028c}, + {0x0000b038, 0x038a0389}, + {0x0000b03c, 0x038c038b}, + {0x0000b040, 0x0390038d}, + {0x0000b044, 0x03920391}, + {0x0000b048, 0x03940393}, + {0x0000b04c, 0x03960395}, + {0x0000b050, 0x00000000}, + {0x0000b054, 0x00000000}, + {0x0000b058, 0x00000000}, + {0x0000b05c, 0x00000000}, + {0x0000b060, 0x00000000}, + {0x0000b064, 0x00000000}, + {0x0000b068, 0x00000000}, + {0x0000b06c, 0x00000000}, + {0x0000b070, 0x00000000}, + {0x0000b074, 0x00000000}, + {0x0000b078, 0x00000000}, + {0x0000b07c, 0x00000000}, + {0x0000b080, 0x23232323}, + {0x0000b084, 0x21232323}, + {0x0000b088, 0x19191c1e}, + {0x0000b08c, 0x12141417}, + {0x0000b090, 0x07070e0e}, + {0x0000b094, 0x03030305}, + {0x0000b098, 0x00000003}, + {0x0000b09c, 0x00000000}, + {0x0000b0a0, 0x00000000}, + {0x0000b0a4, 0x00000000}, + {0x0000b0a8, 0x00000000}, + {0x0000b0ac, 0x00000000}, + {0x0000b0b0, 0x00000000}, + {0x0000b0b4, 0x00000000}, + {0x0000b0b8, 0x00000000}, + {0x0000b0bc, 0x00000000}, + {0x0000b0c0, 0x003f0020}, + {0x0000b0c4, 0x00400041}, + {0x0000b0c8, 0x0140005f}, + {0x0000b0cc, 0x0160015f}, + {0x0000b0d0, 0x017e017f}, + {0x0000b0d4, 0x02410242}, + {0x0000b0d8, 0x025f0240}, + {0x0000b0dc, 0x027f0260}, + {0x0000b0e0, 0x0341027e}, + {0x0000b0e4, 0x035f0340}, + {0x0000b0e8, 0x037f0360}, + {0x0000b0ec, 0x04400441}, + {0x0000b0f0, 0x0460045f}, + {0x0000b0f4, 0x0541047f}, + {0x0000b0f8, 0x055f0540}, + {0x0000b0fc, 0x057f0560}, + {0x0000b100, 0x06400641}, + {0x0000b104, 0x0660065f}, + {0x0000b108, 0x067e067f}, + {0x0000b10c, 0x07410742}, + {0x0000b110, 0x075f0740}, + {0x0000b114, 0x077f0760}, + {0x0000b118, 0x07800781}, + {0x0000b11c, 0x07a0079f}, + {0x0000b120, 0x07c107bf}, + {0x0000b124, 0x000007c0}, + {0x0000b128, 0x00000000}, + {0x0000b12c, 0x00000000}, + {0x0000b130, 0x00000000}, + {0x0000b134, 0x00000000}, + {0x0000b138, 0x00000000}, + {0x0000b13c, 0x00000000}, + {0x0000b140, 0x003f0020}, + {0x0000b144, 0x00400041}, + {0x0000b148, 0x0140005f}, + {0x0000b14c, 0x0160015f}, + {0x0000b150, 0x017e017f}, + {0x0000b154, 0x02410242}, + {0x0000b158, 0x025f0240}, + {0x0000b15c, 0x027f0260}, + {0x0000b160, 0x0341027e}, + {0x0000b164, 0x035f0340}, + {0x0000b168, 0x037f0360}, + {0x0000b16c, 0x04400441}, + {0x0000b170, 0x0460045f}, + {0x0000b174, 0x0541047f}, + {0x0000b178, 0x055f0540}, + {0x0000b17c, 0x057f0560}, + {0x0000b180, 0x06400641}, + {0x0000b184, 0x0660065f}, + {0x0000b188, 0x067e067f}, + {0x0000b18c, 0x07410742}, + {0x0000b190, 0x075f0740}, + {0x0000b194, 0x077f0760}, + {0x0000b198, 0x07800781}, + {0x0000b19c, 0x07a0079f}, + {0x0000b1a0, 0x07c107bf}, + {0x0000b1a4, 0x000007c0}, + {0x0000b1a8, 0x00000000}, + {0x0000b1ac, 0x00000000}, + {0x0000b1b0, 0x00000000}, + {0x0000b1b4, 0x00000000}, + {0x0000b1b8, 0x00000000}, + {0x0000b1bc, 0x00000000}, + {0x0000b1c0, 0x00000000}, + {0x0000b1c4, 0x00000000}, + {0x0000b1c8, 0x00000000}, + {0x0000b1cc, 0x00000000}, + {0x0000b1d0, 0x00000000}, + {0x0000b1d4, 0x00000000}, + {0x0000b1d8, 0x00000000}, + {0x0000b1dc, 0x00000000}, + {0x0000b1e0, 0x00000000}, + {0x0000b1e4, 0x00000000}, + {0x0000b1e8, 0x00000000}, + {0x0000b1ec, 0x00000000}, + {0x0000b1f0, 0x00000396}, + {0x0000b1f4, 0x00000396}, + {0x0000b1f8, 0x00000396}, + {0x0000b1fc, 0x00000196}, +}; + +static const u32 qca956x_1p0_xlna_only[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x00009820, 0x206a022e, 0x206a022e, 0x206a01ae, 0x206a01ae}, + {0x00009824, 0x5ac640d0, 0x5ac640d0, 0x5ac621f1, 0x5ac621f1}, + {0x00009828, 0x06903081, 0x06903081, 0x07d43881, 0x07d43881}, + {0x00009e00, 0x0372111a, 0x0372111a, 0x037216a0, 0x03721720}, + {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000de, 0x6c4000da}, + {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec86d2e, 0x7ec8ad2e}, + {0x00009e14, 0x37b95d5e, 0x37b9605e, 0x317a6062, 0x317a5ae2}, + {0x00009e18, 0x00000000, 0x00000000, 0x03c00000, 0x03c00000}, + {0x00009e20, 0x000003b5, 0x000003b5, 0x000003b2, 0x000003b2}, + {0x00009fc0, 0x813e4788, 0x813e4788, 0x813e4789, 0x813e4789}, + {0x0000ae18, 0x00000000, 0x00000000, 0x03c00000, 0x03c00000}, + {0x0000ae20, 0x000001b5, 0x000001b5, 0x000001b2, 0x000001b2}, + {0x0000be18, 0x00000000, 0x00000000, 0x03c00000, 0x03c00000}, + {0x0000be20, 0x000001b5, 0x000001b5, 0x000001b2, 0x000001b2}, +}; + +#endif /* INITVALS_956X_H */ diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 1a9fe0983a6b..0f8e9464e4ab 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -34,7 +34,7 @@ struct ath_vif; extern struct ieee80211_ops ath9k_ops; extern int ath9k_modparam_nohwcrypt; -extern int led_blink; +extern int ath9k_led_blink; extern bool is_ath9k_unloaded; extern int ath9k_use_chanctx; @@ -830,14 +830,9 @@ static inline void ath_fill_led_pin(struct ath_softc *sc) /* Wake on Wireless LAN */ /************************/ -struct ath9k_wow_pattern { - u8 pattern_bytes[MAX_PATTERN_SIZE]; - u8 mask_bytes[MAX_PATTERN_SIZE]; - u32 pattern_len; -}; - #ifdef CONFIG_ATH9K_WOW void ath9k_init_wow(struct ieee80211_hw *hw); +void ath9k_deinit_wow(struct ieee80211_hw *hw); int ath9k_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); int ath9k_resume(struct ieee80211_hw *hw); @@ -846,6 +841,9 @@ void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled); static inline void ath9k_init_wow(struct ieee80211_hw *hw) { } +static inline void ath9k_deinit_wow(struct ieee80211_hw *hw) +{ +} static inline int ath9k_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { @@ -1039,9 +1037,8 @@ struct ath_softc { s16 tx99_power; #ifdef CONFIG_ATH9K_WOW - atomic_t wow_got_bmiss_intr; - atomic_t wow_sleep_proc_intr; /* in the middle of WoW sleep ? */ u32 wow_intr_before_sleep; + bool force_wow; #endif }; diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c index ec93ddf0863a..5cee231cca1f 100644 --- a/drivers/net/wireless/ath/ath9k/common-spectral.c +++ b/drivers/net/wireless/ath/ath9k/common-spectral.c @@ -582,7 +582,7 @@ static struct rchan_callbacks rfs_spec_scan_cb = { void ath9k_cmn_spectral_deinit_debug(struct ath_spec_scan_priv *spec_priv) { - if (config_enabled(CONFIG_ATH9K_DEBUGFS) && spec_priv->rfs_chan_spec_scan) { + if (config_enabled(CONFIG_ATH9K_DEBUGFS)) { relay_close(spec_priv->rfs_chan_spec_scan); spec_priv->rfs_chan_spec_scan = NULL; } diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 871e969409bf..50a2e0ac3b8b 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -403,7 +403,8 @@ static const struct file_operations fops_antenna_diversity = { static int read_file_dma(struct seq_file *file, void *data) { - struct ath_softc *sc = file->private; + struct ieee80211_hw *hw = dev_get_drvdata(file->private); + struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; u32 val[ATH9K_NUM_DMA_DEBUG_REGS]; int i, qcuOffset = 0, dcuOffset = 0; @@ -470,20 +471,6 @@ static int read_file_dma(struct seq_file *file, void *data) return 0; } -static int open_file_dma(struct inode *inode, struct file *f) -{ - return single_open(f, read_file_dma, inode->i_private); -} - -static const struct file_operations fops_dma = { - .open = open_file_dma, - .read = seq_read, - .owner = THIS_MODULE, - .llseek = seq_lseek, - .release = single_release, -}; - - void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status) { if (status) @@ -539,7 +526,8 @@ void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status) static int read_file_interrupt(struct seq_file *file, void *data) { - struct ath_softc *sc = file->private; + struct ieee80211_hw *hw = dev_get_drvdata(file->private); + struct ath_softc *sc = hw->priv; #define PR_IS(a, s) \ do { \ @@ -600,22 +588,10 @@ static int read_file_interrupt(struct seq_file *file, void *data) return 0; } -static int open_file_interrupt(struct inode *inode, struct file *f) -{ - return single_open(f, read_file_interrupt, inode->i_private); -} - -static const struct file_operations fops_interrupt = { - .read = seq_read, - .open = open_file_interrupt, - .owner = THIS_MODULE, - .llseek = seq_lseek, - .release = single_release, -}; - static int read_file_xmit(struct seq_file *file, void *data) { - struct ath_softc *sc = file->private; + struct ieee80211_hw *hw = dev_get_drvdata(file->private); + struct ath_softc *sc = hw->priv; seq_printf(file, "%30s %10s%10s%10s\n\n", "BE", "BK", "VI", "VO"); @@ -661,7 +637,8 @@ static void print_queue(struct ath_softc *sc, struct ath_txq *txq, static int read_file_queues(struct seq_file *file, void *data) { - struct ath_softc *sc = file->private; + struct ieee80211_hw *hw = dev_get_drvdata(file->private); + struct ath_softc *sc = hw->priv; struct ath_txq *txq; int i; static const char *qname[4] = { @@ -682,7 +659,8 @@ static int read_file_queues(struct seq_file *file, void *data) static int read_file_misc(struct seq_file *file, void *data) { - struct ath_softc *sc = file->private; + struct ieee80211_hw *hw = dev_get_drvdata(file->private); + struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); struct ath9k_vif_iter_data iter_data; struct ath_chanctx *ctx; @@ -773,7 +751,8 @@ static int read_file_misc(struct seq_file *file, void *data) static int read_file_reset(struct seq_file *file, void *data) { - struct ath_softc *sc = file->private; + struct ieee80211_hw *hw = dev_get_drvdata(file->private); + struct ath_softc *sc = hw->priv; static const char * const reset_cause[__RESET_TYPE_MAX] = { [RESET_TYPE_BB_HANG] = "Baseband Hang", [RESET_TYPE_BB_WATCHDOG] = "Baseband Watchdog", @@ -837,58 +816,6 @@ void ath_debug_stat_tx(struct ath_softc *sc, struct ath_buf *bf, TX_STAT_INC(qnum, delim_underrun); } -static int open_file_xmit(struct inode *inode, struct file *f) -{ - return single_open(f, read_file_xmit, inode->i_private); -} - -static const struct file_operations fops_xmit = { - .read = seq_read, - .open = open_file_xmit, - .owner = THIS_MODULE, - .llseek = seq_lseek, - .release = single_release, -}; - -static int open_file_queues(struct inode *inode, struct file *f) -{ - return single_open(f, read_file_queues, inode->i_private); -} - -static const struct file_operations fops_queues = { - .read = seq_read, - .open = open_file_queues, - .owner = THIS_MODULE, - .llseek = seq_lseek, - .release = single_release, -}; - -static int open_file_misc(struct inode *inode, struct file *f) -{ - return single_open(f, read_file_misc, inode->i_private); -} - -static const struct file_operations fops_misc = { - .read = seq_read, - .open = open_file_misc, - .owner = THIS_MODULE, - .llseek = seq_lseek, - .release = single_release, -}; - -static int open_file_reset(struct inode *inode, struct file *f) -{ - return single_open(f, read_file_reset, inode->i_private); -} - -static const struct file_operations fops_reset = { - .read = seq_read, - .open = open_file_reset, - .owner = THIS_MODULE, - .llseek = seq_lseek, - .release = single_release, -}; - void ath_debug_stat_rx(struct ath_softc *sc, struct ath_rx_status *rs) { ath9k_cmn_debug_stat_rx(&sc->debug.stats.rxstats, rs); @@ -1018,7 +945,8 @@ static const struct file_operations fops_regdump = { static int read_file_dump_nfcal(struct seq_file *file, void *data) { - struct ath_softc *sc = file->private; + struct ieee80211_hw *hw = dev_get_drvdata(file->private); + struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath9k_nfcal_hist *h = sc->cur_chan->caldata.nfCalHist; struct ath_common *common = ath9k_hw_common(ah); @@ -1115,6 +1043,133 @@ static const struct file_operations fops_ackto = { }; #endif +#ifdef CONFIG_ATH9K_WOW + +static ssize_t read_file_wow(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + unsigned int len = 0, size = 32; + ssize_t retval; + char *buf; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len += scnprintf(buf + len, size - len, "WOW: %s\n", + sc->force_wow ? "ENABLED" : "DISABLED"); + + if (len > size) + len = size; + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} + +static ssize_t write_file_wow(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + unsigned long val; + char buf[32]; + ssize_t len; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val != 1) + return -EINVAL; + + if (!sc->force_wow) { + sc->force_wow = true; + ath9k_init_wow(sc->hw); + } + + return count; +} + +static const struct file_operations fops_wow = { + .read = read_file_wow, + .write = write_file_wow, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +#endif + +static ssize_t read_file_tpc(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_hw *ah = sc->sc_ah; + unsigned int len = 0, size = 32; + ssize_t retval; + char *buf; + + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + len += scnprintf(buf + len, size - len, "%s\n", + ah->tpc_enabled ? "ENABLED" : "DISABLED"); + + if (len > size) + len = size; + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; +} + +static ssize_t write_file_tpc(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath_softc *sc = file->private_data; + struct ath_hw *ah = sc->sc_ah; + unsigned long val; + char buf[32]; + ssize_t len; + bool tpc_enabled; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + + if (val < 0 || val > 1) + return -EINVAL; + + tpc_enabled = !!val; + + if (tpc_enabled != ah->tpc_enabled) { + ah->tpc_enabled = tpc_enabled; + ath9k_hw_set_txpowerlimit(ah, sc->cur_chan->txpower, false); + } + + return count; +} + +static const struct file_operations fops_tpc = { + .read = read_file_tpc, + .write = write_file_tpc, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + /* Ethtool support for get-stats */ #define AMKSTR(nm) #nm "_BE", #nm "_BK", #nm "_VI", #nm "_VO" @@ -1260,14 +1315,14 @@ int ath9k_init_debug(struct ath_hw *ah) ath9k_tx99_init_debug(sc); ath9k_cmn_spectral_init_debug(&sc->spec_priv, sc->debug.debugfs_phy); - debugfs_create_file("dma", S_IRUSR, sc->debug.debugfs_phy, sc, - &fops_dma); - debugfs_create_file("interrupt", S_IRUSR, sc->debug.debugfs_phy, sc, - &fops_interrupt); - debugfs_create_file("xmit", S_IRUSR, sc->debug.debugfs_phy, sc, - &fops_xmit); - debugfs_create_file("queues", S_IRUSR, sc->debug.debugfs_phy, sc, - &fops_queues); + debugfs_create_devm_seqfile(sc->dev, "dma", sc->debug.debugfs_phy, + read_file_dma); + debugfs_create_devm_seqfile(sc->dev, "interrupt", sc->debug.debugfs_phy, + read_file_interrupt); + debugfs_create_devm_seqfile(sc->dev, "xmit", sc->debug.debugfs_phy, + read_file_xmit); + debugfs_create_devm_seqfile(sc->dev, "queues", sc->debug.debugfs_phy, + read_file_queues); debugfs_create_u32("qlen_bk", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->tx.txq_max_pending[IEEE80211_AC_BK]); debugfs_create_u32("qlen_be", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, @@ -1276,10 +1331,10 @@ int ath9k_init_debug(struct ath_hw *ah) &sc->tx.txq_max_pending[IEEE80211_AC_VI]); debugfs_create_u32("qlen_vo", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, &sc->tx.txq_max_pending[IEEE80211_AC_VO]); - debugfs_create_file("misc", S_IRUSR, sc->debug.debugfs_phy, sc, - &fops_misc); - debugfs_create_file("reset", S_IRUSR, sc->debug.debugfs_phy, sc, - &fops_reset); + debugfs_create_devm_seqfile(sc->dev, "misc", sc->debug.debugfs_phy, + read_file_misc); + debugfs_create_devm_seqfile(sc->dev, "reset", sc->debug.debugfs_phy, + read_file_reset); ath9k_cmn_debug_recv(sc->debug.debugfs_phy, &sc->debug.stats.rxstats); ath9k_cmn_debug_phy_err(sc->debug.debugfs_phy, &sc->debug.stats.rxstats); @@ -1301,8 +1356,9 @@ int ath9k_init_debug(struct ath_hw *ah) &ah->config.cwm_ignore_extcca); debugfs_create_file("regdump", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_regdump); - debugfs_create_file("dump_nfcal", S_IRUSR, sc->debug.debugfs_phy, sc, - &fops_dump_nfcal); + debugfs_create_devm_seqfile(sc->dev, "dump_nfcal", + sc->debug.debugfs_phy, + read_file_dump_nfcal); ath9k_cmn_debug_base_eeprom(sc->debug.debugfs_phy, sc->sc_ah); ath9k_cmn_debug_modal_eeprom(sc->debug.debugfs_phy, sc->sc_ah); @@ -1320,10 +1376,17 @@ int ath9k_init_debug(struct ath_hw *ah) &fops_btcoex); #endif +#ifdef CONFIG_ATH9K_WOW + debugfs_create_file("wow", S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, &fops_wow); +#endif + #ifdef CONFIG_ATH9K_DYNACK debugfs_create_file("ack_to", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, sc, &fops_ackto); #endif + debugfs_create_file("tpc", S_IRUSR | S_IWUSR, + sc->debug.debugfs_phy, sc, &fops_tpc); return 0; } diff --git a/drivers/net/wireless/ath/ath9k/eeprom_4k.c b/drivers/net/wireless/ath/ath9k/eeprom_4k.c index 07b806c56c56..e5a78d4fd66e 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c @@ -748,6 +748,20 @@ static void ath9k_hw_4k_set_txpower(struct ath_hw *ah, | ATH9K_POW_SM(ratesArray[rateDupCck], 0)); } + /* TPC initializations */ + if (ah->tpc_enabled) { + int ht40_delta; + + ht40_delta = (IS_CHAN_HT40(chan)) ? ht40PowerIncForPdadc : 0; + ar5008_hw_init_rate_txpower(ah, ratesArray, chan, ht40_delta); + /* Enable TPC */ + REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX, + MAX_RATE_POWER | AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE); + } else { + /* Disable TPC */ + REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX, MAX_RATE_POWER); + } + REGWRITE_BUFFER_FLUSH(ah); } diff --git a/drivers/net/wireless/ath/ath9k/eeprom_9287.c b/drivers/net/wireless/ath/ath9k/eeprom_9287.c index 5ba1385c9838..6ca33dfde1fd 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c @@ -886,6 +886,21 @@ static void ath9k_hw_ar9287_set_txpower(struct ath_hw *ah, | ATH9K_POW_SM(ratesArray[rateDupOfdm], 8) | ATH9K_POW_SM(ratesArray[rateDupCck], 0)); } + + /* TPC initializations */ + if (ah->tpc_enabled) { + int ht40_delta; + + ht40_delta = (IS_CHAN_HT40(chan)) ? ht40PowerIncForPdadc : 0; + ar5008_hw_init_rate_txpower(ah, ratesArray, chan, ht40_delta); + /* Enable TPC */ + REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX, + MAX_RATE_POWER | AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE); + } else { + /* Disable TPC */ + REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX, MAX_RATE_POWER); + } + REGWRITE_BUFFER_FLUSH(ah); } diff --git a/drivers/net/wireless/ath/ath9k/eeprom_def.c b/drivers/net/wireless/ath/ath9k/eeprom_def.c index 122b846b8ec0..098059039351 100644 --- a/drivers/net/wireless/ath/ath9k/eeprom_def.c +++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c @@ -1332,6 +1332,20 @@ static void ath9k_hw_def_set_txpower(struct ath_hw *ah, ATH9K_POW_SM(pModal->pwrDecreaseFor3Chain, 6) | ATH9K_POW_SM(pModal->pwrDecreaseFor2Chain, 0)); + /* TPC initializations */ + if (ah->tpc_enabled) { + int ht40_delta; + + ht40_delta = (IS_CHAN_HT40(chan)) ? ht40PowerIncForPdadc : 0; + ar5008_hw_init_rate_txpower(ah, ratesArray, chan, ht40_delta); + /* Enable TPC */ + REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX, + MAX_RATE_POWER | AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE); + } else { + /* Disable TPC */ + REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX, MAX_RATE_POWER); + } + REGWRITE_BUFFER_FLUSH(ah); } diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index 2fef7a480fec..da344b27326c 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -49,7 +49,7 @@ void ath_init_leds(struct ath_softc *sc) if (AR_SREV_9100(sc->sc_ah)) return; - if (!led_blink) + if (!ath9k_led_blink) sc->led_cdev.default_trigger = ieee80211_get_radio_led_name(sc->hw); diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index 9dde265d3f84..300d3671d0ef 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -44,6 +44,9 @@ extern struct ieee80211_ops ath9k_htc_ops; extern int htc_modparam_nohwcrypt; +#ifdef CONFIG_MAC80211_LEDS +extern int ath9k_htc_led_blink; +#endif enum htc_phymode { HTC_MODE_11NA = 0, diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c index 50f74a2a4cf8..2aabcbdaba4e 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c @@ -279,6 +279,10 @@ void ath9k_init_leds(struct ath9k_htc_priv *priv) else priv->ah->led_pin = ATH_LED_PIN_DEF; + if (!ath9k_htc_led_blink) + priv->led_cdev.default_trigger = + ieee80211_get_radio_led_name(priv->hw); + ath9k_configure_leds(priv); snprintf(priv->led_name, sizeof(priv->led_name), diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index e8fa9448da24..fd229409f676 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -39,6 +39,10 @@ module_param_named(ps_enable, ath9k_ps_enable, int, 0444); MODULE_PARM_DESC(ps_enable, "Enable WLAN PowerSave"); #ifdef CONFIG_MAC80211_LEDS +int ath9k_htc_led_blink = 1; +module_param_named(blink, ath9k_htc_led_blink, int, 0444); +MODULE_PARM_DESC(blink, "Enable LED blink on activity"); + static const struct ieee80211_tpt_blink ath9k_htc_tpt_blink[] = { { .throughput = 0 * 1024, .blink_time = 334 }, { .throughput = 1 * 1024, .blink_time = 260 }, diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c index a0ff5b637054..d2408da38c1c 100644 --- a/drivers/net/wireless/ath/ath9k/htc_hst.c +++ b/drivers/net/wireless/ath/ath9k/htc_hst.c @@ -351,11 +351,7 @@ void ath9k_htc_txcompletion_cb(struct htc_target *htc_handle, return; ret: - /* HTC-generated packets are freed here. */ - if (htc_hdr && htc_hdr->endpoint_id != ENDPOINT0) - dev_kfree_skb_any(skb); - else - kfree_skb(skb); + kfree_skb(skb); } static void ath9k_htc_fw_panic_report(struct htc_target *htc_handle, diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 6d4b273469b1..60aa8d71e753 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -246,6 +246,8 @@ static void ath9k_hw_read_revisions(struct ath_hw *ah) case AR9300_DEVID_AR953X: ah->hw_version.macVersion = AR_SREV_VERSION_9531; return; + case AR9300_DEVID_QCA956X: + ah->hw_version.macVersion = AR_SREV_VERSION_9561; } val = REG_READ(ah, AR_SREV) & AR_SREV_ID; @@ -422,6 +424,8 @@ static void ath9k_hw_init_defaults(struct ath_hw *ah) ah->power_mode = ATH9K_PM_UNDEFINED; ah->htc_reset_init = true; + ah->tpc_enabled = true; + ah->ani_function = ATH9K_ANI_ALL; if (!AR_SREV_9300_20_OR_LATER(ah)) ah->ani_function &= ~ATH9K_ANI_MRC_CCK; @@ -536,6 +540,7 @@ static int __ath9k_hw_init(struct ath_hw *ah) case AR_SREV_VERSION_9550: case AR_SREV_VERSION_9565: case AR_SREV_VERSION_9531: + case AR_SREV_VERSION_9561: break; default: ath_err(common, @@ -636,6 +641,7 @@ int ath9k_hw_init(struct ath_hw *ah) case AR9485_DEVID_AR1111: case AR9300_DEVID_AR9565: case AR9300_DEVID_AR953X: + case AR9300_DEVID_QCA956X: break; default: if (common->bus_ops->ath_bus_type == ATH_USB) @@ -776,7 +782,8 @@ static void ath9k_hw_init_pll(struct ath_hw *ah, /* program BB PLL phase_shift */ REG_RMW_FIELD(ah, AR_CH0_BB_DPLL3, AR_CH0_BB_DPLL3_PHASE_SHIFT, 0x1); - } else if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) { + } else if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah) || + AR_SREV_9561(ah)) { u32 regval, pll2_divint, pll2_divfrac, refdiv; REG_WRITE(ah, AR_RTC_PLL_CONTROL, @@ -787,7 +794,7 @@ static void ath9k_hw_init_pll(struct ath_hw *ah, udelay(100); if (ah->is_clk_25mhz) { - if (AR_SREV_9531(ah)) { + if (AR_SREV_9531(ah) || AR_SREV_9561(ah)) { pll2_divint = 0x1c; pll2_divfrac = 0xa3d2; refdiv = 1; @@ -803,14 +810,15 @@ static void ath9k_hw_init_pll(struct ath_hw *ah, refdiv = 5; } else { pll2_divint = 0x11; - pll2_divfrac = - AR_SREV_9531(ah) ? 0x26665 : 0x26666; + pll2_divfrac = (AR_SREV_9531(ah) || + AR_SREV_9561(ah)) ? + 0x26665 : 0x26666; refdiv = 1; } } regval = REG_READ(ah, AR_PHY_PLL_MODE); - if (AR_SREV_9531(ah)) + if (AR_SREV_9531(ah) || AR_SREV_9561(ah)) regval |= (0x1 << 22); else regval |= (0x1 << 16); @@ -828,14 +836,16 @@ static void ath9k_hw_init_pll(struct ath_hw *ah, (0x1 << 13) | (0x4 << 26) | (0x18 << 19); - else if (AR_SREV_9531(ah)) + else if (AR_SREV_9531(ah) || AR_SREV_9561(ah)) { regval = (regval & 0x01c00fff) | (0x1 << 31) | (0x2 << 29) | (0xa << 25) | - (0x1 << 19) | - (0x6 << 12); - else + (0x1 << 19); + + if (AR_SREV_9531(ah)) + regval |= (0x6 << 12); + } else regval = (regval & 0x80071fff) | (0x3 << 30) | (0x1 << 13) | @@ -843,7 +853,7 @@ static void ath9k_hw_init_pll(struct ath_hw *ah, (0x60 << 19); REG_WRITE(ah, AR_PHY_PLL_MODE, regval); - if (AR_SREV_9531(ah)) + if (AR_SREV_9531(ah) || AR_SREV_9561(ah)) REG_WRITE(ah, AR_PHY_PLL_MODE, REG_READ(ah, AR_PHY_PLL_MODE) & 0xffbfffff); else @@ -882,7 +892,8 @@ static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah, AR_IMR_RXORN | AR_IMR_BCNMISC; - if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) + if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah) || + AR_SREV_9561(ah)) sync_default &= ~AR_INTR_SYNC_HOST1_FATAL; if (AR_SREV_9300_20_OR_LATER(ah)) { @@ -1671,7 +1682,8 @@ static void ath9k_hw_init_desc(struct ath_hw *ah) } #ifdef __BIG_ENDIAN else if (AR_SREV_9330(ah) || AR_SREV_9340(ah) || - AR_SREV_9550(ah) || AR_SREV_9531(ah)) + AR_SREV_9550(ah) || AR_SREV_9531(ah) || + AR_SREV_9561(ah)) REG_RMW(ah, AR_CFG, AR_CFG_SWRB | AR_CFG_SWTB, 0); else REG_WRITE(ah, AR_CFG, AR_CFG_SWTD | AR_CFG_SWRD); @@ -2459,7 +2471,8 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) if (AR_SREV_9300_20_OR_LATER(ah)) { pCap->hw_caps |= ATH9K_HW_CAP_EDMA | ATH9K_HW_CAP_FASTCLOCK; - if (!AR_SREV_9330(ah) && !AR_SREV_9485(ah) && !AR_SREV_9565(ah)) + if (!AR_SREV_9330(ah) && !AR_SREV_9485(ah) && + !AR_SREV_9561(ah) && !AR_SREV_9565(ah)) pCap->hw_caps |= ATH9K_HW_CAP_LDPC; pCap->rx_hp_qdepth = ATH9K_HW_RX_HP_QDEPTH; @@ -2476,7 +2489,9 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) if (AR_SREV_9300_20_OR_LATER(ah)) pCap->hw_caps |= ATH9K_HW_CAP_RAC_SUPPORTED; - if (AR_SREV_9300_20_OR_LATER(ah)) + if (AR_SREV_9561(ah)) + ah->ent_mode = 0x3BDA000; + else if (AR_SREV_9300_20_OR_LATER(ah)) ah->ent_mode = REG_READ(ah, AR_ENT_OTP); if (AR_SREV_9287_11_OR_LATER(ah) || AR_SREV_9271(ah)) @@ -2529,13 +2544,17 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) pCap->hw_caps |= ATH9K_HW_CAP_RTT; } - if (AR_SREV_9462(ah)) - pCap->hw_caps |= ATH9K_HW_WOW_DEVICE_CAPABLE; - if (AR_SREV_9300_20_OR_LATER(ah) && ah->eep_ops->get_eeprom(ah, EEP_PAPRD)) pCap->hw_caps |= ATH9K_HW_CAP_PAPRD; +#ifdef CONFIG_ATH9K_WOW + if (AR_SREV_9462_20_OR_LATER(ah) || AR_SREV_9565_11_OR_LATER(ah)) + ah->wow.max_patterns = MAX_NUM_PATTERN; + else + ah->wow.max_patterns = MAX_NUM_PATTERN_LEGACY; +#endif + return 0; } diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 1cbd33551513..e82e570de330 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -54,6 +54,7 @@ #define AR9485_DEVID_AR1111 0x0037 #define AR9300_DEVID_AR9565 0x0036 #define AR9300_DEVID_AR953X 0x003d +#define AR9300_DEVID_QCA956X 0x003f #define AR5416_AR9100_DEVID 0x000b @@ -198,12 +199,13 @@ #define KAL_NUM_DESC_WORDS 12 #define KAL_ANTENNA_MODE 1 #define KAL_TO_DS 1 -#define KAL_DELAY 4 /*delay of 4ms between 2 KAL frames */ +#define KAL_DELAY 4 /* delay of 4ms between 2 KAL frames */ #define KAL_TIMEOUT 900 #define MAX_PATTERN_SIZE 256 #define MAX_PATTERN_MASK_SIZE 32 -#define MAX_NUM_PATTERN 8 +#define MAX_NUM_PATTERN 16 +#define MAX_NUM_PATTERN_LEGACY 8 #define MAX_NUM_USER_PATTERN 6 /* deducting the disassociate and deauthenticate packets */ @@ -247,12 +249,10 @@ enum ath9k_hw_caps { #ifdef CONFIG_ATH9K_PCOEM ATH9K_HW_CAP_RTT = BIT(14), ATH9K_HW_CAP_MCI = BIT(15), - ATH9K_HW_WOW_DEVICE_CAPABLE = BIT(16), ATH9K_HW_CAP_BT_ANT_DIV = BIT(17), #else ATH9K_HW_CAP_RTT = 0, ATH9K_HW_CAP_MCI = 0, - ATH9K_HW_WOW_DEVICE_CAPABLE = 0, ATH9K_HW_CAP_BT_ANT_DIV = 0, #endif ATH9K_HW_CAP_DFS = BIT(18), @@ -271,6 +271,12 @@ enum ath9k_hw_caps { * of those types. */ +struct ath9k_hw_wow { + u32 wow_event_mask; + u32 wow_event_mask2; + u8 max_patterns; +}; + struct ath9k_hw_capabilities { u32 hw_caps; /* ATH9K_HW_CAP_* from ath9k_hw_caps */ u16 rts_aggr_limit; @@ -929,7 +935,7 @@ struct ath_hw { u32 ent_mode; #ifdef CONFIG_ATH9K_WOW - u32 wow_event_mask; + struct ath9k_hw_wow wow; #endif bool is_clk_25mhz; int (*get_mac_revision)(void); @@ -1086,6 +1092,8 @@ bool ar9003_is_paprd_enabled(struct ath_hw *ah); void ar9003_hw_set_chain_masks(struct ath_hw *ah, u8 rx, u8 tx); void ar9003_hw_init_rate_txpower(struct ath_hw *ah, u8 *rate_array, struct ath9k_channel *chan); +void ar5008_hw_init_rate_txpower(struct ath_hw *ah, int16_t *rate_array, + struct ath9k_channel *chan, int ht40_delta); /* Hardware family op attach helpers */ int ar5008_hw_attach_phy_ops(struct ath_hw *ah); @@ -1145,23 +1153,19 @@ ath9k_hw_get_btcoex_scheme(struct ath_hw *ah) #ifdef CONFIG_ATH9K_WOW -const char *ath9k_hw_wow_event_to_string(u32 wow_event); -void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern, - u8 *user_mask, int pattern_count, - int pattern_len); +int ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern, + u8 *user_mask, int pattern_count, + int pattern_len); u32 ath9k_hw_wow_wakeup(struct ath_hw *ah); void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable); #else -static inline const char *ath9k_hw_wow_event_to_string(u32 wow_event) -{ - return NULL; -} -static inline void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, - u8 *user_pattern, - u8 *user_mask, - int pattern_count, - int pattern_len) +static inline int ath9k_hw_wow_apply_pattern(struct ath_hw *ah, + u8 *user_pattern, + u8 *user_mask, + int pattern_count, + int pattern_len) { + return 0; } static inline u32 ath9k_hw_wow_wakeup(struct ath_hw *ah) { diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index d1c39346b264..6c6e88495394 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -45,8 +45,8 @@ int ath9k_modparam_nohwcrypt; module_param_named(nohwcrypt, ath9k_modparam_nohwcrypt, int, 0444); MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption"); -int led_blink; -module_param_named(blink, led_blink, int, 0444); +int ath9k_led_blink; +module_param_named(blink, ath9k_led_blink, int, 0444); MODULE_PARM_DESC(blink, "Enable LED blink on activity"); static int ath9k_btcoex_enable; @@ -996,6 +996,7 @@ void ath9k_deinit_device(struct ath_softc *sc) ath9k_ps_restore(sc); ath9k_deinit_debug(sc); + ath9k_deinit_wow(hw); ieee80211_unregister_hw(hw); ath_rx_cleanup(sc); ath9k_deinit_softc(sc); diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index b829263e3d0a..90631d768a60 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -516,14 +516,14 @@ int ath_update_survey_stats(struct ath_softc *sc) ath_hw_cycle_counters_update(common); if (cc->cycles > 0) { - survey->filled |= SURVEY_INFO_CHANNEL_TIME | - SURVEY_INFO_CHANNEL_TIME_BUSY | - SURVEY_INFO_CHANNEL_TIME_RX | - SURVEY_INFO_CHANNEL_TIME_TX; - survey->channel_time += cc->cycles / div; - survey->channel_time_busy += cc->rx_busy / div; - survey->channel_time_rx += cc->rx_frame / div; - survey->channel_time_tx += cc->tx_frame / div; + survey->filled |= SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_RX | + SURVEY_INFO_TIME_TX; + survey->time += cc->cycles / div; + survey->time_busy += cc->rx_busy / div; + survey->time_rx += cc->rx_frame / div; + survey->time_tx += cc->tx_frame / div; } if (cc->cycles < div) diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index 3e58bfa0c1fd..bba85d1a6cd1 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -820,7 +820,8 @@ void ath9k_hw_enable_interrupts(struct ath_hw *ah) return; } - if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah)) + if (AR_SREV_9340(ah) || AR_SREV_9550(ah) || AR_SREV_9531(ah) || + AR_SREV_9561(ah)) sync_default &= ~AR_INTR_SYNC_HOST1_FATAL; async_mask = AR_INTR_MAC_IRQ; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 9a72640237cb..9ede991b8d76 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -285,6 +285,7 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan) __ath_cancel_work(sc); + disable_irq(sc->irq); tasklet_disable(&sc->intr_tq); tasklet_disable(&sc->bcon_tasklet); spin_lock_bh(&sc->sc_pcu_lock); @@ -331,6 +332,7 @@ static int ath_reset_internal(struct ath_softc *sc, struct ath9k_channel *hchan) r = -EIO; out: + enable_irq(sc->irq); spin_unlock_bh(&sc->sc_pcu_lock); tasklet_enable(&sc->bcon_tasklet); tasklet_enable(&sc->intr_tq); @@ -512,9 +514,6 @@ irqreturn_t ath_isr(int irq, void *dev) if (!ah || test_bit(ATH_OP_INVALID, &common->op_flags)) return IRQ_NONE; - if (!AR_SREV_9100(ah) && test_bit(ATH_OP_HW_RESET, &common->op_flags)) - return IRQ_NONE; - /* shared irq, not for us */ if (!ath9k_hw_intrpend(ah)) return IRQ_NONE; @@ -529,7 +528,7 @@ irqreturn_t ath_isr(int irq, void *dev) ath9k_debug_sync_cause(sc, sync_cause); status &= ah->imask; /* discard unasked-for bits */ - if (AR_SREV_9100(ah) && test_bit(ATH_OP_HW_RESET, &common->op_flags)) + if (test_bit(ATH_OP_HW_RESET, &common->op_flags)) return IRQ_HANDLED; /* @@ -556,15 +555,6 @@ irqreturn_t ath_isr(int irq, void *dev) (status & ATH9K_INT_BB_WATCHDOG)) goto chip_reset; -#ifdef CONFIG_ATH9K_WOW - if (status & ATH9K_INT_BMISS) { - if (atomic_read(&sc->wow_sleep_proc_intr) == 0) { - atomic_inc(&sc->wow_got_bmiss_intr); - atomic_dec(&sc->wow_sleep_proc_intr); - } - } -#endif - if (status & ATH9K_INT_SWBA) tasklet_schedule(&sc->bcon_tasklet); diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index f009b5b57e5e..e6fef1be9977 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -427,6 +427,11 @@ static const struct pci_device_id ath_pci_id_table[] = { { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, 0x0036, 0x11AD, /* LITEON */ + 0x1842), + .driver_data = ATH9K_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ 0x6671), .driver_data = ATH9K_PCI_AR9565_1ANT }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, @@ -446,9 +451,19 @@ static const struct pci_device_id ath_pci_id_table[] = { .driver_data = ATH9K_PCI_AR9565_1ANT }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, 0x0036, + 0x1B9A, /* XAVI */ + 0x28A3), + .driver_data = ATH9K_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, PCI_VENDOR_ID_AZWAVE, 0x218A), .driver_data = ATH9K_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x2F8A), + .driver_data = ATH9K_PCI_AR9565_1ANT }, /* WB335 1-ANT / Antenna Diversity */ { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, @@ -508,6 +523,11 @@ static const struct pci_device_id ath_pci_id_table[] = { .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x213C), + .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, PCI_VENDOR_ID_HP, 0x18E3), .driver_data = ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_BT_ANT_DIV }, @@ -555,6 +575,16 @@ static const struct pci_device_id ath_pci_id_table[] = { .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x4129), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x412A), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, PCI_VENDOR_ID_ATHEROS, 0x3027), .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, @@ -586,10 +616,25 @@ static const struct pci_device_id ath_pci_id_table[] = { { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, 0x0036, 0x11AD, /* LITEON */ + 0x1832), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ 0x0692), .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, 0x0036, + 0x11AD, /* LITEON */ + 0x0803), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0813), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, PCI_VENDOR_ID_AZWAVE, 0x2130), .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, @@ -605,6 +650,21 @@ static const struct pci_device_id ath_pci_id_table[] = { .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x218B), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x218C), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x2F82), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, 0x144F, /* ASKEY */ 0x7202), .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, @@ -616,10 +676,20 @@ static const struct pci_device_id ath_pci_id_table[] = { { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, 0x0036, 0x1B9A, /* XAVI */ + 0x2813), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x1B9A, /* XAVI */ 0x28A2), .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, 0x0036, + 0x1B9A, /* XAVI */ + 0x28A4), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, 0x185F, /* WNC */ 0x3027), .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, @@ -636,10 +706,25 @@ static const struct pci_device_id ath_pci_id_table[] = { { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, 0x0036, PCI_VENDOR_ID_FOXCONN, + 0xE08F), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_FOXCONN, 0xE081), .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, 0x0036, + PCI_VENDOR_ID_FOXCONN, + 0xE091), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_FOXCONN, + 0xE099), + .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, PCI_VENDOR_ID_LENOVO, 0x3026), .driver_data = ATH9K_PCI_AR9565_2ANT | ATH9K_PCI_BT_ANT_DIV }, @@ -913,9 +998,12 @@ static int ath_pci_suspend(struct device *device) struct pci_dev *pdev = to_pci_dev(device); struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); - if (sc->wow_enabled) + if (test_bit(ATH_OP_WOW_ENABLED, &common->op_flags)) { + dev_info(&pdev->dev, "WOW is enabled, bypassing PCI suspend\n"); return 0; + } /* The device has to be moved to FULLSLEEP forcibly. * Otherwise the chip never moved to full sleep, diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 7395afbc5124..6fb40ef86fd6 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -425,7 +425,8 @@ u32 ath_calcrxfilter(struct ath_softc *sc) rfilt |= ATH9K_RX_FILTER_MCAST_BCAST_ALL; } - if (AR_SREV_9550(sc->sc_ah) || AR_SREV_9531(sc->sc_ah)) + if (AR_SREV_9550(sc->sc_ah) || AR_SREV_9531(sc->sc_ah) || + AR_SREV_9561(sc->sc_ah)) rfilt |= ATH9K_RX_FILTER_4ADDRESS; if (ath9k_is_chanctx_enabled() && diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index fb11a9172f38..9587ec655680 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -814,6 +814,7 @@ #define AR_SREV_REVISION_9531_10 0 #define AR_SREV_REVISION_9531_11 1 #define AR_SREV_REVISION_9531_20 2 +#define AR_SREV_VERSION_9561 0x600 #define AR_SREV_5416(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_5416_PCI) || \ @@ -899,10 +900,13 @@ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9485)) #define AR_SREV_9565(_ah) \ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9565)) +#define AR_SREV_9003_PCOEM(_ah) \ + (AR_SREV_9462(_ah) || AR_SREV_9485(_ah) || AR_SREV_9565(_ah)) #else #define AR_SREV_9462(_ah) 0 #define AR_SREV_9485(_ah) 0 #define AR_SREV_9565(_ah) 0 +#define AR_SREV_9003_PCOEM(_ah) 0 #endif #define AR_SREV_9485_11_OR_LATER(_ah) \ @@ -974,6 +978,9 @@ (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9531) && \ ((_ah)->hw_version.macRev == AR_SREV_REVISION_9531_20)) +#define AR_SREV_9561(_ah) \ + (((_ah)->hw_version.macVersion == AR_SREV_VERSION_9561)) + /* NOTE: When adding chips newer than Peacock, add chip check here */ #define AR_SREV_9580_10_OR_LATER(_ah) \ (AR_SREV_9580(_ah)) @@ -1876,6 +1883,7 @@ enum { #define AR_FIRST_NDP_TIMER 7 #define AR_NDP2_PERIOD 0x81a0 #define AR_NDP2_TIMER_MODE 0x81c0 +#define AR_GEN_TIMERS2_MODE_ENABLE_MASK 0x000000FF #define AR_GEN_TIMERS(_i) (0x8200 + ((_i) << 2)) #define AR_NEXT_TBTT_TIMER AR_GEN_TIMERS(0) @@ -1971,6 +1979,7 @@ enum { #define AR_DIRECT_CONNECT 0x83a0 #define AR_DC_AP_STA_EN 0x00000001 +#define AR_DC_TSF2_ENABLE 0x00000001 #define AR_AES_MUTE_MASK0 0x805c #define AR_AES_MUTE_MASK0_FC 0x0000FFFF @@ -2003,126 +2012,6 @@ enum { #define AR_WOW_BEACON_TIMO_MAX 0xffffffff -/* - * MAC WoW Registers - */ - -#define AR_WOW_PATTERN 0x825C -#define AR_WOW_COUNT 0x8260 -#define AR_WOW_BCN_EN 0x8270 -#define AR_WOW_BCN_TIMO 0x8274 -#define AR_WOW_KEEP_ALIVE_TIMO 0x8278 -#define AR_WOW_KEEP_ALIVE 0x827c -#define AR_WOW_US_SCALAR 0x8284 -#define AR_WOW_KEEP_ALIVE_DELAY 0x8288 -#define AR_WOW_PATTERN_MATCH 0x828c -#define AR_WOW_PATTERN_OFF1 0x8290 /* pattern bytes 0 -> 3 */ -#define AR_WOW_PATTERN_OFF2 0x8294 /* pattern bytes 4 -> 7 */ - -/* for AR9285 or later version of chips */ -#define AR_WOW_EXACT 0x829c -#define AR_WOW_LENGTH1 0x8360 -#define AR_WOW_LENGTH2 0X8364 -/* register to enable match for less than 256 bytes packets */ -#define AR_WOW_PATTERN_MATCH_LT_256B 0x8368 - -#define AR_SW_WOW_CONTROL 0x20018 -#define AR_SW_WOW_ENABLE 0x1 -#define AR_SWITCH_TO_REFCLK 0x2 -#define AR_RESET_CONTROL 0x4 -#define AR_RESET_VALUE_MASK 0x8 -#define AR_HW_WOW_DISABLE 0x10 -#define AR_CLR_MAC_INTERRUPT 0x20 -#define AR_CLR_KA_INTERRUPT 0x40 - -/* AR_WOW_PATTERN register values */ -#define AR_WOW_BACK_OFF_SHIFT(x) ((x & 0xf) << 28) /* in usecs */ -#define AR_WOW_MAC_INTR_EN 0x00040000 -#define AR_WOW_MAGIC_EN 0x00010000 -#define AR_WOW_PATTERN_EN(x) (x & 0xff) -#define AR_WOW_PAT_FOUND_SHIFT 8 -#define AR_WOW_PATTERN_FOUND(x) (x & (0xff << AR_WOW_PAT_FOUND_SHIFT)) -#define AR_WOW_PATTERN_FOUND_MASK ((0xff) << AR_WOW_PAT_FOUND_SHIFT) -#define AR_WOW_MAGIC_PAT_FOUND 0x00020000 -#define AR_WOW_MAC_INTR 0x00080000 -#define AR_WOW_KEEP_ALIVE_FAIL 0x00100000 -#define AR_WOW_BEACON_FAIL 0x00200000 - -#define AR_WOW_STATUS(x) (x & (AR_WOW_PATTERN_FOUND_MASK | \ - AR_WOW_MAGIC_PAT_FOUND | \ - AR_WOW_KEEP_ALIVE_FAIL | \ - AR_WOW_BEACON_FAIL)) -#define AR_WOW_CLEAR_EVENTS(x) (x & ~(AR_WOW_PATTERN_EN(0xff) | \ - AR_WOW_MAGIC_EN | \ - AR_WOW_MAC_INTR_EN | \ - AR_WOW_BEACON_FAIL | \ - AR_WOW_KEEP_ALIVE_FAIL)) - -/* AR_WOW_COUNT register values */ -#define AR_WOW_AIFS_CNT(x) (x & 0xff) -#define AR_WOW_SLOT_CNT(x) ((x & 0xff) << 8) -#define AR_WOW_KEEP_ALIVE_CNT(x) ((x & 0xff) << 16) - -/* AR_WOW_BCN_EN register */ -#define AR_WOW_BEACON_FAIL_EN 0x00000001 - -/* AR_WOW_BCN_TIMO rgister */ -#define AR_WOW_BEACON_TIMO 0x40000000 /* valid if BCN_EN is set */ - -/* AR_WOW_KEEP_ALIVE_TIMO register */ -#define AR_WOW_KEEP_ALIVE_TIMO_VALUE -#define AR_WOW_KEEP_ALIVE_NEVER 0xffffffff - -/* AR_WOW_KEEP_ALIVE register */ -#define AR_WOW_KEEP_ALIVE_AUTO_DIS 0x00000001 -#define AR_WOW_KEEP_ALIVE_FAIL_DIS 0x00000002 - -/* AR_WOW_KEEP_ALIVE_DELAY register */ -#define AR_WOW_KEEP_ALIVE_DELAY_VALUE 0x000003e8 /* 1 msec */ - - -/* - * keep it long for beacon workaround - ensure no false alarm - */ -#define AR_WOW_BMISSTHRESHOLD 0x20 - -/* AR_WOW_PATTERN_MATCH register */ -#define AR_WOW_PAT_END_OF_PKT(x) (x & 0xf) -#define AR_WOW_PAT_OFF_MATCH(x) ((x & 0xf) << 8) - -/* - * default values for Wow Configuration for backoff, aifs, slot, keep-alive - * to be programmed into various registers. - */ -#define AR_WOW_PAT_BACKOFF 0x00000004 /* AR_WOW_PATTERN_REG */ -#define AR_WOW_CNT_AIFS_CNT 0x00000022 /* AR_WOW_COUNT_REG */ -#define AR_WOW_CNT_SLOT_CNT 0x00000009 /* AR_WOW_COUNT_REG */ -/* - * Keepalive count applicable for AR9280 2.0 and above. - */ -#define AR_WOW_CNT_KA_CNT 0x00000008 /* AR_WOW_COUNT register */ - -/* WoW - Transmit buffer for keep alive frames */ -#define AR_WOW_TRANSMIT_BUFFER 0xe000 /* E000 - EFFC */ - -#define AR_WOW_TXBUF(i) (AR_WOW_TRANSMIT_BUFFER + ((i) << 2)) - -#define AR_WOW_KA_DESC_WORD2 0xe000 - -#define AR_WOW_KA_DATA_WORD0 0xe030 - -/* WoW Transmit Buffer for patterns */ -#define AR_WOW_TB_PATTERN(i) (0xe100 + (i << 8)) -#define AR_WOW_TB_MASK(i) (0xec00 + (i << 5)) - -/* Currently Pattern 0-7 are supported - so bit 0-7 are set */ -#define AR_WOW_PATTERN_SUPPORTED 0xff -#define AR_WOW_LENGTH_MAX 0xff -#define AR_WOW_LEN1_SHIFT(_i) ((0x3 - ((_i) & 0x3)) << 0x3) -#define AR_WOW_LENGTH1_MASK(_i) (AR_WOW_LENGTH_MAX << AR_WOW_LEN1_SHIFT(_i)) -#define AR_WOW_LEN2_SHIFT(_i) ((0x7 - ((_i) & 0x7)) << 0x3) -#define AR_WOW_LENGTH2_MASK(_i) (AR_WOW_LENGTH_MAX << AR_WOW_LEN2_SHIFT(_i)) - #define AR9271_CORE_CLOCK 117 /* clock to 117Mhz */ #define AR9271_TARGET_BAUD_RATE 19200 /* 115200 */ diff --git a/drivers/net/wireless/ath/ath9k/reg_wow.h b/drivers/net/wireless/ath/ath9k/reg_wow.h new file mode 100644 index 000000000000..3abfca56ca58 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/reg_wow.h @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2015 Qualcomm Atheros Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef REG_WOW_H +#define REG_WOW_H + +#define AR_WOW_PATTERN 0x825C +#define AR_WOW_COUNT 0x8260 +#define AR_WOW_BCN_EN 0x8270 +#define AR_WOW_BCN_TIMO 0x8274 +#define AR_WOW_KEEP_ALIVE_TIMO 0x8278 +#define AR_WOW_KEEP_ALIVE 0x827c +#define AR_WOW_KEEP_ALIVE_DELAY 0x8288 +#define AR_WOW_PATTERN_MATCH 0x828c + +/* + * AR_WOW_LENGTH1 + * bit 31:24 pattern 0 length + * bit 23:16 pattern 1 length + * bit 15:8 pattern 2 length + * bit 7:0 pattern 3 length + * + * AR_WOW_LENGTH2 + * bit 31:24 pattern 4 length + * bit 23:16 pattern 5 length + * bit 15:8 pattern 6 length + * bit 7:0 pattern 7 length + * + * AR_WOW_LENGTH3 + * bit 31:24 pattern 8 length + * bit 23:16 pattern 9 length + * bit 15:8 pattern 10 length + * bit 7:0 pattern 11 length + * + * AR_WOW_LENGTH4 + * bit 31:24 pattern 12 length + * bit 23:16 pattern 13 length + * bit 15:8 pattern 14 length + * bit 7:0 pattern 15 length + */ +#define AR_WOW_LENGTH1 0x8360 +#define AR_WOW_LENGTH2 0X8364 +#define AR_WOW_LENGTH3 0X8380 +#define AR_WOW_LENGTH4 0X8384 + +#define AR_WOW_PATTERN_MATCH_LT_256B 0x8368 +#define AR_MAC_PCU_WOW4 0x8370 + +#define AR_SW_WOW_CONTROL 0x20018 +#define AR_SW_WOW_ENABLE 0x1 +#define AR_SWITCH_TO_REFCLK 0x2 +#define AR_RESET_CONTROL 0x4 +#define AR_RESET_VALUE_MASK 0x8 +#define AR_HW_WOW_DISABLE 0x10 +#define AR_CLR_MAC_INTERRUPT 0x20 +#define AR_CLR_KA_INTERRUPT 0x40 + +#define AR_WOW_BACK_OFF_SHIFT(x) ((x & 0xf) << 27) /* in usecs */ +#define AR_WOW_MAC_INTR_EN 0x00040000 +#define AR_WOW_MAGIC_EN 0x00010000 +#define AR_WOW_PATTERN_EN(x) (x & 0xff) +#define AR_WOW_PAT_FOUND_SHIFT 8 +#define AR_WOW_PATTERN_FOUND(x) (x & (0xff << AR_WOW_PAT_FOUND_SHIFT)) +#define AR_WOW_PATTERN_FOUND_MASK ((0xff) << AR_WOW_PAT_FOUND_SHIFT) +#define AR_WOW_MAGIC_PAT_FOUND 0x00020000 +#define AR_WOW_MAC_INTR 0x00080000 +#define AR_WOW_KEEP_ALIVE_FAIL 0x00100000 +#define AR_WOW_BEACON_FAIL 0x00200000 + +#define AR_WOW_STATUS(x) (x & (AR_WOW_PATTERN_FOUND_MASK | \ + AR_WOW_MAGIC_PAT_FOUND | \ + AR_WOW_KEEP_ALIVE_FAIL | \ + AR_WOW_BEACON_FAIL)) +#define AR_WOW_CLEAR_EVENTS(x) (x & ~(AR_WOW_PATTERN_EN(0xff) | \ + AR_WOW_MAGIC_EN | \ + AR_WOW_MAC_INTR_EN | \ + AR_WOW_BEACON_FAIL | \ + AR_WOW_KEEP_ALIVE_FAIL)) + +#define AR_WOW_AIFS_CNT(x) (x & 0xff) +#define AR_WOW_SLOT_CNT(x) ((x & 0xff) << 8) +#define AR_WOW_KEEP_ALIVE_CNT(x) ((x & 0xff) << 16) + +#define AR_WOW_BEACON_FAIL_EN 0x00000001 +#define AR_WOW_BEACON_TIMO 0x40000000 +#define AR_WOW_KEEP_ALIVE_NEVER 0xffffffff +#define AR_WOW_KEEP_ALIVE_AUTO_DIS 0x00000001 +#define AR_WOW_KEEP_ALIVE_FAIL_DIS 0x00000002 +#define AR_WOW_KEEP_ALIVE_DELAY_VALUE 0x000003e8 /* 1 msec */ +#define AR_WOW_BMISSTHRESHOLD 0x20 +#define AR_WOW_PAT_END_OF_PKT(x) (x & 0xf) +#define AR_WOW_PAT_OFF_MATCH(x) ((x & 0xf) << 8) +#define AR_WOW_PAT_BACKOFF 0x00000004 +#define AR_WOW_CNT_AIFS_CNT 0x00000022 +#define AR_WOW_CNT_SLOT_CNT 0x00000009 +#define AR_WOW_CNT_KA_CNT 0x00000008 + +#define AR_WOW_TRANSMIT_BUFFER 0xe000 +#define AR_WOW_TXBUF(i) (AR_WOW_TRANSMIT_BUFFER + ((i) << 2)) +#define AR_WOW_KA_DESC_WORD2 0xe000 +#define AR_WOW_TB_PATTERN(i) (0xe100 + (i << 8)) +#define AR_WOW_TB_MASK(i) (0xec00 + (i << 5)) +#define AR_WOW_PATTERN_SUPPORTED_LEGACY 0xff +#define AR_WOW_PATTERN_SUPPORTED 0xffff +#define AR_WOW_LENGTH_MAX 0xff +#define AR_WOW_LEN1_SHIFT(_i) ((0x3 - ((_i) & 0x3)) << 0x3) +#define AR_WOW_LENGTH1_MASK(_i) (AR_WOW_LENGTH_MAX << AR_WOW_LEN1_SHIFT(_i)) +#define AR_WOW_LEN2_SHIFT(_i) ((0x7 - ((_i) & 0x7)) << 0x3) +#define AR_WOW_LENGTH2_MASK(_i) (AR_WOW_LENGTH_MAX << AR_WOW_LEN2_SHIFT(_i)) +#define AR_WOW_LEN3_SHIFT(_i) ((0xb - ((_i) & 0xb)) << 0x3) +#define AR_WOW_LENGTH3_MASK(_i) (AR_WOW_LENGTH_MAX << AR_WOW_LEN3_SHIFT(_i)) +#define AR_WOW_LEN4_SHIFT(_i) ((0xf - ((_i) & 0xf)) << 0x3) +#define AR_WOW_LENGTH4_MASK(_i) (AR_WOW_LENGTH_MAX << AR_WOW_LEN4_SHIFT(_i)) + +#endif /* REG_WOW_H */ diff --git a/drivers/net/wireless/ath/ath9k/wow.c b/drivers/net/wireless/ath/ath9k/wow.c index 5f30e580d942..8d0b1730a9d5 100644 --- a/drivers/net/wireless/ath/ath9k/wow.c +++ b/drivers/net/wireless/ath/ath9k/wow.c @@ -16,36 +16,43 @@ #include "ath9k.h" -static const struct wiphy_wowlan_support ath9k_wowlan_support = { +static const struct wiphy_wowlan_support ath9k_wowlan_support_legacy = { .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, .n_patterns = MAX_NUM_USER_PATTERN, .pattern_min_len = 1, .pattern_max_len = MAX_PATTERN_SIZE, }; -static void ath9k_wow_map_triggers(struct ath_softc *sc, - struct cfg80211_wowlan *wowlan, - u32 *wow_triggers) +static const struct wiphy_wowlan_support ath9k_wowlan_support = { + .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, + .n_patterns = MAX_NUM_PATTERN - 2, + .pattern_min_len = 1, + .pattern_max_len = MAX_PATTERN_SIZE, +}; + +static u8 ath9k_wow_map_triggers(struct ath_softc *sc, + struct cfg80211_wowlan *wowlan) { + u8 wow_triggers = 0; + if (wowlan->disconnect) - *wow_triggers |= AH_WOW_LINK_CHANGE | - AH_WOW_BEACON_MISS; + wow_triggers |= AH_WOW_LINK_CHANGE | + AH_WOW_BEACON_MISS; if (wowlan->magic_pkt) - *wow_triggers |= AH_WOW_MAGIC_PATTERN_EN; + wow_triggers |= AH_WOW_MAGIC_PATTERN_EN; if (wowlan->n_patterns) - *wow_triggers |= AH_WOW_USER_PATTERN_EN; - - sc->wow_enabled = *wow_triggers; + wow_triggers |= AH_WOW_USER_PATTERN_EN; + return wow_triggers; } -static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc) +static int ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); int pattern_count = 0; - int i, byte_cnt; + int ret, i, byte_cnt = 0; u8 dis_deauth_pattern[MAX_PATTERN_SIZE]; u8 dis_deauth_mask[MAX_PATTERN_SIZE]; @@ -80,12 +87,7 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc) * | x:x:x:x:x:x -- 22 bytes */ - /* Create Disassociate Pattern first */ - - byte_cnt = 0; - /* Fill out the mask with all FF's */ - for (i = 0; i < MAX_PATTERN_MASK_SIZE; i++) dis_deauth_mask[i] = 0xff; @@ -108,19 +110,17 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc) byte_cnt += 6; /* copy the bssid, its same as the source mac address */ - memcpy((dis_deauth_pattern + byte_cnt), common->curbssid, ETH_ALEN); /* Create Disassociate pattern mask */ - dis_deauth_mask[0] = 0xfe; dis_deauth_mask[1] = 0x03; dis_deauth_mask[2] = 0xc0; - ath_dbg(common, WOW, "Adding disassoc/deauth patterns for WoW\n"); - - ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask, - pattern_count, byte_cnt); + ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask, + pattern_count, byte_cnt); + if (ret) + goto exit; pattern_count++; /* @@ -129,59 +129,39 @@ static void ath9k_wow_add_disassoc_deauth_pattern(struct ath_softc *sc) */ dis_deauth_pattern[0] = 0xC0; - ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask, - pattern_count, byte_cnt); - + ret = ath9k_hw_wow_apply_pattern(ah, dis_deauth_pattern, dis_deauth_mask, + pattern_count, byte_cnt); +exit: + return ret; } -static void ath9k_wow_add_pattern(struct ath_softc *sc, - struct cfg80211_wowlan *wowlan) +static int ath9k_wow_add_pattern(struct ath_softc *sc, + struct cfg80211_wowlan *wowlan) { struct ath_hw *ah = sc->sc_ah; - struct ath9k_wow_pattern *wow_pattern = NULL; struct cfg80211_pkt_pattern *patterns = wowlan->patterns; - int mask_len; + u8 wow_pattern[MAX_PATTERN_SIZE]; + u8 wow_mask[MAX_PATTERN_SIZE]; + int mask_len, ret = 0; s8 i = 0; - if (!wowlan->n_patterns) - return; - - /* - * Add the new user configured patterns - */ for (i = 0; i < wowlan->n_patterns; i++) { - - wow_pattern = kzalloc(sizeof(*wow_pattern), GFP_KERNEL); - - if (!wow_pattern) - return; - - /* - * TODO: convert the generic user space pattern to - * appropriate chip specific/802.11 pattern. - */ - - mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); - memset(wow_pattern->pattern_bytes, 0, MAX_PATTERN_SIZE); - memset(wow_pattern->mask_bytes, 0, MAX_PATTERN_SIZE); - memcpy(wow_pattern->pattern_bytes, patterns[i].pattern, - patterns[i].pattern_len); - memcpy(wow_pattern->mask_bytes, patterns[i].mask, mask_len); - wow_pattern->pattern_len = patterns[i].pattern_len; - - /* - * just need to take care of deauth and disssoc pattern, - * make sure we don't overwrite them. - */ - - ath9k_hw_wow_apply_pattern(ah, wow_pattern->pattern_bytes, - wow_pattern->mask_bytes, - i + 2, - wow_pattern->pattern_len); - kfree(wow_pattern); - + mask_len = DIV_ROUND_UP(patterns[i].pattern_len, 8); + memset(wow_pattern, 0, MAX_PATTERN_SIZE); + memset(wow_mask, 0, MAX_PATTERN_SIZE); + memcpy(wow_pattern, patterns[i].pattern, patterns[i].pattern_len); + memcpy(wow_mask, patterns[i].mask, mask_len); + + ret = ath9k_hw_wow_apply_pattern(ah, + wow_pattern, + wow_mask, + i + 2, + patterns[i].pattern_len); + if (ret) + break; } + return ret; } int ath9k_suspend(struct ieee80211_hw *hw, @@ -190,41 +170,39 @@ int ath9k_suspend(struct ieee80211_hw *hw, struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); - u32 wow_triggers_enabled = 0; + u8 triggers; int ret = 0; ath9k_deinit_channel_context(sc); mutex_lock(&sc->mutex); - ath_cancel_work(sc); - ath_stop_ani(sc); - if (test_bit(ATH_OP_INVALID, &common->op_flags)) { - ath_dbg(common, ANY, "Device not present\n"); - ret = -EINVAL; + ath_err(common, "Device not present\n"); + ret = -ENODEV; goto fail_wow; } if (WARN_ON(!wowlan)) { - ath_dbg(common, WOW, "None of the WoW triggers enabled\n"); + ath_err(common, "None of the WoW triggers enabled\n"); ret = -EINVAL; goto fail_wow; } - if (!device_can_wakeup(sc->dev)) { - ath_dbg(common, WOW, "device_can_wakeup failed, WoW is not enabled\n"); + if (sc->cur_chan->nvifs > 1) { + ath_dbg(common, WOW, "WoW for multivif is not yet supported\n"); ret = 1; goto fail_wow; } - /* - * none of the sta vifs are associated - * and we are not currently handling multivif - * cases, for instance we have to seperately - * configure 'keep alive frame' for each - * STA. - */ + if (ath9k_is_chanctx_enabled()) { + if (test_bit(ATH_OP_MULTI_CHANNEL, &common->op_flags)) { + ath_dbg(common, WOW, + "Multi-channel WOW is not supported\n"); + ret = 1; + goto fail_wow; + } + } if (!test_bit(ATH_OP_PRIM_STA_VIF, &common->op_flags)) { ath_dbg(common, WOW, "None of the STA vifs are associated\n"); @@ -232,16 +210,15 @@ int ath9k_suspend(struct ieee80211_hw *hw, goto fail_wow; } - if (sc->cur_chan->nvifs > 1) { - ath_dbg(common, WOW, "WoW for multivif is not yet supported\n"); + triggers = ath9k_wow_map_triggers(sc, wowlan); + if (!triggers) { + ath_dbg(common, WOW, "No valid WoW triggers\n"); ret = 1; goto fail_wow; } - ath9k_wow_map_triggers(sc, wowlan, &wow_triggers_enabled); - - ath_dbg(common, WOW, "WoW triggers enabled 0x%x\n", - wow_triggers_enabled); + ath_cancel_work(sc); + ath_stop_ani(sc); ath9k_ps_wakeup(sc); @@ -251,10 +228,21 @@ int ath9k_suspend(struct ieee80211_hw *hw, * Enable wake up on recieving disassoc/deauth * frame by default. */ - ath9k_wow_add_disassoc_deauth_pattern(sc); + ret = ath9k_wow_add_disassoc_deauth_pattern(sc); + if (ret) { + ath_err(common, + "Unable to add disassoc/deauth pattern: %d\n", ret); + goto fail_wow; + } - if (wow_triggers_enabled & AH_WOW_USER_PATTERN_EN) - ath9k_wow_add_pattern(sc, wowlan); + if (triggers & AH_WOW_USER_PATTERN_EN) { + ret = ath9k_wow_add_pattern(sc, wowlan); + if (ret) { + ath_err(common, + "Unable to add user pattern: %d\n", ret); + goto fail_wow; + } + } spin_lock_bh(&sc->sc_pcu_lock); /* @@ -278,12 +266,12 @@ int ath9k_suspend(struct ieee80211_hw *hw, synchronize_irq(sc->irq); tasklet_kill(&sc->intr_tq); - ath9k_hw_wow_enable(ah, wow_triggers_enabled); + ath9k_hw_wow_enable(ah, triggers); ath9k_ps_restore(sc); - ath_dbg(common, ANY, "WoW enabled in ath9k\n"); - atomic_inc(&sc->wow_sleep_proc_intr); + ath_dbg(common, WOW, "Suspend with WoW triggers: 0x%x\n", triggers); + set_bit(ATH_OP_WOW_ENABLED, &common->op_flags); fail_wow: mutex_unlock(&sc->mutex); return ret; @@ -294,7 +282,7 @@ int ath9k_resume(struct ieee80211_hw *hw) struct ath_softc *sc = hw->priv; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); - u32 wow_status; + u8 status; mutex_lock(&sc->mutex); @@ -309,29 +297,14 @@ int ath9k_resume(struct ieee80211_hw *hw) spin_unlock_bh(&sc->sc_pcu_lock); - wow_status = ath9k_hw_wow_wakeup(ah); - - if (atomic_read(&sc->wow_got_bmiss_intr) == 0) { - /* - * some devices may not pick beacon miss - * as the reason they woke up so we add - * that here for that shortcoming. - */ - wow_status |= AH_WOW_BEACON_MISS; - atomic_dec(&sc->wow_got_bmiss_intr); - ath_dbg(common, ANY, "Beacon miss interrupt picked up during WoW sleep\n"); - } - - atomic_dec(&sc->wow_sleep_proc_intr); - - if (wow_status) { - ath_dbg(common, ANY, "Waking up due to WoW triggers %s with WoW status = %x\n", - ath9k_hw_wow_event_to_string(wow_status), wow_status); - } + status = ath9k_hw_wow_wakeup(ah); + ath_dbg(common, WOW, "Resume with WoW status: 0x%x\n", status); ath_restart_work(sc); ath9k_start_btcoex(sc); + clear_bit(ATH_OP_WOW_ENABLED, &common->op_flags); + ath9k_ps_restore(sc); mutex_unlock(&sc->mutex); @@ -341,22 +314,35 @@ int ath9k_resume(struct ieee80211_hw *hw) void ath9k_set_wakeup(struct ieee80211_hw *hw, bool enabled) { struct ath_softc *sc = hw->priv; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); mutex_lock(&sc->mutex); - device_init_wakeup(sc->dev, 1); device_set_wakeup_enable(sc->dev, enabled); mutex_unlock(&sc->mutex); + + ath_dbg(common, WOW, "WoW wakeup source is %s\n", + (enabled) ? "enabled" : "disabled"); } void ath9k_init_wow(struct ieee80211_hw *hw) { struct ath_softc *sc = hw->priv; + struct ath_hw *ah = sc->sc_ah; + + if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow) { + if (AR_SREV_9462_20_OR_LATER(ah) || AR_SREV_9565_11_OR_LATER(ah)) + hw->wiphy->wowlan = &ath9k_wowlan_support; + else + hw->wiphy->wowlan = &ath9k_wowlan_support_legacy; - if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_WOW_DEVICE_CAPABLE) && - (sc->driver_data & ATH9K_PCI_WOW) && - device_can_wakeup(sc->dev)) - hw->wiphy->wowlan = &ath9k_wowlan_support; + device_init_wakeup(sc->dev, 1); + } +} + +void ath9k_deinit_wow(struct ieee80211_hw *hw) +{ + struct ath_softc *sc = hw->priv; - atomic_set(&sc->wow_sleep_proc_intr, -1); - atomic_set(&sc->wow_got_bmiss_intr, -1); + if ((sc->driver_data & ATH9K_PCI_WOW) || sc->force_wow) + device_init_wakeup(sc->dev, 0); } diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index e9bd02c2e844..1b8e75c4d2c2 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1097,24 +1097,65 @@ void ath_update_max_aggr_framelen(struct ath_softc *sc, int queue, int txop) } static u8 ath_get_rate_txpower(struct ath_softc *sc, struct ath_buf *bf, - u8 rateidx) + u8 rateidx, bool is_40, bool is_cck) { u8 max_power; + struct sk_buff *skb; + struct ath_frame_info *fi; + struct ieee80211_tx_info *info; struct ath_hw *ah = sc->sc_ah; - if (sc->tx99_state) + if (sc->tx99_state || !ah->tpc_enabled) return MAX_RATE_POWER; + skb = bf->bf_mpdu; + fi = get_frame_info(skb); + info = IEEE80211_SKB_CB(skb); + if (!AR_SREV_9300_20_OR_LATER(ah)) { - /* ar9002 is not sipported for the moment */ - return MAX_RATE_POWER; - } + int txpower = fi->tx_power; - if (!bf->bf_state.bfs_paprd) { - struct sk_buff *skb = bf->bf_mpdu; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ath_frame_info *fi = get_frame_info(skb); + if (is_40) { + u8 power_ht40delta; + struct ar5416_eeprom_def *eep = &ah->eeprom.def; + + if (AR5416_VER_MASK >= AR5416_EEP_MINOR_VER_2) { + bool is_2ghz; + struct modal_eep_header *pmodal; + is_2ghz = info->band == IEEE80211_BAND_2GHZ; + pmodal = &eep->modalHeader[is_2ghz]; + power_ht40delta = pmodal->ht40PowerIncForPdadc; + } else { + power_ht40delta = 2; + } + txpower += power_ht40delta; + } + + if (AR_SREV_9287(ah) || AR_SREV_9285(ah) || + AR_SREV_9271(ah)) { + txpower -= 2 * AR9287_PWR_TABLE_OFFSET_DB; + } else if (AR_SREV_9280_20_OR_LATER(ah)) { + s8 power_offset; + + power_offset = ah->eep_ops->get_eeprom(ah, + EEP_PWR_TABLE_OFFSET); + txpower -= 2 * power_offset; + } + + if (OLC_FOR_AR9280_20_LATER && is_cck) + txpower -= 2; + + txpower = max(txpower, 0); + max_power = min_t(u8, ah->tx_power[rateidx], txpower); + + /* XXX: clamp minimum TX power at 1 for AR9160 since if + * max_power is set to 0, frames are transmitted at max + * TX power + */ + if (!max_power && !AR_SREV_9280_20_OR_LATER(ah)) + max_power = 1; + } else if (!bf->bf_state.bfs_paprd) { if (rateidx < 8 && (info->flags & IEEE80211_TX_CTL_STBC)) max_power = min(ah->tx_power_stbc[rateidx], fi->tx_power); @@ -1152,7 +1193,7 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf, info->rtscts_rate = fi->rtscts_rate; for (i = 0; i < ARRAY_SIZE(bf->rates); i++) { - bool is_40, is_sgi, is_sp; + bool is_40, is_sgi, is_sp, is_cck; int phy; if (!rates[i].count || (rates[i].idx < 0)) @@ -1198,7 +1239,8 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf, if (rix < 8 && (tx_info->flags & IEEE80211_TX_CTL_STBC)) info->rates[i].RateFlags |= ATH9K_RATESERIES_STBC; - info->txpower[i] = ath_get_rate_txpower(sc, bf, rix); + info->txpower[i] = ath_get_rate_txpower(sc, bf, rix, + is_40, false); continue; } @@ -1227,7 +1269,9 @@ static void ath_buf_set_rate(struct ath_softc *sc, struct ath_buf *bf, info->rates[i].PktDuration = ath9k_hw_computetxtime(sc->sc_ah, phy, rate->bitrate * 100, len, rix, is_sp); - info->txpower[i] = ath_get_rate_txpower(sc, bf, rix); + is_cck = IS_CCK_RATE(info->rates[i].Rate); + info->txpower[i] = ath_get_rate_txpower(sc, bf, rix, false, + is_cck); } /* For AR5416 - RTS cannot be followed by a frame larger than 8K */ @@ -2259,7 +2303,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, struct ath_txq *txq = txctl->txq; struct ath_atx_tid *tid = NULL; struct ath_buf *bf; - bool queue, skip_uapsd = false; + bool queue, skip_uapsd = false, ps_resp; int q, ret; if (vif) @@ -2268,6 +2312,8 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) txctl->force_channel = true; + ps_resp = !!(info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE); + ret = ath_tx_prepare(hw, skb, txctl); if (ret) return ret; @@ -2310,7 +2356,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, if (txctl->an && queue) tid = ath_get_skb_tid(sc, txctl->an, skb); - if (!skip_uapsd && (info->flags & IEEE80211_TX_CTL_PS_RESPONSE)) { + if (!skip_uapsd && ps_resp) { ath_txq_unlock(sc, txq); txq = sc->tx.uapsdq; ath_txq_lock(sc, txq); @@ -2443,9 +2489,12 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, if (sc->sc_ah->caldata) set_bit(PAPRD_PACKET_SENT, &sc->sc_ah->caldata->cal_flags); - if (!(tx_flags & ATH_TX_ERROR)) - /* Frame was ACKed */ - tx_info->flags |= IEEE80211_TX_STAT_ACK; + if (!(tx_flags & ATH_TX_ERROR)) { + if (tx_info->flags & IEEE80211_TX_CTL_NO_ACK) + tx_info->flags |= IEEE80211_TX_STAT_NOACK_TRANSMITTED; + else + tx_info->flags |= IEEE80211_TX_STAT_ACK; + } padpos = ieee80211_hdrlen(hdr->frame_control); padsize = padpos & 3; diff --git a/drivers/net/wireless/ath/carl9170/cmd.c b/drivers/net/wireless/ath/carl9170/cmd.c index 39a63874b275..f2b4f537e4c1 100644 --- a/drivers/net/wireless/ath/carl9170/cmd.c +++ b/drivers/net/wireless/ath/carl9170/cmd.c @@ -188,12 +188,12 @@ int carl9170_collect_tally(struct ar9170 *ar) if (ar->channel) { info = &ar->survey[ar->channel->hw_value]; - info->channel_time = ar->tally.active; - info->channel_time_busy = ar->tally.cca; - info->channel_time_tx = ar->tally.tx_time; - do_div(info->channel_time, 1000); - do_div(info->channel_time_busy, 1000); - do_div(info->channel_time_tx, 1000); + info->time = ar->tally.active; + info->time_busy = ar->tally.cca; + info->time_tx = ar->tally.tx_time; + do_div(info->time, 1000); + do_div(info->time_busy, 1000); + do_div(info->time_tx, 1000); } } return 0; diff --git a/drivers/net/wireless/ath/carl9170/main.c b/drivers/net/wireless/ath/carl9170/main.c index ef5b6dc7b7f1..f1455a04cb62 100644 --- a/drivers/net/wireless/ath/carl9170/main.c +++ b/drivers/net/wireless/ath/carl9170/main.c @@ -1690,9 +1690,9 @@ found: survey->filled |= SURVEY_INFO_IN_USE; if (ar->fw.hw_counters) { - survey->filled |= SURVEY_INFO_CHANNEL_TIME | - SURVEY_INFO_CHANNEL_TIME_BUSY | - SURVEY_INFO_CHANNEL_TIME_TX; + survey->filled |= SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_TX; } return 0; diff --git a/drivers/net/wireless/ath/dfs_pattern_detector.c b/drivers/net/wireless/ath/dfs_pattern_detector.c index cfd0554cf140..3d57f8772389 100644 --- a/drivers/net/wireless/ath/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/dfs_pattern_detector.c @@ -86,7 +86,7 @@ static const struct radar_detector_specs fcc_radar_ref_types[] = { FCC_PATTERN(1, 0, 5, 150, 230, 1, 23), FCC_PATTERN(2, 6, 10, 200, 500, 1, 16), FCC_PATTERN(3, 11, 20, 200, 500, 1, 12), - FCC_PATTERN(4, 50, 100, 1000, 2000, 1, 20), + FCC_PATTERN(4, 50, 100, 1000, 2000, 1, 1), FCC_PATTERN(5, 0, 1, 333, 333, 1, 9), }; diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c index 73f12f196f14..086549b732b9 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.c +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c @@ -84,6 +84,7 @@ static int wcn36xx_dxe_allocate_ctl_block(struct wcn36xx_dxe_ch *ch) if (!cur_ctl) goto out_fail; + spin_lock_init(&cur_ctl->skb_lock); cur_ctl->ctl_blk_order = i; if (i == 0) { ch->head_blk_ctl = cur_ctl; @@ -354,6 +355,8 @@ static void reap_tx_dxes(struct wcn36xx *wcn, struct wcn36xx_dxe_ch *ch) * and while-do will not make any cycles. */ do { + if (ctl->desc->ctrl & WCN36XX_DXE_CTRL_VALID_MASK) + break; if (ctl->skb) { dma_unmap_single(NULL, ctl->desc->src_addr_l, ctl->skb->len, DMA_TO_DEVICE); diff --git a/drivers/net/wireless/ath/wcn36xx/main.c b/drivers/net/wireless/ath/wcn36xx/main.c index 7dd8873f757e..0783d2ed8238 100644 --- a/drivers/net/wireless/ath/wcn36xx/main.c +++ b/drivers/net/wireless/ath/wcn36xx/main.c @@ -298,6 +298,8 @@ static int wcn36xx_start(struct ieee80211_hw *hw) wcn36xx_debugfs_init(wcn); INIT_LIST_HEAD(&wcn->vif_list); + spin_lock_init(&wcn->dxe_lock); + return 0; out_smd_stop: @@ -795,6 +797,7 @@ static int wcn36xx_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, wcn36xx_dbg(WCN36XX_DBG_MAC, "mac sta add vif %p sta %pM\n", vif, sta->addr); + spin_lock_init(&sta_priv->ampdu_lock); vif_priv->sta = sta_priv; sta_priv->vif = vif_priv; /* @@ -873,21 +876,32 @@ static int wcn36xx_ampdu_action(struct ieee80211_hw *hw, get_sta_index(vif, sta_priv)); wcn36xx_smd_add_ba(wcn); wcn36xx_smd_trigger_ba(wcn, get_sta_index(vif, sta_priv)); - ieee80211_start_tx_ba_session(sta, tid, 0); break; case IEEE80211_AMPDU_RX_STOP: wcn36xx_smd_del_ba(wcn, tid, get_sta_index(vif, sta_priv)); break; case IEEE80211_AMPDU_TX_START: + spin_lock_bh(&sta_priv->ampdu_lock); + sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_START; + spin_unlock_bh(&sta_priv->ampdu_lock); + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: + spin_lock_bh(&sta_priv->ampdu_lock); + sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_OPERATIONAL; + spin_unlock_bh(&sta_priv->ampdu_lock); + wcn36xx_smd_add_ba_session(wcn, sta, tid, ssn, 1, get_sta_index(vif, sta_priv)); break; case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: case IEEE80211_AMPDU_TX_STOP_CONT: + spin_lock_bh(&sta_priv->ampdu_lock); + sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_NONE; + spin_unlock_bh(&sta_priv->ampdu_lock); + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; default: diff --git a/drivers/net/wireless/ath/wcn36xx/smd.c b/drivers/net/wireless/ath/wcn36xx/smd.c index 63986931829e..69ed39731902 100644 --- a/drivers/net/wireless/ath/wcn36xx/smd.c +++ b/drivers/net/wireless/ath/wcn36xx/smd.c @@ -21,6 +21,61 @@ #include <linux/bitops.h> #include "smd.h" +struct wcn36xx_cfg_val { + u32 cfg_id; + u32 value; +}; + +#define WCN36XX_CFG_VAL(id, val) \ +{ \ + .cfg_id = WCN36XX_HAL_CFG_ ## id, \ + .value = val \ +} + +static struct wcn36xx_cfg_val wcn36xx_cfg_vals[] = { + WCN36XX_CFG_VAL(CURRENT_TX_ANTENNA, 1), + WCN36XX_CFG_VAL(CURRENT_RX_ANTENNA, 1), + WCN36XX_CFG_VAL(LOW_GAIN_OVERRIDE, 0), + WCN36XX_CFG_VAL(POWER_STATE_PER_CHAIN, 785), + WCN36XX_CFG_VAL(CAL_PERIOD, 5), + WCN36XX_CFG_VAL(CAL_CONTROL, 1), + WCN36XX_CFG_VAL(PROXIMITY, 0), + WCN36XX_CFG_VAL(NETWORK_DENSITY, 3), + WCN36XX_CFG_VAL(MAX_MEDIUM_TIME, 6000), + WCN36XX_CFG_VAL(MAX_MPDUS_IN_AMPDU, 64), + WCN36XX_CFG_VAL(RTS_THRESHOLD, 2347), + WCN36XX_CFG_VAL(SHORT_RETRY_LIMIT, 6), + WCN36XX_CFG_VAL(LONG_RETRY_LIMIT, 6), + WCN36XX_CFG_VAL(FRAGMENTATION_THRESHOLD, 8000), + WCN36XX_CFG_VAL(DYNAMIC_THRESHOLD_ZERO, 5), + WCN36XX_CFG_VAL(DYNAMIC_THRESHOLD_ONE, 10), + WCN36XX_CFG_VAL(DYNAMIC_THRESHOLD_TWO, 15), + WCN36XX_CFG_VAL(FIXED_RATE, 0), + WCN36XX_CFG_VAL(RETRYRATE_POLICY, 4), + WCN36XX_CFG_VAL(RETRYRATE_SECONDARY, 0), + WCN36XX_CFG_VAL(RETRYRATE_TERTIARY, 0), + WCN36XX_CFG_VAL(FORCE_POLICY_PROTECTION, 5), + WCN36XX_CFG_VAL(FIXED_RATE_MULTICAST_24GHZ, 1), + WCN36XX_CFG_VAL(FIXED_RATE_MULTICAST_5GHZ, 5), + WCN36XX_CFG_VAL(DEFAULT_RATE_INDEX_5GHZ, 5), + WCN36XX_CFG_VAL(MAX_BA_SESSIONS, 40), + WCN36XX_CFG_VAL(PS_DATA_INACTIVITY_TIMEOUT, 200), + WCN36XX_CFG_VAL(PS_ENABLE_BCN_FILTER, 1), + WCN36XX_CFG_VAL(PS_ENABLE_RSSI_MONITOR, 1), + WCN36XX_CFG_VAL(NUM_BEACON_PER_RSSI_AVERAGE, 20), + WCN36XX_CFG_VAL(STATS_PERIOD, 10), + WCN36XX_CFG_VAL(CFP_MAX_DURATION, 30000), + WCN36XX_CFG_VAL(FRAME_TRANS_ENABLED, 0), + WCN36XX_CFG_VAL(BA_THRESHOLD_HIGH, 128), + WCN36XX_CFG_VAL(MAX_BA_BUFFERS, 2560), + WCN36XX_CFG_VAL(DYNAMIC_PS_POLL_VALUE, 0), + WCN36XX_CFG_VAL(TX_PWR_CTRL_ENABLE, 1), + WCN36XX_CFG_VAL(ENABLE_CLOSE_LOOP, 1), + WCN36XX_CFG_VAL(ENABLE_LPWR_IMG_TRANSITION, 0), + WCN36XX_CFG_VAL(MAX_ASSOC_LIMIT, 10), + WCN36XX_CFG_VAL(ENABLE_MCC_ADAPTIVE_SCHEDULER, 0), +}; + static int put_cfg_tlv_u32(struct wcn36xx *wcn, size_t *len, u32 id, u32 value) { struct wcn36xx_hal_cfg *entry; @@ -357,8 +412,10 @@ static int wcn36xx_smd_start_rsp(struct wcn36xx *wcn, void *buf, size_t len) int wcn36xx_smd_start(struct wcn36xx *wcn) { - struct wcn36xx_hal_mac_start_req_msg msg_body; + struct wcn36xx_hal_mac_start_req_msg msg_body, *body; int ret = 0; + int i; + size_t len; mutex_lock(&wcn->hal_mutex); INIT_HAL_MSG(msg_body, WCN36XX_HAL_START_REQ); @@ -368,10 +425,22 @@ int wcn36xx_smd_start(struct wcn36xx *wcn) PREPARE_HAL_BUF(wcn->hal_buf, msg_body); + body = (struct wcn36xx_hal_mac_start_req_msg *)wcn->hal_buf; + len = body->header.len; + + for (i = 0; i < ARRAY_SIZE(wcn36xx_cfg_vals); i++) { + ret = put_cfg_tlv_u32(wcn, &len, wcn36xx_cfg_vals[i].cfg_id, + wcn36xx_cfg_vals[i].value); + if (ret) + goto out; + } + body->header.len = len; + body->params.len = len - sizeof(*body); + wcn36xx_dbg(WCN36XX_DBG_HAL, "hal start type %d\n", msg_body.params.type); - ret = wcn36xx_smd_send_and_wait(wcn, msg_body.header.len); + ret = wcn36xx_smd_send_and_wait(wcn, body->header.len); if (ret) { wcn36xx_err("Sending hal_start failed\n"); goto out; diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.c b/drivers/net/wireless/ath/wcn36xx/txrx.c index 32bb26a0db2a..9bec8237231d 100644 --- a/drivers/net/wireless/ath/wcn36xx/txrx.c +++ b/drivers/net/wireless/ath/wcn36xx/txrx.c @@ -93,6 +93,7 @@ static void wcn36xx_set_tx_pdu(struct wcn36xx_tx_bd *bd, bd->pdu.mpdu_header_off; bd->pdu.mpdu_len = len; bd->pdu.tid = tid; + bd->pdu.bd_ssn = WCN36XX_TXBD_SSN_FILL_DPU_QOS; } static inline struct wcn36xx_vif *get_vif_by_addr(struct wcn36xx *wcn, @@ -110,15 +111,54 @@ static inline struct wcn36xx_vif *get_vif_by_addr(struct wcn36xx *wcn, wcn36xx_warn("vif %pM not found\n", addr); return NULL; } + +static void wcn36xx_tx_start_ampdu(struct wcn36xx *wcn, + struct wcn36xx_sta *sta_priv, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_sta *sta; + u8 *qc, tid; + + if (!conf_is_ht(&wcn->hw->conf)) + return; + + sta = wcn36xx_priv_to_sta(sta_priv); + + if (WARN_ON(!ieee80211_is_data_qos(hdr->frame_control))) + return; + + if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO) + return; + + qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; + + spin_lock(&sta_priv->ampdu_lock); + if (sta_priv->ampdu_state[tid] != WCN36XX_AMPDU_NONE) + goto out_unlock; + + if (sta_priv->non_agg_frame_ct++ >= WCN36XX_AMPDU_START_THRESH) { + sta_priv->ampdu_state[tid] = WCN36XX_AMPDU_START; + sta_priv->non_agg_frame_ct = 0; + ieee80211_start_tx_ba_session(sta, tid, 0); + } +out_unlock: + spin_unlock(&sta_priv->ampdu_lock); +} + static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd, struct wcn36xx *wcn, struct wcn36xx_vif **vif_priv, struct wcn36xx_sta *sta_priv, - struct ieee80211_hdr *hdr, + struct sk_buff *skb, bool bcast) { + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_vif *vif = NULL; struct wcn36xx_vif *__vif_priv = NULL; + bool is_data_qos; + bd->bd_rate = WCN36XX_BD_RATE_DATA; /* @@ -157,14 +197,26 @@ static void wcn36xx_set_tx_data(struct wcn36xx_tx_bd *bd, bd->ack_policy = 1; } *vif_priv = __vif_priv; + + is_data_qos = ieee80211_is_data_qos(hdr->frame_control); + + wcn36xx_set_tx_pdu(bd, + is_data_qos ? + sizeof(struct ieee80211_qos_hdr) : + sizeof(struct ieee80211_hdr_3addr), + skb->len, sta_priv ? sta_priv->tid : 0); + + if (sta_priv && is_data_qos) + wcn36xx_tx_start_ampdu(wcn, sta_priv, skb); } static void wcn36xx_set_tx_mgmt(struct wcn36xx_tx_bd *bd, struct wcn36xx *wcn, struct wcn36xx_vif **vif_priv, - struct ieee80211_hdr *hdr, + struct sk_buff *skb, bool bcast) { + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct wcn36xx_vif *__vif_priv = get_vif_by_addr(wcn, hdr->addr2); bd->sta_index = __vif_priv->self_sta_index; @@ -198,6 +250,12 @@ static void wcn36xx_set_tx_mgmt(struct wcn36xx_tx_bd *bd, } else bd->queue_id = WCN36XX_TX_U_WQ_ID; *vif_priv = __vif_priv; + + wcn36xx_set_tx_pdu(bd, + ieee80211_is_data_qos(hdr->frame_control) ? + sizeof(struct ieee80211_qos_hdr) : + sizeof(struct ieee80211_hdr_3addr), + skb->len, WCN36XX_TID); } int wcn36xx_start_tx(struct wcn36xx *wcn, @@ -237,7 +295,7 @@ int wcn36xx_start_tx(struct wcn36xx *wcn, bd->dpu_rf = WCN36XX_BMU_WQ_TX; - bd->tx_comp = info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS; + bd->tx_comp = !!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS); if (bd->tx_comp) { wcn36xx_dbg(WCN36XX_DBG_DXE, "TX_ACK status requested\n"); spin_lock_irqsave(&wcn->dxe_lock, flags); @@ -259,22 +317,11 @@ int wcn36xx_start_tx(struct wcn36xx *wcn, } /* Data frames served first*/ - if (is_low) { - wcn36xx_set_tx_data(bd, wcn, &vif_priv, sta_priv, hdr, bcast); - wcn36xx_set_tx_pdu(bd, - ieee80211_is_data_qos(hdr->frame_control) ? - sizeof(struct ieee80211_qos_hdr) : - sizeof(struct ieee80211_hdr_3addr), - skb->len, sta_priv ? sta_priv->tid : 0); - } else { + if (is_low) + wcn36xx_set_tx_data(bd, wcn, &vif_priv, sta_priv, skb, bcast); + else /* MGMT and CTRL frames are handeld here*/ - wcn36xx_set_tx_mgmt(bd, wcn, &vif_priv, hdr, bcast); - wcn36xx_set_tx_pdu(bd, - ieee80211_is_data_qos(hdr->frame_control) ? - sizeof(struct ieee80211_qos_hdr) : - sizeof(struct ieee80211_hdr_3addr), - skb->len, WCN36XX_TID); - } + wcn36xx_set_tx_mgmt(bd, wcn, &vif_priv, skb, bcast); buff_to_be((u32 *)bd, sizeof(*bd)/sizeof(u32)); bd->tx_bd_sign = 0xbdbdbdbd; diff --git a/drivers/net/wireless/ath/wcn36xx/txrx.h b/drivers/net/wireless/ath/wcn36xx/txrx.h index bbfbcf808c77..032216e82b2b 100644 --- a/drivers/net/wireless/ath/wcn36xx/txrx.h +++ b/drivers/net/wireless/ath/wcn36xx/txrx.h @@ -32,6 +32,12 @@ #define WCN36XX_BD_RATE_MGMT 2 #define WCN36XX_BD_RATE_CTRL 3 +enum wcn36xx_txbd_ssn_type { + WCN36XX_TXBD_SSN_FILL_HOST = 0, + WCN36XX_TXBD_SSN_FILL_DPU_NON_QOS = 1, + WCN36XX_TXBD_SSN_FILL_DPU_QOS = 2, +}; + struct wcn36xx_pdu { u32 dpu_fb:8; u32 adu_fb:8; @@ -50,7 +56,8 @@ struct wcn36xx_pdu { /* 0x0c*/ u32 reserved4:8; u32 tid:4; - u32 reserved3:4; + u32 bd_ssn:2; + u32 reserved3:2; u32 mpdu_len:16; }; diff --git a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h index f0fb81dfd17b..7b41e833e18c 100644 --- a/drivers/net/wireless/ath/wcn36xx/wcn36xx.h +++ b/drivers/net/wireless/ath/wcn36xx/wcn36xx.h @@ -32,6 +32,9 @@ #define WLAN_NV_FILE "wlan/prima/WCNSS_qcom_wlan_nv.bin" #define WCN36XX_AGGR_BUFFER_SIZE 64 +/* How many frames until we start a-mpdu TX session */ +#define WCN36XX_AMPDU_START_THRESH 20 + extern unsigned int wcn36xx_dbg_mask; enum wcn36xx_debug_mask { @@ -74,6 +77,13 @@ enum wcn36xx_debug_mask { buf, len, false); \ } while (0) +enum wcn36xx_ampdu_state { + WCN36XX_AMPDU_NONE, + WCN36XX_AMPDU_INIT, + WCN36XX_AMPDU_START, + WCN36XX_AMPDU_OPERATIONAL, +}; + #define WCN36XX_HW_CHANNEL(__wcn) (__wcn->hw->conf.chandef.chan->hw_value) #define WCN36XX_BAND(__wcn) (__wcn->hw->conf.chandef.chan->band) #define WCN36XX_CENTER_FREQ(__wcn) (__wcn->hw->conf.chandef.chan->center_freq) @@ -165,6 +175,10 @@ struct wcn36xx_sta { bool is_data_encrypted; /* Rates */ struct wcn36xx_hal_supported_rates supported_rates; + + spinlock_t ampdu_lock; /* protects next two fields */ + enum wcn36xx_ampdu_state ampdu_state[16]; + int non_agg_frame_ct; }; struct wcn36xx_dxe_ch; struct wcn36xx { @@ -243,4 +257,10 @@ static inline bool wcn36xx_is_fw_version(struct wcn36xx *wcn, } void wcn36xx_set_default_rates(struct wcn36xx_hal_supported_rates *rates); +static inline +struct ieee80211_sta *wcn36xx_priv_to_sta(struct wcn36xx_sta *sta_priv) +{ + return container_of((void *)sta_priv, struct ieee80211_sta, drv_priv); +} + #endif /* _WCN36XX_H_ */ diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig index 481680a3aa55..ce8c0381825e 100644 --- a/drivers/net/wireless/ath/wil6210/Kconfig +++ b/drivers/net/wireless/ath/wil6210/Kconfig @@ -39,12 +39,3 @@ config WIL6210_TRACING option if you are interested in debugging the driver. If unsure, say Y to make it easier to debug problems. - -config WIL6210_PLATFORM_MSM - bool "wil6210 MSM platform specific support" - depends on WIL6210 - depends on ARCH_MSM - default y - ---help--- - Say Y here to enable wil6210 driver support for MSM - platform specific features diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile index 8ad4b5f97e04..caa717bf52f3 100644 --- a/drivers/net/wireless/ath/wil6210/Makefile +++ b/drivers/net/wireless/ath/wil6210/Makefile @@ -14,7 +14,6 @@ wil6210-y += ioctl.o wil6210-y += fw.o wil6210-$(CONFIG_WIL6210_TRACING) += trace.o wil6210-y += wil_platform.o -wil6210-$(CONFIG_WIL6210_PLATFORM_MSM) += wil_platform_msm.o wil6210-y += ethtool.o # for tracing framework to find trace.h diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 38332a6dfb3a..2d5ea21be47e 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -142,14 +142,14 @@ int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, sinfo->generation = wil->sinfo_gen; - sinfo->filled = STATION_INFO_RX_BYTES | - STATION_INFO_TX_BYTES | - STATION_INFO_RX_PACKETS | - STATION_INFO_TX_PACKETS | - STATION_INFO_RX_BITRATE | - STATION_INFO_TX_BITRATE | - STATION_INFO_RX_DROP_MISC | - STATION_INFO_TX_FAILED; + sinfo->filled = BIT(NL80211_STA_INFO_RX_BYTES) | + BIT(NL80211_STA_INFO_TX_BYTES) | + BIT(NL80211_STA_INFO_RX_PACKETS) | + BIT(NL80211_STA_INFO_TX_PACKETS) | + BIT(NL80211_STA_INFO_RX_BITRATE) | + BIT(NL80211_STA_INFO_TX_BITRATE) | + BIT(NL80211_STA_INFO_RX_DROP_MISC) | + BIT(NL80211_STA_INFO_TX_FAILED); sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; sinfo->txrate.mcs = le16_to_cpu(reply.evt.bf_mcs); @@ -162,8 +162,8 @@ int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, sinfo->tx_packets = stats->tx_packets; sinfo->tx_failed = stats->tx_errors; - if (test_bit(wil_status_fwconnected, &wil->status)) { - sinfo->filled |= STATION_INFO_SIGNAL; + if (test_bit(wil_status_fwconnected, wil->status)) { + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); sinfo->signal = reply.evt.sqi; } @@ -282,7 +282,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, } /* FW don't support scan after connection attempt */ - if (test_bit(wil_status_dontscan, &wil->status)) { + if (test_bit(wil_status_dontscan, wil->status)) { wil_err(wil, "Can't scan now\n"); return -EBUSY; } @@ -334,6 +334,30 @@ out: return rc; } +static void wil_print_crypto(struct wil6210_priv *wil, + struct cfg80211_crypto_settings *c) +{ + int i, n; + + wil_dbg_misc(wil, "WPA versions: 0x%08x cipher group 0x%08x\n", + c->wpa_versions, c->cipher_group); + wil_dbg_misc(wil, "Pairwise ciphers [%d] {\n", c->n_ciphers_pairwise); + n = min_t(int, c->n_ciphers_pairwise, ARRAY_SIZE(c->ciphers_pairwise)); + for (i = 0; i < n; i++) + wil_dbg_misc(wil, " [%d] = 0x%08x\n", i, + c->ciphers_pairwise[i]); + wil_dbg_misc(wil, "}\n"); + wil_dbg_misc(wil, "AKM suites [%d] {\n", c->n_akm_suites); + n = min_t(int, c->n_akm_suites, ARRAY_SIZE(c->akm_suites)); + for (i = 0; i < n; i++) + wil_dbg_misc(wil, " [%d] = 0x%08x\n", i, + c->akm_suites[i]); + wil_dbg_misc(wil, "}\n"); + wil_dbg_misc(wil, "Control port : %d, eth_type 0x%04x no_encrypt %d\n", + c->control_port, be16_to_cpu(c->control_port_ethertype), + c->control_port_no_encrypt); +} + static void wil_print_connect_params(struct wil6210_priv *wil, struct cfg80211_connect_params *sme) { @@ -348,6 +372,7 @@ static void wil_print_connect_params(struct wil6210_priv *wil, print_hex_dump(KERN_INFO, " SSID: ", DUMP_PREFIX_OFFSET, 16, 1, sme->ssid, sme->ssid_len, true); wil_info(wil, " Privacy: %s\n", sme->privacy ? "secure" : "open"); + wil_print_crypto(wil, &sme->crypto); } static int wil_cfg80211_connect(struct wiphy *wiphy, @@ -362,8 +387,8 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, int ch; int rc = 0; - if (test_bit(wil_status_fwconnecting, &wil->status) || - test_bit(wil_status_fwconnected, &wil->status)) + if (test_bit(wil_status_fwconnecting, wil->status) || + test_bit(wil_status_fwconnected, wil->status)) return -EALREADY; wil_print_connect_params(wil, sme); @@ -450,15 +475,16 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, memcpy(conn.bssid, bss->bssid, ETH_ALEN); memcpy(conn.dst_mac, bss->bssid, ETH_ALEN); - set_bit(wil_status_fwconnecting, &wil->status); + set_bit(wil_status_fwconnecting, wil->status); rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn)); if (rc == 0) { + netif_carrier_on(ndev); /* Connect can take lots of time */ mod_timer(&wil->connect_timer, jiffies + msecs_to_jiffies(2000)); } else { - clear_bit(wil_status_fwconnecting, &wil->status); + clear_bit(wil_status_fwconnecting, wil->status); } out: @@ -618,18 +644,6 @@ static void wil_print_bcon_data(struct cfg80211_beacon_data *b) b->assocresp_ies, b->assocresp_ies_len); } -static void wil_print_crypto(struct wil6210_priv *wil, - struct cfg80211_crypto_settings *c) -{ - wil_dbg_misc(wil, "WPA versions: 0x%08x cipher group 0x%08x\n", - c->wpa_versions, c->cipher_group); - wil_dbg_misc(wil, "Pairwise ciphers [%d]\n", c->n_ciphers_pairwise); - wil_dbg_misc(wil, "AKM suites [%d]\n", c->n_akm_suites); - wil_dbg_misc(wil, "Control port : %d, eth_type 0x%04x no_encrypt %d\n", - c->control_port, be16_to_cpu(c->control_port_ethertype), - c->control_port_no_encrypt); -} - static int wil_fix_bcon(struct wil6210_priv *wil, struct cfg80211_beacon_data *bcon) { @@ -757,12 +771,12 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, wil->secure_pcp = info->privacy; + netif_carrier_on(ndev); + rc = wmi_pcp_start(wil, info->beacon_interval, wmi_nettype, channel->hw_value); if (rc) - goto out; - - netif_carrier_on(ndev); + netif_carrier_off(ndev); out: mutex_unlock(&wil->mutex); @@ -772,23 +786,26 @@ out: static int wil_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) { - int rc, rc1; struct wil6210_priv *wil = wiphy_to_wil(wiphy); wil_dbg_misc(wil, "%s()\n", __func__); + netif_carrier_off(ndev); wil_set_recovery_state(wil, fw_recovery_idle); mutex_lock(&wil->mutex); - rc = wmi_pcp_stop(wil); + wmi_pcp_stop(wil); __wil_down(wil); - rc1 = __wil_up(wil); + __wil_up(wil); mutex_unlock(&wil->mutex); - return min(rc, rc1); + /* some functions above might fail (e.g. __wil_up). Nevertheless, we + * return success because AP has stopped + */ + return 0; } static int wil_cfg80211_del_station(struct wiphy *wiphy, @@ -804,6 +821,96 @@ static int wil_cfg80211_del_station(struct wiphy *wiphy, return 0; } +/* probe_client handling */ +static void wil_probe_client_handle(struct wil6210_priv *wil, + struct wil_probe_client_req *req) +{ + struct net_device *ndev = wil_to_ndev(wil); + struct wil_sta_info *sta = &wil->sta[req->cid]; + /* assume STA is alive if it is still connected, + * else FW will disconnect it + */ + bool alive = (sta->status == wil_sta_connected); + + cfg80211_probe_status(ndev, sta->addr, req->cookie, alive, GFP_KERNEL); +} + +static struct list_head *next_probe_client(struct wil6210_priv *wil) +{ + struct list_head *ret = NULL; + + mutex_lock(&wil->probe_client_mutex); + + if (!list_empty(&wil->probe_client_pending)) { + ret = wil->probe_client_pending.next; + list_del(ret); + } + + mutex_unlock(&wil->probe_client_mutex); + + return ret; +} + +void wil_probe_client_worker(struct work_struct *work) +{ + struct wil6210_priv *wil = container_of(work, struct wil6210_priv, + probe_client_worker); + struct wil_probe_client_req *req; + struct list_head *lh; + + while ((lh = next_probe_client(wil)) != NULL) { + req = list_entry(lh, struct wil_probe_client_req, list); + + wil_probe_client_handle(wil, req); + kfree(req); + } +} + +void wil_probe_client_flush(struct wil6210_priv *wil) +{ + struct wil_probe_client_req *req, *t; + + wil_dbg_misc(wil, "%s()\n", __func__); + + mutex_lock(&wil->probe_client_mutex); + + list_for_each_entry_safe(req, t, &wil->probe_client_pending, list) { + list_del(&req->list); + kfree(req); + } + + mutex_unlock(&wil->probe_client_mutex); +} + +static int wil_cfg80211_probe_client(struct wiphy *wiphy, + struct net_device *dev, + const u8 *peer, u64 *cookie) +{ + struct wil6210_priv *wil = wiphy_to_wil(wiphy); + struct wil_probe_client_req *req; + int cid = wil_find_cid(wil, peer); + + wil_dbg_misc(wil, "%s(%pM => CID %d)\n", __func__, peer, cid); + + if (cid < 0) + return -ENOLINK; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return -ENOMEM; + + req->cid = cid; + req->cookie = cid; + + mutex_lock(&wil->probe_client_mutex); + list_add_tail(&req->list, &wil->probe_client_pending); + mutex_unlock(&wil->probe_client_mutex); + + *cookie = req->cookie; + queue_work(wil->wq_service, &wil->probe_client_worker); + return 0; +} + static struct cfg80211_ops wil_cfg80211_ops = { .scan = wil_cfg80211_scan, .connect = wil_cfg80211_connect, @@ -823,6 +930,7 @@ static struct cfg80211_ops wil_cfg80211_ops = { .start_ap = wil_cfg80211_start_ap, .stop_ap = wil_cfg80211_stop_ap, .del_station = wil_cfg80211_del_station, + .probe_client = wil_cfg80211_probe_client, }; static void wil_wiphy_init(struct wiphy *wiphy) @@ -854,6 +962,7 @@ static void wil_wiphy_init(struct wiphy *wiphy) wiphy->cipher_suites = wil_cipher_suites; wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites); wiphy->mgmt_stypes = wil_mgmt_stypes; + wiphy->features |= NL80211_FEATURE_SK_TX_STATUS; } struct wireless_dev *wil_cfg80211_init(struct device *dev) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 4e6e14501c2f..45c3558ec804 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -50,6 +50,7 @@ static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil, char _s, char _h) { void __iomem *x = wmi_addr(wil, vring->hwtail); + u32 v; seq_printf(s, "VRING %s = {\n", name); seq_printf(s, " pa = %pad\n", &vring->pa); @@ -58,10 +59,12 @@ static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil, seq_printf(s, " swtail = %d\n", vring->swtail); seq_printf(s, " swhead = %d\n", vring->swhead); seq_printf(s, " hwtail = [0x%08x] -> ", vring->hwtail); - if (x) - seq_printf(s, "0x%08x\n", ioread32(x)); - else + if (x) { + v = ioread32(x); + seq_printf(s, "0x%08x = %d\n", v, v); + } else { seq_puts(s, "???\n"); + } if (vring->va && (vring->size < 1025)) { uint i; @@ -101,8 +104,8 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data) char name[10]; /* performance monitoring */ cycles_t now = get_cycles(); - cycles_t idle = txdata->idle * 100; - cycles_t total = now - txdata->begin; + uint64_t idle = txdata->idle * 100; + uint64_t total = now - txdata->begin; do_div(idle, total); txdata->begin = now; @@ -110,9 +113,12 @@ static int wil_vring_debugfs_show(struct seq_file *s, void *data) snprintf(name, sizeof(name), "tx_%2d", i); - seq_printf(s, "\n%pM CID %d TID %d [%3d|%3d] idle %3d%%\n", - wil->sta[cid].addr, cid, tid, used, avail, - (int)idle); + seq_printf(s, + "\n%pM CID %d TID %d BACK([%d] %d TU A%s) [%3d|%3d] idle %3d%%\n", + wil->sta[cid].addr, cid, tid, + txdata->agg_wsize, txdata->agg_timeout, + txdata->agg_amsdu ? "+" : "-", + used, avail, (int)idle); wil_print_vring(s, wil, name, vring, '_', 'H'); } @@ -384,24 +390,67 @@ static int wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil, return 0; } -static const struct dbg_off itr_cnt_off[] = { +static const struct dbg_off lgc_itr_cnt_off[] = { {"TRSH", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_CNT_TRSH), doff_io32}, {"DATA", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_CNT_DATA), doff_io32}, {"CTL", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_CNT_CRL), doff_io32}, {}, }; +static const struct dbg_off tx_itr_cnt_off[] = { + {"TRSH", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_TX_CNT_TRSH), + doff_io32}, + {"DATA", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_TX_CNT_DATA), + doff_io32}, + {"CTL", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_TX_CNT_CTL), + doff_io32}, + {"IDL_TRSH", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_TRSH), + doff_io32}, + {"IDL_DATA", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_DATA), + doff_io32}, + {"IDL_CTL", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_TX_IDL_CNT_CTL), + doff_io32}, + {}, +}; + +static const struct dbg_off rx_itr_cnt_off[] = { + {"TRSH", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_RX_CNT_TRSH), + doff_io32}, + {"DATA", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_RX_CNT_DATA), + doff_io32}, + {"CTL", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_RX_CNT_CTL), + doff_io32}, + {"IDL_TRSH", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_TRSH), + doff_io32}, + {"IDL_DATA", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_DATA), + doff_io32}, + {"IDL_CTL", S_IRUGO | S_IWUSR, HOSTADDR(RGF_DMA_ITR_RX_IDL_CNT_CTL), + doff_io32}, + {}, +}; + static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil, struct dentry *parent) { - struct dentry *d = debugfs_create_dir("ITR_CNT", parent); + struct dentry *d, *dtx, *drx; + d = debugfs_create_dir("ITR_CNT", parent); if (IS_ERR_OR_NULL(d)) return -ENODEV; + dtx = debugfs_create_dir("TX", d); + drx = debugfs_create_dir("RX", d); + if (IS_ERR_OR_NULL(dtx) || IS_ERR_OR_NULL(drx)) + return -ENODEV; + wil6210_debugfs_init_offset(wil, d, (void * __force)wil->csr, - itr_cnt_off); + lgc_itr_cnt_off); + + wil6210_debugfs_init_offset(wil, dtx, (void * __force)wil->csr, + tx_itr_cnt_off); + wil6210_debugfs_init_offset(wil, drx, (void * __force)wil->csr, + rx_itr_cnt_off); return 0; } @@ -558,6 +607,87 @@ static const struct file_operations fops_rxon = { .open = simple_open, }; +/* block ack control, write: + * - "add <ringid> <agg_size> <timeout>" to trigger ADDBA + * - "del_tx <ringid> <reason>" to trigger DELBA for Tx side + * - "del_rx <CID> <TID> <reason>" to trigger DELBA for Rx side + */ +static ssize_t wil_write_back(struct file *file, const char __user *buf, + size_t len, loff_t *ppos) +{ + struct wil6210_priv *wil = file->private_data; + int rc; + char *kbuf = kmalloc(len + 1, GFP_KERNEL); + char cmd[8]; + int p1, p2, p3; + + if (!kbuf) + return -ENOMEM; + + rc = simple_write_to_buffer(kbuf, len, ppos, buf, len); + if (rc != len) { + kfree(kbuf); + return rc >= 0 ? -EIO : rc; + } + + kbuf[len] = '\0'; + rc = sscanf(kbuf, "%8s %d %d %d", cmd, &p1, &p2, &p3); + kfree(kbuf); + + if (rc < 0) + return rc; + if (rc < 2) + return -EINVAL; + + if (0 == strcmp(cmd, "add")) { + if (rc < 3) { + wil_err(wil, "BACK: add require at least 2 params\n"); + return -EINVAL; + } + if (rc < 4) + p3 = 0; + wmi_addba(wil, p1, p2, p3); + } else if (0 == strcmp(cmd, "del_tx")) { + if (rc < 3) + p2 = WLAN_REASON_QSTA_LEAVE_QBSS; + wmi_delba_tx(wil, p1, p2); + } else if (0 == strcmp(cmd, "del_rx")) { + if (rc < 3) { + wil_err(wil, + "BACK: del_rx require at least 2 params\n"); + return -EINVAL; + } + if (rc < 4) + p3 = WLAN_REASON_QSTA_LEAVE_QBSS; + wmi_delba_rx(wil, mk_cidxtid(p1, p2), p3); + } else { + wil_err(wil, "BACK: Unrecognized command \"%s\"\n", cmd); + return -EINVAL; + } + + return len; +} + +static ssize_t wil_read_back(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + static const char text[] = "block ack control, write:\n" + " - \"add <ringid> <agg_size> <timeout>\" to trigger ADDBA\n" + "If missing, <timeout> defaults to 0\n" + " - \"del_tx <ringid> <reason>\" to trigger DELBA for Tx side\n" + " - \"del_rx <CID> <TID> <reason>\" to trigger DELBA for Rx side\n" + "If missing, <reason> set to \"STA_LEAVING\" (36)\n"; + + return simple_read_from_buffer(user_buf, count, ppos, text, + sizeof(text)); +} + +static const struct file_operations fops_back = { + .read = wil_read_back, + .write = wil_write_back, + .open = simple_open, +}; + /*---tx_mgmt---*/ /* Write mgmt frame to this file to send it */ static ssize_t wil_write_file_txmgmt(struct file *file, const char __user *buf, @@ -1116,7 +1246,8 @@ static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r) int i; u16 index = ((r->head_seq_num - r->ssn) & 0xfff) % r->buf_size; - seq_printf(s, "0x%03x [", r->head_seq_num); + seq_printf(s, "([%2d] %3d TU) 0x%03x [", r->buf_size, r->timeout, + r->head_seq_num); for (i = 0; i < r->buf_size; i++) { if (i == index) seq_printf(s, "%c", r->reorder_buf[i] ? 'O' : '|'); @@ -1127,10 +1258,10 @@ static void wil_print_rxtid(struct seq_file *s, struct wil_tid_ampdu_rx *r) } static int wil_sta_debugfs_show(struct seq_file *s, void *data) +__acquires(&p->tid_rx_lock) __releases(&p->tid_rx_lock) { struct wil6210_priv *wil = s->private; int i, tid; - unsigned long flags; for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { struct wil_sta_info *p = &wil->sta[i]; @@ -1151,7 +1282,7 @@ static int wil_sta_debugfs_show(struct seq_file *s, void *data) (p->data_port_open ? " data_port_open" : "")); if (p->status == wil_sta_connected) { - spin_lock_irqsave(&p->tid_rx_lock, flags); + spin_lock_bh(&p->tid_rx_lock); for (tid = 0; tid < WIL_STA_TID_NUM; tid++) { struct wil_tid_ampdu_rx *r = p->tid_rx[tid]; @@ -1160,7 +1291,7 @@ static int wil_sta_debugfs_show(struct seq_file *s, void *data) wil_print_rxtid(s, r); } } - spin_unlock_irqrestore(&p->tid_rx_lock, flags); + spin_unlock_bh(&p->tid_rx_lock); } } @@ -1217,6 +1348,7 @@ static const struct { {"rxon", S_IWUSR, &fops_rxon}, {"tx_mgmt", S_IWUSR, &fops_txmgmt}, {"wmi_send", S_IWUSR, &fops_wmi}, + {"back", S_IRUGO | S_IWUSR, &fops_back}, {"temp", S_IRUGO, &fops_temp}, {"freq", S_IRUGO, &fops_freq}, {"link", S_IRUGO, &fops_link}, @@ -1261,7 +1393,7 @@ static void wil6210_debugfs_init_isr(struct wil6210_priv *wil, /* fields in struct wil6210_priv */ static const struct dbg_off dbg_wil_off[] = { WIL_FIELD(secure_pcp, S_IRUGO | S_IWUSR, doff_u32), - WIL_FIELD(status, S_IRUGO | S_IWUSR, doff_ulong), + WIL_FIELD(status[0], S_IRUGO | S_IWUSR, doff_ulong), WIL_FIELD(fw_version, S_IRUGO, doff_u32), WIL_FIELD(hw_version, S_IRUGO, doff_x32), WIL_FIELD(recovery_count, S_IRUGO, doff_u32), diff --git a/drivers/net/wireless/ath/wil6210/ethtool.c b/drivers/net/wireless/ath/wil6210/ethtool.c index d686638972be..4c44a82c34d7 100644 --- a/drivers/net/wireless/ath/wil6210/ethtool.c +++ b/drivers/net/wireless/ath/wil6210/ethtool.c @@ -45,16 +45,35 @@ static int wil_ethtoolops_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *cp) { struct wil6210_priv *wil = ndev_to_wil(ndev); - u32 itr_en, itr_val = 0; + u32 tx_itr_en, tx_itr_val = 0; + u32 rx_itr_en, rx_itr_val = 0; wil_dbg_misc(wil, "%s()\n", __func__); - itr_en = ioread32(wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_CRL)); - if (itr_en & BIT_DMA_ITR_CNT_CRL_EN) - itr_val = ioread32(wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_TRSH)); - - cp->rx_coalesce_usecs = itr_val; + if (test_bit(hw_capability_advanced_itr_moderation, + wil->hw_capabilities)) { + tx_itr_en = ioread32(wil->csr + + HOSTADDR(RGF_DMA_ITR_TX_CNT_CTL)); + if (tx_itr_en & BIT_DMA_ITR_TX_CNT_CTL_EN) + tx_itr_val = + ioread32(wil->csr + + HOSTADDR(RGF_DMA_ITR_TX_CNT_TRSH)); + + rx_itr_en = ioread32(wil->csr + + HOSTADDR(RGF_DMA_ITR_RX_CNT_CTL)); + if (rx_itr_en & BIT_DMA_ITR_RX_CNT_CTL_EN) + rx_itr_val = + ioread32(wil->csr + + HOSTADDR(RGF_DMA_ITR_RX_CNT_TRSH)); + } else { + rx_itr_en = ioread32(wil->csr + HOSTADDR(RGF_DMA_ITR_CNT_CRL)); + if (rx_itr_en & BIT_DMA_ITR_CNT_CRL_EN) + rx_itr_val = ioread32(wil->csr + + HOSTADDR(RGF_DMA_ITR_CNT_TRSH)); + } + cp->tx_coalesce_usecs = tx_itr_val; + cp->rx_coalesce_usecs = rx_itr_val; return 0; } @@ -63,22 +82,25 @@ static int wil_ethtoolops_set_coalesce(struct net_device *ndev, { struct wil6210_priv *wil = ndev_to_wil(ndev); - wil_dbg_misc(wil, "%s(%d usec)\n", __func__, cp->rx_coalesce_usecs); + wil_dbg_misc(wil, "%s(rx %d usec, tx %d usec)\n", __func__, + cp->rx_coalesce_usecs, cp->tx_coalesce_usecs); if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { wil_dbg_misc(wil, "No IRQ coalescing in monitor mode\n"); return -EINVAL; } - /* only @rx_coalesce_usecs supported, ignore - * other parameters + /* only @rx_coalesce_usecs and @tx_coalesce_usecs supported, + * ignore other parameters */ - if (cp->rx_coalesce_usecs > WIL6210_ITR_TRSH_MAX) + if (cp->rx_coalesce_usecs > WIL6210_ITR_TRSH_MAX || + cp->tx_coalesce_usecs > WIL6210_ITR_TRSH_MAX) goto out_bad; - wil->itr_trsh = cp->rx_coalesce_usecs; - wil_set_itr_trsh(wil); + wil->tx_max_burst_duration = cp->tx_coalesce_usecs; + wil->rx_max_burst_duration = cp->rx_coalesce_usecs; + wil_configure_interrupt_moderation(wil); return 0; diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c index 4bcbd6297b3e..a6f923086f31 100644 --- a/drivers/net/wireless/ath/wil6210/interrupt.c +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -102,7 +102,7 @@ static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil) iowrite32(WIL6210_IRQ_DISABLE, wil->csr + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); - clear_bit(wil_status_irqen, &wil->status); + clear_bit(wil_status_irqen, wil->status); } void wil6210_unmask_irq_tx(struct wil6210_priv *wil) @@ -130,7 +130,7 @@ static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil) { wil_dbg_irq(wil, "%s()\n", __func__); - set_bit(wil_status_irqen, &wil->status); + set_bit(wil_status_irqen, wil->status); iowrite32(WIL6210_IRQ_PSEUDO_MASK, wil->csr + HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); @@ -157,15 +157,91 @@ void wil_unmask_irq(struct wil6210_priv *wil) iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) + offsetof(struct RGF_ICR, ICC)); - /* interrupt moderation parameters */ - wil_set_itr_trsh(wil); - wil6210_unmask_irq_pseudo(wil); wil6210_unmask_irq_tx(wil); wil6210_unmask_irq_rx(wil); wil6210_unmask_irq_misc(wil); } +/* target write operation */ +#define W(a, v) do { iowrite32(v, wil->csr + HOSTADDR(a)); wmb(); } while (0) + +static +void wil_configure_interrupt_moderation_new(struct wil6210_priv *wil) +{ + /* Disable and clear tx counter before (re)configuration */ + W(RGF_DMA_ITR_TX_CNT_CTL, BIT_DMA_ITR_TX_CNT_CTL_CLR); + W(RGF_DMA_ITR_TX_CNT_TRSH, wil->tx_max_burst_duration); + wil_info(wil, "set ITR_TX_CNT_TRSH = %d usec\n", + wil->tx_max_burst_duration); + /* Configure TX max burst duration timer to use usec units */ + W(RGF_DMA_ITR_TX_CNT_CTL, + BIT_DMA_ITR_TX_CNT_CTL_EN | BIT_DMA_ITR_TX_CNT_CTL_EXT_TIC_SEL); + + /* Disable and clear tx idle counter before (re)configuration */ + W(RGF_DMA_ITR_TX_IDL_CNT_CTL, BIT_DMA_ITR_TX_IDL_CNT_CTL_CLR); + W(RGF_DMA_ITR_TX_IDL_CNT_TRSH, wil->tx_interframe_timeout); + wil_info(wil, "set ITR_TX_IDL_CNT_TRSH = %d usec\n", + wil->tx_interframe_timeout); + /* Configure TX max burst duration timer to use usec units */ + W(RGF_DMA_ITR_TX_IDL_CNT_CTL, BIT_DMA_ITR_TX_IDL_CNT_CTL_EN | + BIT_DMA_ITR_TX_IDL_CNT_CTL_EXT_TIC_SEL); + + /* Disable and clear rx counter before (re)configuration */ + W(RGF_DMA_ITR_RX_CNT_CTL, BIT_DMA_ITR_RX_CNT_CTL_CLR); + W(RGF_DMA_ITR_RX_CNT_TRSH, wil->rx_max_burst_duration); + wil_info(wil, "set ITR_RX_CNT_TRSH = %d usec\n", + wil->rx_max_burst_duration); + /* Configure TX max burst duration timer to use usec units */ + W(RGF_DMA_ITR_RX_CNT_CTL, + BIT_DMA_ITR_RX_CNT_CTL_EN | BIT_DMA_ITR_RX_CNT_CTL_EXT_TIC_SEL); + + /* Disable and clear rx idle counter before (re)configuration */ + W(RGF_DMA_ITR_RX_IDL_CNT_CTL, BIT_DMA_ITR_RX_IDL_CNT_CTL_CLR); + W(RGF_DMA_ITR_RX_IDL_CNT_TRSH, wil->rx_interframe_timeout); + wil_info(wil, "set ITR_RX_IDL_CNT_TRSH = %d usec\n", + wil->rx_interframe_timeout); + /* Configure TX max burst duration timer to use usec units */ + W(RGF_DMA_ITR_RX_IDL_CNT_CTL, BIT_DMA_ITR_RX_IDL_CNT_CTL_EN | + BIT_DMA_ITR_RX_IDL_CNT_CTL_EXT_TIC_SEL); +} + +static +void wil_configure_interrupt_moderation_lgc(struct wil6210_priv *wil) +{ + /* disable, use usec resolution */ + W(RGF_DMA_ITR_CNT_CRL, BIT_DMA_ITR_CNT_CRL_CLR); + + wil_info(wil, "set ITR_TRSH = %d usec\n", wil->rx_max_burst_duration); + W(RGF_DMA_ITR_CNT_TRSH, wil->rx_max_burst_duration); + /* start it */ + W(RGF_DMA_ITR_CNT_CRL, + BIT_DMA_ITR_CNT_CRL_EN | BIT_DMA_ITR_CNT_CRL_EXT_TICK); +} + +#undef W + +void wil_configure_interrupt_moderation(struct wil6210_priv *wil) +{ + wil_dbg_irq(wil, "%s()\n", __func__); + + /* disable interrupt moderation for monitor + * to get better timestamp precision + */ + if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) + return; + + if (test_bit(hw_capability_advanced_itr_moderation, + wil->hw_capabilities)) + wil_configure_interrupt_moderation_new(wil); + else { + /* Advanced interrupt moderation is not available before + * Sparrow v2. Will use legacy interrupt moderation + */ + wil_configure_interrupt_moderation_lgc(wil); + } +} + static irqreturn_t wil6210_irq_rx(int irq, void *cookie) { struct wil6210_priv *wil = cookie; @@ -194,18 +270,19 @@ static irqreturn_t wil6210_irq_rx(int irq, void *cookie) wil_dbg_irq(wil, "RX done\n"); if (isr & BIT_DMA_EP_RX_ICR_RX_HTRSH) - wil_err_ratelimited(wil, "Received \"Rx buffer is in risk " - "of overflow\" interrupt\n"); + wil_err_ratelimited(wil, + "Received \"Rx buffer is in risk of overflow\" interrupt\n"); - isr &= ~(BIT_DMA_EP_RX_ICR_RX_DONE | BIT_DMA_EP_RX_ICR_RX_HTRSH); - if (test_bit(wil_status_reset_done, &wil->status)) { - if (test_bit(wil_status_napi_en, &wil->status)) { + isr &= ~(BIT_DMA_EP_RX_ICR_RX_DONE | + BIT_DMA_EP_RX_ICR_RX_HTRSH); + if (test_bit(wil_status_reset_done, wil->status)) { + if (test_bit(wil_status_napi_en, wil->status)) { wil_dbg_txrx(wil, "NAPI(Rx) schedule\n"); need_unmask = false; napi_schedule(&wil->napi_rx); } else { - wil_err(wil, "Got Rx interrupt while " - "stopping interface\n"); + wil_err(wil, + "Got Rx interrupt while stopping interface\n"); } } else { wil_err(wil, "Got Rx interrupt while in reset\n"); @@ -248,7 +325,7 @@ static irqreturn_t wil6210_irq_tx(int irq, void *cookie) isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE; /* clear also all VRING interrupts */ isr &= ~(BIT(25) - 1UL); - if (test_bit(wil_status_reset_done, &wil->status)) { + if (test_bit(wil_status_reset_done, wil->status)) { wil_dbg_txrx(wil, "NAPI(Tx) schedule\n"); need_unmask = false; napi_schedule(&wil->napi_tx); @@ -310,7 +387,7 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie) if (isr & ISR_MISC_FW_ERROR) { wil_err(wil, "Firmware error detected\n"); - clear_bit(wil_status_fwready, &wil->status); + clear_bit(wil_status_fwready, wil->status); /* * do not clear @isr here - we do 2-nd part in thread * there, user space get notified, and it should be done @@ -321,7 +398,7 @@ static irqreturn_t wil6210_irq_misc(int irq, void *cookie) if (isr & ISR_MISC_FW_READY) { wil_dbg_irq(wil, "IRQ: FW ready\n"); wil_cache_mbox_regs(wil); - set_bit(wil_status_reset_done, &wil->status); + set_bit(wil_status_reset_done, wil->status); /** * Actual FW ready indicated by the * WMI_FW_READY_EVENTID @@ -394,7 +471,7 @@ static irqreturn_t wil6210_thread_irq(int irq, void *cookie) */ static int wil6210_debug_irq_mask(struct wil6210_priv *wil, u32 pseudo_cause) { - if (!test_bit(wil_status_irqen, &wil->status)) { + if (!test_bit(wil_status_irqen, wil->status)) { u32 icm_rx = wil_ioread32_and_clear(wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) + offsetof(struct RGF_ICR, ICM)); diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 8ff3fe34fe05..b04e0afdcb21 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -33,15 +33,18 @@ static bool no_fw_load = true; module_param(no_fw_load, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(no_fw_load, " do not download FW, use one in on-card flash."); -static unsigned int itr_trsh = WIL6210_ITR_TRSH_DEFAULT; - -module_param(itr_trsh, uint, S_IRUGO); -MODULE_PARM_DESC(itr_trsh, " Interrupt moderation threshold, usecs."); +/* if not set via modparam, will be set to default value of 1/8 of + * rx ring size during init flow + */ +unsigned short rx_ring_overflow_thrsh = WIL6210_RX_HIGH_TRSH_INIT; +module_param(rx_ring_overflow_thrsh, ushort, S_IRUGO); +MODULE_PARM_DESC(rx_ring_overflow_thrsh, + " RX ring overflow threshold in descriptors."); /* We allow allocation of more than 1 page buffers to support large packets. * It is suboptimal behavior performance wise in case MTU above page size. */ -unsigned int mtu_max = TXRX_BUF_LEN_DEFAULT - ETH_HLEN; +unsigned int mtu_max = TXRX_BUF_LEN_DEFAULT - WIL_MAX_MPDU_OVERHEAD; static int mtu_max_set(const char *val, const struct kernel_param *kp) { int ret; @@ -53,7 +56,7 @@ static int mtu_max_set(const char *val, const struct kernel_param *kp) if (ret) return ret; - if (mtu_max < 68 || mtu_max > IEEE80211_MAX_DATA_LEN_DMG) + if (mtu_max < 68 || mtu_max > WIL_MAX_ETH_MTU) ret = -EINVAL; return ret; @@ -135,12 +138,14 @@ void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, static void wil_disconnect_cid(struct wil6210_priv *wil, int cid, u16 reason_code, bool from_event) +__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) { uint i; struct net_device *ndev = wil_to_ndev(wil); struct wireless_dev *wdev = wil->wdev; struct wil_sta_info *sta = &wil->sta[cid]; + might_sleep(); wil_dbg_misc(wil, "%s(CID %d, status %d)\n", __func__, cid, sta->status); @@ -163,15 +168,14 @@ static void wil_disconnect_cid(struct wil6210_priv *wil, int cid, for (i = 0; i < WIL_STA_TID_NUM; i++) { struct wil_tid_ampdu_rx *r; - unsigned long flags; - spin_lock_irqsave(&sta->tid_rx_lock, flags); + spin_lock_bh(&sta->tid_rx_lock); r = sta->tid_rx[i]; sta->tid_rx[i] = NULL; wil_tid_ampdu_rx_free(wil, r); - spin_unlock_irqrestore(&sta->tid_rx_lock, flags); + spin_unlock_bh(&sta->tid_rx_lock); } for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { if (wil->vring2cid_tid[i][0] == cid) @@ -188,34 +192,47 @@ static void _wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, struct wireless_dev *wdev = wil->wdev; might_sleep(); - if (bssid) { + wil_dbg_misc(wil, "%s(bssid=%pM, reason=%d, ev%s)\n", __func__, bssid, + reason_code, from_event ? "+" : "-"); + + /* Cases are: + * - disconnect single STA, still connected + * - disconnect single STA, already disconnected + * - disconnect all + * + * For "disconnect all", there are 2 options: + * - bssid == NULL + * - bssid is our MAC address + */ + if (bssid && memcmp(ndev->dev_addr, bssid, ETH_ALEN)) { cid = wil_find_cid(wil, bssid); - wil_dbg_misc(wil, "%s(%pM, CID %d)\n", __func__, bssid, cid); - } else { - wil_dbg_misc(wil, "%s(all)\n", __func__); - } - - if (cid >= 0) /* disconnect 1 peer */ - wil_disconnect_cid(wil, cid, reason_code, from_event); - else /* disconnect all */ + wil_dbg_misc(wil, "Disconnect %pM, CID=%d, reason=%d\n", + bssid, cid, reason_code); + if (cid >= 0) /* disconnect 1 peer */ + wil_disconnect_cid(wil, cid, reason_code, from_event); + } else { /* all */ + wil_dbg_misc(wil, "Disconnect all\n"); for (cid = 0; cid < WIL6210_MAX_CID; cid++) wil_disconnect_cid(wil, cid, reason_code, from_event); + } /* link state */ switch (wdev->iftype) { case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_P2P_CLIENT: - wil_link_off(wil); - if (test_bit(wil_status_fwconnected, &wil->status)) { - clear_bit(wil_status_fwconnected, &wil->status); + netif_tx_stop_all_queues(ndev); + netif_carrier_off(ndev); + + if (test_bit(wil_status_fwconnected, wil->status)) { + clear_bit(wil_status_fwconnected, wil->status); cfg80211_disconnected(ndev, reason_code, NULL, 0, GFP_KERNEL); - } else if (test_bit(wil_status_fwconnecting, &wil->status)) { + } else if (test_bit(wil_status_fwconnecting, wil->status)) { cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, WLAN_STATUS_UNSPECIFIED_FAILURE, GFP_KERNEL); } - clear_bit(wil_status_fwconnecting, &wil->status); + clear_bit(wil_status_fwconnecting, wil->status); break; default: break; @@ -248,7 +265,7 @@ static void wil_scan_timer_fn(ulong x) { struct wil6210_priv *wil = (void *)x; - clear_bit(wil_status_fwready, &wil->status); + clear_bit(wil_status_fwready, wil->status); wil_err(wil, "Scan timeout detected, start fw error recovery\n"); wil->recovery_state = fw_recovery_pending; schedule_work(&wil->fw_error_worker); @@ -352,6 +369,8 @@ static void wil_connect_worker(struct work_struct *work) int rc; struct wil6210_priv *wil = container_of(work, struct wil6210_priv, connect_worker); + struct net_device *ndev = wil_to_ndev(wil); + int cid = wil->pending_connect_cid; int ringid = wil_find_free_vring(wil); @@ -366,7 +385,7 @@ static void wil_connect_worker(struct work_struct *work) wil->pending_connect_cid = -1; if (rc == 0) { wil->sta[cid].status = wil_sta_connected; - wil_link_on(wil); + netif_tx_wake_all_queues(ndev); } else { wil->sta[cid].status = wil_sta_unused; } @@ -384,6 +403,9 @@ int wil_priv_init(struct wil6210_priv *wil) mutex_init(&wil->mutex); mutex_init(&wil->wmi_mutex); + mutex_init(&wil->back_rx_mutex); + mutex_init(&wil->back_tx_mutex); + mutex_init(&wil->probe_client_mutex); init_completion(&wil->wmi_ready); init_completion(&wil->wmi_call); @@ -396,25 +418,39 @@ int wil_priv_init(struct wil6210_priv *wil) INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); INIT_WORK(&wil->fw_error_worker, wil_fw_error_worker); + INIT_WORK(&wil->back_rx_worker, wil_back_rx_worker); + INIT_WORK(&wil->back_tx_worker, wil_back_tx_worker); + INIT_WORK(&wil->probe_client_worker, wil_probe_client_worker); INIT_LIST_HEAD(&wil->pending_wmi_ev); + INIT_LIST_HEAD(&wil->back_rx_pending); + INIT_LIST_HEAD(&wil->back_tx_pending); + INIT_LIST_HEAD(&wil->probe_client_pending); spin_lock_init(&wil->wmi_ev_lock); init_waitqueue_head(&wil->wq); - wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi"); + wil->wmi_wq = create_singlethread_workqueue(WIL_NAME "_wmi"); if (!wil->wmi_wq) return -EAGAIN; - wil->wmi_wq_conn = create_singlethread_workqueue(WIL_NAME"_connect"); - if (!wil->wmi_wq_conn) { - destroy_workqueue(wil->wmi_wq); - return -EAGAIN; - } + wil->wq_service = create_singlethread_workqueue(WIL_NAME "_service"); + if (!wil->wq_service) + goto out_wmi_wq; wil->last_fw_recovery = jiffies; - wil->itr_trsh = itr_trsh; + wil->tx_interframe_timeout = WIL6210_ITR_TX_INTERFRAME_TIMEOUT_DEFAULT; + wil->rx_interframe_timeout = WIL6210_ITR_RX_INTERFRAME_TIMEOUT_DEFAULT; + wil->tx_max_burst_duration = WIL6210_ITR_TX_MAX_BURST_DURATION_DEFAULT; + wil->rx_max_burst_duration = WIL6210_ITR_RX_MAX_BURST_DURATION_DEFAULT; + if (rx_ring_overflow_thrsh == WIL6210_RX_HIGH_TRSH_INIT) + rx_ring_overflow_thrsh = WIL6210_RX_HIGH_TRSH_DEFAULT; return 0; + +out_wmi_wq: + destroy_workqueue(wil->wmi_wq); + + return -EAGAIN; } /** @@ -448,7 +484,13 @@ void wil_priv_deinit(struct wil6210_priv *wil) wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false); mutex_unlock(&wil->mutex); wmi_event_flush(wil); - destroy_workqueue(wil->wmi_wq_conn); + wil_back_rx_flush(wil); + cancel_work_sync(&wil->back_rx_worker); + wil_back_tx_flush(wil); + cancel_work_sync(&wil->back_tx_worker); + wil_probe_client_flush(wil); + cancel_work_sync(&wil->probe_client_worker); + destroy_workqueue(wil->wq_service); destroy_workqueue(wil->wmi_wq); } @@ -478,13 +520,10 @@ static int wil_target_reset(struct wil6210_priv *wil) { int delay = 0; u32 x; - u32 rev_id; - bool is_sparrow = (wil->board->board == WIL_BOARD_SPARROW); + bool is_reset_v2 = test_bit(hw_capability_reset_v2, + wil->hw_capabilities); - wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->board->name); - - wil->hw_version = R(RGF_USER_FW_REV_ID); - rev_id = wil->hw_version & 0xff; + wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->hw_name); /* Clear MAC link up */ S(RGF_HP_CTRL, BIT(15)); @@ -496,7 +535,7 @@ static int wil_target_reset(struct wil6210_priv *wil) /* Clear Fw Download notification */ C(RGF_USER_USAGE_6, BIT(0)); - if (is_sparrow) { + if (is_reset_v2) { S(RGF_CAF_OSC_CONTROL, BIT_CAF_OSC_XTAL_EN); /* XTAL stabilization should take about 3ms */ usleep_range(5000, 7000); @@ -517,10 +556,11 @@ static int wil_target_reset(struct wil6210_priv *wil) W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F); - W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, is_sparrow ? 0x000000f0 : 0x00000170); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, + is_reset_v2 ? 0x000000f0 : 0x00000170); W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FE00); - if (is_sparrow) { + if (is_reset_v2) { W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x0); W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_1, 0x0); } @@ -530,19 +570,14 @@ static int wil_target_reset(struct wil6210_priv *wil) W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0); W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); - if (is_sparrow) { + if (is_reset_v2) { W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000003); /* reset A2 PCIE AHB */ W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000); } else { W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001); - if (rev_id == 1) { - /* reset A1 BOTH PCIE AHB & PCIE RGF */ - W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); - } else { - W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8)); - W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000); - } + W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8)); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000); } /* TODO: check order here!!! Erez code is different */ @@ -559,8 +594,7 @@ static int wil_target_reset(struct wil6210_priv *wil) } } while (x != HW_MACHINE_BOOT_DONE); - /* TODO: Erez check rev_id != 1 */ - if (!is_sparrow && (rev_id != 1)) + if (!is_reset_v2) W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8)); C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); @@ -569,26 +603,6 @@ static int wil_target_reset(struct wil6210_priv *wil) return 0; } -/** - * wil_set_itr_trsh: - apply interrupt coalescing params - */ -void wil_set_itr_trsh(struct wil6210_priv *wil) -{ - /* disable, use usec resolution */ - W(RGF_DMA_ITR_CNT_CRL, BIT_DMA_ITR_CNT_CRL_EXT_TICK); - - /* disable interrupt moderation for monitor - * to get better timestamp precision - */ - if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) - return; - - wil_info(wil, "set ITR_TRSH = %d usec\n", wil->itr_trsh); - W(RGF_DMA_ITR_CNT_TRSH, wil->itr_trsh); - W(RGF_DMA_ITR_CNT_CRL, BIT_DMA_ITR_CNT_CRL_EN | - BIT_DMA_ITR_CNT_CRL_EXT_TICK); /* start it */ -} - #undef R #undef W #undef S @@ -629,13 +643,17 @@ int wil_reset(struct wil6210_priv *wil) wil_dbg_misc(wil, "%s()\n", __func__); + if (wil->hw_version == HW_VER_UNKNOWN) + return -ENODEV; + WARN_ON(!mutex_is_locked(&wil->mutex)); - WARN_ON(test_bit(wil_status_napi_en, &wil->status)); + WARN_ON(test_bit(wil_status_napi_en, wil->status)); cancel_work_sync(&wil->disconnect_worker); wil6210_disconnect(wil, NULL, WLAN_REASON_DEAUTH_LEAVING, false); - wil->status = 0; /* prevent NAPI from being scheduled */ + /* prevent NAPI from being scheduled */ + bitmap_zero(wil->status, wil_status_last); if (wil->scan_request) { wil_dbg_misc(wil, "Abort scan_request 0x%p\n", @@ -649,7 +667,7 @@ int wil_reset(struct wil6210_priv *wil) wmi_event_flush(wil); - flush_workqueue(wil->wmi_wq_conn); + flush_workqueue(wil->wq_service); flush_workqueue(wil->wmi_wq); rc = wil_target_reset(wil); @@ -688,6 +706,7 @@ int wil_reset(struct wil6210_priv *wil) reinit_completion(&wil->wmi_ready); reinit_completion(&wil->wmi_call); + wil_configure_interrupt_moderation(wil); wil_unmask_irq(wil); /* we just started MAC, wait for FW ready */ @@ -703,28 +722,6 @@ void wil_fw_error_recovery(struct wil6210_priv *wil) schedule_work(&wil->fw_error_worker); } -void wil_link_on(struct wil6210_priv *wil) -{ - struct net_device *ndev = wil_to_ndev(wil); - - wil_dbg_misc(wil, "%s()\n", __func__); - - netif_carrier_on(ndev); - wil_dbg_misc(wil, "netif_tx_wake : link on\n"); - netif_tx_wake_all_queues(ndev); -} - -void wil_link_off(struct wil6210_priv *wil) -{ - struct net_device *ndev = wil_to_ndev(wil); - - wil_dbg_misc(wil, "%s()\n", __func__); - - netif_tx_stop_all_queues(ndev); - wil_dbg_misc(wil, "netif_tx_stop : link off\n"); - netif_carrier_off(ndev); -} - int __wil_up(struct wil6210_priv *wil) { struct net_device *ndev = wil_to_ndev(wil); @@ -774,7 +771,7 @@ int __wil_up(struct wil6210_priv *wil) wil_dbg_misc(wil, "NAPI enable\n"); napi_enable(&wil->napi_rx); napi_enable(&wil->napi_tx); - set_bit(wil_status_napi_en, &wil->status); + set_bit(wil_status_napi_en, wil->status); if (wil->platform_ops.bus_request) wil->platform_ops.bus_request(wil->platform_handle, @@ -807,7 +804,7 @@ int __wil_down(struct wil6210_priv *wil) wil->platform_ops.bus_request(wil->platform_handle, 0); wil_disable_irq(wil); - if (test_and_clear_bit(wil_status_napi_en, &wil->status)) { + if (test_and_clear_bit(wil_status_napi_en, wil->status)) { napi_disable(&wil->napi_rx); napi_disable(&wil->napi_tx); wil_dbg_misc(wil, "NAPI disable\n"); @@ -822,15 +819,15 @@ int __wil_down(struct wil6210_priv *wil) wil->scan_request = NULL; } - if (test_bit(wil_status_fwconnected, &wil->status) || - test_bit(wil_status_fwconnecting, &wil->status)) + if (test_bit(wil_status_fwconnected, wil->status) || + test_bit(wil_status_fwconnecting, wil->status)) wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0); /* make sure wil is idle (not connected) */ mutex_unlock(&wil->mutex); while (iter--) { - int idle = !test_bit(wil_status_fwconnected, &wil->status) && - !test_bit(wil_status_fwconnecting, &wil->status); + int idle = !test_bit(wil_status_fwconnected, wil->status) && + !test_bit(wil_status_fwconnecting, wil->status); if (idle) break; msleep(WAIT_FOR_DISCONNECT_INTERVAL_MS); diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index e81703ca7701..ace30c1b5c64 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -15,7 +15,6 @@ */ #include <linux/etherdevice.h> - #include "wil6210.h" #include "txrx.h" @@ -122,6 +121,12 @@ static int wil6210_netdev_poll_tx(struct napi_struct *napi, int budget) return min(tx_done, budget); } +static void wil_dev_setup(struct net_device *dev) +{ + ether_setup(dev); + dev->tx_queue_len = WIL_TX_Q_LEN_DEFAULT; +} + void *wil_if_alloc(struct device *dev, void __iomem *csr) { struct net_device *ndev; @@ -153,7 +158,7 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr) ch = wdev->wiphy->bands[IEEE80211_BAND_60GHZ]->channels; cfg80211_chandef_create(&wdev->preset_chandef, ch, NL80211_CHAN_NO_HT); - ndev = alloc_netdev(0, "wlan%d", NET_NAME_UNKNOWN, ether_setup); + ndev = alloc_netdev(0, "wlan%d", NET_NAME_UNKNOWN, wil_dev_setup); if (!ndev) { dev_err(dev, "alloc_netdev_mqs failed\n"); rc = -ENOMEM; @@ -174,7 +179,7 @@ void *wil_if_alloc(struct device *dev, void __iomem *csr) netif_napi_add(ndev, &wil->napi_tx, wil6210_netdev_poll_tx, WIL6210_NAPI_BUDGET); - wil_link_off(wil); + netif_tx_stop_all_queues(ndev); return wil; @@ -217,8 +222,6 @@ int wil_if_add(struct wil6210_priv *wil) return rc; } - wil_link_off(wil); - return 0; } diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 66626a8ee728..3dd26709ccb2 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -31,6 +31,46 @@ static bool debug_fw; /* = false; */ module_param(debug_fw, bool, S_IRUGO); MODULE_PARM_DESC(debug_fw, " load driver if FW not ready. For FW debug"); +static +void wil_set_capabilities(struct wil6210_priv *wil) +{ + u32 rev_id = ioread32(wil->csr + HOSTADDR(RGF_USER_JTAG_DEV_ID)); + + bitmap_zero(wil->hw_capabilities, hw_capability_last); + + switch (rev_id) { + case JTAG_DEV_ID_MARLON_B0: + wil->hw_name = "Marlon B0"; + wil->hw_version = HW_VER_MARLON_B0; + break; + case JTAG_DEV_ID_SPARROW_A0: + wil->hw_name = "Sparrow A0"; + wil->hw_version = HW_VER_SPARROW_A0; + break; + case JTAG_DEV_ID_SPARROW_A1: + wil->hw_name = "Sparrow A1"; + wil->hw_version = HW_VER_SPARROW_A1; + break; + case JTAG_DEV_ID_SPARROW_B0: + wil->hw_name = "Sparrow B0"; + wil->hw_version = HW_VER_SPARROW_B0; + break; + default: + wil_err(wil, "Unknown board hardware 0x%08x\n", rev_id); + wil->hw_name = "Unknown"; + wil->hw_version = HW_VER_UNKNOWN; + } + + wil_info(wil, "Board hardware is %s\n", wil->hw_name); + + if (wil->hw_version >= HW_VER_SPARROW_A0) + set_bit(hw_capability_reset_v2, wil->hw_capabilities); + + if (wil->hw_version >= HW_VER_SPARROW_B0) + set_bit(hw_capability_advanced_itr_moderation, + wil->hw_capabilities); +} + void wil_disable_irq(struct wil6210_priv *wil) { int irq = wil->pdev->irq; @@ -149,12 +189,11 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) struct wil6210_priv *wil; struct device *dev = &pdev->dev; void __iomem *csr; - struct wil_board *board = (struct wil_board *)id->driver_data; int rc; /* check HW */ dev_info(&pdev->dev, WIL_NAME - " \"%s\" device found [%04x:%04x] (rev %x)\n", board->name, + " device found [%04x:%04x] (rev %x)\n", (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) { @@ -204,8 +243,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_drvdata(pdev, wil); wil->pdev = pdev; - wil->board = board; - + wil_set_capabilities(wil); wil6210_clear_irq(wil); wil->platform_handle = @@ -266,23 +304,10 @@ static void wil_pcie_remove(struct pci_dev *pdev) pci_disable_device(pdev); } -static const struct wil_board wil_board_marlon = { - .board = WIL_BOARD_MARLON, - .name = "marlon", -}; - -static const struct wil_board wil_board_sparrow = { - .board = WIL_BOARD_SPARROW, - .name = "sparrow", -}; - static const struct pci_device_id wil6210_pcie_ids[] = { - { PCI_DEVICE(0x1ae9, 0x0301), - .driver_data = (kernel_ulong_t)&wil_board_marlon }, - { PCI_DEVICE(0x1ae9, 0x0310), - .driver_data = (kernel_ulong_t)&wil_board_sparrow }, - { PCI_DEVICE(0x1ae9, 0x0302), /* same as above, firmware broken */ - .driver_data = (kernel_ulong_t)&wil_board_sparrow }, + { PCI_DEVICE(0x1ae9, 0x0301) }, + { PCI_DEVICE(0x1ae9, 0x0310) }, + { PCI_DEVICE(0x1ae9, 0x0302) }, /* same as above, firmware broken */ { /* end: all zeroes */ }, }; MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids); diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c index 489cb73d139b..ca10dcf0986e 100644 --- a/drivers/net/wireless/ath/wil6210/rx_reorder.c +++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Qualcomm Atheros, Inc. + * Copyright (c) 2014-2015 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -89,7 +89,9 @@ static void wil_reorder_release(struct wil6210_priv *wil, } } +/* called in NAPI context */ void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb) +__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) { struct net_device *ndev = wil_to_ndev(wil); struct vring_rx_desc *d = wil_skb_rxdesc(skb); @@ -97,22 +99,26 @@ void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb) int cid = wil_rxdesc_cid(d); int mid = wil_rxdesc_mid(d); u16 seq = wil_rxdesc_seq(d); + int mcast = wil_rxdesc_mcast(d); struct wil_sta_info *sta = &wil->sta[cid]; struct wil_tid_ampdu_rx *r; u16 hseq; int index; - unsigned long flags; - wil_dbg_txrx(wil, "MID %d CID %d TID %d Seq 0x%03x\n", - mid, cid, tid, seq); + wil_dbg_txrx(wil, "MID %d CID %d TID %d Seq 0x%03x mcast %01x\n", + mid, cid, tid, seq, mcast); - spin_lock_irqsave(&sta->tid_rx_lock, flags); + if (unlikely(mcast)) { + wil_netif_rx_any(skb, ndev); + return; + } + + spin_lock(&sta->tid_rx_lock); r = sta->tid_rx[tid]; if (!r) { - spin_unlock_irqrestore(&sta->tid_rx_lock, flags); wil_netif_rx_any(skb, ndev); - return; + goto out; } hseq = r->head_seq_num; @@ -121,13 +127,24 @@ void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb) * reported, and data Rx, few packets may be pass up before reorder * buffer get allocated. Catch up by pretending SSN is what we * see in the 1-st Rx packet + * + * Another scenario, Rx get delayed and we got packet from before + * BACK. Pass it to the stack and wait. */ if (r->first_time) { r->first_time = false; if (seq != r->head_seq_num) { - wil_err(wil, "Error: 1-st frame with wrong sequence" - " %d, should be %d. Fixing...\n", seq, - r->head_seq_num); + if (seq_less(seq, r->head_seq_num)) { + wil_err(wil, + "Error: frame with early sequence 0x%03x, should be 0x%03x. Waiting...\n", + seq, r->head_seq_num); + r->first_time = true; + wil_netif_rx_any(skb, ndev); + goto out; + } + wil_err(wil, + "Error: 1-st frame with wrong sequence 0x%03x, should be 0x%03x. Fixing...\n", + seq, r->head_seq_num); r->head_seq_num = seq; r->ssn = seq; } @@ -179,7 +196,7 @@ void wil_rx_reorder(struct wil6210_priv *wil, struct sk_buff *skb) wil_reorder_release(wil, r); out: - spin_unlock_irqrestore(&sta->tid_rx_lock, flags); + spin_unlock(&sta->tid_rx_lock); } struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil, @@ -219,3 +236,241 @@ void wil_tid_ampdu_rx_free(struct wil6210_priv *wil, kfree(r->reorder_time); kfree(r); } + +/* ADDBA processing */ +static u16 wil_agg_size(struct wil6210_priv *wil, u16 req_agg_wsize) +{ + u16 max_agg_size = min_t(u16, WIL_MAX_AGG_WSIZE, WIL_MAX_AMPDU_SIZE / + (mtu_max + WIL_MAX_MPDU_OVERHEAD)); + + if (!req_agg_wsize) + return max_agg_size; + + return min(max_agg_size, req_agg_wsize); +} + +/* Block Ack - Rx side (recipient */ +int wil_addba_rx_request(struct wil6210_priv *wil, u8 cidxtid, + u8 dialog_token, __le16 ba_param_set, + __le16 ba_timeout, __le16 ba_seq_ctrl) +{ + struct wil_back_rx *req = kzalloc(sizeof(*req), GFP_KERNEL); + + if (!req) + return -ENOMEM; + + req->cidxtid = cidxtid; + req->dialog_token = dialog_token; + req->ba_param_set = le16_to_cpu(ba_param_set); + req->ba_timeout = le16_to_cpu(ba_timeout); + req->ba_seq_ctrl = le16_to_cpu(ba_seq_ctrl); + + mutex_lock(&wil->back_rx_mutex); + list_add_tail(&req->list, &wil->back_rx_pending); + mutex_unlock(&wil->back_rx_mutex); + + queue_work(wil->wq_service, &wil->back_rx_worker); + + return 0; +} + +static void wil_back_rx_handle(struct wil6210_priv *wil, + struct wil_back_rx *req) +__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) +{ + struct wil_sta_info *sta; + u8 cid, tid; + u16 agg_wsize = 0; + /* bit 0: A-MSDU supported + * bit 1: policy (should be 0 for us) + * bits 2..5: TID + * bits 6..15: buffer size + */ + u16 req_agg_wsize = WIL_GET_BITS(req->ba_param_set, 6, 15); + bool agg_amsdu = !!(req->ba_param_set & BIT(0)); + int ba_policy = req->ba_param_set & BIT(1); + u16 agg_timeout = req->ba_timeout; + u16 status = WLAN_STATUS_SUCCESS; + u16 ssn = req->ba_seq_ctrl >> 4; + struct wil_tid_ampdu_rx *r; + int rc; + + might_sleep(); + parse_cidxtid(req->cidxtid, &cid, &tid); + + /* sanity checks */ + if (cid >= WIL6210_MAX_CID) { + wil_err(wil, "BACK: invalid CID %d\n", cid); + return; + } + + sta = &wil->sta[cid]; + if (sta->status != wil_sta_connected) { + wil_err(wil, "BACK: CID %d not connected\n", cid); + return; + } + + wil_dbg_wmi(wil, + "ADDBA request for CID %d %pM TID %d size %d timeout %d AMSDU%s policy %d token %d SSN 0x%03x\n", + cid, sta->addr, tid, req_agg_wsize, req->ba_timeout, + agg_amsdu ? "+" : "-", !!ba_policy, req->dialog_token, ssn); + + /* apply policies */ + if (ba_policy) { + wil_err(wil, "BACK requested unsupported ba_policy == 1\n"); + status = WLAN_STATUS_INVALID_QOS_PARAM; + } + if (status == WLAN_STATUS_SUCCESS) + agg_wsize = wil_agg_size(wil, req_agg_wsize); + + rc = wmi_addba_rx_resp(wil, cid, tid, req->dialog_token, status, + agg_amsdu, agg_wsize, agg_timeout); + if (rc || (status != WLAN_STATUS_SUCCESS)) + return; + + /* apply */ + r = wil_tid_ampdu_rx_alloc(wil, agg_wsize, ssn); + spin_lock_bh(&sta->tid_rx_lock); + wil_tid_ampdu_rx_free(wil, sta->tid_rx[tid]); + sta->tid_rx[tid] = r; + spin_unlock_bh(&sta->tid_rx_lock); +} + +void wil_back_rx_flush(struct wil6210_priv *wil) +{ + struct wil_back_rx *evt, *t; + + wil_dbg_misc(wil, "%s()\n", __func__); + + mutex_lock(&wil->back_rx_mutex); + + list_for_each_entry_safe(evt, t, &wil->back_rx_pending, list) { + list_del(&evt->list); + kfree(evt); + } + + mutex_unlock(&wil->back_rx_mutex); +} + +/* Retrieve next ADDBA request from the pending list */ +static struct list_head *next_back_rx(struct wil6210_priv *wil) +{ + struct list_head *ret = NULL; + + mutex_lock(&wil->back_rx_mutex); + + if (!list_empty(&wil->back_rx_pending)) { + ret = wil->back_rx_pending.next; + list_del(ret); + } + + mutex_unlock(&wil->back_rx_mutex); + + return ret; +} + +void wil_back_rx_worker(struct work_struct *work) +{ + struct wil6210_priv *wil = container_of(work, struct wil6210_priv, + back_rx_worker); + struct wil_back_rx *evt; + struct list_head *lh; + + while ((lh = next_back_rx(wil)) != NULL) { + evt = list_entry(lh, struct wil_back_rx, list); + + wil_back_rx_handle(wil, evt); + kfree(evt); + } +} + +/* BACK - Tx (originator) side */ +static void wil_back_tx_handle(struct wil6210_priv *wil, + struct wil_back_tx *req) +{ + struct vring_tx_data *txdata = &wil->vring_tx_data[req->ringid]; + int rc; + + if (txdata->addba_in_progress) { + wil_dbg_misc(wil, "ADDBA for vring[%d] already in progress\n", + req->ringid); + return; + } + if (txdata->agg_wsize) { + wil_dbg_misc(wil, + "ADDBA for vring[%d] already established wsize %d\n", + req->ringid, txdata->agg_wsize); + return; + } + txdata->addba_in_progress = true; + rc = wmi_addba(wil, req->ringid, req->agg_wsize, req->agg_timeout); + if (rc) + txdata->addba_in_progress = false; +} + +static struct list_head *next_back_tx(struct wil6210_priv *wil) +{ + struct list_head *ret = NULL; + + mutex_lock(&wil->back_tx_mutex); + + if (!list_empty(&wil->back_tx_pending)) { + ret = wil->back_tx_pending.next; + list_del(ret); + } + + mutex_unlock(&wil->back_tx_mutex); + + return ret; +} + +void wil_back_tx_worker(struct work_struct *work) +{ + struct wil6210_priv *wil = container_of(work, struct wil6210_priv, + back_tx_worker); + struct wil_back_tx *evt; + struct list_head *lh; + + while ((lh = next_back_tx(wil)) != NULL) { + evt = list_entry(lh, struct wil_back_tx, list); + + wil_back_tx_handle(wil, evt); + kfree(evt); + } +} + +void wil_back_tx_flush(struct wil6210_priv *wil) +{ + struct wil_back_tx *evt, *t; + + wil_dbg_misc(wil, "%s()\n", __func__); + + mutex_lock(&wil->back_tx_mutex); + + list_for_each_entry_safe(evt, t, &wil->back_tx_pending, list) { + list_del(&evt->list); + kfree(evt); + } + + mutex_unlock(&wil->back_tx_mutex); +} + +int wil_addba_tx_request(struct wil6210_priv *wil, u8 ringid, u16 wsize) +{ + struct wil_back_tx *req = kzalloc(sizeof(*req), GFP_KERNEL); + + if (!req) + return -ENOMEM; + + req->ringid = ringid; + req->agg_wsize = wil_agg_size(wil, wsize); + req->agg_timeout = 0; + + mutex_lock(&wil->back_tx_mutex); + list_add_tail(&req->list, &wil->back_tx_pending); + mutex_unlock(&wil->back_tx_mutex); + + queue_work(wil->wq_service, &wil->back_tx_worker); + + return 0; +} diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index e3f8bdce5abc..8439f65db259 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -463,7 +463,7 @@ static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, * and in case of error drop the packet * higher stack layers will handle retransmission (if required) */ - if (d->dma.status & RX_DMA_STATUS_L4_IDENT) { + if (d->dma.status & RX_DMA_STATUS_L4I) { /* L4 protocol identified, csum calculated */ if ((d->dma.error & RX_DMA_ERROR_L4_ERR) == 0) skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -581,14 +581,8 @@ void wil_rx_handle(struct wil6210_priv *wil, int *quota) skb->protocol = htons(ETH_P_802_2); wil_netif_rx_any(skb, ndev); } else { - struct ethhdr *eth = (void *)skb->data; - skb->protocol = eth_type_trans(skb, ndev); - - if (is_unicast_ether_addr(eth->h_dest)) - wil_rx_reorder(wil, skb); - else - wil_netif_rx_any(skb, ndev); + wil_rx_reorder(wil, skb); } } wil_rx_refill(wil, v->size); @@ -645,7 +639,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, .vring_cfg = { .tx_sw_ring = { .max_mpdu_size = - cpu_to_le16(mtu_max + ETH_HLEN), + cpu_to_le16(wil_mtu2macbuf(mtu_max)), .ring_size = cpu_to_le16(size), }, .ringid = id, @@ -653,7 +647,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, .encap_trans_type = WMI_VRING_ENC_TYPE_802_3, .mac_ctrl = 0, .to_resolution = 0, - .agg_max_wsize = 16, + .agg_max_wsize = 0, .schd_params = { .priority = cpu_to_le16(0), .timeslot_us = cpu_to_le16(0xfff), @@ -677,6 +671,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, } memset(txdata, 0, sizeof(*txdata)); + spin_lock_init(&txdata->lock); vring->size = size; rc = wil_vring_alloc(wil, vring); if (rc) @@ -701,6 +696,8 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr); txdata->enabled = 1; + if (wil->sta[cid].data_port_open && (agg_wsize >= 0)) + wil_addba_tx_request(wil, id, agg_wsize); return 0; out_free: @@ -713,6 +710,7 @@ int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, void wil_vring_fini_tx(struct wil6210_priv *wil, int id) { struct vring *vring = &wil->vring_tx[id]; + struct vring_tx_data *txdata = &wil->vring_tx_data[id]; WARN_ON(!mutex_is_locked(&wil->mutex)); @@ -721,12 +719,15 @@ void wil_vring_fini_tx(struct wil6210_priv *wil, int id) wil_dbg_misc(wil, "%s() id=%d\n", __func__, id); + spin_lock_bh(&txdata->lock); + txdata->enabled = 0; /* no Tx can be in progress or start anew */ + spin_unlock_bh(&txdata->lock); /* make sure NAPI won't touch this vring */ - wil->vring_tx_data[id].enabled = 0; - if (test_bit(wil_status_napi_en, &wil->status)) + if (test_bit(wil_status_napi_en, wil->status)) napi_synchronize(&wil->napi_tx); wil_vring_free(wil, vring, 1); + memset(txdata, 0, sizeof(*txdata)); } static struct vring *wil_find_tx_vring(struct wil6210_priv *wil, @@ -773,6 +774,38 @@ static void wil_set_da_for_vring(struct wil6210_priv *wil, static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, struct sk_buff *skb); + +static struct vring *wil_find_tx_vring_sta(struct wil6210_priv *wil, + struct sk_buff *skb) +{ + struct vring *v; + int i; + u8 cid; + + /* In the STA mode, it is expected to have only 1 VRING + * for the AP we connected to. + * find 1-st vring and see whether it is eligible for data + */ + for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { + v = &wil->vring_tx[i]; + if (!v->va) + continue; + + cid = wil->vring2cid_tid[i][0]; + if (!wil->sta[cid].data_port_open && + (skb->protocol != cpu_to_be16(ETH_P_PAE))) + break; + + wil_dbg_txrx(wil, "Tx -> ring %d\n", i); + + return v; + } + + wil_dbg_txrx(wil, "Tx while no vrings active?\n"); + + return NULL; +} + /* * Find 1-st vring and return it; set dest address for this vring in skb * duplicate skb and send it to other active vrings @@ -843,9 +876,6 @@ static int wil_tx_desc_map(struct vring_tx_desc *d, dma_addr_t pa, u32 len, d->mac.d[1] = 0; d->mac.d[2] = 0; d->mac.ucode_cmd = 0; - /* use dst index 0 */ - d->mac.d[1] |= BIT(MAC_CFG_DESC_TX_1_DST_INDEX_EN_POS) | - (0 << MAC_CFG_DESC_TX_1_DST_INDEX_POS); /* translation type: 0 - bypass; 1 - 802.3; 2 - native wifi */ d->mac.d[2] = BIT(MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS) | (1 << MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS); @@ -908,8 +938,8 @@ static int wil_tx_desc_offload_cksum_set(struct wil6210_priv *wil, return 0; } -static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, - struct sk_buff *skb) +static int __wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, + struct sk_buff *skb) { struct device *dev = wil_to_dev(wil); struct vring_tx_desc dd, *d = ⅆ @@ -925,18 +955,21 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, wil_dbg_txrx(wil, "%s()\n", __func__); + if (unlikely(!txdata->enabled)) + return -EINVAL; + if (avail < 1 + nr_frags) { wil_err_ratelimited(wil, - "Tx ring full. No space for %d fragments\n", - 1 + nr_frags); + "Tx ring[%2d] full. No space for %d fragments\n", + vring_index, 1 + nr_frags); return -ENOMEM; } _d = &vring->va[i].tx; pa = dma_map_single(dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); - wil_dbg_txrx(wil, "Tx skb %d bytes 0x%p -> %pad\n", skb_headlen(skb), - skb->data, &pa); + wil_dbg_txrx(wil, "Tx[%2d] skb %d bytes 0x%p -> %pad\n", vring_index, + skb_headlen(skb), skb->data, &pa); wil_hex_dump_txrx("Tx ", DUMP_PREFIX_OFFSET, 16, 1, skb->data, skb_headlen(skb), false); @@ -947,15 +980,13 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, wil_tx_desc_map(d, pa, skb_headlen(skb), vring_index); /* Process TCP/UDP checksum offloading */ if (wil_tx_desc_offload_cksum_set(wil, d, skb)) { - wil_err(wil, "VRING #%d Failed to set cksum, drop packet\n", + wil_err(wil, "Tx[%2d] Failed to set cksum, drop packet\n", vring_index); goto dma_error; } vring->ctx[i].nr_frags = nr_frags; wil_tx_desc_set_nr_frags(d, nr_frags); - if (nr_frags) - *_d = *d; /* middle segments */ for (; f < nr_frags; f++) { @@ -963,6 +994,10 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, &skb_shinfo(skb)->frags[f]; int len = skb_frag_size(frag); + *_d = *d; + wil_dbg_txrx(wil, "Tx[%2d] desc[%4d]\n", vring_index, i); + wil_hex_dump_txrx("TxD ", DUMP_PREFIX_NONE, 32, 4, + (const void *)d, sizeof(*d), false); i = (swhead + f + 1) % vring->size; _d = &vring->va[i].tx; pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag), @@ -976,13 +1011,15 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, * it will succeed here too */ wil_tx_desc_offload_cksum_set(wil, d, skb); - *_d = *d; } /* for the last seg only */ d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS); d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_MARK_WB_POS); d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS); *_d = *d; + wil_dbg_txrx(wil, "Tx[%2d] desc[%4d]\n", vring_index, i); + wil_hex_dump_txrx("TxD ", DUMP_PREFIX_NONE, 32, 4, + (const void *)d, sizeof(*d), false); /* hold reference to skb * to prevent skb release before accounting @@ -990,15 +1027,13 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, */ vring->ctx[i].skb = skb_get(skb); - wil_hex_dump_txrx("Tx ", DUMP_PREFIX_NONE, 32, 4, - (const void *)d, sizeof(*d), false); - if (wil_vring_is_empty(vring)) /* performance monitoring */ txdata->idle += get_cycles() - txdata->last_idle; /* advance swhead */ wil_vring_advance_head(vring, nr_frags + 1); - wil_dbg_txrx(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead); + wil_dbg_txrx(wil, "Tx[%2d] swhead %d -> %d\n", vring_index, swhead, + vring->swhead); trace_wil6210_tx(vring_index, swhead, skb->len, nr_frags); iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail)); @@ -1025,6 +1060,19 @@ static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, return -EINVAL; } +static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, + struct sk_buff *skb) +{ + int vring_index = vring - wil->vring_tx; + struct vring_tx_data *txdata = &wil->vring_tx_data[vring_index]; + int rc; + + spin_lock(&txdata->lock); + rc = __wil_tx_vring(wil, vring, skb); + spin_unlock(&txdata->lock); + return rc; +} + netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) { struct wil6210_priv *wil = ndev_to_wil(ndev); @@ -1034,14 +1082,14 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) int rc; wil_dbg_txrx(wil, "%s()\n", __func__); - if (!test_bit(wil_status_fwready, &wil->status)) { + if (!test_bit(wil_status_fwready, wil->status)) { if (!pr_once_fw) { wil_err(wil, "FW not ready\n"); pr_once_fw = true; } goto drop; } - if (!test_bit(wil_status_fwconnected, &wil->status)) { + if (!test_bit(wil_status_fwconnected, wil->status)) { wil_err(wil, "FW not connected\n"); goto drop; } @@ -1052,15 +1100,19 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) pr_once_fw = false; /* find vring */ - if (is_unicast_ether_addr(eth->h_dest)) - vring = wil_find_tx_vring(wil, skb); - else - vring = wil_tx_bcast(wil, skb); + if (wil->wdev->iftype == NL80211_IFTYPE_STATION) { + /* in STA mode (ESS), all to same VRING */ + vring = wil_find_tx_vring_sta(wil, skb); + } else { /* direct communication, find matching VRING */ + if (is_unicast_ether_addr(eth->h_dest)) + vring = wil_find_tx_vring(wil, skb); + else + vring = wil_tx_bcast(wil, skb); + } if (!vring) { wil_dbg_txrx(wil, "No Tx VRING found for %pM\n", eth->h_dest); goto drop; } - /* set up vring entry */ rc = wil_tx_vring(wil, vring, skb); @@ -1087,6 +1139,22 @@ netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) return NET_XMIT_DROP; } +static inline bool wil_need_txstat(struct sk_buff *skb) +{ + struct ethhdr *eth = (void *)skb->data; + + return is_unicast_ether_addr(eth->h_dest) && skb->sk && + (skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS); +} + +static inline void wil_consume_skb(struct sk_buff *skb, bool acked) +{ + if (unlikely(wil_need_txstat(skb))) + skb_complete_wifi_ack(skb, acked); + else + acked ? dev_consume_skb_any(skb) : dev_kfree_skb_any(skb); +} + /** * Clean up transmitted skb's from the Tx VRING * @@ -1147,10 +1215,10 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) trace_wil6210_tx_done(ringid, vring->swtail, dmalen, d->dma.error); wil_dbg_txrx(wil, - "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n", - vring->swtail, dmalen, d->dma.status, - d->dma.error); - wil_hex_dump_txrx("TxC ", DUMP_PREFIX_NONE, 32, 4, + "TxC[%2d][%3d] : %d bytes, status 0x%02x err 0x%02x\n", + ringid, vring->swtail, dmalen, + d->dma.status, d->dma.error); + wil_hex_dump_txrx("TxCD ", DUMP_PREFIX_NONE, 32, 4, (const void *)d, sizeof(*d), false); wil_txdesc_unmap(dev, d, ctx); @@ -1165,8 +1233,7 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) ndev->stats.tx_errors++; stats->tx_errors++; } - - dev_kfree_skb_any(skb); + wil_consume_skb(skb, d->dma.error == 0); } memset(ctx, 0, sizeof(*ctx)); /* There is no need to touch HW descriptor: diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h index 630aeb5fa7f4..d90c8aa20c15 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.h +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -20,17 +20,15 @@ #define BUF_SW_OWNED (1) #define BUF_HW_OWNED (0) -/* size of max. Tx/Rx buffers, as supported by FW */ -#define TXRX_BUF_LEN_DEFAULT (2242) +/* default size of MAC Tx/Rx buffers */ +#define TXRX_BUF_LEN_DEFAULT (2048) /* how many bytes to reserve for rtap header? */ #define WIL6210_RTAP_SIZE (128) /* Tx/Rx path */ -/* - * Common representation of physical address in Vring - */ +/* Common representation of physical address in Vring */ struct vring_dma_addr { __le32 addr_low; __le16 addr_high; @@ -49,11 +47,10 @@ static inline void wil_desc_addr_set(struct vring_dma_addr *addr, addr->addr_high = cpu_to_le16((u16)upper_32_bits(pa)); } -/* - * Tx descriptor - MAC part +/* Tx descriptor - MAC part * [dword 0] * bit 0.. 9 : lifetime_expiry_value:10 - * bit 10 : interrup_en:1 + * bit 10 : interrupt_en:1 * bit 11 : status_en:1 * bit 12..13 : txss_override:2 * bit 14 : timestamp_insertion:1 @@ -61,15 +58,12 @@ static inline void wil_desc_addr_set(struct vring_dma_addr *addr, * bit 16..21 : reserved0:6 * bit 22..26 : mcs_index:5 * bit 27 : mcs_en:1 - * bit 28..29 : reserved1:2 - * bit 30 : reserved2:1 + * bit 28..30 : reserved1:3 * bit 31 : sn_preserved:1 * [dword 1] * bit 0.. 3 : pkt_mode:4 * bit 4 : pkt_mode_en:1 - * bit 5.. 7 : reserved0:3 - * bit 8..13 : reserved1:6 - * bit 14 : reserved2:1 + * bit 5..14 : reserved0:10 * bit 15 : ack_policy_en:1 * bit 16..19 : dst_index:4 * bit 20 : dst_index_en:1 @@ -80,7 +74,7 @@ static inline void wil_desc_addr_set(struct vring_dma_addr *addr, * [dword 2] * bit 0.. 7 : num_of_descriptors:8 * bit 8..17 : reserved:10 - * bit 18..19 : l2_translation_type:2 + * bit 18..19 : l2_translation_type:2 00 - bypass, 01 - 802.3, 10 - 802.11 * bit 20 : snap_hdr_insertion_en:1 * bit 21 : vlan_removal_en:1 * bit 22..31 : reserved0:10 @@ -247,6 +241,46 @@ struct vring_tx_mac { #define TX_DMA_STATUS_DU BIT(0) +/* Tx descriptor - DMA part + * [dword 0] + * bit 0.. 7 : l4_length:8 layer 4 length + * bit 8 : cmd_eop:1 This descriptor is the last one in the packet + * bit 9 : reserved + * bit 10 : cmd_dma_it:1 immediate interrupt + * bit 11..12 : SBD - Segment Buffer Details + * 00 - Header Segment + * 01 - First Data Segment + * 10 - Medium Data Segment + * 11 - Last Data Segment + * bit 13 : TSE - TCP Segmentation Enable + * bit 14 : IIC - Directs the HW to Insert IPv4 Checksum + * bit 15 : ITC - Directs the HW to Insert TCP/UDP Checksum + * bit 16..20 : QID - The target QID that the packet should be stored + * in the MAC. + * bit 21 : PO - Pseudo header Offload: + * 0 - Use the pseudo header value from the TCP checksum field + * 1- Calculate Pseudo header Checksum + * bit 22 : NC - No UDP Checksum + * bit 23..29 : reserved + * bit 30..31 : L4T - Layer 4 Type: 00 - UDP , 10 - TCP , 10, 11 - Reserved + * If L4Len equal 0, no L4 at all + * [dword 1] + * bit 0..31 : addr_low:32 The payload buffer low address + * [dword 2] + * bit 0..15 : addr_high:16 The payload buffer high address + * bit 16..23 : ip_length:8 The IP header length for the TX IP checksum + * offload feature + * bit 24..30 : mac_length:7 + * bit 31 : ip_version:1 1 - IPv4, 0 - IPv6 + * [dword 3] + * [byte 12] error + * bit 0 2 : mac_status:3 + * bit 3 7 : reserved:5 + * [byte 13] status + * bit 0 : DU:1 Descriptor Used + * bit 1 7 : reserved:7 + * [word 7] length + */ struct vring_tx_dma { u32 d0; struct vring_dma_addr addr; @@ -257,45 +291,45 @@ struct vring_tx_dma { __le16 length; } __packed; -/* - * Rx descriptor - MAC part +/* Rx descriptor - MAC part * [dword 0] * bit 0.. 3 : tid:4 The QoS (b3-0) TID Field - * bit 4.. 6 : connection_id:3 :The Source index that was found during - * Parsing the TA. This field is used to define the source of the packet + * bit 4.. 6 : cid:3 The Source index that was found during parsing the TA. + * This field is used to define the source of the packet * bit 7 : reserved:1 - * bit 8.. 9 : mac_id:2 : The MAC virtual Ring number (always zero) - * bit 10..11 : frame_type:2 : The FC Control (b3-2) - MPDU Type - * (management, data, control and extension) - * bit 12..15 : frame_subtype:4 : The FC Control (b7-4) - Frame Subtype + * bit 8.. 9 : mid:2 The MAC virtual number + * bit 10..11 : frame_type:2 : The FC (b3-2) - MPDU Type + * (management, data, control and extension) + * bit 12..15 : frame_subtype:4 : The FC (b7-4) - Frame Subtype * bit 16..27 : seq_number:12 The received Sequence number field * bit 28..31 : extended:4 extended subtype * [dword 1] * bit 0.. 3 : reserved * bit 4.. 5 : key_id:2 * bit 6 : decrypt_bypass:1 - * bit 7 : security:1 - * bit 8.. 9 : ds_bits:2 - * bit 10 : a_msdu_present:1 from qos header - * bit 11 : a_msdu_type:1 from qos header + * bit 7 : security:1 FC (b14) + * bit 8.. 9 : ds_bits:2 FC (b9-8) + * bit 10 : a_msdu_present:1 QoS (b7) + * bit 11 : a_msdu_type:1 QoS (b8) * bit 12 : a_mpdu:1 part of AMPDU aggregation * bit 13 : broadcast:1 * bit 14 : mutlicast:1 * bit 15 : reserved:1 - * bit 16..20 : rx_mac_qid:5 The Queue Identifier that the packet - * is received from + * bit 16..20 : rx_mac_qid:5 The Queue Identifier that the packet + * is received from * bit 21..24 : mcs:4 - * bit 25..28 : mic_icr:4 + * bit 25..28 : mic_icr:4 this signal tells the DMA to assert an interrupt + * after it writes the packet * bit 29..31 : reserved:3 * [dword 2] * bit 0.. 2 : time_slot:3 The timeslot that the MPDU is received - * bit 3 : fc_protocol_ver:1 The FC Control (b0) - Protocol Version - * bit 4 : fc_order:1 The FC Control (b15) -Order - * bit 5.. 7 : qos_ack_policy:3 The QoS (b6-5) ack policy Field + * bit 3.. 4 : fc_protocol_ver:1 The FC (b1-0) - Protocol Version + * bit 5 : fc_order:1 The FC Control (b15) -Order + * bit 6.. 7 : qos_ack_policy:2 The QoS (b6-5) ack policy Field * bit 8 : esop:1 The QoS (b4) ESOP field - * bit 9 : qos_rdg_more_ppdu:1 The QoS (b9) RDG field - * bit 10..14 : qos_reserved:5 The QoS (b14-10) Reserved field - * bit 15 : qos_ac_constraint:1 + * bit 9 : qos_rdg_more_ppdu:1 The QoS (b9) RDG field + * bit 10..14 : qos_reserved:5 The QoS (b14-10) Reserved field + * bit 15 : qos_ac_constraint:1 QoS (b15) * bit 16..31 : pn_15_0:16 low 2 bytes of PN * [dword 3] * bit 0..31 : pn_47_16:32 high 4 bytes of PN @@ -308,35 +342,46 @@ struct vring_rx_mac { u32 pn_47_16; } __packed; -/* - * Rx descriptor - DMA part +/* Rx descriptor - DMA part * [dword 0] - * bit 0.. 7 : l4_length:8 layer 4 length - * bit 8.. 9 : reserved:2 - * bit 10 : cmd_dma_it:1 + * bit 0.. 7 : l4_length:8 layer 4 length. The field is only valid if + * L4I bit is set + * bit 8 : cmd_eop:1 set to 1 + * bit 9 : cmd_rt:1 set to 1 + * bit 10 : cmd_dma_it:1 immediate interrupt * bit 11..15 : reserved:5 - * bit 16..29 : phy_info_length:14 + * bit 16..29 : phy_info_length:14 It is valid when the PII is set. + * When the FFM bit is set bits 29-27 are used for for + * Flex Filter Match. Matching Index to one of the L2 + * EtherType Flex Filter * bit 30..31 : l4_type:2 valid if the L4I bit is set in the status field + * 00 - UDP, 01 - TCP, 10, 11 - reserved * [dword 1] * bit 0..31 : addr_low:32 The payload buffer low address * [dword 2] * bit 0..15 : addr_high:16 The payload buffer high address - * bit 16..23 : ip_length:8 + * bit 16..23 : ip_length:8 The filed is valid only if the L3I bit is set * bit 24..30 : mac_length:7 - * bit 31 : ip_version:1 + * bit 31 : ip_version:1 1 - IPv4, 0 - IPv6 * [dword 3] * [byte 12] error + * bit 0 : FCS:1 + * bit 1 : MIC:1 + * bit 2 : Key miss:1 + * bit 3 : Replay:1 + * bit 4 : L3:1 IPv4 checksum + * bit 5 : L4:1 TCP/UDP checksum + * bit 6 7 : reserved:2 * [byte 13] status - * bit 0 : du:1 - * bit 1 : eop:1 + * bit 0 : DU:1 Descriptor Used + * bit 1 : EOP:1 The descriptor indicates the End of Packet * bit 2 : error:1 - * bit 3 : mi:1 - * bit 4 : l3_identified:1 - * bit 5 : l4_identified:1 - * bit 6 : phy_info_included:1 - * bit 7 : reserved:1 + * bit 3 : MI:1 MAC Interrupt is asserted (according to parser decision) + * bit 4 : L3I:1 L3 identified and checksum calculated + * bit 5 : L4I:1 L4 identified and checksum calculated + * bit 6 : PII:1 PHY Info Included in the packet + * bit 7 : FFM:1 EtherType Flex Filter Match * [word 7] length - * */ #define RX_DMA_D0_CMD_DMA_IT BIT(10) @@ -349,9 +394,9 @@ struct vring_rx_mac { #define RX_DMA_STATUS_DU BIT(0) #define RX_DMA_STATUS_ERROR BIT(2) -#define RX_DMA_STATUS_L3_IDENT BIT(4) -#define RX_DMA_STATUS_L4_IDENT BIT(5) -#define RX_DMA_STATUS_PHY_INFO BIT(6) +#define RX_DMA_STATUS_L3I BIT(4) +#define RX_DMA_STATUS_L4I BIT(5) +#define RX_DMA_STATUS_PHY_INFO BIT(6) struct vring_rx_dma { u32 d0; @@ -423,6 +468,11 @@ static inline int wil_rxdesc_mcs(struct vring_rx_desc *d) return WIL_GET_BITS(d->mac.d1, 21, 24); } +static inline int wil_rxdesc_mcast(struct vring_rx_desc *d) +{ + return WIL_GET_BITS(d->mac.d1, 13, 14); +} + static inline int wil_rxdesc_phy_length(struct vring_rx_desc *d) { return WIL_GET_BITS(d->dma.d0, 16, 29); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index c6ec5b99ac7d..94611568fc9a 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Qualcomm Atheros, Inc. + * Copyright (c) 2012-2015 Qualcomm Atheros, Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -25,19 +25,14 @@ extern bool no_fw_recovery; extern unsigned int mtu_max; +extern unsigned short rx_ring_overflow_thrsh; +extern int agg_wsize; #define WIL_NAME "wil6210" #define WIL_FW_NAME "wil6210.fw" #define WIL_MAX_BUS_REQUEST_KBPS 800000 /* ~6.1Gbps */ -struct wil_board { - int board; -#define WIL_BOARD_MARLON (1) -#define WIL_BOARD_SPARROW (2) - const char * const name; -}; - /** * extract bits [@b0:@b1] (inclusive) from the value @x * it should be @b0 <= @b1, or result is incorrect @@ -49,21 +44,50 @@ static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) #define WIL6210_MEM_SIZE (2*1024*1024UL) -#define WIL_RX_RING_SIZE_ORDER_DEFAULT (9) -#define WIL_TX_RING_SIZE_ORDER_DEFAULT (9) +#define WIL_TX_Q_LEN_DEFAULT (4000) +#define WIL_RX_RING_SIZE_ORDER_DEFAULT (10) +#define WIL_TX_RING_SIZE_ORDER_DEFAULT (10) /* limit ring size in range [32..32k] */ #define WIL_RING_SIZE_ORDER_MIN (5) #define WIL_RING_SIZE_ORDER_MAX (15) #define WIL6210_MAX_TX_RINGS (24) /* HW limit */ #define WIL6210_MAX_CID (8) /* HW limit */ #define WIL6210_NAPI_BUDGET (16) /* arbitrary */ +#define WIL_MAX_AMPDU_SIZE (64 * 1024) /* FW/HW limit */ +#define WIL_MAX_AGG_WSIZE (32) /* FW/HW limit */ +/* Hardware offload block adds the following: + * 26 bytes - 3-address QoS data header + * 8 bytes - IV + EIV (for GCMP) + * 8 bytes - SNAP + * 16 bytes - MIC (for GCMP) + * 4 bytes - CRC + */ +#define WIL_MAX_MPDU_OVERHEAD (62) + +/* Calculate MAC buffer size for the firmware. It includes all overhead, + * as it will go over the air, and need to be 8 byte aligned + */ +static inline u32 wil_mtu2macbuf(u32 mtu) +{ + return ALIGN(mtu + WIL_MAX_MPDU_OVERHEAD, 8); +} + +/* MTU for Ethernet need to take into account 8-byte SNAP header + * to be added when encapsulating Ethernet frame into 802.11 + */ +#define WIL_MAX_ETH_MTU (IEEE80211_MAX_DATA_LEN_DMG - 8) /* Max supported by wil6210 value for interrupt threshold is 5sec. */ #define WIL6210_ITR_TRSH_MAX (5000000) -#define WIL6210_ITR_TRSH_DEFAULT (300) /* usec */ +#define WIL6210_ITR_TX_INTERFRAME_TIMEOUT_DEFAULT (13) /* usec */ +#define WIL6210_ITR_RX_INTERFRAME_TIMEOUT_DEFAULT (13) /* usec */ +#define WIL6210_ITR_TX_MAX_BURST_DURATION_DEFAULT (500) /* usec */ +#define WIL6210_ITR_RX_MAX_BURST_DURATION_DEFAULT (500) /* usec */ #define WIL6210_FW_RECOVERY_RETRIES (5) /* try to recover this many times */ #define WIL6210_FW_RECOVERY_TO msecs_to_jiffies(5000) #define WIL6210_SCAN_TO msecs_to_jiffies(10000) - +#define WIL6210_RX_HIGH_TRSH_INIT (0) +#define WIL6210_RX_HIGH_TRSH_DEFAULT \ + (1 << (WIL_RX_RING_SIZE_ORDER_DEFAULT - 3)) /* Hardware definitions begin */ /* @@ -135,7 +159,7 @@ struct RGF_ICR { #define BIT_DMA_EP_MISC_ICR_TX_NO_ACT BIT(1) #define BIT_DMA_EP_MISC_ICR_FW_INT(n) BIT(28+n) /* n = [0..3] */ -/* Interrupt moderation control */ +/* Legacy interrupt moderation control (before Sparrow v2)*/ #define RGF_DMA_ITR_CNT_TRSH (0x881c5c) #define RGF_DMA_ITR_CNT_DATA (0x881c60) #define RGF_DMA_ITR_CNT_CRL (0x881c64) @@ -145,6 +169,46 @@ struct RGF_ICR { #define BIT_DMA_ITR_CNT_CRL_CLR BIT(3) #define BIT_DMA_ITR_CNT_CRL_REACH_TRSH BIT(4) +/* New (sparrow v2+) interrupt moderation control */ +#define RGF_DMA_ITR_TX_DESQ_NO_MOD (0x881d40) +#define RGF_DMA_ITR_TX_CNT_TRSH (0x881d34) +#define RGF_DMA_ITR_TX_CNT_DATA (0x881d38) +#define RGF_DMA_ITR_TX_CNT_CTL (0x881d3c) + #define BIT_DMA_ITR_TX_CNT_CTL_EN BIT(0) + #define BIT_DMA_ITR_TX_CNT_CTL_EXT_TIC_SEL BIT(1) + #define BIT_DMA_ITR_TX_CNT_CTL_FOREVER BIT(2) + #define BIT_DMA_ITR_TX_CNT_CTL_CLR BIT(3) + #define BIT_DMA_ITR_TX_CNT_CTL_REACHED_TRESH BIT(4) + #define BIT_DMA_ITR_TX_CNT_CTL_CROSS_EN BIT(5) + #define BIT_DMA_ITR_TX_CNT_CTL_FREE_RUNNIG BIT(6) +#define RGF_DMA_ITR_TX_IDL_CNT_TRSH (0x881d60) +#define RGF_DMA_ITR_TX_IDL_CNT_DATA (0x881d64) +#define RGF_DMA_ITR_TX_IDL_CNT_CTL (0x881d68) + #define BIT_DMA_ITR_TX_IDL_CNT_CTL_EN BIT(0) + #define BIT_DMA_ITR_TX_IDL_CNT_CTL_EXT_TIC_SEL BIT(1) + #define BIT_DMA_ITR_TX_IDL_CNT_CTL_FOREVER BIT(2) + #define BIT_DMA_ITR_TX_IDL_CNT_CTL_CLR BIT(3) + #define BIT_DMA_ITR_TX_IDL_CNT_CTL_REACHED_TRESH BIT(4) +#define RGF_DMA_ITR_RX_DESQ_NO_MOD (0x881d50) +#define RGF_DMA_ITR_RX_CNT_TRSH (0x881d44) +#define RGF_DMA_ITR_RX_CNT_DATA (0x881d48) +#define RGF_DMA_ITR_RX_CNT_CTL (0x881d4c) + #define BIT_DMA_ITR_RX_CNT_CTL_EN BIT(0) + #define BIT_DMA_ITR_RX_CNT_CTL_EXT_TIC_SEL BIT(1) + #define BIT_DMA_ITR_RX_CNT_CTL_FOREVER BIT(2) + #define BIT_DMA_ITR_RX_CNT_CTL_CLR BIT(3) + #define BIT_DMA_ITR_RX_CNT_CTL_REACHED_TRESH BIT(4) + #define BIT_DMA_ITR_RX_CNT_CTL_CROSS_EN BIT(5) + #define BIT_DMA_ITR_RX_CNT_CTL_FREE_RUNNIG BIT(6) +#define RGF_DMA_ITR_RX_IDL_CNT_TRSH (0x881d54) +#define RGF_DMA_ITR_RX_IDL_CNT_DATA (0x881d58) +#define RGF_DMA_ITR_RX_IDL_CNT_CTL (0x881d5c) + #define BIT_DMA_ITR_RX_IDL_CNT_CTL_EN BIT(0) + #define BIT_DMA_ITR_RX_IDL_CNT_CTL_EXT_TIC_SEL BIT(1) + #define BIT_DMA_ITR_RX_IDL_CNT_CTL_FOREVER BIT(2) + #define BIT_DMA_ITR_RX_IDL_CNT_CTL_CLR BIT(3) + #define BIT_DMA_ITR_RX_IDL_CNT_CTL_REACHED_TRESH BIT(4) + #define RGF_DMA_PSEUDO_CAUSE (0x881c68) #define RGF_DMA_PSEUDO_CAUSE_MASK_SW (0x881c6c) #define RGF_DMA_PSEUDO_CAUSE_MASK_FW (0x881c70) @@ -164,6 +228,20 @@ struct RGF_ICR { #define RGF_CAF_PLL_LOCK_STATUS (0x88afec) #define BIT_CAF_OSC_DIG_XTAL_STABLE BIT(0) +#define RGF_USER_JTAG_DEV_ID (0x880b34) /* device ID */ + #define JTAG_DEV_ID_MARLON_B0 (0x0612072f) + #define JTAG_DEV_ID_SPARROW_A0 (0x0632072f) + #define JTAG_DEV_ID_SPARROW_A1 (0x1632072f) + #define JTAG_DEV_ID_SPARROW_B0 (0x2632072f) + +enum { + HW_VER_UNKNOWN, + HW_VER_MARLON_B0, /* JTAG_DEV_ID_MARLON_B0 */ + HW_VER_SPARROW_A0, /* JTAG_DEV_ID_SPARROW_A0 */ + HW_VER_SPARROW_A1, /* JTAG_DEV_ID_SPARROW_A1 */ + HW_VER_SPARROW_B0, /* JTAG_DEV_ID_SPARROW_B0 */ +}; + /* popular locations */ #define HOST_MBOX HOSTADDR(RGF_USER_USER_SCRATCH_PAD) #define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \ @@ -303,6 +381,11 @@ struct vring { struct vring_tx_data { int enabled; cycles_t idle, last_idle, begin; + u8 agg_wsize; /* agreed aggregation window, 0 - no agg */ + u16 agg_timeout; + u8 agg_amsdu; + bool addba_in_progress; /* if set, agg_xxx is for request in progress */ + spinlock_t lock; }; enum { /* for wil6210_priv.status */ @@ -313,6 +396,7 @@ enum { /* for wil6210_priv.status */ wil_status_reset_done, wil_status_irqen, /* FIXME: interrupts enabled - for debug */ wil_status_napi_en, /* NAPI enabled protected by wil->mutex */ + wil_status_last /* keep last */ }; struct pci_dev; @@ -397,15 +481,46 @@ enum { fw_recovery_running = 2, }; +enum { + hw_capability_reset_v2 = 0, + hw_capability_advanced_itr_moderation = 1, + hw_capability_last +}; + +struct wil_back_rx { + struct list_head list; + /* request params, converted to CPU byte order - what we asked for */ + u8 cidxtid; + u8 dialog_token; + u16 ba_param_set; + u16 ba_timeout; + u16 ba_seq_ctrl; +}; + +struct wil_back_tx { + struct list_head list; + /* request params, converted to CPU byte order - what we asked for */ + u8 ringid; + u8 agg_wsize; + u16 agg_timeout; +}; + +struct wil_probe_client_req { + struct list_head list; + u64 cookie; + u8 cid; +}; + struct wil6210_priv { struct pci_dev *pdev; int n_msi; struct wireless_dev *wdev; void __iomem *csr; - ulong status; + DECLARE_BITMAP(status, wil_status_last); u32 fw_version; u32 hw_version; - struct wil_board *board; + const char *hw_name; + DECLARE_BITMAP(hw_capabilities, hw_capability_last); u8 n_mids; /* number of additional MIDs as reported by FW */ u32 recovery_count; /* num of FW recovery attempts in a short time */ u32 recovery_state; /* FW recovery state machine */ @@ -415,7 +530,11 @@ struct wil6210_priv { u32 monitor_flags; u32 secure_pcp; /* create secure PCP? */ int sinfo_gen; - u32 itr_trsh; + /* interrupt moderation */ + u32 tx_max_burst_duration; + u32 tx_interframe_timeout; + u32 rx_max_burst_duration; + u32 rx_interframe_timeout; /* cached ISR registers */ u32 isr_misc; /* mailbox related */ @@ -429,7 +548,7 @@ struct wil6210_priv { u16 reply_size; struct workqueue_struct *wmi_wq; /* for deferred calls */ struct work_struct wmi_event_worker; - struct workqueue_struct *wmi_wq_conn; /* for connect worker */ + struct workqueue_struct *wq_service; struct work_struct connect_worker; struct work_struct disconnect_worker; struct work_struct fw_error_worker; /* for FW error recovery */ @@ -445,6 +564,17 @@ struct wil6210_priv { spinlock_t wmi_ev_lock; struct napi_struct napi_rx; struct napi_struct napi_tx; + /* BACK */ + struct list_head back_rx_pending; + struct mutex back_rx_mutex; /* protect @back_rx_pending */ + struct work_struct back_rx_worker; + struct list_head back_tx_pending; + struct mutex back_tx_mutex; /* protect @back_tx_pending */ + struct work_struct back_tx_worker; + /* keep alive */ + struct list_head probe_client_pending; + struct mutex probe_client_mutex; /* protect @probe_client_pending */ + struct work_struct probe_client_worker; /* DMA related */ struct vring vring_rx; struct vring vring_tx[WIL6210_MAX_TX_RINGS]; @@ -529,11 +659,8 @@ void wil_if_remove(struct wil6210_priv *wil); int wil_priv_init(struct wil6210_priv *wil); void wil_priv_deinit(struct wil6210_priv *wil); int wil_reset(struct wil6210_priv *wil); -void wil_set_itr_trsh(struct wil6210_priv *wil); void wil_fw_error_recovery(struct wil6210_priv *wil); void wil_set_recovery_state(struct wil6210_priv *wil, int state); -void wil_link_on(struct wil6210_priv *wil); -void wil_link_off(struct wil6210_priv *wil); int wil_up(struct wil6210_priv *wil); int __wil_up(struct wil6210_priv *wil); int wil_down(struct wil6210_priv *wil); @@ -567,12 +694,26 @@ int wmi_p2p_cfg(struct wil6210_priv *wil, int channel); int wmi_rxon(struct wil6210_priv *wil, bool on); int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r); int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason); +int wmi_addba(struct wil6210_priv *wil, u8 ringid, u8 size, u16 timeout); +int wmi_delba_tx(struct wil6210_priv *wil, u8 ringid, u16 reason); +int wmi_delba_rx(struct wil6210_priv *wil, u8 cidxtid, u16 reason); +int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token, + u16 status, bool amsdu, u16 agg_wsize, u16 timeout); +int wil_addba_rx_request(struct wil6210_priv *wil, u8 cidxtid, + u8 dialog_token, __le16 ba_param_set, + __le16 ba_timeout, __le16 ba_seq_ctrl); +void wil_back_rx_worker(struct work_struct *work); +void wil_back_rx_flush(struct wil6210_priv *wil); +int wil_addba_tx_request(struct wil6210_priv *wil, u8 ringid, u16 wsize); +void wil_back_tx_worker(struct work_struct *work); +void wil_back_tx_flush(struct wil6210_priv *wil); void wil6210_clear_irq(struct wil6210_priv *wil); int wil6210_init_irq(struct wil6210_priv *wil, int irq); void wil6210_fini_irq(struct wil6210_priv *wil, int irq); void wil_mask_irq(struct wil6210_priv *wil); void wil_unmask_irq(struct wil6210_priv *wil); +void wil_configure_interrupt_moderation(struct wil6210_priv *wil); void wil_disable_irq(struct wil6210_priv *wil); void wil_enable_irq(struct wil6210_priv *wil); int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, @@ -592,6 +733,8 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, u8 chan); int wmi_pcp_stop(struct wil6210_priv *wil); void wil6210_disconnect(struct wil6210_priv *wil, const u8 *bssid, u16 reason_code, bool from_event); +void wil_probe_client_flush(struct wil6210_priv *wil); +void wil_probe_client_worker(struct work_struct *work); int wil_rx_init(struct wil6210_priv *wil, u16 size); void wil_rx_fini(struct wil6210_priv *wil); diff --git a/drivers/net/wireless/ath/wil6210/wil_platform.c b/drivers/net/wireless/ath/wil6210/wil_platform.c index 8f1d78f8a74d..976a071ba74e 100644 --- a/drivers/net/wireless/ath/wil6210/wil_platform.c +++ b/drivers/net/wireless/ath/wil6210/wil_platform.c @@ -17,10 +17,6 @@ #include "linux/device.h" #include "wil_platform.h" -#ifdef CONFIG_WIL6210_PLATFORM_MSM -#include "wil_platform_msm.h" -#endif - /** * wil_platform_init() - wil6210 platform module init * @@ -37,13 +33,7 @@ void *wil_platform_init(struct device *dev, struct wil_platform_ops *ops) return NULL; } -#ifdef CONFIG_WIL6210_PLATFORM_MSM - handle = wil_platform_msm_init(dev, ops); - if (handle) - return handle; -#endif - - /* other platform specific init functions should be called here */ + /* platform specific init functions should be called here */ return handle; } diff --git a/drivers/net/wireless/ath/wil6210/wil_platform_msm.c b/drivers/net/wireless/ath/wil6210/wil_platform_msm.c deleted file mode 100644 index b354a743240d..000000000000 --- a/drivers/net/wireless/ath/wil6210/wil_platform_msm.c +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Copyright (c) 2014 Qualcomm Atheros, Inc. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <linux/of.h> -#include <linux/slab.h> -#include <linux/msm-bus.h> - -#include "wil_platform.h" -#include "wil_platform_msm.h" - -/** - * struct wil_platform_msm - wil6210 msm platform module info - * - * @dev: device object - * @msm_bus_handle: handle for using msm_bus API - * @pdata: bus scale info retrieved from DT - */ -struct wil_platform_msm { - struct device *dev; - uint32_t msm_bus_handle; - struct msm_bus_scale_pdata *pdata; -}; - -#define KBTOB(a) (a * 1000ULL) - -/** - * wil_platform_get_pdata() - Generate bus client data from device tree - * provided by clients. - * - * dev: device object - * of_node: Device tree node to extract information from - * - * The function returns a valid pointer to the allocated bus-scale-pdata - * if the vectors were correctly read from the client's device node. - * Any error in reading or parsing the device node will return NULL - * to the caller. - */ -static struct msm_bus_scale_pdata *wil_platform_get_pdata( - struct device *dev, - struct device_node *of_node) -{ - struct msm_bus_scale_pdata *pdata; - struct msm_bus_paths *usecase; - int i, j, ret, len; - unsigned int num_usecases, num_paths, mem_size; - const uint32_t *vec_arr; - struct msm_bus_vectors *vectors; - - /* first read num_usecases and num_paths so we can calculate - * amount of memory to allocate - */ - ret = of_property_read_u32(of_node, "qcom,msm-bus,num-cases", - &num_usecases); - if (ret) { - dev_err(dev, "Error: num-usecases not found\n"); - return NULL; - } - - ret = of_property_read_u32(of_node, "qcom,msm-bus,num-paths", - &num_paths); - if (ret) { - dev_err(dev, "Error: num_paths not found\n"); - return NULL; - } - - /* pdata memory layout: - * msm_bus_scale_pdata - * msm_bus_paths[num_usecases] - * msm_bus_vectors[num_usecases][num_paths] - */ - mem_size = sizeof(struct msm_bus_scale_pdata) + - sizeof(struct msm_bus_paths) * num_usecases + - sizeof(struct msm_bus_vectors) * num_usecases * num_paths; - - pdata = kzalloc(mem_size, GFP_KERNEL); - if (!pdata) - return NULL; - - ret = of_property_read_string(of_node, "qcom,msm-bus,name", - &pdata->name); - if (ret) { - dev_err(dev, "Error: Client name not found\n"); - goto err; - } - - if (of_property_read_bool(of_node, "qcom,msm-bus,active-only")) { - pdata->active_only = 1; - } else { - dev_info(dev, "active_only flag absent.\n"); - dev_info(dev, "Using dual context by default\n"); - } - - pdata->num_usecases = num_usecases; - pdata->usecase = (struct msm_bus_paths *)(pdata + 1); - - vec_arr = of_get_property(of_node, "qcom,msm-bus,vectors-KBps", &len); - if (vec_arr == NULL) { - dev_err(dev, "Error: Vector array not found\n"); - goto err; - } - - if (len != num_usecases * num_paths * sizeof(uint32_t) * 4) { - dev_err(dev, "Error: Length-error on getting vectors\n"); - goto err; - } - - vectors = (struct msm_bus_vectors *)(pdata->usecase + num_usecases); - for (i = 0; i < num_usecases; i++) { - usecase = &pdata->usecase[i]; - usecase->num_paths = num_paths; - usecase->vectors = &vectors[i]; - - for (j = 0; j < num_paths; j++) { - int index = ((i * num_paths) + j) * 4; - - usecase->vectors[j].src = be32_to_cpu(vec_arr[index]); - usecase->vectors[j].dst = - be32_to_cpu(vec_arr[index + 1]); - usecase->vectors[j].ab = (uint64_t) - KBTOB(be32_to_cpu(vec_arr[index + 2])); - usecase->vectors[j].ib = (uint64_t) - KBTOB(be32_to_cpu(vec_arr[index + 3])); - } - } - - return pdata; - -err: - kfree(pdata); - - return NULL; -} - -/* wil_platform API (callbacks) */ - -static int wil_platform_bus_request(void *handle, - uint32_t kbps /* KBytes/Sec */) -{ - int rc, i; - struct wil_platform_msm *msm = (struct wil_platform_msm *)handle; - int vote = 0; /* vote 0 in case requested kbps cannot be satisfied */ - struct msm_bus_paths *usecase; - uint32_t usecase_kbps; - uint32_t min_kbps = ~0; - - /* find the lowest usecase that is bigger than requested kbps */ - for (i = 0; i < msm->pdata->num_usecases; i++) { - usecase = &msm->pdata->usecase[i]; - /* assume we have single path (vectors[0]). If we ever - * have multiple paths, need to define the behavior */ - usecase_kbps = div64_u64(usecase->vectors[0].ib, 1000); - if (usecase_kbps >= kbps && usecase_kbps < min_kbps) { - min_kbps = usecase_kbps; - vote = i; - } - } - - rc = msm_bus_scale_client_update_request(msm->msm_bus_handle, vote); - if (rc) - dev_err(msm->dev, "Failed msm_bus voting. kbps=%d vote=%d, rc=%d\n", - kbps, vote, rc); - else - /* TOOD: remove */ - dev_info(msm->dev, "msm_bus_scale_client_update_request succeeded. kbps=%d vote=%d\n", - kbps, vote); - - return rc; -} - -static void wil_platform_uninit(void *handle) -{ - struct wil_platform_msm *msm = (struct wil_platform_msm *)handle; - - dev_info(msm->dev, "wil_platform_uninit\n"); - - if (msm->msm_bus_handle) - msm_bus_scale_unregister_client(msm->msm_bus_handle); - - kfree(msm->pdata); - kfree(msm); -} - -static int wil_platform_msm_bus_register(struct wil_platform_msm *msm, - struct device_node *node) -{ - msm->pdata = wil_platform_get_pdata(msm->dev, node); - if (!msm->pdata) { - dev_err(msm->dev, "Failed getting DT info\n"); - return -EINVAL; - } - - msm->msm_bus_handle = msm_bus_scale_register_client(msm->pdata); - if (!msm->msm_bus_handle) { - dev_err(msm->dev, "Failed msm_bus registration\n"); - return -EINVAL; - } - - dev_info(msm->dev, "msm_bus registration succeeded! handle 0x%x\n", - msm->msm_bus_handle); - - return 0; -} - -/** - * wil_platform_msm_init() - wil6210 msm platform module init - * - * The function must be called before all other functions in this module. - * It returns a handle which is used with the rest of the API - * - */ -void *wil_platform_msm_init(struct device *dev, struct wil_platform_ops *ops) -{ - struct device_node *of_node; - struct wil_platform_msm *msm; - int rc; - - of_node = of_find_compatible_node(NULL, NULL, "qcom,wil6210"); - if (!of_node) { - /* this could mean non-msm platform */ - dev_err(dev, "DT node not found\n"); - return NULL; - } - - msm = kzalloc(sizeof(*msm), GFP_KERNEL); - if (!msm) - return NULL; - - msm->dev = dev; - - /* register with msm_bus module for scaling requests */ - rc = wil_platform_msm_bus_register(msm, of_node); - if (rc) - goto cleanup; - - memset(ops, 0, sizeof(*ops)); - ops->bus_request = wil_platform_bus_request; - ops->uninit = wil_platform_uninit; - - return (void *)msm; - -cleanup: - kfree(msm); - return NULL; -} diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 63476c86cd0e..0f3e4334c8e3 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -23,10 +23,15 @@ #include "wmi.h" #include "trace.h" -static uint max_assoc_sta = 1; +static uint max_assoc_sta = WIL6210_MAX_CID; module_param(max_assoc_sta, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(max_assoc_sta, " Max number of stations associated to the AP"); +int agg_wsize; /* = 0; */ +module_param(agg_wsize, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(agg_wsize, " Window size for Tx Block Ack after connect;" + " 0 - use default; < 0 - don't auto-establish"); + /** * WMI event receiving - theory of operations * @@ -197,7 +202,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) might_sleep(); - if (!test_bit(wil_status_fwready, &wil->status)) { + if (!test_bit(wil_status_fwready, wil->status)) { wil_err(wil, "WMI: cannot send command while FW not ready\n"); return -EAGAIN; } @@ -300,7 +305,7 @@ static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d, wil_dbg_wmi(wil, "WMI: got FW ready event\n"); wil_set_recovery_state(wil, fw_recovery_idle); - set_bit(wil_status_fwready, &wil->status); + set_bit(wil_status_fwready, wil->status); /* let the reset sequence continue */ complete(&wil->wmi_ready); } @@ -438,7 +443,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) if ((wdev->iftype == NL80211_IFTYPE_STATION) || (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { - if (!test_bit(wil_status_fwconnecting, &wil->status)) { + if (!test_bit(wil_status_fwconnecting, wil->status)) { wil_err(wil, "Not in connecting state\n"); return; } @@ -457,13 +462,12 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) if (assoc_req_ie) { sinfo.assoc_req_ies = assoc_req_ie; sinfo.assoc_req_ies_len = assoc_req_ielen; - sinfo.filled |= STATION_INFO_ASSOC_REQ_IES; } cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL); } - clear_bit(wil_status_fwconnecting, &wil->status); - set_bit(wil_status_fwconnected, &wil->status); + clear_bit(wil_status_fwconnecting, wil->status); + set_bit(wil_status_fwconnected, wil->status); /* FIXME FW can transmit only ucast frames to peer */ /* FIXME real ring_id instead of hard coded 0 */ @@ -471,7 +475,7 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) wil->sta[evt->cid].status = wil_sta_conn_pending; wil->pending_connect_cid = evt->cid; - queue_work(wil->wmi_wq_conn, &wil->connect_worker); + queue_work(wil->wq_service, &wil->connect_worker); } static void wmi_evt_disconnect(struct wil6210_priv *wil, int id, @@ -544,9 +548,24 @@ static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id, } } +static void wil_addba_tx_cid(struct wil6210_priv *wil, u8 cid, u16 wsize) +{ + struct vring_tx_data *t; + int i; + + for (i = 0; i < WIL6210_MAX_TX_RINGS; i++) { + if (cid != wil->vring2cid_tid[i][0]) + continue; + t = &wil->vring_tx_data[i]; + if (!t->enabled) + continue; + + wil_addba_tx_request(wil, i, wsize); + } +} + static void wmi_evt_linkup(struct wil6210_priv *wil, int id, void *d, int len) { - struct net_device *ndev = wil_to_ndev(wil); struct wmi_data_port_open_event *evt = d; u8 cid = evt->cid; @@ -558,7 +577,8 @@ static void wmi_evt_linkup(struct wil6210_priv *wil, int id, void *d, int len) } wil->sta[cid].data_port_open = true; - netif_carrier_on(ndev); + if (agg_wsize >= 0) + wil_addba_tx_cid(wil, cid, agg_wsize); } static void wmi_evt_linkdown(struct wil6210_priv *wil, int id, void *d, int len) @@ -583,55 +603,89 @@ static void wmi_evt_ba_status(struct wil6210_priv *wil, int id, void *d, int len) { struct wmi_vring_ba_status_event *evt = d; - struct wil_sta_info *sta; - uint i, cid; + struct vring_tx_data *txdata; - /* TODO: use Rx BA status, not Tx one */ - - wil_dbg_wmi(wil, "BACK[%d] %s {%d} timeout %d\n", + wil_dbg_wmi(wil, "BACK[%d] %s {%d} timeout %d AMSDU%s\n", evt->ringid, evt->status == WMI_BA_AGREED ? "OK" : "N/A", - evt->agg_wsize, __le16_to_cpu(evt->ba_timeout)); + evt->agg_wsize, __le16_to_cpu(evt->ba_timeout), + evt->amsdu ? "+" : "-"); if (evt->ringid >= WIL6210_MAX_TX_RINGS) { wil_err(wil, "invalid ring id %d\n", evt->ringid); return; } - mutex_lock(&wil->mutex); - - cid = wil->vring2cid_tid[evt->ringid][0]; - if (cid >= WIL6210_MAX_CID) { - wil_err(wil, "invalid CID %d for vring %d\n", cid, evt->ringid); - goto out; + if (evt->status != WMI_BA_AGREED) { + evt->ba_timeout = 0; + evt->agg_wsize = 0; + evt->amsdu = 0; } - sta = &wil->sta[cid]; - if (sta->status == wil_sta_unused) { - wil_err(wil, "CID %d unused\n", cid); - goto out; - } + txdata = &wil->vring_tx_data[evt->ringid]; - wil_dbg_wmi(wil, "BACK for CID %d %pM\n", cid, sta->addr); - for (i = 0; i < WIL_STA_TID_NUM; i++) { - struct wil_tid_ampdu_rx *r; - unsigned long flags; + txdata->agg_timeout = le16_to_cpu(evt->ba_timeout); + txdata->agg_wsize = evt->agg_wsize; + txdata->agg_amsdu = evt->amsdu; + txdata->addba_in_progress = false; +} - spin_lock_irqsave(&sta->tid_rx_lock, flags); +static void wmi_evt_addba_rx_req(struct wil6210_priv *wil, int id, void *d, + int len) +{ + struct wmi_rcp_addba_req_event *evt = d; - r = sta->tid_rx[i]; - sta->tid_rx[i] = NULL; - wil_tid_ampdu_rx_free(wil, r); + wil_addba_rx_request(wil, evt->cidxtid, evt->dialog_token, + evt->ba_param_set, evt->ba_timeout, + evt->ba_seq_ctrl); +} - spin_unlock_irqrestore(&sta->tid_rx_lock, flags); +static void wmi_evt_delba(struct wil6210_priv *wil, int id, void *d, int len) +__acquires(&sta->tid_rx_lock) __releases(&sta->tid_rx_lock) +{ + struct wmi_delba_event *evt = d; + u8 cid, tid; + u16 reason = __le16_to_cpu(evt->reason); + struct wil_sta_info *sta; + struct wil_tid_ampdu_rx *r; - if ((evt->status == WMI_BA_AGREED) && evt->agg_wsize) - sta->tid_rx[i] = wil_tid_ampdu_rx_alloc(wil, - evt->agg_wsize, 0); + might_sleep(); + parse_cidxtid(evt->cidxtid, &cid, &tid); + wil_dbg_wmi(wil, "DELBA CID %d TID %d from %s reason %d\n", + cid, tid, + evt->from_initiator ? "originator" : "recipient", + reason); + if (!evt->from_initiator) { + int i; + /* find Tx vring it belongs to */ + for (i = 0; i < ARRAY_SIZE(wil->vring2cid_tid); i++) { + if ((wil->vring2cid_tid[i][0] == cid) && + (wil->vring2cid_tid[i][1] == tid)) { + struct vring_tx_data *txdata = + &wil->vring_tx_data[i]; + + wil_dbg_wmi(wil, "DELBA Tx vring %d\n", i); + txdata->agg_timeout = 0; + txdata->agg_wsize = 0; + txdata->addba_in_progress = false; + + break; /* max. 1 matching ring */ + } + } + if (i >= ARRAY_SIZE(wil->vring2cid_tid)) + wil_err(wil, "DELBA: unable to find Tx vring\n"); + return; } -out: - mutex_unlock(&wil->mutex); + sta = &wil->sta[cid]; + + spin_lock_bh(&sta->tid_rx_lock); + + r = sta->tid_rx[tid]; + sta->tid_rx[tid] = NULL; + wil_tid_ampdu_rx_free(wil, r); + + spin_unlock_bh(&sta->tid_rx_lock); } static const struct { @@ -649,6 +703,8 @@ static const struct { {WMI_DATA_PORT_OPEN_EVENTID, wmi_evt_linkup}, {WMI_WBE_LINKDOWN_EVENTID, wmi_evt_linkdown}, {WMI_BA_STATUS_EVENTID, wmi_evt_ba_status}, + {WMI_RCP_ADDBA_REQ_EVENTID, wmi_evt_addba_rx_req}, + {WMI_DELBA_EVENTID, wmi_evt_delba}, }; /* @@ -668,7 +724,7 @@ void wmi_recv_cmd(struct wil6210_priv *wil) ulong flags; unsigned n; - if (!test_bit(wil_status_reset_done, &wil->status)) { + if (!test_bit(wil_status_reset_done, wil->status)) { wil_err(wil, "Reset in progress. Cannot handle WMI event\n"); return; } @@ -1025,13 +1081,14 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring) struct wmi_cfg_rx_chain_cmd cmd = { .action = WMI_RX_CHAIN_ADD, .rx_sw_ring = { - .max_mpdu_size = cpu_to_le16(mtu_max + ETH_HLEN), + .max_mpdu_size = cpu_to_le16(wil_mtu2macbuf(mtu_max)), .ring_mem_base = cpu_to_le64(vring->pa), .ring_size = cpu_to_le16(vring->size), }, .mid = 0, /* TODO - what is it? */ .decap_trans_type = WMI_DECAP_TYPE_802_3, .reorder_type = WMI_RX_SW_REORDER, + .host_thrsh = cpu_to_le16(rx_ring_overflow_thrsh), }; struct { struct wil6210_mbox_hdr_wmi wmi; @@ -1074,12 +1131,13 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring) return rc; } -int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r) +int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_bb, u32 *t_rf) { int rc; struct wmi_temp_sense_cmd cmd = { - .measure_marlon_m_en = cpu_to_le32(!!t_m), - .measure_marlon_r_en = cpu_to_le32(!!t_r), + .measure_baseband_en = cpu_to_le32(!!t_bb), + .measure_rf_en = cpu_to_le32(!!t_rf), + .measure_mode = cpu_to_le32(TEMPERATURE_MEASURE_NOW), }; struct { struct wil6210_mbox_hdr_wmi wmi; @@ -1091,10 +1149,10 @@ int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r) if (rc) return rc; - if (t_m) - *t_m = le32_to_cpu(reply.evt.marlon_m_t1000); - if (t_r) - *t_r = le32_to_cpu(reply.evt.marlon_r_t1000); + if (t_bb) + *t_bb = le32_to_cpu(reply.evt.baseband_t1000); + if (t_rf) + *t_rf = le32_to_cpu(reply.evt.rf_t1000); return 0; } @@ -1111,6 +1169,87 @@ int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason) return wmi_send(wil, WMI_DISCONNECT_STA_CMDID, &cmd, sizeof(cmd)); } +int wmi_addba(struct wil6210_priv *wil, u8 ringid, u8 size, u16 timeout) +{ + struct wmi_vring_ba_en_cmd cmd = { + .ringid = ringid, + .agg_max_wsize = size, + .ba_timeout = cpu_to_le16(timeout), + .amsdu = 0, + }; + + wil_dbg_wmi(wil, "%s(ring %d size %d timeout %d)\n", __func__, + ringid, size, timeout); + + return wmi_send(wil, WMI_VRING_BA_EN_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_delba_tx(struct wil6210_priv *wil, u8 ringid, u16 reason) +{ + struct wmi_vring_ba_dis_cmd cmd = { + .ringid = ringid, + .reason = cpu_to_le16(reason), + }; + + wil_dbg_wmi(wil, "%s(ring %d reason %d)\n", __func__, + ringid, reason); + + return wmi_send(wil, WMI_VRING_BA_DIS_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_delba_rx(struct wil6210_priv *wil, u8 cidxtid, u16 reason) +{ + struct wmi_rcp_delba_cmd cmd = { + .cidxtid = cidxtid, + .reason = cpu_to_le16(reason), + }; + + wil_dbg_wmi(wil, "%s(CID %d TID %d reason %d)\n", __func__, + cidxtid & 0xf, (cidxtid >> 4) & 0xf, reason); + + return wmi_send(wil, WMI_RCP_DELBA_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token, + u16 status, bool amsdu, u16 agg_wsize, u16 timeout) +{ + int rc; + struct wmi_rcp_addba_resp_cmd cmd = { + .cidxtid = mk_cidxtid(cid, tid), + .dialog_token = token, + .status_code = cpu_to_le16(status), + /* bit 0: A-MSDU supported + * bit 1: policy (should be 0 for us) + * bits 2..5: TID + * bits 6..15: buffer size + */ + .ba_param_set = cpu_to_le16((amsdu ? 1 : 0) | (tid << 2) | + (agg_wsize << 6)), + .ba_timeout = cpu_to_le16(timeout), + }; + struct { + struct wil6210_mbox_hdr_wmi wmi; + struct wmi_rcp_addba_resp_sent_event evt; + } __packed reply; + + wil_dbg_wmi(wil, + "ADDBA response for CID %d TID %d size %d timeout %d status %d AMSDU%s\n", + cid, tid, agg_wsize, timeout, status, amsdu ? "+" : "-"); + + rc = wmi_call(wil, WMI_RCP_ADDBA_RESP_CMDID, &cmd, sizeof(cmd), + WMI_ADDBA_RESP_SENT_EVENTID, &reply, sizeof(reply), 100); + if (rc) + return rc; + + if (reply.evt.status) { + wil_err(wil, "ADDBA response failed with status %d\n", + le16_to_cpu(reply.evt.status)); + rc = -EINVAL; + } + + return rc; +} + void wmi_event_flush(struct wil6210_priv *wil) { struct pending_wmi_event *evt, *t; diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h index 27b97432d1c2..8a4af613e191 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.h +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -29,8 +29,10 @@ /* General */ #define WILOCITY_MAX_ASSOC_STA (8) +#define WILOCITY_DEFAULT_ASSOC_STA (1) #define WMI_MAC_LEN (6) #define WMI_PROX_RANGE_NUM (3) +#define WMI_MAX_LOSS_DMG_BEACONS (32) /* List of Commands */ enum wmi_command_id { @@ -48,7 +50,7 @@ enum wmi_command_id { WMI_SET_WSC_STATUS_CMDID = 0x0041, WMI_PXMT_RANGE_CFG_CMDID = 0x0042, WMI_PXMT_SNR2_RANGE_CFG_CMDID = 0x0043, - WMI_FAST_MEM_ACC_MODE_CMDID = 0x0300, +/* WMI_FAST_MEM_ACC_MODE_CMDID = 0x0300, */ WMI_MEM_READ_CMDID = 0x0800, WMI_MEM_WR_CMDID = 0x0801, WMI_ECHO_CMDID = 0x0803, @@ -102,6 +104,8 @@ enum wmi_command_id { WMI_MAINTAIN_RESUME_CMDID = 0x0851, WMI_RS_MGMT_CMDID = 0x0852, WMI_RF_MGMT_CMDID = 0x0853, + WMI_THERMAL_THROTTLING_CTRL_CMDID = 0x0854, + WMI_THERMAL_THROTTLING_GET_STATUS_CMDID = 0x0855, /* Performance monitoring commands */ WMI_BF_CTRL_CMDID = 0x0862, WMI_NOTIFY_REQ_CMDID = 0x0863, @@ -136,6 +140,7 @@ enum wmi_command_id { WMI_EAPOL_TX_CMDID = 0xf04c, WMI_MAC_ADDR_REQ_CMDID = 0xf04d, WMI_FW_VER_CMDID = 0xf04e, + WMI_PMC_CMDID = 0xf04f, }; /* @@ -283,8 +288,8 @@ enum wmi_scan_type { WMI_LONG_SCAN = 0, WMI_SHORT_SCAN = 1, WMI_PBC_SCAN = 2, - WMI_ACTIVE_SCAN = 3, - WMI_DIRECT_SCAN = 4, + WMI_DIRECT_SCAN = 3, + WMI_ACTIVE_SCAN = 4, }; struct wmi_start_scan_cmd { @@ -375,6 +380,17 @@ struct wmi_rf_mgmt_cmd { } __packed; /* + * WMI_THERMAL_THROTTLING_CTRL_CMDID + */ +#define THERMAL_THROTTLING_USE_DEFAULT_MAX_TXOP_LENGTH (0xFFFFFFFF) + +struct wmi_thermal_throttling_ctrl_cmd { + __le32 time_on_usec; + __le32 time_off_usec; + __le32 max_txop_length_usec; +} __packed; + +/* * WMI_RF_RX_TEST_CMDID */ struct wmi_rf_rx_test_cmd { @@ -586,6 +602,7 @@ struct wmi_vring_ba_en_cmd { u8 ringid; u8 agg_max_wsize; __le16 ba_timeout; + u8 amsdu; } __packed; /* @@ -647,6 +664,7 @@ enum wmi_cfg_rx_chain_cmd_action { enum wmi_cfg_rx_chain_cmd_decap_trans_type { WMI_DECAP_TYPE_802_3 = 0, WMI_DECAP_TYPE_NATIVE_WIFI = 1, + WMI_DECAP_TYPE_NONE = 2, }; enum wmi_cfg_rx_chain_cmd_nwifi_ds_trans_type { @@ -784,9 +802,17 @@ struct wmi_echo_cmd { * * Measure MAC and radio temperatures */ + +/* Possible modes for temperature measurement */ +enum wmi_temperature_measure_mode { + TEMPERATURE_USE_OLD_VALUE = 0x1, + TEMPERATURE_MEASURE_NOW = 0x2, +}; + struct wmi_temp_sense_cmd { - __le32 measure_marlon_m_en; - __le32 measure_marlon_r_en; + __le32 measure_baseband_en; + __le32 measure_rf_en; + __le32 measure_mode; } __packed; /* @@ -842,6 +868,7 @@ enum wmi_event_id { WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839, WMI_RS_MGMT_DONE_EVENTID = 0x1852, WMI_RF_MGMT_STATUS_EVENTID = 0x1853, + WMI_THERMAL_THROTTLING_STATUS_EVENTID = 0x1855, WMI_BF_SM_MGMT_DONE_EVENTID = 0x1838, WMI_RX_MGMT_PACKET_EVENTID = 0x1840, WMI_TX_MGMT_PACKET_EVENTID = 0x1841, @@ -858,6 +885,7 @@ enum wmi_event_id { WMI_FLASH_READ_DONE_EVENTID = 0x1902, WMI_FLASH_WRITE_DONE_EVENTID = 0x1903, /*P2P*/ + WMI_P2P_CFG_DONE_EVENTID = 0x1910, WMI_PORT_ALLOCATED_EVENTID = 0x1911, WMI_PORT_DELETED_EVENTID = 0x1912, WMI_LISTEN_STARTED_EVENTID = 0x1914, @@ -898,6 +926,15 @@ struct wmi_rf_mgmt_status_event { } __packed; /* + * WMI_THERMAL_THROTTLING_STATUS_EVENTID + */ +struct wmi_thermal_throttling_status_event { + __le32 time_on_usec; + __le32 time_off_usec; + __le32 max_txop_length_usec; +} __packed; + +/* * WMI_GET_STATUS_DONE_EVENTID */ struct wmi_get_status_done_event { @@ -1052,14 +1089,23 @@ struct wmi_scan_complete_event { enum wmi_vring_ba_status { WMI_BA_AGREED = 0, WMI_BA_NON_AGREED = 1, + /* BA_EN in middle of teardown flow */ + WMI_BA_TD_WIP = 2, + /* BA_DIS or BA_EN in middle of BA SETUP flow */ + WMI_BA_SETUP_WIP = 3, + /* BA_EN when the BA session is already active */ + WMI_BA_SESSION_ACTIVE = 4, + /* BA_DIS when the BA session is not active */ + WMI_BA_SESSION_NOT_ACTIVE = 5, }; struct wmi_vring_ba_status_event { - __le16 status; + __le16 status; /* enum wmi_vring_ba_status */ u8 reserved[2]; u8 ringid; u8 agg_wsize; __le16 ba_timeout; + u8 amsdu; } __packed; /* @@ -1145,6 +1191,14 @@ struct wmi_get_pcp_channel_event { } __packed; /* + * WMI_P2P_CFG_DONE_EVENTID + */ +struct wmi_p2p_cfg_done_event { + u8 status; /* wmi_fw_status */ + u8 reserved[3]; +} __packed; + +/* * WMI_PORT_ALLOCATED_EVENTID */ struct wmi_port_allocated_event { @@ -1272,8 +1326,8 @@ struct wmi_echo_event { * Measure MAC and radio temperatures */ struct wmi_temp_sense_done_event { - __le32 marlon_m_t1000; - __le32 marlon_r_t1000; + __le32 baseband_t1000; + __le32 rf_t1000; } __packed; #endif /* __WILOCITY_WMI_H__ */ diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c index 9183f1cf89a7..55db9f03eb2a 100644 --- a/drivers/net/wireless/atmel.c +++ b/drivers/net/wireless/atmel.c @@ -45,7 +45,6 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/string.h> -#include <linux/ctype.h> #include <linux/timer.h> #include <asm/byteorder.h> #include <asm/io.h> @@ -2699,16 +2698,7 @@ static int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) domain[REGDOMAINSZ] = 0; rc = -EINVAL; for (i = 0; i < ARRAY_SIZE(channel_table); i++) { - /* strcasecmp doesn't exist in the library */ - char *a = channel_table[i].name; - char *b = domain; - while (*a) { - char c1 = *a++; - char c2 = *b++; - if (tolower(c1) != tolower(c2)) - break; - } - if (!*a && !*b) { + if (!strcasecmp(channel_table[i].name, domain)) { priv->config_reg_domain = channel_table[i].reg_domain; rc = 0; } diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig index 64a5b672e30a..759fb8d41fc9 100644 --- a/drivers/net/wireless/b43/Kconfig +++ b/drivers/net/wireless/b43/Kconfig @@ -166,6 +166,15 @@ config B43_PHY_LCN Say N, this is BROKEN and crashes driver. +config B43_PHY_AC + bool "Support for AC-PHY (802.11ac) devices (BROKEN)" + depends on B43 && B43_BCMA && BROKEN + ---help--- + This PHY type can be found in the following chipsets: + PCI: BCM4352, BCM4360 + + Say N, this is BROKEN and crashes driver. + # This config option automatically enables b43 LEDS support, # if it's possible. config B43_LEDS diff --git a/drivers/net/wireless/b43/Makefile b/drivers/net/wireless/b43/Makefile index 9f7965aae93d..c624d4d90e4f 100644 --- a/drivers/net/wireless/b43/Makefile +++ b/drivers/net/wireless/b43/Makefile @@ -13,6 +13,7 @@ b43-$(CONFIG_B43_PHY_HT) += phy_ht.o b43-$(CONFIG_B43_PHY_HT) += tables_phy_ht.o b43-$(CONFIG_B43_PHY_HT) += radio_2059.o b43-$(CONFIG_B43_PHY_LCN) += phy_lcn.o tables_phy_lcn.o +b43-$(CONFIG_B43_PHY_AC) += phy_ac.o b43-y += sysfs.o b43-y += xmit.o b43-y += dma.o diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h index bb12586cd7cd..036552439816 100644 --- a/drivers/net/wireless/b43/b43.h +++ b/drivers/net/wireless/b43/b43.h @@ -500,6 +500,8 @@ enum { #define B43_BCMA_IOCTL_PHY_BW_10MHZ 0x00000000 /* 10 MHz bandwidth, 40 MHz PHY */ #define B43_BCMA_IOCTL_PHY_BW_20MHZ 0x00000040 /* 20 MHz bandwidth, 80 MHz PHY */ #define B43_BCMA_IOCTL_PHY_BW_40MHZ 0x00000080 /* 40 MHz bandwidth, 160 MHz PHY */ +#define B43_BCMA_IOCTL_PHY_BW_80MHZ 0x000000C0 /* 80 MHz bandwidth */ +#define B43_BCMA_IOCTL_DAC 0x00000300 /* Highspeed DAC mode control field */ #define B43_BCMA_IOCTL_GMODE 0x00002000 /* G Mode Enable */ /* BCMA 802.11 core specific IO status (BCMA_IOST) flags */ @@ -941,6 +943,7 @@ struct b43_wl { bool beacon1_uploaded; bool beacon_templates_virgin; /* Never wrote the templates? */ struct work_struct beacon_update_trigger; + spinlock_t beacon_lock; /* The current QOS parameters for the 4 queues. */ struct b43_qos_params qos_params[B43_QOS_QUEUE_NUM]; diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 47731cb0d815..2c9088633ec6 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -1262,6 +1262,23 @@ static void b43_bcma_wireless_core_reset(struct b43_wldev *dev, bool gmode) flags |= B43_BCMA_IOCTL_GMODE; b43_device_enable(dev, flags); + if (dev->phy.type == B43_PHYTYPE_AC) { + u16 tmp; + + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + tmp &= ~B43_BCMA_IOCTL_DAC; + tmp |= 0x100; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + tmp &= ~B43_BCMA_IOCTL_PHY_CLKEN; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + + tmp = bcma_aread32(dev->dev->bdev, BCMA_IOCTL); + tmp |= B43_BCMA_IOCTL_PHY_CLKEN; + bcma_awrite32(dev->dev->bdev, BCMA_IOCTL, tmp); + } + bcma_core_set_clockmode(dev->dev->bdev, BCMA_CLKMODE_FAST); b43_bcma_phy_reset(dev); bcma_core_pll_ctl(dev->dev->bdev, req, status, true); @@ -1601,12 +1618,26 @@ static void b43_write_beacon_template(struct b43_wldev *dev, unsigned int rate; u16 ctl; int antenna; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(dev->wl->current_beacon); + struct ieee80211_tx_info *info; + unsigned long flags; + struct sk_buff *beacon_skb; - bcn = (const struct ieee80211_mgmt *)(dev->wl->current_beacon->data); - len = min_t(size_t, dev->wl->current_beacon->len, - 0x200 - sizeof(struct b43_plcp_hdr6)); + spin_lock_irqsave(&dev->wl->beacon_lock, flags); + info = IEEE80211_SKB_CB(dev->wl->current_beacon); rate = ieee80211_get_tx_rate(dev->wl->hw, info)->hw_value; + /* Clone the beacon, so it cannot go away, while we write it to hw. */ + beacon_skb = skb_clone(dev->wl->current_beacon, GFP_ATOMIC); + spin_unlock_irqrestore(&dev->wl->beacon_lock, flags); + + if (!beacon_skb) { + b43dbg(dev->wl, "Could not upload beacon. " + "Failed to clone beacon skb."); + return; + } + + bcn = (const struct ieee80211_mgmt *)(beacon_skb->data); + len = min_t(size_t, beacon_skb->len, + 0x200 - sizeof(struct b43_plcp_hdr6)); b43_write_template_common(dev, (const u8 *)bcn, len, ram_offset, shm_size_offset, rate); @@ -1674,6 +1705,8 @@ static void b43_write_beacon_template(struct b43_wldev *dev, B43_SHM_SH_DTIMPER, 0); } b43dbg(dev->wl, "Updated beacon template at 0x%x\n", ram_offset); + + dev_kfree_skb_any(beacon_skb); } static void b43_upload_beacon0(struct b43_wldev *dev) @@ -1790,13 +1823,13 @@ static void b43_beacon_update_trigger_work(struct work_struct *work) mutex_unlock(&wl->mutex); } -/* Asynchronously update the packet templates in template RAM. - * Locking: Requires wl->mutex to be locked. */ +/* Asynchronously update the packet templates in template RAM. */ static void b43_update_templates(struct b43_wl *wl) { - struct sk_buff *beacon; + struct sk_buff *beacon, *old_beacon; + unsigned long flags; - /* This is the top half of the ansynchronous beacon update. + /* This is the top half of the asynchronous beacon update. * The bottom half is the beacon IRQ. * Beacon update must be asynchronous to avoid sending an * invalid beacon. This can happen for example, if the firmware @@ -1810,12 +1843,17 @@ static void b43_update_templates(struct b43_wl *wl) if (unlikely(!beacon)) return; - if (wl->current_beacon) - dev_kfree_skb_any(wl->current_beacon); + spin_lock_irqsave(&wl->beacon_lock, flags); + old_beacon = wl->current_beacon; wl->current_beacon = beacon; wl->beacon0_uploaded = false; wl->beacon1_uploaded = false; + spin_unlock_irqrestore(&wl->beacon_lock, flags); + ieee80211_queue_work(wl->hw, &wl->beacon_update_trigger); + + if (old_beacon) + dev_kfree_skb_any(old_beacon); } static void b43_set_beacon_int(struct b43_wldev *dev, u16 beacon_int) @@ -4318,6 +4356,7 @@ redo: mutex_unlock(&wl->mutex); cancel_delayed_work_sync(&dev->periodic_work); cancel_work_sync(&wl->tx_work); + b43_leds_stop(dev); mutex_lock(&wl->mutex); dev = wl->current_dev; if (!dev || b43_status(dev) < B43_STAT_STARTED) { @@ -4505,6 +4544,12 @@ static int b43_phy_versioning(struct b43_wldev *dev) unsupported = 1; break; #endif +#ifdef CONFIG_B43_PHY_AC + case B43_PHYTYPE_AC: + if (phy_rev > 1) + unsupported = 1; + break; +#endif default: unsupported = 1; } @@ -4601,6 +4646,10 @@ static int b43_phy_versioning(struct b43_wldev *dev) if (radio_id != 0x2064) unsupported = 1; break; + case B43_PHYTYPE_AC: + if (radio_id != 0x2069) + unsupported = 1; + break; default: B43_WARN_ON(1); } @@ -5094,7 +5143,6 @@ static int b43_op_beacon_set_tim(struct ieee80211_hw *hw, { struct b43_wl *wl = hw_to_b43_wl(hw); - /* FIXME: add locking */ b43_update_templates(wl); return 0; @@ -5584,6 +5632,7 @@ static struct b43_wl *b43_wireless_init(struct b43_bus_dev *dev) wl->hw = hw; mutex_init(&wl->mutex); spin_lock_init(&wl->hardirq_lock); + spin_lock_init(&wl->beacon_lock); INIT_WORK(&wl->beacon_update_trigger, b43_beacon_update_trigger_work); INIT_WORK(&wl->txpower_adjust_work, b43_phy_txpower_adjust_work); INIT_WORK(&wl->tx_work, b43_tx_work); diff --git a/drivers/net/wireless/b43/phy_ac.c b/drivers/net/wireless/b43/phy_ac.c new file mode 100644 index 000000000000..e75633d67938 --- /dev/null +++ b/drivers/net/wireless/b43/phy_ac.c @@ -0,0 +1,92 @@ +/* + * Broadcom B43 wireless driver + * IEEE 802.11ac AC-PHY support + * + * Copyright (c) 2015 RafaĹ‚ MiĹ‚ecki <zajec5@gmail.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 "b43.h" +#include "phy_ac.h" + +/************************************************** + * Basic PHY ops + **************************************************/ + +static int b43_phy_ac_op_allocate(struct b43_wldev *dev) +{ + struct b43_phy_ac *phy_ac; + + phy_ac = kzalloc(sizeof(*phy_ac), GFP_KERNEL); + if (!phy_ac) + return -ENOMEM; + dev->phy.ac = phy_ac; + + return 0; +} + +static void b43_phy_ac_op_free(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_ac *phy_ac = phy->ac; + + kfree(phy_ac); + phy->ac = NULL; +} + +static void b43_phy_ac_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, + u16 set) +{ + b43_write16f(dev, B43_MMIO_PHY_CONTROL, reg); + b43_write16(dev, B43_MMIO_PHY_DATA, + (b43_read16(dev, B43_MMIO_PHY_DATA) & mask) | set); +} + +static u16 b43_phy_ac_op_radio_read(struct b43_wldev *dev, u16 reg) +{ + b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg); + return b43_read16(dev, B43_MMIO_RADIO24_DATA); +} + +static void b43_phy_ac_op_radio_write(struct b43_wldev *dev, u16 reg, + u16 value) +{ + b43_write16f(dev, B43_MMIO_RADIO24_CONTROL, reg); + b43_write16(dev, B43_MMIO_RADIO24_DATA, value); +} + +static unsigned int b43_phy_ac_op_get_default_chan(struct b43_wldev *dev) +{ + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + return 11; + return 36; +} + +static enum b43_txpwr_result +b43_phy_ac_op_recalc_txpower(struct b43_wldev *dev, bool ignore_tssi) +{ + return B43_TXPWR_RES_DONE; +} + +static void b43_phy_ac_op_adjust_txpower(struct b43_wldev *dev) +{ +} + +/************************************************** + * PHY ops struct + **************************************************/ + +const struct b43_phy_operations b43_phyops_ac = { + .allocate = b43_phy_ac_op_allocate, + .free = b43_phy_ac_op_free, + .phy_maskset = b43_phy_ac_op_maskset, + .radio_read = b43_phy_ac_op_radio_read, + .radio_write = b43_phy_ac_op_radio_write, + .get_default_chan = b43_phy_ac_op_get_default_chan, + .recalc_txpower = b43_phy_ac_op_recalc_txpower, + .adjust_txpower = b43_phy_ac_op_adjust_txpower, +}; diff --git a/drivers/net/wireless/b43/phy_ac.h b/drivers/net/wireless/b43/phy_ac.h new file mode 100644 index 000000000000..d1ca79e0eb24 --- /dev/null +++ b/drivers/net/wireless/b43/phy_ac.h @@ -0,0 +1,38 @@ +#ifndef B43_PHY_AC_H_ +#define B43_PHY_AC_H_ + +#include "phy_common.h" + +#define B43_PHY_AC_BBCFG 0x001 +#define B43_PHY_AC_BBCFG_RSTCCA 0x4000 /* Reset CCA */ +#define B43_PHY_AC_BANDCTL 0x003 /* Band control */ +#define B43_PHY_AC_BANDCTL_5GHZ 0x0001 +#define B43_PHY_AC_TABLE_ID 0x00d +#define B43_PHY_AC_TABLE_OFFSET 0x00e +#define B43_PHY_AC_TABLE_DATA1 0x00f +#define B43_PHY_AC_TABLE_DATA2 0x010 +#define B43_PHY_AC_TABLE_DATA3 0x011 +#define B43_PHY_AC_CLASSCTL 0x140 /* Classifier control */ +#define B43_PHY_AC_CLASSCTL_CCKEN 0x0001 /* CCK enable */ +#define B43_PHY_AC_CLASSCTL_OFDMEN 0x0002 /* OFDM enable */ +#define B43_PHY_AC_CLASSCTL_WAITEDEN 0x0004 /* Waited enable */ +#define B43_PHY_AC_BW1A 0x371 +#define B43_PHY_AC_BW2 0x372 +#define B43_PHY_AC_BW3 0x373 +#define B43_PHY_AC_BW4 0x374 +#define B43_PHY_AC_BW5 0x375 +#define B43_PHY_AC_BW6 0x376 +#define B43_PHY_AC_RFCTL_CMD 0x408 +#define B43_PHY_AC_C1_CLIP 0x6d4 +#define B43_PHY_AC_C1_CLIP_DIS 0x4000 +#define B43_PHY_AC_C2_CLIP 0x8d4 +#define B43_PHY_AC_C2_CLIP_DIS 0x4000 +#define B43_PHY_AC_C3_CLIP 0xad4 +#define B43_PHY_AC_C3_CLIP_DIS 0x4000 + +struct b43_phy_ac { +}; + +extern const struct b43_phy_operations b43_phyops_ac; + +#endif /* B43_PHY_AC_H_ */ diff --git a/drivers/net/wireless/b43/phy_common.c b/drivers/net/wireless/b43/phy_common.c index ee27b06074e1..ec2b9c577b90 100644 --- a/drivers/net/wireless/b43/phy_common.c +++ b/drivers/net/wireless/b43/phy_common.c @@ -33,6 +33,7 @@ #include "phy_lp.h" #include "phy_ht.h" #include "phy_lcn.h" +#include "phy_ac.h" #include "b43.h" #include "main.h" @@ -70,6 +71,11 @@ int b43_phy_allocate(struct b43_wldev *dev) phy->ops = &b43_phyops_lcn; #endif break; + case B43_PHYTYPE_AC: +#ifdef CONFIG_B43_PHY_AC + phy->ops = &b43_phyops_ac; +#endif + break; } if (B43_WARN_ON(!phy->ops)) return -ENODEV; @@ -572,7 +578,8 @@ void b43_phy_force_clock(struct b43_wldev *dev, bool force) u32 tmp; WARN_ON(dev->phy.type != B43_PHYTYPE_N && - dev->phy.type != B43_PHYTYPE_HT); + dev->phy.type != B43_PHYTYPE_HT && + dev->phy.type != B43_PHYTYPE_AC); switch (dev->dev->bus_type) { #ifdef CONFIG_B43_BCMA diff --git a/drivers/net/wireless/b43/phy_common.h b/drivers/net/wireless/b43/phy_common.h index 3912274f71e3..78d86526799e 100644 --- a/drivers/net/wireless/b43/phy_common.h +++ b/drivers/net/wireless/b43/phy_common.h @@ -222,6 +222,8 @@ struct b43_phy { struct b43_phy_ht *ht; /* LCN-PHY specific information */ struct b43_phy_lcn *lcn; + /* AC-PHY specific information */ + struct b43_phy_ac *ac; }; /* Band support flags. */ diff --git a/drivers/net/wireless/b43legacy/radio.c b/drivers/net/wireless/b43legacy/radio.c index 896177690394..9501420340a9 100644 --- a/drivers/net/wireless/b43legacy/radio.c +++ b/drivers/net/wireless/b43legacy/radio.c @@ -1743,25 +1743,6 @@ u16 freq_r3A_value(u16 frequency) return value; } -void b43legacy_radio_set_tx_iq(struct b43legacy_wldev *dev) -{ - static const u8 data_high[5] = { 0x00, 0x40, 0x80, 0x90, 0xD0 }; - static const u8 data_low[5] = { 0x00, 0x01, 0x05, 0x06, 0x0A }; - u16 tmp = b43legacy_radio_read16(dev, 0x001E); - int i; - int j; - - for (i = 0; i < 5; i++) { - for (j = 0; j < 5; j++) { - if (tmp == (data_high[i] | data_low[j])) { - b43legacy_phy_write(dev, 0x0069, (i - j) << 8 | - 0x00C0); - return; - } - } - } -} - int b43legacy_radio_selectchannel(struct b43legacy_wldev *dev, u8 channel, int synthetic_pu_workaround) diff --git a/drivers/net/wireless/b43legacy/radio.h b/drivers/net/wireless/b43legacy/radio.h index bccb3d7da682..dd2976d1d561 100644 --- a/drivers/net/wireless/b43legacy/radio.h +++ b/drivers/net/wireless/b43legacy/radio.h @@ -92,7 +92,6 @@ void b43legacy_nrssi_hw_write(struct b43legacy_wldev *dev, u16 offset, s16 val); void b43legacy_nrssi_hw_update(struct b43legacy_wldev *dev, u16 val); void b43legacy_nrssi_mem_update(struct b43legacy_wldev *dev); -void b43legacy_radio_set_tx_iq(struct b43legacy_wldev *dev); u16 b43legacy_radio_calibrationvalue(struct b43legacy_wldev *dev); #endif /* B43legacy_RADIO_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 9880dae2a569..7944224e3fc9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -97,25 +97,6 @@ static void brcmf_sdiod_dummy_irqhandler(struct sdio_func *func) { } -static bool brcmf_sdiod_pm_resume_error(struct brcmf_sdio_dev *sdiodev) -{ - bool is_err = false; -#ifdef CONFIG_PM_SLEEP - is_err = atomic_read(&sdiodev->suspend); -#endif - return is_err; -} - -static void brcmf_sdiod_pm_resume_wait(struct brcmf_sdio_dev *sdiodev, - wait_queue_head_t *wq) -{ -#ifdef CONFIG_PM_SLEEP - int retry = 0; - while (atomic_read(&sdiodev->suspend) && retry++ != 30) - wait_event_timeout(*wq, false, HZ/100); -#endif -} - int brcmf_sdiod_intr_register(struct brcmf_sdio_dev *sdiodev) { int ret = 0; @@ -244,10 +225,6 @@ static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn, brcmf_dbg(SDIO, "rw=%d, func=%d, addr=0x%05x, nbytes=%d\n", write, fn, addr, regsz); - brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_word_wait); - if (brcmf_sdiod_pm_resume_error(sdiodev)) - return -EIO; - /* only allow byte access on F0 */ if (WARN_ON(regsz > 1 && !fn)) return -EINVAL; @@ -292,6 +269,12 @@ static int brcmf_sdiod_request_data(struct brcmf_sdio_dev *sdiodev, u8 fn, return ret; } +static void brcmf_sdiod_nomedium_state(struct brcmf_sdio_dev *sdiodev) +{ + sdiodev->state = BRCMF_STATE_NOMEDIUM; + brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_DOWN); +} + static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, u8 regsz, void *data, bool write) { @@ -299,7 +282,7 @@ static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, s32 retry = 0; int ret; - if (sdiodev->bus_if->state == BRCMF_BUS_NOMEDIUM) + if (sdiodev->state == BRCMF_STATE_NOMEDIUM) return -ENOMEDIUM; /* @@ -325,7 +308,7 @@ static int brcmf_sdiod_regrw_helper(struct brcmf_sdio_dev *sdiodev, u32 addr, retry++ < SDIOH_API_ACCESS_RETRY_LIMIT); if (ret == -ENOMEDIUM) - brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_NOMEDIUM); + brcmf_sdiod_nomedium_state(sdiodev); else if (ret != 0) { /* * SleepCSR register access can fail when @@ -348,7 +331,7 @@ brcmf_sdiod_set_sbaddr_window(struct brcmf_sdio_dev *sdiodev, u32 address) int err = 0, i; u8 addr[3]; - if (sdiodev->bus_if->state == BRCMF_BUS_NOMEDIUM) + if (sdiodev->state == BRCMF_STATE_NOMEDIUM) return -ENOMEDIUM; addr[0] = (address >> 8) & SBSDIO_SBADDRLOW_MASK; @@ -462,10 +445,6 @@ static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, unsigned int req_sz; int err; - brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait); - if (brcmf_sdiod_pm_resume_error(sdiodev)) - return -EIO; - /* Single skb use the standard mmc interface */ req_sz = pkt->len + 3; req_sz &= (uint)~3; @@ -481,7 +460,7 @@ static int brcmf_sdiod_buffrw(struct brcmf_sdio_dev *sdiodev, uint fn, err = sdio_readsb(sdiodev->func[fn], ((u8 *)(pkt->data)), addr, req_sz); if (err == -ENOMEDIUM) - brcmf_bus_change_state(sdiodev->bus_if, BRCMF_BUS_NOMEDIUM); + brcmf_sdiod_nomedium_state(sdiodev); return err; } @@ -516,10 +495,6 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, if (!pktlist->qlen) return -EINVAL; - brcmf_sdiod_pm_resume_wait(sdiodev, &sdiodev->request_buffer_wait); - if (brcmf_sdiod_pm_resume_error(sdiodev)) - return -EIO; - target_list = pktlist; /* for host with broken sg support, prepare a page aligned list */ __skb_queue_head_init(&local_list); @@ -620,8 +595,7 @@ static int brcmf_sdiod_sglist_rw(struct brcmf_sdio_dev *sdiodev, uint fn, ret = mmc_cmd.error ? mmc_cmd.error : mmc_dat.error; if (ret == -ENOMEDIUM) { - brcmf_bus_change_state(sdiodev->bus_if, - BRCMF_BUS_NOMEDIUM); + brcmf_sdiod_nomedium_state(sdiodev); break; } else if (ret != 0) { brcmf_err("CMD53 sg block %s failed %d\n", @@ -996,18 +970,20 @@ out: } #define BRCMF_SDIO_DEVICE(dev_id) \ - {SDIO_DEVICE(BRCM_SDIO_VENDOR_ID_BROADCOM, dev_id)} + {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, dev_id)} /* devices we support, null terminated */ static const struct sdio_device_id brcmf_sdmmc_ids[] = { - BRCMF_SDIO_DEVICE(BRCM_SDIO_43143_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_43241_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_4329_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_4330_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_4334_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_43362_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_4335_4339_DEVICE_ID), - BRCMF_SDIO_DEVICE(BRCM_SDIO_4354_DEVICE_ID), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43143), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43241), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4329), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4330), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4334), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43340), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43341), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_43362), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4335_4339), + BRCMF_SDIO_DEVICE(SDIO_DEVICE_ID_BROADCOM_4354), { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); @@ -1074,9 +1050,9 @@ static int brcmf_ops_sdio_probe(struct sdio_func *func, bus_if->wowl_supported = true; #endif + sdiodev->sleeping = false; atomic_set(&sdiodev->suspend, false); - init_waitqueue_head(&sdiodev->request_word_wait); - init_waitqueue_head(&sdiodev->request_buffer_wait); + init_waitqueue_head(&sdiodev->idle_wait); brcmf_dbg(SDIO, "F2 found, calling brcmf_sdiod_probe...\n"); err = brcmf_sdiod_probe(sdiodev); @@ -1138,12 +1114,23 @@ void brcmf_sdio_wowl_config(struct device *dev, bool enabled) #ifdef CONFIG_PM_SLEEP static int brcmf_ops_sdio_suspend(struct device *dev) { - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_bus *bus_if; + struct brcmf_sdio_dev *sdiodev; mmc_pm_flag_t sdio_flags; brcmf_dbg(SDIO, "Enter\n"); + bus_if = dev_get_drvdata(dev); + sdiodev = bus_if->bus_priv.sdio; + + /* wait for watchdog to go idle */ + if (wait_event_timeout(sdiodev->idle_wait, sdiodev->sleeping, + msecs_to_jiffies(3 * BRCMF_WD_POLL_MS)) == 0) { + brcmf_err("bus still active\n"); + return -EBUSY; + } + /* disable watchdog */ + brcmf_sdio_wd_timer(sdiodev->bus, 0); atomic_set(&sdiodev->suspend, true); if (sdiodev->wowl_enabled) { @@ -1155,9 +1142,6 @@ static int brcmf_ops_sdio_suspend(struct device *dev) if (sdio_set_host_pm_flags(sdiodev->func[1], sdio_flags)) brcmf_err("Failed to set pm_flags %x\n", sdio_flags); } - - brcmf_sdio_wd_timer(sdiodev->bus, 0); - return 0; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bus.h b/drivers/net/wireless/brcm80211/brcmfmac/bus.h index ef344e47218a..89e6a4dc105e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/bus.h @@ -33,11 +33,8 @@ /* The level of bus communication with the dongle */ enum brcmf_bus_state { - BRCMF_BUS_UNKNOWN, /* Not determined yet */ - BRCMF_BUS_NOMEDIUM, /* No medium access to dongle */ BRCMF_BUS_DOWN, /* Not ready for frame transfers */ - BRCMF_BUS_LOAD, /* Download access only (CPU reset) */ - BRCMF_BUS_DATA /* Ready for frame transfers */ + BRCMF_BUS_UP /* Ready for frame transfers */ }; /* The level of bus communication with the dongle */ @@ -188,22 +185,6 @@ void brcmf_bus_wowl_config(struct brcmf_bus *bus, bool enabled) bus->ops->wowl_config(bus->dev, enabled); } -static inline bool brcmf_bus_ready(struct brcmf_bus *bus) -{ - return bus->state == BRCMF_BUS_LOAD || bus->state == BRCMF_BUS_DATA; -} - -static inline void brcmf_bus_change_state(struct brcmf_bus *bus, - enum brcmf_bus_state new_state) -{ - /* NOMEDIUM is permanent */ - if (bus->state == BRCMF_BUS_NOMEDIUM) - return; - - brcmf_dbg(TRACE, "%d -> %d\n", bus->state, new_state); - bus->state = new_state; -} - /* * interface functions from common layer */ @@ -226,6 +207,9 @@ void brcmf_txflowblock(struct device *dev, bool state); /* Notify the bus has transferred the tx packet to firmware */ void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success); +/* Configure the "global" bus state used by upper layers */ +void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state); + int brcmf_bus_start(struct device *dev); s32 brcmf_iovar_data_set(struct device *dev, char *name, void *data, u32 len); void brcmf_bus_add_txhdrlen(struct device *dev, uint len); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c index 3aecc5f48719..b59b8c6c42ab 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c @@ -38,6 +38,7 @@ #include "proto.h" #include "vendor.h" #include "bus.h" +#include "common.h" #define BRCMF_SCAN_IE_LEN_MAX 2048 #define BRCMF_PNO_VERSION 2 @@ -452,16 +453,16 @@ static void convert_key_from_CPU(struct brcmf_wsec_key *key, } static int -send_key_to_dongle(struct net_device *ndev, struct brcmf_wsec_key *key) +send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key) { int err; struct brcmf_wsec_key_le key_le; convert_key_from_CPU(key, &key_le); - brcmf_netdev_wait_pend8021x(ndev); + brcmf_netdev_wait_pend8021x(ifp); - err = brcmf_fil_bsscfg_data_set(netdev_priv(ndev), "wsec_key", &key_le, + err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le, sizeof(key_le)); if (err) @@ -1228,7 +1229,25 @@ static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof) memset(prof, 0, sizeof(*prof)); } -static void brcmf_link_down(struct brcmf_cfg80211_vif *vif) +static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e) +{ + u16 reason; + + switch (e->event_code) { + case BRCMF_E_DEAUTH: + case BRCMF_E_DEAUTH_IND: + case BRCMF_E_DISASSOC_IND: + reason = e->reason; + break; + case BRCMF_E_LINK: + default: + reason = 0; + break; + } + return reason; +} + +static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy); s32 err = 0; @@ -1243,7 +1262,8 @@ static void brcmf_link_down(struct brcmf_cfg80211_vif *vif) brcmf_err("WLC_DISASSOC failed (%d)\n", err); } clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state); - cfg80211_disconnected(vif->wdev.netdev, 0, NULL, 0, GFP_KERNEL); + cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0, + GFP_KERNEL); } clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state); @@ -1413,7 +1433,7 @@ brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) if (!check_vif_up(ifp->vif)) return -EIO; - brcmf_link_down(ifp->vif); + brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING); brcmf_dbg(TRACE, "Exit\n"); @@ -1670,7 +1690,7 @@ brcmf_set_sharedkey(struct net_device *ndev, brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n", key.len, key.index, key.algo); brcmf_dbg(CONN, "key \"%s\"\n", key.data); - err = send_key_to_dongle(ndev, &key); + err = send_key_to_dongle(netdev_priv(ndev), &key); if (err) return err; @@ -2052,7 +2072,7 @@ brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev, /* check for key index change */ if (key.len == 0) { /* key delete */ - err = send_key_to_dongle(ndev, &key); + err = send_key_to_dongle(ifp, &key); if (err) brcmf_err("key delete error (%d)\n", err); } else { @@ -2108,7 +2128,7 @@ brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev, brcmf_err("Invalid cipher (0x%x)\n", params->cipher); return -EINVAL; } - err = send_key_to_dongle(ndev, &key); + err = send_key_to_dongle(ifp, &key); if (err) brcmf_err("wsec_key error (%d)\n", err); } @@ -2121,7 +2141,7 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, struct key_params *params) { struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_wsec_key key; + struct brcmf_wsec_key *key; s32 val; s32 wsec; s32 err = 0; @@ -2132,54 +2152,62 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, if (!check_vif_up(ifp->vif)) return -EIO; + if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) { + /* we ignore this key index in this case */ + brcmf_err("invalid key index (%d)\n", key_idx); + return -EINVAL; + } + if (mac_addr && (params->cipher != WLAN_CIPHER_SUITE_WEP40) && (params->cipher != WLAN_CIPHER_SUITE_WEP104)) { brcmf_dbg(TRACE, "Exit"); return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params); } - memset(&key, 0, sizeof(key)); - key.len = (u32) params->key_len; - key.index = (u32) key_idx; + key = &ifp->vif->profile.key[key_idx]; + memset(key, 0, sizeof(*key)); - if (key.len > sizeof(key.data)) { - brcmf_err("Too long key length (%u)\n", key.len); + if (params->key_len > sizeof(key->data)) { + brcmf_err("Too long key length (%u)\n", params->key_len); err = -EINVAL; goto done; } - memcpy(key.data, params->key, key.len); + key->len = params->key_len; + key->index = key_idx; - key.flags = BRCMF_PRIMARY_KEY; + memcpy(key->data, params->key, key->len); + + key->flags = BRCMF_PRIMARY_KEY; switch (params->cipher) { case WLAN_CIPHER_SUITE_WEP40: - key.algo = CRYPTO_ALGO_WEP1; + key->algo = CRYPTO_ALGO_WEP1; val = WEP_ENABLED; brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n"); break; case WLAN_CIPHER_SUITE_WEP104: - key.algo = CRYPTO_ALGO_WEP128; + key->algo = CRYPTO_ALGO_WEP128; val = WEP_ENABLED; brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); break; case WLAN_CIPHER_SUITE_TKIP: if (!brcmf_is_apmode(ifp->vif)) { brcmf_dbg(CONN, "Swapping RX/TX MIC key\n"); - memcpy(keybuf, &key.data[24], sizeof(keybuf)); - memcpy(&key.data[24], &key.data[16], sizeof(keybuf)); - memcpy(&key.data[16], keybuf, sizeof(keybuf)); + memcpy(keybuf, &key->data[24], sizeof(keybuf)); + memcpy(&key->data[24], &key->data[16], sizeof(keybuf)); + memcpy(&key->data[16], keybuf, sizeof(keybuf)); } - key.algo = CRYPTO_ALGO_TKIP; + key->algo = CRYPTO_ALGO_TKIP; val = TKIP_ENABLED; brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n"); break; case WLAN_CIPHER_SUITE_AES_CMAC: - key.algo = CRYPTO_ALGO_AES_CCM; + key->algo = CRYPTO_ALGO_AES_CCM; val = AES_ENABLED; brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n"); break; case WLAN_CIPHER_SUITE_CCMP: - key.algo = CRYPTO_ALGO_AES_CCM; + key->algo = CRYPTO_ALGO_AES_CCM; val = AES_ENABLED; brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n"); break; @@ -2189,7 +2217,7 @@ brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, goto done; } - err = send_key_to_dongle(ndev, &key); + err = send_key_to_dongle(ifp, key); if (err) goto done; @@ -2222,7 +2250,7 @@ brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, if (!check_vif_up(ifp->vif)) return -EIO; - if (key_idx >= DOT11_MAX_DEFAULT_KEYS) { + if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) { /* we ignore this key index in this case */ brcmf_err("invalid key index (%d)\n", key_idx); return -EINVAL; @@ -2237,7 +2265,7 @@ brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, brcmf_dbg(CONN, "key index (%d)\n", key_idx); /* Set the new key/index */ - err = send_key_to_dongle(ndev, &key); + err = send_key_to_dongle(ifp, &key); brcmf_dbg(TRACE, "Exit\n"); return err; @@ -2305,6 +2333,39 @@ brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, return -EOPNOTSUPP; } +static void +brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp) +{ + s32 err; + u8 key_idx; + struct brcmf_wsec_key *key; + s32 wsec; + + for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) { + key = &ifp->vif->profile.key[key_idx]; + if ((key->algo == CRYPTO_ALGO_WEP1) || + (key->algo == CRYPTO_ALGO_WEP128)) + break; + } + if (key_idx == BRCMF_MAX_DEFAULT_KEYS) + return; + + err = send_key_to_dongle(ifp, key); + if (err) { + brcmf_err("Setting WEP key failed (%d)\n", err); + return; + } + err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); + if (err) { + brcmf_err("get wsec error (%d)\n", err); + return; + } + wsec |= WEP_ENABLED; + err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec); + if (err) + brcmf_err("set wsec error (%d)\n", err); +} + static s32 brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, const u8 *mac, struct station_info *sinfo) @@ -2333,10 +2394,10 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, brcmf_err("GET STA INFO failed, %d\n", err); goto done; } - sinfo->filled = STATION_INFO_INACTIVE_TIME; + sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME); sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000; if (le32_to_cpu(sta_info_le.flags) & BRCMF_STA_ASSOC) { - sinfo->filled |= STATION_INFO_CONNECTED_TIME; + sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME); sinfo->connected_time = le32_to_cpu(sta_info_le.in); } brcmf_dbg(TRACE, "STA idle time : %d ms, connected time :%d sec\n", @@ -2354,7 +2415,7 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, brcmf_err("Could not get rate (%d)\n", err); goto done; } else { - sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); sinfo->txrate.legacy = rate * 5; brcmf_dbg(CONN, "Rate %d Mbps\n", rate / 2); } @@ -2369,7 +2430,7 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, goto done; } else { rssi = le32_to_cpu(scb_val.val); - sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); sinfo->signal = rssi; brcmf_dbg(CONN, "RSSI %d dBm\n", rssi); } @@ -2396,7 +2457,7 @@ brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, brcmf_dbg(CONN, "DTIM peroid %d\n", dtim_period); } - sinfo->filled |= STATION_INFO_BSS_PARAM; + sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM); } } else err = -EPERM; @@ -2999,7 +3060,7 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, * disassociate from AP to save power while system is * in suspended state */ - brcmf_link_down(vif); + brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED); /* Make sure WPA_Supplicant receives all the event * generated due to DISASSOC call to the fw to keep * the state fw and WPA_Supplicant state consistent @@ -3695,17 +3756,12 @@ static u32 brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd) { - __le32 iecount_le; - __le32 pktflag_le; - strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1); iebuf[VNDR_IE_CMD_LEN - 1] = '\0'; - iecount_le = cpu_to_le32(1); - memcpy(&iebuf[VNDR_IE_COUNT_OFFSET], &iecount_le, sizeof(iecount_le)); + put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]); - pktflag_le = cpu_to_le32(pktflag); - memcpy(&iebuf[VNDR_IE_PKTFLAG_OFFSET], &pktflag_le, sizeof(pktflag_le)); + put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]); memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len); @@ -3924,6 +3980,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(ndev); const struct brcmf_tlv *ssid_ie; + const struct brcmf_tlv *country_ie; struct brcmf_ssid_le ssid_le; s32 err = -EPERM; const struct brcmf_tlv *rsn_ie; @@ -3933,6 +3990,7 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, struct brcmf_fil_bss_enable_le bss_enable; u16 chanspec; bool mbss; + int is_11d; brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n", settings->chandef.chan->hw_value, @@ -3941,10 +3999,16 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n", settings->ssid, settings->ssid_len, settings->auth_type, settings->inactivity_timeout); - dev_role = ifp->vif->wdev.iftype; mbss = ifp->vif->mbss; + /* store current 11d setting */ + brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, &ifp->vif->is_11d); + country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail, + settings->beacon.tail_len, + WLAN_EID_COUNTRY); + is_11d = country_ie ? 1 : 0; + memset(&ssid_le, 0, sizeof(ssid_le)); if (settings->ssid == NULL || settings->ssid_len == 0) { ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; @@ -4010,6 +4074,14 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, goto exit; } + if (is_11d != ifp->vif->is_11d) { + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY, + is_11d); + if (err < 0) { + brcmf_err("Regulatory Set Error, %d\n", err); + goto exit; + } + } if (settings->beacon_interval) { err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, settings->beacon_interval); @@ -4042,6 +4114,10 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_err("SET INFRA error %d\n", err); goto exit; } + } else if (WARN_ON(is_11d != ifp->vif->is_11d)) { + /* Multiple-BSS should use same 11d configuration */ + err = -EINVAL; + goto exit; } if (dev_role == NL80211_IFTYPE_AP) { if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss)) @@ -4057,6 +4133,10 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, brcmf_err("BRCMF_C_UP error (%d)\n", err); goto exit; } + /* On DOWN the firmware removes the WEP keys, reconfigure + * them if they were set. + */ + brcmf_cfg80211_reconfigure_wep(ifp); memset(&join_params, 0, sizeof(join_params)); /* join parameters starts with ssid */ @@ -4133,6 +4213,11 @@ static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev) brcmf_err("setting INFRA mode failed %d\n", err); if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) brcmf_fil_iovar_int_set(ifp, "mbss", 0); + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY, + ifp->vif->is_11d); + if (err < 0) + brcmf_err("restoring REGULATORY setting failed %d\n", + err); /* Bring device back up so it can be used again */ err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); if (err < 0) @@ -4197,6 +4282,34 @@ brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev, return err; } +static int +brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev, + const u8 *mac, struct station_parameters *params) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + s32 err; + + brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac, + params->sta_flags_mask, params->sta_flags_set); + + /* Ignore all 00 MAC */ + if (is_zero_ether_addr(mac)) + return 0; + + if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))) + return 0; + + if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE, + (void *)mac, ETH_ALEN); + else + err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE, + (void *)mac, ETH_ALEN); + if (err < 0) + brcmf_err("Setting SCB (de-)authorize failed, %d\n", err); + + return err; +} static void brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy, @@ -4471,6 +4584,7 @@ static struct cfg80211_ops wl_cfg80211_ops = { .stop_ap = brcmf_cfg80211_stop_ap, .change_beacon = brcmf_cfg80211_change_beacon, .del_station = brcmf_cfg80211_del_station, + .change_station = brcmf_cfg80211_change_station, .sched_scan_start = brcmf_cfg80211_sched_scan_start, .sched_scan_stop = brcmf_cfg80211_sched_scan_stop, .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register, @@ -4778,7 +4892,6 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg, if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) && (reason == BRCMF_E_STATUS_SUCCESS)) { memset(&sinfo, 0, sizeof(sinfo)); - sinfo.filled = STATION_INFO_ASSOC_REQ_IES; if (!data) { brcmf_err("No IEs present in ASSOC/REASSOC_IND"); return -EINVAL; @@ -4833,7 +4946,7 @@ brcmf_notify_connect_status(struct brcmf_if *ifp, if (!brcmf_is_ibssmode(ifp->vif)) { brcmf_bss_connect_done(cfg, ndev, e, false); } - brcmf_link_down(ifp->vif); + brcmf_link_down(ifp->vif, brcmf_map_fw_linkdown_reason(e)); brcmf_init_prof(ndev_to_prof(ndev)); if (ndev != cfg_to_ndev(cfg)) complete(&cfg->vif_disabled); @@ -5774,7 +5887,7 @@ static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp) * from AP to save power */ if (check_vif_up(ifp->vif)) { - brcmf_link_down(ifp->vif); + brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED); /* Make sure WPA_Supplicant receives all the event generated due to DISASSOC call to the fw to keep @@ -5876,6 +5989,29 @@ int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg, vif_event_equals(event, action), timeout); } +static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *req) +{ + struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct brcmf_fil_country_le ccreq; + int i; + + brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator, + req->alpha2[0], req->alpha2[1]); + + /* ignore non-ISO3166 country codes */ + for (i = 0; i < sizeof(req->alpha2); i++) + if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') { + brcmf_err("not a ISO3166 code\n"); + return; + } + memset(&ccreq, 0, sizeof(ccreq)); + ccreq.rev = cpu_to_le32(-1); + memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2)); + brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq)); +} + static void brcmf_free_wiphy(struct wiphy *wiphy) { kfree(wiphy->iface_combinations); @@ -5952,6 +6088,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, goto priv_out; brcmf_dbg(INFO, "Registering custom regulatory\n"); + wiphy->reg_notifier = brcmf_cfg80211_reg_notifier; wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h index 9e98b8d52757..d9e6d01b2b69 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.h @@ -75,6 +75,8 @@ #define BRCMF_VNDR_IE_P2PAF_SHIFT 12 +#define BRCMF_MAX_DEFAULT_KEYS 4 + /** * enum brcmf_scan_status - scan engine status @@ -125,11 +127,13 @@ struct brcmf_cfg80211_security { * @ssid: ssid of associated/associating ap. * @bssid: bssid of joined/joining ibss. * @sec: security information. + * @key: key information */ struct brcmf_cfg80211_profile { struct brcmf_ssid ssid; u8 bssid[ETH_ALEN]; struct brcmf_cfg80211_security sec; + struct brcmf_wsec_key key[BRCMF_MAX_DEFAULT_KEYS]; }; /** @@ -196,6 +200,7 @@ struct brcmf_cfg80211_vif { struct list_head list; u16 mgmt_rx_reg; bool mbss; + int is_11d; }; /* association inform */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index ddae0b5e56ec..04d2ca0d87d6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -101,14 +101,7 @@ /* ARM Cortex M3 core, ID 0x82a */ #define BCM4329_CORE_ARM_BASE 0x18002000 #define BCM4329_RAMSIZE 0x48000 - /* bcm43143 */ -/* SDIO device core */ -#define BCM43143_CORE_BUS_BASE 0x18002000 -/* internal memory core */ -#define BCM43143_CORE_SOCRAM_BASE 0x18004000 -/* ARM Cortex M3 core, ID 0x82a */ -#define BCM43143_CORE_ARM_BASE 0x18003000 #define BCM43143_RAMSIZE 0x70000 #define CORE_SB(base, field) \ @@ -164,13 +157,6 @@ struct brcmf_core_priv { struct brcmf_chip_priv *chip; }; -/* ARM CR4 core specific control flag bits */ -#define ARMCR4_BCMA_IOCTL_CPUHALT 0x0020 - -/* D11 core specific control flag bits */ -#define D11_BCMA_IOCTL_PHYCLOCKEN 0x0004 -#define D11_BCMA_IOCTL_PHYRESET 0x0008 - struct brcmf_chip_priv { struct brcmf_chip pub; const struct brcmf_buscore_ops *ops; @@ -495,6 +481,7 @@ static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) ci->pub.ramsize = 0x48000; break; case BRCM_CC_4334_CHIP_ID: + case BRCM_CC_43340_CHIP_ID: ci->pub.ramsize = 0x80000; break; case BRCM_CC_4335_CHIP_ID: diff --git a/drivers/net/wireless/brcm80211/brcmfmac/common.c b/drivers/net/wireless/brcm80211/brcmfmac/common.c index 1861a13e8d03..fe54844c75e0 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/common.c @@ -25,6 +25,9 @@ #include "fwil.h" #include "fwil_types.h" #include "tracepoint.h" +#include "common.h" + +const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; #define BRCMF_DEFAULT_BCN_TIMEOUT 3 #define BRCMF_DEFAULT_SCAN_CHANNEL_TIME 40 @@ -38,6 +41,8 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) s8 eventmask[BRCMF_EVENTING_MASK_LEN]; u8 buf[BRCMF_DCMD_SMLEN]; struct brcmf_join_pref_params join_pref_params[2]; + struct brcmf_rev_info_le revinfo; + struct brcmf_rev_info *ri; char *ptr; s32 err; @@ -45,12 +50,37 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp) err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr, sizeof(ifp->mac_addr)); if (err < 0) { - brcmf_err("Retreiving cur_etheraddr failed, %d\n", - err); + brcmf_err("Retreiving cur_etheraddr failed, %d\n", err); goto done; } memcpy(ifp->drvr->mac, ifp->mac_addr, sizeof(ifp->drvr->mac)); + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_REVINFO, + &revinfo, sizeof(revinfo)); + ri = &ifp->drvr->revinfo; + if (err < 0) { + brcmf_err("retrieving revision info failed, %d\n", err); + } else { + ri->vendorid = le32_to_cpu(revinfo.vendorid); + ri->deviceid = le32_to_cpu(revinfo.deviceid); + ri->radiorev = le32_to_cpu(revinfo.radiorev); + ri->chiprev = le32_to_cpu(revinfo.chiprev); + ri->corerev = le32_to_cpu(revinfo.corerev); + ri->boardid = le32_to_cpu(revinfo.boardid); + ri->boardvendor = le32_to_cpu(revinfo.boardvendor); + ri->boardrev = le32_to_cpu(revinfo.boardrev); + ri->driverrev = le32_to_cpu(revinfo.driverrev); + ri->ucoderev = le32_to_cpu(revinfo.ucoderev); + ri->bus = le32_to_cpu(revinfo.bus); + ri->chipnum = le32_to_cpu(revinfo.chipnum); + ri->phytype = le32_to_cpu(revinfo.phytype); + ri->phyrev = le32_to_cpu(revinfo.phyrev); + ri->anarev = le32_to_cpu(revinfo.anarev); + ri->chippkg = le32_to_cpu(revinfo.chippkg); + ri->nvramrev = le32_to_cpu(revinfo.nvramrev); + } + ri->result = err; + /* query for 'ver' to get version info from firmware */ memset(buf, 0, sizeof(buf)); strcpy(buf, "ver"); diff --git a/drivers/net/wireless/ath/wil6210/wil_platform_msm.h b/drivers/net/wireless/brcm80211/brcmfmac/common.h index 2f2229edb498..0d39d80cee28 100644 --- a/drivers/net/wireless/ath/wil6210/wil_platform_msm.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/common.h @@ -1,5 +1,4 @@ -/* - * Copyright (c) 2014 Qualcomm Atheros, Inc. +/* Copyright (c) 2014 Broadcom Corporation * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -7,18 +6,15 @@ * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#ifndef BRCMFMAC_COMMON_H +#define BRCMFMAC_COMMON_H -#ifndef __WIL_PLATFORM__MSM_H__ -#define __WIL_PLATFORM_MSM_H__ +extern const u8 ALLFFMAC[ETH_ALEN]; -#include "wil_platform.h" - -void *wil_platform_msm_init(struct device *dev, struct wil_platform_ops *ops); - -#endif /* __WIL_PLATFORM__MSM_H__ */ +#endif /* BRCMFMAC_COMMON_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/commonring.h b/drivers/net/wireless/brcm80211/brcmfmac/commonring.h index 002336e35764..3d404016a92e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/commonring.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/commonring.h @@ -37,6 +37,8 @@ struct brcmf_commonring { unsigned long flags; bool inited; bool was_full; + + atomic_t outstanding_tx; }; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.c b/drivers/net/wireless/brcm80211/brcmfmac/core.c index effe6d7831d9..2d6e2cc1b12c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c @@ -197,7 +197,7 @@ static netdev_tx_t brcmf_netdev_start_xmit(struct sk_buff *skb, brcmf_dbg(DATA, "Enter, idx=%d\n", ifp->bssidx); /* Can the device send data? */ - if (drvr->bus_if->state != BRCMF_BUS_DATA) { + if (drvr->bus_if->state != BRCMF_BUS_UP) { brcmf_err("xmit rejected state=%d\n", drvr->bus_if->state); netif_stop_queue(ndev); dev_kfree_skb(skb); @@ -601,9 +601,12 @@ static void brcmf_ethtool_get_drvinfo(struct net_device *ndev, { struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = ifp->drvr; + char drev[BRCMU_DOTREV_LEN] = "n/a"; + if (drvr->revinfo.result == 0) + brcmu_dotrev_str(drvr->revinfo.driverrev, drev); strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); - snprintf(info->version, sizeof(info->version), "n/a"); + strlcpy(info->version, drev, sizeof(info->version)); strlcpy(info->fw_version, drvr->fwver, sizeof(info->fw_version)); strlcpy(info->bus_info, dev_name(drvr->bus_if->dev), sizeof(info->bus_info)); @@ -637,7 +640,7 @@ static int brcmf_netdev_open(struct net_device *ndev) brcmf_dbg(TRACE, "Enter, idx=%d\n", ifp->bssidx); /* If bus is not ready, can't continue */ - if (bus_if->state != BRCMF_BUS_DATA) { + if (bus_if->state != BRCMF_BUS_UP) { brcmf_err("failed bus is not ready\n"); return -EAGAIN; } @@ -964,13 +967,20 @@ int brcmf_bus_start(struct device *dev) p2p_ifp = NULL; /* signal bus ready */ - brcmf_bus_change_state(bus_if, BRCMF_BUS_DATA); + brcmf_bus_change_state(bus_if, BRCMF_BUS_UP); /* Bus is ready, do any initialization */ ret = brcmf_c_preinit_dcmds(ifp); if (ret < 0) goto fail; + /* assure we have chipid before feature attach */ + if (!bus_if->chip) { + bus_if->chip = drvr->revinfo.chipnum; + bus_if->chiprev = drvr->revinfo.chiprev; + brcmf_dbg(INFO, "firmware revinfo: chip %x (%d) rev %d\n", + bus_if->chip, bus_if->chip, bus_if->chiprev); + } brcmf_feat_attach(drvr); ret = brcmf_fws_init(drvr); @@ -1093,9 +1103,8 @@ static int brcmf_get_pend_8021x_cnt(struct brcmf_if *ifp) return atomic_read(&ifp->pend_8021x_cnt); } -int brcmf_netdev_wait_pend8021x(struct net_device *ndev) +int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp) { - struct brcmf_if *ifp = netdev_priv(ndev); int err; err = wait_event_timeout(ifp->pend_8021x_wait, @@ -1107,6 +1116,27 @@ int brcmf_netdev_wait_pend8021x(struct net_device *ndev) return !err; } +void brcmf_bus_change_state(struct brcmf_bus *bus, enum brcmf_bus_state state) +{ + struct brcmf_pub *drvr = bus->drvr; + struct net_device *ndev; + int ifidx; + + brcmf_dbg(TRACE, "%d -> %d\n", bus->state, state); + bus->state = state; + + if (state == BRCMF_BUS_UP) { + for (ifidx = 0; ifidx < BRCMF_MAX_IFS; ifidx++) { + if ((drvr->iflist[ifidx]) && + (drvr->iflist[ifidx]->ndev)) { + ndev = drvr->iflist[ifidx]->ndev; + if (netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + } + } + } +} + static void brcmf_driver_register(struct work_struct *work) { #ifdef CONFIG_BRCMFMAC_SDIO diff --git a/drivers/net/wireless/brcm80211/brcmfmac/core.h b/drivers/net/wireless/brcm80211/brcmfmac/core.h index 23f74b139cc8..fd74a9c6e9ac 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/core.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h @@ -29,8 +29,6 @@ /* For supporting multiple interfaces */ #define BRCMF_MAX_IFS 16 -#define DOT11_MAX_DEFAULT_KEYS 4 - /* Small, medium and maximum buffer size for dcmd */ #define BRCMF_DCMD_SMLEN 256 @@ -73,6 +71,35 @@ struct brcmf_proto; /* device communication protocol info */ struct brcmf_cfg80211_dev; /* cfg80211 device info */ struct brcmf_fws_info; /* firmware signalling info */ +/* + * struct brcmf_rev_info + * + * The result field stores the error code of the + * revision info request from firmware. For the + * other fields see struct brcmf_rev_info_le in + * fwil_types.h + */ +struct brcmf_rev_info { + int result; + u32 vendorid; + u32 deviceid; + u32 radiorev; + u32 chiprev; + u32 corerev; + u32 boardid; + u32 boardvendor; + u32 boardrev; + u32 driverrev; + u32 ucoderev; + u32 bus; + u32 chipnum; + u32 phytype; + u32 phyrev; + u32 anarev; + u32 chippkg; + u32 nvramrev; +}; + /* Common structure for module and instance linkage */ struct brcmf_pub { /* Linkage ponters */ @@ -106,6 +133,7 @@ struct brcmf_pub { u32 feat_flags; u32 chip_quirks; + struct brcmf_rev_info revinfo; #ifdef DEBUG struct dentry *dbgfs_dir; #endif @@ -167,7 +195,7 @@ struct brcmf_skb_reorder_data { u8 *reorder; }; -int brcmf_netdev_wait_pend8021x(struct net_device *ndev); +int brcmf_netdev_wait_pend8021x(struct brcmf_if *ifp); /* Return pointer to interface name */ char *brcmf_ifname(struct brcmf_pub *drvr, int idx); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c index 1ff787d1a36b..9cb99152ad17 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c @@ -103,7 +103,11 @@ static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp) c = nvp->fwnv->data[nvp->pos]; if (c == '=') { - st = VALUE; + /* ignore RAW1 by treating as comment */ + if (strncmp(&nvp->fwnv->data[nvp->entry], "RAW1", 4) == 0) + st = COMMENT; + else + st = VALUE; } else if (!is_nvram_char(c)) { brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n", nvp->line, nvp->column); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c index 44f3a84d1999..910fbb561469 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/flowring.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/flowring.c @@ -25,6 +25,7 @@ #include "proto.h" #include "flowring.h" #include "msgbuf.h" +#include "common.h" #define BRCMF_FLOWRING_HIGH 1024 @@ -34,9 +35,6 @@ #define BRCMF_FLOWRING_HASH_AP(da, fifo, ifidx) (da[5] + fifo + ifidx * 16) #define BRCMF_FLOWRING_HASH_STA(fifo, ifidx) (fifo + ifidx * 16) -static const u8 ALLZEROMAC[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; -static const u8 ALLFFMAC[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - static const u8 brcmf_flowring_prio2fifo[] = { 1, 0, @@ -137,7 +135,7 @@ u32 brcmf_flowring_create(struct brcmf_flowring *flow, u8 da[ETH_ALEN], hash = flow->hash; for (i = 0; i < BRCMF_FLOWRING_HASHSIZE; i++) { if ((hash[hash_idx].ifidx == BRCMF_FLOWRING_INVALID_IFIDX) && - (memcmp(hash[hash_idx].mac, ALLZEROMAC, ETH_ALEN) == 0)) { + (is_zero_ether_addr(hash[hash_idx].mac))) { found = true; break; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index 03f2c406a17b..dcfa0bb149ce 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -109,7 +109,7 @@ brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) struct brcmf_pub *drvr = ifp->drvr; s32 err; - if (drvr->bus_if->state != BRCMF_BUS_DATA) { + if (drvr->bus_if->state != BRCMF_BUS_UP) { brcmf_err("bus is down. we have nothing to do.\n"); return -EIO; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h index a30be683f4a1..5434dcf64f7d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.h @@ -43,6 +43,8 @@ #define BRCMF_C_SET_RADIO 38 #define BRCMF_C_GET_PHYTYPE 39 #define BRCMF_C_SET_KEY 45 +#define BRCMF_C_GET_REGULATORY 46 +#define BRCMF_C_SET_REGULATORY 47 #define BRCMF_C_SET_PASSIVE_SCAN 49 #define BRCMF_C_SCAN 50 #define BRCMF_C_SCAN_RESULTS 51 @@ -57,9 +59,12 @@ #define BRCMF_C_SET_COUNTRY 84 #define BRCMF_C_GET_PM 85 #define BRCMF_C_SET_PM 86 +#define BRCMF_C_GET_REVINFO 98 #define BRCMF_C_GET_CURR_RATESET 114 #define BRCMF_C_GET_AP 117 #define BRCMF_C_SET_AP 118 +#define BRCMF_C_SET_SCB_AUTHORIZE 121 +#define BRCMF_C_SET_SCB_DEAUTHORIZE 122 #define BRCMF_C_GET_RSSI 127 #define BRCMF_C_GET_WSEC 133 #define BRCMF_C_SET_WSEC 134 diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h index 50891c02c4c1..374920965108 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil_types.h @@ -112,6 +112,7 @@ #define BRCMF_WOWL_MAXPATTERNS 8 #define BRCMF_WOWL_MAXPATTERNSIZE 128 +#define BRCMF_COUNTRY_BUF_SZ 4 /* join preference types for join_pref iovar */ enum brcmf_join_pref_types { @@ -525,4 +526,58 @@ struct brcmf_mbss_ssid_le { unsigned char SSID[32]; }; +/** + * struct brcmf_fil_country_le - country configuration structure. + * + * @country_abbrev: null-terminated country code used in the country IE. + * @rev: revision specifier for ccode. on set, -1 indicates unspecified. + * @ccode: null-terminated built-in country code. + */ +struct brcmf_fil_country_le { + char country_abbrev[BRCMF_COUNTRY_BUF_SZ]; + __le32 rev; + char ccode[BRCMF_COUNTRY_BUF_SZ]; +}; + +/** + * struct brcmf_rev_info_le - device revision info. + * + * @vendorid: PCI vendor id. + * @deviceid: device id of chip. + * @radiorev: radio revision. + * @chiprev: chip revision. + * @corerev: core revision. + * @boardid: board identifier (usu. PCI sub-device id). + * @boardvendor: board vendor (usu. PCI sub-vendor id). + * @boardrev: board revision. + * @driverrev: driver version. + * @ucoderev: microcode version. + * @bus: bus type. + * @chipnum: chip number. + * @phytype: phy type. + * @phyrev: phy revision. + * @anarev: anacore rev. + * @chippkg: chip package info. + * @nvramrev: nvram revision number. + */ +struct brcmf_rev_info_le { + __le32 vendorid; + __le32 deviceid; + __le32 radiorev; + __le32 chiprev; + __le32 corerev; + __le32 boardid; + __le32 boardvendor; + __le32 boardrev; + __le32 driverrev; + __le32 ucoderev; + __le32 bus; + __le32 chipnum; + __le32 phytype; + __le32 phyrev; + __le32 anarev; + __le32 chippkg; + __le32 nvramrev; +}; + #endif /* FWIL_TYPES_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c index 456944a6a2db..6262612dec45 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c @@ -73,6 +73,8 @@ #define BRCMF_MSGBUF_TX_FLUSH_CNT1 32 #define BRCMF_MSGBUF_TX_FLUSH_CNT2 96 +#define BRCMF_MSGBUF_DELAY_TXWORKER_THRS 64 +#define BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS 32 struct msgbuf_common_hdr { u8 msgtype; @@ -583,7 +585,7 @@ brcmf_msgbuf_flowring_create_worker(struct brcmf_msgbuf *msgbuf, u32 flowid; void *dma_buf; u32 dma_sz; - long long address; + u64 address; int err; flowid = work->flowid; @@ -620,7 +622,7 @@ brcmf_msgbuf_flowring_create_worker(struct brcmf_msgbuf *msgbuf, BRCMF_NROF_H2D_COMMON_MSGRINGS); memcpy(create->sa, work->sa, ETH_ALEN); memcpy(create->da, work->da, ETH_ALEN); - address = (long long)(long)msgbuf->flowring_dma_handle[flowid]; + address = (u64)msgbuf->flowring_dma_handle[flowid]; create->flow_ring_addr.high_addr = cpu_to_le32(address >> 32); create->flow_ring_addr.low_addr = cpu_to_le32(address & 0xffffffff); create->max_items = cpu_to_le16(BRCMF_H2D_TXFLOWRING_MAX_ITEM); @@ -698,7 +700,7 @@ static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid) dma_addr_t physaddr; u32 pktid; struct msgbuf_tx_msghdr *tx_msghdr; - long long address; + u64 address; commonring = msgbuf->flowrings[flowid]; if (!brcmf_commonring_write_available(commonring)) @@ -742,13 +744,14 @@ static void brcmf_msgbuf_txflow(struct brcmf_msgbuf *msgbuf, u8 flowid) tx_msghdr->seg_cnt = 1; memcpy(tx_msghdr->txhdr, skb->data, ETH_HLEN); tx_msghdr->data_len = cpu_to_le16(skb->len - ETH_HLEN); - address = (long long)(long)physaddr; + address = (u64)physaddr; tx_msghdr->data_buf_addr.high_addr = cpu_to_le32(address >> 32); tx_msghdr->data_buf_addr.low_addr = cpu_to_le32(address & 0xffffffff); tx_msghdr->metadata_buf_len = 0; tx_msghdr->metadata_buf_addr.high_addr = 0; tx_msghdr->metadata_buf_addr.low_addr = 0; + atomic_inc(&commonring->outstanding_tx); if (count >= BRCMF_MSGBUF_TX_FLUSH_CNT2) { brcmf_commonring_write_complete(commonring); count = 0; @@ -773,10 +776,16 @@ static void brcmf_msgbuf_txflow_worker(struct work_struct *worker) } -static int brcmf_msgbuf_schedule_txdata(struct brcmf_msgbuf *msgbuf, u32 flowid) +static int brcmf_msgbuf_schedule_txdata(struct brcmf_msgbuf *msgbuf, u32 flowid, + bool force) { + struct brcmf_commonring *commonring; + set_bit(flowid, msgbuf->flow_map); - queue_work(msgbuf->txflow_wq, &msgbuf->txflow_work); + commonring = msgbuf->flowrings[flowid]; + if ((force) || (atomic_read(&commonring->outstanding_tx) < + BRCMF_MSGBUF_DELAY_TXWORKER_THRS)) + queue_work(msgbuf->txflow_wq, &msgbuf->txflow_work); return 0; } @@ -797,7 +806,7 @@ static int brcmf_msgbuf_txdata(struct brcmf_pub *drvr, int ifidx, return -ENOMEM; } brcmf_flowring_enqueue(flow, flowid, skb); - brcmf_msgbuf_schedule_txdata(msgbuf, flowid); + brcmf_msgbuf_schedule_txdata(msgbuf, flowid, false); return 0; } @@ -854,6 +863,7 @@ brcmf_msgbuf_process_ioctl_complete(struct brcmf_msgbuf *msgbuf, void *buf) static void brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf) { + struct brcmf_commonring *commonring; struct msgbuf_tx_status *tx_status; u32 idx; struct sk_buff *skb; @@ -871,6 +881,8 @@ brcmf_msgbuf_process_txstatus(struct brcmf_msgbuf *msgbuf, void *buf) } set_bit(flowid, msgbuf->txstatus_done_map); + commonring = msgbuf->flowrings[flowid]; + atomic_dec(&commonring->outstanding_tx); brcmf_txfinalize(msgbuf->drvr, skb, tx_status->msg.ifidx, true); } @@ -885,7 +897,7 @@ static u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count) u32 pktlen; dma_addr_t physaddr; struct msgbuf_rx_bufpost *rx_bufpost; - long long address; + u64 address; u32 pktid; u32 i; @@ -894,7 +906,7 @@ static u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count) count, &alloced); if (!ret_ptr) { - brcmf_err("Failed to reserve space in commonring\n"); + brcmf_dbg(MSGBUF, "Failed to reserve space in commonring\n"); return 0; } @@ -921,7 +933,7 @@ static u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count) } if (msgbuf->rx_metadata_offset) { - address = (long long)(long)physaddr; + address = (u64)physaddr; rx_bufpost->metadata_buf_len = cpu_to_le16(msgbuf->rx_metadata_offset); rx_bufpost->metadata_buf_addr.high_addr = @@ -936,7 +948,7 @@ static u32 brcmf_msgbuf_rxbuf_data_post(struct brcmf_msgbuf *msgbuf, u32 count) rx_bufpost->msg.msgtype = MSGBUF_TYPE_RXBUF_POST; rx_bufpost->msg.request_id = cpu_to_le32(pktid); - address = (long long)(long)physaddr; + address = (u64)physaddr; rx_bufpost->data_buf_len = cpu_to_le16((u16)pktlen); rx_bufpost->data_buf_addr.high_addr = cpu_to_le32(address >> 32); @@ -992,7 +1004,7 @@ brcmf_msgbuf_rxbuf_ctrl_post(struct brcmf_msgbuf *msgbuf, bool event_buf, u32 pktlen; dma_addr_t physaddr; struct msgbuf_rx_ioctl_resp_or_event *rx_bufpost; - long long address; + u64 address; u32 pktid; u32 i; @@ -1035,7 +1047,7 @@ brcmf_msgbuf_rxbuf_ctrl_post(struct brcmf_msgbuf *msgbuf, bool event_buf, MSGBUF_TYPE_IOCTLRESP_BUF_POST; rx_bufpost->msg.request_id = cpu_to_le32(pktid); - address = (long long)(long)physaddr; + address = (u64)physaddr; rx_bufpost->host_buf_len = cpu_to_le16((u16)pktlen); rx_bufpost->host_buf_addr.high_addr = cpu_to_le32(address >> 32); @@ -1181,7 +1193,7 @@ brcmf_msgbuf_process_flow_ring_create_response(struct brcmf_msgbuf *msgbuf, brcmf_flowring_open(msgbuf->flow, flowid); - brcmf_msgbuf_schedule_txdata(msgbuf, flowid); + brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true); } @@ -1280,8 +1292,10 @@ int brcmf_proto_msgbuf_rx_trigger(struct device *dev) struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; struct brcmf_msgbuf *msgbuf = (struct brcmf_msgbuf *)drvr->proto->pd; + struct brcmf_commonring *commonring; void *buf; u32 flowid; + int qlen; buf = msgbuf->commonrings[BRCMF_D2H_MSGRING_RX_COMPLETE]; brcmf_msgbuf_process_rx(msgbuf, buf); @@ -1293,8 +1307,12 @@ int brcmf_proto_msgbuf_rx_trigger(struct device *dev) for_each_set_bit(flowid, msgbuf->txstatus_done_map, msgbuf->nrof_flowrings) { clear_bit(flowid, msgbuf->txstatus_done_map); - if (brcmf_flowring_qlen(msgbuf->flow, flowid)) - brcmf_msgbuf_schedule_txdata(msgbuf, flowid); + commonring = msgbuf->flowrings[flowid]; + qlen = brcmf_flowring_qlen(msgbuf->flow, flowid); + if ((qlen > BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS) || + ((qlen) && (atomic_read(&commonring->outstanding_tx) < + BRCMF_MSGBUF_TRICKLE_TXWORKER_THRS))) + brcmf_msgbuf_schedule_txdata(msgbuf, flowid, true); } return 0; @@ -1348,7 +1366,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) { struct brcmf_bus_msgbuf *if_msgbuf; struct brcmf_msgbuf *msgbuf; - long long address; + u64 address; u32 count; if_msgbuf = drvr->bus_if->msgbuf; @@ -1379,7 +1397,7 @@ int brcmf_proto_msgbuf_attach(struct brcmf_pub *drvr) GFP_KERNEL); if (!msgbuf->ioctbuf) goto fail; - address = (long long)(long)msgbuf->ioctbuf_handle; + address = (u64)msgbuf->ioctbuf_handle; msgbuf->ioctbuf_phys_hi = address >> 32; msgbuf->ioctbuf_phys_lo = address & 0xffffffff; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c index 905991fdb7b1..61c053a729be 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/pcie.c @@ -959,14 +959,14 @@ brcmf_pcie_init_dmabuffer_for_device(struct brcmf_pciedev_info *devinfo, dma_addr_t *dma_handle) { void *ring; - long long address; + u64 address; ring = dma_alloc_coherent(&devinfo->pdev->dev, size, dma_handle, GFP_KERNEL); if (!ring) return NULL; - address = (long long)(long)*dma_handle; + address = (u64)*dma_handle; brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr, address & 0xffffffff); brcmf_pcie_write_tcm32(devinfo, tcm_dma_phys_addr + 4, address >> 32); @@ -1166,7 +1166,7 @@ brcmf_pcie_release_scratchbuffers(struct brcmf_pciedev_info *devinfo) static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) { - long long address; + u64 address; u32 addr; devinfo->shared.scratch = dma_alloc_coherent(&devinfo->pdev->dev, @@ -1180,7 +1180,7 @@ static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) addr = devinfo->shared.tcm_base_address + BRCMF_SHARED_DMA_SCRATCH_ADDR_OFFSET; - address = (long long)(long)devinfo->shared.scratch_dmahandle; + address = (u64)devinfo->shared.scratch_dmahandle; brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); addr = devinfo->shared.tcm_base_address + @@ -1198,7 +1198,7 @@ static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devinfo) addr = devinfo->shared.tcm_base_address + BRCMF_SHARED_DMA_RINGUPD_ADDR_OFFSET; - address = (long long)(long)devinfo->shared.ringupd_dmahandle; + address = (u64)devinfo->shared.ringupd_dmahandle; brcmf_pcie_write_tcm32(devinfo, addr, address & 0xffffffff); brcmf_pcie_write_tcm32(devinfo, addr + 4, address >> 32); addr = devinfo->shared.tcm_base_address + @@ -1828,7 +1828,7 @@ static int brcmf_pcie_resume(struct pci_dev *pdev) goto cleanup; brcmf_dbg(PCIE, "Hot resume, continue....\n"); brcmf_pcie_select_core(devinfo, BCMA_CORE_PCIE2); - brcmf_bus_change_state(bus, BRCMF_BUS_DATA); + brcmf_bus_change_state(bus, BRCMF_BUS_UP); brcmf_pcie_intr_enable(devinfo); return 0; } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c index 0b0d51a61060..faec35c899ec 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c @@ -44,7 +44,8 @@ #include "chip.h" #include "firmware.h" -#define DCMD_RESP_TIMEOUT 2000 /* In milli second */ +#define DCMD_RESP_TIMEOUT 2000 /* In milli second */ +#define CTL_DONE_TIMEOUT 2000 /* In milli second */ #ifdef DEBUG @@ -495,9 +496,9 @@ struct brcmf_sdio { u8 *ctrl_frame_buf; u16 ctrl_frame_len; bool ctrl_frame_stat; + int ctrl_frame_err; spinlock_t txq_lock; /* protect bus->txq */ - struct semaphore tx_seq_lock; /* protect bus->tx_seq */ wait_queue_head_t ctrl_wait; wait_queue_head_t dcmd_resp_wait; @@ -514,7 +515,6 @@ struct brcmf_sdio { bool txoff; /* Transmit flow-controlled */ struct brcmf_sdio_count sdcnt; bool sr_enabled; /* SaveRestore enabled */ - bool sleeping; /* SDIO bus sleeping */ u8 tx_hdrlen; /* sdio bus header length for tx packet */ bool txglom; /* host tx glomming enable flag */ @@ -608,6 +608,8 @@ static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { #define BCM4330_NVRAM_NAME "brcm/brcmfmac4330-sdio.txt" #define BCM4334_FIRMWARE_NAME "brcm/brcmfmac4334-sdio.bin" #define BCM4334_NVRAM_NAME "brcm/brcmfmac4334-sdio.txt" +#define BCM43340_FIRMWARE_NAME "brcm/brcmfmac43340-sdio.bin" +#define BCM43340_NVRAM_NAME "brcm/brcmfmac43340-sdio.txt" #define BCM4335_FIRMWARE_NAME "brcm/brcmfmac4335-sdio.bin" #define BCM4335_NVRAM_NAME "brcm/brcmfmac4335-sdio.txt" #define BCM43362_FIRMWARE_NAME "brcm/brcmfmac43362-sdio.bin" @@ -629,6 +631,8 @@ MODULE_FIRMWARE(BCM4330_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4330_NVRAM_NAME); MODULE_FIRMWARE(BCM4334_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4334_NVRAM_NAME); +MODULE_FIRMWARE(BCM43340_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43340_NVRAM_NAME); MODULE_FIRMWARE(BCM4335_FIRMWARE_NAME); MODULE_FIRMWARE(BCM4335_NVRAM_NAME); MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME); @@ -660,6 +664,7 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = { { BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) }, { BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) }, { BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) }, + { BRCM_CC_43340_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43340) }, { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, @@ -1008,12 +1013,12 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) brcmf_dbg(SDIO, "Enter: request %s currently %s\n", (sleep ? "SLEEP" : "WAKE"), - (bus->sleeping ? "SLEEP" : "WAKE")); + (bus->sdiodev->sleeping ? "SLEEP" : "WAKE")); /* If SR is enabled control bus state with KSO */ if (bus->sr_enabled) { /* Done if we're already in the requested state */ - if (sleep == bus->sleeping) + if (sleep == bus->sdiodev->sleeping) goto end; /* Going to sleep */ @@ -1045,12 +1050,7 @@ brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) bus->idlecount = 0; err = brcmf_sdio_kso_control(bus, true); } - if (!err) { - /* Change state */ - bus->sleeping = sleep; - brcmf_dbg(SDIO, "new state %s\n", - (sleep ? "SLEEP" : "WAKE")); - } else { + if (err) { brcmf_err("error while changing bus sleep state %d\n", err); goto done; @@ -1065,6 +1065,11 @@ end: } else { brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok); } + bus->sdiodev->sleeping = sleep; + if (sleep) + wake_up(&bus->sdiodev->idle_wait); + brcmf_dbg(SDIO, "new state %s\n", + (sleep ? "SLEEP" : "WAKE")); done: brcmf_dbg(SDIO, "Exit: err=%d\n", err); return err; @@ -1904,7 +1909,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) bus->rxpending = true; for (rd->seq_num = bus->rx_seq, rxleft = maxframes; - !bus->rxskip && rxleft && brcmf_bus_ready(bus->sdiodev->bus_if); + !bus->rxskip && rxleft && bus->sdiodev->state == BRCMF_STATE_DATA; rd->seq_num++, rxleft--) { /* Handle glomming separately */ @@ -2371,8 +2376,6 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) /* Send frames until the limit or some other event */ for (cnt = 0; (cnt < maxframes) && data_ok(bus);) { pkt_num = 1; - if (down_interruptible(&bus->tx_seq_lock)) - return cnt; if (bus->txglom) pkt_num = min_t(u8, bus->tx_max - bus->tx_seq, bus->sdiodev->txglomsz); @@ -2388,13 +2391,10 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) __skb_queue_tail(&pktq, pkt); } spin_unlock_bh(&bus->txq_lock); - if (i == 0) { - up(&bus->tx_seq_lock); + if (i == 0) break; - } ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL); - up(&bus->tx_seq_lock); cnt += i; @@ -2415,7 +2415,7 @@ static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes) } /* Deflow-control stack if needed */ - if ((bus->sdiodev->bus_if->state == BRCMF_BUS_DATA) && + if ((bus->sdiodev->state == BRCMF_STATE_DATA) && bus->txoff && (pktq_len(&bus->txq) < TXLOW)) { bus->txoff = false; brcmf_txflowblock(bus->sdiodev->dev, false); @@ -2503,7 +2503,7 @@ static void brcmf_sdio_bus_stop(struct device *dev) bus->watchdog_tsk = NULL; } - if (bus_if->state == BRCMF_BUS_DOWN) { + if (sdiodev->state != BRCMF_STATE_NOMEDIUM) { sdio_claim_host(sdiodev->func[1]); /* Enable clock for device interrupts */ @@ -2538,8 +2538,7 @@ static void brcmf_sdio_bus_stop(struct device *dev) brcmu_pktq_flush(&bus->txq, true, NULL, NULL); /* Clear any held glomming stuff */ - if (bus->glomd) - brcmu_pkt_buf_free_skb(bus->glomd); + brcmu_pkt_buf_free_skb(bus->glomd); brcmf_sdio_free_glom(bus); /* Clear rx control and wake any waiters */ @@ -2604,6 +2603,21 @@ static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) return ret; } +static int brcmf_sdio_pm_resume_wait(struct brcmf_sdio_dev *sdiodev) +{ +#ifdef CONFIG_PM_SLEEP + int retry; + + /* Wait for possible resume to complete */ + retry = 0; + while ((atomic_read(&sdiodev->suspend)) && (retry++ != 50)) + msleep(20); + if (atomic_read(&sdiodev->suspend)) + return -EIO; +#endif + return 0; +} + static void brcmf_sdio_dpc(struct brcmf_sdio *bus) { u32 newstatus = 0; @@ -2614,6 +2628,9 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) brcmf_dbg(TRACE, "Enter\n"); + if (brcmf_sdio_pm_resume_wait(bus->sdiodev)) + return; + sdio_claim_host(bus->sdiodev->func[1]); /* If waiting for HTAVAIL, check status */ @@ -2720,17 +2737,14 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) brcmf_sdio_clrintr(bus); if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) && - (down_interruptible(&bus->tx_seq_lock) == 0)) { - if (data_ok(bus)) { - sdio_claim_host(bus->sdiodev->func[1]); - err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf, - bus->ctrl_frame_len); - sdio_release_host(bus->sdiodev->func[1]); - - bus->ctrl_frame_stat = false; - brcmf_sdio_wait_event_wakeup(bus); - } - up(&bus->tx_seq_lock); + data_ok(bus)) { + sdio_claim_host(bus->sdiodev->func[1]); + err = brcmf_sdio_tx_ctrlframe(bus, bus->ctrl_frame_buf, + bus->ctrl_frame_len); + sdio_release_host(bus->sdiodev->func[1]); + bus->ctrl_frame_err = err; + bus->ctrl_frame_stat = false; + brcmf_sdio_wait_event_wakeup(bus); } /* Send queued frames (limit 1 if rx may still be pending) */ if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) && @@ -2741,7 +2755,7 @@ static void brcmf_sdio_dpc(struct brcmf_sdio *bus) brcmf_sdio_sendfromq(bus, framecnt); } - if (!brcmf_bus_ready(bus->sdiodev->bus_if) || (err != 0)) { + if ((bus->sdiodev->state != BRCMF_STATE_DATA) || (err != 0)) { brcmf_err("failed backplane access over SDIO, halting operation\n"); atomic_set(&bus->intstatus, 0); } else if (atomic_read(&bus->intstatus) || @@ -2942,43 +2956,30 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; - int ret = -1; + int ret; brcmf_dbg(TRACE, "Enter\n"); - if (down_interruptible(&bus->tx_seq_lock)) - return -EINTR; - - if (!data_ok(bus)) { - brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n", - bus->tx_max, bus->tx_seq); - up(&bus->tx_seq_lock); - /* Send from dpc */ - bus->ctrl_frame_buf = msg; - bus->ctrl_frame_len = msglen; - bus->ctrl_frame_stat = true; - - wait_event_interruptible_timeout(bus->ctrl_wait, - !bus->ctrl_frame_stat, - msecs_to_jiffies(2000)); - - if (!bus->ctrl_frame_stat) { - brcmf_dbg(SDIO, "ctrl_frame_stat == false\n"); - ret = 0; - } else { - brcmf_dbg(SDIO, "ctrl_frame_stat == true\n"); - bus->ctrl_frame_stat = false; - if (down_interruptible(&bus->tx_seq_lock)) - return -EINTR; - ret = -1; - } + /* Send from dpc */ + bus->ctrl_frame_buf = msg; + bus->ctrl_frame_len = msglen; + bus->ctrl_frame_stat = true; + if (atomic_read(&bus->dpc_tskcnt) == 0) { + atomic_inc(&bus->dpc_tskcnt); + queue_work(bus->brcmf_wq, &bus->datawork); } - if (ret == -1) { - sdio_claim_host(bus->sdiodev->func[1]); - brcmf_sdio_bus_sleep(bus, false, false); - ret = brcmf_sdio_tx_ctrlframe(bus, msg, msglen); - sdio_release_host(bus->sdiodev->func[1]); - up(&bus->tx_seq_lock); + + wait_event_interruptible_timeout(bus->ctrl_wait, !bus->ctrl_frame_stat, + msecs_to_jiffies(CTL_DONE_TIMEOUT)); + + if (!bus->ctrl_frame_stat) { + brcmf_dbg(SDIO, "ctrl_frame complete, err=%d\n", + bus->ctrl_frame_err); + ret = bus->ctrl_frame_err; + } else { + brcmf_dbg(SDIO, "ctrl_frame timeout\n"); + bus->ctrl_frame_stat = false; + ret = -ETIMEDOUT; } if (ret) @@ -2986,7 +2987,7 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) else bus->sdcnt.tx_ctlpkts++; - return ret ? -EIO : 0; + return ret; } #ifdef DEBUG @@ -3409,8 +3410,8 @@ static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, goto err; } - /* Allow HT Clock now that the ARM is running. */ - brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_LOAD); + /* Allow full data communication using DPC from now on. */ + bus->sdiodev->state = BRCMF_STATE_DATA; bcmerror = 0; err: @@ -3556,7 +3557,7 @@ void brcmf_sdio_isr(struct brcmf_sdio *bus) return; } - if (!brcmf_bus_ready(bus->sdiodev->bus_if)) { + if (bus->sdiodev->state != BRCMF_STATE_DATA) { brcmf_err("bus is down. we have nothing to do\n"); return; } @@ -3579,10 +3580,6 @@ void brcmf_sdio_isr(struct brcmf_sdio *bus) static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) { -#ifdef DEBUG - struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev); -#endif /* DEBUG */ - brcmf_dbg(TIMER, "Enter\n"); /* Poll period: check device if appropriate. */ @@ -3626,7 +3623,7 @@ static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus) } #ifdef DEBUG /* Poll for console output periodically */ - if (bus_if && bus_if->state == BRCMF_BUS_DATA && + if (bus->sdiodev->state == BRCMF_STATE_DATA && bus->console_interval != 0) { bus->console.count += BRCMF_WD_POLL_MS; if (bus->console.count >= bus->console_interval) { @@ -3811,7 +3808,7 @@ static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr) u32 val, rev; val = brcmf_sdiod_regrl(sdiodev, addr, NULL); - if (sdiodev->func[0]->device == BRCM_SDIO_4335_4339_DEVICE_ID && + if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 && addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) { rev = (val & CID_REV_MASK) >> CID_REV_SHIFT; if (rev >= 2) { @@ -3867,11 +3864,6 @@ brcmf_sdio_probe_attach(struct brcmf_sdio *bus) goto fail; } - /* SDIO register access works so moving - * state from UNKNOWN to DOWN. - */ - brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_DOWN); - bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops); if (IS_ERR(bus->ci)) { brcmf_err("brcmf_chip_attach failed!\n"); @@ -4005,18 +3997,16 @@ static void brcmf_sdio_firmware_callback(struct device *dev, brcmf_dbg(TRACE, "Enter: dev=%s\n", dev_name(dev)); - /* try to download image and nvram to the dongle */ - if (bus_if->state == BRCMF_BUS_DOWN) { - bus->alp_only = true; - err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len); - if (err) - goto fail; - bus->alp_only = false; - } - if (!bus_if->drvr) return; + /* try to download image and nvram to the dongle */ + bus->alp_only = true; + err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len); + if (err) + goto fail; + bus->alp_only = false; + /* Start the watchdog timer */ bus->sdcnt.tickcnt = 0; brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); @@ -4142,7 +4132,6 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) spin_lock_init(&bus->rxctl_lock); spin_lock_init(&bus->txq_lock); - sema_init(&bus->tx_seq_lock, 1); init_waitqueue_head(&bus->ctrl_wait); init_waitqueue_head(&bus->dcmd_resp_wait); @@ -4213,7 +4202,6 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) bus->idleclock = BRCMF_IDLE_ACTIVE; /* SR state */ - bus->sleeping = false; bus->sr_enabled = false; brcmf_sdio_debugfs_create(bus); @@ -4254,7 +4242,7 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus) destroy_workqueue(bus->brcmf_wq); if (bus->ci) { - if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) { + if (bus->sdiodev->state != BRCMF_STATE_NOMEDIUM) { sdio_claim_host(bus->sdiodev->func[1]); brcmf_sdio_clkctl(bus, CLK_AVAIL, false); /* Leave the device in state where it is @@ -4289,7 +4277,7 @@ void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick) } /* don't start the wd until fw is loaded */ - if (bus->sdiodev->bus_if->state != BRCMF_BUS_DATA) + if (bus->sdiodev->state != BRCMF_STATE_DATA) return; if (wdtick) { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h index 8eb42620129c..ec2586a8425c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.h @@ -155,6 +155,13 @@ /* watchdog polling interval in ms */ #define BRCMF_WD_POLL_MS 10 +/* The state of the bus */ +enum brcmf_sdio_state { + BRCMF_STATE_DOWN, /* Device available, still initialising */ + BRCMF_STATE_DATA, /* Ready for data transfers, DPC enabled */ + BRCMF_STATE_NOMEDIUM /* No medium access to dongle possible */ +}; + struct brcmf_sdreg { int func; int offset; @@ -169,8 +176,8 @@ struct brcmf_sdio_dev { u32 sbwad; /* Save backplane window address */ struct brcmf_sdio *bus; atomic_t suspend; /* suspend flag */ - wait_queue_head_t request_word_wait; - wait_queue_head_t request_buffer_wait; + bool sleeping; + wait_queue_head_t idle_wait; struct device *dev; struct brcmf_bus *bus_if; struct brcmfmac_sdio_platform_data *pdata; @@ -187,6 +194,7 @@ struct brcmf_sdio_dev { char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; bool wowl_enabled; + enum brcmf_sdio_state state; }; /* sdio core registers */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 4572defc280f..5df6aa72cc2d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -421,7 +421,7 @@ fail: brcmf_err("fail!\n"); while (!list_empty(q)) { req = list_entry(q->next, struct brcmf_usbreq, list); - if (req && req->urb) + if (req) usb_free_urb(req->urb); list_del(q->next); } @@ -576,7 +576,7 @@ brcmf_usb_state_change(struct brcmf_usbdev_info *devinfo, int state) brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_DOWN); } else if (state == BRCMFMAC_USB_STATE_UP) { brcmf_dbg(USB, "DBUS is up\n"); - brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_DATA); + brcmf_bus_change_state(bcmf_bus, BRCMF_BUS_UP); } else { brcmf_dbg(USB, "DBUS current state=%d\n", state); } @@ -1263,6 +1263,8 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) ret = brcmf_usb_bus_setup(devinfo); if (ret) goto fail; + /* we are done */ + return 0; } bus->chip = bus_pub->devid; bus->chiprev = bus_pub->chiprev; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/debug.c b/drivers/net/wireless/brcm80211/brcmsmac/debug.c index c9a8b9360ab1..7a1fbb2e3a71 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/debug.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/debug.c @@ -78,7 +78,7 @@ int brcms_debugfs_hardware_read(struct seq_file *s, void *data) struct brcms_hardware *hw = drvr->wlc->hw; struct bcma_device *core = hw->d11core; struct bcma_bus *bus = core->bus; - char boardrev[10]; + char boardrev[BRCMU_BOARDREV_LEN]; seq_printf(s, "chipnum 0x%x\n" "chiprev 0x%x\n" diff --git a/drivers/net/wireless/brcm80211/brcmutil/utils.c b/drivers/net/wireless/brcm80211/brcmutil/utils.c index 906e89ddf319..0543607002fd 100644 --- a/drivers/net/wireless/brcm80211/brcmutil/utils.c +++ b/drivers/net/wireless/brcm80211/brcmutil/utils.c @@ -267,15 +267,43 @@ char *brcmu_boardrev_str(u32 brev, char *buf) char c; if (brev < 0x100) { - snprintf(buf, 8, "%d.%d", (brev & 0xf0) >> 4, brev & 0xf); + snprintf(buf, BRCMU_BOARDREV_LEN, "%d.%d", + (brev & 0xf0) >> 4, brev & 0xf); } else { c = (brev & 0xf000) == 0x1000 ? 'P' : 'A'; - snprintf(buf, 8, "%c%03x", c, brev & 0xfff); + snprintf(buf, BRCMU_BOARDREV_LEN, "%c%03x", c, brev & 0xfff); } return buf; } EXPORT_SYMBOL(brcmu_boardrev_str); +char *brcmu_dotrev_str(u32 dotrev, char *buf) +{ + u8 dotval[4]; + + if (!dotrev) { + snprintf(buf, BRCMU_DOTREV_LEN, "unknown"); + return buf; + } + dotval[0] = (dotrev >> 24) & 0xFF; + dotval[1] = (dotrev >> 16) & 0xFF; + dotval[2] = (dotrev >> 8) & 0xFF; + dotval[3] = dotrev & 0xFF; + + if (dotval[3]) + snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d.%d.%d", dotval[0], + dotval[1], dotval[2], dotval[3]); + else if (dotval[2]) + snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d.%d", dotval[0], + dotval[1], dotval[2]); + else + snprintf(buf, BRCMU_DOTREV_LEN, "%d.%d", dotval[0], + dotval[1]); + + return buf; +} +EXPORT_SYMBOL(brcmu_dotrev_str); + #if defined(DEBUG) /* pretty hex print a pkt buffer chain */ void brcmu_prpkt(const char *msg, struct sk_buff *p0) diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index 6996fcc144cf..2124a17d0bfd 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -22,7 +22,6 @@ #define BRCM_USB_VENDOR_ID_BROADCOM 0x0a5c #define BRCM_PCIE_VENDOR_ID_BROADCOM PCI_VENDOR_ID_BROADCOM -#define BRCM_SDIO_VENDOR_ID_BROADCOM SDIO_VENDOR_ID_BROADCOM /* Chipcommon Core Chip IDs */ #define BRCM_CC_43143_CHIP_ID 43143 @@ -34,6 +33,7 @@ #define BRCM_CC_4329_CHIP_ID 0x4329 #define BRCM_CC_4330_CHIP_ID 0x4330 #define BRCM_CC_4334_CHIP_ID 0x4334 +#define BRCM_CC_43340_CHIP_ID 43340 #define BRCM_CC_43362_CHIP_ID 43362 #define BRCM_CC_4335_CHIP_ID 0x4335 #define BRCM_CC_4339_CHIP_ID 0x4339 @@ -45,16 +45,6 @@ #define BRCM_CC_43570_CHIP_ID 43570 #define BRCM_CC_43602_CHIP_ID 43602 -/* SDIO Device IDs */ -#define BRCM_SDIO_43143_DEVICE_ID BRCM_CC_43143_CHIP_ID -#define BRCM_SDIO_43241_DEVICE_ID BRCM_CC_43241_CHIP_ID -#define BRCM_SDIO_4329_DEVICE_ID BRCM_CC_4329_CHIP_ID -#define BRCM_SDIO_4330_DEVICE_ID BRCM_CC_4330_CHIP_ID -#define BRCM_SDIO_4334_DEVICE_ID BRCM_CC_4334_CHIP_ID -#define BRCM_SDIO_43362_DEVICE_ID BRCM_CC_43362_CHIP_ID -#define BRCM_SDIO_4335_4339_DEVICE_ID BRCM_CC_4335_CHIP_ID -#define BRCM_SDIO_4354_DEVICE_ID BRCM_CC_4354_CHIP_ID - /* USB Device IDs */ #define BRCM_USB_43143_DEVICE_ID 0xbd1e #define BRCM_USB_43236_DEVICE_ID 0xbd17 diff --git a/drivers/net/wireless/brcm80211/include/brcmu_utils.h b/drivers/net/wireless/brcm80211/include/brcmu_utils.h index a043e29f07e2..41969527b459 100644 --- a/drivers/net/wireless/brcm80211/include/brcmu_utils.h +++ b/drivers/net/wireless/brcm80211/include/brcmu_utils.h @@ -218,6 +218,10 @@ void brcmu_dbg_hex_dump(const void *data, size_t size, const char *fmt, ...) } #endif +#define BRCMU_BOARDREV_LEN 8 +#define BRCMU_DOTREV_LEN 16 + char *brcmu_boardrev_str(u32 brev, char *buf); +char *brcmu_dotrev_str(u32 dotrev, char *buf); #endif /* _BRCMU_UTILS_H_ */ diff --git a/drivers/net/wireless/cw1200/fwio.c b/drivers/net/wireless/cw1200/fwio.c index 6f1b9aace8b3..30e7646d04af 100644 --- a/drivers/net/wireless/cw1200/fwio.c +++ b/drivers/net/wireless/cw1200/fwio.c @@ -66,25 +66,31 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) do { \ ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \ if (ret < 0) \ - goto error; \ + goto exit; \ + } while (0) +#define APB_WRITE2(reg, val) \ + do { \ + ret = cw1200_apb_write_32(priv, CW1200_APB(reg), (val)); \ + if (ret < 0) \ + goto free_buffer; \ } while (0) #define APB_READ(reg, val) \ do { \ ret = cw1200_apb_read_32(priv, CW1200_APB(reg), &(val)); \ if (ret < 0) \ - goto error; \ + goto free_buffer; \ } while (0) #define REG_WRITE(reg, val) \ do { \ ret = cw1200_reg_write_32(priv, (reg), (val)); \ if (ret < 0) \ - goto error; \ + goto exit; \ } while (0) #define REG_READ(reg, val) \ do { \ ret = cw1200_reg_read_32(priv, (reg), &(val)); \ if (ret < 0) \ - goto error; \ + goto exit; \ } while (0) switch (priv->hw_revision) { @@ -142,14 +148,14 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) ret = request_firmware(&firmware, fw_path, priv->pdev); if (ret) { pr_err("Can't load firmware file %s.\n", fw_path); - goto error; + goto exit; } buf = kmalloc(DOWNLOAD_BLOCK_SIZE, GFP_KERNEL | GFP_DMA); if (!buf) { pr_err("Can't allocate firmware load buffer.\n"); ret = -ENOMEM; - goto error; + goto firmware_release; } /* Check if the bootloader is ready */ @@ -163,7 +169,7 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) if (val32 != DOWNLOAD_I_AM_HERE) { pr_err("Bootloader is not ready.\n"); ret = -ETIMEDOUT; - goto error; + goto free_buffer; } /* Calculcate number of download blocks */ @@ -171,7 +177,7 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) /* Updating the length in Download Ctrl Area */ val32 = firmware->size; /* Explicit cast from size_t to u32 */ - APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, val32); + APB_WRITE2(DOWNLOAD_IMAGE_SIZE_REG, val32); /* Firmware downloading loop */ for (block = 0; block < num_blocks; block++) { @@ -183,7 +189,7 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) if (val32 != DOWNLOAD_PENDING) { pr_err("Bootloader reported error %d.\n", val32); ret = -EIO; - goto error; + goto free_buffer; } /* loop until put - get <= 24K */ @@ -198,7 +204,7 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) { pr_err("Timeout waiting for FIFO.\n"); ret = -ETIMEDOUT; - goto error; + goto free_buffer; } /* calculate the block size */ @@ -220,12 +226,12 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) if (ret < 0) { pr_err("Can't write firmware block @ %d!\n", put & (DOWNLOAD_FIFO_SIZE - 1)); - goto error; + goto free_buffer; } /* update the put register */ put += block_size; - APB_WRITE(DOWNLOAD_PUT_REG, put); + APB_WRITE2(DOWNLOAD_PUT_REG, put); } /* End of firmware download loop */ /* Wait for the download completion */ @@ -238,19 +244,21 @@ static int cw1200_load_firmware_cw1200(struct cw1200_common *priv) if (val32 != DOWNLOAD_SUCCESS) { pr_err("Wait for download completion failed: 0x%.8X\n", val32); ret = -ETIMEDOUT; - goto error; + goto free_buffer; } else { pr_info("Firmware download completed.\n"); ret = 0; } -error: +free_buffer: kfree(buf); - if (firmware) - release_firmware(firmware); +firmware_release: + release_firmware(firmware); +exit: return ret; #undef APB_WRITE +#undef APB_WRITE2 #undef APB_READ #undef REG_WRITE #undef REG_READ diff --git a/drivers/net/wireless/cw1200/main.c b/drivers/net/wireless/cw1200/main.c index 3e78cc3ccb78..3689dbbd10bd 100644 --- a/drivers/net/wireless/cw1200/main.c +++ b/drivers/net/wireless/cw1200/main.c @@ -282,7 +282,6 @@ static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr, IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | IEEE80211_HW_REPORTS_TX_ACK_STATUS | - IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_AMPDU_AGGREGATION | IEEE80211_HW_TX_AMPDU_SETUP_IN_HW | @@ -374,9 +373,8 @@ static struct ieee80211_hw *cw1200_init_common(const u8 *macaddr, INIT_WORK(&priv->update_filtering_work, cw1200_update_filtering_work); INIT_WORK(&priv->set_beacon_wakeup_period_work, cw1200_set_beacon_wakeup_period_work); - init_timer(&priv->mcast_timeout); - priv->mcast_timeout.data = (unsigned long)priv; - priv->mcast_timeout.function = cw1200_mcast_timeout; + setup_timer(&priv->mcast_timeout, cw1200_mcast_timeout, + (unsigned long)priv); if (cw1200_queue_stats_init(&priv->tx_queue_stats, CW1200_LINK_ID_MAX, diff --git a/drivers/net/wireless/cw1200/pm.c b/drivers/net/wireless/cw1200/pm.c index 6907c8fd4578..d2202ae92bdd 100644 --- a/drivers/net/wireless/cw1200/pm.c +++ b/drivers/net/wireless/cw1200/pm.c @@ -101,9 +101,8 @@ int cw1200_pm_init(struct cw1200_pm_state *pm, { spin_lock_init(&pm->lock); - init_timer(&pm->stay_awake); - pm->stay_awake.data = (unsigned long)pm; - pm->stay_awake.function = cw1200_pm_stay_awake_tmo; + setup_timer(&pm->stay_awake, cw1200_pm_stay_awake_tmo, + (unsigned long)pm); return 0; } diff --git a/drivers/net/wireless/cw1200/queue.c b/drivers/net/wireless/cw1200/queue.c index 9c3925f58d79..0ba5ef9b3e7b 100644 --- a/drivers/net/wireless/cw1200/queue.c +++ b/drivers/net/wireless/cw1200/queue.c @@ -179,9 +179,7 @@ int cw1200_queue_init(struct cw1200_queue *queue, INIT_LIST_HEAD(&queue->pending); INIT_LIST_HEAD(&queue->free_pool); spin_lock_init(&queue->lock); - init_timer(&queue->gc); - queue->gc.data = (unsigned long)queue; - queue->gc.function = cw1200_queue_gc; + setup_timer(&queue->gc, cw1200_queue_gc, (unsigned long)queue); queue->pool = kzalloc(sizeof(struct cw1200_queue_item) * capacity, GFP_KERNEL); diff --git a/drivers/net/wireless/cw1200/scan.c b/drivers/net/wireless/cw1200/scan.c index f2e276faca70..bff81b8d4164 100644 --- a/drivers/net/wireless/cw1200/scan.c +++ b/drivers/net/wireless/cw1200/scan.c @@ -39,9 +39,9 @@ static int cw1200_scan_start(struct cw1200_common *priv, struct wsm_scan *scan) cancel_delayed_work_sync(&priv->clear_recent_scan_work); atomic_set(&priv->scan.in_progress, 1); atomic_set(&priv->recent_scan, 1); - cw1200_pm_stay_awake(&priv->pm_state, tmo * HZ / 1000); + cw1200_pm_stay_awake(&priv->pm_state, msecs_to_jiffies(tmo)); queue_delayed_work(priv->workqueue, &priv->scan.timeout, - tmo * HZ / 1000); + msecs_to_jiffies(tmo)); ret = wsm_scan(priv, scan); if (ret) { atomic_set(&priv->scan.in_progress, 0); @@ -386,8 +386,8 @@ void cw1200_probe_work(struct work_struct *work) if (down_trylock(&priv->scan.lock)) { /* Scan is already in progress. Requeue self. */ schedule(); - queue_delayed_work(priv->workqueue, - &priv->scan.probe_work, HZ / 10); + queue_delayed_work(priv->workqueue, &priv->scan.probe_work, + msecs_to_jiffies(100)); mutex_unlock(&priv->conf_mutex); return; } diff --git a/drivers/net/wireless/cw1200/sta.c b/drivers/net/wireless/cw1200/sta.c index 5b84664db13b..4a47c7f8a246 100644 --- a/drivers/net/wireless/cw1200/sta.c +++ b/drivers/net/wireless/cw1200/sta.c @@ -213,6 +213,7 @@ int cw1200_add_interface(struct ieee80211_hw *dev, /* __le32 auto_calibration_mode = __cpu_to_le32(1); */ vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_UAPSD | IEEE80211_VIF_SUPPORTS_CQM_RSSI; mutex_lock(&priv->conf_mutex); @@ -708,7 +709,8 @@ int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, if (sta) peer_addr = sta->addr; - key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; + key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE | + IEEE80211_KEY_FLAG_RESERVE_TAILROOM; switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c index 596525528f50..fd8d83dd4f62 100644 --- a/drivers/net/wireless/hostap/hostap_ap.c +++ b/drivers/net/wireless/hostap/hostap_ap.c @@ -145,7 +145,7 @@ static void ap_free_sta(struct ap_data *ap, struct sta_info *sta) if (sta->aid > 0) ap->sta_aid[sta->aid - 1] = NULL; - if (!sta->ap && sta->u.sta.challenge) + if (!sta->ap) kfree(sta->u.sta.challenge); del_timer_sync(&sta->timer); #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/iwlegacy/3945-mac.c index dc1d20cf64ee..e5665804d986 100644 --- a/drivers/net/wireless/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/iwlegacy/3945-mac.c @@ -3429,9 +3429,7 @@ il3945_setup_deferred_work(struct il_priv *il) il3945_hw_setup_deferred_work(il); - init_timer(&il->watchdog); - il->watchdog.data = (unsigned long)il; - il->watchdog.function = il_bg_watchdog; + setup_timer(&il->watchdog, il_bg_watchdog, (unsigned long)il); tasklet_init(&il->irq_tasklet, (void (*)(unsigned long))il3945_irq_tasklet, diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index 2748fde4b90c..976f65fe9c38 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -6247,13 +6247,10 @@ il4965_setup_deferred_work(struct il_priv *il) INIT_WORK(&il->txpower_work, il4965_bg_txpower_work); - init_timer(&il->stats_periodic); - il->stats_periodic.data = (unsigned long)il; - il->stats_periodic.function = il4965_bg_stats_periodic; + setup_timer(&il->stats_periodic, il4965_bg_stats_periodic, + (unsigned long)il); - init_timer(&il->watchdog); - il->watchdog.data = (unsigned long)il; - il->watchdog.function = il_bg_watchdog; + setup_timer(&il->watchdog, il_bg_watchdog, (unsigned long)il); tasklet_init(&il->irq_tasklet, (void (*)(unsigned long))il4965_irq_tasklet, diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 0b7f46f0b079..c4d6dd7402d9 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -64,22 +64,8 @@ * ******************************************************************************/ -/* - * module name, copyright, version, etc. - */ #define DRV_DESCRIPTION "Intel(R) Wireless WiFi Link AGN driver for Linux" - -#ifdef CONFIG_IWLWIFI_DEBUG -#define VD "d" -#else -#define VD -#endif - -#define DRV_VERSION IWLWIFI_VERSION VD - - MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); MODULE_LICENSE("GPL"); @@ -1011,13 +997,11 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv) if (priv->lib->bt_params) iwlagn_bt_setup_deferred_work(priv); - init_timer(&priv->statistics_periodic); - priv->statistics_periodic.data = (unsigned long)priv; - priv->statistics_periodic.function = iwl_bg_statistics_periodic; + setup_timer(&priv->statistics_periodic, iwl_bg_statistics_periodic, + (unsigned long)priv); - init_timer(&priv->ucode_trace); - priv->ucode_trace.data = (unsigned long)priv; - priv->ucode_trace.function = iwl_bg_ucode_trace; + setup_timer(&priv->ucode_trace, iwl_bg_ucode_trace, + (unsigned long)priv); } void iwl_cancel_deferred_work(struct iwl_priv *priv) @@ -1244,11 +1228,8 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, trans_cfg.no_reclaim_cmds = no_reclaim_cmds; trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K; - if (!iwlwifi_mod_params.wd_disable) - trans_cfg.queue_watchdog_timeout = - priv->cfg->base_params->wd_timeout; - else - trans_cfg.queue_watchdog_timeout = IWL_WATCHDOG_DISABLED; + trans_cfg.cmd_q_wdg_timeout = IWL_WATCHDOG_DISABLED; + trans_cfg.command_names = iwl_dvm_cmd_strings; trans_cfg.cmd_fifo = IWLAGN_CMD_FIFO_NUM; diff --git a/drivers/net/wireless/iwlwifi/dvm/tt.c b/drivers/net/wireless/iwlwifi/dvm/tt.c index acb981a0a0aa..c4736c8834c5 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tt.c +++ b/drivers/net/wireless/iwlwifi/dvm/tt.c @@ -612,15 +612,10 @@ void iwl_tt_initialize(struct iwl_priv *priv) memset(tt, 0, sizeof(struct iwl_tt_mgmt)); tt->state = IWL_TI_0; - init_timer(&priv->thermal_throttle.ct_kill_exit_tm); - priv->thermal_throttle.ct_kill_exit_tm.data = (unsigned long)priv; - priv->thermal_throttle.ct_kill_exit_tm.function = - iwl_tt_check_exit_ct_kill; - init_timer(&priv->thermal_throttle.ct_kill_waiting_tm); - priv->thermal_throttle.ct_kill_waiting_tm.data = - (unsigned long)priv; - priv->thermal_throttle.ct_kill_waiting_tm.function = - iwl_tt_ready_for_ct_kill; + setup_timer(&priv->thermal_throttle.ct_kill_exit_tm, + iwl_tt_check_exit_ct_kill, (unsigned long)priv); + setup_timer(&priv->thermal_throttle.ct_kill_waiting_tm, + iwl_tt_ready_for_ct_kill, (unsigned long)priv); /* setup deferred ct kill work */ INIT_WORK(&priv->tt_work, iwl_bg_tt_work); INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter); diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index d1ce3ce13591..1e40a12de077 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -715,7 +715,7 @@ int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif, fifo = ctx->ac_to_fifo[tid_to_ac[tid]]; iwl_trans_txq_enable(priv->trans, q, fifo, sta_priv->sta_id, tid, - buf_size, ssn); + buf_size, ssn, 0); /* * If the limit is 0, then it wasn't initialised yet, diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c index d5cee1530597..4dbef7e58c2e 100644 --- a/drivers/net/wireless/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c @@ -267,7 +267,7 @@ static int iwl_alive_notify(struct iwl_priv *priv) for (i = 0; i < n_queues; i++) if (queue_to_txf[i] != IWL_TX_FIFO_UNUSED) iwl_trans_ac_txq_enable(priv->trans, i, - queue_to_txf[i]); + queue_to_txf[i], 0); priv->passive_no_rx = false; priv->transport_queue_stop = 0; diff --git a/drivers/net/wireless/iwlwifi/iwl-7000.c b/drivers/net/wireless/iwlwifi/iwl-7000.c index e5be2d21868f..97e38d2e2983 100644 --- a/drivers/net/wireless/iwlwifi/iwl-7000.c +++ b/drivers/net/wireless/iwlwifi/iwl-7000.c @@ -69,8 +69,8 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL7260_UCODE_API_MAX 10 -#define IWL3160_UCODE_API_MAX 10 +#define IWL7260_UCODE_API_MAX 12 +#define IWL3160_UCODE_API_MAX 12 /* Oldest version we won't warn about */ #define IWL7260_UCODE_API_OK 10 @@ -92,6 +92,12 @@ #define IWL7265D_NVM_VERSION 0x0c11 #define IWL7265_TX_POWER_VERSION 0xffff /* meaningless */ +/* DCCM offsets and lengths */ +#define IWL7000_DCCM_OFFSET 0x800000 +#define IWL7260_DCCM_LEN 0x14000 +#define IWL3160_DCCM_LEN 0x10000 +#define IWL7265_DCCM_LEN 0x17A00 + #define IWL7260_FW_PRE "iwlwifi-7260-" #define IWL7260_MODULE_FIRMWARE(api) IWL7260_FW_PRE __stringify(api) ".ucode" @@ -105,7 +111,7 @@ #define IWL7265_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode" #define IWL7265D_FW_PRE "iwlwifi-7265D-" -#define IWL7265D_MODULE_FIRMWARE(api) IWL7265_FW_PRE __stringify(api) ".ucode" +#define IWL7265D_MODULE_FIRMWARE(api) IWL7265D_FW_PRE __stringify(api) ".ucode" #define NVM_HW_SECTION_NUM_FAMILY_7000 0 @@ -138,7 +144,8 @@ static const struct iwl_ht_params iwl7000_ht_params = { .led_mode = IWL_LED_RF_STATE, \ .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_7000, \ .non_shared_ant = ANT_A, \ - .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, \ + .dccm_offset = IWL7000_DCCM_OFFSET const struct iwl_cfg iwl7260_2ac_cfg = { .name = "Intel(R) Dual Band Wireless AC 7260", @@ -149,6 +156,7 @@ const struct iwl_cfg iwl7260_2ac_cfg = { .nvm_calib_ver = IWL7260_TX_POWER_VERSION, .host_interrupt_operation_mode = true, .lp_xtal_workaround = true, + .dccm_len = IWL7260_DCCM_LEN, }; const struct iwl_cfg iwl7260_2ac_cfg_high_temp = { @@ -161,6 +169,7 @@ const struct iwl_cfg iwl7260_2ac_cfg_high_temp = { .high_temp = true, .host_interrupt_operation_mode = true, .lp_xtal_workaround = true, + .dccm_len = IWL7260_DCCM_LEN, }; const struct iwl_cfg iwl7260_2n_cfg = { @@ -172,6 +181,7 @@ const struct iwl_cfg iwl7260_2n_cfg = { .nvm_calib_ver = IWL7260_TX_POWER_VERSION, .host_interrupt_operation_mode = true, .lp_xtal_workaround = true, + .dccm_len = IWL7260_DCCM_LEN, }; const struct iwl_cfg iwl7260_n_cfg = { @@ -183,6 +193,7 @@ const struct iwl_cfg iwl7260_n_cfg = { .nvm_calib_ver = IWL7260_TX_POWER_VERSION, .host_interrupt_operation_mode = true, .lp_xtal_workaround = true, + .dccm_len = IWL7260_DCCM_LEN, }; const struct iwl_cfg iwl3160_2ac_cfg = { @@ -193,6 +204,7 @@ const struct iwl_cfg iwl3160_2ac_cfg = { .nvm_ver = IWL3160_NVM_VERSION, .nvm_calib_ver = IWL3160_TX_POWER_VERSION, .host_interrupt_operation_mode = true, + .dccm_len = IWL3160_DCCM_LEN, }; const struct iwl_cfg iwl3160_2n_cfg = { @@ -203,6 +215,7 @@ const struct iwl_cfg iwl3160_2n_cfg = { .nvm_ver = IWL3160_NVM_VERSION, .nvm_calib_ver = IWL3160_TX_POWER_VERSION, .host_interrupt_operation_mode = true, + .dccm_len = IWL3160_DCCM_LEN, }; const struct iwl_cfg iwl3160_n_cfg = { @@ -213,6 +226,7 @@ const struct iwl_cfg iwl3160_n_cfg = { .nvm_ver = IWL3160_NVM_VERSION, .nvm_calib_ver = IWL3160_TX_POWER_VERSION, .host_interrupt_operation_mode = true, + .dccm_len = IWL3160_DCCM_LEN, }; static const struct iwl_pwr_tx_backoff iwl7265_pwr_tx_backoffs[] = { @@ -240,6 +254,7 @@ const struct iwl_cfg iwl3165_2ac_cfg = { .nvm_ver = IWL3165_NVM_VERSION, .nvm_calib_ver = IWL3165_TX_POWER_VERSION, .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, }; const struct iwl_cfg iwl7265_2ac_cfg = { @@ -250,6 +265,7 @@ const struct iwl_cfg iwl7265_2ac_cfg = { .nvm_ver = IWL7265_NVM_VERSION, .nvm_calib_ver = IWL7265_TX_POWER_VERSION, .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, }; const struct iwl_cfg iwl7265_2n_cfg = { @@ -260,6 +276,7 @@ const struct iwl_cfg iwl7265_2n_cfg = { .nvm_ver = IWL7265_NVM_VERSION, .nvm_calib_ver = IWL7265_TX_POWER_VERSION, .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, }; const struct iwl_cfg iwl7265_n_cfg = { @@ -270,6 +287,7 @@ const struct iwl_cfg iwl7265_n_cfg = { .nvm_ver = IWL7265_NVM_VERSION, .nvm_calib_ver = IWL7265_TX_POWER_VERSION, .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, }; const struct iwl_cfg iwl7265d_2ac_cfg = { @@ -280,6 +298,7 @@ const struct iwl_cfg iwl7265d_2ac_cfg = { .nvm_ver = IWL7265D_NVM_VERSION, .nvm_calib_ver = IWL7265_TX_POWER_VERSION, .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, }; const struct iwl_cfg iwl7265d_2n_cfg = { @@ -290,6 +309,7 @@ const struct iwl_cfg iwl7265d_2n_cfg = { .nvm_ver = IWL7265D_NVM_VERSION, .nvm_calib_ver = IWL7265_TX_POWER_VERSION, .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, }; const struct iwl_cfg iwl7265d_n_cfg = { @@ -300,6 +320,7 @@ const struct iwl_cfg iwl7265d_n_cfg = { .nvm_ver = IWL7265D_NVM_VERSION, .nvm_calib_ver = IWL7265_TX_POWER_VERSION, .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, + .dccm_len = IWL7265_DCCM_LEN, }; MODULE_FIRMWARE(IWL7260_MODULE_FIRMWARE(IWL7260_UCODE_API_OK)); diff --git a/drivers/net/wireless/iwlwifi/iwl-8000.c b/drivers/net/wireless/iwlwifi/iwl-8000.c index bf0a95cb7153..2f7fe8167dc9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-8000.c +++ b/drivers/net/wireless/iwlwifi/iwl-8000.c @@ -69,7 +69,7 @@ #include "iwl-agn-hw.h" /* Highest firmware API version supported */ -#define IWL8000_UCODE_API_MAX 10 +#define IWL8000_UCODE_API_MAX 12 /* Oldest version we won't warn about */ #define IWL8000_UCODE_API_OK 10 @@ -81,12 +81,21 @@ #define IWL8000_NVM_VERSION 0x0a1d #define IWL8000_TX_POWER_VERSION 0xffff /* meaningless */ +/* Memory offsets and lengths */ +#define IWL8260_DCCM_OFFSET 0x800000 +#define IWL8260_DCCM_LEN 0x18000 +#define IWL8260_DCCM2_OFFSET 0x880000 +#define IWL8260_DCCM2_LEN 0x8000 +#define IWL8260_SMEM_OFFSET 0x400000 +#define IWL8260_SMEM_LEN 0x68000 + #define IWL8000_FW_PRE "iwlwifi-8000" #define IWL8000_MODULE_FIRMWARE(api) \ IWL8000_FW_PRE "-" __stringify(api) ".ucode" #define NVM_HW_SECTION_NUM_FAMILY_8000 10 -#define DEFAULT_NVM_FILE_FAMILY_8000 "iwl_nvm_8000.bin" +#define DEFAULT_NVM_FILE_FAMILY_8000A "iwl_nvm_8000.bin" +#define DEFAULT_NVM_FILE_FAMILY_8000 "iwl_nvm_8000B.bin" /* Max SDIO RX aggregation size of the ADDBA request/response */ #define MAX_RX_AGG_SIZE_8260_SDIO 28 @@ -124,7 +133,13 @@ static const struct iwl_ht_params iwl8000_ht_params = { .led_mode = IWL_LED_RF_STATE, \ .nvm_hw_section_num = NVM_HW_SECTION_NUM_FAMILY_8000, \ .d0i3 = true, \ - .non_shared_ant = ANT_A + .non_shared_ant = ANT_A, \ + .dccm_offset = IWL8260_DCCM_OFFSET, \ + .dccm_len = IWL8260_DCCM_LEN, \ + .dccm2_offset = IWL8260_DCCM2_OFFSET, \ + .dccm2_len = IWL8260_DCCM2_LEN, \ + .smem_offset = IWL8260_SMEM_OFFSET, \ + .smem_len = IWL8260_SMEM_LEN const struct iwl_cfg iwl8260_2n_cfg = { .name = "Intel(R) Dual Band Wireless N 8260", @@ -145,6 +160,16 @@ const struct iwl_cfg iwl8260_2ac_cfg = { .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, }; +const struct iwl_cfg iwl4165_2ac_cfg = { + .name = "Intel(R) Dual Band Wireless AC 4165", + .fw_name_pre = IWL8000_FW_PRE, + IWL_DEVICE_8000, + .ht_params = &iwl8000_ht_params, + .nvm_ver = IWL8000_NVM_VERSION, + .nvm_calib_ver = IWL8000_TX_POWER_VERSION, + .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, +}; + const struct iwl_cfg iwl8260_2ac_sdio_cfg = { .name = "Intel(R) Dual Band Wireless-AC 8260", .fw_name_pre = IWL8000_FW_PRE, @@ -153,6 +178,7 @@ const struct iwl_cfg iwl8260_2ac_sdio_cfg = { .nvm_ver = IWL8000_NVM_VERSION, .nvm_calib_ver = IWL8000_TX_POWER_VERSION, .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000, + .default_nvm_file_8000A = DEFAULT_NVM_FILE_FAMILY_8000A, .max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO, .disable_dummy_notification = true, .max_ht_ampdu_exponent = MAX_HT_AMPDU_EXPONENT_8260_SDIO, @@ -167,6 +193,7 @@ const struct iwl_cfg iwl4165_2ac_sdio_cfg = { .nvm_ver = IWL8000_NVM_VERSION, .nvm_calib_ver = IWL8000_TX_POWER_VERSION, .default_nvm_file = DEFAULT_NVM_FILE_FAMILY_8000, + .default_nvm_file_8000A = DEFAULT_NVM_FILE_FAMILY_8000A, .max_rx_agg_size = MAX_RX_AGG_SIZE_8260_SDIO, .bt_shared_single_ant = true, .disable_dummy_notification = true, diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index 3a4b9c7fc083..4b190d98a1ec 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -126,7 +126,7 @@ enum iwl_led_mode { /* TX queue watchdog timeouts in mSecs */ #define IWL_WATCHDOG_DISABLED 0 -#define IWL_DEF_WD_TIMEOUT 2000 +#define IWL_DEF_WD_TIMEOUT 2500 #define IWL_LONG_WD_TIMEOUT 10000 #define IWL_MAX_WD_TIMEOUT 120000 @@ -261,6 +261,12 @@ struct iwl_pwr_tx_backoff { * station can receive in HT * @max_vht_ampdu_exponent: the exponent of the max length of A-MPDU that the * station can receive in VHT + * @dccm_offset: offset from which DCCM begins + * @dccm_len: length of DCCM (including runtime stack CCM) + * @dccm2_offset: offset from which the second DCCM begins + * @dccm2_len: length of the second DCCM + * @smem_offset: offset from which the SMEM begins + * @smem_len: the length of SMEM * * We enable the driver to be backward compatible wrt. hardware features. * API differences in uCode shouldn't be handled here but through TLVs @@ -298,11 +304,18 @@ struct iwl_cfg { const struct iwl_pwr_tx_backoff *pwr_tx_backoffs; bool no_power_up_nic_in_init; const char *default_nvm_file; + const char *default_nvm_file_8000A; unsigned int max_rx_agg_size; bool disable_dummy_notification; unsigned int max_tx_agg_size; unsigned int max_ht_ampdu_exponent; unsigned int max_vht_ampdu_exponent; + const u32 dccm_offset; + const u32 dccm_len; + const u32 dccm2_offset; + const u32 dccm2_len; + const u32 smem_offset; + const u32 smem_len; }; /* @@ -369,8 +382,8 @@ extern const struct iwl_cfg iwl7265d_2n_cfg; extern const struct iwl_cfg iwl7265d_n_cfg; extern const struct iwl_cfg iwl8260_2n_cfg; extern const struct iwl_cfg iwl8260_2ac_cfg; +extern const struct iwl_cfg iwl4165_2ac_cfg; extern const struct iwl_cfg iwl8260_2ac_sdio_cfg; -extern const struct iwl_cfg iwl4265_2ac_sdio_cfg; extern const struct iwl_cfg iwl4165_2ac_sdio_cfg; #endif /* CONFIG_IWLMVM */ diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h index aff63c3f5bf8..faa17f2e352a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/iwlwifi/iwl-csr.h @@ -184,6 +184,7 @@ #define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000) /* PCI_OWN_SEM */ #define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) /* ME_OWN */ #define CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000) /* WAKE_ME */ +#define CSR_HW_IF_CONFIG_REG_ENABLE_PME (0x10000000) #define CSR_HW_IF_CONFIG_REG_PERSIST_MODE (0x40000000) /* PERSISTENCE */ #define CSR_MBOX_SET_REG_OS_ALIVE BIT(5) @@ -306,6 +307,7 @@ enum { SILICON_A_STEP = 0, SILICON_B_STEP, + SILICON_C_STEP, }; diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c index 850b85a47806..996e7f16adf9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/iwlwifi/iwl-drv.c @@ -84,21 +84,8 @@ * ******************************************************************************/ -/* - * module name, copyright, version, etc. - */ #define DRV_DESCRIPTION "Intel(R) Wireless WiFi driver for Linux" - -#ifdef CONFIG_IWLWIFI_DEBUG -#define VD "d" -#else -#define VD -#endif - -#define DRV_VERSION IWLWIFI_VERSION VD - MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); MODULE_LICENSE("GPL"); @@ -250,9 +237,6 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first) /* * Starting 8000B - FW name format has changed. This overwrites the * previous name and uses the new format. - * - * TODO: - * Once there is only one supported step for 8000 family - delete this! */ if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) { char rev_step[2] = { @@ -263,13 +247,6 @@ static int iwl_request_firmware(struct iwl_drv *drv, bool first) if (CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_A_STEP) rev_step[0] = 0; - /* - * If hw_rev wasn't set yet - default as B-step. If it IS A-step - * we'll reload that FW later instead. - */ - if (drv->trans->hw_rev == 0) - rev_step[0] = 'B'; - snprintf(drv->firmware_name, sizeof(drv->firmware_name), "%s%s-%s.ucode", name_pre, rev_step, tag); } @@ -926,6 +903,12 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, IWL_UCODE_REGULAR_USNIFFER, tlv_len); break; + case IWL_UCODE_TLV_SDIO_ADMA_ADDR: + if (tlv_len != sizeof(u32)) + goto invalid_tlv_len; + drv->fw.sdio_adma_addr = + le32_to_cpup((__le32 *)tlv_data); + break; default: IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type); break; @@ -1082,7 +1065,6 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) u32 api_ver; int i; bool load_module = false; - u32 hw_rev = drv->trans->hw_rev; fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH; fw->ucode_capa.standard_phy_calibration_size = @@ -1275,50 +1257,6 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) op->name, err); #endif } - - /* - * We may have loaded the wrong FW file in 8000 HW family if it is an - * A-step card, and if drv->trans->hw_rev wasn't properly read when - * the FW file had been loaded. (This might happen in SDIO.) In such a - * case - unload and reload the correct file. - * - * TODO: - * Once there is only one supported step for 8000 family - delete this! - */ - if (drv->trans->cfg->device_family == IWL_DEVICE_FAMILY_8000 && - CSR_HW_REV_STEP(drv->trans->hw_rev) == SILICON_A_STEP && - drv->trans->hw_rev != hw_rev) { - char firmware_name[32]; - - /* Free previous FW resources */ - if (drv->op_mode) - _iwl_op_mode_stop(drv); - iwl_dealloc_ucode(drv); - - /* Build name of correct-step FW */ - snprintf(firmware_name, sizeof(firmware_name), - strrchr(drv->firmware_name, '-')); - snprintf(drv->firmware_name, sizeof(drv->firmware_name), - "%s%s", drv->cfg->fw_name_pre, firmware_name); - - /* Clear data before loading correct FW */ - list_del(&drv->list); - - /* Request correct FW file this time */ - IWL_DEBUG_INFO(drv, "attempting to load A-step FW %s\n", - drv->firmware_name); - err = request_firmware(&ucode_raw, drv->firmware_name, - drv->trans->dev); - if (err) { - IWL_ERR(drv, "Failed swapping FW!\n"); - goto out_unbind; - } - - /* Redo callback function - this time with right FW */ - iwl_req_fw_callback(ucode_raw, context); - } - - kfree(pieces); return; try_again: @@ -1429,7 +1367,7 @@ struct iwl_mod_params iwlwifi_mod_params = { .restart_fw = true, .bt_coex_active = true, .power_level = IWL_POWER_INDEX_1, - .wd_disable = true, + .d0i3_disable = true, #ifndef CONFIG_IWLWIFI_UAPSD .uapsd_disable = true, #endif /* CONFIG_IWLWIFI_UAPSD */ @@ -1492,7 +1430,7 @@ static int __init iwl_drv_init(void) for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) INIT_LIST_HEAD(&iwlwifi_opmode_table[i].drv); - pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n"); + pr_info(DRV_DESCRIPTION "\n"); pr_info(DRV_COPYRIGHT "\n"); #ifdef CONFIG_IWLWIFI_DEBUGFS @@ -1539,15 +1477,15 @@ module_param_named(antenna_coupling, iwlwifi_mod_params.ant_coupling, MODULE_PARM_DESC(antenna_coupling, "specify antenna coupling in dB (default: 0 dB)"); -module_param_named(wd_disable, iwlwifi_mod_params.wd_disable, int, S_IRUGO); -MODULE_PARM_DESC(wd_disable, - "Disable stuck queue watchdog timer 0=system default, 1=disable (default: 1)"); - module_param_named(nvm_file, iwlwifi_mod_params.nvm_file, charp, S_IRUGO); MODULE_PARM_DESC(nvm_file, "NVM file name"); -module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable, +module_param_named(d0i3_disable, iwlwifi_mod_params.d0i3_disable, bool, S_IRUGO); +MODULE_PARM_DESC(d0i3_disable, "disable d0i3 functionality (default: Y)"); + +module_param_named(uapsd_disable, iwlwifi_mod_params.uapsd_disable, + bool, S_IRUGO | S_IWUSR); #ifdef CONFIG_IWLWIFI_UAPSD MODULE_PARM_DESC(uapsd_disable, "disable U-APSD functionality (default: N)"); #else diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.h b/drivers/net/wireless/iwlwifi/iwl-drv.h index be4f8972241a..adf522c756e6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-drv.h +++ b/drivers/net/wireless/iwlwifi/iwl-drv.h @@ -68,7 +68,6 @@ /* for all modules */ #define DRV_NAME "iwlwifi" -#define IWLWIFI_VERSION "in-tree:" #define DRV_COPYRIGHT "Copyright(c) 2003- 2014 Intel Corporation" #define DRV_AUTHOR "<ilw@linux.intel.com>" diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h index 20a8a64c9fe3..919a2548a92c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h @@ -71,7 +71,6 @@ /** * enum iwl_fw_error_dump_type - types of data in the dump file - * @IWL_FW_ERROR_DUMP_SRAM: * @IWL_FW_ERROR_DUMP_CSR: Control Status Registers - from offset 0 * @IWL_FW_ERROR_DUMP_RXF: * @IWL_FW_ERROR_DUMP_TXCMD: last TX command data, structured as @@ -82,9 +81,10 @@ * @IWL_FW_ERROR_DUMP_PRPH: range of periphery registers - there can be several * sections like this in a single file. * @IWL_FW_ERROR_DUMP_FH_REGS: range of FH registers + * @IWL_FW_ERROR_DUMP_MEM: chunk of memory */ enum iwl_fw_error_dump_type { - IWL_FW_ERROR_DUMP_SRAM = 0, + /* 0 is deprecated */ IWL_FW_ERROR_DUMP_CSR = 1, IWL_FW_ERROR_DUMP_RXF = 2, IWL_FW_ERROR_DUMP_TXCMD = 3, @@ -93,6 +93,7 @@ enum iwl_fw_error_dump_type { IWL_FW_ERROR_DUMP_PRPH = 6, IWL_FW_ERROR_DUMP_TXF = 7, IWL_FW_ERROR_DUMP_FH_REGS = 8, + IWL_FW_ERROR_DUMP_MEM = 9, IWL_FW_ERROR_DUMP_MAX, }; @@ -133,6 +134,27 @@ struct iwl_fw_error_dump_txcmd { u8 data[]; } __packed; +/** + * struct iwl_fw_error_dump_fifo - RX/TX FIFO data + * @fifo_num: number of FIFO (starting from 0) + * @available_bytes: num of bytes available in FIFO (may be less than FIFO size) + * @wr_ptr: position of write pointer + * @rd_ptr: position of read pointer + * @fence_ptr: position of fence pointer + * @fence_mode: the current mode of the fence (before locking) - + * 0=follow RD pointer ; 1 = freeze + * @data: all of the FIFO's data + */ +struct iwl_fw_error_dump_fifo { + __le32 fifo_num; + __le32 available_bytes; + __le32 wr_ptr; + __le32 rd_ptr; + __le32 fence_ptr; + __le32 fence_mode; + u8 data[]; +} __packed; + enum iwl_fw_error_dump_family { IWL_FW_ERROR_DUMP_FAMILY_7 = 7, IWL_FW_ERROR_DUMP_FAMILY_8 = 8, @@ -180,6 +202,23 @@ struct iwl_fw_error_dump_prph { __le32 data[]; }; +enum iwl_fw_error_dump_mem_type { + IWL_FW_ERROR_DUMP_MEM_SRAM, + IWL_FW_ERROR_DUMP_MEM_SMEM, +}; + +/** + * struct iwl_fw_error_dump_mem - chunk of memory + * @type: %enum iwl_fw_error_dump_mem_type + * @offset: the offset from which the memory was read + * @data: the content of the memory + */ +struct iwl_fw_error_dump_mem { + __le32 type; + __le32 offset; + u8 data[]; +}; + /** * iwl_fw_error_next_data - advance fw error dump data pointer * @data: previous data block diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h index f2a047f6bb3e..016d91384681 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h @@ -132,6 +132,7 @@ enum iwl_ucode_tlv_type { IWL_UCODE_TLV_ENABLED_CAPABILITIES = 30, IWL_UCODE_TLV_N_SCAN_CHANNELS = 31, IWL_UCODE_TLV_SEC_RT_USNIFFER = 34, + IWL_UCODE_TLV_SDIO_ADMA_ADDR = 35, IWL_UCODE_TLV_FW_DBG_DEST = 38, IWL_UCODE_TLV_FW_DBG_CONF = 39, }; @@ -234,25 +235,34 @@ enum iwl_ucode_tlv_flag { /** * enum iwl_ucode_tlv_api - ucode api - * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field. - * @IWL_UCODE_TLV_CAPA_EXTENDED_BEACON: Support Extended beacon notification * @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex - * @IWL_UCODE_TLV_API_CSA_FLOW: ucode can do unbind-bind flow for CSA. * @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit. * @IWL_UCODE_TLV_API_LMAC_SCAN: This ucode uses LMAC unified scan API. * @IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF: ucode supports disabling dummy notif. * @IWL_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time * longer than the passive one, which is essential for fragmented scan. + * IWL_UCODE_TLV_API_HDC_PHASE_0: ucode supports finer configuration of LTR + * @IWL_UCODE_TLV_API_BASIC_DWELL: use only basic dwell time in scan command, + * regardless of the band or the number of the probes. FW will calculate + * the actual dwell time. + * @IWL_UCODE_TLV_API_SCD_CFG: This firmware can configure the scheduler + * through the dedicated host command. + * @IWL_UCODE_TLV_API_SINGLE_SCAN_EBS: EBS is supported for single scans too. + * @IWL_UCODE_TLV_API_ASYNC_DTM: Async temperature notifications are supported. + * @IWL_UCODE_TLV_API_LQ_SS_PARAMS: Configure STBC/BFER via LQ CMD ss_params */ enum iwl_ucode_tlv_api { - IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID = BIT(0), - IWL_UCODE_TLV_CAPA_EXTENDED_BEACON = BIT(1), IWL_UCODE_TLV_API_BT_COEX_SPLIT = BIT(3), - IWL_UCODE_TLV_API_CSA_FLOW = BIT(4), IWL_UCODE_TLV_API_DISABLE_STA_TX = BIT(5), IWL_UCODE_TLV_API_LMAC_SCAN = BIT(6), IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF = BIT(7), IWL_UCODE_TLV_API_FRAGMENTED_SCAN = BIT(8), + IWL_UCODE_TLV_API_HDC_PHASE_0 = BIT(10), + IWL_UCODE_TLV_API_BASIC_DWELL = BIT(13), + IWL_UCODE_TLV_API_SCD_CFG = BIT(15), + IWL_UCODE_TLV_API_SINGLE_SCAN_EBS = BIT(16), + IWL_UCODE_TLV_API_ASYNC_DTM = BIT(17), + IWL_UCODE_TLV_API_LQ_SS_PARAMS = BIT(18), }; /** @@ -260,6 +270,7 @@ enum iwl_ucode_tlv_api { * @IWL_UCODE_TLV_CAPA_D0I3_SUPPORT: supports D0i3 * @IWL_UCODE_TLV_CAPA_LAR_SUPPORT: supports Location Aware Regulatory * @IWL_UCODE_TLV_CAPA_UMAC_SCAN: supports UMAC scan. + * @IWL_UCODE_TLV_CAPA_BEAMFORMER: supports Beamformer * @IWL_UCODE_TLV_CAPA_TDLS_SUPPORT: support basic TDLS functionality * @IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT: supports insertion of current * tx power value into TPC Report action frame and Link Measurement Report @@ -278,6 +289,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_D0I3_SUPPORT = BIT(0), IWL_UCODE_TLV_CAPA_LAR_SUPPORT = BIT(1), IWL_UCODE_TLV_CAPA_UMAC_SCAN = BIT(2), + IWL_UCODE_TLV_CAPA_BEAMFORMER = BIT(3), IWL_UCODE_TLV_CAPA_TDLS_SUPPORT = BIT(6), IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT = BIT(8), IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT = BIT(9), diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index e6dc3b870949..ffd785cc67d6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h @@ -152,6 +152,8 @@ struct iwl_fw_cscheme_list { * @mvm_fw: indicates this is MVM firmware * @cipher_scheme: optional external cipher scheme. * @human_readable: human readable version + * @sdio_adma_addr: the default address to set for the ADMA in SDIO mode until + * we get the ALIVE from the uCode * @dbg_dest_tlv: points to the destination TLV for debug * @dbg_conf_tlv: array of pointers to configuration TLVs for debug * @dbg_conf_tlv_len: lengths of the @dbg_conf_tlv entries @@ -181,6 +183,8 @@ struct iwl_fw { struct ieee80211_cipher_scheme cs[IWL_UCODE_MAX_CS]; u8 human_readable[FW_VER_HUMAN_READABLE_SZ]; + u32 sdio_adma_addr; + struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv; struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX]; size_t dbg_conf_tlv_len[FW_DBG_MAX]; diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c index 7a2cbf6f90db..03250a45272e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/iwlwifi/iwl-io.c @@ -193,11 +193,15 @@ void iwl_force_nmi(struct iwl_trans *trans) * DEVICE_SET_NMI_8000B_REG - is used. */ if ((trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) || - (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_A_STEP)) - iwl_write_prph(trans, DEVICE_SET_NMI_REG, DEVICE_SET_NMI_VAL); - else + (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_A_STEP)) { + iwl_write_prph(trans, DEVICE_SET_NMI_REG, + DEVICE_SET_NMI_VAL_DRV); + iwl_write_prph(trans, DEVICE_SET_NMI_REG, + DEVICE_SET_NMI_VAL_HW); + } else { iwl_write_prph(trans, DEVICE_SET_NMI_8000B_REG, DEVICE_SET_NMI_8000B_VAL); + } } IWL_EXPORT_SYMBOL(iwl_force_nmi); diff --git a/drivers/net/wireless/iwlwifi/iwl-modparams.h b/drivers/net/wireless/iwlwifi/iwl-modparams.h index 71507cf490e6..e8eabd21ccfe 100644 --- a/drivers/net/wireless/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/iwlwifi/iwl-modparams.h @@ -96,13 +96,13 @@ enum iwl_disable_11n { * use IWL_[DIS,EN]ABLE_HT_* constants * @amsdu_size_8K: enable 8K amsdu size, default = 0 * @restart_fw: restart firmware, default = 1 - * @wd_disable: disable stuck queue check, default = 1 * @bt_coex_active: enable bt coex, default = true * @led_mode: system default, default = 0 * @power_save: enable power save, default = false * @power_level: power level, default = 1 * @debug_level: levels are IWL_DL_* * @ant_coupling: antenna coupling in dB, default = 0 + * @d0i3_disable: disable d0i3, default = 1, * @fw_monitor: allow to use firmware monitor */ struct iwl_mod_params { @@ -110,7 +110,6 @@ struct iwl_mod_params { unsigned int disable_11n; int amsdu_size_8K; bool restart_fw; - int wd_disable; bool bt_coex_active; int led_mode; bool power_save; @@ -121,6 +120,7 @@ struct iwl_mod_params { int ant_coupling; char *nvm_file; bool uapsd_disable; + bool d0i3_disable; bool fw_monitor; }; diff --git a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c index 06e02fcd6f7b..c74f1a4edf23 100644 --- a/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-nvm-parse.c @@ -468,6 +468,8 @@ static void iwl_set_radio_cfg(const struct iwl_cfg *cfg, data->radio_cfg_step = NVM_RF_CFG_STEP_MSK_FAMILY_8000(radio_cfg); data->radio_cfg_dash = NVM_RF_CFG_DASH_MSK_FAMILY_8000(radio_cfg); data->radio_cfg_pnum = NVM_RF_CFG_FLAVOR_MSK_FAMILY_8000(radio_cfg); + data->valid_tx_ant = NVM_RF_CFG_TX_ANT_MSK_FAMILY_8000(radio_cfg); + data->valid_rx_ant = NVM_RF_CFG_RX_ANT_MSK_FAMILY_8000(radio_cfg); } static void iwl_set_hw_address(const struct iwl_cfg *cfg, @@ -592,6 +594,10 @@ iwl_parse_nvm_data(struct device *dev, const struct iwl_cfg *cfg, radio_cfg = iwl_get_radio_cfg(cfg, nvm_sw); iwl_set_radio_cfg(cfg, data, radio_cfg); + if (data->valid_tx_ant) + tx_chains &= data->valid_tx_ant; + if (data->valid_rx_ant) + rx_chains &= data->valid_rx_ant; sku = iwl_get_sku(cfg, nvm_sw); data->sku_cap_band_24GHz_enable = sku & NVM_SKU_CAP_BAND_24GHZ; diff --git a/drivers/net/wireless/iwlwifi/iwl-prph.h b/drivers/net/wireless/iwlwifi/iwl-prph.h index 2df51eab1348..6221e4dfc64f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-prph.h +++ b/drivers/net/wireless/iwlwifi/iwl-prph.h @@ -99,6 +99,7 @@ #define APMG_PCIDEV_STT_VAL_PERSIST_DIS (0x00000200) #define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800) +#define APMG_PCIDEV_STT_VAL_WAKE_ME (0x00004000) #define APMG_RTC_INT_STT_RFKILL (0x10000000) @@ -107,7 +108,8 @@ /* Device NMI register */ #define DEVICE_SET_NMI_REG 0x00a01c30 -#define DEVICE_SET_NMI_VAL 0x1 +#define DEVICE_SET_NMI_VAL_HW BIT(0) +#define DEVICE_SET_NMI_VAL_DRV BIT(7) #define DEVICE_SET_NMI_8000B_REG 0x00a01c24 #define DEVICE_SET_NMI_8000B_VAL 0x1000000 @@ -250,6 +252,7 @@ #define SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK (0x0000007F) #define SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS (16) #define SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK (0x007F0000) +#define SCD_GP_CTRL_ENABLE_31_QUEUES BIT(0) /* Context Data */ #define SCD_CONTEXT_MEM_LOWER_BOUND (SCD_MEM_LOWER_BOUND + 0x600) @@ -283,32 +286,9 @@ #define SCD_CHAINEXT_EN (SCD_BASE + 0x244) #define SCD_AGGR_SEL (SCD_BASE + 0x248) #define SCD_INTERRUPT_MASK (SCD_BASE + 0x108) +#define SCD_GP_CTRL (SCD_BASE + 0x1a8) #define SCD_EN_CTRL (SCD_BASE + 0x254) -static inline unsigned int SCD_QUEUE_WRPTR(unsigned int chnl) -{ - if (chnl < 20) - return SCD_BASE + 0x18 + chnl * 4; - WARN_ON_ONCE(chnl >= 32); - return SCD_BASE + 0x284 + (chnl - 20) * 4; -} - -static inline unsigned int SCD_QUEUE_RDPTR(unsigned int chnl) -{ - if (chnl < 20) - return SCD_BASE + 0x68 + chnl * 4; - WARN_ON_ONCE(chnl >= 32); - return SCD_BASE + 0x2B4 + (chnl - 20) * 4; -} - -static inline unsigned int SCD_QUEUE_STATUS_BITS(unsigned int chnl) -{ - if (chnl < 20) - return SCD_BASE + 0x10c + chnl * 4; - WARN_ON_ONCE(chnl >= 32); - return SCD_BASE + 0x384 + (chnl - 20) * 4; -} - /*********************** END TX SCHEDULER *************************************/ /* Oscillator clock */ @@ -358,18 +338,40 @@ enum secure_load_status_reg { /* Rx FIFO */ #define RXF_SIZE_ADDR (0xa00c88) +#define RXF_RD_D_SPACE (0xa00c40) +#define RXF_RD_WR_PTR (0xa00c50) +#define RXF_RD_RD_PTR (0xa00c54) +#define RXF_RD_FENCE_PTR (0xa00c4c) +#define RXF_SET_FENCE_MODE (0xa00c14) +#define RXF_LD_WR2FENCE (0xa00c1c) +#define RXF_FIFO_RD_FENCE_INC (0xa00c68) #define RXF_SIZE_BYTE_CND_POS (7) #define RXF_SIZE_BYTE_CNT_MSK (0x3ff << RXF_SIZE_BYTE_CND_POS) +#define RXF_DIFF_FROM_PREV (0x200) #define RXF_LD_FENCE_OFFSET_ADDR (0xa00c10) #define RXF_FIFO_RD_FENCE_ADDR (0xa00c0c) +/* Tx FIFO */ +#define TXF_FIFO_ITEM_CNT (0xa00438) +#define TXF_WR_PTR (0xa00414) +#define TXF_RD_PTR (0xa00410) +#define TXF_FENCE_PTR (0xa00418) +#define TXF_LOCK_FENCE (0xa00424) +#define TXF_LARC_NUM (0xa0043c) +#define TXF_READ_MODIFY_DATA (0xa00448) +#define TXF_READ_MODIFY_ADDR (0xa0044c) + /* FW monitor */ +#define MON_BUFF_SAMPLE_CTL (0xa03c00) #define MON_BUFF_BASE_ADDR (0xa03c3c) #define MON_BUFF_END_ADDR (0xa03c40) #define MON_BUFF_WRPTR (0xa03c44) #define MON_BUFF_CYCLE_CNT (0xa03c48) +#define DBGC_IN_SAMPLE (0xa03c00) +#define DBGC_OUT_CTRL (0xa03c0c) + /* FW chicken bits */ #define LMPM_CHICK 0xA01FF8 enum { diff --git a/drivers/net/wireless/iwlwifi/iwl-scd.h b/drivers/net/wireless/iwlwifi/iwl-scd.h index 6c622b21bba7..f2353ebf2666 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scd.h +++ b/drivers/net/wireless/iwlwifi/iwl-scd.h @@ -69,14 +69,6 @@ #include "iwl-prph.h" -static inline void iwl_scd_txq_set_inactive(struct iwl_trans *trans, - u16 txq_id) -{ - iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id), - (0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)| - (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); -} - static inline void iwl_scd_txq_set_chain(struct iwl_trans *trans, u16 txq_id) { @@ -115,4 +107,37 @@ static inline void iwl_scd_enable_set_active(struct iwl_trans *trans, { iwl_write_prph(trans, SCD_EN_CTRL, value); } + +static inline unsigned int SCD_QUEUE_WRPTR(unsigned int chnl) +{ + if (chnl < 20) + return SCD_BASE + 0x18 + chnl * 4; + WARN_ON_ONCE(chnl >= 32); + return SCD_BASE + 0x284 + (chnl - 20) * 4; +} + +static inline unsigned int SCD_QUEUE_RDPTR(unsigned int chnl) +{ + if (chnl < 20) + return SCD_BASE + 0x68 + chnl * 4; + WARN_ON_ONCE(chnl >= 32); + return SCD_BASE + 0x2B4 + chnl * 4; +} + +static inline unsigned int SCD_QUEUE_STATUS_BITS(unsigned int chnl) +{ + if (chnl < 20) + return SCD_BASE + 0x10c + chnl * 4; + WARN_ON_ONCE(chnl >= 32); + return SCD_BASE + 0x334 + chnl * 4; +} + +static inline void iwl_scd_txq_set_inactive(struct iwl_trans *trans, + u16 txq_id) +{ + iwl_write_prph(trans, SCD_QUEUE_STATUS_BITS(txq_id), + (0 << SCD_QUEUE_STTS_REG_POS_ACTIVE)| + (1 << SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); +} + #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 028408a6ecba..a96bd8db6ceb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -368,6 +368,7 @@ enum iwl_trans_status { * @cmd_queue: the index of the command queue. * Must be set before start_fw. * @cmd_fifo: the fifo for host commands + * @cmd_q_wdg_timeout: the timeout of the watchdog timer for the command queue. * @no_reclaim_cmds: Some devices erroneously don't set the * SEQ_RX_FRAME bit on some notifications, this is the * list of such notifications to filter. Max length is @@ -378,24 +379,26 @@ enum iwl_trans_status { * @bc_table_dword: set to true if the BC table expects the byte count to be * in DWORD (as opposed to bytes) * @scd_set_active: should the transport configure the SCD for HCMD queue - * @queue_watchdog_timeout: time (in ms) after which queues - * are considered stuck and will trigger device restart * @command_names: array of command names, must be 256 entries * (one for each command); for debugging only + * @sdio_adma_addr: the default address to set for the ADMA in SDIO mode until + * we get the ALIVE from the uCode */ struct iwl_trans_config { struct iwl_op_mode *op_mode; u8 cmd_queue; u8 cmd_fifo; + unsigned int cmd_q_wdg_timeout; const u8 *no_reclaim_cmds; unsigned int n_no_reclaim_cmds; bool rx_buf_size_8k; bool bc_table_dword; bool scd_set_active; - unsigned int queue_watchdog_timeout; const char *const *command_names; + + u32 sdio_adma_addr; }; struct iwl_trans_dump_data { @@ -507,7 +510,8 @@ struct iwl_trans_ops { struct sk_buff_head *skbs); void (*txq_enable)(struct iwl_trans *trans, int queue, u16 ssn, - const struct iwl_trans_txq_scd_cfg *cfg); + const struct iwl_trans_txq_scd_cfg *cfg, + unsigned int queue_wdg_timeout); void (*txq_disable)(struct iwl_trans *trans, int queue, bool configure_scd); @@ -552,6 +556,21 @@ enum iwl_trans_state { }; /** + * enum iwl_d0i3_mode - d0i3 mode + * + * @IWL_D0I3_MODE_OFF - d0i3 is disabled + * @IWL_D0I3_MODE_ON_IDLE - enter d0i3 when device is idle + * (e.g. no active references) + * @IWL_D0I3_MODE_ON_SUSPEND - enter d0i3 only on suspend + * (in case of 'any' trigger) + */ +enum iwl_d0i3_mode { + IWL_D0I3_MODE_OFF = 0, + IWL_D0I3_MODE_ON_IDLE, + IWL_D0I3_MODE_ON_SUSPEND, +}; + +/** * struct iwl_trans - transport common data * * @ops - pointer to iwl_trans_ops @@ -612,6 +631,8 @@ struct iwl_trans { const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX]; u8 dbg_dest_reg_num; + enum iwl_d0i3_mode d0i3_mode; + /* pointer to trans specific struct */ /*Ensure that this pointer will always be aligned to sizeof pointer */ char trans_specific[0] __aligned(sizeof(void *)); @@ -808,19 +829,21 @@ static inline void iwl_trans_txq_disable(struct iwl_trans *trans, int queue, static inline void iwl_trans_txq_enable_cfg(struct iwl_trans *trans, int queue, u16 ssn, - const struct iwl_trans_txq_scd_cfg *cfg) + const struct iwl_trans_txq_scd_cfg *cfg, + unsigned int queue_wdg_timeout) { might_sleep(); if (unlikely((trans->state != IWL_TRANS_FW_ALIVE))) IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); - trans->ops->txq_enable(trans, queue, ssn, cfg); + trans->ops->txq_enable(trans, queue, ssn, cfg, queue_wdg_timeout); } static inline void iwl_trans_txq_enable(struct iwl_trans *trans, int queue, int fifo, int sta_id, int tid, - int frame_limit, u16 ssn) + int frame_limit, u16 ssn, + unsigned int queue_wdg_timeout) { struct iwl_trans_txq_scd_cfg cfg = { .fifo = fifo, @@ -830,11 +853,12 @@ static inline void iwl_trans_txq_enable(struct iwl_trans *trans, int queue, .aggregate = sta_id >= 0, }; - iwl_trans_txq_enable_cfg(trans, queue, ssn, &cfg); + iwl_trans_txq_enable_cfg(trans, queue, ssn, &cfg, queue_wdg_timeout); } -static inline void iwl_trans_ac_txq_enable(struct iwl_trans *trans, int queue, - int fifo) +static inline +void iwl_trans_ac_txq_enable(struct iwl_trans *trans, int queue, int fifo, + unsigned int queue_wdg_timeout) { struct iwl_trans_txq_scd_cfg cfg = { .fifo = fifo, @@ -844,16 +868,16 @@ static inline void iwl_trans_ac_txq_enable(struct iwl_trans *trans, int queue, .aggregate = false, }; - iwl_trans_txq_enable_cfg(trans, queue, 0, &cfg); + iwl_trans_txq_enable_cfg(trans, queue, 0, &cfg, queue_wdg_timeout); } static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans, - u32 txq_bm) + u32 txqs) { if (unlikely(trans->state != IWL_TRANS_FW_ALIVE)) IWL_ERR(trans, "%s bad state = %d\n", __func__, trans->state); - return trans->ops->wait_tx_queue_empty(trans, txq_bm); + return trans->ops->wait_tx_queue_empty(trans, txqs); } static inline int iwl_trans_dbgfs_register(struct iwl_trans *trans, diff --git a/drivers/net/wireless/iwlwifi/mvm/coex.c b/drivers/net/wireless/iwlwifi/mvm/coex.c index a3bfda45d9e6..1ec4d55155f7 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex.c @@ -342,7 +342,7 @@ static const struct corunning_block_luts antenna_coupling_ranges[] = { { .range = 12, .lut20 = { - cpu_to_le32(0x00000001), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), @@ -363,7 +363,7 @@ static const struct corunning_block_luts antenna_coupling_ranges[] = { { .range = 20, .lut20 = { - cpu_to_le32(0x00000002), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), @@ -384,7 +384,7 @@ static const struct corunning_block_luts antenna_coupling_ranges[] = { { .range = 21, .lut20 = { - cpu_to_le32(0x00000003), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), @@ -405,7 +405,7 @@ static const struct corunning_block_luts antenna_coupling_ranges[] = { { .range = 23, .lut20 = { - cpu_to_le32(0x00000004), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), @@ -426,7 +426,7 @@ static const struct corunning_block_luts antenna_coupling_ranges[] = { { .range = 27, .lut20 = { - cpu_to_le32(0x00000005), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), @@ -447,7 +447,7 @@ static const struct corunning_block_luts antenna_coupling_ranges[] = { { .range = 30, .lut20 = { - cpu_to_le32(0x00000006), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), @@ -468,7 +468,7 @@ static const struct corunning_block_luts antenna_coupling_ranges[] = { { .range = 32, .lut20 = { - cpu_to_le32(0x00000007), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), @@ -489,7 +489,7 @@ static const struct corunning_block_luts antenna_coupling_ranges[] = { { .range = 33, .lut20 = { - cpu_to_le32(0x00000008), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), @@ -989,7 +989,7 @@ int iwl_mvm_rx_bt_coex_notif(struct iwl_mvm *mvm, static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) { - struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_bt_iterator_data *data = _data; struct iwl_mvm *mvm = data->mvm; @@ -1025,7 +1025,7 @@ static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac, void iwl_mvm_bt_rssi_event(struct iwl_mvm *mvm, struct ieee80211_vif *vif, enum ieee80211_rssi_event rssi_event) { - struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_bt_iterator_data data = { .mvm = mvm, }; diff --git a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c index b3210cfbecc8..d530ef3da107 100644 --- a/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c +++ b/drivers/net/wireless/iwlwifi/mvm/coex_legacy.c @@ -330,7 +330,7 @@ static const struct corunning_block_luts antenna_coupling_ranges[] = { { .range = 12, .lut20 = { - cpu_to_le32(0x00000001), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), @@ -351,7 +351,7 @@ static const struct corunning_block_luts antenna_coupling_ranges[] = { { .range = 20, .lut20 = { - cpu_to_le32(0x00000002), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), @@ -372,7 +372,7 @@ static const struct corunning_block_luts antenna_coupling_ranges[] = { { .range = 21, .lut20 = { - cpu_to_le32(0x00000003), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), @@ -393,7 +393,7 @@ static const struct corunning_block_luts antenna_coupling_ranges[] = { { .range = 23, .lut20 = { - cpu_to_le32(0x00000004), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), @@ -414,7 +414,7 @@ static const struct corunning_block_luts antenna_coupling_ranges[] = { { .range = 27, .lut20 = { - cpu_to_le32(0x00000005), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), @@ -435,7 +435,7 @@ static const struct corunning_block_luts antenna_coupling_ranges[] = { { .range = 30, .lut20 = { - cpu_to_le32(0x00000006), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), @@ -456,7 +456,7 @@ static const struct corunning_block_luts antenna_coupling_ranges[] = { { .range = 32, .lut20 = { - cpu_to_le32(0x00000007), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), @@ -477,7 +477,7 @@ static const struct corunning_block_luts antenna_coupling_ranges[] = { { .range = 33, .lut20 = { - cpu_to_le32(0x00000008), cpu_to_le32(0x00000000), + cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), cpu_to_le32(0x00000000), @@ -1034,7 +1034,7 @@ int iwl_mvm_rx_bt_coex_notif_old(struct iwl_mvm *mvm, static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) { - struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_bt_iterator_data *data = _data; struct iwl_mvm *mvm = data->mvm; @@ -1070,7 +1070,7 @@ static void iwl_mvm_bt_rssi_iterator(void *_data, u8 *mac, void iwl_mvm_bt_rssi_event_old(struct iwl_mvm *mvm, struct ieee80211_vif *vif, enum ieee80211_rssi_event rssi_event) { - struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_bt_iterator_data data = { .mvm = mvm, }; diff --git a/drivers/net/wireless/iwlwifi/mvm/constants.h b/drivers/net/wireless/iwlwifi/mvm/constants.h index 3bd93476ec1c..beba375489f1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/constants.h +++ b/drivers/net/wireless/iwlwifi/mvm/constants.h @@ -94,13 +94,42 @@ #define IWL_MVM_BT_COEX_MPLUT 1 #define IWL_MVM_BT_COEX_RRC 1 #define IWL_MVM_BT_COEX_TTC 1 -#define IWL_MVM_BT_COEX_MPLUT_REG0 0x28412201 +#define IWL_MVM_BT_COEX_MPLUT_REG0 0x22002200 #define IWL_MVM_BT_COEX_MPLUT_REG1 0x11118451 #define IWL_MVM_BT_COEX_ANTENNA_COUPLING_THRS 30 #define IWL_MVM_FW_MCAST_FILTER_PASS_ALL 0 #define IWL_MVM_FW_BCAST_FILTER_PASS_ALL 0 -#define IWL_MVM_QUOTA_THRESHOLD 8 +#define IWL_MVM_QUOTA_THRESHOLD 4 #define IWL_MVM_RS_RSSI_BASED_INIT_RATE 0 -#define IWL_MVM_RS_DISABLE_MIMO 0 +#define IWL_MVM_RS_DISABLE_P2P_MIMO 0 +#define IWL_MVM_RS_NUM_TRY_BEFORE_ANT_TOGGLE 1 +#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE 2 +#define IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE_TW 1 +#define IWL_MVM_RS_INITIAL_MIMO_NUM_RATES 3 +#define IWL_MVM_RS_INITIAL_SISO_NUM_RATES 3 +#define IWL_MVM_RS_INITIAL_LEGACY_NUM_RATES 2 +#define IWL_MVM_RS_INITIAL_LEGACY_RETRIES 2 +#define IWL_MVM_RS_SECONDARY_LEGACY_RETRIES 1 +#define IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES 16 +#define IWL_MVM_RS_SECONDARY_SISO_NUM_RATES 3 +#define IWL_MVM_RS_SECONDARY_SISO_RETRIES 1 +#define IWL_MVM_RS_RATE_MIN_FAILURE_TH 3 +#define IWL_MVM_RS_RATE_MIN_SUCCESS_TH 8 +#define IWL_MVM_RS_STAY_IN_COLUMN_TIMEOUT 5 /* Seconds */ +#define IWL_MVM_RS_IDLE_TIMEOUT 5 /* Seconds */ +#define IWL_MVM_RS_MISSED_RATE_MAX 15 +#define IWL_MVM_RS_LEGACY_FAILURE_LIMIT 160 +#define IWL_MVM_RS_LEGACY_SUCCESS_LIMIT 480 +#define IWL_MVM_RS_LEGACY_TABLE_COUNT 160 +#define IWL_MVM_RS_NON_LEGACY_FAILURE_LIMIT 400 +#define IWL_MVM_RS_NON_LEGACY_SUCCESS_LIMIT 4500 +#define IWL_MVM_RS_NON_LEGACY_TABLE_COUNT 1500 +#define IWL_MVM_RS_SR_FORCE_DECREASE 15 /* percent */ +#define IWL_MVM_RS_SR_NO_DECREASE 85 /* percent */ +#define IWL_MVM_RS_AGG_TIME_LIMIT 4000 /* 4 msecs. valid 100-8000 */ +#define IWL_MVM_RS_AGG_DISABLE_START 3 +#define IWL_MVM_RS_TPC_SR_FORCE_INCREASE 75 /* percent */ +#define IWL_MVM_RS_TPC_SR_NO_INCREASE 85 /* percent */ +#define IWL_MVM_RS_TPC_TX_POWER_STEP 3 #endif /* __MVM_CONSTANTS_H */ diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index 744de262373e..14e8fd661889 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c @@ -793,7 +793,7 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm, struct ieee80211_sta *ap_sta) { int ret; - struct iwl_mvm_sta *mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv; + struct iwl_mvm_sta *mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta); /* TODO: wowlan_config_cmd->wowlan_ba_teardown_tids */ @@ -1137,12 +1137,43 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, return ret; } +static int iwl_mvm_enter_d0i3_sync(struct iwl_mvm *mvm) +{ + struct iwl_notification_wait wait_d3; + static const u8 d3_notif[] = { D3_CONFIG_CMD }; + int ret; + + iwl_init_notification_wait(&mvm->notif_wait, &wait_d3, + d3_notif, ARRAY_SIZE(d3_notif), + NULL, NULL); + + ret = iwl_mvm_enter_d0i3(mvm->hw->priv); + if (ret) + goto remove_notif; + + ret = iwl_wait_notification(&mvm->notif_wait, &wait_d3, HZ); + WARN_ON_ONCE(ret); + return ret; + +remove_notif: + iwl_remove_notification(&mvm->notif_wait, &wait_d3); + return ret; +} + int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); iwl_trans_suspend(mvm->trans); - if (iwl_mvm_is_d0i3_supported(mvm)) { + if (wowlan->any) { + /* 'any' trigger means d0i3 usage */ + if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) { + int ret = iwl_mvm_enter_d0i3_sync(mvm); + + if (ret) + return ret; + } + mutex_lock(&mvm->d0i3_suspend_mutex); __set_bit(D0I3_DEFER_WAKEUP, &mvm->d0i3_suspend_flags); mutex_unlock(&mvm->d0i3_suspend_mutex); @@ -1626,7 +1657,7 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, if (IS_ERR_OR_NULL(ap_sta)) goto out_free; - mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv; + mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta); for (i = 0; i < IWL_MAX_TID_COUNT; i++) { u16 seq = status.qos_seq_ctr[i]; /* firmware stores last-used value, we store next value */ @@ -1876,8 +1907,20 @@ int iwl_mvm_resume(struct ieee80211_hw *hw) iwl_trans_resume(mvm->trans); - if (iwl_mvm_is_d0i3_supported(mvm)) + if (mvm->hw->wiphy->wowlan_config->any) { + /* 'any' trigger means d0i3 usage */ + if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) { + int ret = iwl_mvm_exit_d0i3(hw->priv); + + if (ret) + return ret; + /* + * d0i3 exit will be deferred until reconfig_complete. + * make sure there we are out of d0i3. + */ + } return 0; + } return __iwl_mvm_resume(mvm, false); } diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c index 9aa2311a776c..5fe14591e1c4 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs-vif.c @@ -268,7 +268,7 @@ static ssize_t iwl_dbgfs_mac_params_read(struct file *file, sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[ap_sta_id], lockdep_is_held(&mvm->mutex)); if (!IS_ERR_OR_NULL(sta)) { - struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); pos += scnprintf(buf+pos, bufsz-pos, "ap_sta_id %d - reduced Tx power %d\n", @@ -517,6 +517,34 @@ static ssize_t iwl_dbgfs_low_latency_read(struct file *file, return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf)); } +static ssize_t iwl_dbgfs_uapsd_misbehaving_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_vif *vif = file->private_data; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + char buf[20]; + int len; + + len = sprintf(buf, "%pM\n", mvmvif->uapsd_misbehaving_bssid); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t iwl_dbgfs_uapsd_misbehaving_write(struct ieee80211_vif *vif, + char *buf, size_t count, + loff_t *ppos) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_mvm *mvm = mvmvif->mvm; + bool ret; + + mutex_lock(&mvm->mutex); + ret = mac_pton(buf, mvmvif->uapsd_misbehaving_bssid); + mutex_unlock(&mvm->mutex); + + return ret ? count : -EINVAL; +} + #define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif) #define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ @@ -531,6 +559,7 @@ MVM_DEBUGFS_READ_FILE_OPS(mac_params); MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32); MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256); MVM_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 10); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(uapsd_misbehaving, 20); void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { @@ -564,6 +593,8 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) MVM_DEBUGFS_ADD_FILE_VIF(mac_params, mvmvif->dbgfs_dir, S_IRUSR); MVM_DEBUGFS_ADD_FILE_VIF(low_latency, mvmvif->dbgfs_dir, S_IRUSR | S_IWUSR); + MVM_DEBUGFS_ADD_FILE_VIF(uapsd_misbehaving, mvmvif->dbgfs_dir, + S_IRUSR | S_IWUSR); if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p && mvmvif == mvm->bf_allowed_vif) diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c index 33bf915cd7ea..82c09d86af8c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c @@ -654,10 +654,10 @@ out: return ret ?: count; } -#define PRINT_STATS_LE32(_str, _val) \ +#define PRINT_STATS_LE32(_struct, _memb) \ pos += scnprintf(buf + pos, bufsz - pos, \ - fmt_table, _str, \ - le32_to_cpu(_val)) + fmt_table, #_memb, \ + le32_to_cpu(_struct->_memb)) static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file, char __user *user_buf, size_t count, @@ -692,97 +692,89 @@ static ssize_t iwl_dbgfs_fw_rx_stats_read(struct file *file, pos += scnprintf(buf + pos, bufsz - pos, fmt_header, "Statistics_Rx - OFDM"); - PRINT_STATS_LE32("ina_cnt", ofdm->ina_cnt); - PRINT_STATS_LE32("fina_cnt", ofdm->fina_cnt); - PRINT_STATS_LE32("plcp_err", ofdm->plcp_err); - PRINT_STATS_LE32("crc32_err", ofdm->crc32_err); - PRINT_STATS_LE32("overrun_err", ofdm->overrun_err); - PRINT_STATS_LE32("early_overrun_err", ofdm->early_overrun_err); - PRINT_STATS_LE32("crc32_good", ofdm->crc32_good); - PRINT_STATS_LE32("false_alarm_cnt", ofdm->false_alarm_cnt); - PRINT_STATS_LE32("fina_sync_err_cnt", ofdm->fina_sync_err_cnt); - PRINT_STATS_LE32("sfd_timeout", ofdm->sfd_timeout); - PRINT_STATS_LE32("fina_timeout", ofdm->fina_timeout); - PRINT_STATS_LE32("unresponded_rts", ofdm->unresponded_rts); - PRINT_STATS_LE32("rxe_frame_lmt_overrun", - ofdm->rxe_frame_limit_overrun); - PRINT_STATS_LE32("sent_ack_cnt", ofdm->sent_ack_cnt); - PRINT_STATS_LE32("sent_cts_cnt", ofdm->sent_cts_cnt); - PRINT_STATS_LE32("sent_ba_rsp_cnt", ofdm->sent_ba_rsp_cnt); - PRINT_STATS_LE32("dsp_self_kill", ofdm->dsp_self_kill); - PRINT_STATS_LE32("mh_format_err", ofdm->mh_format_err); - PRINT_STATS_LE32("re_acq_main_rssi_sum", ofdm->re_acq_main_rssi_sum); - PRINT_STATS_LE32("reserved", ofdm->reserved); + PRINT_STATS_LE32(ofdm, ina_cnt); + PRINT_STATS_LE32(ofdm, fina_cnt); + PRINT_STATS_LE32(ofdm, plcp_err); + PRINT_STATS_LE32(ofdm, crc32_err); + PRINT_STATS_LE32(ofdm, overrun_err); + PRINT_STATS_LE32(ofdm, early_overrun_err); + PRINT_STATS_LE32(ofdm, crc32_good); + PRINT_STATS_LE32(ofdm, false_alarm_cnt); + PRINT_STATS_LE32(ofdm, fina_sync_err_cnt); + PRINT_STATS_LE32(ofdm, sfd_timeout); + PRINT_STATS_LE32(ofdm, fina_timeout); + PRINT_STATS_LE32(ofdm, unresponded_rts); + PRINT_STATS_LE32(ofdm, rxe_frame_lmt_overrun); + PRINT_STATS_LE32(ofdm, sent_ack_cnt); + PRINT_STATS_LE32(ofdm, sent_cts_cnt); + PRINT_STATS_LE32(ofdm, sent_ba_rsp_cnt); + PRINT_STATS_LE32(ofdm, dsp_self_kill); + PRINT_STATS_LE32(ofdm, mh_format_err); + PRINT_STATS_LE32(ofdm, re_acq_main_rssi_sum); + PRINT_STATS_LE32(ofdm, reserved); pos += scnprintf(buf + pos, bufsz - pos, fmt_header, "Statistics_Rx - CCK"); - PRINT_STATS_LE32("ina_cnt", cck->ina_cnt); - PRINT_STATS_LE32("fina_cnt", cck->fina_cnt); - PRINT_STATS_LE32("plcp_err", cck->plcp_err); - PRINT_STATS_LE32("crc32_err", cck->crc32_err); - PRINT_STATS_LE32("overrun_err", cck->overrun_err); - PRINT_STATS_LE32("early_overrun_err", cck->early_overrun_err); - PRINT_STATS_LE32("crc32_good", cck->crc32_good); - PRINT_STATS_LE32("false_alarm_cnt", cck->false_alarm_cnt); - PRINT_STATS_LE32("fina_sync_err_cnt", cck->fina_sync_err_cnt); - PRINT_STATS_LE32("sfd_timeout", cck->sfd_timeout); - PRINT_STATS_LE32("fina_timeout", cck->fina_timeout); - PRINT_STATS_LE32("unresponded_rts", cck->unresponded_rts); - PRINT_STATS_LE32("rxe_frame_lmt_overrun", - cck->rxe_frame_limit_overrun); - PRINT_STATS_LE32("sent_ack_cnt", cck->sent_ack_cnt); - PRINT_STATS_LE32("sent_cts_cnt", cck->sent_cts_cnt); - PRINT_STATS_LE32("sent_ba_rsp_cnt", cck->sent_ba_rsp_cnt); - PRINT_STATS_LE32("dsp_self_kill", cck->dsp_self_kill); - PRINT_STATS_LE32("mh_format_err", cck->mh_format_err); - PRINT_STATS_LE32("re_acq_main_rssi_sum", cck->re_acq_main_rssi_sum); - PRINT_STATS_LE32("reserved", cck->reserved); + PRINT_STATS_LE32(cck, ina_cnt); + PRINT_STATS_LE32(cck, fina_cnt); + PRINT_STATS_LE32(cck, plcp_err); + PRINT_STATS_LE32(cck, crc32_err); + PRINT_STATS_LE32(cck, overrun_err); + PRINT_STATS_LE32(cck, early_overrun_err); + PRINT_STATS_LE32(cck, crc32_good); + PRINT_STATS_LE32(cck, false_alarm_cnt); + PRINT_STATS_LE32(cck, fina_sync_err_cnt); + PRINT_STATS_LE32(cck, sfd_timeout); + PRINT_STATS_LE32(cck, fina_timeout); + PRINT_STATS_LE32(cck, unresponded_rts); + PRINT_STATS_LE32(cck, rxe_frame_lmt_overrun); + PRINT_STATS_LE32(cck, sent_ack_cnt); + PRINT_STATS_LE32(cck, sent_cts_cnt); + PRINT_STATS_LE32(cck, sent_ba_rsp_cnt); + PRINT_STATS_LE32(cck, dsp_self_kill); + PRINT_STATS_LE32(cck, mh_format_err); + PRINT_STATS_LE32(cck, re_acq_main_rssi_sum); + PRINT_STATS_LE32(cck, reserved); pos += scnprintf(buf + pos, bufsz - pos, fmt_header, "Statistics_Rx - GENERAL"); - PRINT_STATS_LE32("bogus_cts", general->bogus_cts); - PRINT_STATS_LE32("bogus_ack", general->bogus_ack); - PRINT_STATS_LE32("non_bssid_frames", general->non_bssid_frames); - PRINT_STATS_LE32("filtered_frames", general->filtered_frames); - PRINT_STATS_LE32("non_channel_beacons", general->non_channel_beacons); - PRINT_STATS_LE32("channel_beacons", general->channel_beacons); - PRINT_STATS_LE32("num_missed_bcon", general->num_missed_bcon); - PRINT_STATS_LE32("adc_rx_saturation_time", - general->adc_rx_saturation_time); - PRINT_STATS_LE32("ina_detection_search_time", - general->ina_detection_search_time); - PRINT_STATS_LE32("beacon_silence_rssi_a", - general->beacon_silence_rssi_a); - PRINT_STATS_LE32("beacon_silence_rssi_b", - general->beacon_silence_rssi_b); - PRINT_STATS_LE32("beacon_silence_rssi_c", - general->beacon_silence_rssi_c); - PRINT_STATS_LE32("interference_data_flag", - general->interference_data_flag); - PRINT_STATS_LE32("channel_load", general->channel_load); - PRINT_STATS_LE32("dsp_false_alarms", general->dsp_false_alarms); - PRINT_STATS_LE32("beacon_rssi_a", general->beacon_rssi_a); - PRINT_STATS_LE32("beacon_rssi_b", general->beacon_rssi_b); - PRINT_STATS_LE32("beacon_rssi_c", general->beacon_rssi_c); - PRINT_STATS_LE32("beacon_energy_a", general->beacon_energy_a); - PRINT_STATS_LE32("beacon_energy_b", general->beacon_energy_b); - PRINT_STATS_LE32("beacon_energy_c", general->beacon_energy_c); - PRINT_STATS_LE32("num_bt_kills", general->num_bt_kills); - PRINT_STATS_LE32("mac_id", general->mac_id); - PRINT_STATS_LE32("directed_data_mpdu", general->directed_data_mpdu); + PRINT_STATS_LE32(general, bogus_cts); + PRINT_STATS_LE32(general, bogus_ack); + PRINT_STATS_LE32(general, non_bssid_frames); + PRINT_STATS_LE32(general, filtered_frames); + PRINT_STATS_LE32(general, non_channel_beacons); + PRINT_STATS_LE32(general, channel_beacons); + PRINT_STATS_LE32(general, num_missed_bcon); + PRINT_STATS_LE32(general, adc_rx_saturation_time); + PRINT_STATS_LE32(general, ina_detection_search_time); + PRINT_STATS_LE32(general, beacon_silence_rssi_a); + PRINT_STATS_LE32(general, beacon_silence_rssi_b); + PRINT_STATS_LE32(general, beacon_silence_rssi_c); + PRINT_STATS_LE32(general, interference_data_flag); + PRINT_STATS_LE32(general, channel_load); + PRINT_STATS_LE32(general, dsp_false_alarms); + PRINT_STATS_LE32(general, beacon_rssi_a); + PRINT_STATS_LE32(general, beacon_rssi_b); + PRINT_STATS_LE32(general, beacon_rssi_c); + PRINT_STATS_LE32(general, beacon_energy_a); + PRINT_STATS_LE32(general, beacon_energy_b); + PRINT_STATS_LE32(general, beacon_energy_c); + PRINT_STATS_LE32(general, num_bt_kills); + PRINT_STATS_LE32(general, mac_id); + PRINT_STATS_LE32(general, directed_data_mpdu); pos += scnprintf(buf + pos, bufsz - pos, fmt_header, "Statistics_Rx - HT"); - PRINT_STATS_LE32("plcp_err", ht->plcp_err); - PRINT_STATS_LE32("overrun_err", ht->overrun_err); - PRINT_STATS_LE32("early_overrun_err", ht->early_overrun_err); - PRINT_STATS_LE32("crc32_good", ht->crc32_good); - PRINT_STATS_LE32("crc32_err", ht->crc32_err); - PRINT_STATS_LE32("mh_format_err", ht->mh_format_err); - PRINT_STATS_LE32("agg_crc32_good", ht->agg_crc32_good); - PRINT_STATS_LE32("agg_mpdu_cnt", ht->agg_mpdu_cnt); - PRINT_STATS_LE32("agg_cnt", ht->agg_cnt); - PRINT_STATS_LE32("unsupport_mcs", ht->unsupport_mcs); + PRINT_STATS_LE32(ht, plcp_err); + PRINT_STATS_LE32(ht, overrun_err); + PRINT_STATS_LE32(ht, early_overrun_err); + PRINT_STATS_LE32(ht, crc32_good); + PRINT_STATS_LE32(ht, crc32_err); + PRINT_STATS_LE32(ht, mh_format_err); + PRINT_STATS_LE32(ht, agg_crc32_good); + PRINT_STATS_LE32(ht, agg_mpdu_cnt); + PRINT_STATS_LE32(ht, agg_cnt); + PRINT_STATS_LE32(ht, unsupport_mcs); mutex_unlock(&mvm->mutex); @@ -933,7 +925,7 @@ iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm *mvm, char *buf, return -EINVAL; if (scan_rx_ant > ANT_ABC) return -EINVAL; - if (scan_rx_ant & ~mvm->fw->valid_rx_ant) + if (scan_rx_ant & ~(iwl_mvm_get_valid_rx_ant(mvm))) return -EINVAL; if (mvm->scan_rx_ant != scan_rx_ant) { @@ -945,6 +937,61 @@ iwl_dbgfs_scan_ant_rxchain_write(struct iwl_mvm *mvm, char *buf, return count; } +static ssize_t iwl_dbgfs_fw_dbg_conf_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = file->private_data; + enum iwl_fw_dbg_conf conf; + char buf[8]; + const size_t bufsz = sizeof(buf); + int pos = 0; + + mutex_lock(&mvm->mutex); + conf = mvm->fw_dbg_conf; + mutex_unlock(&mvm->mutex); + + pos += scnprintf(buf + pos, bufsz - pos, "%d\n", conf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_fw_dbg_conf_write(struct iwl_mvm *mvm, + char *buf, size_t count, + loff_t *ppos) +{ + int ret, conf_id; + + ret = kstrtoint(buf, 0, &conf_id); + if (ret) + return ret; + + if (WARN_ON(conf_id >= FW_DBG_MAX)) + return -EINVAL; + + mutex_lock(&mvm->mutex); + ret = iwl_mvm_start_fw_dbg_conf(mvm, conf_id); + mutex_unlock(&mvm->mutex); + + return ret ?: count; +} + +static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm, + char *buf, size_t count, + loff_t *ppos) +{ + int ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PRPH_WRITE); + + if (ret) + return ret; + + iwl_mvm_fw_dbg_collect(mvm); + + iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE); + + return count; +} + #define ADD_TEXT(...) pos += scnprintf(buf + pos, bufsz - pos, __VA_ARGS__) #ifdef CONFIG_IWLWIFI_BCAST_FILTERING static ssize_t iwl_dbgfs_bcast_filters_read(struct file *file, @@ -1340,6 +1387,7 @@ static ssize_t iwl_dbgfs_d0i3_refs_read(struct file *file, PRINT_MVM_REF(IWL_MVM_REF_TM_CMD); PRINT_MVM_REF(IWL_MVM_REF_EXIT_WORK); PRINT_MVM_REF(IWL_MVM_REF_PROTECT_CSA); + PRINT_MVM_REF(IWL_MVM_REF_FW_DBG_COLLECT); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } @@ -1439,6 +1487,26 @@ out: return count; } +static ssize_t iwl_dbgfs_enable_scan_iteration_notif_write(struct iwl_mvm *mvm, + char *buf, + size_t count, + loff_t *ppos) +{ + int val; + + mutex_lock(&mvm->mutex); + + if (kstrtoint(buf, 10, &val)) { + mutex_unlock(&mvm->mutex); + return -EINVAL; + } + + mvm->scan_iter_notif_enabled = val; + mutex_unlock(&mvm->mutex); + + return count; +} + MVM_DEBUGFS_READ_WRITE_FILE_OPS(prph_reg, 64); /* Device wide debugfs entries */ @@ -1459,6 +1527,9 @@ MVM_DEBUGFS_WRITE_FILE_OPS(bt_tx_prio, 10); MVM_DEBUGFS_WRITE_FILE_OPS(bt_force_ant, 10); MVM_DEBUGFS_READ_WRITE_FILE_OPS(scan_ant_rxchain, 8); MVM_DEBUGFS_READ_WRITE_FILE_OPS(d0i3_refs, 8); +MVM_DEBUGFS_READ_WRITE_FILE_OPS(fw_dbg_conf, 8); +MVM_DEBUGFS_WRITE_FILE_OPS(fw_dbg_collect, 8); +MVM_DEBUGFS_WRITE_FILE_OPS(enable_scan_iteration_notif, 8); #ifdef CONFIG_IWLWIFI_BCAST_FILTERING MVM_DEBUGFS_READ_WRITE_FILE_OPS(bcast_filters, 256); @@ -1500,6 +1571,10 @@ int iwl_mvm_dbgfs_register(struct iwl_mvm *mvm, struct dentry *dbgfs_dir) S_IWUSR | S_IRUSR); MVM_DEBUGFS_ADD_FILE(prph_reg, mvm->debugfs_dir, S_IWUSR | S_IRUSR); MVM_DEBUGFS_ADD_FILE(d0i3_refs, mvm->debugfs_dir, S_IRUSR | S_IWUSR); + MVM_DEBUGFS_ADD_FILE(fw_dbg_conf, mvm->debugfs_dir, S_IRUSR | S_IWUSR); + MVM_DEBUGFS_ADD_FILE(fw_dbg_collect, mvm->debugfs_dir, S_IWUSR); + MVM_DEBUGFS_ADD_FILE(enable_scan_iteration_notif, mvm->debugfs_dir, + S_IWUSR); #ifdef CONFIG_IWLWIFI_BCAST_FILTERING if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_BCAST_FILTERING) { diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h index 430020047b77..4fc0938b3fb6 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-power.h @@ -92,14 +92,32 @@ enum iwl_ltr_config_flags { }; /** + * struct iwl_ltr_config_cmd_v1 - configures the LTR + * @flags: See %enum iwl_ltr_config_flags + */ +struct iwl_ltr_config_cmd_v1 { + __le32 flags; + __le32 static_long; + __le32 static_short; +} __packed; /* LTR_CAPABLE_API_S_VER_1 */ + +#define LTR_VALID_STATES_NUM 4 + +/** * struct iwl_ltr_config_cmd - configures the LTR * @flags: See %enum iwl_ltr_config_flags + * @static_long: + * @static_short: + * @ltr_cfg_values: + * @ltr_short_idle_timeout: */ struct iwl_ltr_config_cmd { __le32 flags; __le32 static_long; __le32 static_short; -} __packed; + __le32 ltr_cfg_values[LTR_VALID_STATES_NUM]; + __le32 ltr_short_idle_timeout; +} __packed; /* LTR_CAPABLE_API_S_VER_2 */ /* Radio LP RX Energy Threshold measured in dBm */ #define POWER_LPRX_RSSI_THRESHOLD 75 diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h index 8bb5b94bf963..0f1ea80a55ef 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-rs.h @@ -308,6 +308,42 @@ enum { #define LQ_FLAG_DYNAMIC_BW_POS 6 #define LQ_FLAG_DYNAMIC_BW_MSK (1 << LQ_FLAG_DYNAMIC_BW_POS) +/* Single Stream Tx Parameters (lq_cmd->ss_params) + * Flags to control a smart FW decision about whether BFER/STBC/SISO will be + * used for single stream Tx. + */ + +/* Bit 0-1: Max STBC streams allowed. Can be 0-3. + * (0) - No STBC allowed + * (1) - 2x1 STBC allowed (HT/VHT) + * (2) - 4x2 STBC allowed (HT/VHT) + * (3) - 3x2 STBC allowed (HT only) + * All our chips are at most 2 antennas so only (1) is valid for now. + */ +#define LQ_SS_STBC_ALLOWED_POS 0 +#define LQ_SS_STBC_ALLOWED_MSK (3 << LQ_SS_STBC_ALLOWED_MSK) + +/* 2x1 STBC is allowed */ +#define LQ_SS_STBC_1SS_ALLOWED (1 << LQ_SS_STBC_ALLOWED_POS) + +/* Bit 2: Beamformer (VHT only) is allowed */ +#define LQ_SS_BFER_ALLOWED_POS 2 +#define LQ_SS_BFER_ALLOWED (1 << LQ_SS_BFER_ALLOWED_POS) + +/* Bit 3: Force BFER or STBC for testing + * If this is set: + * If BFER is allowed then force the ucode to choose BFER else + * If STBC is allowed then force the ucode to choose STBC over SISO + */ +#define LQ_SS_FORCE_POS 3 +#define LQ_SS_FORCE (1 << LQ_SS_FORCE_POS) + +/* Bit 31: ss_params field is valid. Used for FW backward compatibility + * with other drivers which don't support the ss_params API yet + */ +#define LQ_SS_PARAMS_VALID_POS 31 +#define LQ_SS_PARAMS_VALID (1 << LQ_SS_PARAMS_VALID_POS) + /** * struct iwl_lq_cmd - link quality command * @sta_id: station to update @@ -330,7 +366,7 @@ enum { * 2 - 0x3f: maximal number of frames (up to 3f == 63) * @rs_table: array of rates for each TX try, each is rate_n_flags, * meaning it is a combination of RATE_MCS_* and IWL_RATE_*_PLCP - * @bf_params: beam forming params, currently not used + * @ss_params: single stream features. declare whether STBC or BFER are allowed. */ struct iwl_lq_cmd { u8 sta_id; @@ -348,6 +384,6 @@ struct iwl_lq_cmd { u8 agg_frame_cnt_limit; __le32 reserved2; __le32 rs_table[LQ_MAX_RETRY_NUM]; - __le32 bf_params; + __le32 ss_params; }; /* LINK_QUALITY_CMD_API_S_VER_1 */ #endif /* __fw_api_rs_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h index 1f2acf47bfb2..cfc0e65b34a5 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-scan.h @@ -653,8 +653,11 @@ enum iwl_scan_channel_flags { }; /* iwl_scan_channel_opt - CHANNEL_OPTIMIZATION_API_S - * @flags: enum iwl_scan_channel_flgs - * @non_ebs_ratio: how many regular scan iteration before EBS + * @flags: enum iwl_scan_channel_flags + * @non_ebs_ratio: defines the ratio of number of scan iterations where EBS is + * involved. + * 1 - EBS is disabled. + * 2 - every second scan will be full scan(and so on). */ struct iwl_scan_channel_opt { __le16 flags; @@ -672,6 +675,7 @@ struct iwl_scan_channel_opt { * @IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED: all passive scans will be fragmented * @IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED: insert WFA vendor-specific TPC report * and DS parameter set IEs into probe requests. + * @IWL_MVM_LMAC_SCAN_FLAG_MATCH: Send match found notification on matches */ enum iwl_mvm_lmac_scan_flags { IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL = BIT(0), @@ -681,6 +685,7 @@ enum iwl_mvm_lmac_scan_flags { IWL_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS = BIT(4), IWL_MVM_LMAC_SCAN_FLAG_FRAGMENTED = BIT(5), IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED = BIT(6), + IWL_MVM_LMAC_SCAN_FLAG_MATCH = BIT(9), }; enum iwl_scan_priority { diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h new file mode 100644 index 000000000000..928168b18346 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-stats.h @@ -0,0 +1,277 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called COPYING. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + * + *****************************************************************************/ + +#ifndef __fw_api_stats_h__ +#define __fw_api_stats_h__ + +struct mvm_statistics_dbg { + __le32 burst_check; + __le32 burst_count; + __le32 wait_for_silence_timeout_cnt; + __le32 reserved[3]; +} __packed; /* STATISTICS_DEBUG_API_S_VER_2 */ + +struct mvm_statistics_div { + __le32 tx_on_a; + __le32 tx_on_b; + __le32 exec_time; + __le32 probe_time; + __le32 rssi_ant; + __le32 reserved2; +} __packed; /* STATISTICS_SLOW_DIV_API_S_VER_2 */ + +struct mvm_statistics_rx_non_phy { + __le32 bogus_cts; /* CTS received when not expecting CTS */ + __le32 bogus_ack; /* ACK received when not expecting ACK */ + __le32 non_bssid_frames; /* number of frames with BSSID that + * doesn't belong to the STA BSSID */ + __le32 filtered_frames; /* count frames that were dumped in the + * filtering process */ + __le32 non_channel_beacons; /* beacons with our bss id but not on + * our serving channel */ + __le32 channel_beacons; /* beacons with our bss id and in our + * serving channel */ + __le32 num_missed_bcon; /* number of missed beacons */ + __le32 adc_rx_saturation_time; /* count in 0.8us units the time the + * ADC was in saturation */ + __le32 ina_detection_search_time;/* total time (in 0.8us) searched + * for INA */ + __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */ + __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */ + __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */ + __le32 interference_data_flag; /* flag for interference data + * availability. 1 when data is + * available. */ + __le32 channel_load; /* counts RX Enable time in uSec */ + __le32 dsp_false_alarms; /* DSP false alarm (both OFDM + * and CCK) counter */ + __le32 beacon_rssi_a; + __le32 beacon_rssi_b; + __le32 beacon_rssi_c; + __le32 beacon_energy_a; + __le32 beacon_energy_b; + __le32 beacon_energy_c; + __le32 num_bt_kills; + __le32 mac_id; + __le32 directed_data_mpdu; +} __packed; /* STATISTICS_RX_NON_PHY_API_S_VER_3 */ + +struct mvm_statistics_rx_phy { + __le32 ina_cnt; + __le32 fina_cnt; + __le32 plcp_err; + __le32 crc32_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 false_alarm_cnt; + __le32 fina_sync_err_cnt; + __le32 sfd_timeout; + __le32 fina_timeout; + __le32 unresponded_rts; + __le32 rxe_frame_lmt_overrun; + __le32 sent_ack_cnt; + __le32 sent_cts_cnt; + __le32 sent_ba_rsp_cnt; + __le32 dsp_self_kill; + __le32 mh_format_err; + __le32 re_acq_main_rssi_sum; + __le32 reserved; +} __packed; /* STATISTICS_RX_PHY_API_S_VER_2 */ + +struct mvm_statistics_rx_ht_phy { + __le32 plcp_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 crc32_err; + __le32 mh_format_err; + __le32 agg_crc32_good; + __le32 agg_mpdu_cnt; + __le32 agg_cnt; + __le32 unsupport_mcs; +} __packed; /* STATISTICS_HT_RX_PHY_API_S_VER_1 */ + +struct mvm_statistics_tx_non_phy { + __le32 preamble_cnt; + __le32 rx_detected_cnt; + __le32 bt_prio_defer_cnt; + __le32 bt_prio_kill_cnt; + __le32 few_bytes_cnt; + __le32 cts_timeout; + __le32 ack_timeout; + __le32 expected_ack_cnt; + __le32 actual_ack_cnt; + __le32 dump_msdu_cnt; + __le32 burst_abort_next_frame_mismatch_cnt; + __le32 burst_abort_missing_next_frame_cnt; + __le32 cts_timeout_collision; + __le32 ack_or_ba_timeout_collision; +} __packed; /* STATISTICS_TX_NON_PHY_API_S_VER_3 */ + +#define MAX_CHAINS 3 + +struct mvm_statistics_tx_non_phy_agg { + __le32 ba_timeout; + __le32 ba_reschedule_frames; + __le32 scd_query_agg_frame_cnt; + __le32 scd_query_no_agg; + __le32 scd_query_agg; + __le32 scd_query_mismatch; + __le32 frame_not_ready; + __le32 underrun; + __le32 bt_prio_kill; + __le32 rx_ba_rsp_cnt; + __s8 txpower[MAX_CHAINS]; + __s8 reserved; + __le32 reserved2; +} __packed; /* STATISTICS_TX_NON_PHY_AGG_API_S_VER_1 */ + +struct mvm_statistics_tx_channel_width { + __le32 ext_cca_narrow_ch20[1]; + __le32 ext_cca_narrow_ch40[2]; + __le32 ext_cca_narrow_ch80[3]; + __le32 ext_cca_narrow_ch160[4]; + __le32 last_tx_ch_width_indx; + __le32 rx_detected_per_ch_width[4]; + __le32 success_per_ch_width[4]; + __le32 fail_per_ch_width[4]; +}; /* STATISTICS_TX_CHANNEL_WIDTH_API_S_VER_1 */ + +struct mvm_statistics_tx { + struct mvm_statistics_tx_non_phy general; + struct mvm_statistics_tx_non_phy_agg agg; + struct mvm_statistics_tx_channel_width channel_width; +} __packed; /* STATISTICS_TX_API_S_VER_4 */ + + +struct mvm_statistics_bt_activity { + __le32 hi_priority_tx_req_cnt; + __le32 hi_priority_tx_denied_cnt; + __le32 lo_priority_tx_req_cnt; + __le32 lo_priority_tx_denied_cnt; + __le32 hi_priority_rx_req_cnt; + __le32 hi_priority_rx_denied_cnt; + __le32 lo_priority_rx_req_cnt; + __le32 lo_priority_rx_denied_cnt; +} __packed; /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */ + +struct mvm_statistics_general { + __le32 radio_temperature; + __le32 radio_voltage; + struct mvm_statistics_dbg dbg; + __le32 sleep_time; + __le32 slots_out; + __le32 slots_idle; + __le32 ttl_timestamp; + struct mvm_statistics_div slow_div; + __le32 rx_enable_counter; + /* + * num_of_sos_states: + * count the number of times we have to re-tune + * in order to get out of bad PHY status + */ + __le32 num_of_sos_states; + __le32 beacon_filtered; + __le32 missed_beacons; + __s8 beacon_filter_average_energy; + __s8 beacon_filter_reason; + __s8 beacon_filter_current_energy; + __s8 beacon_filter_reserved; + __le32 beacon_filter_delta_time; + struct mvm_statistics_bt_activity bt_activity; +} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */ + +struct mvm_statistics_rx { + struct mvm_statistics_rx_phy ofdm; + struct mvm_statistics_rx_phy cck; + struct mvm_statistics_rx_non_phy general; + struct mvm_statistics_rx_ht_phy ofdm_ht; +} __packed; /* STATISTICS_RX_API_S_VER_3 */ + +/* + * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command) + * + * By default, uCode issues this notification after receiving a beacon + * while associated. To disable this behavior, set DISABLE_NOTIF flag in the + * REPLY_STATISTICS_CMD 0x9c, above. + * + * Statistics counters continue to increment beacon after beacon, but are + * cleared when changing channels or when driver issues REPLY_STATISTICS_CMD + * 0x9c with CLEAR_STATS bit set (see above). + * + * uCode also issues this notification during scans. uCode clears statistics + * appropriately so that each notification contains statistics for only the + * one channel that has just been scanned. + */ + +struct iwl_notif_statistics { + __le32 flag; + struct mvm_statistics_rx rx; + struct mvm_statistics_tx tx; + struct mvm_statistics_general general; +} __packed; /* STATISTICS_NTFY_API_S_VER_8 */ + +#endif /* __fw_api_stats_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h index 5bca1f8bfebf..81c4ea3c6958 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-tx.h @@ -592,4 +592,43 @@ static inline u32 iwl_mvm_get_scd_ssn(struct iwl_mvm_tx_resp *tx_resp) tx_resp->frame_count) & 0xfff; } +/** + * struct iwl_scd_txq_cfg_cmd - New txq hw scheduler config command + * @token: + * @sta_id: station id + * @tid: + * @scd_queue: scheduler queue to confiug + * @enable: 1 queue enable, 0 queue disable + * @aggregate: 1 aggregated queue, 0 otherwise + * @tx_fifo: %enum iwl_mvm_tx_fifo + * @window: BA window size + * @ssn: SSN for the BA agreement + */ +struct iwl_scd_txq_cfg_cmd { + u8 token; + u8 sta_id; + u8 tid; + u8 scd_queue; + u8 enable; + u8 aggregate; + u8 tx_fifo; + u8 window; + __le16 ssn; + __le16 reserved; +} __packed; /* SCD_QUEUE_CFG_CMD_API_S_VER_1 */ + +/** + * struct iwl_scd_txq_cfg_rsp + * @token: taken from the command + * @sta_id: station id from the command + * @tid: tid from the command + * @scd_queue: scd_queue from the command + */ +struct iwl_scd_txq_cfg_rsp { + u8 token; + u8 sta_id; + u8 tid; + u8 scd_queue; +} __packed; /* SCD_QUEUE_CFG_RSP_API_S_VER_1 */ + #endif /* __fw_api_tx_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/iwlwifi/mvm/fw-api.h index 88af6dd2ceaa..b56154fe8ec5 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api.h @@ -74,6 +74,7 @@ #include "fw-api-d3.h" #include "fw-api-coex.h" #include "fw-api-scan.h" +#include "fw-api-stats.h" /* Tx queue numbers */ enum { @@ -128,6 +129,9 @@ enum { /* global key */ WEP_KEY = 0x20, + /* Memory */ + SHARED_MEM_CFG = 0x25, + /* TDLS */ TDLS_CHANNEL_SWITCH_CMD = 0x27, TDLS_CHANNEL_SWITCH_NOTIFICATION = 0xaa, @@ -1381,214 +1385,6 @@ struct iwl_mvm_marker { __le32 metadata[0]; } __packed; /* MARKER_API_S_VER_1 */ -struct mvm_statistics_dbg { - __le32 burst_check; - __le32 burst_count; - __le32 wait_for_silence_timeout_cnt; - __le32 reserved[3]; -} __packed; /* STATISTICS_DEBUG_API_S_VER_2 */ - -struct mvm_statistics_div { - __le32 tx_on_a; - __le32 tx_on_b; - __le32 exec_time; - __le32 probe_time; - __le32 rssi_ant; - __le32 reserved2; -} __packed; /* STATISTICS_SLOW_DIV_API_S_VER_2 */ - -struct mvm_statistics_general_common { - __le32 temperature; /* radio temperature */ - __le32 temperature_m; /* radio voltage */ - struct mvm_statistics_dbg dbg; - __le32 sleep_time; - __le32 slots_out; - __le32 slots_idle; - __le32 ttl_timestamp; - struct mvm_statistics_div div; - __le32 rx_enable_counter; - /* - * num_of_sos_states: - * count the number of times we have to re-tune - * in order to get out of bad PHY status - */ - __le32 num_of_sos_states; -} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */ - -struct mvm_statistics_rx_non_phy { - __le32 bogus_cts; /* CTS received when not expecting CTS */ - __le32 bogus_ack; /* ACK received when not expecting ACK */ - __le32 non_bssid_frames; /* number of frames with BSSID that - * doesn't belong to the STA BSSID */ - __le32 filtered_frames; /* count frames that were dumped in the - * filtering process */ - __le32 non_channel_beacons; /* beacons with our bss id but not on - * our serving channel */ - __le32 channel_beacons; /* beacons with our bss id and in our - * serving channel */ - __le32 num_missed_bcon; /* number of missed beacons */ - __le32 adc_rx_saturation_time; /* count in 0.8us units the time the - * ADC was in saturation */ - __le32 ina_detection_search_time;/* total time (in 0.8us) searched - * for INA */ - __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */ - __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */ - __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */ - __le32 interference_data_flag; /* flag for interference data - * availability. 1 when data is - * available. */ - __le32 channel_load; /* counts RX Enable time in uSec */ - __le32 dsp_false_alarms; /* DSP false alarm (both OFDM - * and CCK) counter */ - __le32 beacon_rssi_a; - __le32 beacon_rssi_b; - __le32 beacon_rssi_c; - __le32 beacon_energy_a; - __le32 beacon_energy_b; - __le32 beacon_energy_c; - __le32 num_bt_kills; - __le32 mac_id; - __le32 directed_data_mpdu; -} __packed; /* STATISTICS_RX_NON_PHY_API_S_VER_3 */ - -struct mvm_statistics_rx_phy { - __le32 ina_cnt; - __le32 fina_cnt; - __le32 plcp_err; - __le32 crc32_err; - __le32 overrun_err; - __le32 early_overrun_err; - __le32 crc32_good; - __le32 false_alarm_cnt; - __le32 fina_sync_err_cnt; - __le32 sfd_timeout; - __le32 fina_timeout; - __le32 unresponded_rts; - __le32 rxe_frame_limit_overrun; - __le32 sent_ack_cnt; - __le32 sent_cts_cnt; - __le32 sent_ba_rsp_cnt; - __le32 dsp_self_kill; - __le32 mh_format_err; - __le32 re_acq_main_rssi_sum; - __le32 reserved; -} __packed; /* STATISTICS_RX_PHY_API_S_VER_2 */ - -struct mvm_statistics_rx_ht_phy { - __le32 plcp_err; - __le32 overrun_err; - __le32 early_overrun_err; - __le32 crc32_good; - __le32 crc32_err; - __le32 mh_format_err; - __le32 agg_crc32_good; - __le32 agg_mpdu_cnt; - __le32 agg_cnt; - __le32 unsupport_mcs; -} __packed; /* STATISTICS_HT_RX_PHY_API_S_VER_1 */ - -#define MAX_CHAINS 3 - -struct mvm_statistics_tx_non_phy_agg { - __le32 ba_timeout; - __le32 ba_reschedule_frames; - __le32 scd_query_agg_frame_cnt; - __le32 scd_query_no_agg; - __le32 scd_query_agg; - __le32 scd_query_mismatch; - __le32 frame_not_ready; - __le32 underrun; - __le32 bt_prio_kill; - __le32 rx_ba_rsp_cnt; - __s8 txpower[MAX_CHAINS]; - __s8 reserved; - __le32 reserved2; -} __packed; /* STATISTICS_TX_NON_PHY_AGG_API_S_VER_1 */ - -struct mvm_statistics_tx_channel_width { - __le32 ext_cca_narrow_ch20[1]; - __le32 ext_cca_narrow_ch40[2]; - __le32 ext_cca_narrow_ch80[3]; - __le32 ext_cca_narrow_ch160[4]; - __le32 last_tx_ch_width_indx; - __le32 rx_detected_per_ch_width[4]; - __le32 success_per_ch_width[4]; - __le32 fail_per_ch_width[4]; -}; /* STATISTICS_TX_CHANNEL_WIDTH_API_S_VER_1 */ - -struct mvm_statistics_tx { - __le32 preamble_cnt; - __le32 rx_detected_cnt; - __le32 bt_prio_defer_cnt; - __le32 bt_prio_kill_cnt; - __le32 few_bytes_cnt; - __le32 cts_timeout; - __le32 ack_timeout; - __le32 expected_ack_cnt; - __le32 actual_ack_cnt; - __le32 dump_msdu_cnt; - __le32 burst_abort_next_frame_mismatch_cnt; - __le32 burst_abort_missing_next_frame_cnt; - __le32 cts_timeout_collision; - __le32 ack_or_ba_timeout_collision; - struct mvm_statistics_tx_non_phy_agg agg; - struct mvm_statistics_tx_channel_width channel_width; -} __packed; /* STATISTICS_TX_API_S_VER_4 */ - - -struct mvm_statistics_bt_activity { - __le32 hi_priority_tx_req_cnt; - __le32 hi_priority_tx_denied_cnt; - __le32 lo_priority_tx_req_cnt; - __le32 lo_priority_tx_denied_cnt; - __le32 hi_priority_rx_req_cnt; - __le32 hi_priority_rx_denied_cnt; - __le32 lo_priority_rx_req_cnt; - __le32 lo_priority_rx_denied_cnt; -} __packed; /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */ - -struct mvm_statistics_general { - struct mvm_statistics_general_common common; - __le32 beacon_filtered; - __le32 missed_beacons; - __s8 beacon_filter_average_energy; - __s8 beacon_filter_reason; - __s8 beacon_filter_current_energy; - __s8 beacon_filter_reserved; - __le32 beacon_filter_delta_time; - struct mvm_statistics_bt_activity bt_activity; -} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */ - -struct mvm_statistics_rx { - struct mvm_statistics_rx_phy ofdm; - struct mvm_statistics_rx_phy cck; - struct mvm_statistics_rx_non_phy general; - struct mvm_statistics_rx_ht_phy ofdm_ht; -} __packed; /* STATISTICS_RX_API_S_VER_3 */ - -/* - * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command) - * - * By default, uCode issues this notification after receiving a beacon - * while associated. To disable this behavior, set DISABLE_NOTIF flag in the - * REPLY_STATISTICS_CMD 0x9c, above. - * - * Statistics counters continue to increment beacon after beacon, but are - * cleared when changing channels or when driver issues REPLY_STATISTICS_CMD - * 0x9c with CLEAR_STATS bit set (see above). - * - * uCode also issues this notification during scans. uCode clears statistics - * appropriately so that each notification contains statistics for only the - * one channel that has just been scanned. - */ - -struct iwl_notif_statistics { /* STATISTICS_NTFY_API_S_VER_8 */ - __le32 flag; - struct mvm_statistics_rx rx; - struct mvm_statistics_tx tx; - struct mvm_statistics_general general; -} __packed; - /*********************************** * Smart Fifo API ***********************************/ @@ -1680,63 +1476,6 @@ struct iwl_dts_measurement_notif { __le32 voltage; } __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S */ -/** - * enum iwl_scd_control - scheduler config command control flags - * @IWL_SCD_CONTROL_RM_TID: remove TID from this queue - * @IWL_SCD_CONTROL_SET_SSN: use the SSN and program it into HW - */ -enum iwl_scd_control { - IWL_SCD_CONTROL_RM_TID = BIT(4), - IWL_SCD_CONTROL_SET_SSN = BIT(5), -}; - -/** - * enum iwl_scd_flags - scheduler config command flags - * @IWL_SCD_FLAGS_SHARE_TID: multiple TIDs map to this queue - * @IWL_SCD_FLAGS_SHARE_RA: multiple RAs map to this queue - * @IWL_SCD_FLAGS_DQA_ENABLED: DQA is enabled - */ -enum iwl_scd_flags { - IWL_SCD_FLAGS_SHARE_TID = BIT(0), - IWL_SCD_FLAGS_SHARE_RA = BIT(1), - IWL_SCD_FLAGS_DQA_ENABLED = BIT(2), -}; - -#define IWL_SCDQ_INVALID_STA 0xff - -/** - * struct iwl_scd_txq_cfg_cmd - New txq hw scheduler config command - * @token: dialog token addba - unused legacy - * @sta_id: station id 4-bit - * @tid: TID 0..7 - * @scd_queue: TFD queue num 0 .. 31 - * @enable: 1 queue enable, 0 queue disable - * @aggregate: 1 aggregated queue, 0 otherwise - * @tx_fifo: tx fifo num 0..7 - * @window: up to 64 - * @ssn: starting seq num 12-bit - * @control: command control flags - * @flags: flags - see &enum iwl_scd_flags - * - * Note that every time the command is sent, all parameters must - * be filled with the exception of - * - the SSN, which is only used with @IWL_SCD_CONTROL_SET_SSN - * - the window, which is only relevant when starting aggregation - */ -struct iwl_scd_txq_cfg_cmd { - u8 token; - u8 sta_id; - u8 tid; - u8 scd_queue; - u8 enable; - u8 aggregate; - u8 tx_fifo; - u8 window; - __le16 ssn; - u8 control; - u8 flags; -} __packed; - /*********************************** * TDLS API ***********************************/ @@ -1878,4 +1617,36 @@ struct iwl_tdls_config_res { struct iwl_tdls_config_sta_info_res sta_info[IWL_MVM_TDLS_STA_COUNT]; } __packed; /* TDLS_CONFIG_RSP_API_S_VER_1 */ +#define TX_FIFO_MAX_NUM 8 +#define RX_FIFO_MAX_NUM 2 + +/** + * Shared memory configuration information from the FW + * + * @shared_mem_addr: shared memory addr (pre 8000 HW set to 0x0 as MARBH is not + * accessible) + * @shared_mem_size: shared memory size + * @sample_buff_addr: internal sample (mon/adc) buff addr (pre 8000 HW set to + * 0x0 as accessible only via DBGM RDAT) + * @sample_buff_size: internal sample buff size + * @txfifo_addr: start addr of TXF0 (excluding the context table 0.5KB), (pre + * 8000 HW set to 0x0 as not accessible) + * @txfifo_size: size of TXF0 ... TXF7 + * @rxfifo_size: RXF1, RXF2 sizes. If there is no RXF2, it'll have a value of 0 + * @page_buff_addr: used by UMAC and performance debug (page miss analysis), + * when paging is not supported this should be 0 + * @page_buff_size: size of %page_buff_addr + */ +struct iwl_shared_mem_cfg { + __le32 shared_mem_addr; + __le32 shared_mem_size; + __le32 sample_buff_addr; + __le32 sample_buff_size; + __le32 txfifo_addr; + __le32 txfifo_size[TX_FIFO_MAX_NUM]; + __le32 rxfifo_size[RX_FIFO_MAX_NUM]; + __le32 page_buff_addr; + __le32 page_buff_size; +} __packed; /* SHARED_MEM_ALLOC_API_S_VER_1 */ + #endif /* __fw_api_h__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c index d0fa6e9ed590..ca38e9817374 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/iwlwifi/mvm/fw.c @@ -70,6 +70,7 @@ #include "iwl-debug.h" #include "iwl-csr.h" /* for iwl_mvm_rx_card_state_notif */ #include "iwl-io.h" /* for iwl_mvm_rx_card_state_notif */ +#include "iwl-prph.h" #include "iwl-eeprom-parse.h" #include "mvm.h" @@ -269,7 +270,7 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) enum iwl_ucode_type ucode_type = mvm->cur_ucode; /* Set parameters */ - phy_cfg_cmd.phy_cfg = cpu_to_le32(mvm->fw->phy_config); + phy_cfg_cmd.phy_cfg = cpu_to_le32(iwl_mvm_get_phy_config(mvm)); phy_cfg_cmd.calib_control.event_trigger = mvm->fw->default_calib[ucode_type].event_trigger; phy_cfg_cmd.calib_control.flow_trigger = @@ -346,7 +347,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) mvm->calibrating = true; /* Send TX valid antennas before triggering calibrations */ - ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant); + ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); if (ret) goto error; @@ -399,8 +400,71 @@ out: return ret; } -static int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, - enum iwl_fw_dbg_conf conf_id) +static void iwl_mvm_get_shared_mem_conf(struct iwl_mvm *mvm) +{ + struct iwl_host_cmd cmd = { + .id = SHARED_MEM_CFG, + .flags = CMD_WANT_SKB, + .data = { NULL, }, + .len = { 0, }, + }; + struct iwl_rx_packet *pkt; + struct iwl_shared_mem_cfg *mem_cfg; + u32 i; + + lockdep_assert_held(&mvm->mutex); + + if (WARN_ON(iwl_mvm_send_cmd(mvm, &cmd))) + return; + + pkt = cmd.resp_pkt; + if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { + IWL_ERR(mvm, "Bad return from SHARED_MEM_CFG (0x%08X)\n", + pkt->hdr.flags); + goto exit; + } + + mem_cfg = (void *)pkt->data; + + mvm->shared_mem_cfg.shared_mem_addr = + le32_to_cpu(mem_cfg->shared_mem_addr); + mvm->shared_mem_cfg.shared_mem_size = + le32_to_cpu(mem_cfg->shared_mem_size); + mvm->shared_mem_cfg.sample_buff_addr = + le32_to_cpu(mem_cfg->sample_buff_addr); + mvm->shared_mem_cfg.sample_buff_size = + le32_to_cpu(mem_cfg->sample_buff_size); + mvm->shared_mem_cfg.txfifo_addr = le32_to_cpu(mem_cfg->txfifo_addr); + for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) + mvm->shared_mem_cfg.txfifo_size[i] = + le32_to_cpu(mem_cfg->txfifo_size[i]); + for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) + mvm->shared_mem_cfg.rxfifo_size[i] = + le32_to_cpu(mem_cfg->rxfifo_size[i]); + mvm->shared_mem_cfg.page_buff_addr = + le32_to_cpu(mem_cfg->page_buff_addr); + mvm->shared_mem_cfg.page_buff_size = + le32_to_cpu(mem_cfg->page_buff_size); + IWL_DEBUG_INFO(mvm, "SHARED MEM CFG: got memory offsets/sizes\n"); + +exit: + iwl_free_resp(&cmd); +} + +void iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm) +{ + /* stop recording */ + if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) { + iwl_set_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100); + } else { + iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 0); + iwl_write_prph(mvm->trans, DBGC_OUT_CTRL, 0); + } + + schedule_work(&mvm->fw_error_dump_wk); +} + +int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, enum iwl_fw_dbg_conf conf_id) { u8 *ptr; int ret; @@ -435,6 +499,35 @@ static int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, return ret; } +static int iwl_mvm_config_ltr_v1(struct iwl_mvm *mvm) +{ + struct iwl_ltr_config_cmd_v1 cmd_v1 = { + .flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE), + }; + + if (!mvm->trans->ltr_enabled) + return 0; + + return iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0, + sizeof(cmd_v1), &cmd_v1); +} + +static int iwl_mvm_config_ltr(struct iwl_mvm *mvm) +{ + struct iwl_ltr_config_cmd cmd = { + .flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE), + }; + + if (!mvm->trans->ltr_enabled) + return 0; + + if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_HDC_PHASE_0)) + return iwl_mvm_config_ltr_v1(mvm); + + return iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0, + sizeof(cmd), &cmd); +} + int iwl_mvm_up(struct iwl_mvm *mvm) { int ret, i; @@ -482,6 +575,9 @@ int iwl_mvm_up(struct iwl_mvm *mvm) goto error; } + if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 10) + iwl_mvm_get_shared_mem_conf(mvm); + ret = iwl_mvm_sf_update(mvm, NULL, false); if (ret) IWL_ERR(mvm, "Failed to initialize Smart Fifo\n"); @@ -489,7 +585,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm) mvm->fw_dbg_conf = FW_DBG_INVALID; iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_CUSTOM); - ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant); + ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); if (ret) goto error; @@ -538,14 +634,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm) /* Initialize tx backoffs to the minimal possible */ iwl_mvm_tt_tx_backoff(mvm, 0); - if (mvm->trans->ltr_enabled) { - struct iwl_ltr_config_cmd cmd = { - .flags = cpu_to_le32(LTR_CFG_FLAG_FEATURE_ENABLE), - }; - - WARN_ON(iwl_mvm_send_cmd_pdu(mvm, LTR_CONFIG, 0, - sizeof(cmd), &cmd)); - } + WARN_ON(iwl_mvm_config_ltr(mvm)); ret = iwl_mvm_power_update_device(mvm); if (ret) @@ -584,7 +673,7 @@ int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm) goto error; } - ret = iwl_send_tx_ant_cfg(mvm, mvm->fw->valid_tx_ant); + ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); if (ret) goto error; diff --git a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c index f6d86ccce6a8..7bdc6220743f 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac-ctxt.c @@ -208,8 +208,10 @@ u32 iwl_mvm_mac_get_queues_mask(struct ieee80211_vif *vif) if (vif->type == NL80211_IFTYPE_P2P_DEVICE) return BIT(IWL_MVM_OFFCHANNEL_QUEUE); - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - qmask |= BIT(vif->hw_queue[ac]); + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + if (vif->hw_queue[ac] != IEEE80211_INVAL_HW_QUEUE) + qmask |= BIT(vif->hw_queue[ac]); + } if (vif->type == NL80211_IFTYPE_AP) qmask |= BIT(vif->cab_queue); @@ -460,6 +462,9 @@ exit_fail: int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { + unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ? + mvm->cfg->base_params->wd_timeout : + IWL_WATCHDOG_DISABLED; u32 ac; int ret; @@ -472,16 +477,17 @@ int iwl_mvm_mac_ctxt_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif) switch (vif->type) { case NL80211_IFTYPE_P2P_DEVICE: iwl_mvm_enable_ac_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE, - IWL_MVM_TX_FIFO_VO); + IWL_MVM_TX_FIFO_VO, wdg_timeout); break; case NL80211_IFTYPE_AP: iwl_mvm_enable_ac_txq(mvm, vif->cab_queue, - IWL_MVM_TX_FIFO_MCAST); + IWL_MVM_TX_FIFO_MCAST, wdg_timeout); /* fall through */ default: for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) iwl_mvm_enable_ac_txq(mvm, vif->hw_queue[ac], - iwl_mvm_ac_to_tx_fifo[ac]); + iwl_mvm_ac_to_tx_fifo[ac], + wdg_timeout); break; } @@ -496,14 +502,14 @@ void iwl_mvm_mac_ctxt_release(struct iwl_mvm *mvm, struct ieee80211_vif *vif) switch (vif->type) { case NL80211_IFTYPE_P2P_DEVICE: - iwl_mvm_disable_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE); + iwl_mvm_disable_txq(mvm, IWL_MVM_OFFCHANNEL_QUEUE, 0); break; case NL80211_IFTYPE_AP: - iwl_mvm_disable_txq(mvm, vif->cab_queue); + iwl_mvm_disable_txq(mvm, vif->cab_queue, 0); /* fall through */ default: for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) - iwl_mvm_disable_txq(mvm, vif->hw_queue[ac]); + iwl_mvm_disable_txq(mvm, vif->hw_queue[ac], 0); } } @@ -975,7 +981,7 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, beacon_cmd.tx.tx_flags = cpu_to_le32(tx_flags); mvm->mgmt_last_antenna_idx = - iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant, + iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), mvm->mgmt_last_antenna_idx); beacon_cmd.tx.rate_n_flags = diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c index e880f9d4717b..1ff7ec08532d 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c @@ -85,6 +85,7 @@ #include "testmode.h" #include "iwl-fw-error-dump.h" #include "iwl-prph.h" +#include "iwl-csr.h" static const struct ieee80211_iface_limit iwl_mvm_limits[] = { { @@ -105,7 +106,7 @@ static const struct ieee80211_iface_limit iwl_mvm_limits[] = { static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = { { - .num_different_channels = 1, + .num_different_channels = 2, .max_interfaces = 3, .limits = iwl_mvm_limits, .n_limits = ARRAY_SIZE(iwl_mvm_limits), @@ -326,6 +327,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC | IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED; hw->rate_control_algorithm = "iwl-mvm-rs"; + hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES; + hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; /* * Enable 11w if advertised by firmware and software crypto @@ -336,13 +339,6 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) !iwlwifi_mod_params.sw_crypto) hw->flags |= IEEE80211_HW_MFP_CAPABLE; - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT && - !iwlwifi_mod_params.uapsd_disable) { - hw->flags |= IEEE80211_HW_SUPPORTS_UAPSD; - hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES; - hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; - } - if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN || mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) { hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS; @@ -377,6 +373,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->wiphy->max_remain_on_channel_duration = 10000; hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; + /* we can compensate an offset of up to 3 channels = 15 MHz */ + hw->wiphy->max_adj_channel_rssi_comp = 3 * 5; /* Extract MAC address */ memcpy(mvm->addresses[0].addr, mvm->nvm_data->hw_addr, ETH_ALEN); @@ -403,10 +401,15 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) if (mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels) hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ]; - if (mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels) + if (mvm->nvm_data->bands[IEEE80211_BAND_5GHZ].n_channels) { hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ]; + if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_BEAMFORMER) + hw->wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap.cap |= + IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE; + } + hw->wiphy->hw_version = mvm->trans->hw_id; if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) @@ -459,15 +462,17 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) device_can_wakeup(mvm->trans->dev)) { mvm->wowlan.flags = WIPHY_WOWLAN_ANY; hw->wiphy->wowlan = &mvm->wowlan; - } else if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len && + } + + if (mvm->fw->img[IWL_UCODE_WOWLAN].sec[0].len && mvm->trans->ops->d3_suspend && mvm->trans->ops->d3_resume && device_can_wakeup(mvm->trans->dev)) { - mvm->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | - WIPHY_WOWLAN_DISCONNECT | - WIPHY_WOWLAN_EAP_IDENTITY_REQ | - WIPHY_WOWLAN_RFKILL_RELEASE | - WIPHY_WOWLAN_NET_DETECT; + mvm->wowlan.flags |= WIPHY_WOWLAN_MAGIC_PKT | + WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_EAP_IDENTITY_REQ | + WIPHY_WOWLAN_RFKILL_RELEASE | + WIPHY_WOWLAN_NET_DETECT; if (!iwlwifi_mod_params.sw_crypto) mvm->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | WIPHY_WOWLAN_GTK_REKEY_FAILURE | @@ -707,9 +712,6 @@ static void iwl_mvm_cleanup_iterator(void *data, u8 *mac, mvmvif->uploaded = false; mvmvif->ap_sta_id = IWL_MVM_STATION_COUNT; - /* does this make sense at all? */ - mvmvif->color++; - spin_lock_bh(&mvm->time_event_lock); iwl_mvm_te_clear_data(mvm, &mvmvif->time_event_data); spin_unlock_bh(&mvm->time_event_lock); @@ -761,41 +763,215 @@ static void iwl_mvm_free_coredump(const void *data) kfree(fw_error_dump); } +static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, + struct iwl_fw_error_dump_data **dump_data) +{ + struct iwl_fw_error_dump_fifo *fifo_hdr; + u32 *fifo_data; + u32 fifo_len; + unsigned long flags; + int i, j; + + if (!iwl_trans_grab_nic_access(mvm->trans, false, &flags)) + return; + + /* Pull RXF data from all RXFs */ + for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.rxfifo_size); i++) { + /* + * Keep aside the additional offset that might be needed for + * next RXF + */ + u32 offset_diff = RXF_DIFF_FROM_PREV * i; + + fifo_hdr = (void *)(*dump_data)->data; + fifo_data = (void *)fifo_hdr->data; + fifo_len = mvm->shared_mem_cfg.rxfifo_size[i]; + + /* No need to try to read the data if the length is 0 */ + if (fifo_len == 0) + continue; + + /* Add a TLV for the RXF */ + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); + (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); + + fifo_hdr->fifo_num = cpu_to_le32(i); + fifo_hdr->available_bytes = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + RXF_RD_D_SPACE + + offset_diff)); + fifo_hdr->wr_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + RXF_RD_WR_PTR + + offset_diff)); + fifo_hdr->rd_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + RXF_RD_RD_PTR + + offset_diff)); + fifo_hdr->fence_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + RXF_RD_FENCE_PTR + + offset_diff)); + fifo_hdr->fence_mode = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + RXF_SET_FENCE_MODE + + offset_diff)); + + /* Lock fence */ + iwl_trans_write_prph(mvm->trans, + RXF_SET_FENCE_MODE + offset_diff, 0x1); + /* Set fence pointer to the same place like WR pointer */ + iwl_trans_write_prph(mvm->trans, + RXF_LD_WR2FENCE + offset_diff, 0x1); + /* Set fence offset */ + iwl_trans_write_prph(mvm->trans, + RXF_LD_FENCE_OFFSET_ADDR + offset_diff, + 0x0); + + /* Read FIFO */ + fifo_len /= sizeof(u32); /* Size in DWORDS */ + for (j = 0; j < fifo_len; j++) + fifo_data[j] = iwl_trans_read_prph(mvm->trans, + RXF_FIFO_RD_FENCE_INC + + offset_diff); + *dump_data = iwl_fw_error_next_data(*dump_data); + } + + /* Pull TXF data from all TXFs */ + for (i = 0; i < ARRAY_SIZE(mvm->shared_mem_cfg.txfifo_size); i++) { + /* Mark the number of TXF we're pulling now */ + iwl_trans_write_prph(mvm->trans, TXF_LARC_NUM, i); + + fifo_hdr = (void *)(*dump_data)->data; + fifo_data = (void *)fifo_hdr->data; + fifo_len = mvm->shared_mem_cfg.txfifo_size[i]; + + /* No need to try to read the data if the length is 0 */ + if (fifo_len == 0) + continue; + + /* Add a TLV for the FIFO */ + (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_TXF); + (*dump_data)->len = cpu_to_le32(fifo_len + sizeof(*fifo_hdr)); + + fifo_hdr->fifo_num = cpu_to_le32(i); + fifo_hdr->available_bytes = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + TXF_FIFO_ITEM_CNT)); + fifo_hdr->wr_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + TXF_WR_PTR)); + fifo_hdr->rd_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + TXF_RD_PTR)); + fifo_hdr->fence_ptr = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + TXF_FENCE_PTR)); + fifo_hdr->fence_mode = + cpu_to_le32(iwl_trans_read_prph(mvm->trans, + TXF_LOCK_FENCE)); + + /* Set the TXF_READ_MODIFY_ADDR to TXF_WR_PTR */ + iwl_trans_write_prph(mvm->trans, TXF_READ_MODIFY_ADDR, + TXF_WR_PTR); + + /* Dummy-read to advance the read pointer to the head */ + iwl_trans_read_prph(mvm->trans, TXF_READ_MODIFY_DATA); + + /* Read FIFO */ + fifo_len /= sizeof(u32); /* Size in DWORDS */ + for (j = 0; j < fifo_len; j++) + fifo_data[j] = iwl_trans_read_prph(mvm->trans, + TXF_READ_MODIFY_DATA); + *dump_data = iwl_fw_error_next_data(*dump_data); + } + + iwl_trans_release_nic_access(mvm->trans, &flags); +} + void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) { struct iwl_fw_error_dump_file *dump_file; struct iwl_fw_error_dump_data *dump_data; struct iwl_fw_error_dump_info *dump_info; + struct iwl_fw_error_dump_mem *dump_mem; struct iwl_mvm_dump_ptrs *fw_error_dump; - const struct fw_img *img; u32 sram_len, sram_ofs; - u32 file_len, rxf_len; - unsigned long flags; - int reg_val; + u32 file_len, fifo_data_len = 0; + u32 smem_len = mvm->cfg->smem_len; + u32 sram2_len = mvm->cfg->dccm2_len; lockdep_assert_held(&mvm->mutex); + /* W/A for 8000 HW family A-step */ + if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_8000 && + CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_A_STEP) { + if (smem_len) + smem_len = 0x38000; + + if (sram2_len) + sram2_len = 0x10000; + } + fw_error_dump = kzalloc(sizeof(*fw_error_dump), GFP_KERNEL); if (!fw_error_dump) return; - img = &mvm->fw->img[mvm->cur_ucode]; - sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; - sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; + /* SRAM - include stack CCM if driver knows the values for it */ + if (!mvm->cfg->dccm_offset || !mvm->cfg->dccm_len) { + const struct fw_img *img; - /* reading buffer size */ - reg_val = iwl_trans_read_prph(mvm->trans, RXF_SIZE_ADDR); - rxf_len = (reg_val & RXF_SIZE_BYTE_CNT_MSK) >> RXF_SIZE_BYTE_CND_POS; + img = &mvm->fw->img[mvm->cur_ucode]; + sram_ofs = img->sec[IWL_UCODE_SECTION_DATA].offset; + sram_len = img->sec[IWL_UCODE_SECTION_DATA].len; + } else { + sram_ofs = mvm->cfg->dccm_offset; + sram_len = mvm->cfg->dccm_len; + } - /* the register holds the value divided by 128 */ - rxf_len = rxf_len << 7; + /* reading RXF/TXF sizes */ + if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) { + struct iwl_mvm_shared_mem_cfg *mem_cfg = &mvm->shared_mem_cfg; + int i; + + fifo_data_len = 0; + + /* Count RXF size */ + for (i = 0; i < ARRAY_SIZE(mem_cfg->rxfifo_size); i++) { + if (!mem_cfg->rxfifo_size[i]) + continue; + + /* Add header info */ + fifo_data_len += mem_cfg->rxfifo_size[i] + + sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_fifo); + } + + for (i = 0; i < ARRAY_SIZE(mem_cfg->txfifo_size); i++) { + if (!mem_cfg->txfifo_size[i]) + continue; + + /* Add header info */ + fifo_data_len += mem_cfg->txfifo_size[i] + + sizeof(*dump_data) + + sizeof(struct iwl_fw_error_dump_fifo); + } + } file_len = sizeof(*dump_file) + - sizeof(*dump_data) * 3 + - sram_len + - rxf_len + + sizeof(*dump_data) * 2 + + sram_len + sizeof(*dump_mem) + + fifo_data_len + sizeof(*dump_info); + /* Make room for the SMEM, if it exists */ + if (smem_len) + file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len; + + /* Make room for the secondary SRAM, if it exists */ + if (sram2_len) + file_len += sizeof(*dump_data) + sizeof(*dump_mem) + sram2_len; + dump_file = vzalloc(file_len); if (!dump_file) { kfree(fw_error_dump); @@ -814,6 +990,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000 ? cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_7) : cpu_to_le32(IWL_FW_ERROR_DUMP_FAMILY_8); + dump_info->hw_step = cpu_to_le32(CSR_HW_REV_STEP(mvm->trans->hw_rev)); memcpy(dump_info->fw_human_readable, mvm->fw->human_readable, sizeof(dump_info->fw_human_readable)); strncpy(dump_info->dev_human_readable, mvm->cfg->name, @@ -822,28 +999,39 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) sizeof(dump_info->bus_human_readable)); dump_data = iwl_fw_error_next_data(dump_data); - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_RXF); - dump_data->len = cpu_to_le32(rxf_len); - - if (iwl_trans_grab_nic_access(mvm->trans, false, &flags)) { - u32 *rxf = (void *)dump_data->data; - int i; + /* We only dump the FIFOs if the FW is in error state */ + if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) + iwl_mvm_dump_fifos(mvm, &dump_data); + + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem)); + dump_mem = (void *)dump_data->data; + dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); + dump_mem->offset = cpu_to_le32(sram_ofs); + iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_mem->data, + sram_len); - for (i = 0; i < (rxf_len / sizeof(u32)); i++) { - iwl_trans_write_prph(mvm->trans, - RXF_LD_FENCE_OFFSET_ADDR, - i * sizeof(u32)); - rxf[i] = iwl_trans_read_prph(mvm->trans, - RXF_FIFO_RD_FENCE_ADDR); - } - iwl_trans_release_nic_access(mvm->trans, &flags); + if (smem_len) { + dump_data = iwl_fw_error_next_data(dump_data); + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + dump_data->len = cpu_to_le32(smem_len + sizeof(*dump_mem)); + dump_mem = (void *)dump_data->data; + dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SMEM); + dump_mem->offset = cpu_to_le32(mvm->cfg->smem_offset); + iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->smem_offset, + dump_mem->data, smem_len); } - dump_data = iwl_fw_error_next_data(dump_data); - dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_SRAM); - dump_data->len = cpu_to_le32(sram_len); - iwl_trans_read_mem_bytes(mvm->trans, sram_ofs, dump_data->data, - sram_len); + if (sram2_len) { + dump_data = iwl_fw_error_next_data(dump_data); + dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); + dump_data->len = cpu_to_le32(sram2_len + sizeof(*dump_mem)); + dump_mem = (void *)dump_data->data; + dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_SRAM); + dump_mem->offset = cpu_to_le32(mvm->cfg->dccm2_offset); + iwl_trans_read_mem_bytes(mvm->trans, mvm->cfg->dccm2_offset, + dump_mem->data, sram2_len); + } fw_error_dump->trans_ptr = iwl_trans_dump_data(mvm->trans); fw_error_dump->op_mode_len = file_len; @@ -864,6 +1052,11 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status)) iwl_mvm_fw_error_dump(mvm); + /* cleanup all stale references (scan, roc), but keep the + * ucode_down ref until reconfig is complete + */ + iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN); + iwl_trans_stop_device(mvm->trans); mvm->scan_status = IWL_MVM_SCAN_NONE; @@ -893,10 +1086,6 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) ieee80211_wake_queues(mvm->hw); - /* cleanup all stale references (scan, roc), but keep the - * ucode_down ref until reconfig is complete */ - iwl_mvm_unref_all_except(mvm, IWL_MVM_REF_UCODE_DOWN); - /* clear any stale d0i3 state */ clear_bit(IWL_MVM_STATUS_IN_D0I3, &mvm->status); @@ -933,6 +1122,19 @@ static int iwl_mvm_mac_start(struct ieee80211_hw *hw) struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); int ret; + /* Some hw restart cleanups must not hold the mutex */ + if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { + /* + * Make sure we are out of d0i3. This is needed + * to make sure the reference accounting is correct + * (and there is no stale d0i3_exit_work). + */ + wait_event_timeout(mvm->d0i3_exit_waitq, + !test_bit(IWL_MVM_STATUS_IN_D0I3, + &mvm->status), + HZ); + } + mutex_lock(&mvm->mutex); ret = __iwl_mvm_mac_start(mvm); mutex_unlock(&mvm->mutex); @@ -982,6 +1184,13 @@ static void iwl_mvm_resume_complete(struct iwl_mvm *mvm) IWL_DEBUG_RPM(mvm, "Run deferred d0i3 exit\n"); _iwl_mvm_exit_d0i3(mvm); } + + if (mvm->trans->d0i3_mode == IWL_D0I3_MODE_ON_SUSPEND) + if (!wait_event_timeout(mvm->d0i3_exit_waitq, + !test_bit(IWL_MVM_STATUS_IN_D0I3, + &mvm->status), + HZ)) + WARN_ONCE(1, "D0i3 exit on resume timed out\n"); } static void @@ -1146,7 +1355,7 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, ret = iwl_mvm_power_update_mac(mvm); if (ret) - goto out_release; + goto out_remove_mac; /* beacon filtering */ ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); @@ -2088,7 +2297,7 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, struct ieee80211_sta *sta) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); /* * This is called before mac80211 does RCU synchronisation, @@ -2105,6 +2314,20 @@ static void iwl_mvm_sta_pre_rcu_remove(struct ieee80211_hw *hw, mutex_unlock(&mvm->mutex); } +static void iwl_mvm_check_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + const u8 *bssid) +{ + if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT)) + return; + + if (iwlwifi_mod_params.uapsd_disable) { + vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD; + return; + } + + vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; +} + static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, @@ -2164,6 +2387,7 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, * Reset EBS status here assuming environment has been changed. */ mvm->last_ebs_successful = true; + iwl_mvm_check_uapsd(mvm, vif, sta->addr); ret = 0; } else if (old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_ASSOC) { @@ -3103,7 +3327,7 @@ static int iwl_mvm_set_tim(struct ieee80211_hw *hw, bool set) { struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); if (!mvm_sta || !mvm_sta->vif) { IWL_ERR(mvm, "Station is not associated to a vif\n"); diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index d24660fb4ef2..6c69d0584f6c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h @@ -119,11 +119,13 @@ extern const struct ieee80211_ops iwl_mvm_hw_ops; * We will register to mac80211 to have testmode working. The NIC must not * be up'ed after the INIT fw asserted. This is useful to be able to use * proprietary tools over testmode to debug the INIT fw. + * @tfd_q_hang_detect: enabled the detection of hung transmit queues * @power_scheme: CAM(Continuous Active Mode)-1, BPS(Balanced Power * Save)-2(default), LP(Low Power)-3 */ struct iwl_mvm_mod_params { bool init_dbg; + bool tfd_q_hang_detect; int power_scheme; }; extern struct iwl_mvm_mod_params iwlmvm_mod_params; @@ -276,6 +278,7 @@ enum iwl_mvm_ref_type { IWL_MVM_REF_TM_CMD, IWL_MVM_REF_EXIT_WORK, IWL_MVM_REF_PROTECT_CSA, + IWL_MVM_REF_FW_DBG_COLLECT, /* update debugfs.c when changing this */ @@ -531,10 +534,23 @@ enum { enum iwl_mvm_tdls_cs_state { IWL_MVM_TDLS_SW_IDLE = 0, IWL_MVM_TDLS_SW_REQ_SENT, + IWL_MVM_TDLS_SW_RESP_RCVD, IWL_MVM_TDLS_SW_REQ_RCVD, IWL_MVM_TDLS_SW_ACTIVE, }; +struct iwl_mvm_shared_mem_cfg { + u32 shared_mem_addr; + u32 shared_mem_size; + u32 sample_buff_addr; + u32 sample_buff_size; + u32 txfifo_addr; + u32 txfifo_size[TX_FIFO_MAX_NUM]; + u32 rxfifo_size[RX_FIFO_MAX_NUM]; + u32 page_buff_addr; + u32 page_buff_size; +}; + struct iwl_mvm { /* for logger access */ struct device *dev; @@ -641,6 +657,8 @@ struct iwl_mvm { bool disable_power_off; bool disable_power_off_d3; + bool scan_iter_notif_enabled; + struct debugfs_blob_wrapper nvm_hw_blob; struct debugfs_blob_wrapper nvm_sw_blob; struct debugfs_blob_wrapper nvm_calib_blob; @@ -782,8 +800,13 @@ struct iwl_mvm { struct cfg80211_chan_def chandef; struct sk_buff *skb; /* ch sw template */ u32 ch_sw_tm_ie; + + /* timestamp of last ch-sw request sent (GP2 time) */ + u32 sent_timestamp; } peer; } tdls_cs; + + struct iwl_mvm_shared_mem_cfg shared_mem_cfg; }; /* Extract MVM priv from op_mode and _hw */ @@ -850,12 +873,14 @@ iwl_mvm_sta_from_staid_protected(struct iwl_mvm *mvm, u8 sta_id) static inline bool iwl_mvm_is_d0i3_supported(struct iwl_mvm *mvm) { return mvm->trans->cfg->d0i3 && + mvm->trans->d0i3_mode != IWL_D0I3_MODE_OFF && + !iwlwifi_mod_params.d0i3_disable && (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_D0I3_SUPPORT); } -static inline bool iwl_mvm_is_dqa_supported(struct iwl_mvm *mvm) +static inline bool iwl_mvm_is_scd_cfg_supported(struct iwl_mvm *mvm) { - return mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_DQA_SUPPORT; + return mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_SCD_CFG; } extern const u8 iwl_mvm_ac_to_tx_fifo[]; @@ -937,6 +962,33 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic); int iwl_mvm_load_nvm_to_nic(struct iwl_mvm *mvm); +static inline u8 iwl_mvm_get_valid_tx_ant(struct iwl_mvm *mvm) +{ + return mvm->nvm_data && mvm->nvm_data->valid_tx_ant ? + mvm->fw->valid_tx_ant & mvm->nvm_data->valid_tx_ant : + mvm->fw->valid_tx_ant; +} + +static inline u8 iwl_mvm_get_valid_rx_ant(struct iwl_mvm *mvm) +{ + return mvm->nvm_data && mvm->nvm_data->valid_rx_ant ? + mvm->fw->valid_rx_ant & mvm->nvm_data->valid_rx_ant : + mvm->fw->valid_rx_ant; +} + +static inline u32 iwl_mvm_get_phy_config(struct iwl_mvm *mvm) +{ + u32 phy_config = ~(FW_PHY_CFG_TX_CHAIN | + FW_PHY_CFG_RX_CHAIN); + u32 valid_rx_ant = iwl_mvm_get_valid_rx_ant(mvm); + u32 valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm); + + phy_config |= valid_tx_ant << FW_PHY_CFG_TX_CHAIN_POS | + valid_rx_ant << FW_PHY_CFG_RX_CHAIN_POS; + + return mvm->fw->phy_config & phy_config; +} + int iwl_mvm_up(struct iwl_mvm *mvm); int iwl_mvm_load_d3_fw(struct iwl_mvm *mvm); @@ -970,6 +1022,9 @@ int iwl_mvm_rx_radio_ver(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); int iwl_mvm_rx_mfuart_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); +int iwl_mvm_rx_shared_mem_cfg_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); /* MVM PHY */ int iwl_mvm_phy_ctxt_add(struct iwl_mvm *mvm, struct iwl_mvm_phy_ctxt *ctxt, @@ -1031,6 +1086,9 @@ int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm, bool is_sched_scan); int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd); +int iwl_mvm_rx_scan_offload_iter_complete_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd); int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct cfg80211_sched_scan_request *req, @@ -1091,9 +1149,7 @@ iwl_mvm_vif_dbgfs_clean(struct iwl_mvm *mvm, struct ieee80211_vif *vif) /* rate scaling */ int iwl_mvm_send_lq_cmd(struct iwl_mvm *mvm, struct iwl_lq_cmd *lq, bool init); -void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, - struct iwl_mvm_frame_stats *stats, - u32 rate, bool agg); +void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg); int rs_pretty_print_rate(char *buf, const u32 rate); void rs_update_last_rssi(struct iwl_mvm *mvm, struct iwl_lq_sta *lq_sta, @@ -1159,6 +1215,8 @@ void iwl_mvm_unref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); int iwl_mvm_ref_sync(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); bool iwl_mvm_ref_taken(struct iwl_mvm *mvm); void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq); +int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode); +int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode); int _iwl_mvm_exit_d0i3(struct iwl_mvm *mvm); /* BT Coex */ @@ -1260,11 +1318,13 @@ static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif) /* hw scheduler queue config */ void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, u16 ssn, - const struct iwl_trans_txq_scd_cfg *cfg); -void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue); + const struct iwl_trans_txq_scd_cfg *cfg, + unsigned int wdg_timeout); +void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, u8 flags); -static inline void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, - u8 fifo) +static inline +void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, + u8 fifo, unsigned int wdg_timeout) { struct iwl_trans_txq_scd_cfg cfg = { .fifo = fifo, @@ -1273,12 +1333,13 @@ static inline void iwl_mvm_enable_ac_txq(struct iwl_mvm *mvm, int queue, .frame_limit = IWL_FRAME_LIMIT, }; - iwl_mvm_enable_txq(mvm, queue, 0, &cfg); + iwl_mvm_enable_txq(mvm, queue, 0, &cfg, wdg_timeout); } static inline void iwl_mvm_enable_agg_txq(struct iwl_mvm *mvm, int queue, int fifo, int sta_id, int tid, - int frame_limit, u16 ssn) + int frame_limit, u16 ssn, + unsigned int wdg_timeout) { struct iwl_trans_txq_scd_cfg cfg = { .fifo = fifo, @@ -1288,7 +1349,7 @@ static inline void iwl_mvm_enable_agg_txq(struct iwl_mvm *mvm, int queue, .aggregate = true, }; - iwl_mvm_enable_txq(mvm, queue, ssn, &cfg); + iwl_mvm_enable_txq(mvm, queue, ssn, &cfg, wdg_timeout); } /* Assoc status */ @@ -1344,4 +1405,7 @@ struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm); void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error); void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm); +int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, enum iwl_fw_dbg_conf id); +void iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm); + #endif /* __IWL_MVM_H__ */ diff --git a/drivers/net/wireless/iwlwifi/mvm/nvm.c b/drivers/net/wireless/iwlwifi/mvm/nvm.c index d55fd8e3654c..5383429d96c1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/iwlwifi/mvm/nvm.c @@ -356,7 +356,7 @@ static int iwl_mvm_read_external_nvm(struct iwl_mvm *mvm) max_section_size = IWL_MAX_NVM_SECTION_SIZE; else if (CSR_HW_REV_STEP(mvm->trans->hw_rev) == SILICON_A_STEP) max_section_size = IWL_MAX_NVM_8000A_SECTION_SIZE; - else /* Family 8000 B-step */ + else /* Family 8000 B-step or C-step */ max_section_size = IWL_MAX_NVM_8000B_SECTION_SIZE; /* @@ -565,6 +565,8 @@ int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic) mvm->nvm_data = iwl_parse_nvm_sections(mvm); if (!mvm->nvm_data) return -ENODATA; + IWL_DEBUG_EEPROM(mvm->trans->dev, "nvm version = %x\n", + mvm->nvm_data->nvm_version); return 0; } diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 97dfba50c682..2dffc3600ed3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c @@ -84,15 +84,8 @@ #include "time-event.h" #include "iwl-fw-error-dump.h" -/* - * module name, copyright, version, etc. - */ #define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux" - -#define DRV_VERSION IWLWIFI_VERSION - MODULE_DESCRIPTION(DRV_DESCRIPTION); -MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); MODULE_LICENSE("GPL"); @@ -100,6 +93,7 @@ static const struct iwl_op_mode_ops iwl_mvm_ops; struct iwl_mvm_mod_params iwlmvm_mod_params = { .power_scheme = IWL_POWER_SCHEME_BPS, + .tfd_q_hang_detect = true /* rest of fields are 0 by default */ }; @@ -109,6 +103,10 @@ MODULE_PARM_DESC(init_dbg, module_param_named(power_scheme, iwlmvm_mod_params.power_scheme, int, S_IRUGO); MODULE_PARM_DESC(power_scheme, "power management scheme: 1-active, 2-balanced, 3-low power, default: 2"); +module_param_named(tfd_q_hang_detect, iwlmvm_mod_params.tfd_q_hang_detect, + bool, S_IRUGO); +MODULE_PARM_DESC(tfd_q_hang_detect, + "TFD queues hang detection (default: true"); /* * module init and exit functions @@ -146,13 +144,14 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode) struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); u8 radio_cfg_type, radio_cfg_step, radio_cfg_dash; u32 reg_val = 0; + u32 phy_config = iwl_mvm_get_phy_config(mvm); - radio_cfg_type = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_TYPE) >> - FW_PHY_CFG_RADIO_TYPE_POS; - radio_cfg_step = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_STEP) >> - FW_PHY_CFG_RADIO_STEP_POS; - radio_cfg_dash = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_DASH) >> - FW_PHY_CFG_RADIO_DASH_POS; + radio_cfg_type = (phy_config & FW_PHY_CFG_RADIO_TYPE) >> + FW_PHY_CFG_RADIO_TYPE_POS; + radio_cfg_step = (phy_config & FW_PHY_CFG_RADIO_STEP) >> + FW_PHY_CFG_RADIO_STEP_POS; + radio_cfg_dash = (phy_config & FW_PHY_CFG_RADIO_DASH) >> + FW_PHY_CFG_RADIO_DASH_POS; /* SKU control */ reg_val |= CSR_HW_REV_STEP(mvm->trans->hw_rev) << @@ -240,6 +239,8 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false), RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, true), + RX_HANDLER(SCAN_ITERATION_COMPLETE, + iwl_mvm_rx_scan_offload_iter_complete_notif, false), RX_HANDLER(SCAN_OFFLOAD_COMPLETE, iwl_mvm_rx_scan_offload_complete_notif, true), RX_HANDLER(MATCH_FOUND_NOTIFICATION, iwl_mvm_rx_scan_offload_results, @@ -274,6 +275,7 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = { CMD(MGMT_MCAST_KEY), CMD(TX_CMD), CMD(TXPATH_FLUSH), + CMD(SHARED_MEM_CFG), CMD(MAC_CONTEXT_CMD), CMD(TIME_EVENT_CMD), CMD(TIME_EVENT_NOTIFICATION), @@ -476,17 +478,19 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DW_BC_TABLE) trans_cfg.bc_table_dword = true; - if (!iwlwifi_mod_params.wd_disable) - trans_cfg.queue_watchdog_timeout = cfg->base_params->wd_timeout; - else - trans_cfg.queue_watchdog_timeout = IWL_WATCHDOG_DISABLED; - trans_cfg.command_names = iwl_mvm_cmd_strings; trans_cfg.cmd_queue = IWL_MVM_CMD_QUEUE; trans_cfg.cmd_fifo = IWL_MVM_TX_FIFO_CMD; trans_cfg.scd_set_active = true; + trans_cfg.sdio_adma_addr = fw->sdio_adma_addr; + + /* Set a short watchdog for the command queue */ + trans_cfg.cmd_q_wdg_timeout = + iwlmvm_mod_params.tfd_q_hang_detect ? IWL_DEF_WD_TIMEOUT : + IWL_WATCHDOG_DISABLED; + snprintf(mvm->hw->wiphy->fw_version, sizeof(mvm->hw->wiphy->fw_version), "%s", fw->fw_version); @@ -517,10 +521,15 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, min_backoff = calc_min_backoff(trans, cfg); iwl_mvm_tt_initialize(mvm, min_backoff); /* set the nvm_file_name according to priority */ - if (iwlwifi_mod_params.nvm_file) + if (iwlwifi_mod_params.nvm_file) { mvm->nvm_file_name = iwlwifi_mod_params.nvm_file; - else - mvm->nvm_file_name = mvm->cfg->default_nvm_file; + } else { + if ((trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) && + (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_A_STEP)) + mvm->nvm_file_name = mvm->cfg->default_nvm_file_8000A; + else + mvm->nvm_file_name = mvm->cfg->default_nvm_file; + } if (WARN(cfg->no_power_up_nic_in_init && !mvm->nvm_file_name, "not allowing power-up and not having nvm_file\n")) @@ -559,6 +568,9 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, if (!mvm->scan_cmd) goto out_free; + /* Set EBS as successful as long as not stated otherwise by the FW. */ + mvm->last_ebs_successful = true; + err = iwl_mvm_mac_setup_register(mvm); if (err) goto out_free; @@ -817,9 +829,20 @@ static void iwl_mvm_fw_error_dump_wk(struct work_struct *work) struct iwl_mvm *mvm = container_of(work, struct iwl_mvm, fw_error_dump_wk); + if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_FW_DBG_COLLECT)) + return; + mutex_lock(&mvm->mutex); iwl_mvm_fw_error_dump(mvm); + + /* start recording again if the firmware is not crashed */ + WARN_ON_ONCE((!test_bit(STATUS_FW_ERROR, &mvm->trans->status)) && + mvm->fw->dbg_dest_tlv && + iwl_mvm_start_fw_dbg_conf(mvm, mvm->fw_dbg_conf)); + mutex_unlock(&mvm->mutex); + + iwl_mvm_unref(mvm, IWL_MVM_REF_FW_DBG_COLLECT); } void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) @@ -855,7 +878,10 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) * If WoWLAN fw asserted, don't restart either, mac80211 * can't recover this since we're already half suspended. */ - if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status)) { + if (!mvm->restart_fw && fw_error) { + schedule_work(&mvm->fw_error_dump_wk); + } else if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART, + &mvm->status)) { struct iwl_mvm_reprobe *reprobe; IWL_ERR(mvm, @@ -879,16 +905,13 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) reprobe->dev = mvm->trans->dev; INIT_WORK(&reprobe->work, iwl_mvm_reprobe_wk); schedule_work(&reprobe->work); - } else if (mvm->cur_ucode == IWL_UCODE_REGULAR && - (!fw_error || mvm->restart_fw)) { + } else if (mvm->cur_ucode == IWL_UCODE_REGULAR) { /* don't let the transport/FW power down */ iwl_mvm_ref(mvm, IWL_MVM_REF_UCODE_DOWN); if (fw_error && mvm->restart_fw > 0) mvm->restart_fw--; ieee80211_restart_hw(mvm->hw); - } else if (fw_error) { - schedule_work(&mvm->fw_error_dump_wk); } } @@ -1031,7 +1054,8 @@ static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm, out: rcu_read_unlock(); } -static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) + +int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE; @@ -1047,6 +1071,7 @@ static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) }; struct iwl_d3_manager_config d3_cfg_cmd = { .min_sleep_time = cpu_to_le32(1000), + .wakeup_flags = cpu_to_le32(IWL_WAKEUP_D3_CONFIG_FW_ERROR), }; IWL_DEBUG_RPM(mvm, "MVM entering D0i3\n"); @@ -1146,7 +1171,7 @@ void iwl_mvm_d0i3_enable_tx(struct iwl_mvm *mvm, __le16 *qos_seq) if (mvm->d0i3_offloading && qos_seq) { /* update qos seq numbers if offloading was enabled */ - mvm_ap_sta = (struct iwl_mvm_sta *)sta->drv_priv; + mvm_ap_sta = iwl_mvm_sta_from_mac80211(sta); for (i = 0; i < IWL_MAX_TID_COUNT; i++) { u16 seq = le16_to_cpu(qos_seq[i]); /* firmware stores last-used one, we store next one */ @@ -1245,7 +1270,7 @@ out: return ret; } -static int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode) +int iwl_mvm_exit_d0i3(struct iwl_op_mode *op_mode) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); diff --git a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c index 1c0d4a45c1a8..5b43616eeb06 100644 --- a/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/iwlwifi/mvm/phy-ctxt.c @@ -170,13 +170,13 @@ static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm, active_cnt = 2; } - cmd->rxchain_info = cpu_to_le32(mvm->fw->valid_rx_ant << + cmd->rxchain_info = cpu_to_le32(iwl_mvm_get_valid_rx_ant(mvm) << PHY_RX_CHAIN_VALID_POS); cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS); cmd->rxchain_info |= cpu_to_le32(active_cnt << PHY_RX_CHAIN_MIMO_CNT_POS); - cmd->txchain_info = cpu_to_le32(mvm->fw->valid_tx_ant); + cmd->txchain_info = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm)); } /* diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.c b/drivers/net/wireless/iwlwifi/mvm/rs.c index 30ceb67ed7a7..194bd1f939ca 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/iwlwifi/mvm/rs.c @@ -39,28 +39,16 @@ #include "sta.h" #include "iwl-op-mode.h" #include "mvm.h" +#include "debugfs.h" #define RS_NAME "iwl-mvm-rs" -#define NUM_TRY_BEFORE_ANT_TOGGLE 1 -#define RS_LEGACY_RETRIES_PER_RATE 1 -#define RS_HT_VHT_RETRIES_PER_RATE 2 -#define RS_HT_VHT_RETRIES_PER_RATE_TW 1 -#define RS_INITIAL_MIMO_NUM_RATES 3 -#define RS_INITIAL_SISO_NUM_RATES 3 -#define RS_INITIAL_LEGACY_NUM_RATES LINK_QUAL_MAX_RETRY_NUM -#define RS_SECONDARY_LEGACY_NUM_RATES LINK_QUAL_MAX_RETRY_NUM -#define RS_SECONDARY_SISO_NUM_RATES 3 -#define RS_SECONDARY_SISO_RETRIES 1 - #define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */ -#define IWL_RATE_MIN_FAILURE_TH 3 /* min failures to calc tpt */ -#define IWL_RATE_MIN_SUCCESS_TH 8 /* min successes to calc tpt */ -/* max allowed rate miss before sync LQ cmd */ -#define IWL_MISSED_RATE_MAX 15 -#define RS_STAY_IN_COLUMN_TIMEOUT (5*HZ) -#define RS_IDLE_TIMEOUT (5*HZ) +/* Calculations of success ratio are done in fixed point where 12800 is 100%. + * Use this macro when dealing with thresholds consts set as a percentage + */ +#define RS_PERCENT(x) (128 * x) static u8 rs_ht_to_legacy[] = { [IWL_RATE_MCS_0_INDEX] = IWL_RATE_6M_INDEX, @@ -173,7 +161,7 @@ static bool rs_mimo_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, if (sta->smps_mode == IEEE80211_SMPS_STATIC) return false; - if (num_of_ant(mvm->fw->valid_tx_ant) < 2) + if (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) < 2) return false; if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) @@ -613,7 +601,8 @@ static s32 get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index) * at this rate. window->data contains the bitmask of successful * packets. */ -static int _rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, +static int _rs_collect_tx_data(struct iwl_mvm *mvm, + struct iwl_scale_tbl_info *tbl, int scale_index, int attempts, int successes, struct iwl_rate_scale_data *window) { @@ -668,8 +657,8 @@ static int _rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, fail_count = window->counter - window->success_counter; /* Calculate average throughput, if we have enough history. */ - if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) || - (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH)) + if ((fail_count >= IWL_MVM_RS_RATE_MIN_FAILURE_TH) || + (window->success_counter >= IWL_MVM_RS_RATE_MIN_SUCCESS_TH)) window->average_tpt = (window->success_ratio * tpt + 64) / 128; else window->average_tpt = IWL_INVALID_VALUE; @@ -677,7 +666,8 @@ static int _rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, return 0; } -static int rs_collect_tx_data(struct iwl_lq_sta *lq_sta, +static int rs_collect_tx_data(struct iwl_mvm *mvm, + struct iwl_lq_sta *lq_sta, struct iwl_scale_tbl_info *tbl, int scale_index, int attempts, int successes, u8 reduced_txp) @@ -698,7 +688,7 @@ static int rs_collect_tx_data(struct iwl_lq_sta *lq_sta, /* Select window for current tx bit rate */ window = &(tbl->win[scale_index]); - ret = _rs_collect_tx_data(tbl, scale_index, attempts, successes, + ret = _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes, window); if (ret) return ret; @@ -707,7 +697,7 @@ static int rs_collect_tx_data(struct iwl_lq_sta *lq_sta, return -EINVAL; window = &tbl->tpc_win[reduced_txp]; - return _rs_collect_tx_data(tbl, scale_index, attempts, successes, + return _rs_collect_tx_data(mvm, tbl, scale_index, attempts, successes, window); } @@ -928,7 +918,6 @@ static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask, break; if (rate_mask & (1 << low)) break; - IWL_DEBUG_RATE(mvm, "Skipping masked lower rate: %d\n", low); } high = index; @@ -938,7 +927,6 @@ static u16 rs_get_adjacent_rate(struct iwl_mvm *mvm, u8 index, u16 rate_mask, break; if (rate_mask & (1 << high)) break; - IWL_DEBUG_RATE(mvm, "Skipping masked higher rate: %d\n", high); } return (high << 8) | low; @@ -1004,7 +992,7 @@ static void rs_get_lower_rate_down_column(struct iwl_lq_sta *lq_sta, } if (num_of_ant(rate->ant) > 1) - rate->ant = first_antenna(mvm->fw->valid_tx_ant); + rate->ant = first_antenna(iwl_mvm_get_valid_tx_ant(mvm)); /* Relevant in both switching to SISO or Legacy */ rate->sgi = false; @@ -1125,7 +1113,8 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, } if (time_after(jiffies, - (unsigned long)(lq_sta->last_tx + RS_IDLE_TIMEOUT))) { + (unsigned long)(lq_sta->last_tx + + (IWL_MVM_RS_IDLE_TIMEOUT * HZ)))) { int t; IWL_DEBUG_RATE(mvm, "Tx idle for too long. reinit rs\n"); @@ -1158,7 +1147,7 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, * ... driver. */ lq_sta->missed_rate_counter++; - if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) { + if (lq_sta->missed_rate_counter > IWL_MVM_RS_MISSED_RATE_MAX) { lq_sta->missed_rate_counter = 0; IWL_DEBUG_RATE(mvm, "Too many rates mismatch. Send sync LQ. rs_state %d\n", @@ -1213,7 +1202,7 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, ucode_rate = le32_to_cpu(table->rs_table[0]); rs_rate_from_ucode_rate(ucode_rate, info->band, &rate); - rs_collect_tx_data(lq_sta, curr_tbl, rate.index, + rs_collect_tx_data(mvm, lq_sta, curr_tbl, rate.index, info->status.ampdu_len, info->status.ampdu_ack_len, reduced_txp); @@ -1249,7 +1238,7 @@ void iwl_mvm_rs_tx_status(struct iwl_mvm *mvm, struct ieee80211_sta *sta, else continue; - rs_collect_tx_data(lq_sta, tmp_tbl, rate.index, 1, + rs_collect_tx_data(mvm, lq_sta, tmp_tbl, rate.index, 1, i < retries ? 0 : legacy_success, reduced_txp); } @@ -1303,13 +1292,13 @@ static void rs_set_stay_in_table(struct iwl_mvm *mvm, u8 is_legacy, IWL_DEBUG_RATE(mvm, "Moving to RS_STATE_STAY_IN_COLUMN\n"); lq_sta->rs_state = RS_STATE_STAY_IN_COLUMN; if (is_legacy) { - lq_sta->table_count_limit = IWL_LEGACY_TABLE_COUNT; - lq_sta->max_failure_limit = IWL_LEGACY_FAILURE_LIMIT; - lq_sta->max_success_limit = IWL_LEGACY_SUCCESS_LIMIT; + lq_sta->table_count_limit = IWL_MVM_RS_LEGACY_TABLE_COUNT; + lq_sta->max_failure_limit = IWL_MVM_RS_LEGACY_FAILURE_LIMIT; + lq_sta->max_success_limit = IWL_MVM_RS_LEGACY_SUCCESS_LIMIT; } else { - lq_sta->table_count_limit = IWL_NONE_LEGACY_TABLE_COUNT; - lq_sta->max_failure_limit = IWL_NONE_LEGACY_FAILURE_LIMIT; - lq_sta->max_success_limit = IWL_NONE_LEGACY_SUCCESS_LIMIT; + lq_sta->table_count_limit = IWL_MVM_RS_NON_LEGACY_TABLE_COUNT; + lq_sta->max_failure_limit = IWL_MVM_RS_NON_LEGACY_FAILURE_LIMIT; + lq_sta->max_success_limit = IWL_MVM_RS_NON_LEGACY_SUCCESS_LIMIT; } lq_sta->table_count = 0; lq_sta->total_failed = 0; @@ -1318,6 +1307,13 @@ static void rs_set_stay_in_table(struct iwl_mvm *mvm, u8 is_legacy, lq_sta->visited_columns = 0; } +static inline int rs_get_max_rate_from_mask(unsigned long rate_mask) +{ + if (rate_mask) + return find_last_bit(&rate_mask, BITS_PER_LONG); + return IWL_RATE_INVALID; +} + static int rs_get_max_allowed_rate(struct iwl_lq_sta *lq_sta, const struct rs_tx_column *column) { @@ -1420,7 +1416,7 @@ static s32 rs_get_best_rate(struct iwl_mvm *mvm, u32 target_tpt; int rate_idx; - if (success_ratio > RS_SR_NO_DECREASE) { + if (success_ratio > IWL_MVM_RS_SR_NO_DECREASE) { target_tpt = 100 * expected_current_tpt; IWL_DEBUG_RATE(mvm, "SR %d high. Find rate exceeding EXPECTED_CURRENT %d\n", @@ -1488,7 +1484,7 @@ static void rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) flush_interval_passed = time_after(jiffies, (unsigned long)(lq_sta->flush_timer + - RS_STAY_IN_COLUMN_TIMEOUT)); + (IWL_MVM_RS_STAY_IN_COLUMN_TIMEOUT * HZ))); /* * Check if we should allow search for new modulation mode. @@ -1567,7 +1563,7 @@ static enum rs_column rs_get_next_column(struct iwl_mvm *mvm, const struct rs_tx_column *curr_col = &rs_tx_columns[tbl->column]; const struct rs_tx_column *next_col; allow_column_func_t allow_func; - u8 valid_ants = mvm->fw->valid_tx_ant; + u8 valid_ants = iwl_mvm_get_valid_tx_ant(mvm); const u16 *expected_tpt_tbl; u16 tpt, max_expected_tpt; @@ -1613,8 +1609,12 @@ static enum rs_column rs_get_next_column(struct iwl_mvm *mvm, continue; max_rate = rs_get_max_allowed_rate(lq_sta, next_col); - if (WARN_ON_ONCE(max_rate == IWL_RATE_INVALID)) + if (max_rate == IWL_RATE_INVALID) { + IWL_DEBUG_RATE(mvm, + "Skip column %d: no rate is allowed in this column\n", + next_col_id); continue; + } max_expected_tpt = expected_tpt_tbl[max_rate]; if (tpt >= max_expected_tpt) { @@ -1724,7 +1724,8 @@ static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm, { enum rs_action action = RS_ACTION_STAY; - if ((sr <= RS_SR_FORCE_DECREASE) || (current_tpt == 0)) { + if ((sr <= RS_PERCENT(IWL_MVM_RS_SR_FORCE_DECREASE)) || + (current_tpt == 0)) { IWL_DEBUG_RATE(mvm, "Decrease rate because of low SR\n"); return RS_ACTION_DOWNSCALE; @@ -1783,7 +1784,7 @@ static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm, out: if ((action == RS_ACTION_DOWNSCALE) && (low != IWL_RATE_INVALID)) { - if (sr >= RS_SR_NO_DECREASE) { + if (sr >= RS_PERCENT(IWL_MVM_RS_SR_NO_DECREASE)) { IWL_DEBUG_RATE(mvm, "SR is above NO DECREASE. Avoid downscale\n"); action = RS_ACTION_STAY; @@ -1802,18 +1803,10 @@ out: static bool rs_stbc_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta) { - struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); - struct ieee80211_vif *vif = mvmsta->vif; - bool sta_ps_disabled = (vif->type == NL80211_IFTYPE_STATION && - !vif->bss_conf.ps); - /* Our chip supports Tx STBC and the peer is an HT/VHT STA which * supports STBC of at least 1*SS */ - if (!lq_sta->stbc) - return false; - - if (!mvm->ps_disabled && !sta_ps_disabled) + if (!lq_sta->stbc_capable) return false; if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) @@ -1825,11 +1818,11 @@ static bool rs_stbc_allow(struct iwl_mvm *mvm, struct ieee80211_sta *sta, static void rs_get_adjacent_txp(struct iwl_mvm *mvm, int index, int *weaker, int *stronger) { - *weaker = index + TPC_TX_POWER_STEP; + *weaker = index + IWL_MVM_RS_TPC_TX_POWER_STEP; if (*weaker > TPC_MAX_REDUCTION) *weaker = TPC_INVALID; - *stronger = index - TPC_TX_POWER_STEP; + *stronger = index - IWL_MVM_RS_TPC_TX_POWER_STEP; if (*stronger < 0) *stronger = TPC_INVALID; } @@ -1885,7 +1878,8 @@ static enum tpc_action rs_get_tpc_action(struct iwl_mvm *mvm, } /* Too many failures, increase txp */ - if (sr <= TPC_SR_FORCE_INCREASE || current_tpt == 0) { + if (sr <= RS_PERCENT(IWL_MVM_RS_TPC_SR_FORCE_INCREASE) || + current_tpt == 0) { IWL_DEBUG_RATE(mvm, "increase txp because of weak SR\n"); return TPC_ACTION_NO_RESTIRCTION; } @@ -1908,7 +1902,8 @@ static enum tpc_action rs_get_tpc_action(struct iwl_mvm *mvm, } /* next, increase if needed */ - if (sr < TPC_SR_NO_INCREASE && strong != TPC_INVALID) { + if (sr < RS_PERCENT(IWL_MVM_RS_TPC_SR_NO_INCREASE) && + strong != TPC_INVALID) { if (weak_tpt == IWL_INVALID_VALUE && strong_tpt != IWL_INVALID_VALUE && current_tpt < strong_tpt) { @@ -1935,7 +1930,7 @@ static bool rs_tpc_perform(struct iwl_mvm *mvm, struct iwl_lq_sta *lq_sta, struct iwl_scale_tbl_info *tbl) { - struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); struct ieee80211_vif *vif = mvm_sta->vif; struct ieee80211_chanctx_conf *chanctx_conf; enum ieee80211_band band; @@ -2044,7 +2039,7 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, u16 high_low; s32 sr; u8 prev_agg = lq_sta->is_agg; - struct iwl_mvm_sta *sta_priv = (void *)sta->drv_priv; + struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data; struct rs_rate *rate; @@ -2106,8 +2101,8 @@ static void rs_rate_scale_perform(struct iwl_mvm *mvm, * in current association (use new rate found above). */ fail_count = window->counter - window->success_counter; - if ((fail_count < IWL_RATE_MIN_FAILURE_TH) && - (window->success_counter < IWL_RATE_MIN_SUCCESS_TH)) { + if ((fail_count < IWL_MVM_RS_RATE_MIN_FAILURE_TH) && + (window->success_counter < IWL_MVM_RS_RATE_MIN_SUCCESS_TH)) { IWL_DEBUG_RATE(mvm, "(%s: %d): Test Window: succ %d total %d\n", rs_pretty_lq_type(rate->type), @@ -2385,7 +2380,7 @@ static void rs_get_initial_rate(struct iwl_mvm *mvm, int i, nentries; s8 best_rssi = S8_MIN; u8 best_ant = ANT_NONE; - u8 valid_tx_ant = mvm->fw->valid_tx_ant; + u8 valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm); const struct rs_init_rate_info *initial_rates; for (i = 0; i < ARRAY_SIZE(lq_sta->pers.chain_signal); i++) { @@ -2530,7 +2525,7 @@ static void rs_get_rate(void *mvm_r, struct ieee80211_sta *sta, void *mvm_sta, static void *rs_alloc_sta(void *mvm_rate, struct ieee80211_sta *sta, gfp_t gfp) { - struct iwl_mvm_sta *sta_priv = (struct iwl_mvm_sta *)sta->drv_priv; + struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta); struct iwl_op_mode *op_mode = (struct iwl_op_mode *)mvm_rate; struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta; @@ -2606,68 +2601,121 @@ static void rs_vht_set_enabled_rates(struct ieee80211_sta *sta, } } +static void rs_ht_init(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + struct ieee80211_sta_ht_cap *ht_cap) +{ + /* active_siso_rate mask includes 9 MBits (bit 5), + * and CCK (bits 0-3), supp_rates[] does not; + * shift to convert format, force 9 MBits off. + */ + lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; + lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; + lq_sta->active_siso_rate &= ~((u16)0x2); + lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE; + + lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; + lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; + lq_sta->active_mimo2_rate &= ~((u16)0x2); + lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; + + if (mvm->cfg->ht_params->ldpc && + (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)) + lq_sta->ldpc = true; + + if (mvm->cfg->ht_params->stbc && + (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && + (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC)) + lq_sta->stbc_capable = true; + + lq_sta->is_vht = false; +} + +static void rs_vht_init(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + struct ieee80211_sta_vht_cap *vht_cap) +{ + rs_vht_set_enabled_rates(sta, vht_cap, lq_sta); + + if (mvm->cfg->ht_params->ldpc && + (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC)) + lq_sta->ldpc = true; + + if (mvm->cfg->ht_params->stbc && + (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && + (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK)) + lq_sta->stbc_capable = true; + + if ((mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_BEAMFORMER) && + (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && + (vht_cap->cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) + lq_sta->bfer_capable = true; + + lq_sta->is_vht = true; +} + #ifdef CONFIG_IWLWIFI_DEBUGFS -static void iwl_mvm_reset_frame_stats(struct iwl_mvm *mvm, - struct iwl_mvm_frame_stats *stats) +static void iwl_mvm_reset_frame_stats(struct iwl_mvm *mvm) { spin_lock_bh(&mvm->drv_stats_lock); - memset(stats, 0, sizeof(*stats)); + memset(&mvm->drv_rx_stats, 0, sizeof(mvm->drv_rx_stats)); spin_unlock_bh(&mvm->drv_stats_lock); } -void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, - struct iwl_mvm_frame_stats *stats, - u32 rate, bool agg) +void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg) { u8 nss = 0, mcs = 0; spin_lock(&mvm->drv_stats_lock); if (agg) - stats->agg_frames++; + mvm->drv_rx_stats.agg_frames++; - stats->success_frames++; + mvm->drv_rx_stats.success_frames++; switch (rate & RATE_MCS_CHAN_WIDTH_MSK) { case RATE_MCS_CHAN_WIDTH_20: - stats->bw_20_frames++; + mvm->drv_rx_stats.bw_20_frames++; break; case RATE_MCS_CHAN_WIDTH_40: - stats->bw_40_frames++; + mvm->drv_rx_stats.bw_40_frames++; break; case RATE_MCS_CHAN_WIDTH_80: - stats->bw_80_frames++; + mvm->drv_rx_stats.bw_80_frames++; break; default: WARN_ONCE(1, "bad BW. rate 0x%x", rate); } if (rate & RATE_MCS_HT_MSK) { - stats->ht_frames++; + mvm->drv_rx_stats.ht_frames++; mcs = rate & RATE_HT_MCS_RATE_CODE_MSK; nss = ((rate & RATE_HT_MCS_NSS_MSK) >> RATE_HT_MCS_NSS_POS) + 1; } else if (rate & RATE_MCS_VHT_MSK) { - stats->vht_frames++; + mvm->drv_rx_stats.vht_frames++; mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK; nss = ((rate & RATE_VHT_MCS_NSS_MSK) >> RATE_VHT_MCS_NSS_POS) + 1; } else { - stats->legacy_frames++; + mvm->drv_rx_stats.legacy_frames++; } if (nss == 1) - stats->siso_frames++; + mvm->drv_rx_stats.siso_frames++; else if (nss == 2) - stats->mimo2_frames++; + mvm->drv_rx_stats.mimo2_frames++; if (rate & RATE_MCS_SGI_MSK) - stats->sgi_frames++; + mvm->drv_rx_stats.sgi_frames++; else - stats->ngi_frames++; + mvm->drv_rx_stats.ngi_frames++; - stats->last_rates[stats->last_frame_idx] = rate; - stats->last_frame_idx = (stats->last_frame_idx + 1) % - ARRAY_SIZE(stats->last_rates); + mvm->drv_rx_stats.last_rates[mvm->drv_rx_stats.last_frame_idx] = rate; + mvm->drv_rx_stats.last_frame_idx = + (mvm->drv_rx_stats.last_frame_idx + 1) % + ARRAY_SIZE(mvm->drv_rx_stats.last_rates); spin_unlock(&mvm->drv_stats_lock); } @@ -2683,14 +2731,11 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct ieee80211_hw *hw = mvm->hw; struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap; - struct iwl_mvm_sta *sta_priv; - struct iwl_lq_sta *lq_sta; + struct iwl_mvm_sta *sta_priv = iwl_mvm_sta_from_mac80211(sta); + struct iwl_lq_sta *lq_sta = &sta_priv->lq_sta; struct ieee80211_supported_band *sband; unsigned long supp; /* must be unsigned long for for_each_set_bit */ - sta_priv = (struct iwl_mvm_sta *)sta->drv_priv; - lq_sta = &sta_priv->lq_sta; - /* clear all non-persistent lq data */ memset(lq_sta, 0, offsetof(typeof(*lq_sta), pers)); @@ -2712,7 +2757,7 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, * previous packets? Need to have IEEE 802.1X auth succeed immediately * after assoc.. */ - lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX; + lq_sta->missed_rate_counter = IWL_MVM_RS_MISSED_RATE_MAX; lq_sta->band = sband->band; /* * active legacy rates as per supported rates bitmap @@ -2723,61 +2768,28 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, lq_sta->active_legacy_rate |= BIT(sband->bitrates[i].hw_value); /* TODO: should probably account for rx_highest for both HT/VHT */ - if (!vht_cap || !vht_cap->vht_supported) { - /* active_siso_rate mask includes 9 MBits (bit 5), - * and CCK (bits 0-3), supp_rates[] does not; - * shift to convert format, force 9 MBits off. - */ - lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; - lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; - lq_sta->active_siso_rate &= ~((u16)0x2); - lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE; - - /* Same here */ - lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; - lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; - lq_sta->active_mimo2_rate &= ~((u16)0x2); - lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; - - lq_sta->is_vht = false; - if (mvm->cfg->ht_params->ldpc && - (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)) - lq_sta->ldpc = true; - - if (mvm->cfg->ht_params->stbc && - (num_of_ant(mvm->fw->valid_tx_ant) > 1) && - (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC)) - lq_sta->stbc = true; - } else { - rs_vht_set_enabled_rates(sta, vht_cap, lq_sta); - lq_sta->is_vht = true; - - if (mvm->cfg->ht_params->ldpc && - (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC)) - lq_sta->ldpc = true; - - if (mvm->cfg->ht_params->stbc && - (num_of_ant(mvm->fw->valid_tx_ant) > 1) && - (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK)) - lq_sta->stbc = true; - } + if (!vht_cap || !vht_cap->vht_supported) + rs_ht_init(mvm, sta, lq_sta, ht_cap); + else + rs_vht_init(mvm, sta, lq_sta, vht_cap); - if (IWL_MVM_RS_DISABLE_MIMO) + if (IWL_MVM_RS_DISABLE_P2P_MIMO && sta_priv->vif->p2p) lq_sta->active_mimo2_rate = 0; - lq_sta->max_legacy_rate_idx = find_last_bit(&lq_sta->active_legacy_rate, - BITS_PER_LONG); - lq_sta->max_siso_rate_idx = find_last_bit(&lq_sta->active_siso_rate, - BITS_PER_LONG); - lq_sta->max_mimo2_rate_idx = find_last_bit(&lq_sta->active_mimo2_rate, - BITS_PER_LONG); + lq_sta->max_legacy_rate_idx = + rs_get_max_rate_from_mask(lq_sta->active_legacy_rate); + lq_sta->max_siso_rate_idx = + rs_get_max_rate_from_mask(lq_sta->active_siso_rate); + lq_sta->max_mimo2_rate_idx = + rs_get_max_rate_from_mask(lq_sta->active_mimo2_rate); IWL_DEBUG_RATE(mvm, - "RATE MASK: LEGACY=%lX SISO=%lX MIMO2=%lX VHT=%d LDPC=%d STBC%d\n", + "LEGACY=%lX SISO=%lX MIMO2=%lX VHT=%d LDPC=%d STBC=%d BFER=%d\n", lq_sta->active_legacy_rate, lq_sta->active_siso_rate, lq_sta->active_mimo2_rate, - lq_sta->is_vht, lq_sta->ldpc, lq_sta->stbc); + lq_sta->is_vht, lq_sta->ldpc, lq_sta->stbc_capable, + lq_sta->bfer_capable); IWL_DEBUG_RATE(mvm, "MAX RATE: LEGACY=%d SISO=%d MIMO2=%d\n", lq_sta->max_legacy_rate_idx, lq_sta->max_siso_rate_idx, @@ -2785,14 +2797,14 @@ void iwl_mvm_rs_rate_init(struct iwl_mvm *mvm, struct ieee80211_sta *sta, /* These values will be overridden later */ lq_sta->lq.single_stream_ant_msk = - first_antenna(mvm->fw->valid_tx_ant); + first_antenna(iwl_mvm_get_valid_tx_ant(mvm)); lq_sta->lq.dual_stream_ant_msk = ANT_AB; /* as default allow aggregation for all tids */ lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID; lq_sta->is_agg = 0; #ifdef CONFIG_IWLWIFI_DEBUGFS - iwl_mvm_reset_frame_stats(mvm, &mvm->drv_rx_stats); + iwl_mvm_reset_frame_stats(mvm); #endif rs_initialize_lq(mvm, sta, lq_sta, band, init); } @@ -2861,12 +2873,13 @@ static void rs_fill_rates_for_column(struct iwl_mvm *mvm, int index = *rs_table_index; for (i = 0; i < num_rates && index < end; i++) { - ucode_rate = cpu_to_le32(ucode_rate_from_rs_rate(mvm, rate)); - for (j = 0; j < num_retries && index < end; j++, index++) + for (j = 0; j < num_retries && index < end; j++, index++) { + ucode_rate = cpu_to_le32(ucode_rate_from_rs_rate(mvm, + rate)); rs_table[index] = ucode_rate; - - if (toggle_ant) - rs_toggle_antenna(valid_tx_ant, rate); + if (toggle_ant) + rs_toggle_antenna(valid_tx_ant, rate); + } prev_rate_idx = rate->index; bottom_reached = rs_get_lower_rate_in_column(lq_sta, rate); @@ -2874,7 +2887,7 @@ static void rs_fill_rates_for_column(struct iwl_mvm *mvm, break; } - if (!bottom_reached) + if (!bottom_reached && !is_legacy(rate)) rate->index = prev_rate_idx; *rs_table_index = index; @@ -2913,18 +2926,22 @@ static void rs_build_rates_table(struct iwl_mvm *mvm, memcpy(&rate, initial_rate, sizeof(rate)); - valid_tx_ant = mvm->fw->valid_tx_ant; - rate.stbc = rs_stbc_allow(mvm, sta, lq_sta); + valid_tx_ant = iwl_mvm_get_valid_tx_ant(mvm); + + /* TODO: remove old API when min FW API hits 14 */ + if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LQ_SS_PARAMS) && + rs_stbc_allow(mvm, sta, lq_sta)) + rate.stbc = true; if (is_siso(&rate)) { - num_rates = RS_INITIAL_SISO_NUM_RATES; - num_retries = RS_HT_VHT_RETRIES_PER_RATE; + num_rates = IWL_MVM_RS_INITIAL_SISO_NUM_RATES; + num_retries = IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE; } else if (is_mimo(&rate)) { - num_rates = RS_INITIAL_MIMO_NUM_RATES; - num_retries = RS_HT_VHT_RETRIES_PER_RATE; + num_rates = IWL_MVM_RS_INITIAL_MIMO_NUM_RATES; + num_retries = IWL_MVM_RS_HT_VHT_RETRIES_PER_RATE; } else { - num_rates = RS_INITIAL_LEGACY_NUM_RATES; - num_retries = RS_LEGACY_RETRIES_PER_RATE; + num_rates = IWL_MVM_RS_INITIAL_LEGACY_NUM_RATES; + num_retries = IWL_MVM_RS_INITIAL_LEGACY_RETRIES; toggle_ant = true; } @@ -2935,12 +2952,12 @@ static void rs_build_rates_table(struct iwl_mvm *mvm, rs_get_lower_rate_down_column(lq_sta, &rate); if (is_siso(&rate)) { - num_rates = RS_SECONDARY_SISO_NUM_RATES; - num_retries = RS_SECONDARY_SISO_RETRIES; + num_rates = IWL_MVM_RS_SECONDARY_SISO_NUM_RATES; + num_retries = IWL_MVM_RS_SECONDARY_SISO_RETRIES; lq_cmd->mimo_delim = index; } else if (is_legacy(&rate)) { - num_rates = RS_SECONDARY_LEGACY_NUM_RATES; - num_retries = RS_LEGACY_RETRIES_PER_RATE; + num_rates = IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES; + num_retries = IWL_MVM_RS_SECONDARY_LEGACY_RETRIES; } else { WARN_ON_ONCE(1); } @@ -2953,8 +2970,8 @@ static void rs_build_rates_table(struct iwl_mvm *mvm, rs_get_lower_rate_down_column(lq_sta, &rate); - num_rates = RS_SECONDARY_LEGACY_NUM_RATES; - num_retries = RS_LEGACY_RETRIES_PER_RATE; + num_rates = IWL_MVM_RS_SECONDARY_LEGACY_NUM_RATES; + num_retries = IWL_MVM_RS_SECONDARY_LEGACY_RETRIES; rs_fill_rates_for_column(mvm, lq_sta, &rate, lq_cmd->rs_table, &index, num_rates, num_retries, valid_tx_ant, @@ -2962,6 +2979,142 @@ static void rs_build_rates_table(struct iwl_mvm *mvm, } +struct rs_bfer_active_iter_data { + struct ieee80211_sta *exclude_sta; + struct iwl_mvm_sta *bfer_mvmsta; +}; + +static void rs_bfer_active_iter(void *_data, + struct ieee80211_sta *sta) +{ + struct rs_bfer_active_iter_data *data = _data; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct iwl_lq_cmd *lq_cmd = &mvmsta->lq_sta.lq; + u32 ss_params = le32_to_cpu(lq_cmd->ss_params); + + if (sta == data->exclude_sta) + return; + + /* The current sta has BFER allowed */ + if (ss_params & LQ_SS_BFER_ALLOWED) { + WARN_ON_ONCE(data->bfer_mvmsta != NULL); + + data->bfer_mvmsta = mvmsta; + } +} + +static int rs_bfer_priority(struct iwl_mvm_sta *sta) +{ + int prio = -1; + enum nl80211_iftype viftype = ieee80211_vif_type_p2p(sta->vif); + + switch (viftype) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_P2P_GO: + prio = 3; + break; + case NL80211_IFTYPE_P2P_CLIENT: + prio = 2; + break; + case NL80211_IFTYPE_STATION: + prio = 1; + break; + default: + WARN_ONCE(true, "viftype %d sta_id %d", viftype, sta->sta_id); + prio = -1; + } + + return prio; +} + +/* Returns >0 if sta1 has a higher BFER priority compared to sta2 */ +static int rs_bfer_priority_cmp(struct iwl_mvm_sta *sta1, + struct iwl_mvm_sta *sta2) +{ + int prio1 = rs_bfer_priority(sta1); + int prio2 = rs_bfer_priority(sta2); + + if (prio1 > prio2) + return 1; + if (prio1 < prio2) + return -1; + return 0; +} + +static void rs_set_lq_ss_params(struct iwl_mvm *mvm, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta, + const struct rs_rate *initial_rate) +{ + struct iwl_lq_cmd *lq_cmd = &lq_sta->lq; + struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct rs_bfer_active_iter_data data = { + .exclude_sta = sta, + .bfer_mvmsta = NULL, + }; + struct iwl_mvm_sta *bfer_mvmsta = NULL; + u32 ss_params = LQ_SS_PARAMS_VALID; + + if (!iwl_mvm_bt_coex_is_mimo_allowed(mvm, sta)) + goto out; + + /* Check if forcing the decision is configured. + * Note that SISO is forced by not allowing STBC or BFER + */ + if (lq_sta->ss_force == RS_SS_FORCE_STBC) + ss_params |= (LQ_SS_STBC_1SS_ALLOWED | LQ_SS_FORCE); + else if (lq_sta->ss_force == RS_SS_FORCE_BFER) + ss_params |= (LQ_SS_BFER_ALLOWED | LQ_SS_FORCE); + + if (lq_sta->ss_force != RS_SS_FORCE_NONE) { + IWL_DEBUG_RATE(mvm, "Forcing single stream Tx decision %d\n", + lq_sta->ss_force); + goto out; + } + + if (lq_sta->stbc_capable) + ss_params |= LQ_SS_STBC_1SS_ALLOWED; + + if (!lq_sta->bfer_capable) + goto out; + + ieee80211_iterate_stations_atomic(mvm->hw, + rs_bfer_active_iter, + &data); + bfer_mvmsta = data.bfer_mvmsta; + + /* This code is safe as it doesn't run concurrently for different + * stations. This is guaranteed by the fact that calls to + * ieee80211_tx_status wouldn't run concurrently for a single HW. + */ + if (!bfer_mvmsta) { + IWL_DEBUG_RATE(mvm, "No sta with BFER allowed found. Allow\n"); + + ss_params |= LQ_SS_BFER_ALLOWED; + goto out; + } + + IWL_DEBUG_RATE(mvm, "Found existing sta %d with BFER activated\n", + bfer_mvmsta->sta_id); + + /* Disallow BFER on another STA if active and we're a higher priority */ + if (rs_bfer_priority_cmp(mvmsta, bfer_mvmsta) > 0) { + struct iwl_lq_cmd *bfersta_lq_cmd = &bfer_mvmsta->lq_sta.lq; + u32 bfersta_ss_params = le32_to_cpu(bfersta_lq_cmd->ss_params); + + bfersta_ss_params &= ~LQ_SS_BFER_ALLOWED; + bfersta_lq_cmd->ss_params = cpu_to_le32(bfersta_ss_params); + iwl_mvm_send_lq_cmd(mvm, bfersta_lq_cmd, false); + + ss_params |= LQ_SS_BFER_ALLOWED; + IWL_DEBUG_RATE(mvm, + "Lower priority BFER sta found (%d). Switch BFER\n", + bfer_mvmsta->sta_id); + } +out: + lq_cmd->ss_params = cpu_to_le32(ss_params); +} + static void rs_fill_lq_cmd(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct iwl_lq_sta *lq_sta, @@ -2971,9 +3124,9 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta; struct iwl_mvm_vif *mvmvif; - lq_cmd->agg_disable_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; + lq_cmd->agg_disable_start_th = IWL_MVM_RS_AGG_DISABLE_START; lq_cmd->agg_time_limit = - cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); + cpu_to_le16(IWL_MVM_RS_AGG_TIME_LIMIT); #ifdef CONFIG_MAC80211_DEBUGFS if (lq_sta->pers.dbg_fixed_rate) { @@ -2988,6 +3141,9 @@ static void rs_fill_lq_cmd(struct iwl_mvm *mvm, rs_build_rates_table(mvm, sta, lq_sta, initial_rate); + if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LQ_SS_PARAMS) + rs_set_lq_ss_params(mvm, sta, lq_sta, initial_rate); + if (num_of_ant(initial_rate->ant) == 1) lq_cmd->single_stream_ant_msk = initial_rate->ant; @@ -3167,9 +3323,9 @@ static ssize_t rs_sta_dbgfs_scale_table_read(struct file *file, desc += sprintf(buff+desc, "fixed rate 0x%X\n", lq_sta->pers.dbg_fixed_rate); desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n", - (mvm->fw->valid_tx_ant & ANT_A) ? "ANT_A," : "", - (mvm->fw->valid_tx_ant & ANT_B) ? "ANT_B," : "", - (mvm->fw->valid_tx_ant & ANT_C) ? "ANT_C" : ""); + (iwl_mvm_get_valid_tx_ant(mvm) & ANT_A) ? "ANT_A," : "", + (iwl_mvm_get_valid_tx_ant(mvm) & ANT_B) ? "ANT_B," : "", + (iwl_mvm_get_valid_tx_ant(mvm) & ANT_C) ? "ANT_C" : ""); desc += sprintf(buff+desc, "lq type %s\n", (is_legacy(rate)) ? "legacy" : is_vht(rate) ? "VHT" : "HT"); @@ -3361,9 +3517,73 @@ static const struct file_operations rs_sta_dbgfs_drv_tx_stats_ops = { .llseek = default_llseek, }; +static ssize_t iwl_dbgfs_ss_force_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_lq_sta *lq_sta = file->private_data; + char buf[12]; + int bufsz = sizeof(buf); + int pos = 0; + static const char * const ss_force_name[] = { + [RS_SS_FORCE_NONE] = "none", + [RS_SS_FORCE_STBC] = "stbc", + [RS_SS_FORCE_BFER] = "bfer", + [RS_SS_FORCE_SISO] = "siso", + }; + + pos += scnprintf(buf+pos, bufsz-pos, "%s\n", + ss_force_name[lq_sta->ss_force]); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_dbgfs_ss_force_write(struct iwl_lq_sta *lq_sta, char *buf, + size_t count, loff_t *ppos) +{ + struct iwl_mvm *mvm = lq_sta->pers.drv; + int ret = 0; + + if (!strncmp("none", buf, 4)) { + lq_sta->ss_force = RS_SS_FORCE_NONE; + } else if (!strncmp("siso", buf, 4)) { + lq_sta->ss_force = RS_SS_FORCE_SISO; + } else if (!strncmp("stbc", buf, 4)) { + if (lq_sta->stbc_capable) { + lq_sta->ss_force = RS_SS_FORCE_STBC; + } else { + IWL_ERR(mvm, + "can't force STBC. peer doesn't support\n"); + ret = -EINVAL; + } + } else if (!strncmp("bfer", buf, 4)) { + if (lq_sta->bfer_capable) { + lq_sta->ss_force = RS_SS_FORCE_BFER; + } else { + IWL_ERR(mvm, + "can't force BFER. peer doesn't support\n"); + ret = -EINVAL; + } + } else { + IWL_ERR(mvm, "valid values none|siso|stbc|bfer\n"); + ret = -EINVAL; + } + return ret ?: count; +} + +#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ + _MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, struct iwl_lq_sta) +#define MVM_DEBUGFS_ADD_FILE_RS(name, parent, mode) do { \ + if (!debugfs_create_file(#name, mode, parent, lq_sta, \ + &iwl_dbgfs_##name##_ops)) \ + goto err; \ + } while (0) + +MVM_DEBUGFS_READ_WRITE_FILE_OPS(ss_force, 32); + static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir) { struct iwl_lq_sta *lq_sta = mvm_sta; + debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, lq_sta, &rs_sta_dbgfs_scale_table_ops); debugfs_create_file("rate_stats_table", S_IRUSR, dir, @@ -3374,6 +3594,11 @@ static void rs_add_debugfs(void *mvm, void *mvm_sta, struct dentry *dir) &lq_sta->tx_agg_tid_en); debugfs_create_u8("reduced_tpc", S_IRUSR | S_IWUSR, dir, &lq_sta->pers.dbg_fixed_txp_reduction); + + MVM_DEBUGFS_ADD_FILE_RS(ss_force, dir, S_IRUSR | S_IWUSR); + return; +err: + IWL_ERR((struct iwl_mvm *)mvm, "Can't create debugfs entity\n"); } static void rs_remove_debugfs(void *mvm, void *mvm_sta) diff --git a/drivers/net/wireless/iwlwifi/mvm/rs.h b/drivers/net/wireless/iwlwifi/mvm/rs.h index defd70a6d9e6..dc4ef3dfafe1 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/iwlwifi/mvm/rs.h @@ -137,42 +137,10 @@ enum { #define IWL_INVALID_VALUE -1 -#define IWL_MIN_RSSI_VAL -100 -#define IWL_MAX_RSSI_VAL 0 - -/* These values specify how many Tx frame attempts before - * searching for a new modulation mode */ -#define IWL_LEGACY_FAILURE_LIMIT 160 -#define IWL_LEGACY_SUCCESS_LIMIT 480 -#define IWL_LEGACY_TABLE_COUNT 160 - -#define IWL_NONE_LEGACY_FAILURE_LIMIT 400 -#define IWL_NONE_LEGACY_SUCCESS_LIMIT 4500 -#define IWL_NONE_LEGACY_TABLE_COUNT 1500 - -/* Success ratio (ACKed / attempted tx frames) values (perfect is 128 * 100) */ -#define IWL_RS_GOOD_RATIO 12800 /* 100% */ -#define IWL_RATE_SCALE_SWITCH 10880 /* 85% */ -#define IWL_RATE_HIGH_TH 10880 /* 85% */ -#define IWL_RATE_INCREASE_TH 6400 /* 50% */ -#define RS_SR_FORCE_DECREASE 1920 /* 15% */ -#define RS_SR_NO_DECREASE 10880 /* 85% */ - -#define TPC_SR_FORCE_INCREASE 9600 /* 75% */ -#define TPC_SR_NO_INCREASE 10880 /* 85% */ -#define TPC_TX_POWER_STEP 3 #define TPC_MAX_REDUCTION 15 #define TPC_NO_REDUCTION 0 #define TPC_INVALID 0xff -#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */ -#define LINK_QUAL_AGG_TIME_LIMIT_MAX (8000) -#define LINK_QUAL_AGG_TIME_LIMIT_MIN (100) - -#define LINK_QUAL_AGG_DISABLE_START_DEF (3) -#define LINK_QUAL_AGG_DISABLE_START_MAX (255) -#define LINK_QUAL_AGG_DISABLE_START_MIN (0) - #define LINK_QUAL_AGG_FRAME_LIMIT_DEF (63) #define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63) #define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0) @@ -181,14 +149,7 @@ enum { /* load per tid defines for A-MPDU activation */ #define IWL_AGG_TPT_THREHOLD 0 -#define IWL_AGG_LOAD_THRESHOLD 10 #define IWL_AGG_ALL_TID 0xff -#define TID_QUEUE_CELL_SPACING 50 /*mS */ -#define TID_QUEUE_MAX_SIZE 20 -#define TID_ROUND_VALUE 5 /* mS */ - -#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING) -#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y)) enum iwl_table_type { LQ_NONE, @@ -279,6 +240,13 @@ enum rs_column { RS_COLUMN_INVALID, }; +enum rs_ss_force_opt { + RS_SS_FORCE_NONE = 0, + RS_SS_FORCE_STBC, + RS_SS_FORCE_BFER, + RS_SS_FORCE_SISO, +}; + /* Packet stats per rate */ struct rs_rate_stats { u64 success; @@ -332,7 +300,9 @@ struct iwl_lq_sta { u64 last_tx; bool is_vht; bool ldpc; /* LDPC Rx is supported by the STA */ - bool stbc; /* Tx STBC is supported by chip and Rx by STA */ + bool stbc_capable; /* Tx STBC is supported by chip and Rx by STA */ + bool bfer_capable; /* Remote supports beamformee and we BFer */ + enum ieee80211_band band; /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ @@ -361,6 +331,9 @@ struct iwl_lq_sta { /* tx power reduce for this sta */ int tpc_reduce; + /* force STBC/BFER/SISO for testing */ + enum rs_ss_force_opt ss_force; + /* persistent fields - initialized only once - keep last! */ struct lq_sta_pers { #ifdef CONFIG_MAC80211_DEBUGFS diff --git a/drivers/net/wireless/iwlwifi/mvm/rx.c b/drivers/net/wireless/iwlwifi/mvm/rx.c index 94b6e7297a1e..f922131b4eab 100644 --- a/drivers/net/wireless/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/iwlwifi/mvm/rx.c @@ -407,7 +407,7 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, } #ifdef CONFIG_IWLWIFI_DEBUGFS - iwl_mvm_update_frame_stats(mvm, &mvm->drv_rx_stats, rate_n_flags, + iwl_mvm_update_frame_stats(mvm, rate_n_flags, rx_status->flag & RX_FLAG_AMPDU_DETAILS); #endif iwl_mvm_pass_packet_to_mac80211(mvm, skb, hdr, len, ampdu_status, @@ -511,13 +511,17 @@ int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_notif_statistics *stats = (void *)&pkt->data; - struct mvm_statistics_general_common *common = &stats->general.common; struct iwl_mvm_stat_data data = { .stats = stats, .mvm = mvm, }; - iwl_mvm_tt_temp_changed(mvm, le32_to_cpu(common->temperature)); + /* Only handle rx statistics temperature changes if async temp + * notifications are not supported + */ + if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_ASYNC_DTM)) + iwl_mvm_tt_temp_changed(mvm, + le32_to_cpu(stats->general.radio_temperature)); iwl_mvm_update_rx_statistics(mvm, stats); diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index e5294d01181e..7e9aa3cb3254 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -72,6 +72,8 @@ #define IWL_PLCP_QUIET_THRESH 1 #define IWL_ACTIVE_QUIET_TIME 10 +#define IWL_DENSE_EBS_SCAN_RATIO 5 +#define IWL_SPARSE_EBS_SCAN_RATIO 1 struct iwl_mvm_scan_params { u32 max_out_time; @@ -97,7 +99,7 @@ static u8 iwl_mvm_scan_rx_ant(struct iwl_mvm *mvm) { if (mvm->scan_rx_ant != ANT_NONE) return mvm->scan_rx_ant; - return mvm->fw->valid_rx_ant; + return iwl_mvm_get_valid_rx_ant(mvm); } static inline __le16 iwl_mvm_scan_rx_chain(struct iwl_mvm *mvm) @@ -128,7 +130,7 @@ iwl_mvm_scan_rate_n_flags(struct iwl_mvm *mvm, enum ieee80211_band band, u32 tx_ant; mvm->scan_last_antenna_idx = - iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant, + iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), mvm->scan_last_antenna_idx); tx_ant = BIT(mvm->scan_last_antenna_idx) << RATE_MCS_ANT_POS; @@ -171,15 +173,21 @@ static void iwl_mvm_scan_fill_ssids(struct iwl_ssid_ie *cmd_ssid, * already included in the probe template, so we need to set only * req->n_ssids - 1 bits in addition to the first bit. */ -static u16 iwl_mvm_get_active_dwell(enum ieee80211_band band, int n_ssids) +static u16 iwl_mvm_get_active_dwell(struct iwl_mvm *mvm, + enum ieee80211_band band, int n_ssids) { + if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BASIC_DWELL) + return 10; if (band == IEEE80211_BAND_2GHZ) return 20 + 3 * (n_ssids + 1); return 10 + 2 * (n_ssids + 1); } -static u16 iwl_mvm_get_passive_dwell(enum ieee80211_band band) +static u16 iwl_mvm_get_passive_dwell(struct iwl_mvm *mvm, + enum ieee80211_band band) { + if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BASIC_DWELL) + return 110; return band == IEEE80211_BAND_2GHZ ? 100 + 20 : 100 + 10; } @@ -282,11 +290,11 @@ static void iwl_mvm_scan_condition_iterator(void *data, u8 *mac, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - bool *global_bound = data; + int *global_cnt = data; if (vif->type != NL80211_IFTYPE_P2P_DEVICE && mvmvif->phy_ctxt && mvmvif->phy_ctxt->id < MAX_PHYS) - *global_bound = true; + *global_cnt += 1; } static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm, @@ -294,27 +302,31 @@ static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm, int n_ssids, u32 flags, struct iwl_mvm_scan_params *params) { - bool global_bound = false; + int global_cnt = 0; enum ieee80211_band band; u8 frag_passive_dwell = 0; ieee80211_iterate_active_interfaces_atomic(mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_scan_condition_iterator, - &global_bound); + &global_cnt); - if (!global_bound) + if (!global_cnt) goto not_bound; params->suspend_time = 30; - params->max_out_time = 170; + params->max_out_time = 120; if (iwl_mvm_low_latency(mvm)) { if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_FRAGMENTED_SCAN) { params->suspend_time = 105; - params->max_out_time = 70; - frag_passive_dwell = 20; + /* + * If there is more than one active interface make + * passive scan more fragmented. + */ + frag_passive_dwell = (global_cnt < 2) ? 40 : 20; + params->max_out_time = frag_passive_dwell; } else { params->suspend_time = 120; params->max_out_time = 120; @@ -331,7 +343,8 @@ static void iwl_mvm_scan_calc_params(struct iwl_mvm *mvm, */ if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { u32 passive_dwell = - iwl_mvm_get_passive_dwell(IEEE80211_BAND_2GHZ); + iwl_mvm_get_passive_dwell(mvm, + IEEE80211_BAND_2GHZ); params->max_out_time = passive_dwell; } else { params->passive_fragmented = true; @@ -348,8 +361,8 @@ not_bound: params->dwell[band].passive = frag_passive_dwell; else params->dwell[band].passive = - iwl_mvm_get_passive_dwell(band); - params->dwell[band].active = iwl_mvm_get_active_dwell(band, + iwl_mvm_get_passive_dwell(mvm, band); + params->dwell[band].active = iwl_mvm_get_active_dwell(mvm, band, n_ssids); } } @@ -530,6 +543,19 @@ int iwl_mvm_rx_scan_response(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, return 0; } +int iwl_mvm_rx_scan_offload_iter_complete_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_scan_complete_notif *notif = (void *)pkt->data; + + IWL_DEBUG_SCAN(mvm, + "Scan offload iteration complete: status=0x%x scanned channels=%d\n", + notif->status, notif->scanned_channels); + return 0; +} + int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd) { @@ -678,7 +704,8 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm, iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); } - mvm->last_ebs_successful = !ebs_status; + if (ebs_status) + mvm->last_ebs_successful = false; return 0; } @@ -1098,6 +1125,12 @@ int iwl_mvm_scan_offload_stop(struct iwl_mvm *mvm, bool notify) return iwl_umac_scan_stop(mvm, IWL_UMAC_SCAN_UID_SCHED_SCAN, notify); + if (mvm->scan_status == IWL_MVM_SCAN_NONE) + return 0; + + if (iwl_mvm_is_radio_killed(mvm)) + goto out; + if (mvm->scan_status != IWL_MVM_SCAN_SCHED && (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) || mvm->scan_status != IWL_MVM_SCAN_OS)) { @@ -1134,6 +1167,7 @@ int iwl_mvm_scan_offload_stop(struct iwl_mvm *mvm, bool notify) if (mvm->scan_status == IWL_MVM_SCAN_OS) iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); +out: mvm->scan_status = IWL_MVM_SCAN_NONE; if (notify) { @@ -1290,18 +1324,6 @@ iwl_mvm_build_generic_unified_scan_cmd(struct iwl_mvm *mvm, cmd->scan_prio = cpu_to_le32(IWL_SCAN_PRIORITY_HIGH); cmd->iter_num = cpu_to_le32(1); - if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT && - mvm->last_ebs_successful) { - cmd->channel_opt[0].flags = - cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | - IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | - IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); - cmd->channel_opt[1].flags = - cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | - IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | - IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); - } - if (iwl_mvm_rrm_scan_needed(mvm)) cmd->scan_flags |= cpu_to_le32(IWL_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED); @@ -1376,6 +1398,22 @@ int iwl_mvm_unified_scan_lmac(struct iwl_mvm *mvm, cmd->schedule[1].iterations = 0; cmd->schedule[1].full_scan_mul = 0; + if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_SINGLE_SCAN_EBS && + mvm->last_ebs_successful) { + cmd->channel_opt[0].flags = + cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); + cmd->channel_opt[0].non_ebs_ratio = + cpu_to_le16(IWL_DENSE_EBS_SCAN_RATIO); + cmd->channel_opt[1].flags = + cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); + cmd->channel_opt[1].non_ebs_ratio = + cpu_to_le16(IWL_SPARSE_EBS_SCAN_RATIO); + } + for (i = 1; i <= req->req.n_ssids; i++) ssid_bitmap |= BIT(i); @@ -1448,6 +1486,8 @@ int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm, if (iwl_mvm_scan_pass_all(mvm, req)) flags |= IWL_MVM_LMAC_SCAN_FLAG_PASS_ALL; + else + flags |= IWL_MVM_LMAC_SCAN_FLAG_MATCH; if (req->n_ssids == 1 && req->ssids[0].ssid_len != 0) flags |= IWL_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION; @@ -1458,6 +1498,11 @@ int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm, if (req->n_ssids == 0) flags |= IWL_MVM_LMAC_SCAN_FLAG_PASSIVE; +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mvm->scan_iter_notif_enabled) + flags |= IWL_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE; +#endif + cmd->scan_flags |= cpu_to_le32(flags); cmd->flags = iwl_mvm_scan_rxon_flags(req->channels[0]->band); @@ -1474,6 +1519,22 @@ int iwl_mvm_unified_sched_scan_lmac(struct iwl_mvm *mvm, cmd->schedule[1].iterations = 0xff; cmd->schedule[1].full_scan_mul = IWL_FULL_SCAN_MULTIPLIER; + if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT && + mvm->last_ebs_successful) { + cmd->channel_opt[0].flags = + cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); + cmd->channel_opt[0].non_ebs_ratio = + cpu_to_le16(IWL_DENSE_EBS_SCAN_RATIO); + cmd->channel_opt[1].flags = + cpu_to_le16(IWL_SCAN_CHANNEL_FLAG_EBS | + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD); + cmd->channel_opt[1].non_ebs_ratio = + cpu_to_le16(IWL_SPARSE_EBS_SCAN_RATIO); + } + iwl_mvm_lmac_scan_cfg_channels(mvm, req->channels, req->n_channels, ssid_bitmap, cmd); @@ -1603,7 +1664,7 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) SCAN_CONFIG_FLAG_SET_MAC_ADDR | SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS| SCAN_CONFIG_N_CHANNELS(num_channels)); - scan_config->tx_chains = cpu_to_le32(mvm->fw->valid_tx_ant); + scan_config->tx_chains = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm)); scan_config->rx_chains = cpu_to_le32(iwl_mvm_scan_rx_ant(mvm)); scan_config->legacy_rates = iwl_mvm_scan_config_rates(mvm); scan_config->out_of_channel_time = cpu_to_le32(170); @@ -1622,10 +1683,10 @@ int iwl_mvm_config_scan(struct iwl_mvm *mvm) band = &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ]; for (i = 0; i < band->n_channels; i++, j++) - scan_config->channel_array[j] = band->channels[i].center_freq; + scan_config->channel_array[j] = band->channels[i].hw_value; band = &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ]; for (i = 0; i < band->n_channels; i++, j++) - scan_config->channel_array[j] = band->channels[i].center_freq; + scan_config->channel_array[j] = band->channels[i].hw_value; cmd.data[0] = scan_config; cmd.len[0] = cmd_size; @@ -1802,6 +1863,13 @@ int iwl_mvm_scan_umac(struct iwl_mvm *mvm, struct ieee80211_vif *vif, flags |= IWL_UMAC_SCAN_GEN_FLAGS_PASS_ALL; cmd->general_flags = cpu_to_le32(flags); + + if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_SINGLE_SCAN_EBS && + mvm->last_ebs_successful) + cmd->channel_flags = IWL_SCAN_CHANNEL_FLAG_EBS | + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD; + cmd->n_channels = req->req.n_channels; for (i = 0; i < req->req.n_ssids; i++) @@ -1965,7 +2033,9 @@ int iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm, notif->ebs_status == IWL_SCAN_EBS_SUCCESS ? "success" : "failed"); - mvm->last_ebs_successful = !notif->ebs_status; + if (notif->ebs_status) + mvm->last_ebs_successful = false; + mvm->scan_uid[uid_idx] = 0; if (!sched) { @@ -1998,10 +2068,14 @@ static bool iwl_scan_umac_done_check(struct iwl_notif_wait_data *notif_wait, /* * Clear scan uid of scans that was aborted from above and completed - * in FW so the RX handler does nothing. + * in FW so the RX handler does nothing. Set last_ebs_successful here if + * needed. */ scan_done->mvm->scan_uid[uid_idx] = 0; + if (notif->ebs_status) + scan_done->mvm->last_ebs_successful = false; + return !iwl_mvm_find_scan_type(scan_done->mvm, scan_done->type); } diff --git a/drivers/net/wireless/iwlwifi/mvm/sta.c b/drivers/net/wireless/iwlwifi/mvm/sta.c index d86fe432e51f..5c23cddaaae3 100644 --- a/drivers/net/wireless/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/iwlwifi/mvm/sta.c @@ -99,7 +99,7 @@ static int iwl_mvm_find_free_sta_id(struct iwl_mvm *mvm, int iwl_mvm_sta_send_to_fw(struct iwl_mvm *mvm, struct ieee80211_sta *sta, bool update) { - struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_add_sta_cmd add_sta_cmd = { .sta_id = mvm_sta->sta_id, .mac_id_n_color = cpu_to_le32(mvm_sta->mac_id_n_color), @@ -209,6 +209,9 @@ static int iwl_mvm_tdls_sta_init(struct iwl_mvm *mvm, { unsigned long used_hw_queues; struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ? + mvm->cfg->base_params->wd_timeout : + IWL_WATCHDOG_DISABLED; u32 ac; lockdep_assert_held(&mvm->mutex); @@ -232,7 +235,7 @@ static int iwl_mvm_tdls_sta_init(struct iwl_mvm *mvm, /* Found a place for all queues - enable them */ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { iwl_mvm_enable_ac_txq(mvm, mvmsta->hw_queue[ac], - iwl_mvm_ac_to_tx_fifo[ac]); + iwl_mvm_ac_to_tx_fifo[ac], wdg_timeout); mvmsta->tfd_queue_msk |= BIT(mvmsta->hw_queue[ac]); } @@ -250,8 +253,8 @@ static void iwl_mvm_tdls_sta_deinit(struct iwl_mvm *mvm, /* disable the TDLS STA-specific queues */ sta_msk = mvmsta->tfd_queue_msk; - for_each_set_bit(i, &sta_msk, sizeof(sta_msk)) - iwl_mvm_disable_txq(mvm, i); + for_each_set_bit(i, &sta_msk, sizeof(sta_msk) * BITS_PER_BYTE) + iwl_mvm_disable_txq(mvm, i, 0); } int iwl_mvm_add_sta(struct iwl_mvm *mvm, @@ -259,7 +262,7 @@ int iwl_mvm_add_sta(struct iwl_mvm *mvm, struct ieee80211_sta *sta) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); int i, ret, sta_id; lockdep_assert_held(&mvm->mutex); @@ -464,8 +467,8 @@ void iwl_mvm_sta_drained_wk(struct work_struct *wk) if (mvm->tfd_drained[sta_id]) { unsigned long i, msk = mvm->tfd_drained[sta_id]; - for_each_set_bit(i, &msk, sizeof(msk)) - iwl_mvm_disable_txq(mvm, i); + for_each_set_bit(i, &msk, sizeof(msk) * BITS_PER_BYTE) + iwl_mvm_disable_txq(mvm, i, 0); mvm->tfd_drained[sta_id] = 0; IWL_DEBUG_TDLS(mvm, "Drained sta %d, with queues %ld\n", @@ -481,7 +484,7 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, struct ieee80211_sta *sta) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); int ret; lockdep_assert_held(&mvm->mutex); @@ -626,13 +629,16 @@ static int iwl_mvm_add_int_sta_common(struct iwl_mvm *mvm, int iwl_mvm_add_aux_sta(struct iwl_mvm *mvm) { + unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ? + mvm->cfg->base_params->wd_timeout : + IWL_WATCHDOG_DISABLED; int ret; lockdep_assert_held(&mvm->mutex); /* Map Aux queue to fifo - needs to happen before adding Aux station */ iwl_mvm_enable_ac_txq(mvm, mvm->aux_queue, - IWL_MVM_TX_FIFO_MCAST); + IWL_MVM_TX_FIFO_MCAST, wdg_timeout); /* Allocate aux station and assign to it the aux queue */ ret = iwl_mvm_allocate_int_sta(mvm, &mvm->aux_sta, BIT(mvm->aux_queue), @@ -774,7 +780,7 @@ int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, int tid, u16 ssn, bool start) { - struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_add_sta_cmd cmd = {}; int ret; u32 status; @@ -834,7 +840,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, static int iwl_mvm_sta_tx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, int tid, u8 queue, bool start) { - struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_add_sta_cmd cmd = {}; int ret; u32 status; @@ -965,6 +971,9 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; + unsigned int wdg_timeout = iwlmvm_mod_params.tfd_q_hang_detect ? + mvm->cfg->base_params->wd_timeout : + IWL_WATCHDOG_DISABLED; int queue, fifo, ret; u16 ssn; @@ -988,7 +997,7 @@ int iwl_mvm_sta_tx_agg_oper(struct iwl_mvm *mvm, struct ieee80211_vif *vif, return -EIO; iwl_mvm_enable_agg_txq(mvm, queue, fifo, mvmsta->sta_id, tid, - buf_size, ssn); + buf_size, ssn, wdg_timeout); /* * Even though in theory the peer could have different @@ -1058,7 +1067,7 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false); - iwl_mvm_disable_txq(mvm, txq_id); + iwl_mvm_disable_txq(mvm, txq_id, 0); return 0; case IWL_AGG_STARTING: case IWL_EMPTYING_HW_QUEUE_ADDBA: @@ -1116,7 +1125,7 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, iwl_mvm_sta_tx_agg(mvm, sta, tid, txq_id, false); - iwl_mvm_disable_txq(mvm, tid_data->txq_id); + iwl_mvm_disable_txq(mvm, tid_data->txq_id, 0); } mvm->queue_to_mac80211[tid_data->txq_id] = @@ -1144,10 +1153,10 @@ static int iwl_mvm_set_fw_key_idx(struct iwl_mvm *mvm) static u8 iwl_mvm_get_key_sta_id(struct ieee80211_vif *vif, struct ieee80211_sta *sta) { - struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); if (sta) { - struct iwl_mvm_sta *mvm_sta = (void *)sta->drv_priv; + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); return mvm_sta->sta_id; } @@ -1196,6 +1205,7 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, break; case WLAN_CIPHER_SUITE_WEP104: key_flags |= cpu_to_le16(STA_KEY_FLG_WEP_13BYTES); + /* fall through */ case WLAN_CIPHER_SUITE_WEP40: key_flags |= cpu_to_le16(STA_KEY_FLG_WEP); memcpy(cmd.key + 3, keyconf->key, keyconf->keylen); @@ -1280,7 +1290,7 @@ static inline u8 *iwl_mvm_get_mac_addr(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta) { - struct iwl_mvm_vif *mvmvif = (void *)vif->drv_priv; + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); if (sta) return sta->addr; diff --git a/drivers/net/wireless/iwlwifi/mvm/tdls.c b/drivers/net/wireless/iwlwifi/mvm/tdls.c index c0e00bae5bd0..a87b506c8c72 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tdls.c +++ b/drivers/net/wireless/iwlwifi/mvm/tdls.c @@ -64,6 +64,8 @@ #include <linux/etherdevice.h> #include "mvm.h" #include "time-event.h" +#include "iwl-io.h" +#include "iwl-prph.h" #define TU_TO_US(x) (x * 1024) #define TU_TO_MS(x) (TU_TO_US(x) / 1000) @@ -228,6 +230,8 @@ iwl_mvm_tdls_cs_state_str(enum iwl_mvm_tdls_cs_state state) return "IDLE"; case IWL_MVM_TDLS_SW_REQ_SENT: return "REQ SENT"; + case IWL_MVM_TDLS_SW_RESP_RCVD: + return "RESP RECEIVED"; case IWL_MVM_TDLS_SW_REQ_RCVD: return "REQ RECEIVED"; case IWL_MVM_TDLS_SW_ACTIVE: @@ -248,6 +252,11 @@ static void iwl_mvm_tdls_update_cs_state(struct iwl_mvm *mvm, iwl_mvm_tdls_cs_state_str(state)); mvm->tdls_cs.state = state; + /* we only send requests to our switching peer - update sent time */ + if (state == IWL_MVM_TDLS_SW_REQ_SENT) + mvm->tdls_cs.peer.sent_timestamp = + iwl_read_prph(mvm->trans, DEVICE_SYSTEM_TIME_REG); + if (state == IWL_MVM_TDLS_SW_IDLE) mvm->tdls_cs.cur_sta_id = IWL_MVM_STATION_COUNT; } @@ -300,7 +309,7 @@ out: static int iwl_mvm_tdls_check_action(struct iwl_mvm *mvm, enum iwl_tdls_channel_switch_type type, - const u8 *peer, bool peer_initiator) + const u8 *peer, bool peer_initiator, u32 timestamp) { bool same_peer = false; int ret = 0; @@ -325,17 +334,30 @@ iwl_mvm_tdls_check_action(struct iwl_mvm *mvm, ret = -EINVAL; break; case IWL_MVM_TDLS_SW_REQ_SENT: + /* only allow requests from the same peer */ + if (!same_peer) + ret = -EBUSY; + else if (type == TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH && + !peer_initiator) + /* + * We received a ch-switch request while an outgoing + * one is pending. Allow it if the peer is the link + * initiator. + */ + ret = -EBUSY; + else if (type == TDLS_SEND_CHAN_SW_REQ) + /* wait for idle before sending another request */ + ret = -EBUSY; + else if (timestamp <= mvm->tdls_cs.peer.sent_timestamp) + /* we got a stale response - ignore it */ + ret = -EINVAL; + break; + case IWL_MVM_TDLS_SW_RESP_RCVD: /* - * We received a ch-switch request while an outgoing one is - * pending. Allow it to proceed if the other peer is the same - * one we sent to, and we are not the link initiator. + * we are waiting for the FW to give an "active" notification, + * so ignore requests in the meantime */ - if (type == TDLS_SEND_CHAN_SW_RESP_AND_MOVE_CH) { - if (!same_peer) - ret = -EBUSY; - else if (!peer_initiator) /* we are the initiator */ - ret = -EBUSY; - } + ret = -EBUSY; break; case IWL_MVM_TDLS_SW_REQ_RCVD: /* as above, allow the link initiator to proceed */ @@ -349,9 +371,12 @@ iwl_mvm_tdls_check_action(struct iwl_mvm *mvm, } break; case IWL_MVM_TDLS_SW_ACTIVE: - /* we don't allow initiations during active channel switch */ - if (type == TDLS_SEND_CHAN_SW_REQ) - ret = -EINVAL; + /* + * the only valid request when active is a request to return + * to the base channel by the current off-channel peer + */ + if (type != TDLS_MOVE_CH || !same_peer) + ret = -EBUSY; break; } @@ -384,7 +409,8 @@ iwl_mvm_tdls_config_channel_switch(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); - ret = iwl_mvm_tdls_check_action(mvm, type, peer, peer_initiator); + ret = iwl_mvm_tdls_check_action(mvm, type, peer, peer_initiator, + timestamp); if (ret) return ret; @@ -473,6 +499,8 @@ iwl_mvm_tdls_config_channel_switch(struct iwl_mvm *mvm, type == TDLS_SEND_CHAN_SW_REQ ? IWL_MVM_TDLS_SW_REQ_SENT : IWL_MVM_TDLS_SW_REQ_RCVD); + } else { + iwl_mvm_tdls_update_cs_state(mvm, IWL_MVM_TDLS_SW_RESP_RCVD); } out: @@ -657,12 +685,15 @@ iwl_mvm_tdls_recv_channel_switch(struct ieee80211_hw *hw, struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); enum iwl_tdls_channel_switch_type type; unsigned int delay; + const char *action_str = + params->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ? + "REQ" : "RESP"; mutex_lock(&mvm->mutex); IWL_DEBUG_TDLS(mvm, - "Received TDLS ch switch action %d from %pM status %d\n", - params->action_code, params->sta->addr, params->status); + "Received TDLS ch switch action %s from %pM status %d\n", + action_str, params->sta->addr, params->status); /* * we got a non-zero status from a peer we were switching to - move to diff --git a/drivers/net/wireless/iwlwifi/mvm/tt.c b/drivers/net/wireless/iwlwifi/mvm/tt.c index 2b1e61fac34a..ba615ad2176c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/iwlwifi/mvm/tt.c @@ -69,6 +69,7 @@ static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm) { + struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; u32 duration = mvm->thermal_throttle.params->ct_kill_duration; if (test_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status)) @@ -77,12 +78,15 @@ static void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm) IWL_ERR(mvm, "Enter CT Kill\n"); iwl_mvm_set_hw_ctkill_state(mvm, true); + tt->throttle = false; + tt->dynamic_smps = false; + /* Don't schedule an exit work if we're in test mode, since * the temperature will not change unless we manually set it * again (or disable testing). */ if (!mvm->temperature_test) - schedule_delayed_work(&mvm->thermal_throttle.ct_kill_exit, + schedule_delayed_work(&tt->ct_kill_exit, round_jiffies_relative(duration * HZ)); } @@ -452,6 +456,7 @@ void iwl_mvm_tt_initialize(struct iwl_mvm *mvm, u32 min_backoff) tt->params = &iwl7000_tt_params; tt->throttle = false; + tt->dynamic_smps = false; tt->min_backoff = min_backoff; INIT_DELAYED_WORK(&tt->ct_kill_exit, check_exit_ctkill); } diff --git a/drivers/net/wireless/iwlwifi/mvm/tx.c b/drivers/net/wireless/iwlwifi/mvm/tx.c index 4f15d9decc81..07304e1fd64a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/iwlwifi/mvm/tx.c @@ -90,8 +90,6 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, if (ieee80211_is_probe_resp(fc)) tx_flags |= TX_CMD_FLG_TSF; - else if (ieee80211_is_back_req(fc)) - tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR; if (ieee80211_has_morefrags(fc)) tx_flags |= TX_CMD_FLG_MORE_FRAG; @@ -100,6 +98,15 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, u8 *qc = ieee80211_get_qos_ctl(hdr); tx_cmd->tid_tspec = qc[0] & 0xf; tx_flags &= ~TX_CMD_FLG_SEQ_CTL; + } else if (ieee80211_is_back_req(fc)) { + struct ieee80211_bar *bar = (void *)skb->data; + u16 control = le16_to_cpu(bar->control); + + tx_flags |= TX_CMD_FLG_ACK | TX_CMD_FLG_BAR; + tx_cmd->tid_tspec = (control & + IEEE80211_BAR_CTRL_TID_INFO_MASK) >> + IEEE80211_BAR_CTRL_TID_INFO_SHIFT; + WARN_ON_ONCE(tx_cmd->tid_tspec >= IWL_MAX_TID_COUNT); } else { tx_cmd->tid_tspec = IWL_TID_NON_QOS; if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) @@ -108,8 +115,12 @@ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, tx_flags &= ~TX_CMD_FLG_SEQ_CTL; } - /* tid_tspec will default to 0 = BE when QOS isn't enabled */ - ac = tid_to_mac80211_ac[tx_cmd->tid_tspec]; + /* Default to 0 (BE) when tid_spec is set to IWL_TID_NON_QOS */ + if (tx_cmd->tid_tspec < IWL_MAX_TID_COUNT) + ac = tid_to_mac80211_ac[tx_cmd->tid_tspec]; + else + ac = tid_to_mac80211_ac[0]; + tx_flags |= iwl_mvm_bt_coex_tx_prio(mvm, hdr, info, ac) << TX_CMD_FLG_BT_PRIO_POS; @@ -209,7 +220,7 @@ void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd, rate_plcp = iwl_mvm_mac80211_idx_to_hwrate(rate_idx); mvm->mgmt_last_antenna_idx = - iwl_mvm_next_antenna(mvm, mvm->fw->valid_tx_ant, + iwl_mvm_next_antenna(mvm, iwl_mvm_get_valid_tx_ant(mvm), mvm->mgmt_last_antenna_idx); if (info->band == IEEE80211_BAND_2GHZ && @@ -496,7 +507,7 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, IWL_DEBUG_TX_QUEUES(mvm, "Can continue DELBA flow ssn = next_recl = %d\n", tid_data->next_reclaimed); - iwl_mvm_disable_txq(mvm, tid_data->txq_id); + iwl_mvm_disable_txq(mvm, tid_data->txq_id, CMD_ASYNC); tid_data->state = IWL_AGG_OFF; /* * we can't hold the mutex - but since we are after a sequence @@ -656,7 +667,8 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, /* Single frame failure in an AMPDU queue => send BAR */ if (txq_id >= mvm->first_agg_queue && - !(info->flags & IEEE80211_TX_STAT_ACK)) + !(info->flags & IEEE80211_TX_STAT_ACK) && + !(info->flags & IEEE80211_TX_STAT_TX_FILTERED)) info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; /* W/A FW bug: seq_ctl is wrong when the status isn't success */ @@ -919,6 +931,11 @@ int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, sta_id = ba_notif->sta_id; tid = ba_notif->tid; + if (WARN_ONCE(sta_id >= IWL_MVM_STATION_COUNT || + tid >= IWL_MAX_TID_COUNT, + "sta_id %d tid %d", sta_id, tid)) + return 0; + rcu_read_lock(); sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); diff --git a/drivers/net/wireless/iwlwifi/mvm/utils.c b/drivers/net/wireless/iwlwifi/mvm/utils.c index e56e77ef5d2e..8decf9953229 100644 --- a/drivers/net/wireless/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/iwlwifi/mvm/utils.c @@ -432,7 +432,7 @@ static void iwl_mvm_dump_umac_error_log(struct iwl_mvm *mvm) mvm->status, table.valid); } - IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id, + IWL_ERR(mvm, "0x%08X | %s\n", table.error_id, desc_lookup(table.error_id)); IWL_ERR(mvm, "0x%08X | umac branchlink1\n", table.blink1); IWL_ERR(mvm, "0x%08X | umac branchlink2\n", table.blink2); @@ -531,49 +531,50 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) } void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, u16 ssn, - const struct iwl_trans_txq_scd_cfg *cfg) + const struct iwl_trans_txq_scd_cfg *cfg, + unsigned int wdg_timeout) { - if (iwl_mvm_is_dqa_supported(mvm)) { - struct iwl_scd_txq_cfg_cmd cmd = { - .scd_queue = queue, - .enable = 1, - .window = cfg->frame_limit, - .sta_id = cfg->sta_id, - .ssn = cpu_to_le16(ssn), - .tx_fifo = cfg->fifo, - .aggregate = cfg->aggregate, - .flags = IWL_SCD_FLAGS_DQA_ENABLED, - .tid = cfg->tid, - .control = IWL_SCD_CONTROL_SET_SSN, - }; - int ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, - sizeof(cmd), &cmd); - if (ret) - IWL_ERR(mvm, - "Failed to configure queue %d on FIFO %d\n", - queue, cfg->fifo); + struct iwl_scd_txq_cfg_cmd cmd = { + .scd_queue = queue, + .enable = 1, + .window = cfg->frame_limit, + .sta_id = cfg->sta_id, + .ssn = cpu_to_le16(ssn), + .tx_fifo = cfg->fifo, + .aggregate = cfg->aggregate, + .tid = cfg->tid, + }; + + if (!iwl_mvm_is_scd_cfg_supported(mvm)) { + iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, cfg, + wdg_timeout); + return; } - iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, - iwl_mvm_is_dqa_supported(mvm) ? NULL : cfg); + iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, NULL, wdg_timeout); + WARN(iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd), &cmd), + "Failed to configure queue %d on FIFO %d\n", queue, cfg->fifo); } -void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue) +void iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, u8 flags) { - iwl_trans_txq_disable(mvm->trans, queue, - !iwl_mvm_is_dqa_supported(mvm)); - - if (iwl_mvm_is_dqa_supported(mvm)) { - struct iwl_scd_txq_cfg_cmd cmd = { - .scd_queue = queue, - .enable = 0, - }; - int ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, CMD_ASYNC, - sizeof(cmd), &cmd); - if (ret) - IWL_ERR(mvm, "Failed to disable queue %d (ret=%d)\n", - queue, ret); + struct iwl_scd_txq_cfg_cmd cmd = { + .scd_queue = queue, + .enable = 0, + }; + int ret; + + if (!iwl_mvm_is_scd_cfg_supported(mvm)) { + iwl_trans_txq_disable(mvm->trans, queue, true); + return; } + + iwl_trans_txq_disable(mvm->trans, queue, false); + ret = iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, flags, + sizeof(cmd), &cmd); + if (ret) + IWL_ERR(mvm, "Failed to disable queue %d (ret=%d)\n", + queue, ret); } /** @@ -620,7 +621,7 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, lockdep_assert_held(&mvm->mutex); /* SMPS is irrelevant for NICs that don't have at least 2 RX antenna */ - if (num_of_ant(mvm->fw->valid_rx_ant) == 1) + if (num_of_ant(iwl_mvm_get_valid_rx_ant(mvm)) == 1) return; if (vif->type == NL80211_IFTYPE_AP) @@ -662,10 +663,10 @@ bool iwl_mvm_rx_diversity_allowed(struct iwl_mvm *mvm) lockdep_assert_held(&mvm->mutex); - if (num_of_ant(mvm->fw->valid_rx_ant) == 1) + if (num_of_ant(iwl_mvm_get_valid_rx_ant(mvm)) == 1) return false; - if (!mvm->cfg->rx_with_siso_diversity) + if (mvm->cfg->rx_with_siso_diversity) return false; ieee80211_iterate_active_interfaces_atomic( diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index 2f0c4b170344..dbd6bcf52205 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -415,6 +415,8 @@ static const struct pci_device_id iwl_hw_card_ids[] = { {IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)}, {IWL_PCI_DEVICE(0x24F3, 0x0004, iwl8260_2n_cfg)}, {IWL_PCI_DEVICE(0x24F4, 0x0030, iwl8260_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F5, 0x0010, iwl4165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F6, 0x0030, iwl4165_2ac_cfg)}, #endif /* CONFIG_IWLMVM */ {0} @@ -527,8 +529,10 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) else if (cfg == &iwl7265_n_cfg) cfg_7265d = &iwl7265d_n_cfg; if (cfg_7265d && - (iwl_trans->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_7265D) + (iwl_trans->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_7265D) { cfg = cfg_7265d; + iwl_trans->cfg = cfg_7265d; + } #endif pci_set_drvdata(pdev, iwl_trans); diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 1aea6b66c594..cae0eb8835ce 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -216,6 +216,7 @@ struct iwl_pcie_txq_scratch_buf { * @need_update: indicates need to update read/write index * @active: stores if queue is active * @ampdu: true if this queue is an ampdu queue for an specific RA/TID + * @wd_timeout: queue watchdog timeout (jiffies) - per queue * * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame * descriptors) and required locking structures. @@ -232,6 +233,7 @@ struct iwl_txq { bool need_update; u8 active; bool ampdu; + unsigned long wd_timeout; }; static inline dma_addr_t @@ -259,7 +261,6 @@ iwl_pcie_get_scratchbuf_dma(struct iwl_txq *txq, int idx) * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes) * @scd_set_active: should the transport configure the SCD for HCMD queue * @rx_page_order: page order for receive buffer size - * @wd_timeout: queue watchdog timeout (jiffies) * @reg_lock: protect hw register access * @cmd_in_flight: true when we have a host command in flight * @fw_mon_phys: physical address of the buffer for the firmware monitor @@ -302,6 +303,7 @@ struct iwl_trans_pcie { u8 cmd_queue; u8 cmd_fifo; + unsigned int cmd_q_wdg_timeout; u8 n_no_reclaim_cmds; u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS]; @@ -312,12 +314,14 @@ struct iwl_trans_pcie { const char *const *command_names; - /* queue watchdog */ - unsigned long wd_timeout; - /*protect hw register */ spinlock_t reg_lock; bool cmd_in_flight; + bool ref_cmd_in_flight; + + /* protect ref counter */ + spinlock_t ref_lock; + u32 ref_count; dma_addr_t fw_mon_phys; struct page *fw_mon_page; @@ -368,7 +372,8 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr); int iwl_pcie_tx_stop(struct iwl_trans *trans); void iwl_pcie_tx_free(struct iwl_trans *trans); void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int queue, u16 ssn, - const struct iwl_trans_txq_scd_cfg *cfg); + const struct iwl_trans_txq_scd_cfg *cfg, + unsigned int wdg_timeout); void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue, bool configure_scd); int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, @@ -381,6 +386,9 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, struct sk_buff_head *skbs); void iwl_trans_pcie_tx_reset(struct iwl_trans *trans); +void iwl_trans_pcie_ref(struct iwl_trans *trans); +void iwl_trans_pcie_unref(struct iwl_trans *trans); + static inline u16 iwl_pcie_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx) { struct iwl_tfd_tb *tb = &tfd->tbs[idx]; diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 523fe0c88dcb..69935aa5a1b3 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -75,6 +75,7 @@ #include "iwl-trans.h" #include "iwl-csr.h" #include "iwl-prph.h" +#include "iwl-scd.h" #include "iwl-agn-hw.h" #include "iwl-fw-error-dump.h" #include "internal.h" @@ -443,10 +444,25 @@ static int iwl_pcie_apm_stop_master(struct iwl_trans *trans) return ret; } -static void iwl_pcie_apm_stop(struct iwl_trans *trans) +static void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave) { IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n"); + if (op_mode_leave) { + if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status)) + iwl_pcie_apm_init(trans); + + /* inform ME that we are leaving */ + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) + iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_WAKE_ME); + else if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_PREPARE | + CSR_HW_IF_CONFIG_REG_ENABLE_PME); + mdelay(5); + } + clear_bit(STATUS_DEVICE_ENABLED, &trans->status); /* Stop device's DMA activity */ @@ -707,6 +723,11 @@ static int iwl_pcie_load_cpu_sections_8000b(struct iwl_trans *trans, *first_ucode_section = last_read_idx; + if (cpu == 1) + iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFF); + else + iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFFFFFF); + return 0; } @@ -893,8 +914,8 @@ static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans, if (ret) return ret; - /* Notify FW loading is done */ - iwl_write_direct32(trans, FH_UCODE_LOAD_STATUS, 0xFFFFFFFF); + if (trans->dbg_dest_tlv) + iwl_pcie_apply_destination(trans); /* wait for image verification to complete */ ret = iwl_poll_prph_bit(trans, LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0, @@ -916,6 +937,7 @@ static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans, static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, const struct fw_img *fw, bool run_in_rfkill) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; bool hw_rfkill; @@ -945,6 +967,9 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, return ret; } + /* init ref_count to 1 (should be cleared when ucode is loaded) */ + trans_pcie->ref_count = 1; + /* make sure rfkill handshake bits are cleared */ iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, @@ -960,7 +985,7 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, /* Load the given image to the HW */ if ((trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) && - (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_B_STEP)) + (CSR_HW_REV_STEP(trans->hw_rev) != SILICON_A_STEP)) return iwl_pcie_load_given_ucode_8000b(trans, fw); else return iwl_pcie_load_given_ucode(trans, fw); @@ -1010,7 +1035,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); /* Stop the device, and put it in low power state */ - iwl_pcie_apm_stop(trans); + iwl_pcie_apm_stop(trans, false); /* stop and reset the on-board processor */ iwl_write32(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); @@ -1192,7 +1217,7 @@ static void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans) iwl_disable_interrupts(trans); spin_unlock(&trans_pcie->irq_lock); - iwl_pcie_apm_stop(trans); + iwl_pcie_apm_stop(trans, true); spin_lock(&trans_pcie->irq_lock); iwl_disable_interrupts(trans); @@ -1244,6 +1269,7 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans, trans_pcie->cmd_queue = trans_cfg->cmd_queue; trans_pcie->cmd_fifo = trans_cfg->cmd_fifo; + trans_pcie->cmd_q_wdg_timeout = trans_cfg->cmd_q_wdg_timeout; if (WARN_ON(trans_cfg->n_no_reclaim_cmds > MAX_NO_RECLAIM_CMDS)) trans_pcie->n_no_reclaim_cmds = 0; else @@ -1258,9 +1284,6 @@ static void iwl_trans_pcie_configure(struct iwl_trans *trans, else trans_pcie->rx_page_order = get_order(4 * 1024); - trans_pcie->wd_timeout = - msecs_to_jiffies(trans_cfg->queue_watchdog_timeout); - trans_pcie->command_names = trans_cfg->command_names; trans_pcie->bc_table_dword = trans_cfg->bc_table_dword; trans_pcie->scd_set_active = trans_cfg->scd_set_active; @@ -1540,6 +1563,38 @@ static void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg, spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); } +void iwl_trans_pcie_ref(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + unsigned long flags; + + if (iwlwifi_mod_params.d0i3_disable) + return; + + spin_lock_irqsave(&trans_pcie->ref_lock, flags); + IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count); + trans_pcie->ref_count++; + spin_unlock_irqrestore(&trans_pcie->ref_lock, flags); +} + +void iwl_trans_pcie_unref(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + unsigned long flags; + + if (iwlwifi_mod_params.d0i3_disable) + return; + + spin_lock_irqsave(&trans_pcie->ref_lock, flags); + IWL_DEBUG_RPM(trans, "ref_counter: %d\n", trans_pcie->ref_count); + if (WARN_ON_ONCE(trans_pcie->ref_count == 0)) { + spin_unlock_irqrestore(&trans_pcie->ref_lock, flags); + return; + } + trans_pcie->ref_count--; + spin_unlock_irqrestore(&trans_pcie->ref_lock, flags); +} + static const char *get_csr_string(int cmd) { #define IWL_CMD(x) case x: return #x @@ -2264,6 +2319,9 @@ static const struct iwl_trans_ops trans_ops_pcie = { .release_nic_access = iwl_trans_pcie_release_nic_access, .set_bits_mask = iwl_trans_pcie_set_bits_mask, + .ref = iwl_trans_pcie_ref, + .unref = iwl_trans_pcie_unref, + .dump_data = iwl_trans_pcie_dump_data, }; @@ -2291,6 +2349,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, trans_pcie->trans = trans; spin_lock_init(&trans_pcie->irq_lock); spin_lock_init(&trans_pcie->reg_lock); + spin_lock_init(&trans_pcie->ref_lock); init_waitqueue_head(&trans_pcie->ucode_write_waitq); err = pci_enable_device(pdev); @@ -2404,6 +2463,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, } trans_pcie->inta_mask = CSR_INI_SET_MASK; + trans->d0i3_mode = IWL_D0I3_MODE_ON_SUSPEND; return trans; diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 8a6c7a084aa1..af0bce736358 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -147,7 +147,6 @@ static void iwl_pcie_free_dma_ptr(struct iwl_trans *trans, static void iwl_pcie_txq_stuck_timer(unsigned long data) { struct iwl_txq *txq = (void *)data; - struct iwl_queue *q = &txq->q; struct iwl_trans_pcie *trans_pcie = txq->trans_pcie; struct iwl_trans *trans = iwl_trans_pcie_get_trans(trans_pcie); u32 scd_sram_addr = trans_pcie->scd_base_addr + @@ -164,7 +163,7 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data) spin_unlock(&txq->lock); IWL_ERR(trans, "Queue %d stuck for %u ms.\n", txq->q.id, - jiffies_to_msecs(trans_pcie->wd_timeout)); + jiffies_to_msecs(txq->wd_timeout)); IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", txq->q.read_ptr, txq->q.write_ptr); @@ -198,11 +197,6 @@ static void iwl_pcie_txq_stuck_timer(unsigned long data) iwl_read_prph(trans, SCD_QUEUE_WRPTR(i))); } - for (i = q->read_ptr; i != q->write_ptr; - i = iwl_queue_inc_wrap(i)) - IWL_ERR(trans, "scratch %d = 0x%08x\n", i, - le32_to_cpu(txq->scratchbufs[i].scratch)); - iwl_force_nmi(trans); } @@ -680,7 +674,8 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr) iwl_write_prph(trans, SCD_CHAINEXT_EN, 0); iwl_trans_ac_txq_enable(trans, trans_pcie->cmd_queue, - trans_pcie->cmd_fifo); + trans_pcie->cmd_fifo, + trans_pcie->cmd_q_wdg_timeout); /* Activate all Tx DMA/FIFO channels */ iwl_scd_activate_fifos(trans); @@ -722,7 +717,12 @@ void iwl_trans_pcie_tx_reset(struct iwl_trans *trans) iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG, trans_pcie->kw.dma >> 4); - iwl_pcie_tx_start(trans, trans_pcie->scd_base_addr); + /* + * Send 0 as the scd_base_addr since the device may have be reset + * while we were in WoWLAN in which case SCD_SRAM_BASE_ADDR will + * contain garbage. + */ + iwl_pcie_tx_start(trans, 0); } /* @@ -898,6 +898,10 @@ int iwl_pcie_tx_init(struct iwl_trans *trans) } } + if (trans->cfg->base_params->num_of_queues > 20) + iwl_set_bits_prph(trans, SCD_GP_CTRL, + SCD_GP_CTRL_ENABLE_31_QUEUES); + return 0; error: /*Upon error, free only if we allocated something */ @@ -906,10 +910,9 @@ error: return ret; } -static inline void iwl_pcie_txq_progress(struct iwl_trans_pcie *trans_pcie, - struct iwl_txq *txq) +static inline void iwl_pcie_txq_progress(struct iwl_txq *txq) { - if (!trans_pcie->wd_timeout) + if (!txq->wd_timeout) return; /* @@ -919,7 +922,7 @@ static inline void iwl_pcie_txq_progress(struct iwl_trans_pcie *trans_pcie, if (txq->q.read_ptr == txq->q.write_ptr) del_timer(&txq->stuck_timer); else - mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); + mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout); } /* Frees buffers until index _not_ inclusive */ @@ -981,21 +984,35 @@ void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, iwl_pcie_txq_free_tfd(trans, txq); } - iwl_pcie_txq_progress(trans_pcie, txq); + iwl_pcie_txq_progress(txq); if (iwl_queue_space(&txq->q) > txq->q.low_mark) iwl_wake_queue(trans, txq); + + if (q->read_ptr == q->write_ptr) { + IWL_DEBUG_RPM(trans, "Q %d - last tx reclaimed\n", q->id); + iwl_trans_pcie_unref(trans); + } + out: spin_unlock_bh(&txq->lock); } -static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans) +static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans, + const struct iwl_host_cmd *cmd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; lockdep_assert_held(&trans_pcie->reg_lock); + if (!(cmd->flags & CMD_SEND_IN_IDLE) && + !trans_pcie->ref_cmd_in_flight) { + trans_pcie->ref_cmd_in_flight = true; + IWL_DEBUG_RPM(trans, "set ref_cmd_in_flight - ref\n"); + iwl_trans_pcie_ref(trans); + } + if (trans_pcie->cmd_in_flight) return 0; @@ -1036,6 +1053,12 @@ static int iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans) lockdep_assert_held(&trans_pcie->reg_lock); + if (trans_pcie->ref_cmd_in_flight) { + trans_pcie->ref_cmd_in_flight = false; + IWL_DEBUG_RPM(trans, "clear ref_cmd_in_flight - unref\n"); + iwl_trans_pcie_unref(trans); + } + if (WARN_ON(!trans_pcie->cmd_in_flight)) return 0; @@ -1089,7 +1112,7 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx) spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); } - iwl_pcie_txq_progress(trans_pcie, txq); + iwl_pcie_txq_progress(txq); } static int iwl_pcie_txq_set_ratid_map(struct iwl_trans *trans, u16 ra_tid, @@ -1122,14 +1145,18 @@ static int iwl_pcie_txq_set_ratid_map(struct iwl_trans *trans, u16 ra_tid, #define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid)) void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn, - const struct iwl_trans_txq_scd_cfg *cfg) + const struct iwl_trans_txq_scd_cfg *cfg, + unsigned int wdg_timeout) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq = &trans_pcie->txq[txq_id]; int fifo = -1; if (test_and_set_bit(txq_id, trans_pcie->queue_used)) WARN_ONCE(1, "queue %d already used - expect issues", txq_id); + txq->wd_timeout = msecs_to_jiffies(wdg_timeout); + if (cfg) { fifo = cfg->fifo; @@ -1153,7 +1180,7 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn, /* enable aggregations for the queue */ iwl_scd_txq_enable_agg(trans, txq_id); - trans_pcie->txq[txq_id].ampdu = true; + txq->ampdu = true; } else { /* * disable aggregations for the queue, this will also @@ -1162,20 +1189,20 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn, */ iwl_scd_txq_disable_agg(trans, txq_id); - ssn = trans_pcie->txq[txq_id].q.read_ptr; + ssn = txq->q.read_ptr; } } /* Place first TFD at index corresponding to start sequence number. * Assumes that ssn_idx is valid (!= 0xFFF) */ - trans_pcie->txq[txq_id].q.read_ptr = (ssn & 0xff); - trans_pcie->txq[txq_id].q.write_ptr = (ssn & 0xff); + txq->q.read_ptr = (ssn & 0xff); + txq->q.write_ptr = (ssn & 0xff); + iwl_write_direct32(trans, HBUS_TARG_WRPTR, + (ssn & 0xff) | (txq_id << 8)); if (cfg) { u8 frame_limit = cfg->frame_limit; - iwl_write_direct32(trans, HBUS_TARG_WRPTR, - (ssn & 0xff) | (txq_id << 8)); iwl_write_prph(trans, SCD_QUEUE_RDPTR(txq_id), ssn); /* Set up Tx window size and frame limit for this queue */ @@ -1200,11 +1227,17 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn, if (txq_id == trans_pcie->cmd_queue && trans_pcie->scd_set_active) iwl_scd_enable_set_active(trans, BIT(txq_id)); + + IWL_DEBUG_TX_QUEUES(trans, + "Activate queue %d on FIFO %d WrPtr: %d\n", + txq_id, fifo, ssn & 0xff); + } else { + IWL_DEBUG_TX_QUEUES(trans, + "Activate queue %d WrPtr: %d\n", + txq_id, ssn & 0xff); } - trans_pcie->txq[txq_id].active = true; - IWL_DEBUG_TX_QUEUES(trans, "Activate queue %d on FIFO %d WrPtr: %d\n", - txq_id, fifo, ssn & 0xff); + txq->active = true; } void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id, @@ -1469,11 +1502,11 @@ static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, trace_iwlwifi_dev_hcmd(trans->dev, cmd, cmd_size, &out_cmd->hdr); /* start timer if queue currently empty */ - if (q->read_ptr == q->write_ptr && trans_pcie->wd_timeout) - mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); + if (q->read_ptr == q->write_ptr && txq->wd_timeout) + mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout); spin_lock_irqsave(&trans_pcie->reg_lock, flags); - ret = iwl_pcie_set_cmd_in_flight(trans); + ret = iwl_pcie_set_cmd_in_flight(trans, cmd); if (ret < 0) { idx = ret; spin_unlock_irqrestore(&trans_pcie->reg_lock, flags); @@ -1819,9 +1852,12 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, wait_write_ptr = ieee80211_has_morefrags(fc); /* start timer if queue currently empty */ - if (txq->need_update && q->read_ptr == q->write_ptr && - trans_pcie->wd_timeout) - mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); + if (q->read_ptr == q->write_ptr) { + if (txq->wd_timeout) + mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout); + IWL_DEBUG_RPM(trans, "Q: %d first tx - take ref\n", q->id); + iwl_trans_pcie_ref(trans); + } /* Tell device the write index *just past* this latest filled TFD */ q->write_ptr = iwl_queue_inc_wrap(q->write_ptr); diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 34f09ef90bb3..a92985a6ea21 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -1616,10 +1616,10 @@ static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev, lbs_deb_enter(LBS_DEB_CFG80211); - sinfo->filled |= STATION_INFO_TX_BYTES | - STATION_INFO_TX_PACKETS | - STATION_INFO_RX_BYTES | - STATION_INFO_RX_PACKETS; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES) | + BIT(NL80211_STA_INFO_TX_PACKETS) | + BIT(NL80211_STA_INFO_RX_BYTES) | + BIT(NL80211_STA_INFO_RX_PACKETS); sinfo->tx_bytes = priv->dev->stats.tx_bytes; sinfo->tx_packets = priv->dev->stats.tx_packets; sinfo->rx_bytes = priv->dev->stats.rx_bytes; @@ -1629,14 +1629,14 @@ static int lbs_cfg_get_station(struct wiphy *wiphy, struct net_device *dev, ret = lbs_get_rssi(priv, &signal, &noise); if (ret == 0) { sinfo->signal = signal; - sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); } /* Convert priv->cur_rate from hw_value to NL80211 value */ for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) { if (priv->cur_rate == lbs_rates[i].hw_value) { sinfo->txrate.legacy = lbs_rates[i].bitrate; - sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); break; } } diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index ef58a8862d91..4a4c6586a8d2 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -625,22 +625,22 @@ static int hwsim_fops_ps_write(void *dat, u64 val) old_ps = data->ps; data->ps = val; + local_bh_disable(); if (val == PS_MANUAL_POLL) { - ieee80211_iterate_active_interfaces(data->hw, - IEEE80211_IFACE_ITER_NORMAL, - hwsim_send_ps_poll, data); + ieee80211_iterate_active_interfaces_atomic( + data->hw, IEEE80211_IFACE_ITER_NORMAL, + hwsim_send_ps_poll, data); data->ps_poll_pending = true; } else if (old_ps == PS_DISABLED && val != PS_DISABLED) { - ieee80211_iterate_active_interfaces(data->hw, - IEEE80211_IFACE_ITER_NORMAL, - hwsim_send_nullfunc_ps, - data); + ieee80211_iterate_active_interfaces_atomic( + data->hw, IEEE80211_IFACE_ITER_NORMAL, + hwsim_send_nullfunc_ps, data); } else if (old_ps != PS_DISABLED && val == PS_DISABLED) { - ieee80211_iterate_active_interfaces(data->hw, - IEEE80211_IFACE_ITER_NORMAL, - hwsim_send_nullfunc_no_ps, - data); + ieee80211_iterate_active_interfaces_atomic( + data->hw, IEEE80211_IFACE_ITER_NORMAL, + hwsim_send_nullfunc_no_ps, data); } + local_bh_enable(); return 0; } @@ -2149,14 +2149,14 @@ static int append_radio_msg(struct sk_buff *skb, int id, if (param->regd) { int i; - for (i = 0; hwsim_world_regdom_custom[i] != param->regd && - i < ARRAY_SIZE(hwsim_world_regdom_custom); i++) - ; + for (i = 0; i < ARRAY_SIZE(hwsim_world_regdom_custom); i++) { + if (hwsim_world_regdom_custom[i] != param->regd) + continue; - if (i < ARRAY_SIZE(hwsim_world_regdom_custom)) { ret = nla_put_u32(skb, HWSIM_ATTR_REG_CUSTOM_REG, i); if (ret < 0) return ret; + break; } } @@ -2557,7 +2557,8 @@ static int mac80211_hwsim_get_radio(struct sk_buff *skb, if (res < 0) goto out_err; - return genlmsg_end(skb, hdr); + genlmsg_end(skb, hdr); + return 0; out_err: genlmsg_cancel(skb, hdr); diff --git a/drivers/net/wireless/mwifiex/11h.c b/drivers/net/wireless/mwifiex/11h.c index 2668e83afbb6..3ab87a855122 100644 --- a/drivers/net/wireless/mwifiex/11h.c +++ b/drivers/net/wireless/mwifiex/11h.c @@ -21,6 +21,16 @@ #include "fw.h" +void mwifiex_init_11h_params(struct mwifiex_private *priv) +{ + priv->state_11h.is_11h_enabled = true; + priv->state_11h.is_11h_active = false; +} + +inline int mwifiex_is_11h_active(struct mwifiex_private *priv) +{ + return priv->state_11h.is_11h_active; +} /* This function appends 11h info to a buffer while joining an * infrastructure BSS */ @@ -39,7 +49,7 @@ mwifiex_11h_process_infra_join(struct mwifiex_private *priv, u8 **buffer, return; radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band); - sband = priv->wdev->wiphy->bands[radio_type]; + sband = priv->wdev.wiphy->bands[radio_type]; cap = (struct mwifiex_ie_types_pwr_capability *)*buffer; cap->header.type = cpu_to_le16(WLAN_EID_PWR_CAPABILITY); @@ -69,10 +79,14 @@ mwifiex_11h_process_infra_join(struct mwifiex_private *priv, u8 **buffer, } /* Enable or disable the 11h extensions in the firmware */ -static int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag) +int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag) { u32 enable = flag; + /* enable master mode radar detection on AP interface */ + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) && enable) + enable |= MWIFIEX_MASTER_RADAR_DET_MASK; + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, HostCmd_ACT_GEN_SET, DOT11H_I, &enable, true); } @@ -91,11 +105,191 @@ void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer, * bit */ mwifiex_11h_activate(priv, true); + priv->state_11h.is_11h_active = true; bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_SPECTRUM_MGMT; mwifiex_11h_process_infra_join(priv, buffer, bss_desc); } else { /* Deactivate 11h functions in the firmware */ mwifiex_11h_activate(priv, false); + priv->state_11h.is_11h_active = false; bss_desc->cap_info_bitmap &= ~WLAN_CAPABILITY_SPECTRUM_MGMT; } } + +/* This is DFS CAC work queue function. + * This delayed work emits CAC finished event for cfg80211 if + * CAC was started earlier. + */ +void mwifiex_dfs_cac_work_queue(struct work_struct *work) +{ + struct cfg80211_chan_def chandef; + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct mwifiex_private *priv = + container_of(delayed_work, struct mwifiex_private, + dfs_cac_work); + + if (WARN_ON(!priv)) + return; + + chandef = priv->dfs_chandef; + if (priv->wdev.cac_started) { + dev_dbg(priv->adapter->dev, + "CAC timer finished; No radar detected\n"); + cfg80211_cac_event(priv->netdev, &chandef, + NL80211_RADAR_CAC_FINISHED, + GFP_KERNEL); + } +} + +/* This function prepares channel report request command to FW for + * starting radar detection. + */ +int mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf) +{ + struct host_cmd_ds_chan_rpt_req *cr_req = &cmd->params.chan_rpt_req; + struct mwifiex_radar_params *radar_params = (void *)data_buf; + + cmd->command = cpu_to_le16(HostCmd_CMD_CHAN_REPORT_REQUEST); + cmd->size = cpu_to_le16(S_DS_GEN); + le16_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_chan_rpt_req)); + + cr_req->chan_desc.start_freq = cpu_to_le16(MWIFIEX_A_BAND_START_FREQ); + cr_req->chan_desc.chan_num = radar_params->chandef->chan->hw_value; + cr_req->chan_desc.chan_width = radar_params->chandef->width; + cr_req->msec_dwell_time = cpu_to_le32(radar_params->cac_time_ms); + + dev_dbg(priv->adapter->dev, + "11h: issuing DFS Radar check for channel=%d\n", + radar_params->chandef->chan->hw_value); + + return 0; +} + +/* This function is to abort ongoing CAC upon stopping AP operations + * or during unload. + */ +void mwifiex_abort_cac(struct mwifiex_private *priv) +{ + if (priv->wdev.cac_started) { + dev_dbg(priv->adapter->dev, + "Aborting delayed work for CAC.\n"); + cancel_delayed_work_sync(&priv->dfs_cac_work); + cfg80211_cac_event(priv->netdev, &priv->dfs_chandef, + NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); + } +} + +/* This function handles channel report event from FW during CAC period. + * If radar is detected during CAC, driver indicates the same to cfg80211 + * and also cancels ongoing delayed work. + */ +int mwifiex_11h_handle_chanrpt_ready(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct host_cmd_ds_chan_rpt_event *rpt_event; + struct mwifiex_ie_types_chan_rpt_data *rpt; + u8 *evt_buf; + u16 event_len, tlv_len; + + rpt_event = (void *)(skb->data + sizeof(u32)); + event_len = skb->len - (sizeof(struct host_cmd_ds_chan_rpt_event)+ + sizeof(u32)); + + if (le32_to_cpu(rpt_event->result) != HostCmd_RESULT_OK) { + dev_err(priv->adapter->dev, "Error in channel report event\n"); + return -1; + } + + evt_buf = (void *)&rpt_event->tlvbuf; + + while (event_len >= sizeof(struct mwifiex_ie_types_header)) { + rpt = (void *)&rpt_event->tlvbuf; + tlv_len = le16_to_cpu(rpt->header.len); + + switch (le16_to_cpu(rpt->header.type)) { + case TLV_TYPE_CHANRPT_11H_BASIC: + if (rpt->map.radar) { + dev_notice(priv->adapter->dev, + "RADAR Detected on channel %d!\n", + priv->dfs_chandef.chan->hw_value); + cancel_delayed_work_sync(&priv->dfs_cac_work); + cfg80211_cac_event(priv->netdev, + &priv->dfs_chandef, + NL80211_RADAR_DETECTED, + GFP_KERNEL); + } + break; + default: + break; + } + + evt_buf += (tlv_len + sizeof(rpt->header)); + event_len -= (tlv_len + sizeof(rpt->header)); + } + + return 0; +} + +/* Handler for radar detected event from FW.*/ +int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct mwifiex_radar_det_event *rdr_event; + + rdr_event = (void *)(skb->data + sizeof(u32)); + + if (le32_to_cpu(rdr_event->passed)) { + dev_notice(priv->adapter->dev, + "radar detected; indicating kernel\n"); + cfg80211_radar_event(priv->adapter->wiphy, &priv->dfs_chandef, + GFP_KERNEL); + dev_dbg(priv->adapter->dev, "regdomain: %d\n", + rdr_event->reg_domain); + dev_dbg(priv->adapter->dev, "radar detection type: %d\n", + rdr_event->det_type); + } else { + dev_dbg(priv->adapter->dev, "false radar detection event!\n"); + } + + return 0; +} + +/* This is work queue function for channel switch handling. + * This function takes care of updating new channel definitin to + * bss config structure, restart AP and indicate channel switch success + * to cfg80211. + */ +void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work) +{ + struct mwifiex_uap_bss_param *bss_cfg; + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct mwifiex_private *priv = + container_of(delayed_work, struct mwifiex_private, + dfs_chan_sw_work); + + if (WARN_ON(!priv)) + return; + + bss_cfg = &priv->bss_cfg; + if (!bss_cfg->beacon_period) { + dev_err(priv->adapter->dev, + "channel switch: AP already stopped\n"); + return; + } + + mwifiex_uap_set_channel(bss_cfg, priv->dfs_chandef); + + if (mwifiex_config_start_uap(priv, bss_cfg)) { + dev_dbg(priv->adapter->dev, + "Failed to start AP after channel switch\n"); + return; + } + + dev_notice(priv->adapter->dev, + "indicating channel switch completion to kernel\n"); + cfg80211_ch_switch_notify(priv->netdev, &priv->dfs_chandef); +} diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c index 9d4786e7ddff..543148d27b01 100644 --- a/drivers/net/wireless/mwifiex/11n.c +++ b/drivers/net/wireless/mwifiex/11n.c @@ -39,7 +39,7 @@ int mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type, { uint16_t ht_ext_cap = le16_to_cpu(ht_cap->extended_ht_cap_info); struct ieee80211_supported_band *sband = - priv->wdev->wiphy->bands[radio_type]; + priv->wdev.wiphy->bands[radio_type]; if (WARN_ON_ONCE(!sband)) { dev_err(priv->adapter->dev, "Invalid radio type!\n"); @@ -314,7 +314,7 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, return ret_len; radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band); - sband = priv->wdev->wiphy->bands[radio_type]; + sband = priv->wdev.wiphy->bands[radio_type]; if (bss_desc->bcn_ht_cap) { ht_cap = (struct mwifiex_ie_types_htcap *) *buffer; @@ -558,10 +558,10 @@ int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac) spin_lock_irqsave(&priv->sta_list_spinlock, flags); sta_ptr = mwifiex_get_sta_entry(priv, peer_mac); if (!sta_ptr) { + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); dev_warn(priv->adapter->dev, "BA setup with unknown TDLS peer %pM!\n", peer_mac); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); return -1; } if (sta_ptr->is_11ac_enabled) diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h index f275675cdbd3..8e2e39422ad8 100644 --- a/drivers/net/wireless/mwifiex/11n.h +++ b/drivers/net/wireless/mwifiex/11n.h @@ -130,7 +130,9 @@ static inline u8 mwifiex_space_avail_for_new_ba_stream( { struct mwifiex_private *priv; u8 i; - u32 ba_stream_num = 0; + u32 ba_stream_num = 0, ba_stream_max; + + ba_stream_max = MWIFIEX_MAX_TX_BASTREAM_SUPPORTED; for (i = 0; i < adapter->priv_num; i++) { priv = adapter->priv[i]; @@ -139,8 +141,14 @@ static inline u8 mwifiex_space_avail_for_new_ba_stream( &priv->tx_ba_stream_tbl_ptr); } - return ((ba_stream_num < - MWIFIEX_MAX_TX_BASTREAM_SUPPORTED) ? true : false); + if (adapter->fw_api_ver == MWIFIEX_FW_V15) { + ba_stream_max = + GETSUPP_TXBASTREAMS(adapter->hw_dot_11n_dev_cap); + if (!ba_stream_max) + ba_stream_max = MWIFIEX_MAX_TX_BASTREAM_SUPPORTED; + } + + return ((ba_stream_num < ba_stream_max) ? true : false); } /* diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c index 8720a3d3c755..9b983b5cebbd 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/mwifiex/11n_aggr.c @@ -101,6 +101,13 @@ mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv, { struct txpd *local_tx_pd; struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); + unsigned int pad; + int headroom = (priv->adapter->iface_type == + MWIFIEX_USB) ? 0 : INTF_HEADER_LEN; + + pad = ((void *)skb->data - sizeof(*local_tx_pd) - + headroom - NULL) & (MWIFIEX_DMA_ALIGN_SZ - 1); + skb_push(skb, pad); skb_push(skb, sizeof(*local_tx_pd)); @@ -114,10 +121,12 @@ mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv, local_tx_pd->bss_num = priv->bss_num; local_tx_pd->bss_type = priv->bss_type; /* Always zero as the data is followed by struct txpd */ - local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd)); + local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd) + + pad); local_tx_pd->tx_pkt_type = cpu_to_le16(PKT_TYPE_AMSDU); local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len - - sizeof(*local_tx_pd)); + sizeof(*local_tx_pd) - + pad); if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET; @@ -182,7 +191,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, ra_list_flags); return -1; } - skb_reserve(skb_aggr, headroom + sizeof(struct txpd)); + skb_reserve(skb_aggr, MWIFIEX_MIN_DATA_HEADER_LEN); tx_info_aggr = MWIFIEX_SKB_TXCB(skb_aggr); memset(tx_info_aggr, 0, sizeof(*tx_info_aggr)); diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c index d73fda312c87..a2e8817b56d8 100644 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c @@ -45,7 +45,7 @@ static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv, skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length)); ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr, - priv->wdev->iftype, 0, false); + priv->wdev.iftype, 0, false); while (!skb_queue_empty(&list)) { rx_skb = __skb_dequeue(&list); @@ -353,9 +353,6 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, spin_lock_irqsave(&priv->sta_list_spinlock, flags); if (mwifiex_queuing_ra_based(priv)) { - dev_dbg(priv->adapter->dev, - "info: AP/ADHOC:last_seq=%d start_win=%d\n", - last_seq, new_node->start_win); if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) { node = mwifiex_get_sta_entry(priv, ta); if (node) @@ -370,6 +367,9 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, } spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + dev_dbg(priv->adapter->dev, "info: last_seq=%d start_win=%d\n", + last_seq, new_node->start_win); + if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM && last_seq >= new_node->start_win) { new_node->start_win = last_seq + 1; @@ -391,10 +391,8 @@ mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, new_node->timer_context.priv = priv; new_node->timer_context.timer_is_set = false; - init_timer(&new_node->timer_context.timer); - new_node->timer_context.timer.function = mwifiex_flush_data; - new_node->timer_context.timer.data = - (unsigned long) &new_node->timer_context; + setup_timer(&new_node->timer_context.timer, mwifiex_flush_data, + (unsigned long)&new_node->timer_context); for (i = 0; i < win_size; ++i) new_node->rx_reorder_ptr[i] = NULL; @@ -468,10 +466,10 @@ int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, sta_ptr = mwifiex_get_sta_entry(priv, cmd_addba_req->peer_mac_addr); if (!sta_ptr) { + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); dev_warn(priv->adapter->dev, "BA setup with unknown TDLS peer %pM!\n", cmd_addba_req->peer_mac_addr); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); return -1; } if (sta_ptr->is_11ac_enabled) diff --git a/drivers/net/wireless/mwifiex/Makefile b/drivers/net/wireless/mwifiex/Makefile index 9487d728ac20..fdfd9bf15ed4 100644 --- a/drivers/net/wireless/mwifiex/Makefile +++ b/drivers/net/wireless/mwifiex/Makefile @@ -53,3 +53,5 @@ obj-$(CONFIG_MWIFIEX_PCIE) += mwifiex_pcie.o mwifiex_usb-y += usb.o obj-$(CONFIG_MWIFIEX_USB) += mwifiex_usb.o + +ccflags-y += -D__CHECK_ENDIAN diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 4a66a6555366..41c8e25df954 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -590,77 +590,62 @@ mwifiex_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); struct mwifiex_private *priv; struct mwifiex_uap_bss_param *bss_cfg; - int ret, bss_started, i; - - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - - switch (priv->bss_role) { - case MWIFIEX_BSS_ROLE_UAP: - bss_cfg = kzalloc(sizeof(struct mwifiex_uap_bss_param), - GFP_KERNEL); - if (!bss_cfg) - return -ENOMEM; - - mwifiex_set_sys_config_invalid_data(bss_cfg); - - if (changed & WIPHY_PARAM_RTS_THRESHOLD) - bss_cfg->rts_threshold = wiphy->rts_threshold; - if (changed & WIPHY_PARAM_FRAG_THRESHOLD) - bss_cfg->frag_threshold = wiphy->frag_threshold; - if (changed & WIPHY_PARAM_RETRY_LONG) - bss_cfg->retry_limit = wiphy->retry_long; - - bss_started = priv->bss_started; - - ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP, - HostCmd_ACT_GEN_SET, 0, - NULL, true); - if (ret) { - wiphy_err(wiphy, "Failed to stop the BSS\n"); - kfree(bss_cfg); - return ret; - } + int ret; - ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, - HostCmd_ACT_GEN_SET, - UAP_BSS_PARAMS_I, bss_cfg, - false); + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - kfree(bss_cfg); + switch (priv->bss_role) { + case MWIFIEX_BSS_ROLE_UAP: + if (priv->bss_started) { + dev_err(adapter->dev, + "cannot change wiphy params when bss started"); + return -EINVAL; + } - if (ret) { - wiphy_err(wiphy, "Failed to set bss config\n"); - return ret; - } + bss_cfg = kzalloc(sizeof(*bss_cfg), GFP_KERNEL); + if (!bss_cfg) + return -ENOMEM; - if (!bss_started) - break; + mwifiex_set_sys_config_invalid_data(bss_cfg); - ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START, - HostCmd_ACT_GEN_SET, 0, - NULL, false); - if (ret) { - wiphy_err(wiphy, "Failed to start BSS\n"); - return ret; - } + if (changed & WIPHY_PARAM_RTS_THRESHOLD) + bss_cfg->rts_threshold = wiphy->rts_threshold; + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) + bss_cfg->frag_threshold = wiphy->frag_threshold; + if (changed & WIPHY_PARAM_RETRY_LONG) + bss_cfg->retry_limit = wiphy->retry_long; + + ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, + HostCmd_ACT_GEN_SET, + UAP_BSS_PARAMS_I, bss_cfg, + false); + + kfree(bss_cfg); + if (ret) { + wiphy_err(wiphy, "Failed to set wiphy phy params\n"); + return ret; + } + break; - break; case MWIFIEX_BSS_ROLE_STA: - if (changed & WIPHY_PARAM_RTS_THRESHOLD) { - ret = mwifiex_set_rts(priv, - wiphy->rts_threshold); - if (ret) - return ret; - } - if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { - ret = mwifiex_set_frag(priv, - wiphy->frag_threshold); - if (ret) - return ret; - } - break; + if (priv->media_connected) { + dev_err(adapter->dev, + "cannot change wiphy params when connected"); + return -EINVAL; } + if (changed & WIPHY_PARAM_RTS_THRESHOLD) { + ret = mwifiex_set_rts(priv, + wiphy->rts_threshold); + if (ret) + return ret; + } + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { + ret = mwifiex_set_frag(priv, + wiphy->frag_threshold); + if (ret) + return ret; + } + break; } return 0; @@ -671,9 +656,6 @@ mwifiex_cfg80211_deinit_p2p(struct mwifiex_private *priv) { u16 mode = P2P_MODE_DISABLE; - if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) - mwifiex_set_bss_role(priv, MWIFIEX_BSS_ROLE_STA); - if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, HostCmd_ACT_GEN_SET, 0, &mode, true)) return -1; @@ -730,12 +712,249 @@ mwifiex_cfg80211_init_p2p_go(struct mwifiex_private *priv) HostCmd_ACT_GEN_SET, 0, &mode, true)) return -1; - if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP) - mwifiex_set_bss_role(priv, MWIFIEX_BSS_ROLE_UAP); + return 0; +} + +static int mwifiex_deinit_priv_params(struct mwifiex_private *priv) +{ + priv->mgmt_frame_mask = 0; + if (mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG, + HostCmd_ACT_GEN_SET, 0, + &priv->mgmt_frame_mask, false)) { + dev_warn(priv->adapter->dev, + "could not unregister mgmt frame rx\n"); + return -1; + } + + mwifiex_deauthenticate(priv, NULL); + mwifiex_free_priv(priv); + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; + + return 0; +} + +static int +mwifiex_init_new_priv_params(struct mwifiex_private *priv, + struct net_device *dev, + enum nl80211_iftype type) +{ + mwifiex_init_priv(priv); + + priv->bss_mode = type; + priv->wdev.iftype = type; + + mwifiex_init_priv_params(priv, priv->netdev); + priv->bss_started = 0; + + switch (type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + priv->bss_role = MWIFIEX_BSS_ROLE_STA; + priv->bss_type = MWIFIEX_BSS_TYPE_STA; + break; + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + priv->bss_role = MWIFIEX_BSS_ROLE_STA; + priv->bss_type = MWIFIEX_BSS_TYPE_P2P; + break; + case NL80211_IFTYPE_AP: + priv->bss_type = MWIFIEX_BSS_TYPE_UAP; + priv->bss_role = MWIFIEX_BSS_ROLE_UAP; + break; + default: + dev_err(priv->adapter->dev, + "%s: changing to %d not supported\n", + dev->name, type); + return -EOPNOTSUPP; + } return 0; } +static int +mwifiex_change_vif_to_p2p(struct net_device *dev, + enum nl80211_iftype curr_iftype, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct mwifiex_private *priv; + struct mwifiex_adapter *adapter; + + priv = mwifiex_netdev_get_priv(dev); + + if (!priv) + return -1; + + adapter = priv->adapter; + + if (adapter->curr_iface_comb.p2p_intf == + adapter->iface_limit.p2p_intf) { + dev_err(adapter->dev, + "cannot create multiple P2P ifaces\n"); + return -1; + } + + dev_dbg(priv->adapter->dev, "%s: changing role to p2p\n", dev->name); + + if (mwifiex_deinit_priv_params(priv)) + return -1; + if (mwifiex_init_new_priv_params(priv, dev, type)) + return -1; + + switch (type) { + case NL80211_IFTYPE_P2P_CLIENT: + if (mwifiex_cfg80211_init_p2p_client(priv)) + return -EFAULT; + break; + case NL80211_IFTYPE_P2P_GO: + if (mwifiex_cfg80211_init_p2p_go(priv)) + return -EFAULT; + break; + default: + dev_err(priv->adapter->dev, + "%s: changing to %d not supported\n", + dev->name, type); + return -EOPNOTSUPP; + } + + if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, NULL, true)) + return -1; + + if (mwifiex_sta_init_cmd(priv, false, false)) + return -1; + + switch (curr_iftype) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + adapter->curr_iface_comb.sta_intf--; + break; + case NL80211_IFTYPE_AP: + adapter->curr_iface_comb.uap_intf--; + break; + default: + break; + } + + adapter->curr_iface_comb.p2p_intf++; + dev->ieee80211_ptr->iftype = type; + + return 0; +} + +static int +mwifiex_change_vif_to_sta_adhoc(struct net_device *dev, + enum nl80211_iftype curr_iftype, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct mwifiex_private *priv; + struct mwifiex_adapter *adapter; + + priv = mwifiex_netdev_get_priv(dev); + + if (!priv) + return -1; + + adapter = priv->adapter; + + if ((curr_iftype != NL80211_IFTYPE_P2P_CLIENT && + curr_iftype != NL80211_IFTYPE_P2P_GO) && + (adapter->curr_iface_comb.sta_intf == + adapter->iface_limit.sta_intf)) { + dev_err(adapter->dev, + "cannot create multiple station/adhoc ifaces\n"); + return -1; + } + + if (type == NL80211_IFTYPE_STATION) + dev_notice(adapter->dev, + "%s: changing role to station\n", dev->name); + else + dev_notice(adapter->dev, + "%s: changing role to adhoc\n", dev->name); + + if (mwifiex_deinit_priv_params(priv)) + return -1; + if (mwifiex_init_new_priv_params(priv, dev, type)) + return -1; + if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, NULL, true)) + return -1; + if (mwifiex_sta_init_cmd(priv, false, false)) + return -1; + + switch (curr_iftype) { + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + adapter->curr_iface_comb.p2p_intf--; + break; + case NL80211_IFTYPE_AP: + adapter->curr_iface_comb.uap_intf--; + break; + default: + break; + } + + adapter->curr_iface_comb.sta_intf++; + dev->ieee80211_ptr->iftype = type; + return 0; +} + +static int +mwifiex_change_vif_to_ap(struct net_device *dev, + enum nl80211_iftype curr_iftype, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct mwifiex_private *priv; + struct mwifiex_adapter *adapter; + + priv = mwifiex_netdev_get_priv(dev); + + if (!priv) + return -1; + + adapter = priv->adapter; + + if (adapter->curr_iface_comb.uap_intf == + adapter->iface_limit.uap_intf) { + dev_err(adapter->dev, + "cannot create multiple AP ifaces\n"); + return -1; + } + + dev_notice(adapter->dev, "%s: changing role to AP\n", dev->name); + + if (mwifiex_deinit_priv_params(priv)) + return -1; + if (mwifiex_init_new_priv_params(priv, dev, type)) + return -1; + if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, NULL, true)) + return -1; + if (mwifiex_sta_init_cmd(priv, false, false)) + return -1; + + switch (curr_iftype) { + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + adapter->curr_iface_comb.p2p_intf--; + break; + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + adapter->curr_iface_comb.sta_intf--; + break; + default: + break; + } + + adapter->curr_iface_comb.uap_intf++; + dev->ieee80211_ptr->iftype = type; + return 0; +} /* * CFG802.11 operation handler to change interface type. */ @@ -745,19 +964,32 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, enum nl80211_iftype type, u32 *flags, struct vif_params *params) { - int ret; struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + enum nl80211_iftype curr_iftype = dev->ieee80211_ptr->iftype; - switch (dev->ieee80211_ptr->iftype) { + switch (curr_iftype) { case NL80211_IFTYPE_ADHOC: switch (type) { case NL80211_IFTYPE_STATION: - break; + priv->bss_mode = type; + priv->sec_info.authentication_mode = + NL80211_AUTHTYPE_OPEN_SYSTEM; + dev->ieee80211_ptr->iftype = type; + mwifiex_deauthenticate(priv, NULL); + return mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, NULL, + true); + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + return mwifiex_change_vif_to_p2p(dev, curr_iftype, + type, flags, params); + case NL80211_IFTYPE_AP: + return mwifiex_change_vif_to_ap(dev, curr_iftype, type, + flags, params); case NL80211_IFTYPE_UNSPECIFIED: wiphy_warn(wiphy, "%s: kept type as IBSS\n", dev->name); case NL80211_IFTYPE_ADHOC: /* This shouldn't happen */ return 0; - case NL80211_IFTYPE_AP: default: wiphy_err(wiphy, "%s: changing to %d not supported\n", dev->name, type); @@ -767,22 +999,25 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, case NL80211_IFTYPE_STATION: switch (type) { case NL80211_IFTYPE_ADHOC: - break; - case NL80211_IFTYPE_P2P_CLIENT: - if (mwifiex_cfg80211_init_p2p_client(priv)) - return -EFAULT; + priv->bss_mode = type; + priv->sec_info.authentication_mode = + NL80211_AUTHTYPE_OPEN_SYSTEM; dev->ieee80211_ptr->iftype = type; - return 0; + mwifiex_deauthenticate(priv, NULL); + return mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, NULL, + true); + case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_GO: - if (mwifiex_cfg80211_init_p2p_go(priv)) - return -EFAULT; - dev->ieee80211_ptr->iftype = type; - return 0; + return mwifiex_change_vif_to_p2p(dev, curr_iftype, + type, flags, params); + case NL80211_IFTYPE_AP: + return mwifiex_change_vif_to_ap(dev, curr_iftype, type, + flags, params); case NL80211_IFTYPE_UNSPECIFIED: wiphy_warn(wiphy, "%s: kept type as STA\n", dev->name); case NL80211_IFTYPE_STATION: /* This shouldn't happen */ return 0; - case NL80211_IFTYPE_AP: default: wiphy_err(wiphy, "%s: changing to %d not supported\n", dev->name, type); @@ -791,12 +1026,20 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, break; case NL80211_IFTYPE_AP: switch (type) { + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_STATION: + return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype, + type, flags, + params); + break; + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + return mwifiex_change_vif_to_p2p(dev, curr_iftype, + type, flags, params); case NL80211_IFTYPE_UNSPECIFIED: wiphy_warn(wiphy, "%s: kept type as AP\n", dev->name); case NL80211_IFTYPE_AP: /* This shouldn't happen */ return 0; - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_STATION: default: wiphy_err(wiphy, "%s: changing to %d not supported\n", dev->name, type); @@ -807,11 +1050,30 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, case NL80211_IFTYPE_P2P_GO: switch (type) { case NL80211_IFTYPE_STATION: - if (mwifiex_cfg80211_deinit_p2p(priv)) + if (mwifiex_cfg80211_init_p2p_client(priv)) return -EFAULT; dev->ieee80211_ptr->iftype = type; + break; + case NL80211_IFTYPE_ADHOC: + if (mwifiex_cfg80211_deinit_p2p(priv)) + return -EFAULT; + return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype, + type, flags, + params); + break; + case NL80211_IFTYPE_AP: + if (mwifiex_cfg80211_deinit_p2p(priv)) + return -EFAULT; + return mwifiex_change_vif_to_ap(dev, curr_iftype, type, + flags, params); + case NL80211_IFTYPE_UNSPECIFIED: + wiphy_warn(wiphy, "%s: kept type as P2P\n", dev->name); + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: return 0; default: + wiphy_err(wiphy, "%s: changing to %d not supported\n", + dev->name, type); return -EOPNOTSUPP; } break; @@ -821,16 +1083,8 @@ mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, return -EOPNOTSUPP; } - dev->ieee80211_ptr->iftype = type; - priv->bss_mode = type; - mwifiex_deauthenticate(priv, NULL); - - priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; - - ret = mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, - HostCmd_ACT_GEN_SET, 0, NULL, true); - return ret; + return 0; } static void @@ -856,16 +1110,16 @@ mwifiex_parse_htinfo(struct mwifiex_private *priv, u8 tx_htinfo, /* HT or VHT */ switch (tx_htinfo & (BIT(3) | BIT(2))) { case 0: - /* This will be 20MHz */ + rate->bw = RATE_INFO_BW_20; break; case (BIT(2)): - rate->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + rate->bw = RATE_INFO_BW_40; break; case (BIT(3)): - rate->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; + rate->bw = RATE_INFO_BW_80; break; case (BIT(3) | BIT(2)): - rate->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH; + rate->bw = RATE_INFO_BW_160; break; } @@ -885,8 +1139,9 @@ mwifiex_parse_htinfo(struct mwifiex_private *priv, u8 tx_htinfo, if ((tx_htinfo & BIT(0)) && (priv->tx_rate < 16)) { rate->mcs = priv->tx_rate; rate->flags |= RATE_INFO_FLAGS_MCS; + rate->bw = RATE_INFO_BW_20; if (tx_htinfo & BIT(1)) - rate->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + rate->bw = RATE_INFO_BW_40; if (tx_htinfo & BIT(2)) rate->flags |= RATE_INFO_FLAGS_SHORT_GI; } @@ -910,10 +1165,10 @@ mwifiex_dump_station_info(struct mwifiex_private *priv, { u32 rate; - sinfo->filled = STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES | - STATION_INFO_RX_PACKETS | STATION_INFO_TX_PACKETS | - STATION_INFO_TX_BITRATE | - STATION_INFO_SIGNAL | STATION_INFO_SIGNAL_AVG; + sinfo->filled = BIT(NL80211_STA_INFO_RX_BYTES) | BIT(NL80211_STA_INFO_TX_BYTES) | + BIT(NL80211_STA_INFO_RX_PACKETS) | BIT(NL80211_STA_INFO_TX_PACKETS) | + BIT(NL80211_STA_INFO_TX_BITRATE) | + BIT(NL80211_STA_INFO_SIGNAL) | BIT(NL80211_STA_INFO_SIGNAL_AVG); /* Get signal information from the firmware */ if (mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO, @@ -944,7 +1199,7 @@ mwifiex_dump_station_info(struct mwifiex_private *priv, sinfo->txrate.legacy = rate * 5; if (priv->bss_mode == NL80211_IFTYPE_STATION) { - sinfo->filled |= STATION_INFO_BSS_PARAM; + sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM); sinfo->bss_param.flags = 0; if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap & WLAN_CAPABILITY_SHORT_PREAMBLE) @@ -1037,10 +1292,11 @@ mwifiex_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *dev, survey->channel = ieee80211_get_channel(wiphy, ieee80211_channel_to_frequency(pchan_stats[idx].chan_num, band)); survey->filled = SURVEY_INFO_NOISE_DBM | - SURVEY_INFO_CHANNEL_TIME | SURVEY_INFO_CHANNEL_TIME_BUSY; + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY; survey->noise = pchan_stats[idx].noise; - survey->channel_time = pchan_stats[idx].cca_scan_dur; - survey->channel_time_busy = pchan_stats[idx].cca_busy_dur; + survey->time = pchan_stats[idx].cca_scan_dur; + survey->time_busy = pchan_stats[idx].cca_busy_dur; return 0; } @@ -1395,10 +1651,13 @@ static int mwifiex_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + mwifiex_abort_cac(priv); + if (mwifiex_del_mgmt_ies(priv)) wiphy_err(wiphy, "Failed to delete mgmt IEs!\n"); priv->ap_11n_enabled = 0; + memset(&priv->bss_cfg, 0, sizeof(priv->bss_cfg)); if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP, HostCmd_ACT_GEN_SET, 0, NULL, true)) { @@ -1420,12 +1679,9 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, { struct mwifiex_uap_bss_param *bss_cfg; struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - u8 config_bands = 0; if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP) return -1; - if (mwifiex_set_mgmt_ies(priv, ¶ms->beacon)) - return -1; bss_cfg = kzalloc(sizeof(struct mwifiex_uap_bss_param), GFP_KERNEL); if (!bss_cfg) @@ -1442,6 +1698,11 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, memcpy(bss_cfg->ssid.ssid, params->ssid, params->ssid_len); bss_cfg->ssid.ssid_len = params->ssid_len; } + if (params->inactivity_timeout > 0) { + /* sta_ao_timer/ps_sta_ao_timer is in unit of 100ms */ + bss_cfg->sta_ao_timer = 10 * params->inactivity_timeout; + bss_cfg->ps_sta_ao_timer = 10 * params->inactivity_timeout; + } switch (params->hidden_ssid) { case NL80211_HIDDEN_SSID_NOT_IN_USE: @@ -1457,33 +1718,8 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, return -EINVAL; } - bss_cfg->channel = ieee80211_frequency_to_channel( - params->chandef.chan->center_freq); - - /* Set appropriate bands */ - if (params->chandef.chan->band == IEEE80211_BAND_2GHZ) { - bss_cfg->band_cfg = BAND_CONFIG_BG; - config_bands = BAND_B | BAND_G; - - if (params->chandef.width > NL80211_CHAN_WIDTH_20_NOHT) - config_bands |= BAND_GN; - } else { - bss_cfg->band_cfg = BAND_CONFIG_A; - config_bands = BAND_A; - - if (params->chandef.width > NL80211_CHAN_WIDTH_20_NOHT) - config_bands |= BAND_AN; - - if (params->chandef.width > NL80211_CHAN_WIDTH_40) - config_bands |= BAND_AAC; - } - - if (!((config_bands | priv->adapter->fw_bands) & - ~priv->adapter->fw_bands)) - priv->adapter->config_bands = config_bands; - + mwifiex_uap_set_channel(bss_cfg, params->chandef); mwifiex_set_uap_rates(bss_cfg, params); - mwifiex_send_domain_info_cmd_fw(wiphy); if (mwifiex_set_secure_params(priv, bss_cfg, params)) { kfree(bss_cfg); @@ -1506,45 +1742,29 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, mwifiex_set_wmm_params(priv, bss_cfg, params); - if (params->inactivity_timeout > 0) { - /* sta_ao_timer/ps_sta_ao_timer is in unit of 100ms */ - bss_cfg->sta_ao_timer = 10 * params->inactivity_timeout; - bss_cfg->ps_sta_ao_timer = 10 * params->inactivity_timeout; + if (mwifiex_is_11h_active(priv) && + !cfg80211_chandef_dfs_required(wiphy, ¶ms->chandef, + priv->bss_mode)) { + dev_dbg(priv->adapter->dev, "Disable 11h extensions in FW\n"); + if (mwifiex_11h_activate(priv, false)) { + dev_err(priv->adapter->dev, + "Failed to disable 11h extensions!!"); + return -1; + } + priv->state_11h.is_11h_active = true; } - if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP, - HostCmd_ACT_GEN_SET, 0, NULL, true)) { - wiphy_err(wiphy, "Failed to stop the BSS\n"); + if (mwifiex_config_start_uap(priv, bss_cfg)) { + wiphy_err(wiphy, "Failed to start AP\n"); kfree(bss_cfg); return -1; } - if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, - HostCmd_ACT_GEN_SET, - UAP_BSS_PARAMS_I, bss_cfg, false)) { - wiphy_err(wiphy, "Failed to set the SSID\n"); - kfree(bss_cfg); + if (mwifiex_set_mgmt_ies(priv, ¶ms->beacon)) return -1; - } + memcpy(&priv->bss_cfg, bss_cfg, sizeof(priv->bss_cfg)); kfree(bss_cfg); - - if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START, - HostCmd_ACT_GEN_SET, 0, NULL, false)) { - wiphy_err(wiphy, "Failed to start the BSS\n"); - return -1; - } - - if (priv->sec_info.wep_enabled) - priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; - else - priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; - - if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, - HostCmd_ACT_GEN_SET, 0, - &priv->curr_pkt_filter, true)) - return -1; - return 0; } @@ -1603,15 +1823,15 @@ static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv) ie_len = ie_buf[1] + sizeof(struct ieee_types_header); band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); - chan = __ieee80211_get_channel(priv->wdev->wiphy, + chan = __ieee80211_get_channel(priv->wdev.wiphy, ieee80211_channel_to_frequency(bss_info.bss_chan, band)); - bss = cfg80211_inform_bss(priv->wdev->wiphy, chan, + bss = cfg80211_inform_bss(priv->wdev.wiphy, chan, 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); + cfg80211_put_bss(priv->wdev.wiphy, bss); memcpy(priv->cfg_bssid, bss_info.bssid, ETH_ALEN); return 0; @@ -1732,12 +1952,12 @@ done: /* Find the BSS we want using available scan results */ if (mode == NL80211_IFTYPE_ADHOC) - bss = cfg80211_get_bss(priv->wdev->wiphy, channel, + bss = cfg80211_get_bss(priv->wdev.wiphy, channel, bssid, ssid, ssid_len, WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); else - bss = cfg80211_get_bss(priv->wdev->wiphy, channel, + bss = cfg80211_get_bss(priv->wdev.wiphy, channel, bssid, ssid, ssid_len, WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); @@ -1784,6 +2004,7 @@ mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_adapter *adapter = priv->adapter; int ret; if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) { @@ -1793,11 +2014,18 @@ mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, return -EINVAL; } - if (priv->wdev && priv->wdev->current_bss) { + if (priv->wdev.current_bss) { wiphy_warn(wiphy, "%s: already connected\n", dev->name); return -EALREADY; } + if (adapter->surprise_removed || adapter->is_cmd_timedout) { + wiphy_err(wiphy, + "%s: Ignore connection. Card removed or FW in bad state\n", + dev->name); + return -EFAULT; + } + wiphy_dbg(wiphy, "info: Trying to associate to %s and bssid %pM\n", (char *) sme->ssid, sme->bssid); @@ -1844,7 +2072,7 @@ mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, static int mwifiex_set_ibss_params(struct mwifiex_private *priv, struct cfg80211_ibss_params *params) { - struct wiphy *wiphy = priv->wdev->wiphy; + struct wiphy *wiphy = priv->wdev.wiphy; struct mwifiex_adapter *adapter = priv->adapter; int index = 0, i; u8 config_bands = 0; @@ -2169,6 +2397,7 @@ mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info, ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; } +#define MWIFIEX_MAX_WQ_LEN 30 /* * create a new virtual interface with the given name */ @@ -2182,7 +2411,7 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, struct mwifiex_private *priv; struct net_device *dev; void *mdev_priv; - struct wireless_dev *wdev; + char dfs_cac_str[MWIFIEX_MAX_WQ_LEN], dfs_chsw_str[MWIFIEX_MAX_WQ_LEN]; if (!adapter) return ERR_PTR(-EFAULT); @@ -2191,20 +2420,22 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_ADHOC: - priv = adapter->priv[MWIFIEX_BSS_TYPE_STA]; - if (priv->bss_mode) { + if (adapter->curr_iface_comb.sta_intf == + adapter->iface_limit.sta_intf) { wiphy_err(wiphy, "cannot create multiple sta/adhoc ifaces\n"); return ERR_PTR(-EINVAL); } - wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); - if (!wdev) - return ERR_PTR(-ENOMEM); + priv = mwifiex_get_unused_priv(adapter); + if (!priv) { + wiphy_err(wiphy, + "could not get free private struct\n"); + return ERR_PTR(-EFAULT); + } - wdev->wiphy = wiphy; - priv->wdev = wdev; - wdev->iftype = NL80211_IFTYPE_STATION; + priv->wdev.wiphy = wiphy; + priv->wdev.iftype = NL80211_IFTYPE_STATION; if (type == NL80211_IFTYPE_UNSPECIFIED) priv->bss_mode = NL80211_IFTYPE_STATION; @@ -2219,20 +2450,22 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, break; case NL80211_IFTYPE_AP: - priv = adapter->priv[MWIFIEX_BSS_TYPE_UAP]; - - if (priv->bss_mode) { - wiphy_err(wiphy, "Can't create multiple AP interfaces"); + if (adapter->curr_iface_comb.uap_intf == + adapter->iface_limit.uap_intf) { + wiphy_err(wiphy, + "cannot create multiple AP ifaces\n"); return ERR_PTR(-EINVAL); } - wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); - if (!wdev) - return ERR_PTR(-ENOMEM); + priv = mwifiex_get_unused_priv(adapter); + if (!priv) { + wiphy_err(wiphy, + "could not get free private struct\n"); + return ERR_PTR(-EFAULT); + } - priv->wdev = wdev; - wdev->wiphy = wiphy; - wdev->iftype = NL80211_IFTYPE_AP; + priv->wdev.wiphy = wiphy; + priv->wdev.iftype = NL80211_IFTYPE_AP; priv->bss_type = MWIFIEX_BSS_TYPE_UAP; priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; @@ -2244,24 +2477,25 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, break; case NL80211_IFTYPE_P2P_CLIENT: - priv = adapter->priv[MWIFIEX_BSS_TYPE_P2P]; - - if (priv->bss_mode) { - wiphy_err(wiphy, "Can't create multiple P2P ifaces"); + if (adapter->curr_iface_comb.p2p_intf == + adapter->iface_limit.p2p_intf) { + wiphy_err(wiphy, + "cannot create multiple P2P ifaces\n"); return ERR_PTR(-EINVAL); } - wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); - if (!wdev) - return ERR_PTR(-ENOMEM); - - priv->wdev = wdev; - wdev->wiphy = wiphy; + priv = mwifiex_get_unused_priv(adapter); + if (!priv) { + wiphy_err(wiphy, + "could not get free private struct\n"); + return ERR_PTR(-EFAULT); + } + priv->wdev.wiphy = wiphy; /* At start-up, wpa_supplicant tries to change the interface * to NL80211_IFTYPE_STATION if it is not managed mode. */ - wdev->iftype = NL80211_IFTYPE_P2P_CLIENT; + priv->wdev.iftype = NL80211_IFTYPE_P2P_CLIENT; priv->bss_mode = NL80211_IFTYPE_P2P_CLIENT; /* Setting bss_type to P2P tells firmware that this interface @@ -2277,8 +2511,9 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, priv->bss_num = 0; if (mwifiex_cfg80211_init_p2p_client(priv)) { - wdev = ERR_PTR(-EFAULT); - goto done; + memset(&priv->wdev, 0, sizeof(priv->wdev)); + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + return ERR_PTR(-EFAULT); } break; @@ -2292,9 +2527,10 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, IEEE80211_NUM_ACS, 1); if (!dev) { wiphy_err(wiphy, "no memory available for netdevice\n"); + memset(&priv->wdev, 0, sizeof(priv->wdev)); + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; - wdev = ERR_PTR(-ENOMEM); - goto done; + return ERR_PTR(-ENOMEM); } mwifiex_init_priv_params(priv, dev); @@ -2314,7 +2550,7 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, &wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap, priv); dev_net_set(dev, wiphy_net(wiphy)); - dev->ieee80211_ptr = priv->wdev; + dev->ieee80211_ptr = &priv->wdev; dev->ieee80211_ptr->iftype = priv->bss_mode; memcpy(dev->dev_addr, wiphy->perm_addr, ETH_ALEN); SET_NETDEV_DEV(dev, wiphy_dev(wiphy)); @@ -2335,10 +2571,47 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, free_netdev(dev); priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; priv->netdev = NULL; - wdev = ERR_PTR(-EFAULT); - goto done; + memset(&priv->wdev, 0, sizeof(priv->wdev)); + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + return ERR_PTR(-EFAULT); + } + + strcpy(dfs_cac_str, "MWIFIEX_DFS_CAC"); + strcat(dfs_cac_str, name); + priv->dfs_cac_workqueue = alloc_workqueue(dfs_cac_str, + WQ_HIGHPRI | + WQ_MEM_RECLAIM | + WQ_UNBOUND, 1); + if (!priv->dfs_cac_workqueue) { + wiphy_err(wiphy, "cannot register virtual network device\n"); + free_netdev(dev); + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + priv->netdev = NULL; + memset(&priv->wdev, 0, sizeof(priv->wdev)); + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + return ERR_PTR(-ENOMEM); } + INIT_DELAYED_WORK(&priv->dfs_cac_work, mwifiex_dfs_cac_work_queue); + + strcpy(dfs_chsw_str, "MWIFIEX_DFS_CHSW"); + strcat(dfs_chsw_str, name); + priv->dfs_chan_sw_workqueue = alloc_workqueue(dfs_chsw_str, + WQ_HIGHPRI | WQ_UNBOUND | + WQ_MEM_RECLAIM, 1); + if (!priv->dfs_chan_sw_workqueue) { + wiphy_err(wiphy, "cannot register virtual network device\n"); + free_netdev(dev); + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + priv->netdev = NULL; + memset(&priv->wdev, 0, sizeof(priv->wdev)); + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + return ERR_PTR(-ENOMEM); + } + + INIT_DELAYED_WORK(&priv->dfs_chan_sw_work, + mwifiex_dfs_chan_sw_work_queue); + sema_init(&priv->async_sem, 1); dev_dbg(adapter->dev, "info: %s: Marvell 802.11 Adapter\n", dev->name); @@ -2347,13 +2620,24 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, mwifiex_dev_debugfs_init(priv); #endif -done: - if (IS_ERR(wdev)) { - kfree(priv->wdev); - priv->wdev = NULL; + switch (type) { + case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + adapter->curr_iface_comb.sta_intf++; + break; + case NL80211_IFTYPE_AP: + adapter->curr_iface_comb.uap_intf++; + break; + case NL80211_IFTYPE_P2P_CLIENT: + adapter->curr_iface_comb.p2p_intf++; + break; + default: + wiphy_err(wiphy, "type not supported\n"); + return ERR_PTR(-EINVAL); } - return wdev; + return &priv->wdev; } EXPORT_SYMBOL_GPL(mwifiex_add_virtual_intf); @@ -2363,12 +2647,13 @@ EXPORT_SYMBOL_GPL(mwifiex_add_virtual_intf); int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); + struct mwifiex_adapter *adapter = priv->adapter; #ifdef CONFIG_DEBUG_FS mwifiex_dev_debugfs_remove(priv); #endif - mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter); + mwifiex_stop_net_dev_queue(priv->netdev, adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); @@ -2376,16 +2661,48 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) if (wdev->netdev->reg_state == NETREG_REGISTERED) unregister_netdevice(wdev->netdev); + if (priv->dfs_cac_workqueue) { + flush_workqueue(priv->dfs_cac_workqueue); + destroy_workqueue(priv->dfs_cac_workqueue); + priv->dfs_cac_workqueue = NULL; + } + + if (priv->dfs_chan_sw_workqueue) { + flush_workqueue(priv->dfs_chan_sw_workqueue); + destroy_workqueue(priv->dfs_chan_sw_workqueue); + priv->dfs_chan_sw_workqueue = NULL; + } /* Clear the priv in adapter */ priv->netdev->ieee80211_ptr = NULL; priv->netdev = NULL; - kfree(wdev); - priv->wdev = NULL; + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; priv->media_connected = false; + switch (priv->bss_mode) { + case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + adapter->curr_iface_comb.sta_intf++; + break; + case NL80211_IFTYPE_AP: + adapter->curr_iface_comb.uap_intf++; + break; + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + adapter->curr_iface_comb.p2p_intf++; + break; + default: + dev_err(adapter->dev, "del_virtual_intf: type not supported\n"); + break; + } + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) + kfree(priv->hist_data); + return 0; } EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf); @@ -2421,30 +2738,16 @@ mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq, } #ifdef CONFIG_PM -static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, - struct cfg80211_wowlan *wowlan) +static int mwifiex_set_mef_filter(struct mwifiex_private *priv, + struct cfg80211_wowlan *wowlan) { - struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); - struct mwifiex_ds_mef_cfg mef_cfg; - struct mwifiex_mef_entry *mef_entry; - int i, filt_num = 0, ret; + int i, filt_num = 0, ret = 0; bool first_pat = true; u8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1]; const u8 ipv4_mc_mac[] = {0x33, 0x33}; const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e}; - struct mwifiex_private *priv = - mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); - - if (!wowlan) { - dev_warn(adapter->dev, "None of the WOWLAN triggers enabled\n"); - return 0; - } - - if (!priv->media_connected) { - dev_warn(adapter->dev, - "Can not configure WOWLAN in disconnected state\n"); - return 0; - } + struct mwifiex_ds_mef_cfg mef_cfg; + struct mwifiex_mef_entry *mef_entry; mef_entry = kzalloc(sizeof(*mef_entry), GFP_KERNEL); if (!mef_entry) @@ -2459,9 +2762,9 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, for (i = 0; i < wowlan->n_patterns; i++) { memset(byte_seq, 0, sizeof(byte_seq)); if (!mwifiex_is_pattern_supported(&wowlan->patterns[i], - byte_seq, - MWIFIEX_MEF_MAX_BYTESEQ)) { - wiphy_err(wiphy, "Pattern not supported\n"); + byte_seq, + MWIFIEX_MEF_MAX_BYTESEQ)) { + dev_err(priv->adapter->dev, "Pattern not supported\n"); kfree(mef_entry); return -EOPNOTSUPP; } @@ -2485,9 +2788,9 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, mef_entry->filter[filt_num].repeat = 1; mef_entry->filter[filt_num].offset = - wowlan->patterns[i].pkt_offset; + wowlan->patterns[i].pkt_offset; memcpy(mef_entry->filter[filt_num].byte_seq, byte_seq, - sizeof(byte_seq)); + sizeof(byte_seq)); mef_entry->filter[filt_num].filt_type = TYPE_EQ; if (first_pat) @@ -2502,9 +2805,9 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST; mef_entry->filter[filt_num].repeat = 16; memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr, - ETH_ALEN); + ETH_ALEN); mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = - ETH_ALEN; + ETH_ALEN; mef_entry->filter[filt_num].offset = 28; mef_entry->filter[filt_num].filt_type = TYPE_EQ; if (filt_num) @@ -2513,9 +2816,9 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, filt_num++; mef_entry->filter[filt_num].repeat = 16; memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr, - ETH_ALEN); + ETH_ALEN); mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = - ETH_ALEN; + ETH_ALEN; mef_entry->filter[filt_num].offset = 56; mef_entry->filter[filt_num].filt_type = TYPE_EQ; mef_entry->filter[filt_num].filt_action = TYPE_OR; @@ -2523,16 +2826,61 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, if (!mef_cfg.criteria) mef_cfg.criteria = MWIFIEX_CRITERIA_BROADCAST | - MWIFIEX_CRITERIA_UNICAST | - MWIFIEX_CRITERIA_MULTICAST; + MWIFIEX_CRITERIA_UNICAST | + MWIFIEX_CRITERIA_MULTICAST; ret = mwifiex_send_cmd(priv, HostCmd_CMD_MEF_CFG, - HostCmd_ACT_GEN_SET, 0, &mef_cfg, true); + HostCmd_ACT_GEN_SET, 0, &mef_cfg, true); kfree(mef_entry); return ret; } +static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, + struct cfg80211_wowlan *wowlan) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_ds_hs_cfg hs_cfg; + int ret = 0; + struct mwifiex_private *priv = + mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); + + if (!wowlan) { + dev_warn(adapter->dev, "None of the WOWLAN triggers enabled\n"); + return 0; + } + + if (!priv->media_connected) { + dev_warn(adapter->dev, + "Can not configure WOWLAN in disconnected state\n"); + return 0; + } + + if (wowlan->n_patterns || wowlan->magic_pkt) { + ret = mwifiex_set_mef_filter(priv, wowlan); + if (ret) { + dev_err(adapter->dev, "Failed to set MEF filter\n"); + return ret; + } + } + + if (wowlan->disconnect) { + memset(&hs_cfg, 0, sizeof(hs_cfg)); + hs_cfg.is_invoke_hostcmd = false; + hs_cfg.conditions = HS_CFG_COND_MAC_EVENT; + hs_cfg.gpio = HS_CFG_GPIO_DEF; + hs_cfg.gap = HS_CFG_GAP_DEF; + ret = mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, + MWIFIEX_SYNC_CMD, &hs_cfg); + if (ret) { + dev_err(adapter->dev, "Failed to set HS params\n"); + return ret; + } + } + + return ret; +} + static int mwifiex_cfg80211_resume(struct wiphy *wiphy) { return 0; @@ -2803,6 +3151,102 @@ mwifiex_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev, } static int +mwifiex_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_csa_settings *params) +{ + struct ieee_types_header *chsw_ie; + struct ieee80211_channel_sw_ie *channel_sw; + int chsw_msec; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (priv->adapter->scan_processing) { + dev_err(priv->adapter->dev, + "radar detection: scan in process...\n"); + return -EBUSY; + } + + if (priv->wdev.cac_started) + return -EBUSY; + + if (cfg80211_chandef_identical(¶ms->chandef, + &priv->dfs_chandef)) + return -EINVAL; + + chsw_ie = (void *)cfg80211_find_ie(WLAN_EID_CHANNEL_SWITCH, + params->beacon_csa.tail, + params->beacon_csa.tail_len); + if (!chsw_ie) { + dev_err(priv->adapter->dev, + "Could not parse channel switch announcement IE\n"); + return -EINVAL; + } + + channel_sw = (void *)(chsw_ie + 1); + if (channel_sw->mode) { + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter); + } + + if (mwifiex_del_mgmt_ies(priv)) + wiphy_err(wiphy, "Failed to delete mgmt IEs!\n"); + + if (mwifiex_set_mgmt_ies(priv, ¶ms->beacon_csa)) { + wiphy_err(wiphy, "%s: setting mgmt ies failed\n", __func__); + return -EFAULT; + } + + memcpy(&priv->dfs_chandef, ¶ms->chandef, sizeof(priv->dfs_chandef)); + memcpy(&priv->beacon_after, ¶ms->beacon_after, + sizeof(priv->beacon_after)); + + chsw_msec = max(channel_sw->count * priv->bss_cfg.beacon_period, 100); + queue_delayed_work(priv->dfs_chan_sw_workqueue, &priv->dfs_chan_sw_work, + msecs_to_jiffies(chsw_msec)); + return 0; +} + +static int +mwifiex_cfg80211_start_radar_detection(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef, + u32 cac_time_ms) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_radar_params radar_params; + + if (priv->adapter->scan_processing) { + dev_err(priv->adapter->dev, + "radar detection: scan already in process...\n"); + return -EBUSY; + } + + if (!mwifiex_is_11h_active(priv)) { + dev_dbg(priv->adapter->dev, "Enable 11h extensions in FW\n"); + if (mwifiex_11h_activate(priv, true)) { + dev_err(priv->adapter->dev, + "Failed to activate 11h extensions!!"); + return -1; + } + priv->state_11h.is_11h_active = true; + } + + memset(&radar_params, 0, sizeof(struct mwifiex_radar_params)); + radar_params.chandef = chandef; + radar_params.cac_time_ms = cac_time_ms; + + memcpy(&priv->dfs_chandef, chandef, sizeof(priv->dfs_chandef)); + + if (mwifiex_send_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST, + HostCmd_ACT_GEN_SET, 0, &radar_params, true)) + return -1; + + queue_delayed_work(priv->dfs_cac_workqueue, &priv->dfs_cac_work, + msecs_to_jiffies(cac_time_ms)); + return 0; +} + +static int mwifiex_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev, const u8 *mac, struct station_parameters *params) @@ -2866,11 +3310,13 @@ static struct cfg80211_ops mwifiex_cfg80211_ops = { .tdls_oper = mwifiex_cfg80211_tdls_oper, .add_station = mwifiex_cfg80211_add_station, .change_station = mwifiex_cfg80211_change_station, + .start_radar_detection = mwifiex_cfg80211_start_radar_detection, + .channel_switch = mwifiex_cfg80211_channel_switch, }; #ifdef CONFIG_PM static const struct wiphy_wowlan_support mwifiex_wowlan_support = { - .flags = WIPHY_WOWLAN_MAGIC_PKT, + .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, .n_patterns = MWIFIEX_MEF_MAX_FILTERS, .pattern_min_len = 1, .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN, @@ -2964,12 +3410,13 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) wiphy->cipher_suites = mwifiex_cipher_suites; wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites); - memcpy(wiphy->perm_addr, priv->curr_addr, ETH_ALEN); + ether_addr_copy(wiphy->perm_addr, adapter->perm_addr); wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | WIPHY_FLAG_AP_UAPSD | - WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + WIPHY_FLAG_HAS_CHANNEL_SWITCH; if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | diff --git a/drivers/net/wireless/mwifiex/cfp.c b/drivers/net/wireless/mwifiex/cfp.c index b8242eb2be6f..e9df8826f124 100644 --- a/drivers/net/wireless/mwifiex/cfp.c +++ b/drivers/net/wireless/mwifiex/cfp.c @@ -322,9 +322,9 @@ mwifiex_get_cfp(struct mwifiex_private *priv, u8 band, u16 channel, u32 freq) return cfp; if (mwifiex_band_to_radio_type(band) == HostCmd_SCAN_RADIO_TYPE_BG) - sband = priv->wdev->wiphy->bands[IEEE80211_BAND_2GHZ]; + sband = priv->wdev.wiphy->bands[IEEE80211_BAND_2GHZ]; else - sband = priv->wdev->wiphy->bands[IEEE80211_BAND_5GHZ]; + sband = priv->wdev.wiphy->bands[IEEE80211_BAND_5GHZ]; if (!sband) { dev_err(priv->adapter->dev, "%s: cannot find cfp by band %d\n", @@ -509,3 +509,21 @@ u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates) return k; } + +u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv, + u8 rx_rate, u8 rate_info) +{ + u8 rate_index = 0; + + /* HT40 */ + if ((rate_info & BIT(0)) && (rate_info & BIT(1))) + rate_index = MWIFIEX_RATE_INDEX_MCS0 + + MWIFIEX_BW20_MCS_NUM + rx_rate; + else if (rate_info & BIT(0)) /* HT20 */ + rate_index = MWIFIEX_RATE_INDEX_MCS0 + rx_rate; + else + rate_index = (rx_rate > MWIFIEX_RATE_INDEX_OFDM0) ? + rx_rate - 1 : rx_rate; + + return rate_index; +} diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index 85597200badc..c5a14ff7eb82 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -315,22 +315,19 @@ static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter) adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure++; return -1; } - if (GET_BSS_ROLE(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY)) - == MWIFIEX_BSS_ROLE_STA) { - if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl)) - /* Response is not needed for sleep - confirm command */ - adapter->ps_state = PS_STATE_SLEEP; - else - adapter->ps_state = PS_STATE_SLEEP_CFM; - - if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl) && - (adapter->is_hs_configured && - !adapter->sleep_period.period)) { - adapter->pm_wakeup_card_req = true; - mwifiex_hs_activated_event(mwifiex_get_priv - (adapter, MWIFIEX_BSS_ROLE_STA), true); - } + + if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl)) + /* Response is not needed for sleep confirm command */ + adapter->ps_state = PS_STATE_SLEEP; + else + adapter->ps_state = PS_STATE_SLEEP_CFM; + + if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl) && + (adapter->is_hs_configured && + !adapter->sleep_period.period)) { + adapter->pm_wakeup_card_req = true; + mwifiex_hs_activated_event(mwifiex_get_priv + (adapter, MWIFIEX_BSS_ROLE_ANY), true); } return ret; @@ -450,6 +447,7 @@ int mwifiex_process_event(struct mwifiex_adapter *adapter) EVENT_GET_BSS_TYPE(eventcause)); if (!priv) priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + /* Clear BSS_NO_BITS from event */ eventcause &= EVENT_ID_MASK; adapter->event_cause = eventcause; @@ -462,12 +460,6 @@ int mwifiex_process_event(struct mwifiex_adapter *adapter) } dev_dbg(adapter->dev, "EVENT: cause: %#x\n", eventcause); - if (eventcause == EVENT_PS_SLEEP || eventcause == EVENT_PS_AWAKE) { - /* Handle PS_SLEEP/AWAKE events on STA */ - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); - if (!priv) - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - } if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) ret = mwifiex_process_uap_event(priv); @@ -1008,11 +1000,9 @@ mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter) list_for_each_entry_safe(cmd_node, tmp_node, &adapter->scan_pending_q, list) { list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); cmd_node->wait_q_enabled = false; mwifiex_insert_cmd_to_free_q(adapter, cmd_node); - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); } spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); @@ -1070,12 +1060,8 @@ mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter) list_for_each_entry_safe(cmd_node, tmp_node, &adapter->scan_pending_q, list) { list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - scan_pending_q_flags); cmd_node->wait_q_enabled = false; mwifiex_insert_cmd_to_free_q(adapter, cmd_node); - spin_lock_irqsave(&adapter->scan_pending_q_lock, - scan_pending_q_flags); } spin_unlock_irqrestore(&adapter->scan_pending_q_lock, scan_pending_q_flags); @@ -1588,9 +1574,7 @@ int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, le16_to_cpu(hw_spec->hw_if_version), le16_to_cpu(hw_spec->version)); - if (priv->curr_addr[0] == 0xff) - memmove(priv->curr_addr, hw_spec->permanent_addr, ETH_ALEN); - + ether_addr_copy(priv->adapter->perm_addr, hw_spec->permanent_addr); adapter->region_code = le16_to_cpu(hw_spec->region_code); for (i = 0; i < MWIFIEX_MAX_REGION_CODE; i++) diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c index 2713f7acd35e..1fb329dc6744 100644 --- a/drivers/net/wireless/mwifiex/debugfs.c +++ b/drivers/net/wireless/mwifiex/debugfs.c @@ -39,111 +39,6 @@ static char *bss_modes[] = { "P2P_DEVICE", }; -/* size/addr for mwifiex_debug_info */ -#define item_size(n) (FIELD_SIZEOF(struct mwifiex_debug_info, n)) -#define item_addr(n) (offsetof(struct mwifiex_debug_info, n)) - -/* size/addr for struct mwifiex_adapter */ -#define adapter_item_size(n) (FIELD_SIZEOF(struct mwifiex_adapter, n)) -#define adapter_item_addr(n) (offsetof(struct mwifiex_adapter, n)) - -struct mwifiex_debug_data { - char name[32]; /* variable/array name */ - u32 size; /* size of the variable/array */ - size_t addr; /* address of the variable/array */ - int num; /* number of variables in an array */ -}; - -static struct mwifiex_debug_data items[] = { - {"int_counter", item_size(int_counter), - item_addr(int_counter), 1}, - {"wmm_ac_vo", item_size(packets_out[WMM_AC_VO]), - item_addr(packets_out[WMM_AC_VO]), 1}, - {"wmm_ac_vi", item_size(packets_out[WMM_AC_VI]), - item_addr(packets_out[WMM_AC_VI]), 1}, - {"wmm_ac_be", item_size(packets_out[WMM_AC_BE]), - item_addr(packets_out[WMM_AC_BE]), 1}, - {"wmm_ac_bk", item_size(packets_out[WMM_AC_BK]), - item_addr(packets_out[WMM_AC_BK]), 1}, - {"tx_buf_size", item_size(tx_buf_size), - item_addr(tx_buf_size), 1}, - {"curr_tx_buf_size", item_size(curr_tx_buf_size), - item_addr(curr_tx_buf_size), 1}, - {"ps_mode", item_size(ps_mode), - item_addr(ps_mode), 1}, - {"ps_state", item_size(ps_state), - item_addr(ps_state), 1}, - {"is_deep_sleep", item_size(is_deep_sleep), - item_addr(is_deep_sleep), 1}, - {"wakeup_dev_req", item_size(pm_wakeup_card_req), - item_addr(pm_wakeup_card_req), 1}, - {"wakeup_tries", item_size(pm_wakeup_fw_try), - item_addr(pm_wakeup_fw_try), 1}, - {"hs_configured", item_size(is_hs_configured), - item_addr(is_hs_configured), 1}, - {"hs_activated", item_size(hs_activated), - item_addr(hs_activated), 1}, - {"num_tx_timeout", item_size(num_tx_timeout), - item_addr(num_tx_timeout), 1}, - {"is_cmd_timedout", item_size(is_cmd_timedout), - item_addr(is_cmd_timedout), 1}, - {"timeout_cmd_id", item_size(timeout_cmd_id), - item_addr(timeout_cmd_id), 1}, - {"timeout_cmd_act", item_size(timeout_cmd_act), - item_addr(timeout_cmd_act), 1}, - {"last_cmd_id", item_size(last_cmd_id), - item_addr(last_cmd_id), DBG_CMD_NUM}, - {"last_cmd_act", item_size(last_cmd_act), - item_addr(last_cmd_act), DBG_CMD_NUM}, - {"last_cmd_index", item_size(last_cmd_index), - item_addr(last_cmd_index), 1}, - {"last_cmd_resp_id", item_size(last_cmd_resp_id), - item_addr(last_cmd_resp_id), DBG_CMD_NUM}, - {"last_cmd_resp_index", item_size(last_cmd_resp_index), - item_addr(last_cmd_resp_index), 1}, - {"last_event", item_size(last_event), - item_addr(last_event), DBG_CMD_NUM}, - {"last_event_index", item_size(last_event_index), - item_addr(last_event_index), 1}, - {"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure), - item_addr(num_cmd_host_to_card_failure), 1}, - {"num_cmd_sleep_cfm_fail", - item_size(num_cmd_sleep_cfm_host_to_card_failure), - item_addr(num_cmd_sleep_cfm_host_to_card_failure), 1}, - {"num_tx_h2c_fail", item_size(num_tx_host_to_card_failure), - item_addr(num_tx_host_to_card_failure), 1}, - {"num_evt_deauth", item_size(num_event_deauth), - item_addr(num_event_deauth), 1}, - {"num_evt_disassoc", item_size(num_event_disassoc), - item_addr(num_event_disassoc), 1}, - {"num_evt_link_lost", item_size(num_event_link_lost), - item_addr(num_event_link_lost), 1}, - {"num_cmd_deauth", item_size(num_cmd_deauth), - item_addr(num_cmd_deauth), 1}, - {"num_cmd_assoc_ok", item_size(num_cmd_assoc_success), - item_addr(num_cmd_assoc_success), 1}, - {"num_cmd_assoc_fail", item_size(num_cmd_assoc_failure), - item_addr(num_cmd_assoc_failure), 1}, - {"cmd_sent", item_size(cmd_sent), - item_addr(cmd_sent), 1}, - {"data_sent", item_size(data_sent), - item_addr(data_sent), 1}, - {"cmd_resp_received", item_size(cmd_resp_received), - item_addr(cmd_resp_received), 1}, - {"event_received", item_size(event_received), - item_addr(event_received), 1}, - - /* variables defined in struct mwifiex_adapter */ - {"cmd_pending", adapter_item_size(cmd_pending), - adapter_item_addr(cmd_pending), 1}, - {"tx_pending", adapter_item_size(tx_pending), - adapter_item_addr(tx_pending), 1}, - {"rx_pending", adapter_item_size(rx_pending), - adapter_item_addr(rx_pending), 1}, -}; - -static int num_of_items = ARRAY_SIZE(items); - /* * Proc info file read handler. * @@ -297,6 +192,8 @@ mwifiex_fw_dump_read(struct file *file, char __user *ubuf, * - Number of FCS errors * - Number of Tx frames * - WEP ICV error counts + * - Number of received beacons + * - Number of missed beacons */ static ssize_t mwifiex_getlog_read(struct file *file, char __user *ubuf, @@ -333,7 +230,9 @@ mwifiex_getlog_read(struct file *file, char __user *ubuf, "wepicverrcnt-1 %u\n" "wepicverrcnt-2 %u\n" "wepicverrcnt-3 %u\n" - "wepicverrcnt-4 %u\n", + "wepicverrcnt-4 %u\n" + "bcn_rcv_cnt %u\n" + "bcn_miss_cnt %u\n", stats.mcast_tx_frame, stats.failed, stats.retry, @@ -349,7 +248,9 @@ mwifiex_getlog_read(struct file *file, char __user *ubuf, stats.wep_icv_error[0], stats.wep_icv_error[1], stats.wep_icv_error[2], - stats.wep_icv_error[3]); + stats.wep_icv_error[3], + stats.bcn_rcv_cnt, + stats.bcn_miss_cnt); ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, @@ -360,6 +261,103 @@ free_and_exit: return ret; } +/* Sysfs histogram file read handler. + * + * This function is called when the 'histogram' file is opened for reading + * It prints the following histogram information - + * - Number of histogram samples + * - Receive packet number of each rx_rate + * - Receive packet number of each snr + * - Receive packet number of each nosie_flr + * - Receive packet number of each signal streath + */ +static ssize_t +mwifiex_histogram_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = + (struct mwifiex_private *)file->private_data; + ssize_t ret; + struct mwifiex_histogram_data *phist_data; + int i, value; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *p = (char *)page; + + if (!p) + return -ENOMEM; + + if (!priv || !priv->hist_data) + return -EFAULT; + phist_data = priv->hist_data; + + p += sprintf(p, "\n" + "total samples = %d\n", + atomic_read(&phist_data->num_samples)); + + p += sprintf(p, "rx rates (in Mbps): 0=1M 1=2M"); + p += sprintf(p, "2=5.5M 3=11M 4=6M 5=9M 6=12M\n"); + p += sprintf(p, "7=18M 8=24M 9=36M 10=48M 11=54M"); + p += sprintf(p, "12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n"); + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { + p += sprintf(p, "44-53=MCS0-9(VHT:BW20)"); + p += sprintf(p, "54-63=MCS0-9(VHT:BW40)"); + p += sprintf(p, "64-73=MCS0-9(VHT:BW80)\n\n"); + } else { + p += sprintf(p, "\n"); + } + + for (i = 0; i < MWIFIEX_MAX_RX_RATES; i++) { + value = atomic_read(&phist_data->rx_rate[i]); + if (value) + p += sprintf(p, "rx_rate[%02d] = %d\n", i, value); + } + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { + for (i = MWIFIEX_MAX_RX_RATES; i < MWIFIEX_MAX_AC_RX_RATES; + i++) { + value = atomic_read(&phist_data->rx_rate[i]); + if (value) + p += sprintf(p, "rx_rate[%02d] = %d\n", + i, value); + } + } + + for (i = 0; i < MWIFIEX_MAX_SNR; i++) { + value = atomic_read(&phist_data->snr[i]); + if (value) + p += sprintf(p, "snr[%02ddB] = %d\n", i, value); + } + for (i = 0; i < MWIFIEX_MAX_NOISE_FLR; i++) { + value = atomic_read(&phist_data->noise_flr[i]); + if (value) + p += sprintf(p, "noise_flr[-%02ddBm] = %d\n", + (int)(i-128), value); + } + for (i = 0; i < MWIFIEX_MAX_SIG_STRENGTH; i++) { + value = atomic_read(&phist_data->sig_str[i]); + if (value) + p += sprintf(p, "sig_strength[-%02ddBm] = %d\n", + i, value); + } + + ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page, + (unsigned long)p - page); + + return ret; +} + +static ssize_t +mwifiex_histogram_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = (void *)file->private_data; + + if (priv && priv->hist_data) + mwifiex_hist_data_reset(priv); + return 0; +} + static struct mwifiex_debug_info info; /* @@ -415,13 +413,9 @@ mwifiex_debug_read(struct file *file, char __user *ubuf, { struct mwifiex_private *priv = (struct mwifiex_private *) file->private_data; - struct mwifiex_debug_data *d = &items[0]; unsigned long page = get_zeroed_page(GFP_KERNEL); char *p = (char *) page; ssize_t ret; - size_t size, addr; - long val; - int i, j; if (!p) return -ENOMEM; @@ -430,68 +424,7 @@ mwifiex_debug_read(struct file *file, char __user *ubuf, if (ret) goto free_and_exit; - for (i = 0; i < num_of_items; i++) { - p += sprintf(p, "%s=", d[i].name); - - size = d[i].size / d[i].num; - - if (i < (num_of_items - 3)) - addr = d[i].addr + (size_t) &info; - else /* The last 3 items are struct mwifiex_adapter variables */ - addr = d[i].addr + (size_t) priv->adapter; - - for (j = 0; j < d[i].num; j++) { - switch (size) { - case 1: - val = *((u8 *) addr); - break; - case 2: - val = *((u16 *) addr); - break; - case 4: - val = *((u32 *) addr); - break; - case 8: - val = *((long long *) addr); - break; - default: - val = -1; - break; - } - - p += sprintf(p, "%#lx ", val); - addr += size; - } - - p += sprintf(p, "\n"); - } - - if (info.tx_tbl_num) { - p += sprintf(p, "Tx BA stream table:\n"); - for (i = 0; i < info.tx_tbl_num; i++) - p += sprintf(p, "tid = %d, ra = %pM\n", - info.tx_tbl[i].tid, info.tx_tbl[i].ra); - } - - if (info.rx_tbl_num) { - p += sprintf(p, "Rx reorder table:\n"); - for (i = 0; i < info.rx_tbl_num; i++) { - p += sprintf(p, "tid = %d, ta = %pM, " - "start_win = %d, " - "win_size = %d, buffer: ", - info.rx_tbl[i].tid, - info.rx_tbl[i].ta, - info.rx_tbl[i].start_win, - info.rx_tbl[i].win_size); - - for (j = 0; j < info.rx_tbl[i].win_size; j++) - p += sprintf(p, "%c ", - info.rx_tbl[i].buffer[j] ? - '1' : '0'); - - p += sprintf(p, "\n"); - } - } + p += mwifiex_debug_info_to_buffer(priv, p, &info); ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, (unsigned long) p - page); @@ -817,6 +750,7 @@ MWIFIEX_DFS_FILE_READ_OPS(fw_dump); MWIFIEX_DFS_FILE_OPS(regrdwr); MWIFIEX_DFS_FILE_OPS(rdeeprom); MWIFIEX_DFS_FILE_OPS(hscfg); +MWIFIEX_DFS_FILE_OPS(histogram); /* * This function creates the debug FS directory structure and the files. @@ -840,6 +774,7 @@ mwifiex_dev_debugfs_init(struct mwifiex_private *priv) MWIFIEX_DFS_ADD_FILE(rdeeprom); MWIFIEX_DFS_ADD_FILE(fw_dump); MWIFIEX_DFS_ADD_FILE(hscfg); + MWIFIEX_DFS_ADD_FILE(histogram); } /* diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h index 2269acf41ad8..88d0eade6bb1 100644 --- a/drivers/net/wireless/mwifiex/decl.h +++ b/drivers/net/wireless/mwifiex/decl.h @@ -32,15 +32,19 @@ #define MWIFIEX_MAX_BSS_NUM (3) -#define MWIFIEX_MIN_DATA_HEADER_LEN 36 /* sizeof(mwifiex_txpd) - * + 4 byte alignment - */ +#define MWIFIEX_DMA_ALIGN_SZ 64 +#define MAX_TXPD_SZ 32 +#define INTF_HDR_ALIGN 4 + +#define MWIFIEX_MIN_DATA_HEADER_LEN (MWIFIEX_DMA_ALIGN_SZ + INTF_HDR_ALIGN + \ + MAX_TXPD_SZ) #define MWIFIEX_MGMT_FRAME_HEADER_SIZE 8 /* sizeof(pkt_type) * + sizeof(tx_control) */ #define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED 2 #define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED 16 +#define MWIFIEX_MAX_TDLS_PEER_SUPPORTED 8 #define MWIFIEX_STA_AMPDU_DEF_TXWINSIZE 64 #define MWIFIEX_STA_AMPDU_DEF_RXWINSIZE 64 @@ -92,6 +96,20 @@ #define MWIFIEX_TDLS_MAX_FAIL_COUNT 4 #define MWIFIEX_AUTO_TDLS_IDLE_TIME 10 +/* 54M rates, index from 0 to 11 */ +#define MWIFIEX_RATE_INDEX_MCS0 12 +/* 12-27=MCS0-15(BW20) */ +#define MWIFIEX_BW20_MCS_NUM 15 + +/* Rate index for OFDM 0 */ +#define MWIFIEX_RATE_INDEX_OFDM0 4 + +#define MWIFIEX_MAX_STA_NUM 1 +#define MWIFIEX_MAX_UAP_NUM 1 +#define MWIFIEX_MAX_P2P_NUM 1 + +#define MWIFIEX_A_BAND_START_FREQ 5000 + enum mwifiex_bss_type { MWIFIEX_BSS_TYPE_STA = 0, MWIFIEX_BSS_TYPE_UAP = 1, @@ -204,4 +222,35 @@ struct mwifiex_chan_stats { u16 cca_scan_dur; u16 cca_busy_dur; } __packed; + +#define MWIFIEX_HIST_MAX_SAMPLES 1048576 +#define MWIFIEX_MAX_RX_RATES 44 +#define MWIFIEX_MAX_AC_RX_RATES 74 +#define MWIFIEX_MAX_SNR 256 +#define MWIFIEX_MAX_NOISE_FLR 256 +#define MWIFIEX_MAX_SIG_STRENGTH 256 + +struct mwifiex_histogram_data { + atomic_t rx_rate[MWIFIEX_MAX_AC_RX_RATES]; + atomic_t snr[MWIFIEX_MAX_SNR]; + atomic_t noise_flr[MWIFIEX_MAX_NOISE_FLR]; + atomic_t sig_str[MWIFIEX_MAX_SIG_STRENGTH]; + atomic_t num_samples; +}; + +struct mwifiex_iface_comb { + u8 sta_intf; + u8 uap_intf; + u8 p2p_intf; +}; + +struct mwifiex_radar_params { + struct cfg80211_chan_def *chandef; + u32 cac_time_ms; +} __packed; + +struct mwifiex_11h_intf_state { + bool is_11h_enabled; + bool is_11h_active; +} __packed; #endif /* !_MWIFIEX_DECL_H_ */ diff --git a/drivers/net/wireless/mwifiex/ethtool.c b/drivers/net/wireless/mwifiex/ethtool.c index 04e56b5fc535..65d8d6d4b6ba 100644 --- a/drivers/net/wireless/mwifiex/ethtool.c +++ b/drivers/net/wireless/mwifiex/ethtool.c @@ -76,7 +76,9 @@ mwifiex_get_dump_flag(struct net_device *dev, struct ethtool_dump *dump) dump->flag = adapter->curr_mem_idx; dump->version = 1; - if (adapter->curr_mem_idx != MWIFIEX_FW_DUMP_IDX) { + if (adapter->curr_mem_idx == MWIFIEX_DRV_INFO_IDX) { + dump->len = adapter->drv_info_size; + } else if (adapter->curr_mem_idx != MWIFIEX_FW_DUMP_IDX) { entry = &adapter->mem_type_mapping_tbl[adapter->curr_mem_idx]; dump->len = entry->mem_size; } else { @@ -98,6 +100,13 @@ mwifiex_get_dump_data(struct net_device *dev, struct ethtool_dump *dump, if (!adapter->if_ops.fw_dump) return -ENOTSUPP; + if (adapter->curr_mem_idx == MWIFIEX_DRV_INFO_IDX) { + if (!adapter->drv_info_dump) + return -EFAULT; + memcpy(p, adapter->drv_info_dump, adapter->drv_info_size); + return 0; + } + if (adapter->curr_mem_idx == MWIFIEX_FW_DUMP_IDX) { dev_err(adapter->dev, "firmware dump in progress!!\n"); return -EBUSY; @@ -125,6 +134,11 @@ static int mwifiex_set_dump(struct net_device *dev, struct ethtool_dump *val) if (!adapter->if_ops.fw_dump) return -ENOTSUPP; + if (val->flag == MWIFIEX_DRV_INFO_IDX) { + adapter->curr_mem_idx = MWIFIEX_DRV_INFO_IDX; + return 0; + } + if (adapter->curr_mem_idx == MWIFIEX_FW_DUMP_IDX) { dev_err(adapter->dev, "firmware dump in progress!!\n"); return -EBUSY; diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index fb5936eb82e3..df553e86a0ad 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -158,6 +158,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define TLV_TYPE_POWER_GROUP (PROPRIETARY_TLV_BASE_ID + 84) #define TLV_TYPE_BSS_SCAN_RSP (PROPRIETARY_TLV_BASE_ID + 86) #define TLV_TYPE_BSS_SCAN_INFO (PROPRIETARY_TLV_BASE_ID + 87) +#define TLV_TYPE_CHANRPT_11H_BASIC (PROPRIETARY_TLV_BASE_ID + 91) #define TLV_TYPE_UAP_RETRY_LIMIT (PROPRIETARY_TLV_BASE_ID + 93) #define TLV_TYPE_WAPI_IE (PROPRIETARY_TLV_BASE_ID + 94) #define TLV_TYPE_UAP_MGMT_FRAME (PROPRIETARY_TLV_BASE_ID + 104) @@ -233,6 +234,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define ISSUPP_RXLDPC(Dot11nDevCap) (Dot11nDevCap & BIT(22)) #define ISSUPP_BEAMFORMING(Dot11nDevCap) (Dot11nDevCap & BIT(30)) #define ISALLOWED_CHANWIDTH40(ht_param) (ht_param & BIT(2)) +#define GETSUPP_TXBASTREAMS(Dot11nDevCap) ((Dot11nDevCap >> 18) & 0xF) /* httxcfg bitmap * 0 reserved @@ -335,6 +337,7 @@ enum MWIFIEX_802_11_PRIVACY_FILTER { #define HostCmd_CMD_11N_ADDBA_RSP 0x00cf #define HostCmd_CMD_11N_DELBA 0x00d0 #define HostCmd_CMD_RECONFIGURE_TX_BUFF 0x00d9 +#define HostCmd_CMD_CHAN_REPORT_REQUEST 0x00dd #define HostCmd_CMD_AMSDU_AGGR_CTRL 0x00df #define HostCmd_CMD_TXPWR_CFG 0x00d1 #define HostCmd_CMD_TX_RATE_CFG 0x00d6 @@ -492,6 +495,8 @@ enum P2P_MODES { #define EVENT_HOSTWAKE_STAIE 0x0000004d #define EVENT_CHANNEL_SWITCH_ANN 0x00000050 #define EVENT_TDLS_GENERIC_EVENT 0x00000052 +#define EVENT_RADAR_DETECTED 0x00000053 +#define EVENT_CHANNEL_REPORT_RDY 0x00000054 #define EVENT_EXT_SCAN_REPORT 0x00000058 #define EVENT_REMAIN_ON_CHAN_EXPIRED 0x0000005f #define EVENT_TX_STATUS_REPORT 0x00000074 @@ -529,6 +534,8 @@ enum P2P_MODES { #define MWIFIEX_FW_V15 15 +#define MWIFIEX_MASTER_RADAR_DET_MASK BIT(1) + struct mwifiex_ie_types_header { __le16 type; __le16 len; @@ -1076,6 +1083,8 @@ struct host_cmd_ds_802_11_get_log { __le32 tx_frame; __le32 reserved; __le32 wep_icv_err_cnt[4]; + __le32 bcn_rcv_cnt; + __le32 bcn_miss_cnt; }; /* Enumeration for rate format */ @@ -1213,6 +1222,24 @@ struct host_cmd_ds_tdls_oper { u8 peer_mac[ETH_ALEN]; } __packed; +struct mwifiex_chan_desc { + __le16 start_freq; + u8 chan_width; + u8 chan_num; +} __packed; + +struct host_cmd_ds_chan_rpt_req { + struct mwifiex_chan_desc chan_desc; + __le32 msec_dwell_time; +} __packed; + +struct host_cmd_ds_chan_rpt_event { + __le32 result; + __le64 start_tsf; + __le32 duration; + u8 tlvbuf[0]; +} __packed; + struct mwifiex_fixed_bcn_param { __le64 timestamp; __le16 beacon_period; @@ -1789,6 +1816,39 @@ struct mwifiex_ie_types_rssi_threshold { u8 evt_freq; } __packed; +#define MWIFIEX_DFS_REC_HDR_LEN 8 +#define MWIFIEX_DFS_REC_HDR_NUM 10 +#define MWIFIEX_BIN_COUNTER_LEN 7 + +struct mwifiex_radar_det_event { + __le32 detect_count; + u8 reg_domain; /*1=fcc, 2=etsi, 3=mic*/ + u8 det_type; /*0=none, 1=pw(chirp), 2=pri(radar)*/ + __le16 pw_chirp_type; + u8 pw_chirp_idx; + u8 pw_value; + u8 pri_radar_type; + u8 pri_bincnt; + u8 bin_counter[MWIFIEX_BIN_COUNTER_LEN]; + u8 num_dfs_records; + u8 dfs_record_hdr[MWIFIEX_DFS_REC_HDR_NUM][MWIFIEX_DFS_REC_HDR_LEN]; + __le32 passed; +} __packed; + +struct meas_rpt_map { + u8 rssi:3; + u8 unmeasured:1; + u8 radar:1; + u8 unidentified_sig:1; + u8 ofdm_preamble:1; + u8 bss:1; +} __packed; + +struct mwifiex_ie_types_chan_rpt_data { + struct mwifiex_ie_types_header header; + struct meas_rpt_map map; +} __packed; + struct host_cmd_ds_802_11_subsc_evt { __le16 action; __le16 events; @@ -1901,6 +1961,7 @@ struct host_cmd_ds_command { struct host_cmd_11ac_vht_cfg vht_cfg; struct host_cmd_ds_coalesce_cfg coalesce_cfg; struct host_cmd_ds_tdls_oper tdls_oper; + struct host_cmd_ds_chan_rpt_req chan_rpt_req; } params; } __packed; diff --git a/drivers/net/wireless/mwifiex/ie.c b/drivers/net/wireless/mwifiex/ie.c index b933794758b7..f3b6ed249403 100644 --- a/drivers/net/wireless/mwifiex/ie.c +++ b/drivers/net/wireless/mwifiex/ie.c @@ -317,27 +317,27 @@ done: return ret; } -/* This function parses different IEs-tail IEs, beacon IEs, probe response IEs, - * association response IEs from cfg80211_ap_settings function and sets these IE - * to FW. +/* This function parses head and tail IEs, from cfg80211_beacon_data and sets + * these IE to FW. */ -int mwifiex_set_mgmt_ies(struct mwifiex_private *priv, - struct cfg80211_beacon_data *info) +static int mwifiex_uap_set_head_tail_ies(struct mwifiex_private *priv, + struct cfg80211_beacon_data *info) { struct mwifiex_ie *gen_ie; - struct ieee_types_header *rsn_ie, *wpa_ie = NULL; - u16 rsn_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0; + struct ieee_types_header *rsn_ie = NULL, *wpa_ie = NULL; + struct ieee_types_header *chsw_ie = NULL; + u16 gen_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0; const u8 *vendor_ie; - if (info->tail && info->tail_len) { - gen_ie = kzalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); - if (!gen_ie) - return -ENOMEM; - gen_ie->ie_index = cpu_to_le16(rsn_idx); - gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON | - MGMT_MASK_PROBE_RESP | - MGMT_MASK_ASSOC_RESP); + gen_ie = kzalloc(sizeof(*gen_ie), GFP_KERNEL); + if (!gen_ie) + return -ENOMEM; + gen_ie->ie_index = cpu_to_le16(gen_idx); + gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON | + MGMT_MASK_PROBE_RESP | + MGMT_MASK_ASSOC_RESP); + if (info->tail && info->tail_len) { rsn_ie = (void *)cfg80211_find_ie(WLAN_EID_RSN, info->tail, info->tail_len); if (rsn_ie) { @@ -358,19 +358,41 @@ int mwifiex_set_mgmt_ies(struct mwifiex_private *priv, gen_ie->ie_length = cpu_to_le16(ie_len); } - if (rsn_ie || wpa_ie) { - if (mwifiex_update_uap_custom_ie(priv, gen_ie, &rsn_idx, - NULL, NULL, - NULL, NULL)) { - kfree(gen_ie); - return -1; - } - priv->rsn_idx = rsn_idx; + chsw_ie = (void *)cfg80211_find_ie(WLAN_EID_CHANNEL_SWITCH, + info->tail, info->tail_len); + if (chsw_ie) { + memcpy(gen_ie->ie_buffer + ie_len, + chsw_ie, chsw_ie->len + 2); + ie_len += chsw_ie->len + 2; + gen_ie->ie_length = cpu_to_le16(ie_len); } + } - kfree(gen_ie); + if (rsn_ie || wpa_ie || chsw_ie) { + if (mwifiex_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL, + NULL, NULL, NULL)) { + kfree(gen_ie); + return -1; + } + priv->gen_idx = gen_idx; } + kfree(gen_ie); + return 0; +} + +/* This function parses different IEs-head & tail IEs, beacon IEs, + * probe response IEs, association response IEs from cfg80211_ap_settings + * function and sets these IE to FW. + */ +int mwifiex_set_mgmt_ies(struct mwifiex_private *priv, + struct cfg80211_beacon_data *info) +{ + int ret; + + ret = mwifiex_uap_set_head_tail_ies(priv, info); + return ret; + return mwifiex_set_mgmt_beacon_data_ies(priv, info); } @@ -378,25 +400,25 @@ int mwifiex_set_mgmt_ies(struct mwifiex_private *priv, int mwifiex_del_mgmt_ies(struct mwifiex_private *priv) { struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL; - struct mwifiex_ie *ar_ie = NULL, *rsn_ie = NULL; + struct mwifiex_ie *ar_ie = NULL, *gen_ie = NULL; int ret = 0; - if (priv->rsn_idx != MWIFIEX_AUTO_IDX_MASK) { - rsn_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); - if (!rsn_ie) + if (priv->gen_idx != MWIFIEX_AUTO_IDX_MASK) { + gen_ie = kmalloc(sizeof(*gen_ie), GFP_KERNEL); + if (!gen_ie) return -ENOMEM; - rsn_ie->ie_index = cpu_to_le16(priv->rsn_idx); - rsn_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); - rsn_ie->ie_length = 0; - if (mwifiex_update_uap_custom_ie(priv, rsn_ie, &priv->rsn_idx, + gen_ie->ie_index = cpu_to_le16(priv->gen_idx); + gen_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); + gen_ie->ie_length = 0; + if (mwifiex_update_uap_custom_ie(priv, gen_ie, &priv->gen_idx, NULL, &priv->proberesp_idx, NULL, &priv->assocresp_idx)) { ret = -1; goto done; } - priv->rsn_idx = MWIFIEX_AUTO_IDX_MASK; + priv->gen_idx = MWIFIEX_AUTO_IDX_MASK; } if (priv->beacon_idx != MWIFIEX_AUTO_IDX_MASK) { @@ -440,7 +462,6 @@ done: kfree(beacon_ie); kfree(pr_ie); kfree(ar_ie); - kfree(rsn_ie); return ret; } diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 520ad4a3018b..b77ba743e1c4 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -52,6 +52,18 @@ static int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv) return 0; } +static void wakeup_timer_fn(unsigned long data) +{ + struct mwifiex_adapter *adapter = (struct mwifiex_adapter *)data; + + dev_err(adapter->dev, "Firmware wakeup failed\n"); + adapter->hw_status = MWIFIEX_HW_STATUS_RESET; + mwifiex_cancel_all_pending_cmd(adapter); + + if (adapter->if_ops.card_reset) + adapter->if_ops.card_reset(adapter); +} + /* * This function initializes the private structure and sets default * values to the members. @@ -140,6 +152,8 @@ int mwifiex_init_priv(struct mwifiex_private *priv) priv->check_tdls_tx = false; memcpy(priv->tos_to_tid_inv, tos_to_tid_inv, MAX_NUM_TID); + mwifiex_init_11h_params(priv); + return mwifiex_add_bss_prio_tbl(priv); } @@ -282,9 +296,16 @@ static void mwifiex_init_adapter(struct mwifiex_adapter *adapter) memset(&adapter->arp_filter, 0, sizeof(adapter->arp_filter)); adapter->arp_filter_size = 0; adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX; - adapter->ext_scan = true; + adapter->ext_scan = false; adapter->key_api_major_ver = 0; adapter->key_api_minor_ver = 0; + memset(adapter->perm_addr, 0xff, ETH_ALEN); + adapter->iface_limit.sta_intf = MWIFIEX_MAX_STA_NUM; + adapter->iface_limit.uap_intf = MWIFIEX_MAX_UAP_NUM; + adapter->iface_limit.p2p_intf = MWIFIEX_MAX_P2P_NUM; + + setup_timer(&adapter->wakeup_timer, wakeup_timer_fn, + (unsigned long)adapter); } /* @@ -391,7 +412,10 @@ mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) return; } + del_timer(&adapter->wakeup_timer); mwifiex_cancel_all_pending_cmd(adapter); + wake_up_interruptible(&adapter->cmd_wait_q.wait); + wake_up_interruptible(&adapter->hs_activate_wait_q); /* Free lock variables */ mwifiex_free_lock_list(adapter); @@ -411,6 +435,11 @@ mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) entry->mem_size = 0; } + if (adapter->drv_info_dump) { + vfree(adapter->drv_info_dump); + adapter->drv_info_size = 0; + } + if (adapter->sleep_cfm) dev_kfree_skb_any(adapter->sleep_cfm); } @@ -528,7 +557,8 @@ int mwifiex_init_fw(struct mwifiex_adapter *adapter) for (i = 0; i < adapter->priv_num; i++) { if (adapter->priv[i]) { - ret = mwifiex_sta_init_cmd(adapter->priv[i], first_sta); + ret = mwifiex_sta_init_cmd(adapter->priv[i], first_sta, + true); if (ret == -1) return -1; @@ -653,6 +683,7 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) priv = adapter->priv[i]; mwifiex_clean_auto_tdls(priv); + mwifiex_abort_cac(priv); mwifiex_clean_txrx(priv); mwifiex_delete_bss_prio_tbl(priv); } diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h index 0847f3e07ab7..d2b05c3a96da 100644 --- a/drivers/net/wireless/mwifiex/ioctl.h +++ b/drivers/net/wireless/mwifiex/ioctl.h @@ -137,6 +137,8 @@ struct mwifiex_ds_get_stats { u32 fcs_error; u32 tx_frame; u32 wep_icv_error[4]; + u32 bcn_rcv_cnt; + u32 bcn_miss_cnt; }; #define MWIFIEX_MAX_VER_STR_LEN 128 @@ -180,7 +182,11 @@ struct mwifiex_ds_tx_ba_stream_tbl { u8 amsdu; }; -#define DBG_CMD_NUM 5 +#define DBG_CMD_NUM 5 + +struct tdls_peer_info { + u8 peer_addr[ETH_ALEN]; +}; struct mwifiex_debug_info { u32 int_counter; @@ -193,6 +199,9 @@ struct mwifiex_debug_info { u32 rx_tbl_num; struct mwifiex_ds_rx_reorder_tbl rx_tbl [MWIFIEX_MAX_RX_BASTREAM_SUPPORTED]; + u32 tdls_peer_num; + struct tdls_peer_info tdls_list + [MWIFIEX_MAX_TDLS_PEER_SUPPORTED]; u16 ps_mode; u32 ps_state; u8 is_deep_sleep; diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index d4d2223d1f31..7e74b4fccddd 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -83,9 +83,8 @@ static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops, } mwifiex_init_lock_list(adapter); - init_timer(&adapter->cmd_timer); - adapter->cmd_timer.function = mwifiex_cmd_timeout_func; - adapter->cmd_timer.data = (unsigned long) adapter; + setup_timer(&adapter->cmd_timer, mwifiex_cmd_timeout_func, + (unsigned long)adapter); return 0; @@ -237,6 +236,7 @@ process_start: (is_command_pending(adapter) || !mwifiex_wmm_lists_empty(adapter))) { adapter->pm_wakeup_fw_try = true; + mod_timer(&adapter->wakeup_timer, jiffies + (HZ*3)); adapter->if_ops.wakeup(adapter); continue; } @@ -244,6 +244,7 @@ process_start: if (IS_CARD_RX_RCVD(adapter)) { adapter->data_received = false; adapter->pm_wakeup_fw_try = false; + del_timer_sync(&adapter->wakeup_timer); if (adapter->ps_state == PS_STATE_SLEEP) adapter->ps_state = PS_STATE_AWAKE; } else { @@ -511,8 +512,7 @@ err_dnld_fw: if (adapter->if_ops.unregister_dev) adapter->if_ops.unregister_dev(adapter); - if ((adapter->hw_status == MWIFIEX_HW_STATUS_FW_READY) || - (adapter->hw_status == MWIFIEX_HW_STATUS_READY)) { + if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) { pr_debug("info: %s: shutdown mwifiex\n", __func__); adapter->init_wait_q_woken = false; @@ -562,7 +562,8 @@ static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter) static int mwifiex_open(struct net_device *dev) { - netif_tx_start_all_queues(dev); + netif_carrier_off(dev); + return 0; } @@ -801,6 +802,114 @@ mwifiex_tx_timeout(struct net_device *dev) } } +void mwifiex_dump_drv_info(struct mwifiex_adapter *adapter) +{ + void *p; + char drv_version[64]; + struct usb_card_rec *cardp; + struct sdio_mmc_card *sdio_card; + struct mwifiex_private *priv; + int i, idx; + struct netdev_queue *txq; + struct mwifiex_debug_info *debug_info; + + if (adapter->drv_info_dump) { + vfree(adapter->drv_info_dump); + adapter->drv_info_size = 0; + } + + dev_info(adapter->dev, "=== DRIVER INFO DUMP START===\n"); + + adapter->drv_info_dump = vzalloc(MWIFIEX_DRV_INFO_SIZE_MAX); + + if (!adapter->drv_info_dump) + return; + + p = (char *)(adapter->drv_info_dump); + p += sprintf(p, "driver_name = " "\"mwifiex\"\n"); + + mwifiex_drv_get_driver_version(adapter, drv_version, + sizeof(drv_version) - 1); + p += sprintf(p, "driver_version = %s\n", drv_version); + + if (adapter->iface_type == MWIFIEX_USB) { + 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, "rx_cmd_urb_pending = %d\n", + atomic_read(&cardp->rx_cmd_urb_pending)); + p += sprintf(p, "rx_data_urb_pending = %d\n", + atomic_read(&cardp->rx_data_urb_pending)); + } + + p += sprintf(p, "tx_pending = %d\n", + atomic_read(&adapter->tx_pending)); + p += sprintf(p, "rx_pending = %d\n", + atomic_read(&adapter->rx_pending)); + + if (adapter->iface_type == MWIFIEX_SDIO) { + sdio_card = (struct sdio_mmc_card *)adapter->card; + p += sprintf(p, "\nmp_rd_bitmap=0x%x curr_rd_port=0x%x\n", + sdio_card->mp_rd_bitmap, sdio_card->curr_rd_port); + p += sprintf(p, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n", + sdio_card->mp_wr_bitmap, sdio_card->curr_wr_port); + } + + for (i = 0; i < adapter->priv_num; i++) { + if (!adapter->priv[i] || !adapter->priv[i]->netdev) + continue; + priv = adapter->priv[i]; + p += sprintf(p, "\n[interface : \"%s\"]\n", + priv->netdev->name); + p += sprintf(p, "wmm_tx_pending[0] = %d\n", + atomic_read(&priv->wmm_tx_pending[0])); + p += sprintf(p, "wmm_tx_pending[1] = %d\n", + atomic_read(&priv->wmm_tx_pending[1])); + p += sprintf(p, "wmm_tx_pending[2] = %d\n", + atomic_read(&priv->wmm_tx_pending[2])); + p += sprintf(p, "wmm_tx_pending[3] = %d\n", + atomic_read(&priv->wmm_tx_pending[3])); + p += sprintf(p, "media_state=\"%s\"\n", !priv->media_connected ? + "Disconnected" : "Connected"); + p += sprintf(p, "carrier %s\n", (netif_carrier_ok(priv->netdev) + ? "on" : "off")); + for (idx = 0; idx < priv->netdev->num_tx_queues; idx++) { + txq = netdev_get_tx_queue(priv->netdev, idx); + p += sprintf(p, "tx queue %d:%s ", idx, + netif_tx_queue_stopped(txq) ? + "stopped" : "started"); + } + p += sprintf(p, "\n%s: num_tx_timeout = %d\n", + priv->netdev->name, priv->num_tx_timeout); + } + + if (adapter->iface_type == MWIFIEX_SDIO) { + p += sprintf(p, "\n=== SDIO register DUMP===\n"); + if (adapter->if_ops.reg_dump) + p += adapter->if_ops.reg_dump(adapter, p); + } + + p += sprintf(p, "\n=== MORE DEBUG INFORMATION\n"); + debug_info = kzalloc(sizeof(*debug_info), GFP_KERNEL); + if (debug_info) { + for (i = 0; i < adapter->priv_num; i++) { + if (!adapter->priv[i] || !adapter->priv[i]->netdev) + continue; + priv = adapter->priv[i]; + mwifiex_get_debug_info(priv, debug_info); + p += mwifiex_debug_info_to_buffer(priv, p, debug_info); + break; + } + kfree(debug_info); + } + + adapter->drv_info_size = p - adapter->drv_info_dump; + dev_info(adapter->dev, "=== DRIVER INFO DUMP END===\n"); +} +EXPORT_SYMBOL_GPL(mwifiex_dump_drv_info); + /* * CFG802.11 network device handler for statistics retrieval. */ @@ -847,26 +956,34 @@ static const struct net_device_ops mwifiex_netdev_ops = { * - Nick name : Set to null * - Number of Tx timeout : Set to 0 * - Device address : Set to current address + * - Rx histogram statistc : Set to 0 * * In addition, the CFG80211 work queue is also created. */ void mwifiex_init_priv_params(struct mwifiex_private *priv, - struct net_device *dev) + struct net_device *dev) { dev->netdev_ops = &mwifiex_netdev_ops; dev->destructor = free_netdev; /* Initialize private structure */ priv->current_key_index = 0; priv->media_connected = false; - memset(&priv->nick_name, 0, sizeof(priv->nick_name)); memset(priv->mgmt_ie, 0, sizeof(struct mwifiex_ie) * MAX_MGMT_IE_INDEX); priv->beacon_idx = MWIFIEX_AUTO_IDX_MASK; priv->proberesp_idx = MWIFIEX_AUTO_IDX_MASK; priv->assocresp_idx = MWIFIEX_AUTO_IDX_MASK; - priv->rsn_idx = MWIFIEX_AUTO_IDX_MASK; + priv->gen_idx = MWIFIEX_AUTO_IDX_MASK; priv->num_tx_timeout = 0; + ether_addr_copy(priv->curr_addr, priv->adapter->perm_addr); memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN); + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + priv->hist_data = kmalloc(sizeof(*priv->hist_data), GFP_KERNEL); + if (priv->hist_data) + mwifiex_hist_data_reset(priv); + } } /* @@ -1000,8 +1117,7 @@ err_init_fw: pr_debug("info: %s: unregister device\n", __func__); if (adapter->if_ops.unregister_dev) adapter->if_ops.unregister_dev(adapter); - if ((adapter->hw_status == MWIFIEX_HW_STATUS_FW_READY) || - (adapter->hw_status == MWIFIEX_HW_STATUS_READY)) { + if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) { pr_debug("info: %s: shutdown mwifiex\n", __func__); adapter->init_wait_q_woken = false; @@ -1052,6 +1168,8 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) adapter->surprise_removed = true; + mwifiex_terminate_workqueue(adapter); + /* Stop data */ for (i = 0; i < adapter->priv_num; i++) { priv = adapter->priv[i]; @@ -1086,16 +1204,15 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) continue; rtnl_lock(); - if (priv->wdev && priv->netdev) - mwifiex_del_virtual_intf(adapter->wiphy, priv->wdev); + if (priv->netdev && + priv->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED) + mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev); rtnl_unlock(); } wiphy_unregister(adapter->wiphy); wiphy_free(adapter->wiphy); - mwifiex_terminate_workqueue(adapter); - /* Unregister device */ dev_dbg(adapter->dev, "info: unregister device\n"); if (adapter->if_ops.unregister_dev) diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index e66993cb5daf..f0a6af179af0 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -41,6 +41,8 @@ #include "util.h" #include "fw.h" #include "pcie.h" +#include "usb.h" +#include "sdio.h" extern const char driver_version[]; @@ -136,6 +138,8 @@ enum { /* Threshold for tx_timeout_cnt before we trigger a card reset */ #define TX_TIMEOUT_THRESHOLD 6 +#define MWIFIEX_DRV_INFO_SIZE_MAX 0x40000 + struct mwifiex_dbg { u32 num_cmd_host_to_card_failure; u32 num_cmd_sleep_cfm_host_to_card_failure; @@ -161,7 +165,6 @@ struct mwifiex_dbg { enum MWIFIEX_HARDWARE_STATUS { MWIFIEX_HW_STATUS_READY, MWIFIEX_HW_STATUS_INITIALIZING, - MWIFIEX_HW_STATUS_FW_READY, MWIFIEX_HW_STATUS_INIT_DONE, MWIFIEX_HW_STATUS_RESET, MWIFIEX_HW_STATUS_CLOSING, @@ -413,6 +416,7 @@ struct mwifiex_roc_cfg { }; #define MWIFIEX_FW_DUMP_IDX 0xff +#define MWIFIEX_DRV_INFO_IDX 20 #define FW_DUMP_MAX_NAME_LEN 8 #define FW_DUMP_HOST_READY 0xEE #define FW_DUMP_DONE 0xFF @@ -543,13 +547,12 @@ struct mwifiex_private { u32 curr_bcn_size; /* spin lock for beacon buffer */ spinlock_t curr_bcn_buf_lock; - struct wireless_dev *wdev; + struct wireless_dev wdev; struct mwifiex_chan_freq_power cfp; char version_str[128]; #ifdef CONFIG_DEBUG_FS struct dentry *dfs_dev_dir; #endif - u8 nick_name[16]; u16 current_key_index; struct semaphore async_sem; struct cfg80211_scan_request *scan_request; @@ -564,7 +567,7 @@ struct mwifiex_private { u16 beacon_idx; u16 proberesp_idx; u16 assocresp_idx; - u16 rsn_idx; + u16 gen_idx; u8 ap_11n_enabled; u8 ap_11ac_enabled; u32 mgmt_frame_mask; @@ -574,6 +577,7 @@ struct mwifiex_private { unsigned long csa_expire_time; u8 del_list_idx; bool hs2_enabled; + struct mwifiex_uap_bss_param bss_cfg; struct station_parameters *sta_params; struct sk_buff_head tdls_txq; u8 check_tdls_tx; @@ -582,6 +586,16 @@ struct mwifiex_private { struct idr ack_status_frames; /* spin lock for ack status */ spinlock_t ack_status_lock; + /** rx histogram data */ + struct mwifiex_histogram_data *hist_data; + struct cfg80211_chan_def dfs_chandef; + struct workqueue_struct *dfs_cac_workqueue; + struct delayed_work dfs_cac_work; + struct timer_list dfs_chan_switch_timer; + struct workqueue_struct *dfs_chan_sw_workqueue; + struct delayed_work dfs_chan_sw_work; + struct cfg80211_beacon_data beacon_after; + struct mwifiex_11h_intf_state state_11h; }; enum mwifiex_ba_status { @@ -717,6 +731,7 @@ struct mwifiex_if_ops { int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *); void (*card_reset) (struct mwifiex_adapter *); void (*fw_dump)(struct mwifiex_adapter *); + int (*reg_dump)(struct mwifiex_adapter *, char *); int (*clean_pcie_ring) (struct mwifiex_adapter *adapter); void (*iface_work)(struct work_struct *work); void (*submit_rem_rx_urbs)(struct mwifiex_adapter *adapter); @@ -724,6 +739,8 @@ struct mwifiex_if_ops { struct mwifiex_adapter { u8 iface_type; + struct mwifiex_iface_comb iface_limit; + struct mwifiex_iface_comb curr_iface_comb; struct mwifiex_private *priv[MWIFIEX_MAX_BSS_NUM]; u8 priv_num; const struct firmware *firmware; @@ -731,6 +748,7 @@ struct mwifiex_adapter { int winner; struct device *dev; struct wiphy *wiphy; + u8 perm_addr[ETH_ALEN]; bool surprise_removed; u32 fw_release_number; u16 init_wait_q_woken; @@ -744,6 +762,8 @@ struct mwifiex_adapter { struct work_struct main_work; struct workqueue_struct *rx_workqueue; struct work_struct rx_work; + struct workqueue_struct *dfs_workqueue; + struct work_struct dfs_work; bool rx_work_enabled; bool rx_processing; bool delay_main_work; @@ -823,6 +843,7 @@ struct mwifiex_adapter { u16 gen_null_pkt; u16 pps_uapsd_mode; u32 pm_wakeup_fw_try; + struct timer_list wakeup_timer; u8 is_hs_configured; struct mwifiex_hs_config_param hs_cfg; u8 hs_activated; @@ -865,6 +886,8 @@ struct mwifiex_adapter { struct memory_type_mapping *mem_type_mapping_tbl; u8 num_mem_types; u8 curr_mem_idx; + void *drv_info_dump; + u32 drv_info_size; bool scan_chan_gap_enabled; struct sk_buff_head rx_data_q; struct mwifiex_chan_stats *chan_stats; @@ -979,7 +1002,7 @@ void mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv, const u8 *ra_addr); void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb); void *mwifiex_process_uap_txpd(struct mwifiex_private *, struct sk_buff *skb); -int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta); +int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta, bool init); int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd, struct mwifiex_scan_cmd_config *scan_cfg); void mwifiex_queue_scan_cmd(struct mwifiex_private *priv, @@ -1140,6 +1163,25 @@ mwifiex_get_priv(struct mwifiex_adapter *adapter, } /* + * This function returns the first available unused private structure pointer. + */ +static inline struct mwifiex_private * +mwifiex_get_unused_priv(struct mwifiex_adapter *adapter) +{ + int i; + + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + if (adapter->priv[i]->bss_mode == + NL80211_IFTYPE_UNSPECIFIED) + break; + } + } + + return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); +} + +/* * This function returns the driver private structure of a network device. */ static inline struct mwifiex_private * @@ -1230,8 +1272,6 @@ int mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, struct ieee80211_channel *chan, unsigned int duration); -int mwifiex_set_bss_role(struct mwifiex_private *priv, u8 bss_role); - int mwifiex_get_stats_info(struct mwifiex_private *priv, struct mwifiex_ds_get_stats *log); @@ -1291,9 +1331,17 @@ int mwifiex_set_mgmt_ies(struct mwifiex_private *priv, struct cfg80211_beacon_data *data); int mwifiex_del_mgmt_ies(struct mwifiex_private *priv); u8 *mwifiex_11d_code_2_region(u8 code); +void mwifiex_uap_set_channel(struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_chan_def chandef); +int mwifiex_config_start_uap(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg); void mwifiex_uap_del_sta_data(struct mwifiex_private *priv, struct mwifiex_sta_node *node); +void mwifiex_init_11h_params(struct mwifiex_private *priv); +int mwifiex_is_11h_active(struct mwifiex_private *priv); +int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag); + void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer, struct mwifiex_bssdescriptor *bss_desc); int mwifiex_11h_handle_event_chanswann(struct mwifiex_private *priv); @@ -1324,6 +1372,8 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, u8 *buf, int len); int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action); int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac); +int mwifiex_get_tdls_list(struct mwifiex_private *priv, + struct tdls_peer_info *buf); void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv); bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv); u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band, @@ -1340,6 +1390,11 @@ void mwifiex_check_auto_tdls(unsigned long context); void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac); void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv); void mwifiex_clean_auto_tdls(struct mwifiex_private *priv); +int mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf); +int mwifiex_11h_handle_chanrpt_ready(struct mwifiex_private *priv, + struct sk_buff *skb); void mwifiex_parse_tx_status_event(struct mwifiex_private *priv, void *event_body); @@ -1347,6 +1402,21 @@ void mwifiex_parse_tx_status_event(struct mwifiex_private *priv, struct sk_buff * mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv, struct sk_buff *skb, u8 flag, u64 *cookie); +void mwifiex_dfs_cac_work_queue(struct work_struct *work); +void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work); +void mwifiex_abort_cac(struct mwifiex_private *priv); +int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv, + struct sk_buff *skb); + +void mwifiex_hist_data_set(struct mwifiex_private *priv, u8 rx_rate, s8 snr, + s8 nflr); +void mwifiex_hist_data_reset(struct mwifiex_private *priv); +void mwifiex_hist_data_add(struct mwifiex_private *priv, + u8 rx_rate, s8 snr, s8 nflr); +u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv, + u8 rx_rate, u8 ht_info); + +void mwifiex_dump_drv_info(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 c3a20f94f3c9..a5828da59365 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -204,6 +204,7 @@ static int mwifiex_pcie_probe(struct pci_dev *pdev, card->pcie.blksz_fw_dl = data->blksz_fw_dl; card->pcie.tx_buf_size = data->tx_buf_size; card->pcie.supports_fw_dump = data->supports_fw_dump; + card->pcie.can_ext_scan = data->can_ext_scan; } if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops, @@ -1952,8 +1953,8 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, offset += txlen; } while (true); - dev_dbg(adapter->dev, "info:\nFW download over, size %d bytes\n", - offset); + dev_notice(adapter->dev, + "info: FW download over, size %d bytes\n", offset); ret = 0; @@ -2064,6 +2065,7 @@ static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) * state until cookie is set */ adapter->ps_state = PS_STATE_AWAKE; adapter->pm_wakeup_fw_try = false; + del_timer(&adapter->wakeup_timer); } } } @@ -2562,6 +2564,7 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) adapter->mem_type_mapping_tbl = mem_type_mapping_tbl; adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl); strcpy(adapter->fw_name, card->pcie.firmware); + adapter->ext_scan = card->pcie.can_ext_scan; return 0; } diff --git a/drivers/net/wireless/mwifiex/pcie.h b/drivers/net/wireless/mwifiex/pcie.h index 200e8b0cb582..666d40e9dbc3 100644 --- a/drivers/net/wireless/mwifiex/pcie.h +++ b/drivers/net/wireless/mwifiex/pcie.h @@ -206,6 +206,7 @@ struct mwifiex_pcie_device { u16 blksz_fw_dl; u16 tx_buf_size; bool supports_fw_dump; + bool can_ext_scan; }; static const struct mwifiex_pcie_device mwifiex_pcie8766 = { @@ -214,6 +215,7 @@ static const struct mwifiex_pcie_device mwifiex_pcie8766 = { .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, .supports_fw_dump = false, + .can_ext_scan = true, }; static const struct mwifiex_pcie_device mwifiex_pcie8897 = { @@ -222,6 +224,7 @@ static const struct mwifiex_pcie_device mwifiex_pcie8897 = { .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, .supports_fw_dump = true, + .can_ext_scan = true, }; struct mwifiex_evt_buf_desc { diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 984a7a4fa93b..0ffdb7c5afd2 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -496,10 +496,10 @@ mwifiex_scan_create_channel_list(struct mwifiex_private *priv, for (band = 0; (band < IEEE80211_NUM_BANDS) ; band++) { - if (!priv->wdev->wiphy->bands[band]) + if (!priv->wdev.wiphy->bands[band]) continue; - sband = priv->wdev->wiphy->bands[band]; + sband = priv->wdev.wiphy->bands[band]; for (i = 0; (i < sband->n_channels) ; i++) { ch = &sband->channels[i]; @@ -1429,6 +1429,12 @@ int mwifiex_scan_networks(struct mwifiex_private *priv, return -EBUSY; } + if (adapter->surprise_removed || adapter->is_cmd_timedout) { + dev_err(adapter->dev, + "Ignore scan. Card removed or firmware in bad state\n"); + return -EFAULT; + } + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); adapter->scan_processing = true; spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); @@ -1727,10 +1733,10 @@ mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info, freq = cfp ? cfp->freq : 0; - chan = ieee80211_get_channel(priv->wdev->wiphy, freq); + chan = ieee80211_get_channel(priv->wdev.wiphy, freq); if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) { - bss = cfg80211_inform_bss(priv->wdev->wiphy, + bss = cfg80211_inform_bss(priv->wdev.wiphy, chan, CFG80211_BSS_FTYPE_UNKNOWN, bssid, timestamp, cap_info_bitmap, beacon_period, @@ -1742,7 +1748,7 @@ mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info, !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); + cfg80211_put_bss(priv->wdev.wiphy, bss); } } else { dev_dbg(adapter->dev, "missing BSS channel IE\n"); diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 933dae137850..91e36cda9543 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -107,6 +107,7 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) card->mp_rx_agg_buf_size = data->mp_rx_agg_buf_size; card->supports_fw_dump = data->supports_fw_dump; card->auto_tdls = data->auto_tdls; + card->can_ext_scan = data->can_ext_scan; } sdio_claim_host(func); @@ -282,6 +283,9 @@ static int mwifiex_sdio_suspend(struct device *dev) #define SDIO_DEVICE_ID_MARVELL_8897 (0x912d) /* Device ID for SD8887 */ #define SDIO_DEVICE_ID_MARVELL_8887 (0x9135) +/* Device ID for SD8801 */ +#define SDIO_DEVICE_ID_MARVELL_8801 (0x9139) + /* WLAN IDs */ static const struct sdio_device_id mwifiex_ids[] = { @@ -295,6 +299,8 @@ static const struct sdio_device_id mwifiex_ids[] = { .driver_data = (unsigned long) &mwifiex_sdio_sd8897}, {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887), .driver_data = (unsigned long)&mwifiex_sdio_sd8887}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8801), + .driver_data = (unsigned long)&mwifiex_sdio_sd8801}, {}, }; @@ -986,8 +992,8 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, offset += txlen; } while (true); - dev_dbg(adapter->dev, "info: FW download over, size %d bytes\n", - offset); + dev_notice(adapter->dev, + "info: FW download over, size %d bytes\n", offset); ret = 0; done: @@ -1882,6 +1888,7 @@ static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) } adapter->auto_tdls = card->auto_tdls; + adapter->ext_scan = card->can_ext_scan; return ret; } @@ -1958,8 +1965,8 @@ static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter) pr_err("Resetting card...\n"); mmc_remove_host(target); - /* 20ms delay is based on experiment with sdhci controller */ - mdelay(20); + /* 200ms delay is based on experiment with sdhci controller */ + mdelay(200); target->rescan_entered = 0; /* rescan non-removable cards */ mmc_add_host(target); } @@ -2023,6 +2030,8 @@ static void mwifiex_sdio_fw_dump_work(struct work_struct *work) u32 memory_size; static char *env[] = { "DRIVER=mwifiex_sdio", "EVENT=fw_dump", NULL }; + mwifiex_dump_drv_info(adapter); + if (!card->supports_fw_dump) return; @@ -2166,6 +2175,99 @@ static void mwifiex_sdio_fw_dump(struct mwifiex_adapter *adapter) schedule_work(&adapter->iface_work); } +/* Function to dump SDIO function registers and SDIO scratch registers in case + * of FW crash + */ +static int +mwifiex_sdio_reg_dump(struct mwifiex_adapter *adapter, char *drv_buf) +{ + char *p = drv_buf; + struct sdio_mmc_card *cardp = adapter->card; + int ret = 0; + u8 count, func, data, index = 0, size = 0; + u8 reg, reg_start, reg_end; + char buf[256], *ptr; + + if (!p) + return 0; + + dev_info(adapter->dev, "SDIO register DUMP START\n"); + + mwifiex_pm_wakeup_card(adapter); + + sdio_claim_host(cardp->func); + + for (count = 0; count < 5; count++) { + memset(buf, 0, sizeof(buf)); + ptr = buf; + + switch (count) { + case 0: + /* Read the registers of SDIO function0 */ + func = count; + reg_start = 0; + reg_end = 9; + break; + case 1: + /* Read the registers of SDIO function1 */ + func = count; + reg_start = cardp->reg->func1_dump_reg_start; + reg_end = cardp->reg->func1_dump_reg_end; + break; + case 2: + index = 0; + func = 1; + reg_start = cardp->reg->func1_spec_reg_table[index++]; + size = cardp->reg->func1_spec_reg_num; + reg_end = cardp->reg->func1_spec_reg_table[size-1]; + break; + default: + /* Read the scratch registers of SDIO function1 */ + if (count == 4) + mdelay(100); + func = 1; + reg_start = cardp->reg->func1_scratch_reg; + reg_end = reg_start + MWIFIEX_SDIO_SCRATCH_SIZE; + } + + if (count != 2) + ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", + func, reg_start, reg_end); + else + ptr += sprintf(ptr, "SDIO Func%d: ", func); + + for (reg = reg_start; reg <= reg_end;) { + if (func == 0) + data = sdio_f0_readb(cardp->func, reg, &ret); + else + data = sdio_readb(cardp->func, reg, &ret); + + if (count == 2) + ptr += sprintf(ptr, "(%#x) ", reg); + if (!ret) { + ptr += sprintf(ptr, "%02x ", data); + } else { + ptr += sprintf(ptr, "ERR"); + break; + } + + if (count == 2 && reg < reg_end) + reg = cardp->reg->func1_spec_reg_table[index++]; + else + reg++; + } + + dev_info(adapter->dev, "%s\n", buf); + p += sprintf(p, "%s\n", buf); + } + + sdio_release_host(cardp->func); + + dev_info(adapter->dev, "SDIO register DUMP END\n"); + + return p - drv_buf; +} + static struct mwifiex_if_ops sdio_ops = { .init_if = mwifiex_init_sdio, .cleanup_if = mwifiex_cleanup_sdio, @@ -2188,6 +2290,7 @@ static struct mwifiex_if_ops sdio_ops = { .card_reset = mwifiex_sdio_card_reset, .iface_work = mwifiex_sdio_work, .fw_dump = mwifiex_sdio_fw_dump, + .reg_dump = mwifiex_sdio_reg_dump, }; /* diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h index 54c07156dd78..957cca246618 100644 --- a/drivers/net/wireless/mwifiex/sdio.h +++ b/drivers/net/wireless/mwifiex/sdio.h @@ -34,6 +34,7 @@ #define SD8797_DEFAULT_FW_NAME "mrvl/sd8797_uapsta.bin" #define SD8897_DEFAULT_FW_NAME "mrvl/sd8897_uapsta.bin" #define SD8887_DEFAULT_FW_NAME "mrvl/sd8887_uapsta.bin" +#define SD8801_DEFAULT_FW_NAME "mrvl/sd8801_uapsta.bin" #define BLOCK_MODE 1 #define BYTE_MODE 0 @@ -44,6 +45,9 @@ #define MWIFIEX_SDIO_BYTE_MODE_MASK 0x80000000 +#define MWIFIEX_MAX_FUNC2_REG_NUM 13 +#define MWIFIEX_SDIO_SCRATCH_SIZE 10 + #define SDIO_MPA_ADDR_BASE 0x1000 #define CTRL_PORT 0 #define CTRL_PORT_MASK 0x0001 @@ -219,6 +223,11 @@ struct mwifiex_sdio_card_reg { u8 fw_dump_ctrl; u8 fw_dump_start; u8 fw_dump_end; + u8 func1_dump_reg_start; + u8 func1_dump_reg_end; + u8 func1_scratch_reg; + u8 func1_spec_reg_num; + u8 func1_spec_reg_table[MWIFIEX_MAX_FUNC2_REG_NUM]; }; struct sdio_mmc_card { @@ -247,6 +256,7 @@ struct sdio_mmc_card { u8 *mp_regs; u8 auto_tdls; + bool can_ext_scan; struct mwifiex_sdio_mpa_tx mpa_tx; struct mwifiex_sdio_mpa_rx mpa_rx; @@ -264,6 +274,7 @@ struct mwifiex_sdio_device { u32 mp_tx_agg_buf_size; u32 mp_rx_agg_buf_size; u8 auto_tdls; + bool can_ext_scan; }; static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = { @@ -291,6 +302,11 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = { .rd_len_p0_l = 0x08, .rd_len_p0_u = 0x09, .card_misc_cfg_reg = 0x6c, + .func1_dump_reg_start = 0x0, + .func1_dump_reg_end = 0x9, + .func1_scratch_reg = 0x60, + .func1_spec_reg_num = 5, + .func1_spec_reg_table = {0x28, 0x30, 0x34, 0x38, 0x3c}, }; static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = { @@ -335,6 +351,12 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8897 = { .fw_dump_ctrl = 0xe2, .fw_dump_start = 0xe3, .fw_dump_end = 0xea, + .func1_dump_reg_start = 0x0, + .func1_dump_reg_end = 0xb, + .func1_scratch_reg = 0xc0, + .func1_spec_reg_num = 8, + .func1_spec_reg_table = {0x4C, 0x50, 0x54, 0x55, 0x58, + 0x59, 0x5c, 0x5d}, }; static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8887 = { @@ -376,6 +398,13 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8887 = { .cmd_cfg_1 = 0xc5, .cmd_cfg_2 = 0xc6, .cmd_cfg_3 = 0xc7, + .func1_dump_reg_start = 0x10, + .func1_dump_reg_end = 0x17, + .func1_scratch_reg = 0x90, + .func1_spec_reg_num = 13, + .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, + 0x61, 0x62, 0x64, 0x65, 0x66, + 0x68, 0x69, 0x6a}, }; static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = { @@ -390,6 +419,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = { .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, .supports_fw_dump = false, .auto_tdls = false, + .can_ext_scan = false, }; static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = { @@ -404,6 +434,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = { .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, .supports_fw_dump = false, .auto_tdls = false, + .can_ext_scan = true, }; static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = { @@ -418,6 +449,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = { .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, .supports_fw_dump = false, .auto_tdls = false, + .can_ext_scan = true, }; static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = { @@ -432,6 +464,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = { .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K, .supports_fw_dump = true, .auto_tdls = false, + .can_ext_scan = true, }; static const struct mwifiex_sdio_device mwifiex_sdio_sd8887 = { @@ -446,6 +479,22 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8887 = { .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K, .supports_fw_dump = false, .auto_tdls = true, + .can_ext_scan = true, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8801 = { + .firmware = SD8801_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd87xx, + .max_ports = 16, + .mp_agg_pkt_limit = 8, + .supports_sdio_new_mode = false, + .has_control_mask = true, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, + .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .supports_fw_dump = false, + .auto_tdls = false, + .can_ext_scan = true, }; /* diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index 1c2ca291d1f5..f7d204ffd6e9 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -26,6 +26,10 @@ #include "11n.h" #include "11ac.h" +static bool disable_auto_ds; +module_param(disable_auto_ds, bool, 0); +MODULE_PARM_DESC(disable_auto_ds, + "deepsleep enabled=0(default), deepsleep disabled=1"); /* * This function prepares command to set/get RSSI information. * @@ -1893,6 +1897,10 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, case HostCmd_CMD_TDLS_OPER: ret = mwifiex_cmd_tdls_oper(priv, cmd_ptr, data_buf); break; + case HostCmd_CMD_CHAN_REPORT_REQUEST: + ret = mwifiex_cmd_issue_chan_report_request(priv, cmd_ptr, + data_buf); + break; default: dev_err(priv->adapter->dev, "PREP_CMD: unknown cmd- %#x\n", cmd_no); @@ -1907,6 +1915,8 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, * * This is called after firmware download to bring the card to * working state. + * Function is also called during reinitialization of virtual + * interfaces. * * The following commands are issued sequentially - * - Set PCI-Express host buffer configuration (PCIE only) @@ -1921,7 +1931,7 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, * - Set 11d control * - Set MAC control (this must be the last command to initialize firmware) */ -int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) +int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) { struct mwifiex_adapter *adapter = priv->adapter; int ret; @@ -2031,7 +2041,8 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) if (ret) return -1; - if (first_sta && priv->adapter->iface_type != MWIFIEX_USB && + if (!disable_auto_ds && + first_sta && priv->adapter->iface_type != MWIFIEX_USB && priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { /* Enable auto deep sleep */ auto_ds.auto_ds = DEEP_SLEEP_ON; @@ -2054,9 +2065,6 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) "11D: failed to enable 11D\n"); } - /* set last_init_cmd before sending the command */ - priv->adapter->last_init_cmd = HostCmd_CMD_11N_CFG; - /* Send cmd to FW to configure 11n specific configuration * (Short GI, Channel BW, Green field support etc.) for transmit */ @@ -2064,7 +2072,11 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_CFG, HostCmd_ACT_GEN_SET, 0, &tx_cfg, true); - ret = -EINPROGRESS; + if (init) { + /* set last_init_cmd before sending the command */ + priv->adapter->last_init_cmd = HostCmd_CMD_11N_CFG; + ret = -EINPROGRESS; + } return ret; } diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index b65e1014b0fc..5f8da5924666 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -248,6 +248,8 @@ static int mwifiex_ret_get_log(struct mwifiex_private *priv, le32_to_cpu(get_log->wep_icv_err_cnt[2]); stats->wep_icv_error[3] = le32_to_cpu(get_log->wep_icv_err_cnt[3]); + stats->bcn_rcv_cnt = le32_to_cpu(get_log->bcn_rcv_cnt); + stats->bcn_miss_cnt = le32_to_cpu(get_log->bcn_miss_cnt); } return 0; @@ -1103,6 +1105,9 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, case HostCmd_CMD_UAP_SYS_CONFIG: break; case HostCmd_CMD_UAP_BSS_START: + adapter->tx_lock_flag = false; + adapter->pps_uapsd_mode = false; + adapter->delay_null_pkt = false; priv->bss_started = 1; break; case HostCmd_CMD_UAP_BSS_STOP: @@ -1117,6 +1122,8 @@ int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, case HostCmd_CMD_TDLS_OPER: ret = mwifiex_ret_tdls_oper(priv, resp); break; + case HostCmd_CMD_CHAN_REPORT_REQUEST: + break; default: dev_err(adapter->dev, "CMD_RESP: unknown cmd response %#x\n", resp->command); diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index b8c171df6223..80ffe7412496 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -90,6 +90,10 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code) priv->is_data_rate_auto = true; priv->data_rate = 0; + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) && priv->hist_data) + mwifiex_hist_data_reset(priv); + if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { priv->adhoc_state = ADHOC_IDLE; priv->adhoc_is_link_sensed = false; @@ -308,6 +312,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) adapter->ps_state = PS_STATE_AWAKE; adapter->pm_wakeup_card_req = false; adapter->pm_wakeup_fw_try = false; + del_timer_sync(&adapter->wakeup_timer); break; } if (!mwifiex_send_null_packet @@ -322,6 +327,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) adapter->ps_state = PS_STATE_AWAKE; adapter->pm_wakeup_card_req = false; adapter->pm_wakeup_fw_try = false; + del_timer_sync(&adapter->wakeup_timer); break; @@ -480,7 +486,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) case EVENT_REMAIN_ON_CHAN_EXPIRED: dev_dbg(adapter->dev, "event: Remain on channel expired\n"); - cfg80211_remain_on_channel_expired(priv->wdev, + cfg80211_remain_on_channel_expired(&priv->wdev, priv->roc_cfg.cookie, &priv->roc_cfg.chan, GFP_ATOMIC); @@ -509,6 +515,16 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) mwifiex_parse_tx_status_event(priv, adapter->event_body); break; + case EVENT_CHANNEL_REPORT_RDY: + dev_dbg(adapter->dev, "event: Channel Report\n"); + ret = mwifiex_11h_handle_chanrpt_ready(priv, + adapter->event_skb); + break; + case EVENT_RADAR_DETECTED: + dev_dbg(adapter->dev, "event: Radar detected\n"); + ret = mwifiex_11h_handle_radar_detected(priv, + adapter->event_skb); + break; default: dev_dbg(adapter->dev, "event: unknown event id: %#x\n", eventcause); diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 1626868a4b5c..0599e41e253c 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -219,7 +219,7 @@ static int mwifiex_process_country_ie(struct mwifiex_private *priv, if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) { rcu_read_unlock(); - wiphy_dbg(priv->wdev->wiphy, + wiphy_dbg(priv->wdev.wiphy, "11D: skip setting domain info in FW\n"); return 0; } @@ -902,9 +902,12 @@ static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv, if (wep_key->key_length) { void *enc_key; - if (encrypt_key->key_disable) + if (encrypt_key->key_disable) { memset(&priv->wep_key[index], 0, sizeof(struct mwifiex_wep_key)); + if (wep_key->key_length) + goto done; + } if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) enc_key = encrypt_key; @@ -918,6 +921,7 @@ static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv, return ret; } +done: if (priv->sec_info.wep_enabled) priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; else @@ -1131,36 +1135,6 @@ mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, return roc_cfg.status; } -int -mwifiex_set_bss_role(struct mwifiex_private *priv, u8 bss_role) -{ - if (GET_BSS_ROLE(priv) == bss_role) { - dev_dbg(priv->adapter->dev, - "info: already in the desired role.\n"); - return 0; - } - - mwifiex_free_priv(priv); - mwifiex_init_priv(priv); - - priv->bss_role = bss_role; - switch (bss_role) { - case MWIFIEX_BSS_ROLE_UAP: - priv->bss_mode = NL80211_IFTYPE_AP; - break; - case MWIFIEX_BSS_ROLE_STA: - case MWIFIEX_BSS_ROLE_ANY: - default: - priv->bss_mode = NL80211_IFTYPE_STATION; - break; - } - - mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, - HostCmd_ACT_GEN_SET, 0, NULL, true); - - return mwifiex_sta_init_cmd(priv, false); -} - /* * Sends IOCTL request to get statistics information. * diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c index c2ad3b63ae70..b8729c9394e9 100644 --- a/drivers/net/wireless/mwifiex/sta_rx.c +++ b/drivers/net/wireless/mwifiex/sta_rx.c @@ -90,6 +90,7 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv, struct ethhdr *eth; u16 rx_pkt_off, rx_pkt_len; u8 *offset; + u8 adj_rx_rate = 0; local_rx_pd = (struct rxpd *) (skb->data); @@ -155,6 +156,14 @@ int mwifiex_process_rx_packet(struct mwifiex_private *priv, priv->rxpd_htinfo = local_rx_pd->ht_info; + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + adj_rx_rate = mwifiex_adjust_data_rate(priv, priv->rxpd_rate, + priv->rxpd_htinfo); + mwifiex_hist_data_add(priv, adj_rx_rate, local_rx_pd->snr, + local_rx_pd->nf); + } + ret = mwifiex_recv_packet(priv, skb); if (ret == -1) dev_err(priv->adapter->dev, "recv packet failed\n"); diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c index b896d7375b52..5ce2d9a4f919 100644 --- a/drivers/net/wireless/mwifiex/sta_tx.c +++ b/drivers/net/wireless/mwifiex/sta_tx.c @@ -47,8 +47,10 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv, struct mwifiex_adapter *adapter = priv->adapter; struct txpd *local_tx_pd; struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); - u8 pad; + unsigned int pad; u16 pkt_type, pkt_offset; + int hroom = (priv->adapter->iface_type == MWIFIEX_USB) ? 0 : + INTF_HEADER_LEN; if (!skb->len) { dev_err(adapter->dev, "Tx: bad packet length: %d\n", skb->len); @@ -56,13 +58,12 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv, return skb->data; } - pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; + BUG_ON(skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN); - /* If skb->data is not aligned; add padding */ - pad = (4 - (((void *)skb->data - NULL) & 0x3)) % 4; + pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; - BUG_ON(skb_headroom(skb) < (sizeof(*local_tx_pd) + INTF_HEADER_LEN - + pad)); + pad = ((void *)skb->data - (sizeof(*local_tx_pd) + hroom)- + NULL) & (MWIFIEX_DMA_ALIGN_SZ - 1); skb_push(skb, sizeof(*local_tx_pd) + pad); local_tx_pd = (struct txpd *) skb->data; @@ -70,8 +71,8 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv, local_tx_pd->bss_num = priv->bss_num; local_tx_pd->bss_type = priv->bss_type; local_tx_pd->tx_pkt_length = cpu_to_le16((u16)(skb->len - - (sizeof(struct txpd) - + pad))); + (sizeof(struct txpd) + + pad))); local_tx_pd->priority = (u8) skb->priority; local_tx_pd->pkt_delay_2ms = @@ -115,7 +116,7 @@ void *mwifiex_process_sta_txpd(struct mwifiex_private *priv, local_tx_pd->tx_pkt_offset = cpu_to_le16(pkt_offset); /* make space for INTF_HEADER_LEN */ - skb_push(skb, INTF_HEADER_LEN); + skb_push(skb, hroom); if (!local_tx_pd->tx_control) /* TxCtrl set by user or default */ @@ -182,9 +183,13 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) } switch (ret) { case -EBUSY: - adapter->data_sent = true; - /* Fall through FAILURE handling */ + dev_kfree_skb_any(skb); + dev_err(adapter->dev, "%s: host_to_card failed: ret=%d\n", + __func__, ret); + adapter->dbg.num_tx_host_to_card_failure++; + break; case -1: + adapter->data_sent = false; dev_kfree_skb_any(skb); dev_err(adapter->dev, "%s: host_to_card failed: ret=%d\n", __func__, ret); @@ -197,6 +202,7 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) adapter->tx_lock_flag = true; break; case -EINPROGRESS: + adapter->tx_lock_flag = true; break; default: break; diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index 22884b429be7..087d84762cd3 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -1123,6 +1123,36 @@ int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac) return TDLS_NOT_SETUP; } +int mwifiex_get_tdls_list(struct mwifiex_private *priv, + struct tdls_peer_info *buf) +{ + struct mwifiex_sta_node *sta_ptr; + struct tdls_peer_info *peer = buf; + int count = 0; + unsigned long flags; + + if (!ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) + return 0; + + /* make sure we are in station mode and connected */ + if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) + return 0; + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + list_for_each_entry(sta_ptr, &priv->sta_list, list) { + if (sta_ptr->tdls_status == TDLS_SETUP_COMPLETE) { + ether_addr_copy(peer->peer_addr, sta_ptr->mac_addr); + peer++; + count++; + if (count >= MWIFIEX_MAX_TDLS_PEER_SUPPORTED) + break; + } + } + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + + return count; +} + void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv) { struct mwifiex_sta_node *sta_ptr; @@ -1367,9 +1397,8 @@ void mwifiex_check_auto_tdls(unsigned long context) void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv) { - init_timer(&priv->auto_tdls_timer); - priv->auto_tdls_timer.function = mwifiex_check_auto_tdls; - priv->auto_tdls_timer.data = (unsigned long)priv; + setup_timer(&priv->auto_tdls_timer, mwifiex_check_auto_tdls, + (unsigned long)priv); priv->auto_tdls_timer_active = true; mod_timer(&priv->auto_tdls_timer, jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S)); diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c index 6ae133333363..ac93557cbdc9 100644 --- a/drivers/net/wireless/mwifiex/txrx.c +++ b/drivers/net/wireless/mwifiex/txrx.c @@ -227,7 +227,7 @@ void mwifiex_parse_tx_status_event(struct mwifiex_private *priv, /* consumes ack_skb */ skb_complete_wifi_ack(ack_skb, !tx_status->status); } else { - cfg80211_mgmt_tx_status(priv->wdev, tx_info->cookie, + cfg80211_mgmt_tx_status(&priv->wdev, tx_info->cookie, ack_skb->data, ack_skb->len, !tx_status->status, GFP_ATOMIC); dev_kfree_skb_any(ack_skb); diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c index 0f347fdefa0a..f5c2af01ba0a 100644 --- a/drivers/net/wireless/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/mwifiex/uap_cmd.c @@ -761,6 +761,11 @@ int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, u16 cmd_no, if (mwifiex_cmd_uap_sta_deauth(priv, cmd, data_buf)) return -1; break; + case HostCmd_CMD_CHAN_REPORT_REQUEST: + if (mwifiex_cmd_issue_chan_report_request(priv, cmd_buf, + data_buf)) + return -1; + break; default: dev_err(priv->adapter->dev, "PREP_CMD: unknown cmd %#x\n", cmd_no); @@ -769,3 +774,68 @@ int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, u16 cmd_no, return 0; } + +void mwifiex_uap_set_channel(struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_chan_def chandef) +{ + u8 config_bands = 0; + + bss_cfg->channel = ieee80211_frequency_to_channel( + chandef.chan->center_freq); + + /* Set appropriate bands */ + if (chandef.chan->band == IEEE80211_BAND_2GHZ) { + bss_cfg->band_cfg = BAND_CONFIG_BG; + config_bands = BAND_B | BAND_G; + + if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT) + config_bands |= BAND_GN; + } else { + bss_cfg->band_cfg = BAND_CONFIG_A; + config_bands = BAND_A; + + if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT) + config_bands |= BAND_AN; + + if (chandef.width > NL80211_CHAN_WIDTH_40) + config_bands |= BAND_AAC; + } +} + +int mwifiex_config_start_uap(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg) +{ + if (mwifiex_del_mgmt_ies(priv)) + dev_err(priv->adapter->dev, "Failed to delete mgmt IEs!\n"); + + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, NULL, true)) { + dev_err(priv->adapter->dev, "Failed to stop the BSS\n"); + return -1; + } + + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, + HostCmd_ACT_GEN_SET, + UAP_BSS_PARAMS_I, bss_cfg, false)) { + dev_err(priv->adapter->dev, "Failed to set the SSID\n"); + return -1; + } + + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START, + HostCmd_ACT_GEN_SET, 0, NULL, false)) { + dev_err(priv->adapter->dev, "Failed to start the BSS\n"); + return -1; + } + + if (priv->sec_info.wep_enabled) + priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; + else + priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; + + if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + &priv->curr_pkt_filter, true)) + return -1; + + return 0; +} diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c index c54a537e31fb..f4794cdc36d2 100644 --- a/drivers/net/wireless/mwifiex/uap_event.c +++ b/drivers/net/wireless/mwifiex/uap_event.c @@ -68,7 +68,6 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv) len = ETH_ALEN; if (len != -1) { - sinfo.filled = STATION_INFO_ASSOC_REQ_IES; sinfo.assoc_req_ies = &event->data[len]; len = (u8 *)sinfo.assoc_req_ies - (u8 *)&event->frame_control; @@ -132,6 +131,8 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv) dev_dbg(adapter->dev, "AP EVENT: event id: %#x\n", eventcause); memcpy(priv->netdev->dev_addr, adapter->event_body + 2, ETH_ALEN); + if (priv->hist_data) + mwifiex_hist_data_reset(priv); break; case EVENT_UAP_MIC_COUNTERMEASURES: /* For future development */ @@ -177,6 +178,53 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv) dev_dbg(adapter->dev, "event: TX_STATUS Report\n"); mwifiex_parse_tx_status_event(priv, adapter->event_body); break; + case EVENT_PS_SLEEP: + dev_dbg(adapter->dev, "info: EVENT: SLEEP\n"); + + adapter->ps_state = PS_STATE_PRE_SLEEP; + + mwifiex_check_ps_cond(adapter); + break; + + case EVENT_PS_AWAKE: + dev_dbg(adapter->dev, "info: EVENT: AWAKE\n"); + if (!adapter->pps_uapsd_mode && + priv->media_connected && adapter->sleep_period.period) { + adapter->pps_uapsd_mode = true; + dev_dbg(adapter->dev, + "event: PPS/UAPSD mode activated\n"); + } + 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) { + adapter->ps_state = PS_STATE_AWAKE; + adapter->pm_wakeup_card_req = false; + adapter->pm_wakeup_fw_try = false; + break; + } + if (!mwifiex_send_null_packet + (priv, + MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET | + MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) + adapter->ps_state = + PS_STATE_SLEEP; + return 0; + } + } + adapter->ps_state = PS_STATE_AWAKE; + adapter->pm_wakeup_card_req = false; + adapter->pm_wakeup_fw_try = false; + break; + + case EVENT_CHANNEL_REPORT_RDY: + dev_dbg(adapter->dev, "event: Channel Report\n"); + mwifiex_11h_handle_chanrpt_ready(priv, adapter->event_skb); + break; + case EVENT_RADAR_DETECTED: + dev_dbg(adapter->dev, "event: Radar detected\n"); + mwifiex_11h_handle_radar_detected(priv, adapter->event_skb); + break; default: dev_dbg(adapter->dev, "event: unknown event id: %#x\n", eventcause); diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c index be3a203a529b..38ac4d74c486 100644 --- a/drivers/net/wireless/mwifiex/uap_txrx.c +++ b/drivers/net/wireless/mwifiex/uap_txrx.c @@ -348,8 +348,10 @@ void *mwifiex_process_uap_txpd(struct mwifiex_private *priv, struct mwifiex_adapter *adapter = priv->adapter; struct uap_txpd *txpd; struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); - int pad, len; - u16 pkt_type; + int pad; + u16 pkt_type, pkt_offset; + int hroom = (priv->adapter->iface_type == MWIFIEX_USB) ? 0 : + INTF_HEADER_LEN; if (!skb->len) { dev_err(adapter->dev, "Tx: bad packet length: %d\n", skb->len); @@ -357,22 +359,21 @@ void *mwifiex_process_uap_txpd(struct mwifiex_private *priv, return skb->data; } - pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; - - /* If skb->data is not aligned, add padding */ - pad = (4 - (((void *)skb->data - NULL) & 0x3)) % 4; + BUG_ON(skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN); - len = sizeof(*txpd) + pad; + pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; - BUG_ON(skb_headroom(skb) < len + INTF_HEADER_LEN); + pad = ((void *)skb->data - (sizeof(*txpd) + hroom) - NULL) & + (MWIFIEX_DMA_ALIGN_SZ - 1); - skb_push(skb, len); + skb_push(skb, sizeof(*txpd) + pad); txpd = (struct uap_txpd *)skb->data; memset(txpd, 0, sizeof(*txpd)); txpd->bss_num = priv->bss_num; txpd->bss_type = priv->bss_type; - txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - len)); + txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - (sizeof(*txpd) + + pad))); txpd->priority = (u8)skb->priority; txpd->pkt_delay_2ms = mwifiex_wmm_compute_drv_pkt_delay(priv, skb); @@ -392,16 +393,17 @@ void *mwifiex_process_uap_txpd(struct mwifiex_private *priv, cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[txpd->priority]); /* Offset of actual data */ + pkt_offset = sizeof(*txpd) + pad; if (pkt_type == PKT_TYPE_MGMT) { /* Set the packet type and add header for management frame */ txpd->tx_pkt_type = cpu_to_le16(pkt_type); - len += MWIFIEX_MGMT_FRAME_HEADER_SIZE; + pkt_offset += MWIFIEX_MGMT_FRAME_HEADER_SIZE; } - txpd->tx_pkt_offset = cpu_to_le16(len); + txpd->tx_pkt_offset = cpu_to_le16(pkt_offset); /* make space for INTF_HEADER_LEN */ - skb_push(skb, INTF_HEADER_LEN); + skb_push(skb, hroom); if (!txpd->tx_control) /* TxCtrl set by user or default */ diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index 1b56495ec872..223873022ffe 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -37,6 +37,11 @@ static struct usb_device_id mwifiex_usb_table[] = { {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8797_PID_2, USB_CLASS_VENDOR_SPEC, USB_SUBCLASS_VENDOR_SPEC, 0xff)}, + /* 8801 */ + {USB_DEVICE(USB8XXX_VID, USB8801_PID_1)}, + {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8801_PID_2, + USB_CLASS_VENDOR_SPEC, + USB_SUBCLASS_VENDOR_SPEC, 0xff)}, /* 8897 */ {USB_DEVICE(USB8XXX_VID, USB8897_PID_1)}, {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8897_PID_2, @@ -361,11 +366,13 @@ static int mwifiex_usb_probe(struct usb_interface *intf, switch (id_product) { case USB8766_PID_1: case USB8797_PID_1: + case USB8801_PID_1: case USB8897_PID_1: card->usb_boot_state = USB8XXX_FW_DNLD; break; case USB8766_PID_2: case USB8797_PID_2: + case USB8801_PID_2: case USB8897_PID_2: card->usb_boot_state = USB8XXX_FW_READY; break; @@ -792,11 +799,19 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter) case USB8897_PID_2: adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K; strcpy(adapter->fw_name, USB8897_DEFAULT_FW_NAME); + adapter->ext_scan = true; break; case USB8766_PID_1: case USB8766_PID_2: adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; strcpy(adapter->fw_name, USB8766_DEFAULT_FW_NAME); + adapter->ext_scan = true; + break; + case USB8801_PID_1: + case USB8801_PID_2: + adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; + strcpy(adapter->fw_name, USB8801_DEFAULT_FW_NAME); + adapter->ext_scan = false; break; case USB8797_PID_1: case USB8797_PID_2: @@ -930,7 +945,8 @@ static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, } while ((dnld_cmd != FW_HAS_LAST_BLOCK) && retries); cleanup: - dev_dbg(adapter->dev, "%s: %d bytes downloaded\n", __func__, tlen); + dev_notice(adapter->dev, + "info: FW download over, size %d bytes\n", tlen); kfree(recv_buff); kfree(fwdata); @@ -990,6 +1006,7 @@ static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) { /* Simulation of HS_AWAKE event */ adapter->pm_wakeup_fw_try = false; + del_timer_sync(&adapter->wakeup_timer); adapter->pm_wakeup_card_req = false; adapter->ps_state = PS_STATE_AWAKE; @@ -1010,6 +1027,13 @@ static void mwifiex_usb_submit_rem_rx_urbs(struct mwifiex_adapter *adapter) } } +/* This function is called after the card has woken up. */ +static inline int +mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) +{ + return 0; +} + static struct mwifiex_if_ops usb_ops = { .register_dev = mwifiex_register_dev, .unregister_dev = mwifiex_unregister_dev, @@ -1074,4 +1098,5 @@ MODULE_VERSION(USB_VERSION); MODULE_LICENSE("GPL v2"); MODULE_FIRMWARE(USB8766_DEFAULT_FW_NAME); MODULE_FIRMWARE(USB8797_DEFAULT_FW_NAME); +MODULE_FIRMWARE(USB8801_DEFAULT_FW_NAME); MODULE_FIRMWARE(USB8897_DEFAULT_FW_NAME); diff --git a/drivers/net/wireless/mwifiex/usb.h b/drivers/net/wireless/mwifiex/usb.h index a7cbba1355af..57e1a5736318 100644 --- a/drivers/net/wireless/mwifiex/usb.h +++ b/drivers/net/wireless/mwifiex/usb.h @@ -30,6 +30,9 @@ #define USB8797_PID_2 0x2044 #define USB8897_PID_1 0x2045 #define USB8897_PID_2 0x2046 +#define USB8801_PID_1 0x2049 +#define USB8801_PID_2 0x204a + #define USB8XXX_FW_DNLD 1 #define USB8XXX_FW_READY 2 @@ -41,6 +44,7 @@ #define USB8766_DEFAULT_FW_NAME "mrvl/usb8766_uapsta.bin" #define USB8797_DEFAULT_FW_NAME "mrvl/usb8797_uapsta.bin" +#define USB8801_DEFAULT_FW_NAME "mrvl/usb8801_uapsta.bin" #define USB8897_DEFAULT_FW_NAME "mrvl/usb8897_uapsta.bin" #define FW_DNLD_TX_BUF_SIZE 620 @@ -96,11 +100,4 @@ struct fw_data { u8 data[1]; }; -/* This function is called after the card has woken up. */ -static inline int -mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) -{ - return 0; -} - #endif /*_MWIFIEX_USB_H */ diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c index b1768fbf98f2..308550611f22 100644 --- a/drivers/net/wireless/mwifiex/util.c +++ b/drivers/net/wireless/mwifiex/util.c @@ -25,6 +25,96 @@ #include "wmm.h" #include "11n.h" +static struct mwifiex_debug_data items[] = { + {"int_counter", item_size(int_counter), + item_addr(int_counter), 1}, + {"wmm_ac_vo", item_size(packets_out[WMM_AC_VO]), + item_addr(packets_out[WMM_AC_VO]), 1}, + {"wmm_ac_vi", item_size(packets_out[WMM_AC_VI]), + item_addr(packets_out[WMM_AC_VI]), 1}, + {"wmm_ac_be", item_size(packets_out[WMM_AC_BE]), + item_addr(packets_out[WMM_AC_BE]), 1}, + {"wmm_ac_bk", item_size(packets_out[WMM_AC_BK]), + item_addr(packets_out[WMM_AC_BK]), 1}, + {"tx_buf_size", item_size(tx_buf_size), + item_addr(tx_buf_size), 1}, + {"curr_tx_buf_size", item_size(curr_tx_buf_size), + item_addr(curr_tx_buf_size), 1}, + {"ps_mode", item_size(ps_mode), + item_addr(ps_mode), 1}, + {"ps_state", item_size(ps_state), + item_addr(ps_state), 1}, + {"is_deep_sleep", item_size(is_deep_sleep), + item_addr(is_deep_sleep), 1}, + {"wakeup_dev_req", item_size(pm_wakeup_card_req), + item_addr(pm_wakeup_card_req), 1}, + {"wakeup_tries", item_size(pm_wakeup_fw_try), + item_addr(pm_wakeup_fw_try), 1}, + {"hs_configured", item_size(is_hs_configured), + item_addr(is_hs_configured), 1}, + {"hs_activated", item_size(hs_activated), + item_addr(hs_activated), 1}, + {"num_tx_timeout", item_size(num_tx_timeout), + item_addr(num_tx_timeout), 1}, + {"is_cmd_timedout", item_size(is_cmd_timedout), + item_addr(is_cmd_timedout), 1}, + {"timeout_cmd_id", item_size(timeout_cmd_id), + item_addr(timeout_cmd_id), 1}, + {"timeout_cmd_act", item_size(timeout_cmd_act), + item_addr(timeout_cmd_act), 1}, + {"last_cmd_id", item_size(last_cmd_id), + item_addr(last_cmd_id), DBG_CMD_NUM}, + {"last_cmd_act", item_size(last_cmd_act), + item_addr(last_cmd_act), DBG_CMD_NUM}, + {"last_cmd_index", item_size(last_cmd_index), + item_addr(last_cmd_index), 1}, + {"last_cmd_resp_id", item_size(last_cmd_resp_id), + item_addr(last_cmd_resp_id), DBG_CMD_NUM}, + {"last_cmd_resp_index", item_size(last_cmd_resp_index), + item_addr(last_cmd_resp_index), 1}, + {"last_event", item_size(last_event), + item_addr(last_event), DBG_CMD_NUM}, + {"last_event_index", item_size(last_event_index), + item_addr(last_event_index), 1}, + {"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure), + item_addr(num_cmd_host_to_card_failure), 1}, + {"num_cmd_sleep_cfm_fail", + item_size(num_cmd_sleep_cfm_host_to_card_failure), + item_addr(num_cmd_sleep_cfm_host_to_card_failure), 1}, + {"num_tx_h2c_fail", item_size(num_tx_host_to_card_failure), + item_addr(num_tx_host_to_card_failure), 1}, + {"num_evt_deauth", item_size(num_event_deauth), + item_addr(num_event_deauth), 1}, + {"num_evt_disassoc", item_size(num_event_disassoc), + item_addr(num_event_disassoc), 1}, + {"num_evt_link_lost", item_size(num_event_link_lost), + item_addr(num_event_link_lost), 1}, + {"num_cmd_deauth", item_size(num_cmd_deauth), + item_addr(num_cmd_deauth), 1}, + {"num_cmd_assoc_ok", item_size(num_cmd_assoc_success), + item_addr(num_cmd_assoc_success), 1}, + {"num_cmd_assoc_fail", item_size(num_cmd_assoc_failure), + item_addr(num_cmd_assoc_failure), 1}, + {"cmd_sent", item_size(cmd_sent), + item_addr(cmd_sent), 1}, + {"data_sent", item_size(data_sent), + item_addr(data_sent), 1}, + {"cmd_resp_received", item_size(cmd_resp_received), + item_addr(cmd_resp_received), 1}, + {"event_received", item_size(event_received), + item_addr(event_received), 1}, + + /* variables defined in struct mwifiex_adapter */ + {"cmd_pending", adapter_item_size(cmd_pending), + adapter_item_addr(cmd_pending), 1}, + {"tx_pending", adapter_item_size(tx_pending), + adapter_item_addr(tx_pending), 1}, + {"rx_pending", adapter_item_size(rx_pending), + adapter_item_addr(rx_pending), 1}, +}; + +static int num_of_items = ARRAY_SIZE(items); + /* * Firmware initialization complete callback handler. * @@ -97,6 +187,8 @@ int mwifiex_get_debug_info(struct mwifiex_private *priv, info->rx_tbl); info->tx_tbl_num = mwifiex_get_tx_ba_stream_tbl(priv, info->tx_tbl); + info->tdls_peer_num = mwifiex_get_tdls_list(priv, + info->tdls_list); info->ps_mode = adapter->ps_mode; info->ps_state = adapter->ps_state; info->is_deep_sleep = adapter->is_deep_sleep; @@ -141,6 +233,93 @@ int mwifiex_get_debug_info(struct mwifiex_private *priv, return 0; } +int mwifiex_debug_info_to_buffer(struct mwifiex_private *priv, char *buf, + struct mwifiex_debug_info *info) +{ + char *p = buf; + struct mwifiex_debug_data *d = &items[0]; + size_t size, addr; + long val; + int i, j; + + if (!info) + return 0; + + for (i = 0; i < num_of_items; i++) { + p += sprintf(p, "%s=", d[i].name); + + size = d[i].size / d[i].num; + + if (i < (num_of_items - 3)) + addr = d[i].addr + (size_t)info; + else /* The last 3 items are struct mwifiex_adapter variables */ + addr = d[i].addr + (size_t)priv->adapter; + + for (j = 0; j < d[i].num; j++) { + switch (size) { + case 1: + val = *((u8 *)addr); + break; + case 2: + val = *((u16 *)addr); + break; + case 4: + val = *((u32 *)addr); + break; + case 8: + val = *((long long *)addr); + break; + default: + val = -1; + break; + } + + p += sprintf(p, "%#lx ", val); + addr += size; + } + + p += sprintf(p, "\n"); + } + + if (info->tx_tbl_num) { + p += sprintf(p, "Tx BA stream table:\n"); + for (i = 0; i < info->tx_tbl_num; i++) + p += sprintf(p, "tid = %d, ra = %pM\n", + info->tx_tbl[i].tid, info->tx_tbl[i].ra); + } + + if (info->rx_tbl_num) { + p += sprintf(p, "Rx reorder table:\n"); + for (i = 0; i < info->rx_tbl_num; i++) { + p += sprintf(p, "tid = %d, ta = %pM, ", + info->rx_tbl[i].tid, + info->rx_tbl[i].ta); + p += sprintf(p, "start_win = %d, ", + info->rx_tbl[i].start_win); + p += sprintf(p, "win_size = %d, buffer: ", + info->rx_tbl[i].win_size); + + for (j = 0; j < info->rx_tbl[i].win_size; j++) + p += sprintf(p, "%c ", + info->rx_tbl[i].buffer[j] ? + '1' : '0'); + + p += sprintf(p, "\n"); + } + } + + if (info->tdls_peer_num) { + p += sprintf(p, "TDLS peer table:\n"); + for (i = 0; i < info->tdls_peer_num; i++) { + p += sprintf(p, "peer = %pM", + info->tdls_list[i].peer_addr); + p += sprintf(p, "\n"); + } + } + + return p - buf; +} + static int mwifiex_parse_mgmt_packet(struct mwifiex_private *priv, u8 *payload, u16 len, struct rxpd *rx_pd) @@ -208,7 +387,7 @@ mwifiex_process_mgmt_packet(struct mwifiex_private *priv, pkt_len -= ETH_ALEN + sizeof(pkt_len); rx_pd->rx_pkt_length = cpu_to_le16(pkt_len); - cfg80211_rx_mgmt(priv->wdev, priv->roc_cfg.chan.center_freq, + cfg80211_rx_mgmt(&priv->wdev, priv->roc_cfg.chan.center_freq, CAL_RSSI(rx_pd->snr, rx_pd->nf), skb->data, pkt_len, 0); @@ -404,3 +583,44 @@ void mwifiex_del_all_sta_list(struct mwifiex_private *priv) spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); return; } + +/* This function adds histogram data to histogram array*/ +void mwifiex_hist_data_add(struct mwifiex_private *priv, + u8 rx_rate, s8 snr, s8 nflr) +{ + struct mwifiex_histogram_data *phist_data = priv->hist_data; + + if (atomic_read(&phist_data->num_samples) > MWIFIEX_HIST_MAX_SAMPLES) + mwifiex_hist_data_reset(priv); + mwifiex_hist_data_set(priv, rx_rate, snr, nflr); +} + +/* function to add histogram record */ +void mwifiex_hist_data_set(struct mwifiex_private *priv, u8 rx_rate, s8 snr, + s8 nflr) +{ + struct mwifiex_histogram_data *phist_data = priv->hist_data; + + atomic_inc(&phist_data->num_samples); + atomic_inc(&phist_data->rx_rate[rx_rate]); + atomic_inc(&phist_data->snr[snr]); + atomic_inc(&phist_data->noise_flr[128 + nflr]); + atomic_inc(&phist_data->sig_str[nflr - snr]); +} + +/* function to reset histogram data during init/reset */ +void mwifiex_hist_data_reset(struct mwifiex_private *priv) +{ + int ix; + struct mwifiex_histogram_data *phist_data = priv->hist_data; + + atomic_set(&phist_data->num_samples, 0); + for (ix = 0; ix < MWIFIEX_MAX_AC_RX_RATES; ix++) + atomic_set(&phist_data->rx_rate[ix], 0); + for (ix = 0; ix < MWIFIEX_MAX_SNR; ix++) + atomic_set(&phist_data->snr[ix], 0); + for (ix = 0; ix < MWIFIEX_MAX_NOISE_FLR; ix++) + atomic_set(&phist_data->noise_flr[ix], 0); + for (ix = 0; ix < MWIFIEX_MAX_SIG_STRENGTH; ix++) + atomic_set(&phist_data->sig_str[ix], 0); +} diff --git a/drivers/net/wireless/mwifiex/util.h b/drivers/net/wireless/mwifiex/util.h index 40296cb4a3f1..b541d66c01eb 100644 --- a/drivers/net/wireless/mwifiex/util.h +++ b/drivers/net/wireless/mwifiex/util.h @@ -20,6 +20,8 @@ #ifndef _MWIFIEX_UTIL_H_ #define _MWIFIEX_UTIL_H_ +struct mwifiex_private; + struct mwifiex_dma_mapping { dma_addr_t addr; size_t len; @@ -33,6 +35,21 @@ struct mwifiex_cb { }; }; +/* size/addr for mwifiex_debug_info */ +#define item_size(n) (FIELD_SIZEOF(struct mwifiex_debug_info, n)) +#define item_addr(n) (offsetof(struct mwifiex_debug_info, n)) + +/* size/addr for struct mwifiex_adapter */ +#define adapter_item_size(n) (FIELD_SIZEOF(struct mwifiex_adapter, n)) +#define adapter_item_addr(n) (offsetof(struct mwifiex_adapter, n)) + +struct mwifiex_debug_data { + char name[32]; /* variable/array name */ + u32 size; /* size of the variable/array */ + size_t addr; /* address of the variable/array */ + int num; /* number of variables in an array */ +}; + static inline struct mwifiex_rxinfo *MWIFIEX_SKB_RXCB(struct sk_buff *skb) { struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; @@ -73,4 +90,7 @@ static inline dma_addr_t MWIFIEX_SKB_DMA_ADDR(struct sk_buff *skb) return mapping.addr; } +int mwifiex_debug_info_to_buffer(struct mwifiex_private *priv, char *buf, + struct mwifiex_debug_info *info); + #endif /* !_MWIFIEX_UTIL_H_ */ diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index ffffd2c5a76e..ef717acec8b7 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -1228,6 +1228,9 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv, case -EINPROGRESS: if (adapter->iface_type != MWIFIEX_PCIE) adapter->data_sent = false; + break; + case 0: + mwifiex_write_data_complete(adapter, skb, 0, ret); default: break; } diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index b8d1e04aa9b9..f9b1218c761a 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -3098,14 +3098,14 @@ static void mwl8k_update_survey(struct mwl8k_priv *priv, cca_cnt = ioread32(priv->regs + NOK_CCA_CNT_REG); cca_cnt /= 1000; /* uSecs to mSecs */ - survey->channel_time_busy = (u64) cca_cnt; + survey->time_busy = (u64) cca_cnt; rx_rdy = ioread32(priv->regs + BBU_RXRDY_CNT_REG); rx_rdy /= 1000; /* uSecs to mSecs */ - survey->channel_time_rx = (u64) rx_rdy; + survey->time_rx = (u64) rx_rdy; priv->channel_time = jiffies - priv->channel_time; - survey->channel_time = jiffies_to_msecs(priv->channel_time); + survey->time = jiffies_to_msecs(priv->channel_time); survey->channel = channel; @@ -3115,9 +3115,9 @@ static void mwl8k_update_survey(struct mwl8k_priv *priv, survey->noise = nf * -1; survey->filled = SURVEY_INFO_NOISE_DBM | - SURVEY_INFO_CHANNEL_TIME | - SURVEY_INFO_CHANNEL_TIME_BUSY | - SURVEY_INFO_CHANNEL_TIME_RX; + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_RX; } /* diff --git a/drivers/net/wireless/orinoco/Kconfig b/drivers/net/wireless/orinoco/Kconfig index 60698b020851..6d831d4d1b5f 100644 --- a/drivers/net/wireless/orinoco/Kconfig +++ b/drivers/net/wireless/orinoco/Kconfig @@ -1,7 +1,8 @@ config HERMES tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)" depends on (PPC_PMAC || PCI || PCMCIA) - depends on CFG80211 && CFG80211_WEXT + depends on CFG80211 + select CFG80211_WEXT select WIRELESS_EXT select WEXT_SPY select WEXT_PRIV diff --git a/drivers/net/wireless/orinoco/main.c b/drivers/net/wireless/orinoco/main.c index 38ec8d19ac29..c410180479e6 100644 --- a/drivers/net/wireless/orinoco/main.c +++ b/drivers/net/wireless/orinoco/main.c @@ -2342,7 +2342,7 @@ void free_orinocodev(struct orinoco_private *priv) list_for_each_entry_safe(sd, sdtemp, &priv->scan_list, list) { list_del(&sd->list); - if ((sd->len > 0) && sd->buf) + if (sd->len > 0) kfree(sd->buf); kfree(sd); } diff --git a/drivers/net/wireless/orinoco/orinoco_pci.c b/drivers/net/wireless/orinoco/orinoco_pci.c index b6bdad632842..74219d59d7e1 100644 --- a/drivers/net/wireless/orinoco/orinoco_pci.c +++ b/drivers/net/wireless/orinoco/orinoco_pci.c @@ -94,7 +94,7 @@ static int orinoco_pci_cor_reset(struct orinoco_private *priv) mdelay(HERMES_PCI_COR_OFFT); /* The card is ready when it's no longer busy */ - timeout = jiffies + (HERMES_PCI_COR_BUSYT * HZ / 1000); + timeout = jiffies + msecs_to_jiffies(HERMES_PCI_COR_BUSYT); reg = hermes_read_regn(hw, CMD); while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) { mdelay(1); diff --git a/drivers/net/wireless/orinoco/orinoco_plx.c b/drivers/net/wireless/orinoco/orinoco_plx.c index b8f6e5c431ae..8b045236b6e0 100644 --- a/drivers/net/wireless/orinoco/orinoco_plx.c +++ b/drivers/net/wireless/orinoco/orinoco_plx.c @@ -121,7 +121,7 @@ static int orinoco_plx_cor_reset(struct orinoco_private *priv) mdelay(1); /* Just in case, wait more until the card is no longer busy */ - timeout = jiffies + (PLX_RESET_TIME * HZ / 1000); + timeout = jiffies + msecs_to_jiffies(PLX_RESET_TIME); reg = hermes_read_regn(hw, CMD); while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) { mdelay(1); diff --git a/drivers/net/wireless/orinoco/orinoco_tmd.c b/drivers/net/wireless/orinoco/orinoco_tmd.c index 79d0e33b625e..20ce569b8a43 100644 --- a/drivers/net/wireless/orinoco/orinoco_tmd.c +++ b/drivers/net/wireless/orinoco/orinoco_tmd.c @@ -71,7 +71,7 @@ static int orinoco_tmd_cor_reset(struct orinoco_private *priv) mdelay(1); /* Just in case, wait more until the card is no longer busy */ - timeout = jiffies + (TMD_RESET_TIME * HZ / 1000); + timeout = jiffies + msecs_to_jiffies(TMD_RESET_TIME); reg = hermes_read_regn(hw, CMD); while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) { mdelay(1); diff --git a/drivers/net/wireless/orinoco/orinoco_usb.c b/drivers/net/wireless/orinoco/orinoco_usb.c index 995846422dc0..91f05442de28 100644 --- a/drivers/net/wireless/orinoco/orinoco_usb.c +++ b/drivers/net/wireless/orinoco/orinoco_usb.c @@ -364,9 +364,7 @@ static struct request_context *ezusb_alloc_ctx(struct ezusb_priv *upriv, atomic_set(&ctx->refcount, 1); init_completion(&ctx->done); - init_timer(&ctx->timer); - ctx->timer.function = ezusb_request_timerfn; - ctx->timer.data = (u_long) ctx; + setup_timer(&ctx->timer, ezusb_request_timerfn, (u_long)ctx); return ctx; } diff --git a/drivers/net/wireless/p54/eeprom.c b/drivers/net/wireless/p54/eeprom.c index 0fe67d2da208..2fe713eda7ad 100644 --- a/drivers/net/wireless/p54/eeprom.c +++ b/drivers/net/wireless/p54/eeprom.c @@ -196,9 +196,9 @@ static int p54_generate_band(struct ieee80211_hw *dev, dest->max_power = chan->max_power; priv->survey[*chan_num].channel = &tmp->channels[j]; priv->survey[*chan_num].filled = SURVEY_INFO_NOISE_DBM | - SURVEY_INFO_CHANNEL_TIME | - SURVEY_INFO_CHANNEL_TIME_BUSY | - SURVEY_INFO_CHANNEL_TIME_TX; + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_TX; dest->hw_value = (*chan_num); j++; (*chan_num)++; diff --git a/drivers/net/wireless/p54/fwio.c b/drivers/net/wireless/p54/fwio.c index bc065e8e348b..5367d510b22d 100644 --- a/drivers/net/wireless/p54/fwio.c +++ b/drivers/net/wireless/p54/fwio.c @@ -220,6 +220,7 @@ int p54_download_eeprom(struct p54_common *priv, void *buf, struct sk_buff *skb; size_t eeprom_hdr_size; int ret = 0; + long timeout; if (priv->fw_var >= 0x509) eeprom_hdr_size = sizeof(*eeprom_hdr); @@ -249,9 +250,11 @@ int p54_download_eeprom(struct p54_common *priv, void *buf, p54_tx(priv, skb); - if (!wait_for_completion_interruptible_timeout( - &priv->eeprom_comp, HZ)) { - wiphy_err(priv->hw->wiphy, "device does not respond!\n"); + timeout = wait_for_completion_interruptible_timeout( + &priv->eeprom_comp, HZ); + if (timeout <= 0) { + wiphy_err(priv->hw->wiphy, + "device does not respond or signal received!\n"); ret = -EBUSY; } priv->eeprom = NULL; diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index 97aeff0edb84..b9250d75d253 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -305,9 +305,9 @@ static void p54_reset_stats(struct p54_common *priv) struct survey_info *info = &priv->survey[chan->hw_value]; /* only reset channel statistics, don't touch .filled, etc. */ - info->channel_time = 0; - info->channel_time_busy = 0; - info->channel_time_tx = 0; + info->time = 0; + info->time_busy = 0; + info->time_tx = 0; } priv->update_stats = true; @@ -575,6 +575,8 @@ static int p54_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, key->hw_key_idx = 0xff; goto out_unlock; } + + key->flags |= IEEE80211_KEY_FLAG_RESERVE_TAILROOM; } else { slot = key->hw_key_idx; @@ -634,7 +636,7 @@ static int p54_get_survey(struct ieee80211_hw *dev, int idx, if (in_use) { /* test if the reported statistics are valid. */ - if (survey->channel_time != 0) { + if (survey->time != 0) { survey->filled |= SURVEY_INFO_IN_USE; } else { /* diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c index d4aee64fb5ea..27a49068d32d 100644 --- a/drivers/net/wireless/p54/p54pci.c +++ b/drivers/net/wireless/p54/p54pci.c @@ -431,6 +431,7 @@ static int p54p_open(struct ieee80211_hw *dev) { struct p54p_priv *priv = dev->priv; int err; + long timeout; init_completion(&priv->boot_comp); err = request_irq(priv->pdev->irq, p54p_interrupt, @@ -468,10 +469,12 @@ static int p54p_open(struct ieee80211_hw *dev) P54P_WRITE(dev_int, cpu_to_le32(ISL38XX_DEV_INT_RESET)); P54P_READ(dev_int); - if (!wait_for_completion_interruptible_timeout(&priv->boot_comp, HZ)) { + timeout = wait_for_completion_interruptible_timeout( + &priv->boot_comp, HZ); + if (timeout <= 0) { wiphy_err(dev->wiphy, "Cannot boot firmware!\n"); p54p_stop(dev); - return -ETIMEDOUT; + return timeout ? -ERESTARTSYS : -ETIMEDOUT; } P54P_WRITE(int_enable, cpu_to_le32(ISL38XX_INT_IDENT_UPDATE)); diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index 153c61539ec8..24e5ff9a9272 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -587,13 +587,13 @@ static void p54_rx_stats(struct p54_common *priv, struct sk_buff *skb) if (chan) { struct survey_info *survey = &priv->survey[chan->hw_value]; survey->noise = clamp(priv->noise, -128, 127); - survey->channel_time = priv->survey_raw.active; - survey->channel_time_tx = priv->survey_raw.tx; - survey->channel_time_busy = priv->survey_raw.tx + + survey->time = priv->survey_raw.active; + survey->time_tx = priv->survey_raw.tx; + survey->time_busy = priv->survey_raw.tx + priv->survey_raw.cca; - do_div(survey->channel_time, 1024); - do_div(survey->channel_time_tx, 1024); - do_div(survey->channel_time_busy, 1024); + do_div(survey->time, 1024); + do_div(survey->time_tx, 1024); + do_div(survey->time_busy, 1024); } tmp = p54_find_and_unlink_skb(priv, hdr->req_id); diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 1a4facd1fbf3..60d44ce9c017 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -2478,7 +2478,7 @@ static void rndis_fill_station_info(struct usbnet *usbdev, ret = rndis_query_oid(usbdev, RNDIS_OID_GEN_LINK_SPEED, &linkspeed, &len); if (ret == 0) { sinfo->txrate.legacy = le32_to_cpu(linkspeed) / 1000; - sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); } len = sizeof(rssi); @@ -2486,7 +2486,7 @@ static void rndis_fill_station_info(struct usbnet *usbdev, &rssi, &len); if (ret == 0) { sinfo->signal = level_to_qual(le32_to_cpu(rssi)); - sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); } } diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c index 4834a9abc171..b6cc9ff47fc2 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c @@ -172,7 +172,6 @@ static int rsi_load_ta_instructions(struct rsi_common *common) (struct rsi_91x_sdiodev *)adapter->rsi_dev; u32 len; u32 num_blocks; - const u8 *fw; const struct firmware *fw_entry = NULL; u32 block_size = dev->tx_blk_size; int status = 0; @@ -201,7 +200,6 @@ static int rsi_load_ta_instructions(struct rsi_common *common) return status; } - fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); len = fw_entry->size; if (len % 4) @@ -212,7 +210,7 @@ static int rsi_load_ta_instructions(struct rsi_common *common) rsi_dbg(INIT_ZONE, "%s: Instruction size:%d\n", __func__, len); rsi_dbg(INIT_ZONE, "%s: num blocks: %d\n", __func__, num_blocks); - status = rsi_copy_to_card(common, fw, len, num_blocks); + status = rsi_copy_to_card(common, fw_entry->data, len, num_blocks); release_firmware(fw_entry); return status; } diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 81ee481487cf..be2d54f257b1 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -8020,13 +8020,13 @@ int rt2800_get_survey(struct ieee80211_hw *hw, int idx, rt2800_register_read(rt2x00dev, CH_BUSY_STA_SEC, &busy_ext); if (idle || busy) { - survey->filled = SURVEY_INFO_CHANNEL_TIME | - SURVEY_INFO_CHANNEL_TIME_BUSY | - SURVEY_INFO_CHANNEL_TIME_EXT_BUSY; + survey->filled = SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY | + SURVEY_INFO_TIME_EXT_BUSY; - survey->channel_time = (idle + busy) / 1000; - survey->channel_time_busy = busy / 1000; - survey->channel_time_ext_busy = busy_ext / 1000; + survey->time = (idle + busy) / 1000; + survey->time_busy = busy / 1000; + survey->time_ext_busy = busy_ext / 1000; } if (!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) diff --git a/drivers/net/wireless/rt2x00/rt2x00config.c b/drivers/net/wireless/rt2x00/rt2x00config.c index 1122dc44c9fd..48a2cad29477 100644 --- a/drivers/net/wireless/rt2x00/rt2x00config.c +++ b/drivers/net/wireless/rt2x00/rt2x00config.c @@ -240,7 +240,7 @@ void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, rt2x00dev->rf_channel = libconf.rf.channel; } - if (test_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags) && + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_PS_AUTOWAKE) && (ieee80211_flags & IEEE80211_CONF_CHANGE_PS)) cancel_delayed_work_sync(&rt2x00dev->autowakeup_work); @@ -257,7 +257,7 @@ void rt2x00lib_config(struct rt2x00_dev *rt2x00dev, rt2x00link_reset_tuner(rt2x00dev, false); if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && - test_bit(REQUIRE_PS_AUTOWAKE, &rt2x00dev->cap_flags) && + rt2x00_has_cap_flag(rt2x00dev, REQUIRE_PS_AUTOWAKE) && (ieee80211_flags & IEEE80211_CONF_CHANGE_PS) && (conf->flags & IEEE80211_CONF_PS)) { beacon_diff = (long)jiffies - (long)rt2x00dev->last_beacon; diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 9967a1d9f0ec..5639ed816813 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -351,7 +351,7 @@ void rt2x00lib_txdone(struct queue_entry *entry, /* * Remove L2 padding which was added during */ - if (test_bit(REQUIRE_L2PAD, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_L2PAD)) rt2x00queue_remove_l2pad(entry->skb, header_length); /* @@ -460,7 +460,7 @@ void rt2x00lib_txdone(struct queue_entry *entry, * send the status report back. */ if (!(skbdesc_flags & SKBDESC_NOT_MAC80211)) { - if (test_bit(REQUIRE_TASKLET_CONTEXT, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_TASKLET_CONTEXT)) ieee80211_tx_status(rt2x00dev->hw, entry->skb); else ieee80211_tx_status_ni(rt2x00dev->hw, entry->skb); @@ -1056,9 +1056,9 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) /* * Take TX headroom required for alignment into account. */ - if (test_bit(REQUIRE_L2PAD, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_L2PAD)) rt2x00dev->hw->extra_tx_headroom += RT2X00_L2PAD_SIZE; - else if (test_bit(REQUIRE_DMA, &rt2x00dev->cap_flags)) + else if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DMA)) rt2x00dev->hw->extra_tx_headroom += RT2X00_ALIGN_SIZE; /* @@ -1069,7 +1069,7 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) /* * Allocate tx status FIFO for driver use. */ - if (test_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_TXSTATUS_FIFO)) { /* * Allocate the txstatus fifo. In the worst case the tx * status fifo has to hold the tx status of all entries @@ -1131,7 +1131,7 @@ static void rt2x00lib_uninitialize(struct rt2x00_dev *rt2x00dev) /* * Stop rfkill polling. */ - if (test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL)) rt2x00rfkill_unregister(rt2x00dev); /* @@ -1173,7 +1173,7 @@ static int rt2x00lib_initialize(struct rt2x00_dev *rt2x00dev) /* * Start rfkill polling. */ - if (test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL)) rt2x00rfkill_register(rt2x00dev); return 0; @@ -1389,7 +1389,7 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) /* * Start rfkill polling. */ - if (!test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags)) + if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL)) rt2x00rfkill_register(rt2x00dev); return 0; @@ -1408,7 +1408,7 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) /* * Stop rfkill polling. */ - if (!test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags)) + if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DELAYED_RFKILL)) rt2x00rfkill_unregister(rt2x00dev); /* diff --git a/drivers/net/wireless/rt2x00/rt2x00firmware.c b/drivers/net/wireless/rt2x00/rt2x00firmware.c index fbae2799e3ee..5813300f68a2 100644 --- a/drivers/net/wireless/rt2x00/rt2x00firmware.c +++ b/drivers/net/wireless/rt2x00/rt2x00firmware.c @@ -96,7 +96,7 @@ int rt2x00lib_load_firmware(struct rt2x00_dev *rt2x00dev) { int retval; - if (!test_bit(REQUIRE_FIRMWARE, &rt2x00dev->cap_flags)) + if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_FIRMWARE)) return 0; if (!rt2x00dev->fw) { diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index cb40245a0695..300876df056f 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -119,7 +119,7 @@ void rt2x00mac_tx(struct ieee80211_hw *hw, * Use the ATIM queue if appropriate and present. */ if (tx_info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM && - test_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags)) + rt2x00_has_cap_flag(rt2x00dev, REQUIRE_ATIM_QUEUE)) qid = QID_ATIM; queue = rt2x00queue_get_tx_queue(rt2x00dev, qid); diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 66ff36447b94..68b620b2462f 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -85,7 +85,7 @@ struct sk_buff *rt2x00queue_alloc_rxskb(struct queue_entry *entry, gfp_t gfp) memset(skbdesc, 0, sizeof(*skbdesc)); skbdesc->entry = entry; - if (test_bit(REQUIRE_DMA, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DMA)) { dma_addr_t skb_dma; skb_dma = dma_map_single(rt2x00dev->dev, skb->data, skb->len, @@ -198,7 +198,7 @@ static void rt2x00queue_create_tx_descriptor_seq(struct rt2x00_dev *rt2x00dev, __set_bit(ENTRY_TXD_GENERATE_SEQ, &txdesc->flags); - if (!test_bit(REQUIRE_SW_SEQNO, &rt2x00dev->cap_flags)) { + if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_SW_SEQNO)) { /* * rt2800 has a H/W (or F/W) bug, device incorrectly increase * seqno on retransmited data (non-QOS) frames. To workaround @@ -484,7 +484,7 @@ static void rt2x00queue_create_tx_descriptor(struct rt2x00_dev *rt2x00dev, rt2x00crypto_create_tx_descriptor(rt2x00dev, skb, txdesc); rt2x00queue_create_tx_descriptor_seq(rt2x00dev, skb, txdesc); - if (test_bit(REQUIRE_HT_TX_DESC, &rt2x00dev->cap_flags)) + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_HT_TX_DESC)) rt2x00queue_create_tx_descriptor_ht(rt2x00dev, skb, txdesc, sta, hwrate); else @@ -526,7 +526,7 @@ static int rt2x00queue_write_tx_data(struct queue_entry *entry, /* * Map the skb to DMA. */ - if (test_bit(REQUIRE_DMA, &rt2x00dev->cap_flags) && + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_DMA) && rt2x00queue_map_txskb(entry)) return -ENOMEM; @@ -646,7 +646,7 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, */ if (test_bit(ENTRY_TXD_ENCRYPT, &txdesc.flags) && !test_bit(ENTRY_TXD_ENCRYPT_IV, &txdesc.flags)) { - if (test_bit(REQUIRE_COPY_IV, &queue->rt2x00dev->cap_flags)) + if (rt2x00_has_cap_flag(queue->rt2x00dev, REQUIRE_COPY_IV)) rt2x00crypto_tx_copy_iv(skb, &txdesc); else rt2x00crypto_tx_remove_iv(skb, &txdesc); @@ -660,9 +660,9 @@ int rt2x00queue_write_tx_frame(struct data_queue *queue, struct sk_buff *skb, * PCI and USB devices, while header alignment only is valid * for PCI devices. */ - if (test_bit(REQUIRE_L2PAD, &queue->rt2x00dev->cap_flags)) + if (rt2x00_has_cap_flag(queue->rt2x00dev, REQUIRE_L2PAD)) rt2x00queue_insert_l2pad(skb, txdesc.header_length); - else if (test_bit(REQUIRE_DMA, &queue->rt2x00dev->cap_flags)) + else if (rt2x00_has_cap_flag(queue->rt2x00dev, REQUIRE_DMA)) rt2x00queue_align_frame(skb); /* @@ -1178,7 +1178,7 @@ int rt2x00queue_initialize(struct rt2x00_dev *rt2x00dev) if (status) goto exit; - if (test_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags)) { + if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_ATIM_QUEUE)) { status = rt2x00queue_alloc_entries(rt2x00dev->atim); if (status) goto exit; @@ -1234,7 +1234,7 @@ int rt2x00queue_allocate(struct rt2x00_dev *rt2x00dev) struct data_queue *queue; enum data_queue_qid qid; unsigned int req_atim = - !!test_bit(REQUIRE_ATIM_QUEUE, &rt2x00dev->cap_flags); + rt2x00_has_cap_flag(rt2x00dev, REQUIRE_ATIM_QUEUE); /* * We need the following queues: diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c index 892270dd3e7b..7627af6098eb 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.c +++ b/drivers/net/wireless/rt2x00/rt2x00usb.c @@ -274,7 +274,7 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb) * Schedule the delayed work for reading the TX status * from the device. */ - if (!test_bit(REQUIRE_TXSTATUS_FIFO, &rt2x00dev->cap_flags) || + if (!rt2x00_has_cap_flag(rt2x00dev, REQUIRE_TXSTATUS_FIFO) || !kfifo_is_empty(&rt2x00dev->txstatus_fifo)) queue_work(rt2x00dev->workqueue, &rt2x00dev->txdone_work); } @@ -456,7 +456,7 @@ static bool rt2x00usb_flush_entry(struct queue_entry *entry, void *data) * Kill guardian urb (if required by driver). */ if ((entry->queue->qid == QID_BEACON) && - (test_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags))) + (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_BEACON_GUARD))) usb_kill_urb(bcn_priv->guardian_urb); return false; @@ -655,7 +655,7 @@ static int rt2x00usb_alloc_entries(struct data_queue *queue) * then we are done. */ if (queue->qid != QID_BEACON || - !test_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags)) + !rt2x00_has_cap_flag(rt2x00dev, REQUIRE_BEACON_GUARD)) return 0; for (i = 0; i < queue->limit; i++) { @@ -690,7 +690,7 @@ static void rt2x00usb_free_entries(struct data_queue *queue) * then we are done. */ if (queue->qid != QID_BEACON || - !test_bit(REQUIRE_BEACON_GUARD, &rt2x00dev->cap_flags)) + !rt2x00_has_cap_flag(rt2x00dev, REQUIRE_BEACON_GUARD)) return; for (i = 0; i < queue->limit; i++) { diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index 40b6d1d006d7..1d4677460711 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -867,63 +867,135 @@ static u8 _rtl_get_highest_n_rate(struct ieee80211_hw *hw, * * B/G rate: * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC92_RATE1M-->DESC92_RATE54M ==> idx is 0-->11, + * DESC_RATE1M-->DESC_RATE54M ==> idx is 0-->11, * * N rate: * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC92_RATEMCS0-->DESC92_RATEMCS15 ==> idx is 0-->15 + * DESC_RATEMCS0-->DESC_RATEMCS15 ==> idx is 0-->15 * * 5G band:rx_status->band == IEEE80211_BAND_5GHZ * A rate: * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC92_RATE6M-->DESC92_RATE54M ==> idx is 0-->7, + * DESC_RATE6M-->DESC_RATE54M ==> idx is 0-->7, * * N rate: * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC92_RATEMCS0-->DESC92_RATEMCS15 ==> idx is 0-->15 + * DESC_RATEMCS0-->DESC_RATEMCS15 ==> idx is 0-->15 + * + * VHT rates: + * DESC_RATEVHT1SS_MCS0-->DESC_RATEVHT1SS_MCS9 ==> idx is 0-->9 + * DESC_RATEVHT2SS_MCS0-->DESC_RATEVHT2SS_MCS9 ==> idx is 0-->9 */ -int rtlwifi_rate_mapping(struct ieee80211_hw *hw, - bool isht, u8 desc_rate, bool first_ampdu) +int rtlwifi_rate_mapping(struct ieee80211_hw *hw, bool isht, bool isvht, + u8 desc_rate) { int rate_idx; + if (isvht) { + switch (desc_rate) { + case DESC_RATEVHT1SS_MCS0: + rate_idx = 0; + break; + case DESC_RATEVHT1SS_MCS1: + rate_idx = 1; + break; + case DESC_RATEVHT1SS_MCS2: + rate_idx = 2; + break; + case DESC_RATEVHT1SS_MCS3: + rate_idx = 3; + break; + case DESC_RATEVHT1SS_MCS4: + rate_idx = 4; + break; + case DESC_RATEVHT1SS_MCS5: + rate_idx = 5; + break; + case DESC_RATEVHT1SS_MCS6: + rate_idx = 6; + break; + case DESC_RATEVHT1SS_MCS7: + rate_idx = 7; + break; + case DESC_RATEVHT1SS_MCS8: + rate_idx = 8; + break; + case DESC_RATEVHT1SS_MCS9: + rate_idx = 9; + break; + case DESC_RATEVHT2SS_MCS0: + rate_idx = 0; + break; + case DESC_RATEVHT2SS_MCS1: + rate_idx = 1; + break; + case DESC_RATEVHT2SS_MCS2: + rate_idx = 2; + break; + case DESC_RATEVHT2SS_MCS3: + rate_idx = 3; + break; + case DESC_RATEVHT2SS_MCS4: + rate_idx = 4; + break; + case DESC_RATEVHT2SS_MCS5: + rate_idx = 5; + break; + case DESC_RATEVHT2SS_MCS6: + rate_idx = 6; + break; + case DESC_RATEVHT2SS_MCS7: + rate_idx = 7; + break; + case DESC_RATEVHT2SS_MCS8: + rate_idx = 8; + break; + case DESC_RATEVHT2SS_MCS9: + rate_idx = 9; + break; + default: + rate_idx = 0; + break; + } + return rate_idx; + } if (false == isht) { if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) { switch (desc_rate) { - case DESC92_RATE1M: + case DESC_RATE1M: rate_idx = 0; break; - case DESC92_RATE2M: + case DESC_RATE2M: rate_idx = 1; break; - case DESC92_RATE5_5M: + case DESC_RATE5_5M: rate_idx = 2; break; - case DESC92_RATE11M: + case DESC_RATE11M: rate_idx = 3; break; - case DESC92_RATE6M: + case DESC_RATE6M: rate_idx = 4; break; - case DESC92_RATE9M: + case DESC_RATE9M: rate_idx = 5; break; - case DESC92_RATE12M: + case DESC_RATE12M: rate_idx = 6; break; - case DESC92_RATE18M: + case DESC_RATE18M: rate_idx = 7; break; - case DESC92_RATE24M: + case DESC_RATE24M: rate_idx = 8; break; - case DESC92_RATE36M: + case DESC_RATE36M: rate_idx = 9; break; - case DESC92_RATE48M: + case DESC_RATE48M: rate_idx = 10; break; - case DESC92_RATE54M: + case DESC_RATE54M: rate_idx = 11; break; default: @@ -932,28 +1004,28 @@ int rtlwifi_rate_mapping(struct ieee80211_hw *hw, } } else { switch (desc_rate) { - case DESC92_RATE6M: + case DESC_RATE6M: rate_idx = 0; break; - case DESC92_RATE9M: + case DESC_RATE9M: rate_idx = 1; break; - case DESC92_RATE12M: + case DESC_RATE12M: rate_idx = 2; break; - case DESC92_RATE18M: + case DESC_RATE18M: rate_idx = 3; break; - case DESC92_RATE24M: + case DESC_RATE24M: rate_idx = 4; break; - case DESC92_RATE36M: + case DESC_RATE36M: rate_idx = 5; break; - case DESC92_RATE48M: + case DESC_RATE48M: rate_idx = 6; break; - case DESC92_RATE54M: + case DESC_RATE54M: rate_idx = 7; break; default: @@ -963,52 +1035,52 @@ int rtlwifi_rate_mapping(struct ieee80211_hw *hw, } } else { switch (desc_rate) { - case DESC92_RATEMCS0: + case DESC_RATEMCS0: rate_idx = 0; break; - case DESC92_RATEMCS1: + case DESC_RATEMCS1: rate_idx = 1; break; - case DESC92_RATEMCS2: + case DESC_RATEMCS2: rate_idx = 2; break; - case DESC92_RATEMCS3: + case DESC_RATEMCS3: rate_idx = 3; break; - case DESC92_RATEMCS4: + case DESC_RATEMCS4: rate_idx = 4; break; - case DESC92_RATEMCS5: + case DESC_RATEMCS5: rate_idx = 5; break; - case DESC92_RATEMCS6: + case DESC_RATEMCS6: rate_idx = 6; break; - case DESC92_RATEMCS7: + case DESC_RATEMCS7: rate_idx = 7; break; - case DESC92_RATEMCS8: + case DESC_RATEMCS8: rate_idx = 8; break; - case DESC92_RATEMCS9: + case DESC_RATEMCS9: rate_idx = 9; break; - case DESC92_RATEMCS10: + case DESC_RATEMCS10: rate_idx = 10; break; - case DESC92_RATEMCS11: + case DESC_RATEMCS11: rate_idx = 11; break; - case DESC92_RATEMCS12: + case DESC_RATEMCS12: rate_idx = 12; break; - case DESC92_RATEMCS13: + case DESC_RATEMCS13: rate_idx = 13; break; - case DESC92_RATEMCS14: + case DESC_RATEMCS14: rate_idx = 14; break; - case DESC92_RATEMCS15: + case DESC_RATEMCS15: rate_idx = 15; break; default: diff --git a/drivers/net/wireless/rtlwifi/base.h b/drivers/net/wireless/rtlwifi/base.h index 982f2450feea..c6cb49c3ee32 100644 --- a/drivers/net/wireless/rtlwifi/base.h +++ b/drivers/net/wireless/rtlwifi/base.h @@ -123,8 +123,8 @@ void rtl_watch_dog_timer_callback(unsigned long data); void rtl_deinit_deferred_work(struct ieee80211_hw *hw); bool rtl_action_proc(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx); -int rtlwifi_rate_mapping(struct ieee80211_hw *hw, - bool isht, u8 desc_rate, bool first_ampdu); +int rtlwifi_rate_mapping(struct ieee80211_hw *hw, bool isht, + bool isvht, u8 desc_rate); bool rtl_tx_mgmt_proc(struct ieee80211_hw *hw, struct sk_buff *skb); u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx); diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index 5fc6f52641bd..a31a12775f1a 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -95,7 +95,8 @@ void rtl_bb_delay(struct ieee80211_hw *hw, u32 addr, u32 data) } EXPORT_SYMBOL(rtl_bb_delay); -void rtl_fw_cb(const struct firmware *firmware, void *context) +static void rtl_fw_do_work(const struct firmware *firmware, void *context, + bool is_wow) { struct ieee80211_hw *hw = context; struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -125,12 +126,31 @@ found_alt: release_firmware(firmware); return; } - memcpy(rtlpriv->rtlhal.pfirmware, firmware->data, firmware->size); + if (!is_wow) { + memcpy(rtlpriv->rtlhal.pfirmware, firmware->data, + firmware->size); + rtlpriv->rtlhal.fwsize = firmware->size; + } else { + memcpy(rtlpriv->rtlhal.wowlan_firmware, firmware->data, + firmware->size); + rtlpriv->rtlhal.wowlan_fwsize = firmware->size; + } rtlpriv->rtlhal.fwsize = firmware->size; release_firmware(firmware); } + +void rtl_fw_cb(const struct firmware *firmware, void *context) +{ + rtl_fw_do_work(firmware, context, false); +} EXPORT_SYMBOL(rtl_fw_cb); +void rtl_wowlan_fw_cb(const struct firmware *firmware, void *context) +{ + rtl_fw_do_work(firmware, context, true); +} +EXPORT_SYMBOL(rtl_wowlan_fw_cb); + /*mutex for start & stop is must here. */ static int rtl_op_start(struct ieee80211_hw *hw) { @@ -990,6 +1010,16 @@ static int rtl_op_conf_tx(struct ieee80211_hw *hw, return 0; } +static void send_beacon_frame(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct sk_buff *skb = ieee80211_beacon_get(hw, vif); + + if (skb) + rtlpriv->intf_ops->adapter_tx(hw, NULL, skb, NULL); +} + static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, @@ -1020,6 +1050,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, if (rtlpriv->cfg->ops->linked_set_reg) rtlpriv->cfg->ops->linked_set_reg(hw); + send_beacon_frame(hw, vif); } } if ((changed & BSS_CHANGED_BEACON_ENABLED && @@ -1851,3 +1882,40 @@ bool rtl_btc_status_false(void) return false; } EXPORT_SYMBOL_GPL(rtl_btc_status_false); + +void rtl_dm_diginit(struct ieee80211_hw *hw, u32 cur_igvalue) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct dig_t *dm_digtable = &rtlpriv->dm_digtable; + + dm_digtable->dig_enable_flag = true; + dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX; + dm_digtable->cur_igvalue = cur_igvalue; + dm_digtable->pre_igvalue = 0; + dm_digtable->cur_sta_cstate = DIG_STA_DISCONNECT; + dm_digtable->presta_cstate = DIG_STA_DISCONNECT; + dm_digtable->curmultista_cstate = DIG_MULTISTA_DISCONNECT; + dm_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW; + dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH; + dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; + dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; + dm_digtable->rx_gain_max = DM_DIG_MAX; + dm_digtable->rx_gain_min = DM_DIG_MIN; + dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT; + dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX; + dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN; + dm_digtable->pre_cck_cca_thres = 0xff; + dm_digtable->cur_cck_cca_thres = 0x83; + dm_digtable->forbidden_igi = DM_DIG_MIN; + dm_digtable->large_fa_hit = 0; + dm_digtable->recover_cnt = 0; + dm_digtable->dig_min_0 = 0x25; + dm_digtable->dig_min_1 = 0x25; + dm_digtable->media_connect_0 = false; + dm_digtable->media_connect_1 = false; + rtlpriv->dm.dm_initialgain_enable = true; + dm_digtable->bt30_cur_igi = 0x32; + dm_digtable->pre_cck_pd_state = CCK_PD_STAGE_MAX; + dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_LOWRSSI; +} +EXPORT_SYMBOL(rtl_dm_diginit); diff --git a/drivers/net/wireless/rtlwifi/core.h b/drivers/net/wireless/rtlwifi/core.h index 624e1dc16d31..7b64e34f421e 100644 --- a/drivers/net/wireless/rtlwifi/core.h +++ b/drivers/net/wireless/rtlwifi/core.h @@ -35,13 +35,55 @@ #define RTL_SUPPORTED_CTRL_FILTER 0xFF +#define DM_DIG_THRESH_HIGH 40 +#define DM_DIG_THRESH_LOW 35 +#define DM_FALSEALARM_THRESH_LOW 400 +#define DM_FALSEALARM_THRESH_HIGH 1000 + +#define DM_DIG_MAX 0x3e +#define DM_DIG_MIN 0x1e +#define DM_DIG_MAX_AP 0x32 +#define DM_DIG_BACKOFF_MAX 12 +#define DM_DIG_BACKOFF_MIN -4 +#define DM_DIG_BACKOFF_DEFAULT 10 + +enum cck_packet_detection_threshold { + CCK_PD_STAGE_LOWRSSI = 0, + CCK_PD_STAGE_HIGHRSSI = 1, + CCK_FA_STAGE_LOW = 2, + CCK_FA_STAGE_HIGH = 3, + CCK_PD_STAGE_MAX = 4, +}; + +enum dm_dig_ext_port_alg_e { + DIG_EXT_PORT_STAGE_0 = 0, + DIG_EXT_PORT_STAGE_1 = 1, + DIG_EXT_PORT_STAGE_2 = 2, + DIG_EXT_PORT_STAGE_3 = 3, + DIG_EXT_PORT_STAGE_MAX = 4, +}; + +enum dm_dig_connect_e { + DIG_STA_DISCONNECT, + DIG_STA_CONNECT, + DIG_STA_BEFORE_CONNECT, + DIG_MULTISTA_DISCONNECT, + DIG_MULTISTA_CONNECT, + DIG_AP_DISCONNECT, + DIG_AP_CONNECT, + DIG_AP_ADD_STATION, + DIG_CONNECT_MAX +}; + extern const struct ieee80211_ops rtl_ops; void rtl_fw_cb(const struct firmware *firmware, void *context); +void rtl_wowlan_fw_cb(const struct firmware *firmware, void *context); void rtl_addr_delay(u32 addr); void rtl_rfreg_delay(struct ieee80211_hw *hw, enum radio_path rfpath, u32 addr, u32 mask, u32 data); void rtl_bb_delay(struct ieee80211_hw *hw, u32 addr, u32 data); bool rtl_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb); bool rtl_btc_status_false(void); +void rtl_dm_diginit(struct ieee80211_hw *hw, u32 cur_igval); #endif diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index 846a2e6e34d8..ec456f0d972e 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -578,6 +578,13 @@ static void _rtl_pci_tx_isr(struct ieee80211_hw *hw, int prio) else entry = (u8 *)(&ring->desc[ring->idx]); + if (rtlpriv->cfg->ops->get_available_desc && + rtlpriv->cfg->ops->get_available_desc(hw, prio) <= 1) { + RT_TRACE(rtlpriv, (COMP_INTR | COMP_SEND), DBG_DMESG, + "no available desc!\n"); + return; + } + if (!rtlpriv->cfg->ops->is_tx_desc_closed(hw, prio, ring->idx)) return; ring->idx = (ring->idx + 1) % ring->entries; @@ -641,10 +648,9 @@ static void _rtl_pci_tx_isr(struct ieee80211_hw *hw, int prio) ieee80211_tx_status_irqsafe(hw, skb); - if ((ring->entries - skb_queue_len(&ring->queue)) - == 2) { + if ((ring->entries - skb_queue_len(&ring->queue)) <= 4) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_LOUD, + RT_TRACE(rtlpriv, COMP_ERR, DBG_DMESG, "more desc left, wake skb_queue@%d, ring->idx = %d, skb_queue_len = 0x%x\n", prio, ring->idx, skb_queue_len(&ring->queue)); @@ -666,7 +672,8 @@ tx_status_ok: } static int _rtl_pci_init_one_rxdesc(struct ieee80211_hw *hw, - u8 *entry, int rxring_idx, int desc_idx) + struct sk_buff *new_skb, u8 *entry, + int rxring_idx, int desc_idx) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); @@ -674,11 +681,15 @@ static int _rtl_pci_init_one_rxdesc(struct ieee80211_hw *hw, u8 tmp_one = 1; struct sk_buff *skb; + if (likely(new_skb)) { + skb = new_skb; + goto remap; + } skb = dev_alloc_skb(rtlpci->rxbuffersize); if (!skb) return 0; - rtlpci->rx_ring[rxring_idx].rx_buf[desc_idx] = skb; +remap: /* just set skb->cb to mapping addr for pci_unmap_single use */ *((dma_addr_t *)skb->cb) = pci_map_single(rtlpci->pdev, skb_tail_pointer(skb), @@ -686,6 +697,7 @@ static int _rtl_pci_init_one_rxdesc(struct ieee80211_hw *hw, bufferaddress = *((dma_addr_t *)skb->cb); if (pci_dma_mapping_error(rtlpci->pdev, bufferaddress)) return 0; + rtlpci->rx_ring[rxring_idx].rx_buf[desc_idx] = skb; if (rtlpriv->use_new_trx_flow) { rtlpriv->cfg->ops->set_desc(hw, (u8 *)entry, false, HW_DESC_RX_PREPARE, @@ -781,12 +793,13 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw) /*rx pkt */ struct sk_buff *skb = rtlpci->rx_ring[rxring_idx].rx_buf[ rtlpci->rx_ring[rxring_idx].idx]; + struct sk_buff *new_skb; if (rtlpriv->use_new_trx_flow) { rx_remained_cnt = rtlpriv->cfg->ops->rx_desc_buff_remained_cnt(hw, hw_queue); - if (rx_remained_cnt < 1) + if (rx_remained_cnt == 0) return; } else { /* rx descriptor */ @@ -807,6 +820,13 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw) pci_unmap_single(rtlpci->pdev, *((dma_addr_t *)skb->cb), rtlpci->rxbuffersize, PCI_DMA_FROMDEVICE); + /* get a new skb - if fail, old one will be reused */ + new_skb = dev_alloc_skb(rtlpci->rxbuffersize); + if (unlikely(!new_skb)) { + pr_err("Allocation of new skb failed in %s\n", + __func__); + goto no_new; + } if (rtlpriv->use_new_trx_flow) { buffer_desc = &rtlpci->rx_ring[rxring_idx].buffer_desc @@ -834,18 +854,18 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw) else skb_reserve(skb, stats.rx_drvinfo_size + stats.rx_bufshift); - } else { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, "skb->end - skb->tail = %d, len is %d\n", skb->end - skb->tail, len); - break; + dev_kfree_skb_any(skb); + goto new_trx_end; } /* handle command packet here */ if (rtlpriv->cfg->ops->rx_command_packet && rtlpriv->cfg->ops->rx_command_packet(hw, stats, skb)) { dev_kfree_skb_any(skb); - goto end; + goto new_trx_end; } /* @@ -895,6 +915,7 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw) } else { dev_kfree_skb_any(skb); } +new_trx_end: if (rtlpriv->use_new_trx_flow) { rtlpci->rx_ring[hw_queue].next_rx_rp += 1; rtlpci->rx_ring[hw_queue].next_rx_rp %= @@ -910,15 +931,16 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw) rtlpriv->enter_ps = false; schedule_work(&rtlpriv->works.lps_change_work); } -end: + skb = new_skb; +no_new: if (rtlpriv->use_new_trx_flow) { - _rtl_pci_init_one_rxdesc(hw, (u8 *)buffer_desc, + _rtl_pci_init_one_rxdesc(hw, skb, (u8 *)buffer_desc, rxring_idx, - rtlpci->rx_ring[rxring_idx].idx); + rtlpci->rx_ring[rxring_idx].idx); } else { - _rtl_pci_init_one_rxdesc(hw, (u8 *)pdesc, rxring_idx, + _rtl_pci_init_one_rxdesc(hw, skb, (u8 *)pdesc, + rxring_idx, rtlpci->rx_ring[rxring_idx].idx); - if (rtlpci->rx_ring[rxring_idx].idx == rtlpci->rxringcount - 1) rtlpriv->cfg->ops->set_desc(hw, (u8 *)pdesc, @@ -1307,7 +1329,7 @@ static int _rtl_pci_init_rx_ring(struct ieee80211_hw *hw, int rxring_idx) rtlpci->rx_ring[rxring_idx].idx = 0; for (i = 0; i < rtlpci->rxringcount; i++) { entry = &rtlpci->rx_ring[rxring_idx].buffer_desc[i]; - if (!_rtl_pci_init_one_rxdesc(hw, (u8 *)entry, + if (!_rtl_pci_init_one_rxdesc(hw, NULL, (u8 *)entry, rxring_idx, i)) return -ENOMEM; } @@ -1332,7 +1354,7 @@ static int _rtl_pci_init_rx_ring(struct ieee80211_hw *hw, int rxring_idx) for (i = 0; i < rtlpci->rxringcount; i++) { entry = &rtlpci->rx_ring[rxring_idx].desc[i]; - if (!_rtl_pci_init_one_rxdesc(hw, (u8 *)entry, + if (!_rtl_pci_init_one_rxdesc(hw, NULL, (u8 *)entry, rxring_idx, i)) return -ENOMEM; } @@ -1672,6 +1694,15 @@ static int rtl_pci_tx(struct ieee80211_hw *hw, } } + if (rtlpriv->cfg->ops->get_available_desc && + rtlpriv->cfg->ops->get_available_desc(hw, hw_queue) == 0) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "get_available_desc fail\n"); + spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, + flags); + return skb->len; + } + if (ieee80211_is_data_qos(fc)) { tid = rtl_get_tid(skb); if (sta) { diff --git a/drivers/net/wireless/rtlwifi/pci.h b/drivers/net/wireless/rtlwifi/pci.h index 5e832306dba9..d4567d12e07e 100644 --- a/drivers/net/wireless/rtlwifi/pci.h +++ b/drivers/net/wireless/rtlwifi/pci.h @@ -325,4 +325,11 @@ static inline void pci_write32_async(struct rtl_priv *rtlpriv, writel(val, (u8 __iomem *) rtlpriv->io.pci_mem_start + addr); } +static inline u16 calc_fifo_space(u16 rp, u16 wp) +{ + if (rp <= wp) + return RTL_PCI_MAX_RX_COUNT - 1 + rp - wp; + return rp - wp - 1; +} + #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c index 2aa34d9055f0..d930c1f78721 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.c @@ -26,6 +26,7 @@ #include "../wifi.h" #include "../base.h" #include "../pci.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -341,38 +342,6 @@ static void dm_tx_pwr_track_set_pwr(struct ieee80211_hw *hw, } } -static void rtl88e_dm_diginit(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct dig_t *dm_dig = &rtlpriv->dm_digtable; - - dm_dig->dig_enable_flag = true; - dm_dig->cur_igvalue = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f); - dm_dig->pre_igvalue = 0; - dm_dig->cur_sta_cstate = DIG_STA_DISCONNECT; - dm_dig->presta_cstate = DIG_STA_DISCONNECT; - dm_dig->curmultista_cstate = DIG_MULTISTA_DISCONNECT; - dm_dig->rssi_lowthresh = DM_DIG_THRESH_LOW; - dm_dig->rssi_highthresh = DM_DIG_THRESH_HIGH; - dm_dig->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; - dm_dig->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; - dm_dig->rx_gain_max = DM_DIG_MAX; - dm_dig->rx_gain_min = DM_DIG_MIN; - dm_dig->back_val = DM_DIG_BACKOFF_DEFAULT; - dm_dig->back_range_max = DM_DIG_BACKOFF_MAX; - dm_dig->back_range_min = DM_DIG_BACKOFF_MIN; - dm_dig->pre_cck_cca_thres = 0xff; - dm_dig->cur_cck_cca_thres = 0x83; - dm_dig->forbidden_igi = DM_DIG_MIN; - dm_dig->large_fa_hit = 0; - dm_dig->recover_cnt = 0; - dm_dig->dig_min_0 = 0x25; - dm_dig->dig_min_1 = 0x25; - dm_dig->media_connect_0 = false; - dm_dig->media_connect_1 = false; - rtlpriv->dm.dm_initialgain_enable = true; -} - static u8 rtl88e_dm_initial_gain_min_pwdb(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -1796,9 +1765,10 @@ static void rtl88e_dm_antenna_diversity(struct ieee80211_hw *hw) void rtl88e_dm_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 cur_igvalue = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f); rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER; - rtl88e_dm_diginit(hw); + rtl_dm_diginit(hw, cur_igvalue); rtl88e_dm_init_dynamic_txpower(hw); rtl88e_dm_init_edca_turbo(hw); rtl88e_dm_init_rate_adaptive_mask(hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h index 64f1f3ea9807..071ccee69eae 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/dm.h @@ -186,28 +186,12 @@ #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 -#define DM_DIG_THRESH_HIGH 40 -#define DM_DIG_THRESH_LOW 35 - -#define DM_FALSEALARM_THRESH_LOW 400 -#define DM_FALSEALARM_THRESH_HIGH 1000 - -#define DM_DIG_MAX 0x3e -#define DM_DIG_MIN 0x1e - -#define DM_DIG_MAX_AP 0x32 -#define DM_DIG_MIN_AP 0x20 - #define DM_DIG_FA_UPPER 0x3e #define DM_DIG_FA_LOWER 0x1e #define DM_DIG_FA_TH0 0x200 #define DM_DIG_FA_TH1 0x300 #define DM_DIG_FA_TH2 0x400 -#define DM_DIG_BACKOFF_MAX 12 -#define DM_DIG_BACKOFF_MIN -4 -#define DM_DIG_BACKOFF_DEFAULT 10 - #define RXPATHSELECTION_SS_TH_W 30 #define RXPATHSELECTION_DIFF_TH 18 @@ -262,14 +246,6 @@ enum tag_dynamic_init_gain_operation_type_definition { DIG_OP_TYPE_MAX }; -enum tag_cck_packet_detection_threshold_type_definition { - CCK_PD_STAGE_LOWRSSI = 0, - CCK_PD_STAGE_HIGHRSSI = 1, - CCK_FA_STAGE_LOW = 2, - CCK_FA_STAGE_HIGH = 3, - CCK_PD_STAGE_MAX = 4, -}; - enum dm_1r_cca_e { CCA_1R = 0, CCA_2R = 1, @@ -288,23 +264,6 @@ enum dm_sw_ant_switch_e { ANS_ANTENNA_MAX = 3, }; -enum dm_dig_ext_port_alg_e { - DIG_EXT_PORT_STAGE_0 = 0, - DIG_EXT_PORT_STAGE_1 = 1, - DIG_EXT_PORT_STAGE_2 = 2, - DIG_EXT_PORT_STAGE_3 = 3, - DIG_EXT_PORT_STAGE_MAX = 4, -}; - -enum dm_dig_connect_e { - DIG_STA_DISCONNECT = 0, - DIG_STA_CONNECT = 1, - DIG_STA_BEFORE_CONNECT = 2, - DIG_MULTISTA_DISCONNECT = 3, - DIG_MULTISTA_CONNECT = 4, - DIG_CONNECT_MAX -}; - enum pwr_track_control_method { BBSWING, TXAGC diff --git a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c index df549c96adef..791efbe6b18c 100644 --- a/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8188ee/trx.c @@ -47,164 +47,6 @@ static u8 _rtl88ee_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue) return skb->priority; } -/* mac80211's rate_idx is like this: - * - * 2.4G band:rx_status->band == IEEE80211_BAND_2GHZ - * - * B/G rate: - * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC92C_RATE1M-->DESC92C_RATE54M ==> idx is 0-->11, - * - * N rate: - * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15 - * - * 5G band:rx_status->band == IEEE80211_BAND_5GHZ - * A rate: - * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC92C_RATE6M-->DESC92C_RATE54M ==> idx is 0-->7, - * - * N rate: - * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15 - */ -static int _rtl88ee_rate_mapping(struct ieee80211_hw *hw, - bool isht, u8 desc_rate) -{ - int rate_idx; - - if (!isht) { - if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) { - switch (desc_rate) { - case DESC92C_RATE1M: - rate_idx = 0; - break; - case DESC92C_RATE2M: - rate_idx = 1; - break; - case DESC92C_RATE5_5M: - rate_idx = 2; - break; - case DESC92C_RATE11M: - rate_idx = 3; - break; - case DESC92C_RATE6M: - rate_idx = 4; - break; - case DESC92C_RATE9M: - rate_idx = 5; - break; - case DESC92C_RATE12M: - rate_idx = 6; - break; - case DESC92C_RATE18M: - rate_idx = 7; - break; - case DESC92C_RATE24M: - rate_idx = 8; - break; - case DESC92C_RATE36M: - rate_idx = 9; - break; - case DESC92C_RATE48M: - rate_idx = 10; - break; - case DESC92C_RATE54M: - rate_idx = 11; - break; - default: - rate_idx = 0; - break; - } - } else { - switch (desc_rate) { - case DESC92C_RATE6M: - rate_idx = 0; - break; - case DESC92C_RATE9M: - rate_idx = 1; - break; - case DESC92C_RATE12M: - rate_idx = 2; - break; - case DESC92C_RATE18M: - rate_idx = 3; - break; - case DESC92C_RATE24M: - rate_idx = 4; - break; - case DESC92C_RATE36M: - rate_idx = 5; - break; - case DESC92C_RATE48M: - rate_idx = 6; - break; - case DESC92C_RATE54M: - rate_idx = 7; - break; - default: - rate_idx = 0; - break; - } - } - } else { - switch (desc_rate) { - case DESC92C_RATEMCS0: - rate_idx = 0; - break; - case DESC92C_RATEMCS1: - rate_idx = 1; - break; - case DESC92C_RATEMCS2: - rate_idx = 2; - break; - case DESC92C_RATEMCS3: - rate_idx = 3; - break; - case DESC92C_RATEMCS4: - rate_idx = 4; - break; - case DESC92C_RATEMCS5: - rate_idx = 5; - break; - case DESC92C_RATEMCS6: - rate_idx = 6; - break; - case DESC92C_RATEMCS7: - rate_idx = 7; - break; - case DESC92C_RATEMCS8: - rate_idx = 8; - break; - case DESC92C_RATEMCS9: - rate_idx = 9; - break; - case DESC92C_RATEMCS10: - rate_idx = 10; - break; - case DESC92C_RATEMCS11: - rate_idx = 11; - break; - case DESC92C_RATEMCS12: - rate_idx = 12; - break; - case DESC92C_RATEMCS13: - rate_idx = 13; - break; - case DESC92C_RATEMCS14: - rate_idx = 14; - break; - case DESC92C_RATEMCS15: - rate_idx = 15; - break; - default: - rate_idx = 0; - break; - } - } - return rate_idx; -} - static void _rtl88ee_query_rxphystatus(struct ieee80211_hw *hw, struct rtl_stats *pstatus, u8 *pdesc, struct rx_fwinfo_88e *p_drvinfo, @@ -630,8 +472,8 @@ bool rtl88ee_rx_query_desc(struct ieee80211_hw *hw, * are use (RX_FLAG_HT) * Notice: this is diff with windows define */ - rx_status->rate_idx = _rtl88ee_rate_mapping(hw, - status->is_ht, status->rate); + rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht, + false, status->rate); rx_status->mactime = status->timestamp_low; if (phystatus == true) { diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c index f6cb5aedfdd1..f5ee67cda73a 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c +++ b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c @@ -32,6 +32,7 @@ #include "phy_common.h" #include "../pci.h" #include "../base.h" +#include "../core.h" #define BT_RSSI_STATE_NORMAL_POWER BIT_OFFSET_LEN_MASK_32(0, 1) #define BT_RSSI_STATE_AMDPU_OFF BIT_OFFSET_LEN_MASK_32(1, 1) @@ -194,36 +195,6 @@ void dm_savepowerindex(struct ieee80211_hw *hw) } EXPORT_SYMBOL_GPL(dm_savepowerindex); -static void rtl92c_dm_diginit(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct dig_t *dm_digtable = &rtlpriv->dm_digtable; - - dm_digtable->dig_enable_flag = true; - dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX; - dm_digtable->cur_igvalue = 0x20; - dm_digtable->pre_igvalue = 0x0; - dm_digtable->cursta_cstate = DIG_STA_DISCONNECT; - dm_digtable->presta_cstate = DIG_STA_DISCONNECT; - dm_digtable->curmultista_cstate = DIG_MULTISTA_DISCONNECT; - dm_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW; - dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH; - dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; - dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; - dm_digtable->rx_gain_max = DM_DIG_MAX; - dm_digtable->rx_gain_min = DM_DIG_MIN; - dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT; - dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX; - dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN; - dm_digtable->pre_cck_pd_state = CCK_PD_STAGE_MAX; - dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_LowRssi; - - dm_digtable->forbidden_igi = DM_DIG_MIN; - dm_digtable->large_fa_hit = 0; - dm_digtable->recover_cnt = 0; - dm_digtable->dig_dynamic_min = 0x25; -} - static u8 rtl92c_dm_initial_gain_min_pwdb(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -507,27 +478,27 @@ static void rtl92c_dm_cck_packet_detection_thresh(struct ieee80211_hw *hw) if (dm_digtable->rssi_val_min > 100) dm_digtable->rssi_val_min = 100; - if (dm_digtable->pre_cck_pd_state == CCK_PD_STAGE_LowRssi) { + if (dm_digtable->pre_cck_pd_state == CCK_PD_STAGE_LOWRSSI) { if (dm_digtable->rssi_val_min <= 25) dm_digtable->cur_cck_pd_state = - CCK_PD_STAGE_LowRssi; + CCK_PD_STAGE_LOWRSSI; else dm_digtable->cur_cck_pd_state = - CCK_PD_STAGE_HighRssi; + CCK_PD_STAGE_HIGHRSSI; } else { if (dm_digtable->rssi_val_min <= 20) dm_digtable->cur_cck_pd_state = - CCK_PD_STAGE_LowRssi; + CCK_PD_STAGE_LOWRSSI; else dm_digtable->cur_cck_pd_state = - CCK_PD_STAGE_HighRssi; + CCK_PD_STAGE_HIGHRSSI; } } else { dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_MAX; } if (dm_digtable->pre_cck_pd_state != dm_digtable->cur_cck_pd_state) { - if ((dm_digtable->cur_cck_pd_state == CCK_PD_STAGE_LowRssi) || + if ((dm_digtable->cur_cck_pd_state == CCK_PD_STAGE_LOWRSSI) || (dm_digtable->cur_cck_pd_state == CCK_PD_STAGE_MAX)) rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, 0x83); else @@ -1374,7 +1345,7 @@ void rtl92c_dm_init(struct ieee80211_hw *hw) rtlpriv->dm.undec_sm_pwdb = -1; rtlpriv->dm.undec_sm_cck = -1; rtlpriv->dm.dm_initialgain_enable = true; - rtl92c_dm_diginit(hw); + rtl_dm_diginit(hw, 0x20); rtlpriv->dm.dm_flag |= HAL_DM_HIPWR_DISABLE; rtl92c_dm_init_dynamic_txpower(hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h index 4f232a063636..4422e31fedd9 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h +++ b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h @@ -47,25 +47,12 @@ #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 -#define DM_DIG_THRESH_HIGH 40 -#define DM_DIG_THRESH_LOW 35 - -#define DM_FALSEALARM_THRESH_LOW 400 -#define DM_FALSEALARM_THRESH_HIGH 1000 - -#define DM_DIG_MAX 0x3e -#define DM_DIG_MIN 0x1e - #define DM_DIG_FA_UPPER 0x32 #define DM_DIG_FA_LOWER 0x20 #define DM_DIG_FA_TH0 0x20 #define DM_DIG_FA_TH1 0x100 #define DM_DIG_FA_TH2 0x200 -#define DM_DIG_BACKOFF_MAX 12 -#define DM_DIG_BACKOFF_MIN -4 -#define DM_DIG_BACKOFF_DEFAULT 10 - #define RXPATHSELECTION_SS_TH_lOW 30 #define RXPATHSELECTION_DIFF_TH 18 @@ -123,14 +110,6 @@ enum tag_dynamic_init_gain_operation_type_definition { DIG_OP_TYPE_MAX }; -enum tag_cck_packet_detection_threshold_type_definition { - CCK_PD_STAGE_LowRssi = 0, - CCK_PD_STAGE_HighRssi = 1, - CCK_FA_STAGE_Low = 2, - CCK_FA_STAGE_High = 3, - CCK_PD_STAGE_MAX = 4, -}; - enum dm_1r_cca_e { CCA_1R = 0, CCA_2R = 1, @@ -149,23 +128,6 @@ enum dm_sw_ant_switch_e { ANS_ANTENNA_MAX = 3, }; -enum dm_dig_ext_port_alg_e { - DIG_EXT_PORT_STAGE_0 = 0, - DIG_EXT_PORT_STAGE_1 = 1, - DIG_EXT_PORT_STAGE_2 = 2, - DIG_EXT_PORT_STAGE_3 = 3, - DIG_EXT_PORT_STAGE_MAX = 4, -}; - -enum dm_dig_connect_e { - DIG_STA_DISCONNECT = 0, - DIG_STA_CONNECT = 1, - DIG_STA_BEFORE_CONNECT = 2, - DIG_MULTISTA_DISCONNECT = 3, - DIG_MULTISTA_CONNECT = 4, - DIG_CONNECT_MAX -}; - void rtl92c_dm_init(struct ieee80211_hw *hw); void rtl92c_dm_watchdog(struct ieee80211_hw *hw); void rtl92c_dm_write_dig(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h index b64ae45dc674..e9f4281f5067 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h +++ b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h @@ -37,6 +37,7 @@ #define FW_8192C_POLLING_DELAY 5 #define FW_8192C_POLLING_TIMEOUT_COUNT 100 #define NORMAL_CHIP BIT(4) +#define H2C_92C_KEEP_ALIVE_CTRL 48 #define IS_FW_HEADER_EXIST(_pfwhdr) \ ((le16_to_cpu(_pfwhdr->signature)&0xFFF0) == 0x92C0 ||\ diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/dm.c b/drivers/net/wireless/rtlwifi/rtl8192ce/dm.c index 74f9c083b80d..09898cf2e07a 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/dm.c @@ -30,6 +30,7 @@ #include "../wifi.h" #include "../base.h" #include "../pci.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "phy.h" diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/dm.h b/drivers/net/wireless/rtlwifi/rtl8192ce/dm.h index 9c5311c299fd..38ba707015f5 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/dm.h @@ -42,25 +42,12 @@ #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 -#define DM_DIG_THRESH_HIGH 40 -#define DM_DIG_THRESH_LOW 35 - -#define DM_FALSEALARM_THRESH_LOW 400 -#define DM_FALSEALARM_THRESH_HIGH 1000 - -#define DM_DIG_MAX 0x3e -#define DM_DIG_MIN 0x1e - #define DM_DIG_FA_UPPER 0x32 #define DM_DIG_FA_LOWER 0x20 #define DM_DIG_FA_TH0 0x20 #define DM_DIG_FA_TH1 0x100 #define DM_DIG_FA_TH2 0x200 -#define DM_DIG_BACKOFF_MAX 12 -#define DM_DIG_BACKOFF_MIN -4 -#define DM_DIG_BACKOFF_DEFAULT 10 - #define RXPATHSELECTION_SS_TH_lOW 30 #define RXPATHSELECTION_DIFF_TH 18 diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c index 5c646d5f7bb8..303b299376c9 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c @@ -544,8 +544,13 @@ void rtl92ce_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) (u8 *)(&fw_current_inps)); } break; } - case HW_VAR_KEEP_ALIVE: - break; + case HW_VAR_KEEP_ALIVE: { + u8 array[2]; + + array[0] = 0xff; + array[1] = *((u8 *)val); + rtl92c_fill_h2c_cmd(hw, H2C_92C_KEEP_ALIVE_CTRL, 2, array); + break; } default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "switch case %d not processed\n", variable); @@ -1156,47 +1161,35 @@ static int _rtl92ce_set_media_status(struct ieee80211_hw *hw, struct rtl_priv *rtlpriv = rtl_priv(hw); u8 bt_msr = rtl_read_byte(rtlpriv, MSR); enum led_ctl_mode ledaction = LED_CTL_NO_LINK; - bt_msr &= 0xfc; + u8 mode = MSR_NOLINK; - if (type == NL80211_IFTYPE_UNSPECIFIED || - type == NL80211_IFTYPE_STATION) { - _rtl92ce_stop_tx_beacon(hw); - _rtl92ce_enable_bcn_sub_func(hw); - } else if (type == NL80211_IFTYPE_ADHOC || type == NL80211_IFTYPE_AP || - type == NL80211_IFTYPE_MESH_POINT) { - _rtl92ce_resume_tx_beacon(hw); - _rtl92ce_disable_bcn_sub_func(hw); - } else { - RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, - "Set HW_VAR_MEDIA_STATUS: No such media status(%x)\n", - type); - } + bt_msr &= 0xfc; switch (type) { case NL80211_IFTYPE_UNSPECIFIED: - bt_msr |= MSR_NOLINK; - ledaction = LED_CTL_LINK; + mode = MSR_NOLINK; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to NO LINK!\n"); break; case NL80211_IFTYPE_ADHOC: - bt_msr |= MSR_ADHOC; + mode = MSR_ADHOC; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to Ad Hoc!\n"); break; case NL80211_IFTYPE_STATION: - bt_msr |= MSR_INFRA; + mode = MSR_INFRA; ledaction = LED_CTL_LINK; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to STA!\n"); break; case NL80211_IFTYPE_AP: - bt_msr |= MSR_AP; + mode = MSR_AP; + ledaction = LED_CTL_LINK; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to AP!\n"); break; case NL80211_IFTYPE_MESH_POINT: - bt_msr |= MSR_ADHOC; + mode = MSR_ADHOC; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Set Network type to Mesh Point!\n"); break; @@ -1207,9 +1200,32 @@ static int _rtl92ce_set_media_status(struct ieee80211_hw *hw, } - rtl_write_byte(rtlpriv, (MSR), bt_msr); + /* MSR_INFRA == Link in infrastructure network; + * MSR_ADHOC == Link in ad hoc network; + * Therefore, check link state is necessary. + * + * MSR_AP == AP mode; link state does not matter here. + */ + if (mode != MSR_AP && + rtlpriv->mac80211.link_state < MAC80211_LINKED) { + mode = MSR_NOLINK; + ledaction = LED_CTL_NO_LINK; + } + if (mode == MSR_NOLINK || mode == MSR_INFRA) { + _rtl92ce_stop_tx_beacon(hw); + _rtl92ce_enable_bcn_sub_func(hw); + } else if (mode == MSR_ADHOC || mode == MSR_AP) { + _rtl92ce_resume_tx_beacon(hw); + _rtl92ce_disable_bcn_sub_func(hw); + } else { + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "Set HW_VAR_MEDIA_STATUS: No such media status(%x).\n", + mode); + } + rtl_write_byte(rtlpriv, MSR, bt_msr | mode); + rtlpriv->cfg->ops->led_control(hw, ledaction); - if ((bt_msr & MSR_MASK) == MSR_AP) + if (mode == MSR_AP) rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); else rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66); @@ -1833,7 +1849,6 @@ static void rtl92ce_update_hal_rate_table(struct ieee80211_hw *hw, u32 ratr_value; u8 ratr_index = 0; u8 nmode = mac->ht_enable; - u8 mimo_ps = IEEE80211_SMPS_OFF; u16 shortgi_rate; u32 tmp_ratr_value; u8 curtxbw_40mhz = mac->bw_40; @@ -1842,6 +1857,7 @@ static void rtl92ce_update_hal_rate_table(struct ieee80211_hw *hw, u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? 1 : 0; enum wireless_mode wirelessmode = mac->mode; + u32 ratr_mask; if (rtlhal->current_bandtype == BAND_ON_5G) ratr_value = sta->supp_rates[1] << 4; @@ -1865,19 +1881,13 @@ static void rtl92ce_update_hal_rate_table(struct ieee80211_hw *hw, case WIRELESS_MODE_N_24G: case WIRELESS_MODE_N_5G: nmode = 1; - if (mimo_ps == IEEE80211_SMPS_STATIC) { - ratr_value &= 0x0007F005; - } else { - u32 ratr_mask; - - if (get_rf_type(rtlphy) == RF_1T2R || - get_rf_type(rtlphy) == RF_1T1R) - ratr_mask = 0x000ff005; - else - ratr_mask = 0x0f0ff005; + if (get_rf_type(rtlphy) == RF_1T2R || + get_rf_type(rtlphy) == RF_1T1R) + ratr_mask = 0x000ff005; + else + ratr_mask = 0x0f0ff005; - ratr_value &= ratr_mask; - } + ratr_value &= ratr_mask; break; default: if (rtlphy->rf_type == RF_1T2R) @@ -1930,17 +1940,16 @@ static void rtl92ce_update_hal_rate_mask(struct ieee80211_hw *hw, struct rtl_sta_info *sta_entry = NULL; u32 ratr_bitmap; u8 ratr_index; - u8 curtxbw_40mhz = (sta->bandwidth >= IEEE80211_STA_RX_BW_40) ? 1 : 0; - u8 curshortgi_40mhz = curtxbw_40mhz && - (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? - 1 : 0; + u8 curtxbw_40mhz = (sta->ht_cap.cap & + IEEE80211_HT_CAP_SUP_WIDTH_20_40) ? 1 : 0; + u8 curshortgi_40mhz = (sta->ht_cap.cap & + IEEE80211_HT_CAP_SGI_40) ? 1 : 0; u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? 1 : 0; enum wireless_mode wirelessmode = 0; bool shortgi = false; u8 rate_mask[5]; u8 macid = 0; - u8 mimo_ps = IEEE80211_SMPS_OFF; sta_entry = (struct rtl_sta_info *) sta->drv_priv; wirelessmode = sta_entry->wireless_mode; @@ -1985,47 +1994,38 @@ static void rtl92ce_update_hal_rate_mask(struct ieee80211_hw *hw, case WIRELESS_MODE_N_5G: ratr_index = RATR_INX_WIRELESS_NGB; - if (mimo_ps == IEEE80211_SMPS_STATIC) { - if (rssi_level == 1) - ratr_bitmap &= 0x00070000; - else if (rssi_level == 2) - ratr_bitmap &= 0x0007f000; - else - ratr_bitmap &= 0x0007f005; + if (rtlphy->rf_type == RF_1T2R || + rtlphy->rf_type == RF_1T1R) { + if (curtxbw_40mhz) { + if (rssi_level == 1) + ratr_bitmap &= 0x000f0000; + else if (rssi_level == 2) + ratr_bitmap &= 0x000ff000; + else + ratr_bitmap &= 0x000ff015; + } else { + if (rssi_level == 1) + ratr_bitmap &= 0x000f0000; + else if (rssi_level == 2) + ratr_bitmap &= 0x000ff000; + else + ratr_bitmap &= 0x000ff005; + } } else { - if (rtlphy->rf_type == RF_1T2R || - rtlphy->rf_type == RF_1T1R) { - if (curtxbw_40mhz) { - if (rssi_level == 1) - ratr_bitmap &= 0x000f0000; - else if (rssi_level == 2) - ratr_bitmap &= 0x000ff000; - else - ratr_bitmap &= 0x000ff015; - } else { - if (rssi_level == 1) - ratr_bitmap &= 0x000f0000; - else if (rssi_level == 2) - ratr_bitmap &= 0x000ff000; - else - ratr_bitmap &= 0x000ff005; - } + if (curtxbw_40mhz) { + if (rssi_level == 1) + ratr_bitmap &= 0x0f0f0000; + else if (rssi_level == 2) + ratr_bitmap &= 0x0f0ff000; + else + ratr_bitmap &= 0x0f0ff015; } else { - if (curtxbw_40mhz) { - if (rssi_level == 1) - ratr_bitmap &= 0x0f0f0000; - else if (rssi_level == 2) - ratr_bitmap &= 0x0f0ff000; - else - ratr_bitmap &= 0x0f0ff015; - } else { - if (rssi_level == 1) - ratr_bitmap &= 0x0f0f0000; - else if (rssi_level == 2) - ratr_bitmap &= 0x0f0ff000; - else - ratr_bitmap &= 0x0f0ff005; - } + if (rssi_level == 1) + ratr_bitmap &= 0x0f0f0000; + else if (rssi_level == 2) + ratr_bitmap &= 0x0f0ff000; + else + ratr_bitmap &= 0x0f0ff005; } } @@ -2058,9 +2058,6 @@ static void rtl92ce_update_hal_rate_mask(struct ieee80211_hw *hw, "Rate_index:%x, ratr_val:%x, %5phC\n", ratr_index, ratr_bitmap, rate_mask); rtl92c_fill_h2c_cmd(hw, H2C_RA_MASK, 5, rate_mask); - - if (macid != 0) - sta_entry->ratr_index = ratr_index; } void rtl92ce_update_hal_rate_tbl(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c b/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c index bc5ca989b915..1ee5a6ae9960 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c @@ -518,11 +518,12 @@ static bool _rtl92ce_phy_set_rf_power_state(struct ieee80211_hw *hw, } case ERFSLEEP:{ if (ppsc->rfpwr_state == ERFOFF) - return false; + break; for (queue_id = 0, i = 0; queue_id < RTL_PCI_MAX_TX_QUEUE_COUNT;) { ring = &pcipriv->dev.tx_ring[queue_id]; - if (skb_queue_len(&ring->queue) == 0) { + if (queue_id == BEACON_QUEUE || + skb_queue_len(&ring->queue) == 0) { queue_id++; continue; } else { diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c index dd5aa089126a..de6cb6c3a48c 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c @@ -334,21 +334,21 @@ static struct rtl_hal_cfg rtl92ce_hal_cfg = { .maps[RTL_IMR_ROK] = IMR_ROK, .maps[RTL_IBSS_INT_MASKS] = (IMR_BCNINT | IMR_TBDOK | IMR_TBDER), - .maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M, - .maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M, - .maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M, - .maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M, - .maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M, - .maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M, - .maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M, - .maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M, - .maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M, - .maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M, - .maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M, - .maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M, - - .maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7, - .maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15, + .maps[RTL_RC_CCK_RATE1M] = DESC_RATE1M, + .maps[RTL_RC_CCK_RATE2M] = DESC_RATE2M, + .maps[RTL_RC_CCK_RATE5_5M] = DESC_RATE5_5M, + .maps[RTL_RC_CCK_RATE11M] = DESC_RATE11M, + .maps[RTL_RC_OFDM_RATE6M] = DESC_RATE6M, + .maps[RTL_RC_OFDM_RATE9M] = DESC_RATE9M, + .maps[RTL_RC_OFDM_RATE12M] = DESC_RATE12M, + .maps[RTL_RC_OFDM_RATE18M] = DESC_RATE18M, + .maps[RTL_RC_OFDM_RATE24M] = DESC_RATE24M, + .maps[RTL_RC_OFDM_RATE36M] = DESC_RATE36M, + .maps[RTL_RC_OFDM_RATE48M] = DESC_RATE48M, + .maps[RTL_RC_OFDM_RATE54M] = DESC_RATE54M, + + .maps[RTL_RC_HT_RATEMCS7] = DESC_RATEMCS7, + .maps[RTL_RC_HT_RATEMCS15] = DESC_RATEMCS15, }; static const struct pci_device_id rtl92ce_pci_ids[] = { diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c index e88dcd0e0af1..84ddd4d07a1d 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c @@ -257,8 +257,8 @@ static void _rtl92ce_query_rxphystatus(struct ieee80211_hw *hw, pstats->recvsignalpower = rx_pwr_all; /* (3)EVM of HT rate */ - if (pstats->is_ht && pstats->rate >= DESC92_RATEMCS8 && - pstats->rate <= DESC92_RATEMCS15) + if (pstats->is_ht && pstats->rate >= DESC_RATEMCS8 && + pstats->rate <= DESC_RATEMCS15) max_spatial_stream = 2; else max_spatial_stream = 1; @@ -400,9 +400,8 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw, * are use (RX_FLAG_HT) * Notice: this is diff with windows define */ - rx_status->rate_idx = rtlwifi_rate_mapping(hw, - stats->is_ht, stats->rate, - stats->isfirst_ampdu); + rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats->is_ht, + false, stats->rate); rx_status->mactime = stats->timestamp_low; if (phystatus) { @@ -501,7 +500,7 @@ void rtl92ce_tx_fill_desc(struct ieee80211_hw *hw, SET_TX_DESC_RTS_BW(pdesc, 0); SET_TX_DESC_RTS_SC(pdesc, tcb_desc->rts_sc); SET_TX_DESC_RTS_SHORT(pdesc, - ((tcb_desc->rts_rate <= DESC92_RATE54M) ? + ((tcb_desc->rts_rate <= DESC_RATE54M) ? (tcb_desc->rts_use_shortpreamble ? 1 : 0) : (tcb_desc->rts_use_shortgi ? 1 : 0))); @@ -624,7 +623,7 @@ void rtl92ce_tx_fill_cmddesc(struct ieee80211_hw *hw, if (firstseg) SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN); - SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE1M); + SET_TX_DESC_TX_RATE(pdesc, DESC_RATE1M); SET_TX_DESC_SEQ(pdesc, 0); diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c index 551321728ae0..fe4b699a12f5 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c @@ -1000,6 +1000,7 @@ int rtl92cu_hw_init(struct ieee80211_hw *hw) local_save_flags(flags); local_irq_enable(); + rtlhal->fw_ready = false; rtlhal->hw_type = HARDWARE_TYPE_RTL8192CU; err = _rtl92cu_init_mac(hw); if (err) { @@ -1013,6 +1014,8 @@ int rtl92cu_hw_init(struct ieee80211_hw *hw) err = 1; goto exit; } + + rtlhal->fw_ready = true; rtlhal->last_hmeboxnum = 0; /* h2c */ _rtl92cu_phy_param_tab_init(hw); rtl92cu_phy_mac_config(hw); @@ -1509,6 +1512,7 @@ void rtl92cu_set_beacon_related_registers(struct ieee80211_hw *hw) /* TODO: Modify later (Find the right parameters) * NOTE: Fix test chip's bug (about contention windows's randomness) */ if ((mac->opmode == NL80211_IFTYPE_ADHOC) || + (mac->opmode == NL80211_IFTYPE_MESH_POINT) || (mac->opmode == NL80211_IFTYPE_AP)) { rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_CCK, 0x50); rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_OFDM, 0x50); diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c b/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c index c2d8ec6afcda..133e395b7401 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/mac.c @@ -880,8 +880,8 @@ static void _rtl92c_query_rxphystatus(struct ieee80211_hw *hw, pstats->rxpower = rx_pwr_all; pstats->recvsignalpower = rx_pwr_all; if (GET_RX_DESC_RX_MCS(pdesc) && - GET_RX_DESC_RX_MCS(pdesc) >= DESC92_RATEMCS8 && - GET_RX_DESC_RX_MCS(pdesc) <= DESC92_RATEMCS15) + GET_RX_DESC_RX_MCS(pdesc) >= DESC_RATEMCS8 && + GET_RX_DESC_RX_MCS(pdesc) <= DESC_RATEMCS15) max_spatial_stream = 2; else max_spatial_stream = 1; diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c index e06bafee37f9..90a714c189a8 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c @@ -257,20 +257,20 @@ static struct rtl_hal_cfg rtl92cu_hal_cfg = { .maps[RTL_IMR_ROK] = IMR_ROK, .maps[RTL_IBSS_INT_MASKS] = (IMR_BCNINT | IMR_TBDOK | IMR_TBDER), - .maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M, - .maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M, - .maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M, - .maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M, - .maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M, - .maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M, - .maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M, - .maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M, - .maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M, - .maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M, - .maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M, - .maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M, - .maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7, - .maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15, + .maps[RTL_RC_CCK_RATE1M] = DESC_RATE1M, + .maps[RTL_RC_CCK_RATE2M] = DESC_RATE2M, + .maps[RTL_RC_CCK_RATE5_5M] = DESC_RATE5_5M, + .maps[RTL_RC_CCK_RATE11M] = DESC_RATE11M, + .maps[RTL_RC_OFDM_RATE6M] = DESC_RATE6M, + .maps[RTL_RC_OFDM_RATE9M] = DESC_RATE9M, + .maps[RTL_RC_OFDM_RATE12M] = DESC_RATE12M, + .maps[RTL_RC_OFDM_RATE18M] = DESC_RATE18M, + .maps[RTL_RC_OFDM_RATE24M] = DESC_RATE24M, + .maps[RTL_RC_OFDM_RATE36M] = DESC_RATE36M, + .maps[RTL_RC_OFDM_RATE48M] = DESC_RATE48M, + .maps[RTL_RC_OFDM_RATE54M] = DESC_RATE54M, + .maps[RTL_RC_HT_RATEMCS7] = DESC_RATEMCS7, + .maps[RTL_RC_HT_RATEMCS15] = DESC_RATEMCS15, }; #define USB_VENDER_ID_REALTEK 0x0bda diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c index f383d5f1fed5..cbead007171f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c @@ -325,6 +325,7 @@ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw, && (GET_RX_DESC_FAGGR(pdesc) == 1)); stats->timestamp_low = GET_RX_DESC_TSFL(pdesc); stats->rx_is40Mhzpacket = (bool) GET_RX_DESC_BW(pdesc); + stats->is_ht = (bool)GET_RX_DESC_RX_HT(pdesc); rx_status->freq = hw->conf.chandef.chan->center_freq; rx_status->band = hw->conf.chandef.chan->band; if (GET_RX_DESC_CRC32(pdesc)) @@ -338,10 +339,8 @@ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw, rx_status->flag |= RX_FLAG_MACTIME_START; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; - rx_status->rate_idx = rtlwifi_rate_mapping(hw, - (bool)GET_RX_DESC_RX_HT(pdesc), - (u8)GET_RX_DESC_RX_MCS(pdesc), - (bool)GET_RX_DESC_PAGGR(pdesc)); + rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats->is_ht, + false, stats->rate); rx_status->mactime = GET_RX_DESC_TSFL(pdesc); if (phystatus) { p_drvinfo = (struct rx_fwinfo_92c *)(skb->data + @@ -393,6 +392,7 @@ static void _rtl_rx_process(struct ieee80211_hw *hw, struct sk_buff *skb) && (GET_RX_DESC_FAGGR(rxdesc) == 1)); stats.timestamp_low = GET_RX_DESC_TSFL(rxdesc); stats.rx_is40Mhzpacket = (bool) GET_RX_DESC_BW(rxdesc); + stats.is_ht = (bool)GET_RX_DESC_RX_HT(rxdesc); /* TODO: is center_freq changed when doing scan? */ /* TODO: Shall we add protection or just skip those two step? */ rx_status->freq = hw->conf.chandef.chan->center_freq; @@ -406,10 +406,8 @@ static void _rtl_rx_process(struct ieee80211_hw *hw, struct sk_buff *skb) if (GET_RX_DESC_RX_HT(rxdesc)) rx_status->flag |= RX_FLAG_HT; /* Data rate */ - rx_status->rate_idx = rtlwifi_rate_mapping(hw, - (bool)GET_RX_DESC_RX_HT(rxdesc), - (u8)GET_RX_DESC_RX_MCS(rxdesc), - (bool)GET_RX_DESC_PAGGR(rxdesc)); + rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats.is_ht, + false, stats.rate); /* There is a phy status after this rx descriptor. */ if (GET_RX_DESC_PHY_STATUS(rxdesc)) { p_drvinfo = (struct rx_fwinfo_92c *)(rxdesc + RTL_RX_DESC_SIZE); @@ -545,7 +543,7 @@ void rtl92cu_tx_fill_desc(struct ieee80211_hw *hw, SET_TX_DESC_RTS_BW(txdesc, 0); SET_TX_DESC_RTS_SC(txdesc, tcb_desc->rts_sc); SET_TX_DESC_RTS_SHORT(txdesc, - ((tcb_desc->rts_rate <= DESC92_RATE54M) ? + ((tcb_desc->rts_rate <= DESC_RATE54M) ? (tcb_desc->rts_use_shortpreamble ? 1 : 0) : (tcb_desc->rts_use_shortgi ? 1 : 0))); if (mac->bw_40) { @@ -644,7 +642,7 @@ void rtl92cu_fill_fake_txdesc(struct ieee80211_hw *hw, u8 * pDesc, } SET_TX_DESC_USE_RATE(pDesc, 1); /* use data rate which is set by Sw */ SET_TX_DESC_OWN(pDesc, 1); - SET_TX_DESC_TX_RATE(pDesc, DESC92_RATE1M); + SET_TX_DESC_TX_RATE(pDesc, DESC_RATE1M); _rtl_tx_desc_checksum(pDesc); } @@ -660,7 +658,7 @@ void rtl92cu_tx_fill_cmddesc(struct ieee80211_hw *hw, memset((void *)pdesc, 0, RTL_TX_HEADER_SIZE); if (firstseg) SET_TX_DESC_OFFSET(pdesc, RTL_TX_HEADER_SIZE); - SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE1M); + SET_TX_DESC_TX_RATE(pdesc, DESC_RATE1M); SET_TX_DESC_SEQ(pdesc, 0); SET_TX_DESC_LINIP(pdesc, 0); SET_TX_DESC_QUEUE_SEL(pdesc, fw_queue); diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c index 304c443b89b2..a1be5a68edfb 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/dm.c @@ -29,6 +29,7 @@ #include "../wifi.h" #include "../base.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -155,34 +156,6 @@ static const u8 cckswing_table_ch14[CCK_TABLE_SIZE][8] = { {0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00} /* 32, -16.0dB */ }; -static void rtl92d_dm_diginit(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct dig_t *de_digtable = &rtlpriv->dm_digtable; - - de_digtable->dig_enable_flag = true; - de_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX; - de_digtable->cur_igvalue = 0x20; - de_digtable->pre_igvalue = 0x0; - de_digtable->cursta_cstate = DIG_STA_DISCONNECT; - de_digtable->presta_cstate = DIG_STA_DISCONNECT; - de_digtable->curmultista_cstate = DIG_MULTISTA_DISCONNECT; - de_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW; - de_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH; - de_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; - de_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; - de_digtable->rx_gain_max = DM_DIG_FA_UPPER; - de_digtable->rx_gain_min = DM_DIG_FA_LOWER; - de_digtable->back_val = DM_DIG_BACKOFF_DEFAULT; - de_digtable->back_range_max = DM_DIG_BACKOFF_MAX; - de_digtable->back_range_min = DM_DIG_BACKOFF_MIN; - de_digtable->pre_cck_pd_state = CCK_PD_STAGE_LOWRSSI; - de_digtable->cur_cck_pd_state = CCK_PD_STAGE_MAX; - de_digtable->large_fa_hit = 0; - de_digtable->recover_cnt = 0; - de_digtable->forbidden_igi = DM_DIG_FA_LOWER; -} - static void rtl92d_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw) { u32 ret_value; @@ -1305,7 +1278,9 @@ void rtl92d_dm_init(struct ieee80211_hw *hw) struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER; - rtl92d_dm_diginit(hw); + rtl_dm_diginit(hw, 0x20); + rtlpriv->dm_digtable.rx_gain_max = DM_DIG_FA_UPPER; + rtlpriv->dm_digtable.rx_gain_min = DM_DIG_FA_LOWER; rtl92d_dm_init_dynamic_txpower(hw); rtl92d_dm_init_edca_turbo(hw); rtl92d_dm_init_rate_adaptive_mask(hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/dm.h b/drivers/net/wireless/rtlwifi/rtl8192de/dm.h index 3fea0c11c24a..f2d318ceeb28 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8192de/dm.h @@ -42,25 +42,12 @@ #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 -#define DM_DIG_THRESH_HIGH 40 -#define DM_DIG_THRESH_LOW 35 - -#define DM_FALSEALARM_THRESH_LOW 400 -#define DM_FALSEALARM_THRESH_HIGH 1000 - -#define DM_DIG_MAX 0x3e -#define DM_DIG_MIN 0x1c - #define DM_DIG_FA_UPPER 0x32 #define DM_DIG_FA_LOWER 0x20 #define DM_DIG_FA_TH0 0x100 #define DM_DIG_FA_TH1 0x400 #define DM_DIG_FA_TH2 0x600 -#define DM_DIG_BACKOFF_MAX 12 -#define DM_DIG_BACKOFF_MIN -4 -#define DM_DIG_BACKOFF_DEFAULT 10 - #define RXPATHSELECTION_SS_TH_lOW 30 #define RXPATHSELECTION_DIFF_TH 18 @@ -108,14 +95,6 @@ enum tag_dynamic_init_gain_operation_type_definition { DIG_OP_TYPE_MAX }; -enum tag_cck_packet_detection_threshold_type_definition { - CCK_PD_STAGE_LOWRSSI = 0, - CCK_PD_STAGE_HIGHRSSI = 1, - CCK_FA_STAGE_LOW = 2, - CCK_FA_STAGE_HIGH = 3, - CCK_PD_STAGE_MAX = 4, -}; - enum dm_1r_cca { CCA_1R = 0, CCA_2R = 1, @@ -134,23 +113,6 @@ enum dm_sw_ant_switch { ANS_ANTENNA_MAX = 3, }; -enum dm_dig_ext_port_alg { - DIG_EXT_PORT_STAGE_0 = 0, - DIG_EXT_PORT_STAGE_1 = 1, - DIG_EXT_PORT_STAGE_2 = 2, - DIG_EXT_PORT_STAGE_3 = 3, - DIG_EXT_PORT_STAGE_MAX = 4, -}; - -enum dm_dig_connect { - DIG_STA_DISCONNECT = 0, - DIG_STA_CONNECT = 1, - DIG_STA_BEFORE_CONNECT = 2, - DIG_MULTISTA_DISCONNECT = 3, - DIG_MULTISTA_CONNECT = 4, - DIG_CONNECT_MAX -}; - void rtl92d_dm_init(struct ieee80211_hw *hw); void rtl92d_dm_watchdog(struct ieee80211_hw *hw); void rtl92d_dm_init_edca_turbo(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/fw.c b/drivers/net/wireless/rtlwifi/rtl8192de/fw.c index 23177076b97f..62ef8209718f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/fw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/fw.c @@ -540,23 +540,6 @@ void rtl92d_fill_h2c_cmd(struct ieee80211_hw *hw, return; } -void rtl92d_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - u8 u1_h2c_set_pwrmode[3] = { 0 }; - struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); - - RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "FW LPS mode = %d\n", mode); - SET_H2CCMD_PWRMODE_PARM_MODE(u1_h2c_set_pwrmode, mode); - SET_H2CCMD_PWRMODE_PARM_SMART_PS(u1_h2c_set_pwrmode, 1); - SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(u1_h2c_set_pwrmode, - ppsc->reg_max_lps_awakeintvl); - RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG, - "rtl92d_set_fw_rsvdpagepkt(): u1_h2c_set_pwrmode", - u1_h2c_set_pwrmode, 3); - rtl92d_fill_h2c_cmd(hw, H2C_SETPWRMODE, 3, u1_h2c_set_pwrmode); -} - static bool _rtl92d_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb) { diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/fw.h b/drivers/net/wireless/rtlwifi/rtl8192de/fw.h index a55a803a0b4d..1646e7c3d0f8 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/fw.h +++ b/drivers/net/wireless/rtlwifi/rtl8192de/fw.h @@ -136,7 +136,6 @@ int rtl92d_download_fw(struct ieee80211_hw *hw); void rtl92d_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id, u32 cmd_len, u8 *p_cmdbuffer); void rtl92d_firmware_selfreset(struct ieee80211_hw *hw); -void rtl92d_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode); void rtl92d_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished); void rtl92d_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus); diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c index 280c3da42993..01bcc2d218dc 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/hw.c @@ -546,7 +546,7 @@ static bool _rtl92de_llt_table_init(struct ieee80211_hw *hw) txpktbuf_bndy = 246; value8 = 0; value32 = 0x80bf0d29; - } else if (rtlpriv->rtlhal.macphymode != SINGLEMAC_SINGLEPHY) { + } else { maxPage = 127; txpktbuf_bndy = 123; value8 = 0; diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c index a0aba088259a..b19d0398215f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/sw.c @@ -337,21 +337,21 @@ static struct rtl_hal_cfg rtl92de_hal_cfg = { .maps[RTL_IMR_ROK] = IMR_ROK, .maps[RTL_IBSS_INT_MASKS] = (IMR_BCNINT | IMR_TBDOK | IMR_TBDER), - .maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M, - .maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M, - .maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M, - .maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M, - .maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M, - .maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M, - .maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M, - .maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M, - .maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M, - .maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M, - .maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M, - .maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M, - - .maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7, - .maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15, + .maps[RTL_RC_CCK_RATE1M] = DESC_RATE1M, + .maps[RTL_RC_CCK_RATE2M] = DESC_RATE2M, + .maps[RTL_RC_CCK_RATE5_5M] = DESC_RATE5_5M, + .maps[RTL_RC_CCK_RATE11M] = DESC_RATE11M, + .maps[RTL_RC_OFDM_RATE6M] = DESC_RATE6M, + .maps[RTL_RC_OFDM_RATE9M] = DESC_RATE9M, + .maps[RTL_RC_OFDM_RATE12M] = DESC_RATE12M, + .maps[RTL_RC_OFDM_RATE18M] = DESC_RATE18M, + .maps[RTL_RC_OFDM_RATE24M] = DESC_RATE24M, + .maps[RTL_RC_OFDM_RATE36M] = DESC_RATE36M, + .maps[RTL_RC_OFDM_RATE48M] = DESC_RATE48M, + .maps[RTL_RC_OFDM_RATE54M] = DESC_RATE54M, + + .maps[RTL_RC_HT_RATEMCS7] = DESC_RATEMCS7, + .maps[RTL_RC_HT_RATEMCS15] = DESC_RATEMCS15, }; static struct pci_device_id rtl92de_pci_ids[] = { diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c index 8efbcc7af250..1feaa629dd4f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c @@ -235,8 +235,8 @@ static void _rtl92de_query_rxphystatus(struct ieee80211_hw *hw, pstats->rx_pwdb_all = pwdb_all; pstats->rxpower = rx_pwr_all; pstats->recvsignalpower = rx_pwr_all; - if (pdesc->rxht && pdesc->rxmcs >= DESC92_RATEMCS8 && - pdesc->rxmcs <= DESC92_RATEMCS15) + if (pdesc->rxht && pdesc->rxmcs >= DESC_RATEMCS8 && + pdesc->rxmcs <= DESC_RATEMCS15) max_spatial_stream = 2; else max_spatial_stream = 1; @@ -499,6 +499,7 @@ bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, && (GET_RX_DESC_FAGGR(pdesc) == 1)); stats->timestamp_low = GET_RX_DESC_TSFL(pdesc); stats->rx_is40Mhzpacket = (bool) GET_RX_DESC_BW(pdesc); + stats->is_ht = (bool)GET_RX_DESC_RXHT(pdesc); rx_status->freq = hw->conf.chandef.chan->center_freq; rx_status->band = hw->conf.chandef.chan->band; if (GET_RX_DESC_CRC32(pdesc)) @@ -512,10 +513,8 @@ bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, rx_status->flag |= RX_FLAG_MACTIME_START; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; - rx_status->rate_idx = rtlwifi_rate_mapping(hw, - (bool)GET_RX_DESC_RXHT(pdesc), - (u8)GET_RX_DESC_RXMCS(pdesc), - (bool)GET_RX_DESC_PAGGR(pdesc)); + rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats->is_ht, + false, stats->rate); rx_status->mactime = GET_RX_DESC_TSFL(pdesc); if (phystatus) { p_drvinfo = (struct rx_fwinfo_92d *)(skb->data + @@ -612,14 +611,14 @@ void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, } /* 5G have no CCK rate */ if (rtlhal->current_bandtype == BAND_ON_5G) - if (ptcb_desc->hw_rate < DESC92_RATE6M) - ptcb_desc->hw_rate = DESC92_RATE6M; + if (ptcb_desc->hw_rate < DESC_RATE6M) + ptcb_desc->hw_rate = DESC_RATE6M; SET_TX_DESC_TX_RATE(pdesc, ptcb_desc->hw_rate); if (ptcb_desc->use_shortgi || ptcb_desc->use_shortpreamble) SET_TX_DESC_DATA_SHORTGI(pdesc, 1); if (rtlhal->macphymode == DUALMAC_DUALPHY && - ptcb_desc->hw_rate == DESC92_RATEMCS7) + ptcb_desc->hw_rate == DESC_RATEMCS7) SET_TX_DESC_DATA_SHORTGI(pdesc, 1); if (info->flags & IEEE80211_TX_CTL_AMPDU) { @@ -635,13 +634,13 @@ void rtl92de_tx_fill_desc(struct ieee80211_hw *hw, SET_TX_DESC_RTS_STBC(pdesc, ((ptcb_desc->rts_stbc) ? 1 : 0)); /* 5G have no CCK rate */ if (rtlhal->current_bandtype == BAND_ON_5G) - if (ptcb_desc->rts_rate < DESC92_RATE6M) - ptcb_desc->rts_rate = DESC92_RATE6M; + if (ptcb_desc->rts_rate < DESC_RATE6M) + ptcb_desc->rts_rate = DESC_RATE6M; SET_TX_DESC_RTS_RATE(pdesc, ptcb_desc->rts_rate); SET_TX_DESC_RTS_BW(pdesc, 0); SET_TX_DESC_RTS_SC(pdesc, ptcb_desc->rts_sc); SET_TX_DESC_RTS_SHORT(pdesc, ((ptcb_desc->rts_rate <= - DESC92_RATE54M) ? + DESC_RATE54M) ? (ptcb_desc->rts_use_shortpreamble ? 1 : 0) : (ptcb_desc->rts_use_shortgi ? 1 : 0))); if (bw_40) { @@ -756,9 +755,9 @@ void rtl92de_tx_fill_cmddesc(struct ieee80211_hw *hw, * The braces are needed no matter what checkpatch says */ if (rtlhal->current_bandtype == BAND_ON_5G) { - SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE6M); + SET_TX_DESC_TX_RATE(pdesc, DESC_RATE6M); } else { - SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE1M); + SET_TX_DESC_TX_RATE(pdesc, DESC_RATE1M); } SET_TX_DESC_SEQ(pdesc, 0); SET_TX_DESC_LINIP(pdesc, 0); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/dm.c b/drivers/net/wireless/rtlwifi/rtl8192ee/dm.c index 77deedf79d1d..459f3d0efa2f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/dm.c @@ -26,6 +26,7 @@ #include "../wifi.h" #include "../base.h" #include "../pci.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -151,35 +152,6 @@ static const u8 cckswing_table_ch14[CCK_TABLE_SIZE][8] = { {0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00} /* 32, -16.0dB */ }; -static void rtl92ee_dm_diginit(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct dig_t *dm_dig = &rtlpriv->dm_digtable; - - dm_dig->cur_igvalue = rtl_get_bbreg(hw, DM_REG_IGI_A_11N, - DM_BIT_IGI_11N); - dm_dig->rssi_lowthresh = DM_DIG_THRESH_LOW; - dm_dig->rssi_highthresh = DM_DIG_THRESH_HIGH; - dm_dig->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; - dm_dig->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; - dm_dig->rx_gain_max = DM_DIG_MAX; - dm_dig->rx_gain_min = DM_DIG_MIN; - dm_dig->back_val = DM_DIG_BACKOFF_DEFAULT; - dm_dig->back_range_max = DM_DIG_BACKOFF_MAX; - dm_dig->back_range_min = DM_DIG_BACKOFF_MIN; - dm_dig->pre_cck_cca_thres = 0xff; - dm_dig->cur_cck_cca_thres = 0x83; - dm_dig->forbidden_igi = DM_DIG_MIN; - dm_dig->large_fa_hit = 0; - dm_dig->recover_cnt = 0; - dm_dig->dig_dynamic_min = DM_DIG_MIN; - dm_dig->dig_dynamic_min_1 = DM_DIG_MIN; - dm_dig->media_connect_0 = false; - dm_dig->media_connect_1 = false; - rtlpriv->dm.dm_initialgain_enable = true; - dm_dig->bt30_cur_igi = 0x32; -} - static void rtl92ee_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw) { u32 ret_value; @@ -298,7 +270,7 @@ static void rtl92ee_dm_dig(struct ieee80211_hw *hw) struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct dig_t *dm_dig = &rtlpriv->dm_digtable; - u8 dig_dynamic_min , dig_maxofmin; + u8 dig_min_0, dig_maxofmin; bool bfirstconnect , bfirstdisconnect; u8 dm_dig_max, dm_dig_min; u8 current_igi = dm_dig->cur_igvalue; @@ -308,7 +280,7 @@ static void rtl92ee_dm_dig(struct ieee80211_hw *hw) if (mac->act_scanning) return; - dig_dynamic_min = dm_dig->dig_dynamic_min; + dig_min_0 = dm_dig->dig_min_0; bfirstconnect = (mac->link_state >= MAC80211_LINKED) && !dm_dig->media_connect_0; bfirstdisconnect = (mac->link_state < MAC80211_LINKED) && @@ -329,19 +301,19 @@ static void rtl92ee_dm_dig(struct ieee80211_hw *hw) if (rtlpriv->dm.one_entry_only) { offset = 0; if (dm_dig->rssi_val_min - offset < dm_dig_min) - dig_dynamic_min = dm_dig_min; + dig_min_0 = dm_dig_min; else if (dm_dig->rssi_val_min - offset > dig_maxofmin) - dig_dynamic_min = dig_maxofmin; + dig_min_0 = dig_maxofmin; else - dig_dynamic_min = dm_dig->rssi_val_min - offset; + dig_min_0 = dm_dig->rssi_val_min - offset; } else { - dig_dynamic_min = dm_dig_min; + dig_min_0 = dm_dig_min; } } else { dm_dig->rx_gain_max = dm_dig_max; - dig_dynamic_min = dm_dig_min; + dig_min_0 = dm_dig_min; RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "no link\n"); } @@ -368,10 +340,10 @@ static void rtl92ee_dm_dig(struct ieee80211_hw *hw) } else { if (dm_dig->large_fa_hit < 3) { if ((dm_dig->forbidden_igi - 1) < - dig_dynamic_min) { - dm_dig->forbidden_igi = dig_dynamic_min; + dig_min_0) { + dm_dig->forbidden_igi = dig_min_0; dm_dig->rx_gain_min = - dig_dynamic_min; + dig_min_0; } else { dm_dig->forbidden_igi--; dm_dig->rx_gain_min = @@ -430,7 +402,7 @@ static void rtl92ee_dm_dig(struct ieee80211_hw *hw) rtl92ee_dm_write_dig(hw , current_igi); dm_dig->media_connect_0 = ((mac->link_state >= MAC80211_LINKED) ? true : false); - dm_dig->dig_dynamic_min = dig_dynamic_min; + dm_dig->dig_min_0 = dig_min_0; } void rtl92ee_dm_write_cck_cca_thres(struct ieee80211_hw *hw, u8 cur_thres) @@ -1088,10 +1060,11 @@ static void rtl92ee_dm_init_dynamic_atc_switch(struct ieee80211_hw *hw) void rtl92ee_dm_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 cur_igvalue = rtl_get_bbreg(hw, DM_REG_IGI_A_11N, DM_BIT_IGI_11N); rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER; - rtl92ee_dm_diginit(hw); + rtl_dm_diginit(hw, cur_igvalue); rtl92ee_dm_init_rate_adaptive_mask(hw); rtl92ee_dm_init_primary_cca_check(hw); rtl92ee_dm_init_edca_turbo(hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/dm.h b/drivers/net/wireless/rtlwifi/rtl8192ee/dm.h index 881db7d6fef7..107d5a488fa8 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/dm.h @@ -189,28 +189,12 @@ #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 -#define DM_DIG_THRESH_HIGH 40 -#define DM_DIG_THRESH_LOW 35 - -#define DM_FALSEALARM_THRESH_LOW 400 -#define DM_FALSEALARM_THRESH_HIGH 1000 - -#define DM_DIG_MAX 0x3e -#define DM_DIG_MIN 0x1e - -#define DM_DIG_MAX_AP 0x32 -#define DM_DIG_MIN_AP 0x20 - #define DM_DIG_FA_UPPER 0x3e #define DM_DIG_FA_LOWER 0x1e #define DM_DIG_FA_TH0 0x200 #define DM_DIG_FA_TH1 0x300 #define DM_DIG_FA_TH2 0x400 -#define DM_DIG_BACKOFF_MAX 12 -#define DM_DIG_BACKOFF_MIN -4 -#define DM_DIG_BACKOFF_DEFAULT 10 - #define RXPATHSELECTION_SS_TH_LOW 30 #define RXPATHSELECTION_DIFF_TH 18 diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/fw.c b/drivers/net/wireless/rtlwifi/rtl8192ee/fw.c index 45c128b91f7f..c5d4b8013cde 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/fw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/fw.c @@ -666,7 +666,6 @@ void rtl92ee_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished) struct sk_buff *skb = NULL; u32 totalpacketlen; - bool rtstatus; u8 u1rsvdpageloc[5] = { 0 }; bool b_dlok = false; @@ -728,10 +727,7 @@ void rtl92ee_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished) memcpy((u8 *)skb_put(skb, totalpacketlen), &reserved_page_packet, totalpacketlen); - rtstatus = rtl_cmd_send_packet(hw, skb); - - if (rtstatus) - b_dlok = true; + b_dlok = true; if (b_dlok) { RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD , diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c index 1a87edca2c3f..b461b3128da5 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/hw.c @@ -85,29 +85,6 @@ static void _rtl92ee_enable_bcn_sub_func(struct ieee80211_hw *hw) _rtl92ee_set_bcn_ctrl_reg(hw, 0, BIT(1)); } -static void _rtl92ee_return_beacon_queue_skb(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); - struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[BEACON_QUEUE]; - unsigned long flags; - - spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags); - while (skb_queue_len(&ring->queue)) { - struct rtl_tx_buffer_desc *entry = - &ring->buffer_desc[ring->idx]; - struct sk_buff *skb = __skb_dequeue(&ring->queue); - - pci_unmap_single(rtlpci->pdev, - rtlpriv->cfg->ops->get_desc( - (u8 *)entry, true, HW_DESC_TXBUFF_ADDR), - skb->len, PCI_DMA_TODEVICE); - kfree_skb(skb); - ring->idx = (ring->idx + 1) % ring->entries; - } - spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags); -} - static void _rtl92ee_disable_bcn_sub_func(struct ieee80211_hw *hw) { _rtl92ee_set_bcn_ctrl_reg(hw, BIT(1), 0); @@ -403,9 +380,6 @@ static void _rtl92ee_download_rsvd_page(struct ieee80211_hw *hw) rtl_write_byte(rtlpriv, REG_DWBCN0_CTRL + 2, bcnvalid_reg | BIT(0)); - /* Return Beacon TCB */ - _rtl92ee_return_beacon_queue_skb(hw); - /* download rsvd page */ rtl92ee_set_fw_rsvdpagepkt(hw, false); @@ -1163,6 +1137,139 @@ void rtl92ee_enable_hw_security_config(struct ieee80211_hw *hw) rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_WPA_CONFIG, &sec_reg_value); } +static bool _rtl8192ee_check_pcie_dma_hang(struct rtl_priv *rtlpriv) +{ + u8 tmp; + + /* write reg 0x350 Bit[26]=1. Enable debug port. */ + tmp = rtl_read_byte(rtlpriv, REG_BACKDOOR_DBI_DATA + 3); + if (!(tmp & BIT(2))) { + rtl_write_byte(rtlpriv, REG_BACKDOOR_DBI_DATA + 3, + tmp | BIT(2)); + mdelay(100); /* Suggested by DD Justin_tsai. */ + } + + /* read reg 0x350 Bit[25] if 1 : RX hang + * read reg 0x350 Bit[24] if 1 : TX hang + */ + tmp = rtl_read_byte(rtlpriv, REG_BACKDOOR_DBI_DATA + 3); + if ((tmp & BIT(0)) || (tmp & BIT(1))) { + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "CheckPcieDMAHang8192EE(): true!!\n"); + return true; + } + return false; +} + +static void _rtl8192ee_reset_pcie_interface_dma(struct rtl_priv *rtlpriv, + bool mac_power_on) +{ + u8 tmp; + bool release_mac_rx_pause; + u8 backup_pcie_dma_pause; + + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "ResetPcieInterfaceDMA8192EE()\n"); + + /* Revise Note: Follow the document "PCIe RX DMA Hang Reset Flow_v03" + * released by SD1 Alan. + */ + + /* 1. disable register write lock + * write 0x1C bit[1:0] = 2'h0 + * write 0xCC bit[2] = 1'b1 + */ + tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL); + tmp &= ~(BIT(1) | BIT(0)); + rtl_write_byte(rtlpriv, REG_RSV_CTRL, tmp); + tmp = rtl_read_byte(rtlpriv, REG_PMC_DBG_CTRL2); + tmp |= BIT(2); + rtl_write_byte(rtlpriv, REG_PMC_DBG_CTRL2, tmp); + + /* 2. Check and pause TRX DMA + * write 0x284 bit[18] = 1'b1 + * write 0x301 = 0xFF + */ + tmp = rtl_read_byte(rtlpriv, REG_RXDMA_CONTROL); + if (tmp & BIT(2)) { + /* Already pause before the function for another reason. */ + release_mac_rx_pause = false; + } else { + rtl_write_byte(rtlpriv, REG_RXDMA_CONTROL, (tmp | BIT(2))); + release_mac_rx_pause = true; + } + + backup_pcie_dma_pause = rtl_read_byte(rtlpriv, REG_PCIE_CTRL_REG + 1); + if (backup_pcie_dma_pause != 0xFF) + rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG + 1, 0xFF); + + if (mac_power_on) { + /* 3. reset TRX function + * write 0x100 = 0x00 + */ + rtl_write_byte(rtlpriv, REG_CR, 0); + } + + /* 4. Reset PCIe DMA + * write 0x003 bit[0] = 0 + */ + tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); + tmp &= ~(BIT(0)); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmp); + + /* 5. Enable PCIe DMA + * write 0x003 bit[0] = 1 + */ + tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); + tmp |= BIT(0); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmp); + + if (mac_power_on) { + /* 6. enable TRX function + * write 0x100 = 0xFF + */ + rtl_write_byte(rtlpriv, REG_CR, 0xFF); + + /* We should init LLT & RQPN and + * prepare Tx/Rx descrptor address later + * because MAC function is reset. + */ + } + + /* 7. Restore PCIe autoload down bit + * write 0xF8 bit[17] = 1'b1 + */ + tmp = rtl_read_byte(rtlpriv, REG_MAC_PHY_CTRL_NORMAL + 2); + tmp |= BIT(1); + rtl_write_byte(rtlpriv, REG_MAC_PHY_CTRL_NORMAL + 2, tmp); + + /* In MAC power on state, BB and RF maybe in ON state, + * if we release TRx DMA here + * it will cause packets to be started to Tx/Rx, + * so we release Tx/Rx DMA later. + */ + if (!mac_power_on) { + /* 8. release TRX DMA + * write 0x284 bit[18] = 1'b0 + * write 0x301 = 0x00 + */ + if (release_mac_rx_pause) { + tmp = rtl_read_byte(rtlpriv, REG_RXDMA_CONTROL); + rtl_write_byte(rtlpriv, REG_RXDMA_CONTROL, + (tmp & (~BIT(2)))); + } + rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG + 1, + backup_pcie_dma_pause); + } + + /* 9. lock system register + * write 0xCC bit[2] = 1'b0 + */ + tmp = rtl_read_byte(rtlpriv, REG_PMC_DBG_CTRL2); + tmp &= ~(BIT(2)); + rtl_write_byte(rtlpriv, REG_PMC_DBG_CTRL2, tmp); +} + int rtl92ee_hw_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -1188,6 +1295,13 @@ int rtl92ee_hw_init(struct ieee80211_hw *hw) rtlhal->fw_ps_state = FW_PS_STATE_ALL_ON_92E; } + if (_rtl8192ee_check_pcie_dma_hang(rtlpriv)) { + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "92ee dma hang!\n"); + _rtl8192ee_reset_pcie_interface_dma(rtlpriv, + rtlhal->mac_func_enable); + rtlhal->mac_func_enable = false; + } + rtstatus = _rtl92ee_init_mac(hw); rtl_write_byte(rtlpriv, 0x577, 0x03); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/reg.h b/drivers/net/wireless/rtlwifi/rtl8192ee/reg.h index 3f2a9596e7cd..1eaa1fab550d 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/reg.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/reg.h @@ -77,9 +77,11 @@ #define REG_HIMRE 0x00B8 #define REG_HISRE 0x00BC +#define REG_PMC_DBG_CTRL2 0x00CC #define REG_EFUSE_ACCESS 0x00CF #define REG_HPON_FSM 0x00EC #define REG_SYS_CFG1 0x00F0 +#define REG_MAC_PHY_CTRL_NORMAL 0x00F8 #define REG_SYS_CFG2 0x00FC #define REG_CR 0x0100 diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/sw.c b/drivers/net/wireless/rtlwifi/rtl8192ee/sw.c index 9b5a7d5be121..c31c6bfb536d 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/sw.c @@ -113,8 +113,6 @@ int rtl92ee_init_sw_vars(struct ieee80211_hw *hw) RCR_HTC_LOC_CTRL | RCR_AMF | RCR_ACF | - RCR_ADF | - RCR_AICV | RCR_ACRC32 | RCR_AB | RCR_AM | @@ -241,6 +239,7 @@ static struct rtl_hal_ops rtl8192ee_hal_ops = { .set_desc = rtl92ee_set_desc, .get_desc = rtl92ee_get_desc, .is_tx_desc_closed = rtl92ee_is_tx_desc_closed, + .get_available_desc = rtl92ee_get_available_desc, .tx_polling = rtl92ee_tx_polling, .enable_hw_sec = rtl92ee_enable_hw_security_config, .set_key = rtl92ee_set_key, diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c index 2fcbef1d029f..d39ee67f6113 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.c @@ -47,164 +47,6 @@ static u8 _rtl92ee_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue) return skb->priority; } -/* mac80211's rate_idx is like this: - * - * 2.4G band:rx_status->band == IEEE80211_BAND_2GHZ - * - * B/G rate: - * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC92C_RATE1M-->DESC92C_RATE54M ==> idx is 0-->11, - * - * N rate: - * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15 - * - * 5G band:rx_status->band == IEEE80211_BAND_5GHZ - * A rate: - * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC92C_RATE6M-->DESC92C_RATE54M ==> idx is 0-->7, - * - * N rate: - * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15 - */ -static int _rtl92ee_rate_mapping(struct ieee80211_hw *hw, - bool isht, u8 desc_rate) -{ - int rate_idx; - - if (!isht) { - if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) { - switch (desc_rate) { - case DESC92C_RATE1M: - rate_idx = 0; - break; - case DESC92C_RATE2M: - rate_idx = 1; - break; - case DESC92C_RATE5_5M: - rate_idx = 2; - break; - case DESC92C_RATE11M: - rate_idx = 3; - break; - case DESC92C_RATE6M: - rate_idx = 4; - break; - case DESC92C_RATE9M: - rate_idx = 5; - break; - case DESC92C_RATE12M: - rate_idx = 6; - break; - case DESC92C_RATE18M: - rate_idx = 7; - break; - case DESC92C_RATE24M: - rate_idx = 8; - break; - case DESC92C_RATE36M: - rate_idx = 9; - break; - case DESC92C_RATE48M: - rate_idx = 10; - break; - case DESC92C_RATE54M: - rate_idx = 11; - break; - default: - rate_idx = 0; - break; - } - } else { - switch (desc_rate) { - case DESC92C_RATE6M: - rate_idx = 0; - break; - case DESC92C_RATE9M: - rate_idx = 1; - break; - case DESC92C_RATE12M: - rate_idx = 2; - break; - case DESC92C_RATE18M: - rate_idx = 3; - break; - case DESC92C_RATE24M: - rate_idx = 4; - break; - case DESC92C_RATE36M: - rate_idx = 5; - break; - case DESC92C_RATE48M: - rate_idx = 6; - break; - case DESC92C_RATE54M: - rate_idx = 7; - break; - default: - rate_idx = 0; - break; - } - } - } else { - switch (desc_rate) { - case DESC92C_RATEMCS0: - rate_idx = 0; - break; - case DESC92C_RATEMCS1: - rate_idx = 1; - break; - case DESC92C_RATEMCS2: - rate_idx = 2; - break; - case DESC92C_RATEMCS3: - rate_idx = 3; - break; - case DESC92C_RATEMCS4: - rate_idx = 4; - break; - case DESC92C_RATEMCS5: - rate_idx = 5; - break; - case DESC92C_RATEMCS6: - rate_idx = 6; - break; - case DESC92C_RATEMCS7: - rate_idx = 7; - break; - case DESC92C_RATEMCS8: - rate_idx = 8; - break; - case DESC92C_RATEMCS9: - rate_idx = 9; - break; - case DESC92C_RATEMCS10: - rate_idx = 10; - break; - case DESC92C_RATEMCS11: - rate_idx = 11; - break; - case DESC92C_RATEMCS12: - rate_idx = 12; - break; - case DESC92C_RATEMCS13: - rate_idx = 13; - break; - case DESC92C_RATEMCS14: - rate_idx = 14; - break; - case DESC92C_RATEMCS15: - rate_idx = 15; - break; - default: - rate_idx = 0; - break; - } - } - return rate_idx; -} - static void _rtl92ee_query_rxphystatus(struct ieee80211_hw *hw, struct rtl_stats *pstatus, u8 *pdesc, struct rx_fwinfo *p_drvinfo, @@ -345,8 +187,8 @@ static void _rtl92ee_query_rxphystatus(struct ieee80211_hw *hw, pstatus->recvsignalpower = rx_pwr_all; /* (3)EVM of HT rate */ - if (pstatus->rate >= DESC92C_RATEMCS8 && - pstatus->rate <= DESC92C_RATEMCS15) + if (pstatus->rate >= DESC_RATEMCS8 && + pstatus->rate <= DESC_RATEMCS15) max_spatial_stream = 2; else max_spatial_stream = 1; @@ -512,6 +354,10 @@ bool rtl92ee_rx_query_desc(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr; u32 phystatus = GET_RX_DESC_PHYST(pdesc); + if (GET_RX_STATUS_DESC_RPT_SEL(pdesc) == 0) + status->packet_report_type = NORMAL_RX; + else + status->packet_report_type = C2H_PACKET; status->length = (u16)GET_RX_DESC_PKT_LEN(pdesc); status->rx_drvinfo_size = (u8)GET_RX_DESC_DRV_INFO_SIZE(pdesc) * RX_DRV_INFO_SIZE_UNIT; @@ -576,9 +422,8 @@ bool rtl92ee_rx_query_desc(struct ieee80211_hw *hw, * are use (RX_FLAG_HT) * Notice: this is diff with windows define */ - rx_status->rate_idx = _rtl92ee_rate_mapping(hw, - status->is_ht, - status->rate); + rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht, + false, status->rate); rx_status->mactime = status->timestamp_low; if (phystatus) { @@ -654,14 +499,7 @@ u16 rtl92ee_rx_desc_buff_remained_cnt(struct ieee80211_hw *hw, u8 queue_index) if (!start_rx) return 0; - if ((last_read_point > (RX_DESC_NUM_92E / 2)) && - (read_point <= (RX_DESC_NUM_92E / 2))) { - remind_cnt = RX_DESC_NUM_92E - write_point; - } else { - remind_cnt = (read_point >= write_point) ? - (read_point - write_point) : - (RX_DESC_NUM_92E - write_point + read_point); - } + remind_cnt = calc_fifo_space(read_point, write_point); if (remind_cnt == 0) return 0; @@ -710,7 +548,7 @@ static u16 get_desc_addr_fr_q_idx(u16 queue_index) return desc_address; } -void rtl92ee_get_available_desc(struct ieee80211_hw *hw, u8 q_idx) +u16 rtl92ee_get_available_desc(struct ieee80211_hw *hw, u8 q_idx) { struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -723,12 +561,11 @@ void rtl92ee_get_available_desc(struct ieee80211_hw *hw, u8 q_idx) current_tx_read_point = (u16)((tmp_4byte >> 16) & 0x0fff); current_tx_write_point = (u16)((tmp_4byte) & 0x0fff); - point_diff = ((current_tx_read_point > current_tx_write_point) ? - (current_tx_read_point - current_tx_write_point) : - (TX_DESC_NUM_92E - current_tx_write_point + - current_tx_read_point)); + point_diff = calc_fifo_space(current_tx_read_point, + current_tx_write_point); rtlpci->tx_ring[q_idx].avl_desc = point_diff; + return point_diff; } void rtl92ee_pre_fill_tx_bd_desc(struct ieee80211_hw *hw, @@ -901,13 +738,13 @@ void rtl92ee_tx_fill_desc(struct ieee80211_hw *hw, } else { if (rtlpriv->ra.is_special_data) { ptcb_desc->use_driver_rate = true; - SET_TX_DESC_TX_RATE(pdesc, DESC92C_RATE11M); + SET_TX_DESC_TX_RATE(pdesc, DESC_RATE11M); } else { ptcb_desc->use_driver_rate = false; } } - if (ptcb_desc->hw_rate > DESC92C_RATEMCS0) + if (ptcb_desc->hw_rate > DESC_RATEMCS0) short_gi = (ptcb_desc->use_shortgi) ? 1 : 0; else short_gi = (ptcb_desc->use_shortpreamble) ? 1 : 0; @@ -927,7 +764,7 @@ void rtl92ee_tx_fill_desc(struct ieee80211_hw *hw, SET_TX_DESC_RTS_RATE(pdesc, ptcb_desc->rts_rate); SET_TX_DESC_RTS_SC(pdesc, ptcb_desc->rts_sc); SET_TX_DESC_RTS_SHORT(pdesc, - ((ptcb_desc->rts_rate <= DESC92C_RATE54M) ? + ((ptcb_desc->rts_rate <= DESC_RATE54M) ? (ptcb_desc->rts_use_shortpreamble ? 1 : 0) : (ptcb_desc->rts_use_shortgi ? 1 : 0))); @@ -1038,7 +875,7 @@ void rtl92ee_tx_fill_cmddesc(struct ieee80211_hw *hw, if (firstseg) SET_TX_DESC_OFFSET(pdesc, txdesc_len); - SET_TX_DESC_TX_RATE(pdesc, DESC92C_RATE1M); + SET_TX_DESC_TX_RATE(pdesc, DESC_RATE1M); SET_TX_DESC_SEQ(pdesc, 0); @@ -1207,8 +1044,7 @@ bool rtl92ee_is_tx_desc_closed(struct ieee80211_hw *hw, u8 hw_queue, u16 index) static u8 stop_report_cnt; struct rtl8192_tx_ring *ring = &rtlpci->tx_ring[hw_queue]; - /*checking Read/Write Point each interrupt wastes CPU */ - if (stop_report_cnt > 15 || !rtlpriv->link_info.busytraffic) { + { u16 point_diff = 0; u16 cur_tx_rp, cur_tx_wp; u32 tmpu32 = 0; diff --git a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.h b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.h index 6f9be1c7515c..8f78ac9e6040 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ee/trx.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ee/trx.h @@ -542,6 +542,8 @@ LE_BITS_TO_4BYTE(__pdesc+8, 12, 4) #define GET_RX_DESC_RX_IS_QOS(__pdesc) \ LE_BITS_TO_4BYTE(__pdesc+8, 16, 1) +#define GET_RX_STATUS_DESC_RPT_SEL(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 28, 1) #define GET_RX_DESC_RXMCS(__pdesc) \ LE_BITS_TO_4BYTE(__pdesc+12, 0, 7) @@ -591,10 +593,10 @@ do { \ } while (0) #define RTL92EE_RX_HAL_IS_CCK_RATE(rxmcs)\ - (rxmcs == DESC92C_RATE1M ||\ - rxmcs == DESC92C_RATE2M ||\ - rxmcs == DESC92C_RATE5_5M ||\ - rxmcs == DESC92C_RATE11M) + (rxmcs == DESC_RATE1M ||\ + rxmcs == DESC_RATE2M ||\ + rxmcs == DESC_RATE5_5M ||\ + rxmcs == DESC_RATE11M) #define IS_LITTLE_ENDIAN 1 @@ -829,7 +831,7 @@ void rtl92ee_rx_check_dma_ok(struct ieee80211_hw *hw, u8 *header_desc, u8 queue_index); u16 rtl92ee_rx_desc_buff_remained_cnt(struct ieee80211_hw *hw, u8 queue_index); -void rtl92ee_get_available_desc(struct ieee80211_hw *hw, u8 queue_index); +u16 rtl92ee_get_available_desc(struct ieee80211_hw *hw, u8 queue_index); void rtl92ee_pre_fill_tx_bd_desc(struct ieee80211_hw *hw, u8 *tx_bd_desc, u8 *desc, u8 queue_index, struct sk_buff *skb, dma_addr_t addr); diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/def.h b/drivers/net/wireless/rtlwifi/rtl8192se/def.h index 6e7a70b43949..ef87c09b77d0 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/def.h +++ b/drivers/net/wireless/rtlwifi/rtl8192se/def.h @@ -450,10 +450,10 @@ SHIFT_AND_MASK_LE(__pdesc + 24, 0, 32) #define SE_RX_HAL_IS_CCK_RATE(_pdesc)\ - (GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE1M || \ - GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE2M || \ - GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE5_5M ||\ - GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC92_RATE11M) + (GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC_RATE1M || \ + GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC_RATE2M || \ + GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC_RATE5_5M ||\ + GET_RX_STATUS_DESC_RX_MCS(_pdesc) == DESC_RATE11M) enum rf_optype { RF_OP_BY_SW_3WIRE = 0, diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/dm.c b/drivers/net/wireless/rtlwifi/rtl8192se/dm.c index b3a2d5ec59e6..575980b88658 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/dm.c @@ -29,6 +29,7 @@ #include "../wifi.h" #include "../base.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -469,7 +470,7 @@ static void _rtl92s_dm_initial_gain_sta_beforeconnect(struct ieee80211_hw *hw) if (digtable->backoff_enable_flag) rtl92s_backoff_enable_flag(hw); else - digtable->back_val = DM_DIG_BACKOFF; + digtable->back_val = DM_DIG_BACKOFF_MAX; if ((digtable->rssi_val + 10 - digtable->back_val) > digtable->rx_gain_max) @@ -503,7 +504,7 @@ static void _rtl92s_dm_initial_gain_sta_beforeconnect(struct ieee80211_hw *hw) digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX; rtl92s_phy_set_fw_cmd(hw, FW_CMD_DIG_ENABLE); - digtable->back_val = DM_DIG_BACKOFF; + digtable->back_val = DM_DIG_BACKOFF_MAX; digtable->cur_igvalue = rtlpriv->phy.default_initialgain[0]; digtable->pre_igvalue = 0; return; @@ -691,7 +692,7 @@ static void _rtl92s_dm_init_dig(struct ieee80211_hw *hw) /* for dig debug rssi value */ digtable->rssi_val = 50; - digtable->back_val = DM_DIG_BACKOFF; + digtable->back_val = DM_DIG_BACKOFF_MAX; digtable->rx_gain_max = DM_DIG_MAX; digtable->rx_gain_min = DM_DIG_MIN; diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/dm.h b/drivers/net/wireless/rtlwifi/rtl8192se/dm.h index 2e9052c8fe4b..de6ac796c74d 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8192se/dm.h @@ -54,24 +54,6 @@ enum dm_dig_sta { DM_STA_DIG_MAX }; -enum dm_dig_connect { - DIG_STA_DISCONNECT = 0, - DIG_STA_CONNECT = 1, - DIG_STA_BEFORE_CONNECT = 2, - DIG_AP_DISCONNECT = 3, - DIG_AP_CONNECT = 4, - DIG_AP_ADD_STATION = 5, - DIG_CONNECT_MAX -}; - -enum dm_dig_ext_port_alg { - DIG_EXT_PORT_STAGE_0 = 0, - DIG_EXT_PORT_STAGE_1 = 1, - DIG_EXT_PORT_STAGE_2 = 2, - DIG_EXT_PORT_STAGE_3 = 3, - DIG_EXT_PORT_STAGE_MAX = 4, -}; - enum dm_ratr_sta { DM_RATR_STA_HIGH = 0, DM_RATR_STA_MIDDLEHIGH = 1, @@ -99,22 +81,12 @@ enum dm_ratr_sta { #define TX_POWER_NEAR_FIELD_THRESH_LVL2 74 #define TX_POWER_NEAR_FIELD_THRESH_LVL1 67 -#define DM_DIG_THRESH_HIGH 40 -#define DM_DIG_THRESH_LOW 35 -#define DM_FALSEALARM_THRESH_LOW 40 -#define DM_FALSEALARM_THRESH_HIGH 1000 #define DM_DIG_HIGH_PWR_THRESH_HIGH 75 #define DM_DIG_HIGH_PWR_THRESH_LOW 70 -#define DM_DIG_BACKOFF 12 -#define DM_DIG_MAX 0x3e -#define DM_DIG_MIN 0x1c #define DM_DIG_MIN_Netcore 0x12 -#define DM_DIG_BACKOFF_MAX 12 -#define DM_DIG_BACKOFF_MIN -4 void rtl92s_dm_watchdog(struct ieee80211_hw *hw); void rtl92s_dm_init(struct ieee80211_hw *hw); void rtl92s_dm_init_edca_turbo(struct ieee80211_hw *hw); #endif - diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c index fb003868bdef..e1fd27c888bf 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/sw.c @@ -383,21 +383,21 @@ static struct rtl_hal_cfg rtl92se_hal_cfg = { .maps[RTL_IMR_ROK] = IMR_ROK, .maps[RTL_IBSS_INT_MASKS] = (IMR_BCNINT | IMR_TBDOK | IMR_TBDER), - .maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M, - .maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M, - .maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M, - .maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M, - .maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M, - .maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M, - .maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M, - .maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M, - .maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M, - .maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M, - .maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M, - .maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M, - - .maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7, - .maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15, + .maps[RTL_RC_CCK_RATE1M] = DESC_RATE1M, + .maps[RTL_RC_CCK_RATE2M] = DESC_RATE2M, + .maps[RTL_RC_CCK_RATE5_5M] = DESC_RATE5_5M, + .maps[RTL_RC_CCK_RATE11M] = DESC_RATE11M, + .maps[RTL_RC_OFDM_RATE6M] = DESC_RATE6M, + .maps[RTL_RC_OFDM_RATE9M] = DESC_RATE9M, + .maps[RTL_RC_OFDM_RATE12M] = DESC_RATE12M, + .maps[RTL_RC_OFDM_RATE18M] = DESC_RATE18M, + .maps[RTL_RC_OFDM_RATE24M] = DESC_RATE24M, + .maps[RTL_RC_OFDM_RATE36M] = DESC_RATE36M, + .maps[RTL_RC_OFDM_RATE48M] = DESC_RATE48M, + .maps[RTL_RC_OFDM_RATE54M] = DESC_RATE54M, + + .maps[RTL_RC_HT_RATEMCS7] = DESC_RATEMCS7, + .maps[RTL_RC_HT_RATEMCS15] = DESC_RATEMCS15, }; static struct pci_device_id rtl92se_pci_ids[] = { diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c index 672fd3b02835..125b29bd2f93 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c @@ -191,8 +191,8 @@ static void _rtl92se_query_rxphystatus(struct ieee80211_hw *hw, pstats->rxpower = rx_pwr_all; pstats->recvsignalpower = rx_pwr_all; - if (pstats->is_ht && pstats->rate >= DESC92_RATEMCS8 && - pstats->rate <= DESC92_RATEMCS15) + if (pstats->is_ht && pstats->rate >= DESC_RATEMCS8 && + pstats->rate <= DESC_RATEMCS15) max_spatial_stream = 2; else max_spatial_stream = 1; @@ -264,7 +264,6 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, struct rx_fwinfo *p_drvinfo; u32 phystatus = (u32)GET_RX_STATUS_DESC_PHY_STATUS(pdesc); struct ieee80211_hdr *hdr; - bool first_ampdu = false; stats->length = (u16)GET_RX_STATUS_DESC_PKT_LEN(pdesc); stats->rx_drvinfo_size = (u8)GET_RX_STATUS_DESC_DRVINFO_SIZE(pdesc) * 8; @@ -319,8 +318,8 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, rx_status->flag |= RX_FLAG_DECRYPTED; } - rx_status->rate_idx = rtlwifi_rate_mapping(hw, - stats->is_ht, stats->rate, first_ampdu); + rx_status->rate_idx = rtlwifi_rate_mapping(hw, stats->is_ht, + false, stats->rate); rx_status->mactime = stats->timestamp_low; if (phystatus) { @@ -394,14 +393,14 @@ void rtl92se_tx_fill_desc(struct ieee80211_hw *hw, SET_TX_DESC_RSVD_MACID(pdesc, reserved_macid); SET_TX_DESC_TXHT(pdesc, ((ptcb_desc->hw_rate >= - DESC92_RATEMCS0) ? 1 : 0)); + DESC_RATEMCS0) ? 1 : 0)); if (rtlhal->version == VERSION_8192S_ACUT) { - if (ptcb_desc->hw_rate == DESC92_RATE1M || - ptcb_desc->hw_rate == DESC92_RATE2M || - ptcb_desc->hw_rate == DESC92_RATE5_5M || - ptcb_desc->hw_rate == DESC92_RATE11M) { - ptcb_desc->hw_rate = DESC92_RATE12M; + if (ptcb_desc->hw_rate == DESC_RATE1M || + ptcb_desc->hw_rate == DESC_RATE2M || + ptcb_desc->hw_rate == DESC_RATE5_5M || + ptcb_desc->hw_rate == DESC_RATE11M) { + ptcb_desc->hw_rate = DESC_RATE12M; } } @@ -430,7 +429,7 @@ void rtl92se_tx_fill_desc(struct ieee80211_hw *hw, SET_TX_DESC_RTS_BANDWIDTH(pdesc, 0); SET_TX_DESC_RTS_SUB_CARRIER(pdesc, ptcb_desc->rts_sc); SET_TX_DESC_RTS_SHORT(pdesc, ((ptcb_desc->rts_rate <= - DESC92_RATE54M) ? + DESC_RATE54M) ? (ptcb_desc->rts_use_shortpreamble ? 1 : 0) : (ptcb_desc->rts_use_shortgi ? 1 : 0))); diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c index a0e86922780a..4c1c96c96a5a 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c @@ -26,6 +26,7 @@ #include "../wifi.h" #include "../base.h" #include "../pci.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -146,31 +147,6 @@ static const u8 cckswing_table_ch14[CCK_TABLE_SIZE][8] = { {0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00} }; -static void rtl8723e_dm_diginit(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct dig_t *dm_digtable = &rtlpriv->dm_digtable; - - dm_digtable->dig_enable_flag = true; - dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX; - dm_digtable->cur_igvalue = 0x20; - dm_digtable->pre_igvalue = 0x0; - dm_digtable->cursta_cstate = DIG_STA_DISCONNECT; - dm_digtable->presta_cstate = DIG_STA_DISCONNECT; - dm_digtable->curmultista_cstate = DIG_MULTISTA_DISCONNECT; - dm_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW; - dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH; - dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; - dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; - dm_digtable->rx_gain_max = DM_DIG_MAX; - dm_digtable->rx_gain_min = DM_DIG_MIN; - dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT; - dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX; - dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN; - dm_digtable->pre_cck_pd_state = CCK_PD_STAGE_MAX; - dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_MAX; -} - static u8 rtl8723e_dm_initial_gain_min_pwdb(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -395,30 +371,30 @@ static void rtl8723e_dm_cck_packet_detection_thresh(struct ieee80211_hw *hw) if (dm_digtable->cursta_cstate == DIG_STA_CONNECT) { dm_digtable->rssi_val_min = rtl8723e_dm_initial_gain_min_pwdb(hw); - if (dm_digtable->pre_cck_pd_state == CCK_PD_STAGE_LowRssi) { + if (dm_digtable->pre_cck_pd_state == CCK_PD_STAGE_LOWRSSI) { if (dm_digtable->rssi_val_min <= 25) dm_digtable->cur_cck_pd_state = - CCK_PD_STAGE_LowRssi; + CCK_PD_STAGE_LOWRSSI; else dm_digtable->cur_cck_pd_state = - CCK_PD_STAGE_HighRssi; + CCK_PD_STAGE_HIGHRSSI; } else { if (dm_digtable->rssi_val_min <= 20) dm_digtable->cur_cck_pd_state = - CCK_PD_STAGE_LowRssi; + CCK_PD_STAGE_LOWRSSI; else dm_digtable->cur_cck_pd_state = - CCK_PD_STAGE_HighRssi; + CCK_PD_STAGE_HIGHRSSI; } } else { dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_MAX; } if (dm_digtable->pre_cck_pd_state != dm_digtable->cur_cck_pd_state) { - if (dm_digtable->cur_cck_pd_state == CCK_PD_STAGE_LowRssi) { + if (dm_digtable->cur_cck_pd_state == CCK_PD_STAGE_LOWRSSI) { if (rtlpriv->falsealm_cnt.cnt_cck_fail > 800) dm_digtable->cur_cck_fa_state = - CCK_FA_STAGE_High; + CCK_FA_STAGE_HIGH; else dm_digtable->cur_cck_fa_state = CCK_FA_STAGE_LOW; @@ -818,7 +794,7 @@ void rtl8723e_dm_init(struct ieee80211_hw *hw) struct rtl_priv *rtlpriv = rtl_priv(hw); rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER; - rtl8723e_dm_diginit(hw); + rtl_dm_diginit(hw, 0x20); rtl8723_dm_init_dynamic_txpower(hw); rtl8723_dm_init_edca_turbo(hw); rtl8723e_dm_init_rate_adaptive_mask(hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h index 6fa0feb05f6d..57111052e86b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h @@ -42,25 +42,12 @@ #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 -#define DM_DIG_THRESH_HIGH 40 -#define DM_DIG_THRESH_LOW 35 - -#define DM_FALSEALARM_THRESH_LOW 400 -#define DM_FALSEALARM_THRESH_HIGH 1000 - -#define DM_DIG_MAX 0x3e -#define DM_DIG_MIN 0x1e - #define DM_DIG_FA_UPPER 0x32 #define DM_DIG_FA_LOWER 0x20 #define DM_DIG_FA_TH0 0x20 #define DM_DIG_FA_TH1 0x100 #define DM_DIG_FA_TH2 0x200 -#define DM_DIG_BACKOFF_MAX 12 -#define DM_DIG_BACKOFF_MIN -4 -#define DM_DIG_BACKOFF_DEFAULT 10 - #define RXPATHSELECTION_SS_TH_LOW 30 #define RXPATHSELECTION_DIFF_TH 18 @@ -108,14 +95,6 @@ enum tag_dynamic_init_gain_operation_type_definition { DIG_OP_TYPE_MAX }; -enum tag_cck_packet_detection_threshold_type_definition { - CCK_PD_STAGE_LowRssi = 0, - CCK_PD_STAGE_HighRssi = 1, - CCK_FA_STAGE_LOW = 2, - CCK_FA_STAGE_High = 3, - CCK_PD_STAGE_MAX = 4, -}; - enum dm_1r_cca_e { CCA_1R = 0, CCA_2R = 1, @@ -134,23 +113,6 @@ enum dm_sw_ant_switch_e { ANS_ANTENNA_MAX = 3, }; -enum dm_dig_ext_port_alg_e { - DIG_EXT_PORT_STAGE_0 = 0, - DIG_EXT_PORT_STAGE_1 = 1, - DIG_EXT_PORT_STAGE_2 = 2, - DIG_EXT_PORT_STAGE_3 = 3, - DIG_EXT_PORT_STAGE_MAX = 4, -}; - -enum dm_dig_connect_e { - DIG_STA_DISCONNECT = 0, - DIG_STA_CONNECT = 1, - DIG_STA_BEFORE_CONNECT = 2, - DIG_MULTISTA_DISCONNECT = 3, - DIG_MULTISTA_CONNECT = 4, - DIG_CONNECT_MAX -}; - #define BT_RSSI_STATE_NORMAL_POWER BIT_OFFSET_LEN_MASK_32(0, 1) #define BT_RSSI_STATE_AMDPU_OFF BIT_OFFSET_LEN_MASK_32(1, 1) #define BT_RSSI_STATE_SPECIAL_LOW BIT_OFFSET_LEN_MASK_32(2, 1) diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c index d372ccaf3465..2f7c144d7980 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c @@ -45,164 +45,6 @@ static u8 _rtl8723e_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue) return skb->priority; } -/* mac80211's rate_idx is like this: - * - * 2.4G band:rx_status->band == IEEE80211_BAND_2GHZ - * - * B/G rate: - * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC92C_RATE1M-->DESC92C_RATE54M ==> idx is 0-->11, - * - * N rate: - * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15 - * - * 5G band:rx_status->band == IEEE80211_BAND_5GHZ - * A rate: - * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC92C_RATE6M-->DESC92C_RATE54M ==> idx is 0-->7, - * - * N rate: - * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15 - */ -static int _rtl8723e_rate_mapping(struct ieee80211_hw *hw, - bool isht, u8 desc_rate) -{ - int rate_idx; - - if (!isht) { - if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) { - switch (desc_rate) { - case DESC92C_RATE1M: - rate_idx = 0; - break; - case DESC92C_RATE2M: - rate_idx = 1; - break; - case DESC92C_RATE5_5M: - rate_idx = 2; - break; - case DESC92C_RATE11M: - rate_idx = 3; - break; - case DESC92C_RATE6M: - rate_idx = 4; - break; - case DESC92C_RATE9M: - rate_idx = 5; - break; - case DESC92C_RATE12M: - rate_idx = 6; - break; - case DESC92C_RATE18M: - rate_idx = 7; - break; - case DESC92C_RATE24M: - rate_idx = 8; - break; - case DESC92C_RATE36M: - rate_idx = 9; - break; - case DESC92C_RATE48M: - rate_idx = 10; - break; - case DESC92C_RATE54M: - rate_idx = 11; - break; - default: - rate_idx = 0; - break; - } - } else { - switch (desc_rate) { - case DESC92C_RATE6M: - rate_idx = 0; - break; - case DESC92C_RATE9M: - rate_idx = 1; - break; - case DESC92C_RATE12M: - rate_idx = 2; - break; - case DESC92C_RATE18M: - rate_idx = 3; - break; - case DESC92C_RATE24M: - rate_idx = 4; - break; - case DESC92C_RATE36M: - rate_idx = 5; - break; - case DESC92C_RATE48M: - rate_idx = 6; - break; - case DESC92C_RATE54M: - rate_idx = 7; - break; - default: - rate_idx = 0; - break; - } - } - } else { - switch (desc_rate) { - case DESC92C_RATEMCS0: - rate_idx = 0; - break; - case DESC92C_RATEMCS1: - rate_idx = 1; - break; - case DESC92C_RATEMCS2: - rate_idx = 2; - break; - case DESC92C_RATEMCS3: - rate_idx = 3; - break; - case DESC92C_RATEMCS4: - rate_idx = 4; - break; - case DESC92C_RATEMCS5: - rate_idx = 5; - break; - case DESC92C_RATEMCS6: - rate_idx = 6; - break; - case DESC92C_RATEMCS7: - rate_idx = 7; - break; - case DESC92C_RATEMCS8: - rate_idx = 8; - break; - case DESC92C_RATEMCS9: - rate_idx = 9; - break; - case DESC92C_RATEMCS10: - rate_idx = 10; - break; - case DESC92C_RATEMCS11: - rate_idx = 11; - break; - case DESC92C_RATEMCS12: - rate_idx = 12; - break; - case DESC92C_RATEMCS13: - rate_idx = 13; - break; - case DESC92C_RATEMCS14: - rate_idx = 14; - break; - case DESC92C_RATEMCS15: - rate_idx = 15; - break; - default: - rate_idx = 0; - break; - } - } - return rate_idx; -} - static void _rtl8723e_query_rxphystatus(struct ieee80211_hw *hw, struct rtl_stats *pstatus, u8 *pdesc, struct rx_fwinfo_8723e *p_drvinfo, @@ -503,8 +345,8 @@ bool rtl8723e_rx_query_desc(struct ieee80211_hw *hw, * are use (RX_FLAG_HT) * Notice: this is diff with windows define */ - rx_status->rate_idx = _rtl8723e_rate_mapping(hw, - status->is_ht, status->rate); + rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht, + false, status->rate); rx_status->mactime = status->timestamp_low; if (phystatus == true) { diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/dm.c b/drivers/net/wireless/rtlwifi/rtl8723be/dm.c index dd7eb4371f49..2367e8f47a5b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8723be/dm.c @@ -26,6 +26,7 @@ #include "../wifi.h" #include "../base.h" #include "../pci.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -211,35 +212,6 @@ void rtl8723be_dm_txpower_track_adjust(struct ieee80211_hw *hw, u8 type, (pwr_val << 16) | (pwr_val << 24); } -static void rtl8723be_dm_diginit(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct dig_t *dm_digtable = &rtlpriv->dm_digtable; - - dm_digtable->dig_enable_flag = true; - dm_digtable->cur_igvalue = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f); - dm_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW; - dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH; - dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; - dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; - dm_digtable->rx_gain_max = DM_DIG_MAX; - dm_digtable->rx_gain_min = DM_DIG_MIN; - dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT; - dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX; - dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN; - dm_digtable->pre_cck_cca_thres = 0xff; - dm_digtable->cur_cck_cca_thres = 0x83; - dm_digtable->forbidden_igi = DM_DIG_MIN; - dm_digtable->large_fa_hit = 0; - dm_digtable->recover_cnt = 0; - dm_digtable->dig_dynamic_min = DM_DIG_MIN; - dm_digtable->dig_dynamic_min_1 = DM_DIG_MIN; - dm_digtable->media_connect_0 = false; - dm_digtable->media_connect_1 = false; - rtlpriv->dm.dm_initialgain_enable = true; - dm_digtable->bt30_cur_igi = 0x32; -} - void rtl8723be_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -293,9 +265,10 @@ static void rtl8723be_dm_init_dynamic_atc_switch(struct ieee80211_hw *hw) void rtl8723be_dm_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 cur_igvalue = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f); rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER; - rtl8723be_dm_diginit(hw); + rtl_dm_diginit(hw, cur_igvalue); rtl8723be_dm_init_rate_adaptive_mask(hw); rtl8723_dm_init_edca_turbo(hw); rtl8723_dm_init_dynamic_bb_powersaving(hw); @@ -424,7 +397,7 @@ static void rtl8723be_dm_dig(struct ieee80211_hw *hw) struct rtl_priv *rtlpriv = rtl_priv(hw); struct dig_t *dm_digtable = &rtlpriv->dm_digtable; struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); - u8 dig_dynamic_min, dig_maxofmin; + u8 dig_min_0, dig_maxofmin; bool bfirstconnect, bfirstdisconnect; u8 dm_dig_max, dm_dig_min; u8 current_igi = dm_digtable->cur_igvalue; @@ -434,7 +407,7 @@ static void rtl8723be_dm_dig(struct ieee80211_hw *hw) if (mac->act_scanning) return; - dig_dynamic_min = dm_digtable->dig_dynamic_min; + dig_min_0 = dm_digtable->dig_min_0; bfirstconnect = (mac->link_state >= MAC80211_LINKED) && !dm_digtable->media_connect_0; bfirstdisconnect = (mac->link_state < MAC80211_LINKED) && @@ -456,20 +429,20 @@ static void rtl8723be_dm_dig(struct ieee80211_hw *hw) if (rtlpriv->dm.one_entry_only) { offset = 12; if (dm_digtable->rssi_val_min - offset < dm_dig_min) - dig_dynamic_min = dm_dig_min; + dig_min_0 = dm_dig_min; else if (dm_digtable->rssi_val_min - offset > dig_maxofmin) - dig_dynamic_min = dig_maxofmin; + dig_min_0 = dig_maxofmin; else - dig_dynamic_min = + dig_min_0 = dm_digtable->rssi_val_min - offset; } else { - dig_dynamic_min = dm_dig_min; + dig_min_0 = dm_dig_min; } } else { dm_digtable->rx_gain_max = dm_dig_max; - dig_dynamic_min = dm_dig_min; + dig_min_0 = dm_dig_min; RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "no link\n"); } @@ -497,11 +470,11 @@ static void rtl8723be_dm_dig(struct ieee80211_hw *hw) } else { if (dm_digtable->large_fa_hit < 3) { if ((dm_digtable->forbidden_igi - 1) < - dig_dynamic_min) { + dig_min_0) { dm_digtable->forbidden_igi = - dig_dynamic_min; + dig_min_0; dm_digtable->rx_gain_min = - dig_dynamic_min; + dig_min_0; } else { dm_digtable->forbidden_igi--; dm_digtable->rx_gain_min = @@ -552,7 +525,7 @@ static void rtl8723be_dm_dig(struct ieee80211_hw *hw) rtl8723be_dm_write_dig(hw, current_igi); dm_digtable->media_connect_0 = ((mac->link_state >= MAC80211_LINKED) ? true : false); - dm_digtable->dig_dynamic_min = dig_dynamic_min; + dm_digtable->dig_min_0 = dig_min_0; } static void rtl8723be_dm_false_alarm_counter_statistics( diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/dm.h b/drivers/net/wireless/rtlwifi/rtl8723be/dm.h index e4c0e8ae6f47..f752a2cad63d 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8723be/dm.h @@ -180,28 +180,12 @@ #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 -#define DM_DIG_THRESH_HIGH 40 -#define DM_DIG_THRESH_LOW 35 - -#define DM_FALSEALARM_THRESH_LOW 400 -#define DM_FALSEALARM_THRESH_HIGH 1000 - -#define DM_DIG_MAX 0x3e -#define DM_DIG_MIN 0x1e - -#define DM_DIG_MAX_AP 0x32 -#define DM_DIG_MIN_AP 0x20 - #define DM_DIG_FA_UPPER 0x3e #define DM_DIG_FA_LOWER 0x1e #define DM_DIG_FA_TH0 0x200 #define DM_DIG_FA_TH1 0x300 #define DM_DIG_FA_TH2 0x400 -#define DM_DIG_BACKOFF_MAX 12 -#define DM_DIG_BACKOFF_MIN -4 -#define DM_DIG_BACKOFF_DEFAULT 10 - #define RXPATHSELECTION_SS_TH_LOW 30 #define RXPATHSELECTION_DIFF_TH 18 @@ -252,23 +236,6 @@ enum dm_sw_ant_switch_e { ANS_ANTENNA_MAX = 3, }; -enum dm_dig_ext_port_alg_e { - DIG_EXT_PORT_STAGE_0 = 0, - DIG_EXT_PORT_STAGE_1 = 1, - DIG_EXT_PORT_STAGE_2 = 2, - DIG_EXT_PORT_STAGE_3 = 3, - DIG_EXT_PORT_STAGE_MAX = 4, -}; - -enum dm_dig_connect_e { - DIG_STA_DISCONNECT = 0, - DIG_STA_CONNECT = 1, - DIG_STA_BEFORE_CONNECT = 2, - DIG_MULTISTA_DISCONNECT = 3, - DIG_MULTISTA_CONNECT = 4, - DIG_CONNECT_MAX -}; - enum pwr_track_control_method { BBSWING, TXAGC diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/phy.c b/drivers/net/wireless/rtlwifi/rtl8723be/phy.c index 20dcc25c506c..b7b73cbe346d 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8723be/phy.c @@ -874,31 +874,6 @@ void rtl8723be_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw) ROFDM0_RXDETECTOR3, rtlphy->framesync); } -void rtl8723be_phy_get_txpower_level(struct ieee80211_hw *hw, long *powerlevel) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct rtl_phy *rtlphy = &rtlpriv->phy; - u8 txpwr_level; - long txpwr_dbm; - - txpwr_level = rtlphy->cur_cck_txpwridx; - txpwr_dbm = rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_B, - txpwr_level); - txpwr_level = rtlphy->cur_ofdm24g_txpwridx; - if (rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G, txpwr_level) > - txpwr_dbm) - txpwr_dbm = - rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G, - txpwr_level); - txpwr_level = rtlphy->cur_ofdm24g_txpwridx; - if (rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G, - txpwr_level) > txpwr_dbm) - txpwr_dbm = - rtl8723_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G, - txpwr_level); - *powerlevel = txpwr_dbm; -} - static u8 _rtl8723be_phy_get_ratesection_intxpower_byrate(enum radio_path path, u8 rate) { diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/phy.h b/drivers/net/wireless/rtlwifi/rtl8723be/phy.h index 6339738a0e33..9021d4745ab7 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/phy.h +++ b/drivers/net/wireless/rtlwifi/rtl8723be/phy.h @@ -114,8 +114,6 @@ bool rtl8723be_phy_mac_config(struct ieee80211_hw *hw); bool rtl8723be_phy_bb_config(struct ieee80211_hw *hw); bool rtl8723be_phy_rf_config(struct ieee80211_hw *hw); void rtl8723be_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); -void rtl8723be_phy_get_txpower_level(struct ieee80211_hw *hw, - long *powerlevel); void rtl8723be_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel); void rtl8723be_phy_scan_operation_backup(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/sw.c b/drivers/net/wireless/rtlwifi/rtl8723be/sw.c index 223eb42992bd..1017f02d7bf7 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8723be/sw.c @@ -387,12 +387,14 @@ module_param_named(swlps, rtl8723be_mod_params.swctrl_lps, bool, 0444); module_param_named(fwlps, rtl8723be_mod_params.fwctrl_lps, bool, 0444); module_param_named(disable_watchdog, rtl8723be_mod_params.disable_watchdog, bool, 0444); -MODULE_PARM_DESC(swenc, "using hardware crypto (default 0 [hardware])\n"); -MODULE_PARM_DESC(ips, "using no link power save (default 1 is open)\n"); -MODULE_PARM_DESC(fwlps, "using linked fw control power save (default 1 is open)\n"); +MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); +MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n"); +MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n"); +MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n"); MODULE_PARM_DESC(msi, "Set to 1 to use MSI interrupts mode (default 0)\n"); MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); -MODULE_PARM_DESC(disable_watchdog, "Set to 1 to disable the watchdog (default 0)\n"); +MODULE_PARM_DESC(disable_watchdog, + "Set to 1 to disable the watchdog (default 0)\n"); static SIMPLE_DEV_PM_OPS(rtlwifi_pm_ops, rtl_pci_suspend, rtl_pci_resume); diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/trx.c b/drivers/net/wireless/rtlwifi/rtl8723be/trx.c index d6a1c70cb657..338ec9a9d09b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8723be/trx.c @@ -47,164 +47,6 @@ static u8 _rtl8723be_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue) return skb->priority; } -/* mac80211's rate_idx is like this: - * - * 2.4G band:rx_status->band == IEEE80211_BAND_2GHZ - * - * B/G rate: - * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC92C_RATE1M-->DESC92C_RATE54M ==> idx is 0-->11, - * - * N rate: - * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15 - * - * 5G band:rx_status->band == IEEE80211_BAND_5GHZ - * A rate: - * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC92C_RATE6M-->DESC92C_RATE54M ==> idx is 0-->7, - * - * N rate: - * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC92C_RATEMCS0-->DESC92C_RATEMCS15 ==> idx is 0-->15 - */ -static int _rtl8723be_rate_mapping(struct ieee80211_hw *hw, - bool isht, u8 desc_rate) -{ - int rate_idx; - - if (!isht) { - if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) { - switch (desc_rate) { - case DESC92C_RATE1M: - rate_idx = 0; - break; - case DESC92C_RATE2M: - rate_idx = 1; - break; - case DESC92C_RATE5_5M: - rate_idx = 2; - break; - case DESC92C_RATE11M: - rate_idx = 3; - break; - case DESC92C_RATE6M: - rate_idx = 4; - break; - case DESC92C_RATE9M: - rate_idx = 5; - break; - case DESC92C_RATE12M: - rate_idx = 6; - break; - case DESC92C_RATE18M: - rate_idx = 7; - break; - case DESC92C_RATE24M: - rate_idx = 8; - break; - case DESC92C_RATE36M: - rate_idx = 9; - break; - case DESC92C_RATE48M: - rate_idx = 10; - break; - case DESC92C_RATE54M: - rate_idx = 11; - break; - default: - rate_idx = 0; - break; - } - } else { - switch (desc_rate) { - case DESC92C_RATE6M: - rate_idx = 0; - break; - case DESC92C_RATE9M: - rate_idx = 1; - break; - case DESC92C_RATE12M: - rate_idx = 2; - break; - case DESC92C_RATE18M: - rate_idx = 3; - break; - case DESC92C_RATE24M: - rate_idx = 4; - break; - case DESC92C_RATE36M: - rate_idx = 5; - break; - case DESC92C_RATE48M: - rate_idx = 6; - break; - case DESC92C_RATE54M: - rate_idx = 7; - break; - default: - rate_idx = 0; - break; - } - } - } else { - switch (desc_rate) { - case DESC92C_RATEMCS0: - rate_idx = 0; - break; - case DESC92C_RATEMCS1: - rate_idx = 1; - break; - case DESC92C_RATEMCS2: - rate_idx = 2; - break; - case DESC92C_RATEMCS3: - rate_idx = 3; - break; - case DESC92C_RATEMCS4: - rate_idx = 4; - break; - case DESC92C_RATEMCS5: - rate_idx = 5; - break; - case DESC92C_RATEMCS6: - rate_idx = 6; - break; - case DESC92C_RATEMCS7: - rate_idx = 7; - break; - case DESC92C_RATEMCS8: - rate_idx = 8; - break; - case DESC92C_RATEMCS9: - rate_idx = 9; - break; - case DESC92C_RATEMCS10: - rate_idx = 10; - break; - case DESC92C_RATEMCS11: - rate_idx = 11; - break; - case DESC92C_RATEMCS12: - rate_idx = 12; - break; - case DESC92C_RATEMCS13: - rate_idx = 13; - break; - case DESC92C_RATEMCS14: - rate_idx = 14; - break; - case DESC92C_RATEMCS15: - rate_idx = 15; - break; - default: - rate_idx = 0; - break; - } - } - return rate_idx; -} - static void _rtl8723be_query_rxphystatus(struct ieee80211_hw *hw, struct rtl_stats *pstatus, u8 *pdesc, struct rx_fwinfo_8723be *p_drvinfo, @@ -558,8 +400,8 @@ bool rtl8723be_rx_query_desc(struct ieee80211_hw *hw, * supported rates or MCS index if HT rates * are use (RX_FLAG_HT) */ - rx_status->rate_idx = _rtl8723be_rate_mapping(hw, status->is_ht, - status->rate); + rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht, + false, status->rate); rx_status->mactime = status->timestamp_low; if (phystatus) { diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/def.h b/drivers/net/wireless/rtlwifi/rtl8821ae/def.h index a730985ae81d..ee7c208bd070 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/def.h +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/def.h @@ -373,60 +373,6 @@ enum rtl_desc_qsel { QSLT_CMD = 0x13, }; -enum rtl_desc8821ae_rate { - DESC_RATE1M = 0x00, - DESC_RATE2M = 0x01, - DESC_RATE5_5M = 0x02, - DESC_RATE11M = 0x03, - - DESC_RATE6M = 0x04, - DESC_RATE9M = 0x05, - DESC_RATE12M = 0x06, - DESC_RATE18M = 0x07, - DESC_RATE24M = 0x08, - DESC_RATE36M = 0x09, - DESC_RATE48M = 0x0a, - DESC_RATE54M = 0x0b, - - DESC_RATEMCS0 = 0x0c, - DESC_RATEMCS1 = 0x0d, - DESC_RATEMCS2 = 0x0e, - DESC_RATEMCS3 = 0x0f, - DESC_RATEMCS4 = 0x10, - DESC_RATEMCS5 = 0x11, - DESC_RATEMCS6 = 0x12, - DESC_RATEMCS7 = 0x13, - DESC_RATEMCS8 = 0x14, - DESC_RATEMCS9 = 0x15, - DESC_RATEMCS10 = 0x16, - DESC_RATEMCS11 = 0x17, - DESC_RATEMCS12 = 0x18, - DESC_RATEMCS13 = 0x19, - DESC_RATEMCS14 = 0x1a, - DESC_RATEMCS15 = 0x1b, - - DESC_RATEVHT1SS_MCS0 = 0x2c, - DESC_RATEVHT1SS_MCS1 = 0x2d, - DESC_RATEVHT1SS_MCS2 = 0x2e, - DESC_RATEVHT1SS_MCS3 = 0x2f, - DESC_RATEVHT1SS_MCS4 = 0x30, - DESC_RATEVHT1SS_MCS5 = 0x31, - DESC_RATEVHT1SS_MCS6 = 0x32, - DESC_RATEVHT1SS_MCS7 = 0x33, - DESC_RATEVHT1SS_MCS8 = 0x34, - DESC_RATEVHT1SS_MCS9 = 0x35, - DESC_RATEVHT2SS_MCS0 = 0x36, - DESC_RATEVHT2SS_MCS1 = 0x37, - DESC_RATEVHT2SS_MCS2 = 0x38, - DESC_RATEVHT2SS_MCS3 = 0x39, - DESC_RATEVHT2SS_MCS4 = 0x3a, - DESC_RATEVHT2SS_MCS5 = 0x3b, - DESC_RATEVHT2SS_MCS6 = 0x3c, - DESC_RATEVHT2SS_MCS7 = 0x3d, - DESC_RATEVHT2SS_MCS8 = 0x3e, - DESC_RATEVHT2SS_MCS9 = 0x3f, -}; - enum rx_packet_type { NORMAL_RX, TX_REPORT1, diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c index ba30b0d250fd..0b2082dc48f1 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.c @@ -26,6 +26,7 @@ #include "../wifi.h" #include "../base.h" #include "../pci.h" +#include "../core.h" #include "reg.h" #include "def.h" #include "phy.h" @@ -519,34 +520,6 @@ void rtl8821ae_dm_initialize_txpower_tracking_thermalmeter( } } -static void rtl8821ae_dm_diginit(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - struct dig_t *dm_digtable = &rtlpriv->dm_digtable; - - dm_digtable->cur_igvalue = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f); - dm_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW; - dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH; - dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; - dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; - dm_digtable->rx_gain_max = DM_DIG_MAX; - dm_digtable->rx_gain_min = DM_DIG_MIN; - dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT; - dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX; - dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN; - dm_digtable->pre_cck_cca_thres = 0xff; - dm_digtable->cur_cck_cca_thres = 0x83; - dm_digtable->forbidden_igi = DM_DIG_MIN; - dm_digtable->large_fa_hit = 0; - dm_digtable->recover_cnt = 0; - dm_digtable->dig_dynamic_min = DM_DIG_MIN; - dm_digtable->dig_dynamic_min_1 = DM_DIG_MIN; - dm_digtable->media_connect_0 = false; - dm_digtable->media_connect_1 = false; - rtlpriv->dm.dm_initialgain_enable = true; - dm_digtable->bt30_cur_igi = 0x32; -} - void rtl8821ae_dm_init_edca_turbo(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -606,6 +579,7 @@ void rtl8821ae_dm_init(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &rtlpriv->phy; + u32 cur_igvalue = rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f); spin_lock(&rtlpriv->locks.iqk_lock); rtlphy->lck_inprogress = false; @@ -613,7 +587,7 @@ void rtl8821ae_dm_init(struct ieee80211_hw *hw) rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER; rtl8821ae_dm_common_info_self_init(hw); - rtl8821ae_dm_diginit(hw); + rtl_dm_diginit(hw, cur_igvalue); rtl8821ae_dm_init_rate_adaptive_mask(hw); rtl8821ae_dm_init_edca_turbo(hw); rtl8821ae_dm_initialize_txpower_tracking_thermalmeter(hw); @@ -822,7 +796,7 @@ static void rtl8821ae_dm_dig(struct ieee80211_hw *hw) struct dig_t *dm_digtable = &rtlpriv->dm_digtable; struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); - u8 dig_dynamic_min; + u8 dig_min_0; u8 dig_max_of_min; bool first_connect, first_disconnect; u8 dm_dig_max, dm_dig_min, offset; @@ -837,7 +811,7 @@ static void rtl8821ae_dm_dig(struct ieee80211_hw *hw) } /*add by Neil Chen to avoid PSD is processing*/ - dig_dynamic_min = dm_digtable->dig_dynamic_min; + dig_min_0 = dm_digtable->dig_min_0; first_connect = (mac->link_state >= MAC80211_LINKED) && (!dm_digtable->media_connect_0); first_disconnect = (mac->link_state < MAC80211_LINKED) && @@ -876,23 +850,23 @@ static void rtl8821ae_dm_dig(struct ieee80211_hw *hw) offset = 0; if (dm_digtable->rssi_val_min - offset < dm_dig_min) - dig_dynamic_min = dm_dig_min; + dig_min_0 = dm_dig_min; else if (dm_digtable->rssi_val_min - offset > dig_max_of_min) - dig_dynamic_min = dig_max_of_min; + dig_min_0 = dig_max_of_min; else - dig_dynamic_min = + dig_min_0 = dm_digtable->rssi_val_min - offset; RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, - "bOneEntryOnly=TRUE, dig_dynamic_min=0x%x\n", - dig_dynamic_min); + "bOneEntryOnly=TRUE, dig_min_0=0x%x\n", + dig_min_0); } else { - dig_dynamic_min = dm_dig_min; + dig_min_0 = dm_dig_min; } } else { dm_digtable->rx_gain_max = dm_dig_max; - dig_dynamic_min = dm_dig_min; + dig_min_0 = dm_dig_min; RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "No Link\n"); } @@ -925,11 +899,11 @@ static void rtl8821ae_dm_dig(struct ieee80211_hw *hw) } else { if (dm_digtable->large_fa_hit < 3) { if ((dm_digtable->forbidden_igi - 1) < - dig_dynamic_min) { + dig_min_0) { dm_digtable->forbidden_igi = - dig_dynamic_min; + dig_min_0; dm_digtable->rx_gain_min = - dig_dynamic_min; + dig_min_0; RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, "Normal Case: At Lower Bound\n"); } else { @@ -1024,7 +998,7 @@ static void rtl8821ae_dm_dig(struct ieee80211_hw *hw) rtl8821ae_dm_write_dig(hw, current_igi); dm_digtable->media_connect_0 = ((mac->link_state >= MAC80211_LINKED) ? true : false); - dm_digtable->dig_dynamic_min = dig_dynamic_min; + dm_digtable->dig_min_0 = dig_min_0; } static void rtl8821ae_dm_common_info_self_update(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.h b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.h index 9dd40dd316c1..625a6bbb21fc 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/dm.h @@ -187,28 +187,12 @@ #define BW_AUTO_SWITCH_HIGH_LOW 25 #define BW_AUTO_SWITCH_LOW_HIGH 30 -#define DM_DIG_THRESH_HIGH 40 -#define DM_DIG_THRESH_LOW 35 - -#define DM_FALSEALARM_THRESH_LOW 400 -#define DM_FALSEALARM_THRESH_HIGH 1000 - -#define DM_DIG_MAX 0x3e -#define DM_DIG_MIN 0x1e - -#define DM_DIG_MAX_AP 0x32 -#define DM_DIG_MIN_AP 0x20 - #define DM_DIG_FA_UPPER 0x3e #define DM_DIG_FA_LOWER 0x1e #define DM_DIG_FA_TH0 200 #define DM_DIG_FA_TH1 0x300 #define DM_DIG_FA_TH2 0x400 -#define DM_DIG_BACKOFF_MAX 12 -#define DM_DIG_BACKOFF_MIN -4 -#define DM_DIG_BACKOFF_DEFAULT 10 - #define RXPATHSELECTION_SS_TH_LOW 30 #define RXPATHSELECTION_DIFF_TH 18 @@ -262,14 +246,6 @@ enum tag_dynamic_init_gain_operation_type_definition { DIG_OP_TYPE_MAX }; -enum tag_cck_packet_detection_threshold_type_definition { - CCK_PD_STAGE_LOWRSSI = 0, - CCK_PD_STAGE_HIGHRSSI = 1, - CCK_FA_STAGE_LOW = 2, - CCK_FA_STAGE_HIGH = 3, - CCK_PD_STAGE_MAX = 4, -}; - enum dm_1r_cca_e { CCA_1R = 0, CCA_2R = 1, @@ -288,23 +264,6 @@ enum dm_sw_ant_switch_e { ANS_ANTENNA_MAX = 3, }; -enum dm_dig_ext_port_alg_e { - DIG_EXT_PORT_STAGE_0 = 0, - DIG_EXT_PORT_STAGE_1 = 1, - DIG_EXT_PORT_STAGE_2 = 2, - DIG_EXT_PORT_STAGE_3 = 3, - DIG_EXT_PORT_STAGE_MAX = 4, -}; - -enum dm_dig_connect_e { - DIG_STA_DISCONNECT = 0, - DIG_STA_CONNECT = 1, - DIG_STA_BEFORE_CONNECT = 2, - DIG_MULTISTA_DISCONNECT = 3, - DIG_MULTISTA_CONNECT = 4, - DIG_CONNECT_MAX -}; - enum pwr_track_control_method { BBSWING, TXAGC, diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/pwrseq.h b/drivers/net/wireless/rtlwifi/rtl8821ae/pwrseq.h index bf0b0ce9519c..36b3e91d996e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/pwrseq.h +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/pwrseq.h @@ -93,9 +93,9 @@ #define RTL8812_TRANS_CARDEMU_TO_SUS \ {0x0042, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK,\ - PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xF0, 0xcc}, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xF0, 0xc0}, \ {0x0042, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK,\ - PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xF0, 0xEC}, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xF0, 0xE0}, \ {0x0043, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK,\ PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x07 \ /* gpio11 input mode, gpio10~8 output mode */}, \ diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c b/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c index fc92dd6a0d07..a4988121e1ab 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/sw.c @@ -85,52 +85,6 @@ static void rtl8821ae_init_aspm_vars(struct ieee80211_hw *hw) rtlpci->const_support_pciaspm = 1; } -static void load_wowlan_fw(struct rtl_priv *rtlpriv) -{ - /* callback routine to load wowlan firmware after main fw has - * been loaded - */ - const struct firmware *wowlan_firmware; - char *fw_name = NULL; - int err; - - /* for wowlan firmware buf */ - rtlpriv->rtlhal.wowlan_firmware = vzalloc(0x8000); - if (!rtlpriv->rtlhal.wowlan_firmware) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "Can't alloc buffer for wowlan fw.\n"); - return; - } - - if (rtlpriv->rtlhal.hw_type == HARDWARE_TYPE_RTL8821AE) - fw_name = "rtlwifi/rtl8821aefw_wowlan.bin"; - else - fw_name = "rtlwifi/rtl8812aefw_wowlan.bin"; - err = request_firmware(&wowlan_firmware, fw_name, rtlpriv->io.dev); - if (err) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "Failed to request wowlan firmware!\n"); - goto error; - } - - if (wowlan_firmware->size > 0x8000) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "Wowlan Firmware is too big!\n"); - goto error; - } - - memcpy(rtlpriv->rtlhal.wowlan_firmware, wowlan_firmware->data, - wowlan_firmware->size); - rtlpriv->rtlhal.wowlan_fwsize = wowlan_firmware->size; - release_firmware(wowlan_firmware); - - RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "WOWLAN FirmwareDownload OK\n"); - return; -error: - release_firmware(wowlan_firmware); - vfree(rtlpriv->rtlhal.wowlan_firmware); -} - /*InitializeVariables8812E*/ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw) { @@ -231,7 +185,6 @@ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw) else if (rtlpriv->psc.reg_fwctrl_lps == 3) rtlpriv->psc.fwctrl_psmode = FW_PS_DTIM_MODE; - rtlpriv->rtl_fw_second_cb = load_wowlan_fw; /* for firmware buf */ rtlpriv->rtlhal.pfirmware = vzalloc(0x8000); if (!rtlpriv->rtlhal.pfirmware) { @@ -239,20 +192,41 @@ int rtl8821ae_init_sw_vars(struct ieee80211_hw *hw) "Can't alloc buffer for fw.\n"); return 1; } + rtlpriv->rtlhal.wowlan_firmware = vzalloc(0x8000); + if (!rtlpriv->rtlhal.wowlan_firmware) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Can't alloc buffer for wowlan fw.\n"); + return 1; + } - if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) + if (rtlhal->hw_type == HARDWARE_TYPE_RTL8812AE) { rtlpriv->cfg->fw_name = "rtlwifi/rtl8812aefw.bin"; - else + rtlpriv->cfg->wowlan_fw_name = "rtlwifi/rtl8812aefw_wowlan.bin"; + } else { rtlpriv->cfg->fw_name = "rtlwifi/rtl8821aefw.bin"; + rtlpriv->cfg->wowlan_fw_name = "rtlwifi/rtl8821aefw_wowlan.bin"; + } rtlpriv->max_fw_size = 0x8000; + /*load normal firmware*/ pr_info("Using firmware %s\n", rtlpriv->cfg->fw_name); err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name, rtlpriv->io.dev, GFP_KERNEL, hw, rtl_fw_cb); if (err) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - "Failed to request firmware!\n"); + "Failed to request normal firmware!\n"); + return 1; + } + /*load wowlan firmware*/ + pr_info("Using firmware %s\n", rtlpriv->cfg->wowlan_fw_name); + err = request_firmware_nowait(THIS_MODULE, 1, + rtlpriv->cfg->wowlan_fw_name, + rtlpriv->io.dev, GFP_KERNEL, hw, + rtl_wowlan_fw_cb); + if (err) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Failed to request wowlan firmware!\n"); return 1; } return 0; diff --git a/drivers/net/wireless/rtlwifi/rtl8821ae/trx.c b/drivers/net/wireless/rtlwifi/rtl8821ae/trx.c index 383b86b05cba..72af4b9ee32b 100644 --- a/drivers/net/wireless/rtlwifi/rtl8821ae/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8821ae/trx.c @@ -48,232 +48,6 @@ static u8 _rtl8821ae_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue) return skb->priority; } -/* mac80211's rate_idx is like this: - * - * 2.4G band:rx_status->band == IEEE80211_BAND_2GHZ - * - * B/G rate: - * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC_RATE1M-->DESC_RATE54M ==> idx is 0-->11, - * - * N rate: - * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC_RATEMCS0-->DESC_RATEMCS15 ==> idx is 0-->15 - * - * 5G band:rx_status->band == IEEE80211_BAND_5GHZ - * A rate: - * (rx_status->flag & RX_FLAG_HT) = 0, - * DESC_RATE6M-->DESC_RATE54M ==> idx is 0-->7, - * - * N rate: - * (rx_status->flag & RX_FLAG_HT) = 1, - * DESC_RATEMCS0-->DESC_RATEMCS15 ==> idx is 0-->15 - */ -static int _rtl8821ae_rate_mapping(struct ieee80211_hw *hw, - bool isht, bool isvht, u8 desc_rate) -{ - int rate_idx; - - if (!isht) { - if (IEEE80211_BAND_2GHZ == hw->conf.chandef.chan->band) { - switch (desc_rate) { - case DESC_RATE1M: - rate_idx = 0; - break; - case DESC_RATE2M: - rate_idx = 1; - break; - case DESC_RATE5_5M: - rate_idx = 2; - break; - case DESC_RATE11M: - rate_idx = 3; - break; - case DESC_RATE6M: - rate_idx = 4; - break; - case DESC_RATE9M: - rate_idx = 5; - break; - case DESC_RATE12M: - rate_idx = 6; - break; - case DESC_RATE18M: - rate_idx = 7; - break; - case DESC_RATE24M: - rate_idx = 8; - break; - case DESC_RATE36M: - rate_idx = 9; - break; - case DESC_RATE48M: - rate_idx = 10; - break; - case DESC_RATE54M: - rate_idx = 11; - break; - default: - rate_idx = 0; - break; - } - } else { - switch (desc_rate) { - case DESC_RATE6M: - rate_idx = 0; - break; - case DESC_RATE9M: - rate_idx = 1; - break; - case DESC_RATE12M: - rate_idx = 2; - break; - case DESC_RATE18M: - rate_idx = 3; - break; - case DESC_RATE24M: - rate_idx = 4; - break; - case DESC_RATE36M: - rate_idx = 5; - break; - case DESC_RATE48M: - rate_idx = 6; - break; - case DESC_RATE54M: - rate_idx = 7; - break; - default: - rate_idx = 0; - break; - } - } - } else { - switch (desc_rate) { - case DESC_RATEMCS0: - rate_idx = 0; - break; - case DESC_RATEMCS1: - rate_idx = 1; - break; - case DESC_RATEMCS2: - rate_idx = 2; - break; - case DESC_RATEMCS3: - rate_idx = 3; - break; - case DESC_RATEMCS4: - rate_idx = 4; - break; - case DESC_RATEMCS5: - rate_idx = 5; - break; - case DESC_RATEMCS6: - rate_idx = 6; - break; - case DESC_RATEMCS7: - rate_idx = 7; - break; - case DESC_RATEMCS8: - rate_idx = 8; - break; - case DESC_RATEMCS9: - rate_idx = 9; - break; - case DESC_RATEMCS10: - rate_idx = 10; - break; - case DESC_RATEMCS11: - rate_idx = 11; - break; - case DESC_RATEMCS12: - rate_idx = 12; - break; - case DESC_RATEMCS13: - rate_idx = 13; - break; - case DESC_RATEMCS14: - rate_idx = 14; - break; - case DESC_RATEMCS15: - rate_idx = 15; - break; - default: - rate_idx = 0; - break; - } - } - - if (isvht) { - switch (desc_rate) { - case DESC_RATEVHT1SS_MCS0: - rate_idx = 0; - break; - case DESC_RATEVHT1SS_MCS1: - rate_idx = 1; - break; - case DESC_RATEVHT1SS_MCS2: - rate_idx = 2; - break; - case DESC_RATEVHT1SS_MCS3: - rate_idx = 3; - break; - case DESC_RATEVHT1SS_MCS4: - rate_idx = 4; - break; - case DESC_RATEVHT1SS_MCS5: - rate_idx = 5; - break; - case DESC_RATEVHT1SS_MCS6: - rate_idx = 6; - break; - case DESC_RATEVHT1SS_MCS7: - rate_idx = 7; - break; - case DESC_RATEVHT1SS_MCS8: - rate_idx = 8; - break; - case DESC_RATEVHT1SS_MCS9: - rate_idx = 9; - break; - case DESC_RATEVHT2SS_MCS0: - rate_idx = 0; - break; - case DESC_RATEVHT2SS_MCS1: - rate_idx = 1; - break; - case DESC_RATEVHT2SS_MCS2: - rate_idx = 2; - break; - case DESC_RATEVHT2SS_MCS3: - rate_idx = 3; - break; - case DESC_RATEVHT2SS_MCS4: - rate_idx = 4; - break; - case DESC_RATEVHT2SS_MCS5: - rate_idx = 5; - break; - case DESC_RATEVHT2SS_MCS6: - rate_idx = 6; - break; - case DESC_RATEVHT2SS_MCS7: - rate_idx = 7; - break; - case DESC_RATEVHT2SS_MCS8: - rate_idx = 8; - break; - case DESC_RATEVHT2SS_MCS9: - rate_idx = 9; - break; - default: - rate_idx = 0; - break; - } - } - return rate_idx; -} - static u16 odm_cfo(char value) { int ret_val; @@ -766,9 +540,9 @@ bool rtl8821ae_rx_query_desc(struct ieee80211_hw *hw, * supported rates or MCS index if HT rates * are use (RX_FLAG_HT) */ - rx_status->rate_idx = - _rtl8821ae_rate_mapping(hw, status->is_ht, - status->is_vht, status->rate); + rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht, + status->is_vht, + status->rate); rx_status->mactime = status->timestamp_low; if (phystatus) { diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h index 6866dcf24340..51572912c53d 100644 --- a/drivers/net/wireless/rtlwifi/wifi.h +++ b/drivers/net/wireless/rtlwifi/wifi.h @@ -331,10 +331,10 @@ enum hardware_type { (IS_HARDWARE_TYPE_8723E(rtlhal) || IS_HARDWARE_TYPE_8723U(rtlhal)) #define RX_HAL_IS_CCK_RATE(rxmcs) \ - ((rxmcs) == DESC92_RATE1M || \ - (rxmcs) == DESC92_RATE2M || \ - (rxmcs) == DESC92_RATE5_5M || \ - (rxmcs) == DESC92_RATE11M) + ((rxmcs) == DESC_RATE1M || \ + (rxmcs) == DESC_RATE2M || \ + (rxmcs) == DESC_RATE5_5M || \ + (rxmcs) == DESC_RATE11M) enum scan_operation_backup_opt { SCAN_OPT_BACKUP = 0, @@ -579,38 +579,59 @@ enum rtl_hal_state { }; enum rtl_desc92_rate { - DESC92_RATE1M = 0x00, - DESC92_RATE2M = 0x01, - DESC92_RATE5_5M = 0x02, - DESC92_RATE11M = 0x03, - - DESC92_RATE6M = 0x04, - DESC92_RATE9M = 0x05, - DESC92_RATE12M = 0x06, - DESC92_RATE18M = 0x07, - DESC92_RATE24M = 0x08, - DESC92_RATE36M = 0x09, - DESC92_RATE48M = 0x0a, - DESC92_RATE54M = 0x0b, - - DESC92_RATEMCS0 = 0x0c, - DESC92_RATEMCS1 = 0x0d, - DESC92_RATEMCS2 = 0x0e, - DESC92_RATEMCS3 = 0x0f, - DESC92_RATEMCS4 = 0x10, - DESC92_RATEMCS5 = 0x11, - DESC92_RATEMCS6 = 0x12, - DESC92_RATEMCS7 = 0x13, - DESC92_RATEMCS8 = 0x14, - DESC92_RATEMCS9 = 0x15, - DESC92_RATEMCS10 = 0x16, - DESC92_RATEMCS11 = 0x17, - DESC92_RATEMCS12 = 0x18, - DESC92_RATEMCS13 = 0x19, - DESC92_RATEMCS14 = 0x1a, - DESC92_RATEMCS15 = 0x1b, - DESC92_RATEMCS15_SG = 0x1c, - DESC92_RATEMCS32 = 0x20, + DESC_RATE1M = 0x00, + DESC_RATE2M = 0x01, + DESC_RATE5_5M = 0x02, + DESC_RATE11M = 0x03, + + DESC_RATE6M = 0x04, + DESC_RATE9M = 0x05, + DESC_RATE12M = 0x06, + DESC_RATE18M = 0x07, + DESC_RATE24M = 0x08, + DESC_RATE36M = 0x09, + DESC_RATE48M = 0x0a, + DESC_RATE54M = 0x0b, + + DESC_RATEMCS0 = 0x0c, + DESC_RATEMCS1 = 0x0d, + DESC_RATEMCS2 = 0x0e, + DESC_RATEMCS3 = 0x0f, + DESC_RATEMCS4 = 0x10, + DESC_RATEMCS5 = 0x11, + DESC_RATEMCS6 = 0x12, + DESC_RATEMCS7 = 0x13, + DESC_RATEMCS8 = 0x14, + DESC_RATEMCS9 = 0x15, + DESC_RATEMCS10 = 0x16, + DESC_RATEMCS11 = 0x17, + DESC_RATEMCS12 = 0x18, + DESC_RATEMCS13 = 0x19, + DESC_RATEMCS14 = 0x1a, + DESC_RATEMCS15 = 0x1b, + DESC_RATEMCS15_SG = 0x1c, + DESC_RATEMCS32 = 0x20, + + DESC_RATEVHT1SS_MCS0 = 0x2c, + DESC_RATEVHT1SS_MCS1 = 0x2d, + DESC_RATEVHT1SS_MCS2 = 0x2e, + DESC_RATEVHT1SS_MCS3 = 0x2f, + DESC_RATEVHT1SS_MCS4 = 0x30, + DESC_RATEVHT1SS_MCS5 = 0x31, + DESC_RATEVHT1SS_MCS6 = 0x32, + DESC_RATEVHT1SS_MCS7 = 0x33, + DESC_RATEVHT1SS_MCS8 = 0x34, + DESC_RATEVHT1SS_MCS9 = 0x35, + DESC_RATEVHT2SS_MCS0 = 0x36, + DESC_RATEVHT2SS_MCS1 = 0x37, + DESC_RATEVHT2SS_MCS2 = 0x38, + DESC_RATEVHT2SS_MCS3 = 0x39, + DESC_RATEVHT2SS_MCS4 = 0x3a, + DESC_RATEVHT2SS_MCS5 = 0x3b, + DESC_RATEVHT2SS_MCS6 = 0x3c, + DESC_RATEVHT2SS_MCS7 = 0x3d, + DESC_RATEVHT2SS_MCS8 = 0x3e, + DESC_RATEVHT2SS_MCS9 = 0x3f, }; enum rtl_var_map { @@ -2161,6 +2182,7 @@ struct rtl_hal_ops { void (*add_wowlan_pattern)(struct ieee80211_hw *hw, struct rtl_wow_pattern *rtl_pattern, u8 index); + u16 (*get_available_desc)(struct ieee80211_hw *hw, u8 q_idx); }; struct rtl_intf_ops { @@ -2242,6 +2264,7 @@ struct rtl_hal_cfg { char *name; char *fw_name; char *alt_fw_name; + char *wowlan_fw_name; struct rtl_hal_ops *ops; struct rtl_mod_params *mod_params; struct rtl_hal_usbint_cfg *usb_interface_cfg; @@ -2390,8 +2413,6 @@ struct dig_t { u8 pre_ccastate; u8 cur_ccasate; u8 large_fa_hit; - u8 dig_dynamic_min; - u8 dig_dynamic_min_1; u8 forbidden_igi; u8 dig_state; u8 dig_highpwrstate; @@ -2518,8 +2539,6 @@ struct proxim { struct rtl_priv { struct ieee80211_hw *hw; - /* Used to load a second firmware */ - void (*rtl_fw_second_cb)(struct rtl_priv *rtlpriv); struct completion firmware_loading_complete; struct list_head list; struct rtl_priv *buddy_priv; diff --git a/drivers/net/wireless/ti/wl1251/main.c b/drivers/net/wireless/ti/wl1251/main.c index 0b30a7b4d663..d4ba009ac9aa 100644 --- a/drivers/net/wireless/ti/wl1251/main.c +++ b/drivers/net/wireless/ti/wl1251/main.c @@ -500,6 +500,7 @@ static int wl1251_op_add_interface(struct ieee80211_hw *hw, int ret = 0; vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_UAPSD | IEEE80211_VIF_SUPPORTS_CQM_RSSI; wl1251_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", @@ -1480,9 +1481,7 @@ int wl1251_init_ieee80211(struct wl1251 *wl) /* unit us */ /* FIXME: find a proper value */ - wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_SUPPORTS_PS | - IEEE80211_HW_SUPPORTS_UAPSD; + wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS; wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC); diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index d6d0d6d9c7a8..144d1f8ba473 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -250,6 +250,7 @@ static struct wlcore_conf wl12xx_conf = { .keep_alive_interval = 55000, .max_listen_interval = 20, .sta_sleep_auth = WL1271_PSM_ILLEGAL, + .suspend_rx_ba_activity = 0, }, .itrim = { .enable = false, @@ -1728,6 +1729,9 @@ static struct wlcore_ops wl12xx_ops = { .convert_hwaddr = wl12xx_convert_hwaddr, .lnk_high_prio = wl12xx_lnk_high_prio, .lnk_low_prio = wl12xx_lnk_low_prio, + .interrupt_notify = NULL, + .rx_ba_filter = NULL, + .ap_sleep = NULL, }; static struct ieee80211_sta_ht_cap wl12xx_ht_cap = { diff --git a/drivers/net/wireless/ti/wl18xx/acx.c b/drivers/net/wireless/ti/wl18xx/acx.c index a169bb5a5dbf..67f2a0eec854 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.c +++ b/drivers/net/wireless/ti/wl18xx/acx.c @@ -24,6 +24,7 @@ #include "../wlcore/acx.h" #include "acx.h" +#include "wl18xx.h" int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, u32 sdio_blk_size, u32 extra_mem_blks, @@ -194,3 +195,90 @@ out: kfree(acx); return ret; } + +/* + * When the host is suspended, we don't want to get any fast-link/PSM + * notifications + */ +int wl18xx_acx_interrupt_notify_config(struct wl1271 *wl, + bool action) +{ + struct wl18xx_acx_interrupt_notify *acx; + int ret = 0; + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->enable = action; + ret = wl1271_cmd_configure(wl, ACX_INTERRUPT_NOTIFY, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx interrupt notify setting failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +/* + * When the host is suspended, we can configure the FW to disable RX BA + * notifications. + */ +int wl18xx_acx_rx_ba_filter(struct wl1271 *wl, bool action) +{ + struct wl18xx_acx_rx_ba_filter *acx; + int ret = 0; + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->enable = (u32)action; + ret = wl1271_cmd_configure(wl, ACX_RX_BA_FILTER, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx rx ba activity filter setting failed: %d", + ret); + goto out; + } + +out: + kfree(acx); + return ret; +} + +int wl18xx_acx_ap_sleep(struct wl1271 *wl) +{ + struct wl18xx_priv *priv = wl->priv; + struct acx_ap_sleep_cfg *acx; + struct conf_ap_sleep_settings *conf = &priv->conf.ap_sleep; + int ret; + + wl1271_debug(DEBUG_ACX, "acx config ap sleep"); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) { + ret = -ENOMEM; + goto out; + } + + acx->idle_duty_cycle = conf->idle_duty_cycle; + acx->connected_duty_cycle = conf->connected_duty_cycle; + acx->max_stations_thresh = conf->max_stations_thresh; + acx->idle_conn_thresh = conf->idle_conn_thresh; + + ret = wl1271_cmd_configure(wl, ACX_AP_SLEEP_CFG, acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx config ap-sleep failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} diff --git a/drivers/net/wireless/ti/wl18xx/acx.h b/drivers/net/wireless/ti/wl18xx/acx.h index 0e636def1217..4afccd4b9467 100644 --- a/drivers/net/wireless/ti/wl18xx/acx.h +++ b/drivers/net/wireless/ti/wl18xx/acx.h @@ -32,7 +32,10 @@ enum { ACX_SIM_CONFIG = 0x0053, ACX_CLEAR_STATISTICS = 0x0054, ACX_AUTO_RX_STREAMING = 0x0055, - ACX_PEER_CAP = 0x0056 + ACX_PEER_CAP = 0x0056, + ACX_INTERRUPT_NOTIFY = 0x0057, + ACX_RX_BA_FILTER = 0x0058, + ACX_AP_SLEEP_CFG = 0x0059 }; /* numbers of bits the length field takes (add 1 for the actual number) */ @@ -326,6 +329,44 @@ struct wlcore_acx_peer_cap { u8 padding; } __packed; +/* + * ACX_INTERRUPT_NOTIFY + * enable/disable fast-link/PSM notification from FW + */ +struct wl18xx_acx_interrupt_notify { + struct acx_header header; + u32 enable; +}; + +/* + * ACX_RX_BA_FILTER + * enable/disable RX BA filtering in FW + */ +struct wl18xx_acx_rx_ba_filter { + struct acx_header header; + u32 enable; +}; + +struct acx_ap_sleep_cfg { + struct acx_header header; + /* Duty Cycle (20-80% of staying Awake) for IDLE AP + * (0: disable) + */ + u8 idle_duty_cycle; + /* Duty Cycle (20-80% of staying Awake) for Connected AP + * (0: disable) + */ + u8 connected_duty_cycle; + /* Maximum stations that are allowed to be connected to AP + * (255: no limit) + */ + u8 max_stations_thresh; + /* Timeout till enabling the Sleep Mechanism after data stops + * [unit: 100 msec] + */ + u8 idle_conn_thresh; +} __packed; + int wl18xx_acx_host_if_cfg_bitmap(struct wl1271 *wl, u32 host_cfg_bitmap, u32 sdio_blk_size, u32 extra_mem_blks, u32 len_field_size); @@ -336,5 +377,8 @@ int wl18xx_acx_set_peer_cap(struct wl1271 *wl, struct ieee80211_sta_ht_cap *ht_cap, bool allow_ht_operation, u32 rate_set, u8 hlid); +int wl18xx_acx_interrupt_notify_config(struct wl1271 *wl, bool action); +int wl18xx_acx_rx_ba_filter(struct wl1271 *wl, bool action); +int wl18xx_acx_ap_sleep(struct wl1271 *wl); #endif /* __WL18XX_ACX_H__ */ diff --git a/drivers/net/wireless/ti/wl18xx/cmd.c b/drivers/net/wireless/ti/wl18xx/cmd.c index 44f0b205b065..a8d176ddc73c 100644 --- a/drivers/net/wireless/ti/wl18xx/cmd.c +++ b/drivers/net/wireless/ti/wl18xx/cmd.c @@ -33,7 +33,8 @@ int wl18xx_cmd_channel_switch(struct wl1271 *wl, u32 supported_rates; int ret; - wl1271_debug(DEBUG_ACX, "cmd channel switch"); + wl1271_debug(DEBUG_ACX, "cmd channel switch (count=%d)", + ch_switch->count); cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { @@ -60,8 +61,12 @@ int wl18xx_cmd_channel_switch(struct wl1271 *wl, goto out_free; } - supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES | - wlcore_hw_sta_get_ap_rate_mask(wl, wlvif); + supported_rates = CONF_TX_ENABLED_RATES | CONF_TX_MCS_RATES; + if (wlvif->bss_type == BSS_TYPE_STA_BSS) + supported_rates |= wlcore_hw_sta_get_ap_rate_mask(wl, wlvif); + else + supported_rates |= + wlcore_hw_ap_get_mimo_wide_rate_mask(wl, wlvif); if (wlvif->p2p) supported_rates &= ~CONF_TX_CCK_RATES; cmd->local_supported_rates = cpu_to_le32(supported_rates); @@ -167,3 +172,85 @@ out_free: out: return ret; } + +int wl18xx_cmd_set_cac(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool start) +{ + struct wlcore_cmd_cac_start *cmd; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd cac (channel %d) %s", + wlvif->channel, start ? "start" : "stop"); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->role_id = wlvif->role_id; + cmd->channel = wlvif->channel; + if (wlvif->band == IEEE80211_BAND_5GHZ) + cmd->band = WLCORE_BAND_5GHZ; + cmd->bandwidth = wlcore_get_native_channel_type(wlvif->channel_type); + + ret = wl1271_cmd_send(wl, + start ? CMD_CAC_START : CMD_CAC_STOP, + cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to send cac command"); + goto out_free; + } + +out_free: + kfree(cmd); + return ret; +} + +int wl18xx_cmd_radar_detection_debug(struct wl1271 *wl, u8 channel) +{ + struct wl18xx_cmd_dfs_radar_debug *cmd; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd radar detection debug (chan %d)", + channel); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->channel = channel; + + ret = wl1271_cmd_send(wl, CMD_DFS_RADAR_DETECTION_DEBUG, + cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to send radar detection debug command"); + goto out_free; + } + +out_free: + kfree(cmd); + return ret; +} + +int wl18xx_cmd_dfs_master_restart(struct wl1271 *wl, struct wl12xx_vif *wlvif) +{ + struct wl18xx_cmd_dfs_master_restart *cmd; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd dfs master restart (role %d)", + wlvif->role_id); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->role_id = wlvif->role_id; + + ret = wl1271_cmd_send(wl, CMD_DFS_MASTER_RESTART, + cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to send dfs master restart command"); + goto out_free; + } +out_free: + kfree(cmd); + return ret; +} diff --git a/drivers/net/wireless/ti/wl18xx/cmd.h b/drivers/net/wireless/ti/wl18xx/cmd.h index 92499e2dfa83..7f9440a2bff8 100644 --- a/drivers/net/wireless/ti/wl18xx/cmd.h +++ b/drivers/net/wireless/ti/wl18xx/cmd.h @@ -59,6 +59,30 @@ struct wl18xx_cmd_smart_config_set_group_key { u8 key[16]; } __packed; +struct wl18xx_cmd_dfs_radar_debug { + struct wl1271_cmd_header header; + + u8 channel; + u8 padding[3]; +} __packed; + +struct wl18xx_cmd_dfs_master_restart { + struct wl1271_cmd_header header; + + u8 role_id; + u8 padding[3]; +} __packed; + +/* cac_start and cac_stop share the same params */ +struct wlcore_cmd_cac_start { + struct wl1271_cmd_header header; + + u8 role_id; + u8 channel; + u8 band; + u8 bandwidth; +} __packed; + int wl18xx_cmd_channel_switch(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_channel_switch *ch_switch); @@ -66,4 +90,7 @@ int wl18xx_cmd_smart_config_start(struct wl1271 *wl, u32 group_bitmap); int wl18xx_cmd_smart_config_stop(struct wl1271 *wl); int wl18xx_cmd_smart_config_set_group_key(struct wl1271 *wl, u16 group_id, u8 key_len, u8 *key); +int wl18xx_cmd_set_cac(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool start); +int wl18xx_cmd_radar_detection_debug(struct wl1271 *wl, u8 channel); +int wl18xx_cmd_dfs_master_restart(struct wl1271 *wl, struct wl12xx_vif *wlvif); #endif diff --git a/drivers/net/wireless/ti/wl18xx/conf.h b/drivers/net/wireless/ti/wl18xx/conf.h index e34302e3b51d..71f1ec448ba5 100644 --- a/drivers/net/wireless/ti/wl18xx/conf.h +++ b/drivers/net/wireless/ti/wl18xx/conf.h @@ -23,7 +23,7 @@ #define __WL18XX_CONF_H__ #define WL18XX_CONF_MAGIC 0x10e100ca -#define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0006) +#define WL18XX_CONF_VERSION (WLCORE_CONF_VERSION | 0x0007) #define WL18XX_CONF_MASK 0x0000ffff #define WL18XX_CONF_SIZE (WLCORE_CONF_SIZE + \ sizeof(struct wl18xx_priv_conf)) @@ -110,12 +110,33 @@ struct wl18xx_ht_settings { u8 mode; } __packed; +struct conf_ap_sleep_settings { + /* Duty Cycle (20-80% of staying Awake) for IDLE AP + * (0: disable) + */ + u8 idle_duty_cycle; + /* Duty Cycle (20-80% of staying Awake) for Connected AP + * (0: disable) + */ + u8 connected_duty_cycle; + /* Maximum stations that are allowed to be connected to AP + * (255: no limit) + */ + u8 max_stations_thresh; + /* Timeout till enabling the Sleep Mechanism after data stops + * [unit: 100 msec] + */ + u8 idle_conn_thresh; +} __packed; + struct wl18xx_priv_conf { /* Module params structures */ struct wl18xx_ht_settings ht; /* this structure is copied wholesale to FW */ struct wl18xx_mac_and_phy_params phy; + + struct conf_ap_sleep_settings ap_sleep; } __packed; #endif /* __WL18XX_CONF_H__ */ diff --git a/drivers/net/wireless/ti/wl18xx/debugfs.c b/drivers/net/wireless/ti/wl18xx/debugfs.c index 7f1669cdea09..c93fae95baac 100644 --- a/drivers/net/wireless/ti/wl18xx/debugfs.c +++ b/drivers/net/wireless/ti/wl18xx/debugfs.c @@ -22,9 +22,12 @@ #include "../wlcore/debugfs.h" #include "../wlcore/wlcore.h" +#include "../wlcore/debug.h" +#include "../wlcore/ps.h" #include "wl18xx.h" #include "acx.h" +#include "cmd.h" #include "debugfs.h" #define WL18XX_DEBUGFS_FWSTATS_FILE(a, b, c) \ @@ -239,6 +242,45 @@ static const struct file_operations clear_fw_stats_ops = { .llseek = default_llseek, }; +static ssize_t radar_detection_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wl1271 *wl = file->private_data; + int ret; + u8 channel; + + ret = kstrtou8_from_user(user_buf, count, 10, &channel); + if (ret < 0) { + wl1271_warning("illegal channel"); + return -EINVAL; + } + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) + goto out; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + ret = wl18xx_cmd_radar_detection_debug(wl, channel); + if (ret < 0) + count = ret; + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); + return count; +} + +static const struct file_operations radar_detection_ops = { + .write = radar_detection_write, + .open = simple_open, + .llseek = default_llseek, +}; + int wl18xx_debugfs_add_files(struct wl1271 *wl, struct dentry *rootdir) { @@ -390,6 +432,7 @@ int wl18xx_debugfs_add_files(struct wl1271 *wl, DEBUGFS_FWSTATS_ADD(mem, fw_gen_free_mem_blks); DEBUGFS_ADD(conf, moddir); + DEBUGFS_ADD(radar_detection, moddir); return 0; diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c index eb1848e08424..c28f06854195 100644 --- a/drivers/net/wireless/ti/wl18xx/event.c +++ b/drivers/net/wireless/ti/wl18xx/event.c @@ -47,6 +47,19 @@ int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event, return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout); } +static const char *wl18xx_radar_type_decode(u8 radar_type) +{ + switch (radar_type) { + case RADAR_TYPE_REGULAR: + return "REGULAR"; + case RADAR_TYPE_CHIRP: + return "CHIRP"; + case RADAR_TYPE_NONE: + default: + return "N/A"; + } +} + static int wlcore_smart_config_sync_event(struct wl1271 *wl, u8 sync_channel, u8 sync_band) { @@ -115,6 +128,14 @@ int wl18xx_process_mailbox_events(struct wl1271 *wl) wl18xx_scan_completed(wl, wl->scan_wlvif); } + if (vector & RADAR_DETECTED_EVENT_ID) { + wl1271_info("radar event: channel %d type %s", + mbox->radar_channel, + wl18xx_radar_type_decode(mbox->radar_type)); + + ieee80211_radar_detected(wl->hw); + } + if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT (results %d)", diff --git a/drivers/net/wireless/ti/wl18xx/event.h b/drivers/net/wireless/ti/wl18xx/event.h index 0680312d4943..266ee87834e4 100644 --- a/drivers/net/wireless/ti/wl18xx/event.h +++ b/drivers/net/wireless/ti/wl18xx/event.h @@ -42,6 +42,12 @@ enum { SMART_CONFIG_DECODE_EVENT_ID = BIT(23), }; +enum wl18xx_radar_types { + RADAR_TYPE_NONE, + RADAR_TYPE_REGULAR, + RADAR_TYPE_CHIRP +}; + struct wl18xx_event_mailbox { __le32 events_vector; @@ -83,13 +89,19 @@ struct wl18xx_event_mailbox { u8 sc_token_len; u8 padding1; u8 sc_ssid[32]; - u8 sc_pwd[32]; + u8 sc_pwd[64]; u8 sc_token[32]; /* smart config sync channel */ u8 sc_sync_channel; u8 sc_sync_band; u8 padding2[2]; + + /* radar detect */ + u8 radar_channel; + u8 radar_type; + + u8 padding3[2]; } __packed; int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event, diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 8e562610bf16..717c4f5a02c2 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -378,6 +378,7 @@ static struct wlcore_conf wl18xx_conf = { .keep_alive_interval = 55000, .max_listen_interval = 20, .sta_sleep_auth = WL1271_PSM_ILLEGAL, + .suspend_rx_ba_activity = 0, }, .itrim = { .enable = false, @@ -567,6 +568,12 @@ static struct wl18xx_priv_conf wl18xx_default_priv_conf = { .high_power_val_2nd = 0xff, .tx_rf_margin = 1, }, + .ap_sleep = { /* disabled by default */ + .idle_duty_cycle = 0, + .connected_duty_cycle = 0, + .max_stations_thresh = 0, + .idle_conn_thresh = 0, + }, }; static const struct wlcore_partition_set wl18xx_ptable[PART_TABLE_LEN] = { @@ -648,7 +655,7 @@ static const struct wl18xx_clk_cfg wl18xx_clk_table[NUM_CLOCK_CONFIGS] = { }; /* TODO: maybe move to a new header file? */ -#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-3.bin" +#define WL18XX_FW_NAME "ti-connectivity/wl18xx-fw-4.bin" static int wl18xx_identify_chip(struct wl1271 *wl) { @@ -983,6 +990,7 @@ static int wl18xx_boot(struct wl1271 *wl) wl->event_mask = BSS_LOSS_EVENT_ID | SCAN_COMPLETE_EVENT_ID | + RADAR_DETECTED_EVENT_ID | RSSI_SNR_TRIGGER_0_EVENT_ID | PERIODIC_SCAN_COMPLETE_EVENT_ID | PERIODIC_SCAN_REPORT_EVENT_ID | @@ -1559,26 +1567,19 @@ static u32 wl18xx_pre_pkt_send(struct wl1271 *wl, } static void wl18xx_sta_rc_update(struct wl1271 *wl, - struct wl12xx_vif *wlvif, - struct ieee80211_sta *sta, - u32 changed) + struct wl12xx_vif *wlvif) { - bool wide = sta->bandwidth >= IEEE80211_STA_RX_BW_40; + bool wide = wlvif->rc_update_bw >= IEEE80211_STA_RX_BW_40; wl1271_debug(DEBUG_MAC80211, "mac80211 sta_rc_update wide %d", wide); - if (!(changed & IEEE80211_RC_BW_CHANGED)) - return; - - mutex_lock(&wl->mutex); - /* sanity */ if (WARN_ON(wlvif->bss_type != BSS_TYPE_STA_BSS)) - goto out; + return; /* ignore the change before association */ if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) - goto out; + return; /* * If we started out as wide, we can change the operation mode. If we @@ -1589,9 +1590,6 @@ static void wl18xx_sta_rc_update(struct wl1271 *wl, wl18xx_acx_peer_ht_operation_mode(wl, wlvif->sta.hlid, wide); else ieee80211_connection_loss(wl12xx_wlvif_to_vif(wlvif)); - -out: - mutex_unlock(&wl->mutex); } static int wl18xx_set_peer_cap(struct wl1271 *wl, @@ -1703,6 +1701,11 @@ static struct wlcore_ops wl18xx_ops = { .smart_config_start = wl18xx_cmd_smart_config_start, .smart_config_stop = wl18xx_cmd_smart_config_stop, .smart_config_set_group_key = wl18xx_cmd_smart_config_set_group_key, + .interrupt_notify = wl18xx_acx_interrupt_notify_config, + .rx_ba_filter = wl18xx_acx_rx_ba_filter, + .ap_sleep = wl18xx_acx_ap_sleep, + .set_cac = wl18xx_cmd_set_cac, + .dfs_master_restart = wl18xx_cmd_dfs_master_restart, }; /* HT cap appropriate for wide channels in 2Ghz */ @@ -1796,6 +1799,10 @@ wl18xx_iface_combinations[] = { .limits = wl18xx_iface_ap_limits, .n_limits = ARRAY_SIZE(wl18xx_iface_ap_limits), .num_different_channels = 1, + .radar_detect_widths = BIT(NL80211_CHAN_NO_HT) | + BIT(NL80211_CHAN_HT20) | + BIT(NL80211_CHAN_HT40MINUS) | + BIT(NL80211_CHAN_HT40PLUS), } }; diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h index 6a2b88030c1d..71e9e382ce80 100644 --- a/drivers/net/wireless/ti/wl18xx/wl18xx.h +++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h @@ -26,10 +26,10 @@ /* minimum FW required for driver */ #define WL18XX_CHIP_VER 8 -#define WL18XX_IFTYPE_VER 8 +#define WL18XX_IFTYPE_VER 9 #define WL18XX_MAJOR_VER WLCORE_FW_VER_IGNORE #define WL18XX_SUBTYPE_VER WLCORE_FW_VER_IGNORE -#define WL18XX_MINOR_VER 13 +#define WL18XX_MINOR_VER 11 #define WL18XX_CMD_MAX_SIZE 740 diff --git a/drivers/net/wireless/ti/wlcore/acx.c b/drivers/net/wireless/ti/wlcore/acx.c index b924ceadc02c..f28fa3b5029d 100644 --- a/drivers/net/wireless/ti/wlcore/acx.c +++ b/drivers/net/wireless/ti/wlcore/acx.c @@ -1725,7 +1725,7 @@ int wl12xx_acx_config_hangover(struct wl1271 *wl) acx->decrease_delta = conf->decrease_delta; acx->quiet_time = conf->quiet_time; acx->increase_time = conf->increase_time; - acx->window_size = acx->window_size; + acx->window_size = conf->window_size; ret = wl1271_cmd_configure(wl, ACX_CONFIG_HANGOVER, acx, sizeof(*acx)); diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index b82661962d33..c26fc2106e5b 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -403,7 +403,7 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) WARN_ON_ONCE(wl->active_link_count < 0); } -static u8 wlcore_get_native_channel_type(u8 nl_channel_type) +u8 wlcore_get_native_channel_type(u8 nl_channel_type) { switch (nl_channel_type) { case NL80211_CHAN_NO_HT: @@ -419,6 +419,7 @@ static u8 wlcore_get_native_channel_type(u8 nl_channel_type) return WLCORE_CHAN_NO_HT; } } +EXPORT_SYMBOL_GPL(wlcore_get_native_channel_type); static int wl12xx_cmd_role_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif, @@ -1686,9 +1687,7 @@ int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl) { struct wl12xx_cmd_regdomain_dfs_config *cmd = NULL; int ret = 0, i, b, ch_bit_idx; - struct ieee80211_channel *channel; u32 tmp_ch_bitmap[2]; - u16 ch; struct wiphy *wiphy = wl->hw->wiphy; struct ieee80211_supported_band *band; bool timeout = false; @@ -1703,12 +1702,16 @@ int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl) for (b = IEEE80211_BAND_2GHZ; b <= IEEE80211_BAND_5GHZ; b++) { band = wiphy->bands[b]; for (i = 0; i < band->n_channels; i++) { - channel = &band->channels[i]; - ch = channel->hw_value; + struct ieee80211_channel *channel = &band->channels[i]; + u16 ch = channel->hw_value; + u32 flags = channel->flags; - if (channel->flags & (IEEE80211_CHAN_DISABLED | - IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR)) + if (flags & (IEEE80211_CHAN_DISABLED | + IEEE80211_CHAN_NO_IR)) + continue; + + if ((flags & IEEE80211_CHAN_RADAR) && + channel->dfs_state != NL80211_DFS_AVAILABLE) continue; ch_bit_idx = wlcore_get_reg_conf_ch_idx(b, ch); @@ -1733,6 +1736,7 @@ int wlcore_cmd_regdomain_config_locked(struct wl1271 *wl) cmd->ch_bit_map1 = cpu_to_le32(tmp_ch_bitmap[0]); cmd->ch_bit_map2 = cpu_to_le32(tmp_ch_bitmap[1]); + cmd->dfs_region = wl->dfs_region; wl1271_debug(DEBUG_CMD, "cmd reg domain bitmap1: 0x%08x, bitmap2: 0x%08x", diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index 453684a71d30..e14cd407a6ae 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -105,6 +105,7 @@ int wl12xx_allocate_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid); int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl, u32 mask, bool *timeout); +u8 wlcore_get_native_channel_type(u8 nl_channel_type); enum wl1271_commands { CMD_INTERROGATE = 1, /* use this to read information elements */ @@ -172,6 +173,11 @@ enum wl1271_commands { CMD_SMART_CONFIG_STOP = 62, CMD_SMART_CONFIG_SET_GROUP_KEY = 63, + CMD_CAC_START = 64, + CMD_CAC_STOP = 65, + CMD_DFS_MASTER_RESTART = 66, + CMD_DFS_RADAR_DETECTION_DEBUG = 67, + MAX_COMMAND_ID = 0xFFFF, }; @@ -642,6 +648,8 @@ struct wl12xx_cmd_regdomain_dfs_config { __le32 ch_bit_map1; __le32 ch_bit_map2; + u8 dfs_region; + u8 padding[3]; } __packed; struct wl12xx_cmd_config_fwlog { diff --git a/drivers/net/wireless/ti/wlcore/conf.h b/drivers/net/wireless/ti/wlcore/conf.h index 40995c42bef8..166add00b50f 100644 --- a/drivers/net/wireless/ti/wlcore/conf.h +++ b/drivers/net/wireless/ti/wlcore/conf.h @@ -997,6 +997,11 @@ struct conf_conn_settings { * whether we can go to ELP. */ u8 sta_sleep_auth; + + /* + * Default RX BA Activity filter configuration + */ + u8 suspend_rx_ba_activity; } __packed; enum { @@ -1347,7 +1352,7 @@ struct conf_recovery_settings { * version, the two LSB are the lower driver's private conf * version. */ -#define WLCORE_CONF_VERSION (0x0005 << 16) +#define WLCORE_CONF_VERSION (0x0006 << 16) #define WLCORE_CONF_MASK 0xffff0000 #define WLCORE_CONF_SIZE (sizeof(struct wlcore_conf_header) + \ sizeof(struct wlcore_conf)) diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index 0be21f62fcb0..68f3bf229b5a 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -929,17 +929,10 @@ static ssize_t beacon_filtering_write(struct file *file, { struct wl1271 *wl = file->private_data; struct wl12xx_vif *wlvif; - char buf[10]; - size_t len; unsigned long value; int ret; - len = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, len)) - return -EFAULT; - buf[len] = '\0'; - - ret = kstrtoul(buf, 0, &value); + ret = kstrtoul_from_user(user_buf, count, 0, &value); if (ret < 0) { wl1271_warning("illegal value for beacon_filtering!"); return -EINVAL; diff --git a/drivers/net/wireless/ti/wlcore/event.c b/drivers/net/wireless/ti/wlcore/event.c index 5153640f4532..c42e78955e7b 100644 --- a/drivers/net/wireless/ti/wlcore/event.c +++ b/drivers/net/wireless/ti/wlcore/event.c @@ -139,7 +139,7 @@ void wlcore_event_channel_switch(struct wl1271 *wl, wl1271_debug(DEBUG_EVENT, "%s: roles=0x%lx success=%d", __func__, roles_bitmap, success); - wl12xx_for_each_wlvif_sta(wl, wlvif) { + wl12xx_for_each_wlvif(wl, wlvif) { if (wlvif->role_id == WL12XX_INVALID_ROLE_ID || !test_bit(wlvif->role_id , &roles_bitmap)) continue; @@ -150,8 +150,13 @@ void wlcore_event_channel_switch(struct wl1271 *wl, vif = wl12xx_wlvif_to_vif(wlvif); - ieee80211_chswitch_done(vif, success); - cancel_delayed_work(&wlvif->channel_switch_work); + if (wlvif->bss_type == BSS_TYPE_STA_BSS) { + ieee80211_chswitch_done(vif, success); + cancel_delayed_work(&wlvif->channel_switch_work); + } else { + set_bit(WLVIF_FLAG_BEACON_DISABLED, &wlvif->flags); + ieee80211_csa_finish(vif); + } } } EXPORT_SYMBOL_GPL(wlcore_event_channel_switch); diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index aa9f82c72296..eec56935b1b6 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -211,11 +211,35 @@ wlcore_hw_pre_pkt_send(struct wl1271 *wl, u32 buf_offset, u32 last_len) } static inline void -wlcore_hw_sta_rc_update(struct wl1271 *wl, struct wl12xx_vif *wlvif, - struct ieee80211_sta *sta, u32 changed) +wlcore_hw_sta_rc_update(struct wl1271 *wl, struct wl12xx_vif *wlvif) { if (wl->ops->sta_rc_update) - wl->ops->sta_rc_update(wl, wlvif, sta, changed); + wl->ops->sta_rc_update(wl, wlvif); +} + +static inline int +wlcore_hw_interrupt_notify(struct wl1271 *wl, bool action) +{ + if (wl->ops->interrupt_notify) + return wl->ops->interrupt_notify(wl, action); + return 0; +} + +static inline int +wlcore_hw_rx_ba_filter(struct wl1271 *wl, bool action) +{ + if (wl->ops->rx_ba_filter) + return wl->ops->rx_ba_filter(wl, action); + return 0; +} + +static inline int +wlcore_hw_ap_sleep(struct wl1271 *wl) +{ + if (wl->ops->ap_sleep) + return wl->ops->ap_sleep(wl); + + return 0; } static inline int @@ -287,4 +311,22 @@ wlcore_smart_config_set_group_key(struct wl1271 *wl, u16 group_id, return wl->ops->smart_config_set_group_key(wl, group_id, key_len, key); } + +static inline int +wlcore_hw_set_cac(struct wl1271 *wl, struct wl12xx_vif *wlvif, bool start) +{ + if (!wl->ops->set_cac) + return -EINVAL; + + return wl->ops->set_cac(wl, wlvif, start); +} + +static inline int +wlcore_hw_dfs_master_restart(struct wl1271 *wl, struct wl12xx_vif *wlvif) +{ + if (!wl->ops->dfs_master_restart) + return -EINVAL; + + return wl->ops->dfs_master_restart(wl, wlvif); +} #endif diff --git a/drivers/net/wireless/ti/wlcore/init.c b/drivers/net/wireless/ti/wlcore/init.c index 199e94120864..5ca1fb161a50 100644 --- a/drivers/net/wireless/ti/wlcore/init.c +++ b/drivers/net/wireless/ti/wlcore/init.c @@ -392,6 +392,11 @@ static int wl1271_ap_hw_init(struct wl1271 *wl, struct wl12xx_vif *wlvif) if (ret < 0) return ret; + /* configure AP sleep, if enabled */ + ret = wlcore_hw_ap_sleep(wl); + if (ret < 0) + return ret; + return 0; } @@ -567,8 +572,7 @@ int wl1271_init_vif_specific(struct wl1271 *wl, struct ieee80211_vif *vif) /* consider all existing roles before configuring psm. */ if (wl->ap_count == 0 && is_ap) { /* first AP */ - /* Configure for power always on */ - ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_CAM); + ret = wl1271_acx_sleep_auth(wl, WL1271_PSM_ELP); if (ret < 0) return ret; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 6ad3fcedab9b..1e136993580f 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -79,22 +79,12 @@ static int wl12xx_set_authorized(struct wl1271 *wl, struct wl12xx_vif *wlvif) static void wl1271_reg_notify(struct wiphy *wiphy, struct regulatory_request *request) { - struct ieee80211_supported_band *band; - struct ieee80211_channel *ch; - int i; struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct wl1271 *wl = hw->priv; - band = wiphy->bands[IEEE80211_BAND_5GHZ]; - for (i = 0; i < band->n_channels; i++) { - ch = &band->channels[i]; - if (ch->flags & IEEE80211_CHAN_DISABLED) - continue; - - if (ch->flags & IEEE80211_CHAN_RADAR) - ch->flags |= IEEE80211_CHAN_NO_IR; - - } + /* copy the current dfs region */ + if (request) + wl->dfs_region = request->dfs_region; wlcore_regdomain_config(wl); } @@ -226,6 +216,29 @@ void wl12xx_rearm_tx_watchdog_locked(struct wl1271 *wl) msecs_to_jiffies(wl->conf.tx.tx_watchdog_timeout)); } +static void wlcore_rc_update_work(struct work_struct *work) +{ + int ret; + struct wl12xx_vif *wlvif = container_of(work, struct wl12xx_vif, + rc_update_work); + struct wl1271 *wl = wlvif->wl; + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) + goto out; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + wlcore_hw_sta_rc_update(wl, wlvif); + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); +} + static void wl12xx_tx_watchdog_work(struct work_struct *work) { struct delayed_work *dwork; @@ -1662,19 +1675,15 @@ static int wl1271_configure_suspend_sta(struct wl1271 *wl, if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) goto out; - ret = wl1271_ps_elp_wakeup(wl); - if (ret < 0) - goto out; - ret = wl1271_configure_wowlan(wl, wow); if (ret < 0) - goto out_sleep; + goto out; if ((wl->conf.conn.suspend_wake_up_event == wl->conf.conn.wake_up_event) && (wl->conf.conn.suspend_listen_interval == wl->conf.conn.listen_interval)) - goto out_sleep; + goto out; ret = wl1271_acx_wake_up_conditions(wl, wlvif, wl->conf.conn.suspend_wake_up_event, @@ -1682,29 +1691,28 @@ static int wl1271_configure_suspend_sta(struct wl1271 *wl, if (ret < 0) wl1271_error("suspend: set wake up conditions failed: %d", ret); - -out_sleep: - wl1271_ps_elp_sleep(wl); out: return ret; } static int wl1271_configure_suspend_ap(struct wl1271 *wl, - struct wl12xx_vif *wlvif) + struct wl12xx_vif *wlvif, + struct cfg80211_wowlan *wow) { int ret = 0; if (!test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags)) goto out; - ret = wl1271_ps_elp_wakeup(wl); + ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true); if (ret < 0) goto out; - ret = wl1271_acx_beacon_filter_opt(wl, wlvif, true); + ret = wl1271_configure_wowlan(wl, wow); + if (ret < 0) + goto out; - wl1271_ps_elp_sleep(wl); out: return ret; @@ -1717,7 +1725,7 @@ static int wl1271_configure_suspend(struct wl1271 *wl, if (wlvif->bss_type == BSS_TYPE_STA_BSS) return wl1271_configure_suspend_sta(wl, wlvif, wow); if (wlvif->bss_type == BSS_TYPE_AP_BSS) - return wl1271_configure_suspend_ap(wl, wlvif); + return wl1271_configure_suspend_ap(wl, wlvif, wow); return 0; } @@ -1730,21 +1738,18 @@ static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif) if ((!is_ap) && (!is_sta)) return; - if (is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) + if ((is_sta && !test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) || + (is_ap && !test_bit(WLVIF_FLAG_AP_STARTED, &wlvif->flags))) return; - ret = wl1271_ps_elp_wakeup(wl); - if (ret < 0) - return; + wl1271_configure_wowlan(wl, NULL); if (is_sta) { - wl1271_configure_wowlan(wl, NULL); - if ((wl->conf.conn.suspend_wake_up_event == wl->conf.conn.wake_up_event) && (wl->conf.conn.suspend_listen_interval == wl->conf.conn.listen_interval)) - goto out_sleep; + return; ret = wl1271_acx_wake_up_conditions(wl, wlvif, wl->conf.conn.wake_up_event, @@ -1757,9 +1762,6 @@ static void wl1271_configure_resume(struct wl1271 *wl, struct wl12xx_vif *wlvif) } else if (is_ap) { ret = wl1271_acx_beacon_filter_opt(wl, wlvif, false); } - -out_sleep: - wl1271_ps_elp_sleep(wl); } static int wl1271_op_suspend(struct ieee80211_hw *hw, @@ -1781,6 +1783,13 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw, wl1271_tx_flush(wl); mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) { + mutex_unlock(&wl->mutex); + return ret; + } + wl->wow_enabled = true; wl12xx_for_each_wlvif(wl, wlvif) { ret = wl1271_configure_suspend(wl, wlvif, wow); @@ -1790,7 +1799,27 @@ static int wl1271_op_suspend(struct ieee80211_hw *hw, return ret; } } + + /* disable fast link flow control notifications from FW */ + ret = wlcore_hw_interrupt_notify(wl, false); + if (ret < 0) + goto out_sleep; + + /* if filtering is enabled, configure the FW to drop all RX BA frames */ + ret = wlcore_hw_rx_ba_filter(wl, + !!wl->conf.conn.suspend_rx_ba_activity); + if (ret < 0) + goto out_sleep; + +out_sleep: + wl1271_ps_elp_sleep(wl); mutex_unlock(&wl->mutex); + + if (ret < 0) { + wl1271_warning("couldn't prepare device to suspend"); + return ret; + } + /* flush any remaining work */ wl1271_debug(DEBUG_MAC80211, "flushing remaining works"); @@ -1864,13 +1893,29 @@ static int wl1271_op_resume(struct ieee80211_hw *hw) if (pending_recovery) { wl1271_warning("queuing forgotten recovery on resume"); ieee80211_queue_work(wl->hw, &wl->recovery_work); - goto out; + goto out_sleep; } + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + wl12xx_for_each_wlvif(wl, wlvif) { wl1271_configure_resume(wl, wlvif); } + ret = wlcore_hw_interrupt_notify(wl, true); + if (ret < 0) + goto out_sleep; + + /* if filtering is enabled, configure the FW to drop all RX BA frames */ + ret = wlcore_hw_rx_ba_filter(wl, false); + if (ret < 0) + goto out_sleep; + +out_sleep: + wl1271_ps_elp_sleep(wl); + out: wl->wow_enabled = false; @@ -2279,6 +2324,7 @@ static int wl12xx_init_vif_data(struct wl1271 *wl, struct ieee80211_vif *vif) wl1271_rx_streaming_enable_work); INIT_WORK(&wlvif->rx_streaming_disable_work, wl1271_rx_streaming_disable_work); + INIT_WORK(&wlvif->rc_update_work, wlcore_rc_update_work); INIT_DELAYED_WORK(&wlvif->channel_switch_work, wlcore_channel_switch_work); INIT_DELAYED_WORK(&wlvif->connection_loss_work, @@ -2508,6 +2554,7 @@ static int wl1271_op_add_interface(struct ieee80211_hw *hw, } vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_UAPSD | IEEE80211_VIF_SUPPORTS_CQM_RSSI; wl1271_debug(DEBUG_MAC80211, "mac80211 add interface type %d mac %pM", @@ -2723,6 +2770,7 @@ unlock: del_timer_sync(&wlvif->rx_streaming_timer); cancel_work_sync(&wlvif->rx_streaming_enable_work); cancel_work_sync(&wlvif->rx_streaming_disable_work); + cancel_work_sync(&wlvif->rc_update_work); cancel_delayed_work_sync(&wlvif->connection_loss_work); cancel_delayed_work_sync(&wlvif->channel_switch_work); cancel_delayed_work_sync(&wlvif->pending_auth_complete_work); @@ -4072,8 +4120,14 @@ static int wl1271_bss_beacon_info_changed(struct wl1271 *wl, ret = wlcore_set_beacon_template(wl, vif, is_ap); if (ret < 0) goto out; - } + if (test_and_clear_bit(WLVIF_FLAG_BEACON_DISABLED, + &wlvif->flags)) { + ret = wlcore_hw_dfs_master_restart(wl, wlvif); + if (ret < 0) + goto out; + } + } out: if (ret != 0) wl1271_error("beacon info change failed: %d", ret); @@ -4574,10 +4628,46 @@ static void wlcore_op_change_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx, u32 changed) { + struct wl1271 *wl = hw->priv; + struct wl12xx_vif *wlvif; + int ret; + int channel = ieee80211_frequency_to_channel( + ctx->def.chan->center_freq); + wl1271_debug(DEBUG_MAC80211, "mac80211 change chanctx %d (type %d) changed 0x%x", - ieee80211_frequency_to_channel(ctx->def.chan->center_freq), - cfg80211_get_chandef_type(&ctx->def), changed); + channel, cfg80211_get_chandef_type(&ctx->def), changed); + + mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + wl12xx_for_each_wlvif(wl, wlvif) { + struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); + + rcu_read_lock(); + if (rcu_access_pointer(vif->chanctx_conf) != ctx) { + rcu_read_unlock(); + continue; + } + rcu_read_unlock(); + + /* start radar if needed */ + if (changed & IEEE80211_CHANCTX_CHANGE_RADAR && + wlvif->bss_type == BSS_TYPE_AP_BSS && + ctx->radar_enabled && !wlvif->radar_enabled && + ctx->def.chan->dfs_state == NL80211_DFS_USABLE) { + wl1271_debug(DEBUG_MAC80211, "Start radar detection"); + wlcore_hw_set_cac(wl, wlvif, true); + wlvif->radar_enabled = true; + } + } + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); } static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw, @@ -4588,13 +4678,26 @@ static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw, struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); int channel = ieee80211_frequency_to_channel( ctx->def.chan->center_freq); + int ret = -EINVAL; wl1271_debug(DEBUG_MAC80211, - "mac80211 assign chanctx (role %d) %d (type %d)", - wlvif->role_id, channel, cfg80211_get_chandef_type(&ctx->def)); + "mac80211 assign chanctx (role %d) %d (type %d) (radar %d dfs_state %d)", + wlvif->role_id, channel, + cfg80211_get_chandef_type(&ctx->def), + ctx->radar_enabled, ctx->def.chan->dfs_state); mutex_lock(&wl->mutex); + if (unlikely(wl->state != WLCORE_STATE_ON)) + goto out; + + if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))) + goto out; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + wlvif->band = ctx->def.chan->band; wlvif->channel = channel; wlvif->channel_type = cfg80211_get_chandef_type(&ctx->def); @@ -4602,6 +4705,15 @@ static int wlcore_op_assign_vif_chanctx(struct ieee80211_hw *hw, /* update default rates according to the band */ wl1271_set_band_rate(wl, wlvif); + if (ctx->radar_enabled && + ctx->def.chan->dfs_state == NL80211_DFS_USABLE) { + wl1271_debug(DEBUG_MAC80211, "Start radar detection"); + wlcore_hw_set_cac(wl, wlvif, true); + wlvif->radar_enabled = true; + } + + wl1271_ps_elp_sleep(wl); +out: mutex_unlock(&wl->mutex); return 0; @@ -4613,6 +4725,7 @@ static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw, { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 unassign chanctx (role %d) %d (type %d)", @@ -4621,6 +4734,99 @@ static void wlcore_op_unassign_vif_chanctx(struct ieee80211_hw *hw, cfg80211_get_chandef_type(&ctx->def)); wl1271_tx_flush(wl); + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) + goto out; + + if (unlikely(!test_bit(WLVIF_FLAG_INITIALIZED, &wlvif->flags))) + goto out; + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + if (wlvif->radar_enabled) { + wl1271_debug(DEBUG_MAC80211, "Stop radar detection"); + wlcore_hw_set_cac(wl, wlvif, false); + wlvif->radar_enabled = false; + } + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); +} + +static int __wlcore_switch_vif_chan(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + struct ieee80211_chanctx_conf *new_ctx) +{ + int channel = ieee80211_frequency_to_channel( + new_ctx->def.chan->center_freq); + + wl1271_debug(DEBUG_MAC80211, + "switch vif (role %d) %d -> %d chan_type: %d", + wlvif->role_id, wlvif->channel, channel, + cfg80211_get_chandef_type(&new_ctx->def)); + + if (WARN_ON_ONCE(wlvif->bss_type != BSS_TYPE_AP_BSS)) + return 0; + + WARN_ON(!test_bit(WLVIF_FLAG_BEACON_DISABLED, &wlvif->flags)); + + if (wlvif->radar_enabled) { + wl1271_debug(DEBUG_MAC80211, "Stop radar detection"); + wlcore_hw_set_cac(wl, wlvif, false); + wlvif->radar_enabled = false; + } + + wlvif->band = new_ctx->def.chan->band; + wlvif->channel = channel; + wlvif->channel_type = cfg80211_get_chandef_type(&new_ctx->def); + + /* start radar if needed */ + if (new_ctx->radar_enabled) { + wl1271_debug(DEBUG_MAC80211, "Start radar detection"); + wlcore_hw_set_cac(wl, wlvif, true); + wlvif->radar_enabled = true; + } + + return 0; +} + +static int +wlcore_op_switch_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, + enum ieee80211_chanctx_switch_mode mode) +{ + struct wl1271 *wl = hw->priv; + int i, ret; + + wl1271_debug(DEBUG_MAC80211, + "mac80211 switch chanctx n_vifs %d mode %d", + n_vifs, mode); + + mutex_lock(&wl->mutex); + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + for (i = 0; i < n_vifs; i++) { + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vifs[i].vif); + + ret = __wlcore_switch_vif_chan(wl, wlvif, vifs[i].new_ctx); + if (ret) + goto out_sleep; + } +out_sleep: + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); + + return 0; } static int wl1271_op_conf_tx(struct ieee80211_hw *hw, @@ -5228,6 +5434,83 @@ out: mutex_unlock(&wl->mutex); } +static const void *wlcore_get_beacon_ie(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + u8 eid) +{ + int ieoffset = offsetof(struct ieee80211_mgmt, u.beacon.variable); + struct sk_buff *beacon = + ieee80211_beacon_get(wl->hw, wl12xx_wlvif_to_vif(wlvif)); + + if (!beacon) + return NULL; + + return cfg80211_find_ie(eid, + beacon->data + ieoffset, + beacon->len - ieoffset); +} + +static int wlcore_get_csa_count(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 *csa_count) +{ + const u8 *ie; + const struct ieee80211_channel_sw_ie *ie_csa; + + ie = wlcore_get_beacon_ie(wl, wlvif, WLAN_EID_CHANNEL_SWITCH); + if (!ie) + return -EINVAL; + + ie_csa = (struct ieee80211_channel_sw_ie *)&ie[2]; + *csa_count = ie_csa->count; + + return 0; +} + +static void wlcore_op_channel_switch_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_chan_def *chandef) +{ + struct wl1271 *wl = hw->priv; + struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); + struct ieee80211_channel_switch ch_switch = { + .block_tx = true, + .chandef = *chandef, + }; + int ret; + + wl1271_debug(DEBUG_MAC80211, + "mac80211 channel switch beacon (role %d)", + wlvif->role_id); + + ret = wlcore_get_csa_count(wl, wlvif, &ch_switch.count); + if (ret < 0) { + wl1271_error("error getting beacon (for CSA counter)"); + return; + } + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) { + ret = -EBUSY; + goto out; + } + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + ret = wl->ops->channel_switch(wl, wlvif, &ch_switch); + if (ret) + goto out_sleep; + + set_bit(WLVIF_FLAG_CS_PROGRESS, &wlvif->flags); + +out_sleep: + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); +} + static void wlcore_op_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop) { @@ -5370,19 +5653,26 @@ static void wlcore_op_sta_rc_update(struct ieee80211_hw *hw, u32 changed) { struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); - struct wl1271 *wl = hw->priv; - wlcore_hw_sta_rc_update(wl, wlvif, sta, changed); + wl1271_debug(DEBUG_MAC80211, "mac80211 sta_rc_update"); + + if (!(changed & IEEE80211_RC_BW_CHANGED)) + return; + + /* this callback is atomic, so schedule a new work */ + wlvif->rc_update_bw = sta->bandwidth; + ieee80211_queue_work(hw, &wlvif->rc_update_work); } -static int wlcore_op_get_rssi(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - s8 *rssi_dbm) +static void wlcore_op_sta_statistics(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct station_info *sinfo) { struct wl1271 *wl = hw->priv; struct wl12xx_vif *wlvif = wl12xx_vif_to_data(vif); - int ret = 0; + s8 rssi_dbm; + int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 get_rssi"); @@ -5395,17 +5685,18 @@ static int wlcore_op_get_rssi(struct ieee80211_hw *hw, if (ret < 0) goto out_sleep; - ret = wlcore_acx_average_rssi(wl, wlvif, rssi_dbm); + ret = wlcore_acx_average_rssi(wl, wlvif, &rssi_dbm); if (ret < 0) goto out_sleep; + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); + sinfo->signal = rssi_dbm; + out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); - - return ret; } static bool wl1271_tx_frames_pending(struct ieee80211_hw *hw) @@ -5596,6 +5887,7 @@ static const struct ieee80211_ops wl1271_ops = { .set_bitrate_mask = wl12xx_set_bitrate_mask, .set_default_unicast_key = wl1271_op_set_default_key_idx, .channel_switch = wl12xx_op_channel_switch, + .channel_switch_beacon = wlcore_op_channel_switch_beacon, .flush = wlcore_op_flush, .remain_on_channel = wlcore_op_remain_on_channel, .cancel_remain_on_channel = wlcore_op_cancel_remain_on_channel, @@ -5604,8 +5896,9 @@ static const struct ieee80211_ops wl1271_ops = { .change_chanctx = wlcore_op_change_chanctx, .assign_vif_chanctx = wlcore_op_assign_vif_chanctx, .unassign_vif_chanctx = wlcore_op_unassign_vif_chanctx, + .switch_vif_chanctx = wlcore_op_switch_vif_chanctx, .sta_rc_update = wlcore_op_sta_rc_update, - .get_rssi = wlcore_op_get_rssi, + .sta_statistics = wlcore_op_sta_statistics, CFG80211_TESTMODE_CMD(wl1271_tm_cmd) }; @@ -5776,7 +6069,6 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SUPPORTS_PS | IEEE80211_HW_SUPPORTS_DYNAMIC_PS | - IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_HAS_RATE_CONTROL | IEEE80211_HW_CONNECTION_MONITOR | IEEE80211_HW_REPORTS_TX_ACK_STATUS | @@ -5811,7 +6103,8 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | - WIPHY_FLAG_SUPPORTS_SCHED_SCAN; + WIPHY_FLAG_SUPPORTS_SCHED_SCAN | + WIPHY_FLAG_HAS_CHANNEL_SWITCH; /* make sure all our channels fit in the scanned_ch bitmask */ BUILD_BUG_ON(ARRAY_SIZE(wl1271_channels) + diff --git a/drivers/net/wireless/ti/wlcore/ps.c b/drivers/net/wireless/ti/wlcore/ps.c index b52516eed7b2..4cd316e61466 100644 --- a/drivers/net/wireless/ti/wlcore/ps.c +++ b/drivers/net/wireless/ti/wlcore/ps.c @@ -56,9 +56,6 @@ void wl1271_elp_work(struct work_struct *work) goto out; wl12xx_for_each_wlvif(wl, wlvif) { - if (wlvif->bss_type == BSS_TYPE_AP_BSS) - goto out; - if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) && test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) goto out; @@ -95,9 +92,6 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl) return; wl12xx_for_each_wlvif(wl, wlvif) { - if (wlvif->bss_type == BSS_TYPE_AP_BSS) - return; - if (!test_bit(WLVIF_FLAG_IN_PS, &wlvif->flags) && test_bit(WLVIF_FLAG_IN_USE, &wlvif->flags)) return; @@ -108,6 +102,7 @@ void wl1271_ps_elp_sleep(struct wl1271 *wl) ieee80211_queue_delayed_work(wl->hw, &wl->elp_work, msecs_to_jiffies(timeout)); } +EXPORT_SYMBOL_GPL(wl1271_ps_elp_sleep); int wl1271_ps_elp_wakeup(struct wl1271 *wl) { @@ -175,6 +170,7 @@ err: out: return 0; } +EXPORT_SYMBOL_GPL(wl1271_ps_elp_wakeup); int wl1271_ps_set_mode(struct wl1271 *wl, struct wl12xx_vif *wlvif, enum wl1271_cmd_ps_mode mode) diff --git a/drivers/net/wireless/ti/wlcore/vendor_cmd.c b/drivers/net/wireless/ti/wlcore/vendor_cmd.c index ad86a48dcfcb..fd4e9ba176c9 100644 --- a/drivers/net/wireless/ti/wlcore/vendor_cmd.c +++ b/drivers/net/wireless/ti/wlcore/vendor_cmd.c @@ -21,7 +21,7 @@ static const struct nla_policy wlcore_vendor_attr_policy[NUM_WLCORE_VENDOR_ATTR] = { [WLCORE_VENDOR_ATTR_FREQ] = { .type = NLA_U32 }, [WLCORE_VENDOR_ATTR_GROUP_ID] = { .type = NLA_U32 }, - [WLCORE_VENDOR_ATTR_GROUP_KEY] = { .type = NLA_U32, + [WLCORE_VENDOR_ATTR_GROUP_KEY] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, }; diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index df78cf12ef15..d599c869e6e8 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -106,8 +106,7 @@ struct wlcore_ops { struct wl12xx_vif *wlvif, struct ieee80211_channel_switch *ch_switch); u32 (*pre_pkt_send)(struct wl1271 *wl, u32 buf_offset, u32 last_len); - void (*sta_rc_update)(struct wl1271 *wl, struct wl12xx_vif *wlvif, - struct ieee80211_sta *sta, u32 changed); + void (*sta_rc_update)(struct wl1271 *wl, struct wl12xx_vif *wlvif); int (*set_peer_cap)(struct wl1271 *wl, struct ieee80211_sta_ht_cap *ht_cap, bool allow_ht_operation, @@ -117,10 +116,16 @@ struct wlcore_ops { struct wl1271_link *lnk); bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid, struct wl1271_link *lnk); + int (*interrupt_notify)(struct wl1271 *wl, bool action); + int (*rx_ba_filter)(struct wl1271 *wl, bool action); + int (*ap_sleep)(struct wl1271 *wl); int (*smart_config_start)(struct wl1271 *wl, u32 group_bitmap); int (*smart_config_stop)(struct wl1271 *wl); int (*smart_config_set_group_key)(struct wl1271 *wl, u16 group_id, u8 key_len, u8 *key); + int (*set_cac)(struct wl1271 *wl, struct wl12xx_vif *wlvif, + bool start); + int (*dfs_master_restart)(struct wl1271 *wl, struct wl12xx_vif *wlvif); }; enum wlcore_partitions { @@ -460,6 +465,9 @@ struct wl1271 { /* HW HT (11n) capabilities */ struct ieee80211_sta_ht_cap ht_cap[WLCORE_NUM_BANDS]; + /* the current dfs region */ + enum nl80211_dfs_regions dfs_region; + /* size of the private FW status data */ size_t fw_status_len; size_t fw_status_priv_len; diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 0e52556044d9..3396ce5a934d 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -251,6 +251,7 @@ enum wl12xx_vif_flags { WLVIF_FLAG_AP_PROBE_RESP_SET, WLVIF_FLAG_IN_USE, WLVIF_FLAG_ACTIVE, + WLVIF_FLAG_BEACON_DISABLED, }; struct wl12xx_vif; @@ -434,6 +435,8 @@ struct wl12xx_vif { bool wmm_enabled; + bool radar_enabled; + /* Rx Streaming */ struct work_struct rx_streaming_enable_work; struct work_struct rx_streaming_disable_work; @@ -463,6 +466,10 @@ struct wl12xx_vif { /* work for canceling ROC after pending auth reply */ struct delayed_work pending_auth_complete_work; + /* update rate conrol */ + enum ieee80211_sta_rx_bandwidth rc_update_bw; + struct work_struct rc_update_work; + /* * total freed FW packets on the link. * For STA this holds the PN of the link to the AP. diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h index 5f1fda44882b..589fa256256b 100644 --- a/drivers/net/xen-netback/common.h +++ b/drivers/net/xen-netback/common.h @@ -251,7 +251,6 @@ struct xenvif { struct xenvif_rx_cb { unsigned long expires; int meta_slots_used; - bool full_coalesce; }; #define XENVIF_RX_CB(skb) ((struct xenvif_rx_cb *)(skb)->cb) diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index 9259a732e8a4..f38227afe099 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -80,7 +80,7 @@ static irqreturn_t xenvif_tx_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -int xenvif_poll(struct napi_struct *napi, int budget) +static int xenvif_poll(struct napi_struct *napi, int budget) { struct xenvif_queue *queue = container_of(napi, struct xenvif_queue, napi); @@ -483,9 +483,8 @@ int xenvif_init_queue(struct xenvif_queue *queue) * better enable it. The long term solution would be to use just a * bunch of valid page descriptors, without dependency on ballooning */ - err = alloc_xenballooned_pages(MAX_PENDING_REQS, - queue->mmap_pages, - false); + err = gnttab_alloc_pages(MAX_PENDING_REQS, + queue->mmap_pages); if (err) { netdev_err(queue->vif->dev, "Could not reserve mmap_pages\n"); return -ENOMEM; @@ -578,6 +577,7 @@ int xenvif_connect(struct xenvif_queue *queue, unsigned long tx_ring_ref, goto err_rx_unbind; } queue->task = task; + get_task_struct(task); task = kthread_create(xenvif_dealloc_kthread, (void *)queue, "%s-dealloc", queue->name); @@ -634,6 +634,7 @@ void xenvif_disconnect(struct xenvif *vif) if (queue->task) { kthread_stop(queue->task); + put_task_struct(queue->task); queue->task = NULL; } @@ -662,7 +663,7 @@ void xenvif_disconnect(struct xenvif *vif) */ void xenvif_deinit_queue(struct xenvif_queue *queue) { - free_xenballooned_pages(MAX_PENDING_REQS, queue->mmap_pages); + gnttab_free_pages(MAX_PENDING_REQS, queue->mmap_pages); } void xenvif_free(struct xenvif *vif) diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 908e65e9b821..f7a31d2cb3f1 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -233,51 +233,6 @@ static void xenvif_rx_queue_drop_expired(struct xenvif_queue *queue) } } -/* - * Returns true if we should start a new receive buffer instead of - * adding 'size' bytes to a buffer which currently contains 'offset' - * bytes. - */ -static bool start_new_rx_buffer(int offset, unsigned long size, int head, - bool full_coalesce) -{ - /* simple case: we have completely filled the current buffer. */ - if (offset == MAX_BUFFER_OFFSET) - return true; - - /* - * complex case: start a fresh buffer if the current frag - * would overflow the current buffer but only if: - * (i) this frag would fit completely in the next buffer - * and (ii) there is already some data in the current buffer - * and (iii) this is not the head buffer. - * and (iv) there is no need to fully utilize the buffers - * - * Where: - * - (i) stops us splitting a frag into two copies - * unless the frag is too large for a single buffer. - * - (ii) stops us from leaving a buffer pointlessly empty. - * - (iii) stops us leaving the first buffer - * empty. Strictly speaking this is already covered - * by (ii) but is explicitly checked because - * netfront relies on the first buffer being - * non-empty and can crash otherwise. - * - (iv) is needed for skbs which can use up more than MAX_SKB_FRAGS - * slot - * - * This means we will effectively linearise small - * frags but do not needlessly split large buffers - * into multiple copies tend to give large frags their - * own buffers as before. - */ - BUG_ON(size > MAX_BUFFER_OFFSET); - if ((offset + size > MAX_BUFFER_OFFSET) && offset && !head && - !full_coalesce) - return true; - - return false; -} - struct netrx_pending_operations { unsigned copy_prod, copy_cons; unsigned meta_prod, meta_cons; @@ -314,9 +269,7 @@ static struct xenvif_rx_meta *get_next_rx_buffer(struct xenvif_queue *queue, static void xenvif_gop_frag_copy(struct xenvif_queue *queue, struct sk_buff *skb, struct netrx_pending_operations *npo, struct page *page, unsigned long size, - unsigned long offset, int *head, - struct xenvif_queue *foreign_queue, - grant_ref_t foreign_gref) + unsigned long offset, int *head) { struct gnttab_copy *copy_gop; struct xenvif_rx_meta *meta; @@ -333,27 +286,18 @@ static void xenvif_gop_frag_copy(struct xenvif_queue *queue, struct sk_buff *skb offset &= ~PAGE_MASK; while (size > 0) { + struct xen_page_foreign *foreign; + BUG_ON(offset >= PAGE_SIZE); BUG_ON(npo->copy_off > MAX_BUFFER_OFFSET); - bytes = PAGE_SIZE - offset; + if (npo->copy_off == MAX_BUFFER_OFFSET) + meta = get_next_rx_buffer(queue, npo); + bytes = PAGE_SIZE - offset; if (bytes > size) bytes = size; - if (start_new_rx_buffer(npo->copy_off, - bytes, - *head, - XENVIF_RX_CB(skb)->full_coalesce)) { - /* - * Netfront requires there to be some data in the head - * buffer. - */ - BUG_ON(*head); - - meta = get_next_rx_buffer(queue, npo); - } - if (npo->copy_off + bytes > MAX_BUFFER_OFFSET) bytes = MAX_BUFFER_OFFSET - npo->copy_off; @@ -361,9 +305,10 @@ static void xenvif_gop_frag_copy(struct xenvif_queue *queue, struct sk_buff *skb copy_gop->flags = GNTCOPY_dest_gref; copy_gop->len = bytes; - if (foreign_queue) { - copy_gop->source.domid = foreign_queue->vif->domid; - copy_gop->source.u.ref = foreign_gref; + foreign = xen_page_foreign(page); + if (foreign) { + copy_gop->source.domid = foreign->domid; + copy_gop->source.u.ref = foreign->gref; copy_gop->flags |= GNTCOPY_source_gref; } else { copy_gop->source.domid = DOMID_SELF; @@ -406,35 +351,6 @@ static void xenvif_gop_frag_copy(struct xenvif_queue *queue, struct sk_buff *skb } /* - * Find the grant ref for a given frag in a chain of struct ubuf_info's - * skb: the skb itself - * i: the frag's number - * ubuf: a pointer to an element in the chain. It should not be NULL - * - * Returns a pointer to the element in the chain where the page were found. If - * not found, returns NULL. - * See the definition of callback_struct in common.h for more details about - * the chain. - */ -static const struct ubuf_info *xenvif_find_gref(const struct sk_buff *const skb, - const int i, - const struct ubuf_info *ubuf) -{ - struct xenvif_queue *foreign_queue = ubuf_to_queue(ubuf); - - do { - u16 pending_idx = ubuf->desc; - - if (skb_shinfo(skb)->frags[i].page.p == - foreign_queue->mmap_pages[pending_idx]) - break; - ubuf = (struct ubuf_info *) ubuf->ctx; - } while (ubuf); - - return ubuf; -} - -/* * Prepare an SKB to be transmitted to the frontend. * * This function is responsible for allocating grant operations, meta @@ -459,8 +375,6 @@ static int xenvif_gop_skb(struct sk_buff *skb, int head = 1; int old_meta_prod; int gso_type; - const struct ubuf_info *ubuf = skb_shinfo(skb)->destructor_arg; - const struct ubuf_info *const head_ubuf = ubuf; old_meta_prod = npo->meta_prod; @@ -507,68 +421,16 @@ static int xenvif_gop_skb(struct sk_buff *skb, len = skb_tail_pointer(skb) - data; xenvif_gop_frag_copy(queue, skb, npo, - virt_to_page(data), len, offset, &head, - NULL, - 0); + virt_to_page(data), len, offset, &head); data += len; } for (i = 0; i < nr_frags; i++) { - /* This variable also signals whether foreign_gref has a real - * value or not. - */ - struct xenvif_queue *foreign_queue = NULL; - grant_ref_t foreign_gref; - - if ((skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) && - (ubuf->callback == &xenvif_zerocopy_callback)) { - const struct ubuf_info *const startpoint = ubuf; - - /* Ideally ubuf points to the chain element which - * belongs to this frag. Or if frags were removed from - * the beginning, then shortly before it. - */ - ubuf = xenvif_find_gref(skb, i, ubuf); - - /* Try again from the beginning of the list, if we - * haven't tried from there. This only makes sense in - * the unlikely event of reordering the original frags. - * For injected local pages it's an unnecessary second - * run. - */ - if (unlikely(!ubuf) && startpoint != head_ubuf) - ubuf = xenvif_find_gref(skb, i, head_ubuf); - - if (likely(ubuf)) { - u16 pending_idx = ubuf->desc; - - foreign_queue = ubuf_to_queue(ubuf); - foreign_gref = - foreign_queue->pending_tx_info[pending_idx].req.gref; - /* Just a safety measure. If this was the last - * element on the list, the for loop will - * iterate again if a local page were added to - * the end. Using head_ubuf here prevents the - * second search on the chain. Or the original - * frags changed order, but that's less likely. - * In any way, ubuf shouldn't be NULL. - */ - ubuf = ubuf->ctx ? - (struct ubuf_info *) ubuf->ctx : - head_ubuf; - } else - /* This frag was a local page, added to the - * array after the skb left netback. - */ - ubuf = head_ubuf; - } xenvif_gop_frag_copy(queue, skb, npo, skb_frag_page(&skb_shinfo(skb)->frags[i]), skb_frag_size(&skb_shinfo(skb)->frags[i]), skb_shinfo(skb)->frags[i].page_offset, - &head, - foreign_queue, - foreign_queue ? foreign_gref : UINT_MAX); + &head); } return npo->meta_prod - old_meta_prod; @@ -652,60 +514,15 @@ static void xenvif_rx_action(struct xenvif_queue *queue) while (xenvif_rx_ring_slots_available(queue, XEN_NETBK_RX_SLOTS_MAX) && (skb = xenvif_rx_dequeue(queue)) != NULL) { - RING_IDX max_slots_needed; RING_IDX old_req_cons; RING_IDX ring_slots_used; - int i; queue->last_rx_time = jiffies; - /* We need a cheap worse case estimate for the number of - * slots we'll use. - */ - - max_slots_needed = DIV_ROUND_UP(offset_in_page(skb->data) + - skb_headlen(skb), - PAGE_SIZE); - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { - unsigned int size; - unsigned int offset; - - size = skb_frag_size(&skb_shinfo(skb)->frags[i]); - offset = skb_shinfo(skb)->frags[i].page_offset; - - /* For a worse-case estimate we need to factor in - * the fragment page offset as this will affect the - * number of times xenvif_gop_frag_copy() will - * call start_new_rx_buffer(). - */ - max_slots_needed += DIV_ROUND_UP(offset + size, - PAGE_SIZE); - } - - /* To avoid the estimate becoming too pessimal for some - * frontends that limit posted rx requests, cap the estimate - * at MAX_SKB_FRAGS. In this case netback will fully coalesce - * the skb into the provided slots. - */ - if (max_slots_needed > MAX_SKB_FRAGS) { - max_slots_needed = MAX_SKB_FRAGS; - XENVIF_RX_CB(skb)->full_coalesce = true; - } else { - XENVIF_RX_CB(skb)->full_coalesce = false; - } - - /* We may need one more slot for GSO metadata */ - if (skb_is_gso(skb) && - (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4 || - skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6)) - max_slots_needed++; - old_req_cons = queue->rx.req_cons; XENVIF_RX_CB(skb)->meta_slots_used = xenvif_gop_skb(skb, &npo, queue); ring_slots_used = queue->rx.req_cons - old_req_cons; - BUG_ON(ring_slots_used > max_slots_needed); - __skb_queue_tail(&rxq, skb); } @@ -1241,12 +1058,6 @@ static void xenvif_fill_frags(struct xenvif_queue *queue, struct sk_buff *skb) /* Take an extra reference to offset network stack's put_page */ get_page(queue->mmap_pages[pending_idx]); } - /* FIXME: __skb_fill_page_desc set this to true because page->pfmemalloc - * overlaps with "index", and "mapping" is not set. I think mapping - * should be set. If delivered to local stack, it would drop this - * skb in sk_filter unless the socket has the right to use it. - */ - skb->pfmemalloc = false; } static int xenvif_get_extras(struct xenvif_queue *queue, @@ -2109,8 +1920,7 @@ int xenvif_kthread_guest_rx(void *data) */ if (unlikely(vif->disabled && queue->id == 0)) { xenvif_carrier_off(vif); - xenvif_rx_queue_purge(queue); - continue; + break; } if (!skb_queue_empty(&queue->rx_queue)) diff --git a/drivers/net/xen-netback/xenbus.c b/drivers/net/xen-netback/xenbus.c index efbaf2ae1999..794204e34fba 100644 --- a/drivers/net/xen-netback/xenbus.c +++ b/drivers/net/xen-netback/xenbus.c @@ -737,6 +737,7 @@ static void connect(struct backend_info *be) } queue->remaining_credit = credit_bytes; + queue->credit_usec = credit_usec; err = connect_rings(be, queue); if (err) { diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index 22bcb4e12e2a..e9b960f0ff32 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -88,10 +88,8 @@ struct netfront_cb { #define IRQ_NAME_SIZE (QUEUE_NAME_SIZE + 3) struct netfront_stats { - u64 rx_packets; - u64 tx_packets; - u64 rx_bytes; - u64 tx_bytes; + u64 packets; + u64 bytes; struct u64_stats_sync syncp; }; @@ -144,10 +142,6 @@ struct netfront_queue { struct sk_buff *rx_skbs[NET_RX_RING_SIZE]; grant_ref_t gref_rx_head; grant_ref_t grant_rx_ref[NET_RX_RING_SIZE]; - - unsigned long rx_pfn_array[NET_RX_RING_SIZE]; - struct multicall_entry rx_mcl[NET_RX_RING_SIZE+1]; - struct mmu_update rx_mmu[NET_RX_RING_SIZE]; }; struct netfront_info { @@ -160,7 +154,8 @@ struct netfront_info { struct netfront_queue *queues; /* Statistics */ - struct netfront_stats __percpu *stats; + struct netfront_stats __percpu *rx_stats; + struct netfront_stats __percpu *tx_stats; atomic_t rx_gso_checksum_fixup; }; @@ -224,11 +219,7 @@ static grant_ref_t xennet_get_rx_ref(struct netfront_queue *queue, } #ifdef CONFIG_SYSFS -static int xennet_sysfs_addif(struct net_device *netdev); -static void xennet_sysfs_delif(struct net_device *netdev); -#else /* !CONFIG_SYSFS */ -#define xennet_sysfs_addif(dev) (0) -#define xennet_sysfs_delif(dev) do { } while (0) +static const struct attribute_group xennet_dev_group; #endif static bool xennet_can_sg(struct net_device *dev) @@ -425,109 +416,68 @@ static void xennet_tx_buf_gc(struct netfront_queue *queue) xennet_maybe_wake_tx(queue); } -static void xennet_make_frags(struct sk_buff *skb, struct netfront_queue *queue, - struct xen_netif_tx_request *tx) +static struct xen_netif_tx_request *xennet_make_one_txreq( + struct netfront_queue *queue, struct sk_buff *skb, + struct page *page, unsigned int offset, unsigned int len) { - char *data = skb->data; - unsigned long mfn; - RING_IDX prod = queue->tx.req_prod_pvt; - int frags = skb_shinfo(skb)->nr_frags; - unsigned int offset = offset_in_page(data); - unsigned int len = skb_headlen(skb); unsigned int id; + struct xen_netif_tx_request *tx; grant_ref_t ref; - int i; - /* While the header overlaps a page boundary (including being - larger than a page), split it it into page-sized chunks. */ - while (len > PAGE_SIZE - offset) { - tx->size = PAGE_SIZE - offset; - tx->flags |= XEN_NETTXF_more_data; - len -= tx->size; - data += tx->size; - offset = 0; + len = min_t(unsigned int, PAGE_SIZE - offset, len); - id = get_id_from_freelist(&queue->tx_skb_freelist, queue->tx_skbs); - queue->tx_skbs[id].skb = skb_get(skb); - tx = RING_GET_REQUEST(&queue->tx, prod++); - tx->id = id; - ref = gnttab_claim_grant_reference(&queue->gref_tx_head); - BUG_ON((signed short)ref < 0); + id = get_id_from_freelist(&queue->tx_skb_freelist, queue->tx_skbs); + tx = RING_GET_REQUEST(&queue->tx, queue->tx.req_prod_pvt++); + ref = gnttab_claim_grant_reference(&queue->gref_tx_head); + BUG_ON((signed short)ref < 0); - mfn = virt_to_mfn(data); - gnttab_grant_foreign_access_ref(ref, queue->info->xbdev->otherend_id, - mfn, GNTMAP_readonly); + gnttab_grant_foreign_access_ref(ref, queue->info->xbdev->otherend_id, + page_to_mfn(page), GNTMAP_readonly); - queue->grant_tx_page[id] = virt_to_page(data); - tx->gref = queue->grant_tx_ref[id] = ref; - tx->offset = offset; - tx->size = len; - tx->flags = 0; - } + queue->tx_skbs[id].skb = skb; + queue->grant_tx_page[id] = page; + queue->grant_tx_ref[id] = ref; - /* Grant backend access to each skb fragment page. */ - for (i = 0; i < frags; i++) { - skb_frag_t *frag = skb_shinfo(skb)->frags + i; - struct page *page = skb_frag_page(frag); + tx->id = id; + tx->gref = ref; + tx->offset = offset; + tx->size = len; + tx->flags = 0; - len = skb_frag_size(frag); - offset = frag->page_offset; + return tx; +} - /* Skip unused frames from start of page */ - page += offset >> PAGE_SHIFT; - offset &= ~PAGE_MASK; +static struct xen_netif_tx_request *xennet_make_txreqs( + struct netfront_queue *queue, struct xen_netif_tx_request *tx, + struct sk_buff *skb, struct page *page, + unsigned int offset, unsigned int len) +{ + /* Skip unused frames from start of page */ + page += offset >> PAGE_SHIFT; + offset &= ~PAGE_MASK; - while (len > 0) { - unsigned long bytes; - - bytes = PAGE_SIZE - offset; - if (bytes > len) - bytes = len; - - tx->flags |= XEN_NETTXF_more_data; - - id = get_id_from_freelist(&queue->tx_skb_freelist, - queue->tx_skbs); - queue->tx_skbs[id].skb = skb_get(skb); - tx = RING_GET_REQUEST(&queue->tx, prod++); - tx->id = id; - ref = gnttab_claim_grant_reference(&queue->gref_tx_head); - BUG_ON((signed short)ref < 0); - - mfn = pfn_to_mfn(page_to_pfn(page)); - gnttab_grant_foreign_access_ref(ref, - queue->info->xbdev->otherend_id, - mfn, GNTMAP_readonly); - - queue->grant_tx_page[id] = page; - tx->gref = queue->grant_tx_ref[id] = ref; - tx->offset = offset; - tx->size = bytes; - tx->flags = 0; - - offset += bytes; - len -= bytes; - - /* Next frame */ - if (offset == PAGE_SIZE && len) { - BUG_ON(!PageCompound(page)); - page++; - offset = 0; - } - } + while (len) { + tx->flags |= XEN_NETTXF_more_data; + tx = xennet_make_one_txreq(queue, skb_get(skb), + page, offset, len); + page++; + offset = 0; + len -= tx->size; } - queue->tx.req_prod_pvt = prod; + return tx; } /* - * Count how many ring slots are required to send the frags of this - * skb. Each frag might be a compound page. + * Count how many ring slots are required to send this skb. Each frag + * might be a compound page. */ -static int xennet_count_skb_frag_slots(struct sk_buff *skb) +static int xennet_count_skb_slots(struct sk_buff *skb) { int i, frags = skb_shinfo(skb)->nr_frags; - int pages = 0; + int pages; + + pages = PFN_UP(offset_in_page(skb->data) + skb_headlen(skb)); for (i = 0; i < frags; i++) { skb_frag_t *frag = skb_shinfo(skb)->frags + i; @@ -563,18 +513,15 @@ static u16 xennet_select_queue(struct net_device *dev, struct sk_buff *skb, static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev) { - unsigned short id; struct netfront_info *np = netdev_priv(dev); - struct netfront_stats *stats = this_cpu_ptr(np->stats); - struct xen_netif_tx_request *tx; - char *data = skb->data; - RING_IDX i; - grant_ref_t ref; - unsigned long mfn; + struct netfront_stats *tx_stats = this_cpu_ptr(np->tx_stats); + struct xen_netif_tx_request *tx, *first_tx; + unsigned int i; int notify; int slots; - unsigned int offset = offset_in_page(data); - unsigned int len = skb_headlen(skb); + struct page *page; + unsigned int offset; + unsigned int len; unsigned long flags; struct netfront_queue *queue = NULL; unsigned int num_queues = dev->real_num_tx_queues; @@ -597,18 +544,18 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev) goto drop; } - slots = DIV_ROUND_UP(offset + len, PAGE_SIZE) + - xennet_count_skb_frag_slots(skb); + slots = xennet_count_skb_slots(skb); if (unlikely(slots > MAX_SKB_FRAGS + 1)) { net_dbg_ratelimited("xennet: skb rides the rocket: %d slots, %d bytes\n", slots, skb->len); if (skb_linearize(skb)) goto drop; - data = skb->data; - offset = offset_in_page(data); - len = skb_headlen(skb); } + page = virt_to_page(skb->data); + offset = offset_in_page(skb->data); + len = skb_headlen(skb); + spin_lock_irqsave(&queue->tx_lock, flags); if (unlikely(!netif_carrier_ok(dev) || @@ -618,25 +565,13 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev) goto drop; } - i = queue->tx.req_prod_pvt; - - id = get_id_from_freelist(&queue->tx_skb_freelist, queue->tx_skbs); - queue->tx_skbs[id].skb = skb; - - tx = RING_GET_REQUEST(&queue->tx, i); - - tx->id = id; - ref = gnttab_claim_grant_reference(&queue->gref_tx_head); - BUG_ON((signed short)ref < 0); - mfn = virt_to_mfn(data); - gnttab_grant_foreign_access_ref( - ref, queue->info->xbdev->otherend_id, mfn, GNTMAP_readonly); - queue->grant_tx_page[id] = virt_to_page(data); - tx->gref = queue->grant_tx_ref[id] = ref; - tx->offset = offset; - tx->size = len; + /* First request for the linear area. */ + first_tx = tx = xennet_make_one_txreq(queue, skb, + page, offset, len); + page++; + offset = 0; + len -= tx->size; - tx->flags = 0; if (skb->ip_summed == CHECKSUM_PARTIAL) /* local packet? */ tx->flags |= XEN_NETTXF_csum_blank | XEN_NETTXF_data_validated; @@ -644,11 +579,12 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev) /* remote but checksummed. */ tx->flags |= XEN_NETTXF_data_validated; + /* Optional extra info after the first request. */ if (skb_shinfo(skb)->gso_size) { struct xen_netif_extra_info *gso; gso = (struct xen_netif_extra_info *) - RING_GET_REQUEST(&queue->tx, ++i); + RING_GET_REQUEST(&queue->tx, queue->tx.req_prod_pvt++); tx->flags |= XEN_NETTXF_extra_info; @@ -663,19 +599,28 @@ static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev) gso->flags = 0; } - queue->tx.req_prod_pvt = i + 1; + /* Requests for the rest of the linear area. */ + tx = xennet_make_txreqs(queue, tx, skb, page, offset, len); + + /* Requests for all the frags. */ + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + tx = xennet_make_txreqs(queue, tx, skb, + skb_frag_page(frag), frag->page_offset, + skb_frag_size(frag)); + } - xennet_make_frags(skb, queue, tx); - tx->size = skb->len; + /* First request has the packet length. */ + first_tx->size = skb->len; RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&queue->tx, notify); if (notify) notify_remote_via_irq(queue->tx_irq); - u64_stats_update_begin(&stats->syncp); - stats->tx_bytes += skb->len; - stats->tx_packets++; - u64_stats_update_end(&stats->syncp); + u64_stats_update_begin(&tx_stats->syncp); + tx_stats->bytes += skb->len; + tx_stats->packets++; + u64_stats_update_end(&tx_stats->syncp); /* Note: It is not safe to access skb after xennet_tx_buf_gc()! */ xennet_tx_buf_gc(queue); @@ -931,7 +876,7 @@ static int checksum_setup(struct net_device *dev, struct sk_buff *skb) static int handle_incoming_queue(struct netfront_queue *queue, struct sk_buff_head *rxq) { - struct netfront_stats *stats = this_cpu_ptr(queue->info->stats); + struct netfront_stats *rx_stats = this_cpu_ptr(queue->info->rx_stats); int packets_dropped = 0; struct sk_buff *skb; @@ -952,10 +897,10 @@ static int handle_incoming_queue(struct netfront_queue *queue, continue; } - u64_stats_update_begin(&stats->syncp); - stats->rx_packets++; - stats->rx_bytes += skb->len; - u64_stats_update_end(&stats->syncp); + u64_stats_update_begin(&rx_stats->syncp); + rx_stats->packets++; + rx_stats->bytes += skb->len; + u64_stats_update_end(&rx_stats->syncp); /* Pass it up. */ napi_gro_receive(&queue->napi, skb); @@ -1079,18 +1024,22 @@ static struct rtnl_link_stats64 *xennet_get_stats64(struct net_device *dev, int cpu; for_each_possible_cpu(cpu) { - struct netfront_stats *stats = per_cpu_ptr(np->stats, cpu); + struct netfront_stats *rx_stats = per_cpu_ptr(np->rx_stats, cpu); + struct netfront_stats *tx_stats = per_cpu_ptr(np->tx_stats, cpu); u64 rx_packets, rx_bytes, tx_packets, tx_bytes; unsigned int start; do { - start = u64_stats_fetch_begin_irq(&stats->syncp); + start = u64_stats_fetch_begin_irq(&tx_stats->syncp); + tx_packets = tx_stats->packets; + tx_bytes = tx_stats->bytes; + } while (u64_stats_fetch_retry_irq(&tx_stats->syncp, start)); - rx_packets = stats->rx_packets; - tx_packets = stats->tx_packets; - rx_bytes = stats->rx_bytes; - tx_bytes = stats->tx_bytes; - } while (u64_stats_fetch_retry_irq(&stats->syncp, start)); + do { + start = u64_stats_fetch_begin_irq(&rx_stats->syncp); + rx_packets = rx_stats->packets; + rx_bytes = rx_stats->bytes; + } while (u64_stats_fetch_retry_irq(&rx_stats->syncp, start)); tot->rx_packets += rx_packets; tot->tx_packets += tx_packets; @@ -1275,6 +1224,15 @@ static const struct net_device_ops xennet_netdev_ops = { #endif }; +static void xennet_free_netdev(struct net_device *netdev) +{ + struct netfront_info *np = netdev_priv(netdev); + + free_percpu(np->rx_stats); + free_percpu(np->tx_stats); + free_netdev(netdev); +} + static struct net_device *xennet_create_dev(struct xenbus_device *dev) { int err; @@ -1295,8 +1253,11 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev) np->queues = NULL; err = -ENOMEM; - np->stats = netdev_alloc_pcpu_stats(struct netfront_stats); - if (np->stats == NULL) + np->rx_stats = netdev_alloc_pcpu_stats(struct netfront_stats); + if (np->rx_stats == NULL) + goto exit; + np->tx_stats = netdev_alloc_pcpu_stats(struct netfront_stats); + if (np->tx_stats == NULL) goto exit; netdev->netdev_ops = &xennet_netdev_ops; @@ -1327,7 +1288,7 @@ static struct net_device *xennet_create_dev(struct xenbus_device *dev) return netdev; exit: - free_netdev(netdev); + xennet_free_netdev(netdev); return ERR_PTR(err); } @@ -1352,24 +1313,19 @@ static int netfront_probe(struct xenbus_device *dev, info = netdev_priv(netdev); dev_set_drvdata(&dev->dev, info); - +#ifdef CONFIG_SYSFS + info->netdev->sysfs_groups[0] = &xennet_dev_group; +#endif err = register_netdev(info->netdev); if (err) { pr_warn("%s: register_netdev err=%d\n", __func__, err); goto fail; } - err = xennet_sysfs_addif(info->netdev); - if (err) { - unregister_netdev(info->netdev); - pr_warn("%s: add sysfs failed err=%d\n", __func__, err); - goto fail; - } - return 0; fail: - free_netdev(netdev); + xennet_free_netdev(netdev); dev_set_drvdata(&dev->dev, NULL); return err; } @@ -2129,39 +2085,20 @@ static ssize_t store_rxbuf(struct device *dev, return len; } -static struct device_attribute xennet_attrs[] = { - __ATTR(rxbuf_min, S_IRUGO|S_IWUSR, show_rxbuf, store_rxbuf), - __ATTR(rxbuf_max, S_IRUGO|S_IWUSR, show_rxbuf, store_rxbuf), - __ATTR(rxbuf_cur, S_IRUGO, show_rxbuf, NULL), -}; +static DEVICE_ATTR(rxbuf_min, S_IRUGO|S_IWUSR, show_rxbuf, store_rxbuf); +static DEVICE_ATTR(rxbuf_max, S_IRUGO|S_IWUSR, show_rxbuf, store_rxbuf); +static DEVICE_ATTR(rxbuf_cur, S_IRUGO, show_rxbuf, NULL); -static int xennet_sysfs_addif(struct net_device *netdev) -{ - int i; - int err; - - for (i = 0; i < ARRAY_SIZE(xennet_attrs); i++) { - err = device_create_file(&netdev->dev, - &xennet_attrs[i]); - if (err) - goto fail; - } - return 0; - - fail: - while (--i >= 0) - device_remove_file(&netdev->dev, &xennet_attrs[i]); - return err; -} - -static void xennet_sysfs_delif(struct net_device *netdev) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(xennet_attrs); i++) - device_remove_file(&netdev->dev, &xennet_attrs[i]); -} +static struct attribute *xennet_dev_attrs[] = { + &dev_attr_rxbuf_min.attr, + &dev_attr_rxbuf_max.attr, + &dev_attr_rxbuf_cur.attr, + NULL +}; +static const struct attribute_group xennet_dev_group = { + .attrs = xennet_dev_attrs +}; #endif /* CONFIG_SYSFS */ static int xennet_remove(struct xenbus_device *dev) @@ -2175,8 +2112,6 @@ static int xennet_remove(struct xenbus_device *dev) xennet_disconnect_backend(info); - xennet_sysfs_delif(info->netdev); - unregister_netdev(info->netdev); for (i = 0; i < num_queues; ++i) { @@ -2189,9 +2124,7 @@ static int xennet_remove(struct xenbus_device *dev) info->queues = NULL; } - free_percpu(info->stats); - - free_netdev(info->netdev); + xennet_free_netdev(info->netdev); return 0; } diff --git a/drivers/nfc/microread/microread.c b/drivers/nfc/microread/microread.c index 963a4a5dc88e..f454dc68cc03 100644 --- a/drivers/nfc/microread/microread.c +++ b/drivers/nfc/microread/microread.c @@ -557,10 +557,11 @@ exit: pr_err("Failed to handle discovered target err=%d\n", r); } -static int microread_event_received(struct nfc_hci_dev *hdev, u8 gate, +static int microread_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event, struct sk_buff *skb) { int r; + u8 gate = hdev->pipes[pipe].gate; u8 mode; pr_info("Microread received event 0x%x to gate 0x%x\n", event, gate); diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index fc02e8d6a193..cdde745b96bd 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -24,11 +24,13 @@ #include <linux/gpio.h> #include <linux/of_gpio.h> #include <linux/of_irq.h> +#include <linux/acpi.h> #include <linux/miscdevice.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/nfc.h> #include <linux/firmware.h> +#include <linux/gpio/consumer.h> #include <linux/platform_data/pn544.h> #include <asm/unaligned.h> @@ -41,6 +43,11 @@ #define PN544_I2C_FRAME_HEADROOM 1 #define PN544_I2C_FRAME_TAILROOM 2 +/* GPIO names */ +#define PN544_GPIO_NAME_IRQ "pn544_irq" +#define PN544_GPIO_NAME_FW "pn544_fw" +#define PN544_GPIO_NAME_EN "pn544_en" + /* framing in HCI mode */ #define PN544_HCI_I2C_LLC_LEN 1 #define PN544_HCI_I2C_LLC_CRC 2 @@ -58,6 +65,13 @@ static struct i2c_device_id pn544_hci_i2c_id_table[] = { MODULE_DEVICE_TABLE(i2c, pn544_hci_i2c_id_table); +static const struct acpi_device_id pn544_hci_i2c_acpi_match[] = { + {"NXP5440", 0}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, pn544_hci_i2c_acpi_match); + #define PN544_HCI_I2C_DRIVER_NAME "pn544_hci_i2c" /* @@ -195,18 +209,19 @@ static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy) nfc_info(&phy->i2c_dev->dev, "Detecting nfc_en polarity\n"); /* Disable fw download */ - gpio_set_value(phy->gpio_fw, 0); + gpio_set_value_cansleep(phy->gpio_fw, 0); for (polarity = 0; polarity < 2; polarity++) { phy->en_polarity = polarity; retry = 3; while (retry--) { /* power off */ - gpio_set_value(phy->gpio_en, !phy->en_polarity); + gpio_set_value_cansleep(phy->gpio_en, + !phy->en_polarity); usleep_range(10000, 15000); /* power on */ - gpio_set_value(phy->gpio_en, phy->en_polarity); + gpio_set_value_cansleep(phy->gpio_en, phy->en_polarity); usleep_range(10000, 15000); /* send reset */ @@ -225,13 +240,14 @@ static void pn544_hci_i2c_platform_init(struct pn544_i2c_phy *phy) "Could not detect nfc_en polarity, fallback to active high\n"); out: - gpio_set_value(phy->gpio_en, !phy->en_polarity); + gpio_set_value_cansleep(phy->gpio_en, !phy->en_polarity); } static void pn544_hci_i2c_enable_mode(struct pn544_i2c_phy *phy, int run_mode) { - gpio_set_value(phy->gpio_fw, run_mode == PN544_FW_MODE ? 1 : 0); - gpio_set_value(phy->gpio_en, phy->en_polarity); + gpio_set_value_cansleep(phy->gpio_fw, + run_mode == PN544_FW_MODE ? 1 : 0); + gpio_set_value_cansleep(phy->gpio_en, phy->en_polarity); usleep_range(10000, 15000); phy->run_mode = run_mode; @@ -254,14 +270,14 @@ static void pn544_hci_i2c_disable(void *phy_id) { struct pn544_i2c_phy *phy = phy_id; - gpio_set_value(phy->gpio_fw, 0); - gpio_set_value(phy->gpio_en, !phy->en_polarity); + gpio_set_value_cansleep(phy->gpio_fw, 0); + gpio_set_value_cansleep(phy->gpio_en, !phy->en_polarity); usleep_range(10000, 15000); - gpio_set_value(phy->gpio_en, phy->en_polarity); + gpio_set_value_cansleep(phy->gpio_en, phy->en_polarity); usleep_range(10000, 15000); - gpio_set_value(phy->gpio_en, !phy->en_polarity); + gpio_set_value_cansleep(phy->gpio_en, !phy->en_polarity); usleep_range(10000, 15000); phy->powered = 0; @@ -859,6 +875,90 @@ exit_state_wait_secure_write_answer: } } +static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client) +{ + struct pn544_i2c_phy *phy = i2c_get_clientdata(client); + const struct acpi_device_id *id; + struct gpio_desc *gpiod_en, *gpiod_irq, *gpiod_fw; + struct device *dev; + int ret; + + if (!client) + return -EINVAL; + + dev = &client->dev; + + /* Match the struct device against a given list of ACPI IDs */ + id = acpi_match_device(dev->driver->acpi_match_table, dev); + + if (!id) + return -ENODEV; + + /* Get EN GPIO from ACPI */ + gpiod_en = devm_gpiod_get_index(dev, PN544_GPIO_NAME_EN, 1); + if (IS_ERR(gpiod_en)) { + nfc_err(dev, + "Unable to get EN GPIO\n"); + return -ENODEV; + } + + phy->gpio_en = desc_to_gpio(gpiod_en); + + /* Configuration EN GPIO */ + ret = gpiod_direction_output(gpiod_en, 0); + if (ret) { + nfc_err(dev, "Fail EN pin direction\n"); + return ret; + } + + /* Get FW GPIO from ACPI */ + gpiod_fw = devm_gpiod_get_index(dev, PN544_GPIO_NAME_FW, 2); + if (IS_ERR(gpiod_fw)) { + nfc_err(dev, + "Unable to get FW GPIO\n"); + return -ENODEV; + } + + phy->gpio_fw = desc_to_gpio(gpiod_fw); + + /* Configuration FW GPIO */ + ret = gpiod_direction_output(gpiod_fw, 0); + if (ret) { + nfc_err(dev, "Fail FW pin direction\n"); + return ret; + } + + /* Get IRQ GPIO */ + gpiod_irq = devm_gpiod_get_index(dev, PN544_GPIO_NAME_IRQ, 0); + if (IS_ERR(gpiod_irq)) { + nfc_err(dev, + "Unable to get IRQ GPIO\n"); + return -ENODEV; + } + + phy->gpio_irq = desc_to_gpio(gpiod_irq); + + /* Configure IRQ GPIO */ + ret = gpiod_direction_input(gpiod_irq); + if (ret) { + nfc_err(dev, "Fail IRQ pin direction\n"); + return ret; + } + + /* Map the pin to an IRQ */ + ret = gpiod_to_irq(gpiod_irq); + if (ret < 0) { + nfc_err(dev, "Fail pin IRQ mapping\n"); + return ret; + } + + nfc_info(dev, "GPIO resource, no:%d irq:%d\n", + desc_to_gpio(gpiod_irq), ret); + client->irq = ret; + + return 0; +} + #ifdef CONFIG_OF static int pn544_hci_i2c_of_request_resources(struct i2c_client *client) @@ -884,7 +984,7 @@ static int pn544_hci_i2c_of_request_resources(struct i2c_client *client) phy->gpio_en = ret; /* Configuration of EN GPIO */ - ret = gpio_request(phy->gpio_en, "pn544_en"); + ret = gpio_request(phy->gpio_en, PN544_GPIO_NAME_EN); if (ret) { nfc_err(&client->dev, "Fail EN pin\n"); goto err_dt; @@ -906,7 +1006,7 @@ static int pn544_hci_i2c_of_request_resources(struct i2c_client *client) phy->gpio_fw = ret; /* Configuration of FW GPIO */ - ret = gpio_request(phy->gpio_fw, "pn544_fw"); + ret = gpio_request(phy->gpio_fw, PN544_GPIO_NAME_FW); if (ret) { nfc_err(&client->dev, "Fail FW pin\n"); goto err_gpio_en; @@ -1001,6 +1101,14 @@ static int pn544_hci_i2c_probe(struct i2c_client *client, phy->gpio_en = pdata->get_gpio(NFC_GPIO_ENABLE); phy->gpio_fw = pdata->get_gpio(NFC_GPIO_FW_RESET); phy->gpio_irq = pdata->get_gpio(NFC_GPIO_IRQ); + /* Using ACPI */ + } else if (ACPI_HANDLE(&client->dev)) { + r = pn544_hci_i2c_acpi_request_resources(client); + if (r) { + nfc_err(&client->dev, + "Cannot get ACPI data\n"); + return r; + } } else { nfc_err(&client->dev, "No platform data\n"); return -EINVAL; @@ -1080,6 +1188,7 @@ static struct i2c_driver pn544_hci_i2c_driver = { .name = PN544_HCI_I2C_DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(of_pn544_i2c_match), + .acpi_match_table = ACPI_PTR(pn544_hci_i2c_acpi_match), }, .probe = pn544_hci_i2c_probe, .id_table = pn544_hci_i2c_id_table, diff --git a/drivers/nfc/pn544/pn544.c b/drivers/nfc/pn544/pn544.c index 9c8051d20cea..12e819ddf17a 100644 --- a/drivers/nfc/pn544/pn544.c +++ b/drivers/nfc/pn544/pn544.c @@ -724,10 +724,11 @@ static int pn544_hci_check_presence(struct nfc_hci_dev *hdev, * <= 0: driver handled the event, skb consumed * 1: driver does not handle the event, please do standard processing */ -static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, u8 event, +static int pn544_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event, struct sk_buff *skb) { struct sk_buff *rgb_skb = NULL; + u8 gate = hdev->pipes[pipe].gate; int r; pr_debug("hci event %d\n", event); diff --git a/drivers/nfc/st21nfca/Makefile b/drivers/nfc/st21nfca/Makefile index 7d688f97aa27..97edab4bbdf8 100644 --- a/drivers/nfc/st21nfca/Makefile +++ b/drivers/nfc/st21nfca/Makefile @@ -2,7 +2,7 @@ # Makefile for ST21NFCA HCI based NFC driver # -st21nfca_hci-objs = st21nfca.o st21nfca_dep.o +st21nfca_hci-objs = st21nfca.o st21nfca_dep.o st21nfca_se.o obj-$(CONFIG_NFC_ST21NFCA) += st21nfca_hci.o st21nfca_i2c-objs = i2c.o diff --git a/drivers/nfc/st21nfca/i2c.c b/drivers/nfc/st21nfca/i2c.c index 05722085a59f..a32143951616 100644 --- a/drivers/nfc/st21nfca/i2c.c +++ b/drivers/nfc/st21nfca/i2c.c @@ -74,6 +74,8 @@ struct st21nfca_i2c_phy { unsigned int gpio_ena; unsigned int irq_polarity; + struct st21nfca_se_status se_status; + struct sk_buff *pending_skb; int current_read_len; /* @@ -537,6 +539,11 @@ static int st21nfca_hci_i2c_of_request_resources(struct i2c_client *client) phy->irq_polarity = irq_get_trigger_type(client->irq); + phy->se_status.is_ese_present = + of_property_read_bool(pp, "ese-present"); + phy->se_status.is_uicc_present = + of_property_read_bool(pp, "uicc-present"); + return 0; } #else @@ -571,6 +578,9 @@ static int st21nfca_hci_i2c_request_resources(struct i2c_client *client) } } + phy->se_status.is_ese_present = pdata->is_ese_present; + phy->se_status.is_uicc_present = pdata->is_uicc_present; + return 0; } @@ -591,11 +601,8 @@ static int st21nfca_hci_i2c_probe(struct i2c_client *client, phy = devm_kzalloc(&client->dev, sizeof(struct st21nfca_i2c_phy), GFP_KERNEL); - if (!phy) { - nfc_err(&client->dev, - "Cannot allocate memory for st21nfca i2c phy.\n"); + if (!phy) return -ENOMEM; - } phy->i2c_dev = client; phy->pending_skb = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE * 2, GFP_KERNEL); @@ -641,8 +648,11 @@ static int st21nfca_hci_i2c_probe(struct i2c_client *client, } return st21nfca_hci_probe(phy, &i2c_phy_ops, LLC_SHDLC_NAME, - ST21NFCA_FRAME_HEADROOM, ST21NFCA_FRAME_TAILROOM, - ST21NFCA_HCI_LLC_MAX_PAYLOAD, &phy->hdev); + ST21NFCA_FRAME_HEADROOM, + ST21NFCA_FRAME_TAILROOM, + ST21NFCA_HCI_LLC_MAX_PAYLOAD, + &phy->hdev, + &phy->se_status); } static int st21nfca_hci_i2c_remove(struct i2c_client *client) @@ -661,6 +671,7 @@ static int st21nfca_hci_i2c_remove(struct i2c_client *client) #ifdef CONFIG_OF static const struct of_device_id of_st21nfca_i2c_match[] = { + { .compatible = "st,st21nfca-i2c", }, { .compatible = "st,st21nfca_i2c", }, {} }; diff --git a/drivers/nfc/st21nfca/st21nfca.c b/drivers/nfc/st21nfca/st21nfca.c index f2596c8d68b0..24d3d240d5f4 100644 --- a/drivers/nfc/st21nfca/st21nfca.c +++ b/drivers/nfc/st21nfca/st21nfca.c @@ -23,6 +23,7 @@ #include "st21nfca.h" #include "st21nfca_dep.h" +#include "st21nfca_se.h" #define DRIVER_DESC "HCI NFC driver for ST21NFCA" @@ -62,7 +63,6 @@ #define ST21NFCA_RF_CARD_F_DATARATE 0x08 #define ST21NFCA_RF_CARD_F_DATARATE_212_424 0x01 -#define ST21NFCA_DEVICE_MGNT_GATE 0x01 #define ST21NFCA_DEVICE_MGNT_PIPE 0x02 #define ST21NFCA_DM_GETINFO 0x13 @@ -78,6 +78,11 @@ #define ST21NFCA_NFC_MODE 0x03 /* NFC_MODE parameter*/ +#define ST21NFCA_EVT_HOT_PLUG 0x03 +#define ST21NFCA_EVT_HOT_PLUG_IS_INHIBITED(x) (x->data[0] & 0x80) + +#define ST21NFCA_SE_TO_PIPES 2000 + static DECLARE_BITMAP(dev_mask, ST21NFCA_NUM_DEVICES); static struct nfc_hci_gate st21nfca_gates[] = { @@ -92,6 +97,10 @@ static struct nfc_hci_gate st21nfca_gates[] = { {ST21NFCA_RF_READER_14443_3_A_GATE, NFC_HCI_INVALID_PIPE}, {ST21NFCA_RF_READER_ISO15693_GATE, NFC_HCI_INVALID_PIPE}, {ST21NFCA_RF_CARD_F_GATE, NFC_HCI_INVALID_PIPE}, + + /* Secure element pipes are created by secure element host */ + {ST21NFCA_CONNECTIVITY_GATE, NFC_HCI_DO_NOT_CREATE_PIPE}, + {ST21NFCA_APDU_READER_GATE, NFC_HCI_DO_NOT_CREATE_PIPE}, }; struct st21nfca_pipe_info { @@ -118,18 +127,6 @@ static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev) NFC_HCI_TERMINAL_HOST_ID, 0 }; - skb_pipe_list = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE, GFP_KERNEL); - if (!skb_pipe_list) { - r = -ENOMEM; - goto free_list; - } - - skb_pipe_info = alloc_skb(ST21NFCA_HCI_LLC_MAX_SIZE, GFP_KERNEL); - if (!skb_pipe_info) { - r = -ENOMEM; - goto free_info; - } - /* On ST21NFCA device pipes number are dynamics * A maximum of 16 pipes can be created at the same time * If pipes are already created, hci_dev_up will fail. @@ -148,7 +145,8 @@ static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev) * Pipe can be closed and need to be open. */ r = nfc_hci_connect_gate(hdev, NFC_HCI_HOST_CONTROLLER_ID, - ST21NFCA_DEVICE_MGNT_GATE, ST21NFCA_DEVICE_MGNT_PIPE); + ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_DEVICE_MGNT_PIPE); if (r < 0) goto free_info; @@ -179,17 +177,28 @@ static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev) * - destination gid (1byte) */ info = (struct st21nfca_pipe_info *) skb_pipe_info->data; + if (info->dst_gate_id == ST21NFCA_APDU_READER_GATE && + info->src_host_id != ST21NFCA_ESE_HOST_ID) { + pr_err("Unexpected apdu_reader pipe on host %x\n", + info->src_host_id); + continue; + } + for (j = 0; (j < ARRAY_SIZE(st21nfca_gates)) && - (st21nfca_gates[j].gate != info->dst_gate_id); - j++) + (st21nfca_gates[j].gate != info->dst_gate_id) ; j++) ; if (j < ARRAY_SIZE(st21nfca_gates) && st21nfca_gates[j].gate == info->dst_gate_id && ST21NFCA_DM_IS_PIPE_OPEN(info->pipe_state)) { st21nfca_gates[j].pipe = pipe_info[2]; + hdev->gate2pipe[st21nfca_gates[j].gate] = - st21nfca_gates[j].pipe; + st21nfca_gates[j].pipe; + hdev->pipes[st21nfca_gates[j].pipe].gate = + st21nfca_gates[j].gate; + hdev->pipes[st21nfca_gates[j].pipe].dest_host = + info->src_host_id; } } @@ -199,7 +208,7 @@ static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev) */ if (skb_pipe_list->len + 3 < ARRAY_SIZE(st21nfca_gates)) { for (i = skb_pipe_list->len + 3; - i < ARRAY_SIZE(st21nfca_gates); i++) { + i < ARRAY_SIZE(st21nfca_gates) - 2; i++) { r = nfc_hci_connect_gate(hdev, NFC_HCI_HOST_CONTROLLER_ID, st21nfca_gates[i].gate, @@ -212,7 +221,6 @@ static int st21nfca_hci_load_session(struct nfc_hci_dev *hdev) memcpy(hdev->init_data.gates, st21nfca_gates, sizeof(st21nfca_gates)); free_info: kfree_skb(skb_pipe_info); -free_list: kfree_skb(skb_pipe_list); return r; } @@ -257,16 +265,33 @@ out: static int st21nfca_hci_ready(struct nfc_hci_dev *hdev) { + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); struct sk_buff *skb; u8 param; + u8 white_list[2]; + int wl_size = 0; int r; - param = NFC_HCI_UICC_HOST_ID; - r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE, - NFC_HCI_ADMIN_WHITELIST, ¶m, 1); - if (r < 0) - return r; + if (info->se_status->is_ese_present && + info->se_status->is_uicc_present) { + white_list[wl_size++] = NFC_HCI_UICC_HOST_ID; + white_list[wl_size++] = ST21NFCA_ESE_HOST_ID; + } else if (!info->se_status->is_ese_present && + info->se_status->is_uicc_present) { + white_list[wl_size++] = NFC_HCI_UICC_HOST_ID; + } else if (info->se_status->is_ese_present && + !info->se_status->is_uicc_present) { + white_list[wl_size++] = ST21NFCA_ESE_HOST_ID; + } + + if (wl_size) { + r = nfc_hci_set_param(hdev, NFC_HCI_ADMIN_GATE, + NFC_HCI_ADMIN_WHITELIST, + (u8 *) &white_list, wl_size); + if (r < 0) + return r; + } /* Set NFC_MODE in device management gate to enable */ r = nfc_hci_get_param(hdev, ST21NFCA_DEVICE_MGNT_GATE, @@ -274,8 +299,9 @@ static int st21nfca_hci_ready(struct nfc_hci_dev *hdev) if (r < 0) return r; - if (skb->data[0] == 0) { - kfree_skb(skb); + param = skb->data[0]; + kfree_skb(skb); + if (param == 0) { param = 1; r = nfc_hci_set_param(hdev, ST21NFCA_DEVICE_MGNT_GATE, @@ -417,9 +443,12 @@ static int st21nfca_hci_start_poll(struct nfc_hci_dev *hdev, r = nfc_hci_set_param(hdev, ST21NFCA_RF_CARD_F_GATE, ST21NFCA_RF_CARD_F_DATARATE, param, 1); - if (r < 0) + if (r < 0) { + kfree_skb(datarate_skb); return r; + } } + kfree_skb(datarate_skb); /* * Configure sens_res @@ -673,15 +702,15 @@ static int st21nfca_hci_complete_target_discovered(struct nfc_hci_dev *hdev, struct nfc_target *target) { int r; - struct sk_buff *nfcid2_skb = NULL, *nfcid1_skb; + struct sk_buff *nfcid_skb = NULL; if (gate == ST21NFCA_RF_READER_F_GATE) { r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_F_GATE, - ST21NFCA_RF_READER_F_NFCID2, &nfcid2_skb); + ST21NFCA_RF_READER_F_NFCID2, &nfcid_skb); if (r < 0) goto exit; - if (nfcid2_skb->len > NFC_SENSF_RES_MAXSIZE) { + if (nfcid_skb->len > NFC_SENSF_RES_MAXSIZE) { r = -EPROTO; goto exit; } @@ -693,11 +722,11 @@ static int st21nfca_hci_complete_target_discovered(struct nfc_hci_dev *hdev, * - After the reception of SEL_RES with NFCIP-1 compliant bit * set for type A frame NFCID1 will be updated */ - if (nfcid2_skb->len > 0) { + if (nfcid_skb->len > 0) { /* P2P in type F */ - memcpy(target->sensf_res, nfcid2_skb->data, - nfcid2_skb->len); - target->sensf_res_len = nfcid2_skb->len; + memcpy(target->sensf_res, nfcid_skb->data, + nfcid_skb->len); + target->sensf_res_len = nfcid_skb->len; /* NFC Forum Digital Protocol Table 44 */ if (target->sensf_res[0] == 0x01 && target->sensf_res[1] == 0xfe) @@ -707,27 +736,28 @@ static int st21nfca_hci_complete_target_discovered(struct nfc_hci_dev *hdev, target->supported_protocols = NFC_PROTO_FELICA_MASK; } else { + kfree_skb(nfcid_skb); /* P2P in type A */ r = nfc_hci_get_param(hdev, ST21NFCA_RF_READER_F_GATE, ST21NFCA_RF_READER_F_NFCID1, - &nfcid1_skb); + &nfcid_skb); if (r < 0) goto exit; - if (nfcid1_skb->len > NFC_NFCID1_MAXSIZE) { + if (nfcid_skb->len > NFC_NFCID1_MAXSIZE) { r = -EPROTO; goto exit; } - memcpy(target->sensf_res, nfcid1_skb->data, - nfcid1_skb->len); - target->sensf_res_len = nfcid1_skb->len; + memcpy(target->sensf_res, nfcid_skb->data, + nfcid_skb->len); + target->sensf_res_len = nfcid_skb->len; target->supported_protocols = NFC_PROTO_NFC_DEP_MASK; } target->hci_reader_gate = ST21NFCA_RF_READER_F_GATE; } r = 1; exit: - kfree_skb(nfcid2_skb); + kfree_skb(nfcid_skb); return r; } @@ -829,24 +859,82 @@ static int st21nfca_hci_check_presence(struct nfc_hci_dev *hdev, } } +static void st21nfca_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd, + struct sk_buff *skb) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + u8 gate = hdev->pipes[pipe].gate; + + pr_debug("cmd: %x\n", cmd); + + switch (cmd) { + case NFC_HCI_ANY_OPEN_PIPE: + if (gate != ST21NFCA_APDU_READER_GATE && + hdev->pipes[pipe].dest_host != NFC_HCI_UICC_HOST_ID) + info->se_info.count_pipes++; + + if (info->se_info.count_pipes == info->se_info.expected_pipes) { + del_timer_sync(&info->se_info.se_active_timer); + info->se_info.se_active = false; + info->se_info.count_pipes = 0; + complete(&info->se_info.req_completion); + } + break; + } +} + +static int st21nfca_admin_event_received(struct nfc_hci_dev *hdev, u8 event, + struct sk_buff *skb) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + pr_debug("admin event: %x\n", event); + + switch (event) { + case ST21NFCA_EVT_HOT_PLUG: + if (info->se_info.se_active) { + if (!ST21NFCA_EVT_HOT_PLUG_IS_INHIBITED(skb)) { + del_timer_sync(&info->se_info.se_active_timer); + info->se_info.se_active = false; + complete(&info->se_info.req_completion); + } else { + mod_timer(&info->se_info.se_active_timer, + jiffies + + msecs_to_jiffies(ST21NFCA_SE_TO_PIPES)); + } + } + break; + } + kfree_skb(skb); + return 0; +} + /* * Returns: * <= 0: driver handled the event, skb consumed * 1: driver does not handle the event, please do standard processing */ -static int st21nfca_hci_event_received(struct nfc_hci_dev *hdev, u8 gate, +static int st21nfca_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event, struct sk_buff *skb) { + u8 gate = hdev->pipes[pipe].gate; + u8 host = hdev->pipes[pipe].dest_host; + pr_debug("hci event: %d gate: %x\n", event, gate); switch (gate) { + case NFC_HCI_ADMIN_GATE: + return st21nfca_admin_event_received(hdev, event, skb); case ST21NFCA_RF_CARD_F_GATE: return st21nfca_dep_event_received(hdev, event, skb); + case ST21NFCA_CONNECTIVITY_GATE: + return st21nfca_connectivity_event_received(hdev, host, + event, skb); + case ST21NFCA_APDU_READER_GATE: + return st21nfca_apdu_reader_event_received(hdev, event, skb); default: return 1; } - kfree_skb(skb); - return 0; } static struct nfc_hci_ops st21nfca_hci_ops = { @@ -865,11 +953,17 @@ static struct nfc_hci_ops st21nfca_hci_ops = { .tm_send = st21nfca_hci_tm_send, .check_presence = st21nfca_hci_check_presence, .event_received = st21nfca_hci_event_received, + .cmd_received = st21nfca_hci_cmd_received, + .discover_se = st21nfca_hci_discover_se, + .enable_se = st21nfca_hci_enable_se, + .disable_se = st21nfca_hci_disable_se, + .se_io = st21nfca_hci_se_io, }; int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, int phy_headroom, int phy_tailroom, - int phy_payload, struct nfc_hci_dev **hdev) + int phy_payload, struct nfc_hci_dev **hdev, + struct st21nfca_se_status *se_status) { struct st21nfca_hci_info *info; int r = 0; @@ -929,6 +1023,8 @@ int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, goto err_alloc_hdev; } + info->se_status = se_status; + nfc_hci_set_clientdata(info->hdev, info); r = nfc_hci_register_device(info->hdev); @@ -937,6 +1033,7 @@ int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, *hdev = info->hdev; st21nfca_dep_init(info->hdev); + st21nfca_se_init(info->hdev); return 0; @@ -955,6 +1052,7 @@ void st21nfca_hci_remove(struct nfc_hci_dev *hdev) struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); st21nfca_dep_deinit(hdev); + st21nfca_se_deinit(hdev); nfc_hci_unregister_device(hdev); nfc_hci_free_device(hdev); kfree(info); diff --git a/drivers/nfc/st21nfca/st21nfca.h b/drivers/nfc/st21nfca/st21nfca.h index 7c2a85292230..15a78d330a9f 100644 --- a/drivers/nfc/st21nfca/st21nfca.h +++ b/drivers/nfc/st21nfca/st21nfca.h @@ -20,6 +20,7 @@ #include <net/nfc/hci.h> #include "st21nfca_dep.h" +#include "st21nfca_se.h" #define HCI_MODE 0 @@ -51,9 +52,15 @@ #define ST21NFCA_NUM_DEVICES 256 +struct st21nfca_se_status { + bool is_ese_present; + bool is_uicc_present; +}; + int st21nfca_hci_probe(void *phy_id, struct nfc_phy_ops *phy_ops, char *llc_name, int phy_headroom, int phy_tailroom, - int phy_payload, struct nfc_hci_dev **hdev); + int phy_payload, struct nfc_hci_dev **hdev, + struct st21nfca_se_status *se_status); void st21nfca_hci_remove(struct nfc_hci_dev *hdev); enum st21nfca_state { @@ -66,6 +73,7 @@ struct st21nfca_hci_info { void *phy_id; struct nfc_hci_dev *hdev; + struct st21nfca_se_status *se_status; enum st21nfca_state state; @@ -76,13 +84,16 @@ struct st21nfca_hci_info { void *async_cb_context; struct st21nfca_dep_info dep_info; + struct st21nfca_se_info se_info; }; /* Reader RF commands */ -#define ST21NFCA_WR_XCHG_DATA 0x10 - -#define ST21NFCA_RF_READER_F_GATE 0x14 +#define ST21NFCA_WR_XCHG_DATA 0x10 -#define ST21NFCA_RF_CARD_F_GATE 0x24 +#define ST21NFCA_DEVICE_MGNT_GATE 0x01 +#define ST21NFCA_RF_READER_F_GATE 0x14 +#define ST21NFCA_RF_CARD_F_GATE 0x24 +#define ST21NFCA_APDU_READER_GATE 0xf0 +#define ST21NFCA_CONNECTIVITY_GATE 0x41 #endif /* __LOCAL_ST21NFCA_H_ */ diff --git a/drivers/nfc/st21nfca/st21nfca_se.c b/drivers/nfc/st21nfca/st21nfca_se.c new file mode 100644 index 000000000000..bd13cac9c66a --- /dev/null +++ b/drivers/nfc/st21nfca/st21nfca_se.c @@ -0,0 +1,411 @@ +/* + * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#include <net/nfc/hci.h> + +#include "st21nfca.h" +#include "st21nfca_se.h" + +#define ST21NFCA_EVT_UICC_ACTIVATE 0x10 +#define ST21NFCA_EVT_UICC_DEACTIVATE 0x13 +#define ST21NFCA_EVT_SE_HARD_RESET 0x20 +#define ST21NFCA_EVT_SE_SOFT_RESET 0x11 +#define ST21NFCA_EVT_SE_END_OF_APDU_TRANSFER 0x21 +#define ST21NFCA_EVT_SE_ACTIVATE 0x22 +#define ST21NFCA_EVT_SE_DEACTIVATE 0x23 + +#define ST21NFCA_EVT_TRANSMIT_DATA 0x10 +#define ST21NFCA_EVT_WTX_REQUEST 0x11 + +#define ST21NFCA_EVT_CONNECTIVITY 0x10 +#define ST21NFCA_EVT_TRANSACTION 0x12 + +#define ST21NFCA_ESE_HOST_ID 0xc0 + +#define ST21NFCA_SE_TO_HOT_PLUG 1000 +/* Connectivity pipe only */ +#define ST21NFCA_SE_COUNT_PIPE_UICC 0x01 +/* Connectivity + APDU Reader pipe */ +#define ST21NFCA_SE_COUNT_PIPE_EMBEDDED 0x02 + +#define ST21NFCA_SE_MODE_OFF 0x00 +#define ST21NFCA_SE_MODE_ON 0x01 + +#define ST21NFCA_PARAM_ATR 0x01 +#define ST21NFCA_ATR_DEFAULT_BWI 0x04 + +/* + * WT = 2^BWI/10[s], convert into msecs and add a secure + * room by increasing by 2 this timeout + */ +#define ST21NFCA_BWI_TO_TIMEOUT(x) ((1 << x) * 200) +#define ST21NFCA_ATR_GET_Y_FROM_TD(x) (x >> 4) + +/* If TA is present bit 0 is set */ +#define ST21NFCA_ATR_TA_PRESENT(x) (x & 0x01) +/* If TB is present bit 1 is set */ +#define ST21NFCA_ATR_TB_PRESENT(x) (x & 0x02) + +static u8 st21nfca_se_get_bwi(struct nfc_hci_dev *hdev) +{ + int i; + u8 td; + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + /* Bits 8 to 5 of the first TB for T=1 encode BWI from zero to nine */ + for (i = 1; i < ST21NFCA_ESE_MAX_LENGTH; i++) { + td = ST21NFCA_ATR_GET_Y_FROM_TD(info->se_info.atr[i]); + if (ST21NFCA_ATR_TA_PRESENT(td)) + i++; + if (ST21NFCA_ATR_TB_PRESENT(td)) { + i++; + return info->se_info.atr[i] >> 4; + } + } + return ST21NFCA_ATR_DEFAULT_BWI; +} + +static void st21nfca_se_get_atr(struct nfc_hci_dev *hdev) +{ + int r; + struct sk_buff *skb; + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + r = nfc_hci_get_param(hdev, ST21NFCA_APDU_READER_GATE, + ST21NFCA_PARAM_ATR, &skb); + if (r < 0) + return; + + if (skb->len <= ST21NFCA_ESE_MAX_LENGTH) { + memcpy(info->se_info.atr, skb->data, skb->len); + info->se_info.wt_timeout = + ST21NFCA_BWI_TO_TIMEOUT(st21nfca_se_get_bwi(hdev)); + } + kfree_skb(skb); +} + +static int st21nfca_hci_control_se(struct nfc_hci_dev *hdev, u32 se_idx, + u8 state) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + int r; + struct sk_buff *sk_host_list; + u8 se_event, host_id; + + switch (se_idx) { + case NFC_HCI_UICC_HOST_ID: + se_event = (state == ST21NFCA_SE_MODE_ON ? + ST21NFCA_EVT_UICC_ACTIVATE : + ST21NFCA_EVT_UICC_DEACTIVATE); + + info->se_info.count_pipes = 0; + info->se_info.expected_pipes = ST21NFCA_SE_COUNT_PIPE_UICC; + break; + case ST21NFCA_ESE_HOST_ID: + se_event = (state == ST21NFCA_SE_MODE_ON ? + ST21NFCA_EVT_SE_ACTIVATE : + ST21NFCA_EVT_SE_DEACTIVATE); + + info->se_info.count_pipes = 0; + info->se_info.expected_pipes = ST21NFCA_SE_COUNT_PIPE_EMBEDDED; + break; + default: + return -EINVAL; + } + + /* + * Wait for an EVT_HOT_PLUG in order to + * retrieve a relevant host list. + */ + reinit_completion(&info->se_info.req_completion); + r = nfc_hci_send_event(hdev, ST21NFCA_DEVICE_MGNT_GATE, se_event, + NULL, 0); + if (r < 0) + return r; + + mod_timer(&info->se_info.se_active_timer, jiffies + + msecs_to_jiffies(ST21NFCA_SE_TO_HOT_PLUG)); + info->se_info.se_active = true; + + /* Ignore return value and check in any case the host_list */ + wait_for_completion_interruptible(&info->se_info.req_completion); + + r = nfc_hci_get_param(hdev, NFC_HCI_ADMIN_GATE, + NFC_HCI_ADMIN_HOST_LIST, + &sk_host_list); + if (r < 0) + return r; + + host_id = sk_host_list->data[sk_host_list->len - 1]; + kfree_skb(sk_host_list); + + if (state == ST21NFCA_SE_MODE_ON && host_id == se_idx) + return se_idx; + else if (state == ST21NFCA_SE_MODE_OFF && host_id != se_idx) + return se_idx; + + return -1; +} + +int st21nfca_hci_discover_se(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + int se_count = 0; + + if (info->se_status->is_uicc_present) { + nfc_add_se(hdev->ndev, NFC_HCI_UICC_HOST_ID, NFC_SE_UICC); + se_count++; + } + + if (info->se_status->is_ese_present) { + nfc_add_se(hdev->ndev, ST21NFCA_ESE_HOST_ID, NFC_SE_EMBEDDED); + se_count++; + } + + return !se_count; +} +EXPORT_SYMBOL(st21nfca_hci_discover_se); + +int st21nfca_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx) +{ + int r; + + /* + * According to upper layer, se_idx == NFC_SE_UICC when + * info->se_status->is_uicc_enable is true should never happen. + * Same for eSE. + */ + r = st21nfca_hci_control_se(hdev, se_idx, ST21NFCA_SE_MODE_ON); + + if (r == ST21NFCA_ESE_HOST_ID) { + st21nfca_se_get_atr(hdev); + r = nfc_hci_send_event(hdev, ST21NFCA_APDU_READER_GATE, + ST21NFCA_EVT_SE_SOFT_RESET, NULL, 0); + if (r < 0) + return r; + } else if (r < 0) { + /* + * The activation tentative failed, the secure element + * is not connected. Remove from the list. + */ + nfc_remove_se(hdev->ndev, se_idx); + return r; + } + + return 0; +} +EXPORT_SYMBOL(st21nfca_hci_enable_se); + +int st21nfca_hci_disable_se(struct nfc_hci_dev *hdev, u32 se_idx) +{ + int r; + + /* + * According to upper layer, se_idx == NFC_SE_UICC when + * info->se_status->is_uicc_enable is true should never happen + * Same for eSE. + */ + r = st21nfca_hci_control_se(hdev, se_idx, ST21NFCA_SE_MODE_OFF); + if (r < 0) + return r; + + return 0; +} +EXPORT_SYMBOL(st21nfca_hci_disable_se); + +int st21nfca_hci_se_io(struct nfc_hci_dev *hdev, u32 se_idx, + u8 *apdu, size_t apdu_length, + se_io_cb_t cb, void *cb_context) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + pr_debug("se_io %x\n", se_idx); + + switch (se_idx) { + case ST21NFCA_ESE_HOST_ID: + info->se_info.cb = cb; + info->se_info.cb_context = cb_context; + mod_timer(&info->se_info.bwi_timer, jiffies + + msecs_to_jiffies(info->se_info.wt_timeout)); + info->se_info.bwi_active = true; + return nfc_hci_send_event(hdev, ST21NFCA_APDU_READER_GATE, + ST21NFCA_EVT_TRANSMIT_DATA, + apdu, apdu_length); + default: + return -ENODEV; + } +} +EXPORT_SYMBOL(st21nfca_hci_se_io); + +static void st21nfca_se_wt_timeout(unsigned long data) +{ + /* + * No answer from the secure element + * within the defined timeout. + * Let's send a reset request as recovery procedure. + * According to the situation, we first try to send a software reset + * to the secure element. If the next command is still not + * answering in time, we send to the CLF a secure element hardware + * reset request. + */ + /* hardware reset managed through VCC_UICC_OUT power supply */ + u8 param = 0x01; + struct st21nfca_hci_info *info = (struct st21nfca_hci_info *) data; + + pr_debug("\n"); + + info->se_info.bwi_active = false; + + if (!info->se_info.xch_error) { + info->se_info.xch_error = true; + nfc_hci_send_event(info->hdev, ST21NFCA_APDU_READER_GATE, + ST21NFCA_EVT_SE_SOFT_RESET, NULL, 0); + } else { + info->se_info.xch_error = false; + nfc_hci_send_event(info->hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_EVT_SE_HARD_RESET, ¶m, 1); + } + info->se_info.cb(info->se_info.cb_context, NULL, 0, -ETIME); +} + +static void st21nfca_se_activation_timeout(unsigned long data) +{ + struct st21nfca_hci_info *info = (struct st21nfca_hci_info *) data; + + pr_debug("\n"); + + info->se_info.se_active = false; + + complete(&info->se_info.req_completion); +} + +/* + * Returns: + * <= 0: driver handled the event, skb consumed + * 1: driver does not handle the event, please do standard processing + */ +int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host, + u8 event, struct sk_buff *skb) +{ + int r = 0; + struct device *dev = &hdev->ndev->dev; + struct nfc_evt_transaction *transaction; + + pr_debug("connectivity gate event: %x\n", event); + + switch (event) { + case ST21NFCA_EVT_CONNECTIVITY: + break; + case ST21NFCA_EVT_TRANSACTION: + if (skb->len < NFC_MIN_AID_LENGTH + 2 && + skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG) + return -EPROTO; + + transaction = (struct nfc_evt_transaction *)devm_kzalloc(dev, + skb->len - 2, GFP_KERNEL); + + transaction->aid_len = skb->data[1]; + memcpy(transaction->aid, &skb->data[2], skb->data[1]); + + if (skb->data[transaction->aid_len + 2] != + NFC_EVT_TRANSACTION_PARAMS_TAG) + return -EPROTO; + + transaction->params_len = skb->data[transaction->aid_len + 3]; + memcpy(transaction->params, skb->data + + transaction->aid_len + 4, transaction->params_len); + + r = nfc_se_transaction(hdev->ndev, host, transaction); + break; + default: + return 1; + } + kfree_skb(skb); + return r; +} +EXPORT_SYMBOL(st21nfca_connectivity_event_received); + +int st21nfca_apdu_reader_event_received(struct nfc_hci_dev *hdev, + u8 event, struct sk_buff *skb) +{ + int r = 0; + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + pr_debug("apdu reader gate event: %x\n", event); + + switch (event) { + case ST21NFCA_EVT_TRANSMIT_DATA: + del_timer_sync(&info->se_info.bwi_timer); + info->se_info.bwi_active = false; + r = nfc_hci_send_event(hdev, ST21NFCA_DEVICE_MGNT_GATE, + ST21NFCA_EVT_SE_END_OF_APDU_TRANSFER, NULL, 0); + if (r < 0) + goto exit; + + info->se_info.cb(info->se_info.cb_context, + skb->data, skb->len, 0); + break; + case ST21NFCA_EVT_WTX_REQUEST: + mod_timer(&info->se_info.bwi_timer, jiffies + + msecs_to_jiffies(info->se_info.wt_timeout)); + break; + } + +exit: + kfree_skb(skb); + return r; +} +EXPORT_SYMBOL(st21nfca_apdu_reader_event_received); + +void st21nfca_se_init(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + init_completion(&info->se_info.req_completion); + /* initialize timers */ + init_timer(&info->se_info.bwi_timer); + info->se_info.bwi_timer.data = (unsigned long)info; + info->se_info.bwi_timer.function = st21nfca_se_wt_timeout; + info->se_info.bwi_active = false; + + init_timer(&info->se_info.se_active_timer); + info->se_info.se_active_timer.data = (unsigned long)info; + info->se_info.se_active_timer.function = st21nfca_se_activation_timeout; + info->se_info.se_active = false; + + info->se_info.count_pipes = 0; + info->se_info.expected_pipes = 0; + + info->se_info.xch_error = false; + + info->se_info.wt_timeout = + ST21NFCA_BWI_TO_TIMEOUT(ST21NFCA_ATR_DEFAULT_BWI); +} +EXPORT_SYMBOL(st21nfca_se_init); + +void st21nfca_se_deinit(struct nfc_hci_dev *hdev) +{ + struct st21nfca_hci_info *info = nfc_hci_get_clientdata(hdev); + + if (info->se_info.bwi_active) + del_timer_sync(&info->se_info.bwi_timer); + if (info->se_info.se_active) + del_timer_sync(&info->se_info.se_active_timer); + + info->se_info.bwi_active = false; + info->se_info.se_active = false; +} +EXPORT_SYMBOL(st21nfca_se_deinit); diff --git a/drivers/nfc/st21nfca/st21nfca_se.h b/drivers/nfc/st21nfca/st21nfca_se.h new file mode 100644 index 000000000000..b172cfcaeb90 --- /dev/null +++ b/drivers/nfc/st21nfca/st21nfca_se.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __ST21NFCA_SE_H +#define __ST21NFCA_SE_H + +#include <linux/skbuff.h> +#include <linux/workqueue.h> + +/* + * ref ISO7816-3 chap 8.1. the initial character TS is followed by a + * sequence of at most 32 characters. + */ +#define ST21NFCA_ESE_MAX_LENGTH 33 +#define ST21NFCA_ESE_HOST_ID 0xc0 + +struct st21nfca_se_info { + u8 atr[ST21NFCA_ESE_MAX_LENGTH]; + struct completion req_completion; + + struct timer_list bwi_timer; + int wt_timeout; /* in msecs */ + bool bwi_active; + + struct timer_list se_active_timer; + bool se_active; + int expected_pipes; + int count_pipes; + + bool xch_error; + + se_io_cb_t cb; + void *cb_context; +}; + +int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host, + u8 event, struct sk_buff *skb); +int st21nfca_apdu_reader_event_received(struct nfc_hci_dev *hdev, + u8 event, struct sk_buff *skb); + +int st21nfca_hci_discover_se(struct nfc_hci_dev *hdev); +int st21nfca_hci_enable_se(struct nfc_hci_dev *hdev, u32 se_idx); +int st21nfca_hci_disable_se(struct nfc_hci_dev *hdev, u32 se_idx); +int st21nfca_hci_se_io(struct nfc_hci_dev *hdev, u32 se_idx, + u8 *apdu, size_t apdu_length, + se_io_cb_t cb, void *cb_context); + +void st21nfca_se_init(struct nfc_hci_dev *hdev); +void st21nfca_se_deinit(struct nfc_hci_dev *hdev); +#endif /* __ST21NFCA_SE_H */ diff --git a/drivers/nfc/st21nfcb/Makefile b/drivers/nfc/st21nfcb/Makefile index f4d835dd15f2..ce659a9e5a1a 100644 --- a/drivers/nfc/st21nfcb/Makefile +++ b/drivers/nfc/st21nfcb/Makefile @@ -2,7 +2,7 @@ # Makefile for ST21NFCB NCI based NFC driver # -st21nfcb_nci-objs = ndlc.o st21nfcb.o +st21nfcb_nci-objs = ndlc.o st21nfcb.o st21nfcb_se.o obj-$(CONFIG_NFC_ST21NFCB) += st21nfcb_nci.o st21nfcb_i2c-objs = i2c.o diff --git a/drivers/nfc/st21nfcb/i2c.c b/drivers/nfc/st21nfcb/i2c.c index 01ba865863ee..eb886932d972 100644 --- a/drivers/nfc/st21nfcb/i2c.c +++ b/drivers/nfc/st21nfcb/i2c.c @@ -199,7 +199,7 @@ static irqreturn_t st21nfcb_nci_irq_thread_fn(int irq, void *phy_id) struct sk_buff *skb = NULL; int r; - if (!phy || irq != phy->i2c_dev->irq) { + if (!phy || !phy->ndlc || irq != phy->i2c_dev->irq) { WARN_ON_ONCE(1); return IRQ_NONE; } @@ -343,18 +343,22 @@ static int st21nfcb_nci_i2c_probe(struct i2c_client *client, return -ENODEV; } + r = ndlc_probe(phy, &i2c_phy_ops, &client->dev, + ST21NFCB_FRAME_HEADROOM, ST21NFCB_FRAME_TAILROOM, + &phy->ndlc); + if (r < 0) { + nfc_err(&client->dev, "Unable to register ndlc layer\n"); + return r; + } + r = devm_request_threaded_irq(&client->dev, client->irq, NULL, st21nfcb_nci_irq_thread_fn, phy->irq_polarity | IRQF_ONESHOT, ST21NFCB_NCI_DRIVER_NAME, phy); - if (r < 0) { + if (r < 0) nfc_err(&client->dev, "Unable to register IRQ handler\n"); - return r; - } - return ndlc_probe(phy, &i2c_phy_ops, &client->dev, - ST21NFCB_FRAME_HEADROOM, ST21NFCB_FRAME_TAILROOM, - &phy->ndlc); + return r; } static int st21nfcb_nci_i2c_remove(struct i2c_client *client) @@ -373,6 +377,7 @@ static int st21nfcb_nci_i2c_remove(struct i2c_client *client) #ifdef CONFIG_OF static const struct of_device_id of_st21nfcb_i2c_match[] = { + { .compatible = "st,st21nfcb-i2c", }, { .compatible = "st,st21nfcb_i2c", }, {} }; diff --git a/drivers/nfc/st21nfcb/ndlc.c b/drivers/nfc/st21nfcb/ndlc.c index bac50e805f1d..5fbf59d2138c 100644 --- a/drivers/nfc/st21nfcb/ndlc.c +++ b/drivers/nfc/st21nfcb/ndlc.c @@ -138,7 +138,7 @@ static void llt_ndlc_requeue_data_pending(struct llt_ndlc *ndlc) default: pr_err("UNKNOWN Packet Control Byte=%d\n", pcb); kfree_skb(skb); - break; + continue; } skb_queue_head(&ndlc->send_q, skb); } @@ -297,6 +297,5 @@ void ndlc_remove(struct llt_ndlc *ndlc) skb_queue_purge(&ndlc->send_q); st21nfcb_nci_remove(ndlc->ndev); - kfree(ndlc); } EXPORT_SYMBOL(ndlc_remove); diff --git a/drivers/nfc/st21nfcb/st21nfcb.c b/drivers/nfc/st21nfcb/st21nfcb.c index ea63d5877831..ca9871ab3fb3 100644 --- a/drivers/nfc/st21nfcb/st21nfcb.c +++ b/drivers/nfc/st21nfcb/st21nfcb.c @@ -22,6 +22,7 @@ #include <net/nfc/nci_core.h> #include "st21nfcb.h" +#include "st21nfcb_se.h" #define DRIVER_DESC "NCI NFC driver for ST21NFCB" @@ -78,6 +79,13 @@ static struct nci_ops st21nfcb_nci_ops = { .close = st21nfcb_nci_close, .send = st21nfcb_nci_send, .get_rfprotocol = st21nfcb_nci_get_rfprotocol, + .discover_se = st21nfcb_nci_discover_se, + .enable_se = st21nfcb_nci_enable_se, + .disable_se = st21nfcb_nci_disable_se, + .se_io = st21nfcb_nci_se_io, + .hci_load_session = st21nfcb_hci_load_session, + .hci_event_received = st21nfcb_hci_event_received, + .hci_cmd_received = st21nfcb_hci_cmd_received, }; int st21nfcb_nci_probe(struct llt_ndlc *ndlc, int phy_headroom, @@ -114,9 +122,10 @@ int st21nfcb_nci_probe(struct llt_ndlc *ndlc, int phy_headroom, if (r) { pr_err("Cannot register nfc device to nci core\n"); nci_free_device(ndlc->ndev); + return r; } - return r; + return st21nfcb_se_init(ndlc->ndev); } EXPORT_SYMBOL_GPL(st21nfcb_nci_probe); diff --git a/drivers/nfc/st21nfcb/st21nfcb.h b/drivers/nfc/st21nfcb/st21nfcb.h index ea58a56ad794..5ef8a58c9839 100644 --- a/drivers/nfc/st21nfcb/st21nfcb.h +++ b/drivers/nfc/st21nfcb/st21nfcb.h @@ -19,6 +19,7 @@ #ifndef __LOCAL_ST21NFCB_H_ #define __LOCAL_ST21NFCB_H_ +#include "st21nfcb_se.h" #include "ndlc.h" /* Define private flags: */ @@ -27,6 +28,7 @@ struct st21nfcb_nci_info { struct llt_ndlc *ndlc; unsigned long flags; + struct st21nfcb_se_info se_info; }; void st21nfcb_nci_remove(struct nci_dev *ndev); diff --git a/drivers/nfc/st21nfcb/st21nfcb_se.c b/drivers/nfc/st21nfcb/st21nfcb_se.c new file mode 100644 index 000000000000..7c82e9d87a65 --- /dev/null +++ b/drivers/nfc/st21nfcb/st21nfcb_se.c @@ -0,0 +1,707 @@ +/* + * NCI based Driver for STMicroelectronics NFC Chip + * + * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/nfc.h> +#include <linux/delay.h> +#include <net/nfc/nci.h> +#include <net/nfc/nci_core.h> + +#include "st21nfcb.h" +#include "st21nfcb_se.h" + +struct st21nfcb_pipe_info { + u8 pipe_state; + u8 src_host_id; + u8 src_gate_id; + u8 dst_host_id; + u8 dst_gate_id; +} __packed; + +/* Hosts */ +#define ST21NFCB_HOST_CONTROLLER_ID 0x00 +#define ST21NFCB_TERMINAL_HOST_ID 0x01 +#define ST21NFCB_UICC_HOST_ID 0x02 +#define ST21NFCB_ESE_HOST_ID 0xc0 + +/* Gates */ +#define ST21NFCB_DEVICE_MGNT_GATE 0x01 +#define ST21NFCB_APDU_READER_GATE 0xf0 +#define ST21NFCB_CONNECTIVITY_GATE 0x41 + +/* Pipes */ +#define ST21NFCB_DEVICE_MGNT_PIPE 0x02 + +/* Connectivity pipe only */ +#define ST21NFCB_SE_COUNT_PIPE_UICC 0x01 +/* Connectivity + APDU Reader pipe */ +#define ST21NFCB_SE_COUNT_PIPE_EMBEDDED 0x02 + +#define ST21NFCB_SE_TO_HOT_PLUG 1000 /* msecs */ +#define ST21NFCB_SE_TO_PIPES 2000 + +#define ST21NFCB_EVT_HOT_PLUG_IS_INHIBITED(x) (x->data[0] & 0x80) + +#define NCI_HCI_APDU_PARAM_ATR 0x01 +#define NCI_HCI_ADMIN_PARAM_SESSION_IDENTITY 0x01 +#define NCI_HCI_ADMIN_PARAM_WHITELIST 0x03 +#define NCI_HCI_ADMIN_PARAM_HOST_LIST 0x04 + +#define ST21NFCB_EVT_SE_HARD_RESET 0x20 +#define ST21NFCB_EVT_TRANSMIT_DATA 0x10 +#define ST21NFCB_EVT_WTX_REQUEST 0x11 +#define ST21NFCB_EVT_SE_SOFT_RESET 0x11 +#define ST21NFCB_EVT_SE_END_OF_APDU_TRANSFER 0x21 +#define ST21NFCB_EVT_HOT_PLUG 0x03 + +#define ST21NFCB_SE_MODE_OFF 0x00 +#define ST21NFCB_SE_MODE_ON 0x01 + +#define ST21NFCB_EVT_CONNECTIVITY 0x10 +#define ST21NFCB_EVT_TRANSACTION 0x12 + +#define ST21NFCB_DM_GETINFO 0x13 +#define ST21NFCB_DM_GETINFO_PIPE_LIST 0x02 +#define ST21NFCB_DM_GETINFO_PIPE_INFO 0x01 +#define ST21NFCB_DM_PIPE_CREATED 0x02 +#define ST21NFCB_DM_PIPE_OPEN 0x04 +#define ST21NFCB_DM_RF_ACTIVE 0x80 +#define ST21NFCB_DM_DISCONNECT 0x30 + +#define ST21NFCB_DM_IS_PIPE_OPEN(p) \ + ((p & 0x0f) == (ST21NFCB_DM_PIPE_CREATED | ST21NFCB_DM_PIPE_OPEN)) + +#define ST21NFCB_ATR_DEFAULT_BWI 0x04 + +/* + * WT = 2^BWI/10[s], convert into msecs and add a secure + * room by increasing by 2 this timeout + */ +#define ST21NFCB_BWI_TO_TIMEOUT(x) ((1 << x) * 200) +#define ST21NFCB_ATR_GET_Y_FROM_TD(x) (x >> 4) + +/* If TA is present bit 0 is set */ +#define ST21NFCB_ATR_TA_PRESENT(x) (x & 0x01) +/* If TB is present bit 1 is set */ +#define ST21NFCB_ATR_TB_PRESENT(x) (x & 0x02) + +#define ST21NFCB_NUM_DEVICES 256 + +static DECLARE_BITMAP(dev_mask, ST21NFCB_NUM_DEVICES); + +/* Here are the mandatory pipe for st21nfcb */ +static struct nci_hci_gate st21nfcb_gates[] = { + {NCI_HCI_ADMIN_GATE, NCI_HCI_ADMIN_PIPE, + ST21NFCB_HOST_CONTROLLER_ID}, + {NCI_HCI_LINK_MGMT_GATE, NCI_HCI_LINK_MGMT_PIPE, + ST21NFCB_HOST_CONTROLLER_ID}, + {ST21NFCB_DEVICE_MGNT_GATE, ST21NFCB_DEVICE_MGNT_PIPE, + ST21NFCB_HOST_CONTROLLER_ID}, + + /* Secure element pipes are created by secure element host */ + {ST21NFCB_CONNECTIVITY_GATE, NCI_HCI_DO_NOT_OPEN_PIPE, + ST21NFCB_HOST_CONTROLLER_ID}, + {ST21NFCB_APDU_READER_GATE, NCI_HCI_DO_NOT_OPEN_PIPE, + ST21NFCB_HOST_CONTROLLER_ID}, +}; + +static u8 st21nfcb_se_get_bwi(struct nci_dev *ndev) +{ + int i; + u8 td; + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + + /* Bits 8 to 5 of the first TB for T=1 encode BWI from zero to nine */ + for (i = 1; i < ST21NFCB_ESE_MAX_LENGTH; i++) { + td = ST21NFCB_ATR_GET_Y_FROM_TD(info->se_info.atr[i]); + if (ST21NFCB_ATR_TA_PRESENT(td)) + i++; + if (ST21NFCB_ATR_TB_PRESENT(td)) { + i++; + return info->se_info.atr[i] >> 4; + } + } + return ST21NFCB_ATR_DEFAULT_BWI; +} + +static void st21nfcb_se_get_atr(struct nci_dev *ndev) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + int r; + struct sk_buff *skb; + + r = nci_hci_get_param(ndev, ST21NFCB_APDU_READER_GATE, + NCI_HCI_APDU_PARAM_ATR, &skb); + if (r < 0) + return; + + if (skb->len <= ST21NFCB_ESE_MAX_LENGTH) { + memcpy(info->se_info.atr, skb->data, skb->len); + + info->se_info.wt_timeout = + ST21NFCB_BWI_TO_TIMEOUT(st21nfcb_se_get_bwi(ndev)); + } + kfree_skb(skb); +} + +int st21nfcb_hci_load_session(struct nci_dev *ndev) +{ + int i, j, r; + struct sk_buff *skb_pipe_list, *skb_pipe_info; + struct st21nfcb_pipe_info *dm_pipe_info; + u8 pipe_list[] = { ST21NFCB_DM_GETINFO_PIPE_LIST, + ST21NFCB_TERMINAL_HOST_ID}; + u8 pipe_info[] = { ST21NFCB_DM_GETINFO_PIPE_INFO, + ST21NFCB_TERMINAL_HOST_ID, 0}; + + /* On ST21NFCB device pipes number are dynamics + * If pipes are already created, hci_dev_up will fail. + * Doing a clear all pipe is a bad idea because: + * - It does useless EEPROM cycling + * - It might cause issue for secure elements support + * (such as removing connectivity or APDU reader pipe) + * A better approach on ST21NFCB is to: + * - get a pipe list for each host. + * (eg: ST21NFCB_HOST_CONTROLLER_ID for now). + * (TODO Later on UICC HOST and eSE HOST) + * - get pipe information + * - match retrieved pipe list in st21nfcb_gates + * ST21NFCB_DEVICE_MGNT_GATE is a proprietary gate + * with ST21NFCB_DEVICE_MGNT_PIPE. + * Pipe can be closed and need to be open. + */ + r = nci_hci_connect_gate(ndev, ST21NFCB_HOST_CONTROLLER_ID, + ST21NFCB_DEVICE_MGNT_GATE, + ST21NFCB_DEVICE_MGNT_PIPE); + if (r < 0) + goto free_info; + + /* Get pipe list */ + r = nci_hci_send_cmd(ndev, ST21NFCB_DEVICE_MGNT_GATE, + ST21NFCB_DM_GETINFO, pipe_list, sizeof(pipe_list), + &skb_pipe_list); + if (r < 0) + goto free_info; + + /* Complete the existing gate_pipe table */ + for (i = 0; i < skb_pipe_list->len; i++) { + pipe_info[2] = skb_pipe_list->data[i]; + r = nci_hci_send_cmd(ndev, ST21NFCB_DEVICE_MGNT_GATE, + ST21NFCB_DM_GETINFO, pipe_info, + sizeof(pipe_info), &skb_pipe_info); + + if (r) + continue; + + /* + * Match pipe ID and gate ID + * Output format from ST21NFC_DM_GETINFO is: + * - pipe state (1byte) + * - source hid (1byte) + * - source gid (1byte) + * - destination hid (1byte) + * - destination gid (1byte) + */ + dm_pipe_info = (struct st21nfcb_pipe_info *)skb_pipe_info->data; + if (dm_pipe_info->dst_gate_id == ST21NFCB_APDU_READER_GATE && + dm_pipe_info->src_host_id != ST21NFCB_ESE_HOST_ID) { + pr_err("Unexpected apdu_reader pipe on host %x\n", + dm_pipe_info->src_host_id); + continue; + } + + for (j = 0; (j < ARRAY_SIZE(st21nfcb_gates)) && + (st21nfcb_gates[j].gate != dm_pipe_info->dst_gate_id); j++) + ; + + if (j < ARRAY_SIZE(st21nfcb_gates) && + st21nfcb_gates[j].gate == dm_pipe_info->dst_gate_id && + ST21NFCB_DM_IS_PIPE_OPEN(dm_pipe_info->pipe_state)) { + st21nfcb_gates[j].pipe = pipe_info[2]; + + ndev->hci_dev->gate2pipe[st21nfcb_gates[j].gate] = + st21nfcb_gates[j].pipe; + ndev->hci_dev->pipes[st21nfcb_gates[j].pipe].gate = + st21nfcb_gates[j].gate; + ndev->hci_dev->pipes[st21nfcb_gates[j].pipe].host = + dm_pipe_info->src_host_id; + } + } + + memcpy(ndev->hci_dev->init_data.gates, st21nfcb_gates, + sizeof(st21nfcb_gates)); + +free_info: + kfree_skb(skb_pipe_info); + kfree_skb(skb_pipe_list); + return r; +} +EXPORT_SYMBOL_GPL(st21nfcb_hci_load_session); + +static void st21nfcb_hci_admin_event_received(struct nci_dev *ndev, + u8 event, struct sk_buff *skb) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + + switch (event) { + case ST21NFCB_EVT_HOT_PLUG: + if (info->se_info.se_active) { + if (!ST21NFCB_EVT_HOT_PLUG_IS_INHIBITED(skb)) { + del_timer_sync(&info->se_info.se_active_timer); + info->se_info.se_active = false; + complete(&info->se_info.req_completion); + } else { + mod_timer(&info->se_info.se_active_timer, + jiffies + + msecs_to_jiffies(ST21NFCB_SE_TO_PIPES)); + } + } + break; + } +} + +static int st21nfcb_hci_apdu_reader_event_received(struct nci_dev *ndev, + u8 event, + struct sk_buff *skb) +{ + int r = 0; + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + + pr_debug("apdu reader gate event: %x\n", event); + + switch (event) { + case ST21NFCB_EVT_TRANSMIT_DATA: + del_timer_sync(&info->se_info.bwi_timer); + info->se_info.bwi_active = false; + info->se_info.cb(info->se_info.cb_context, + skb->data, skb->len, 0); + break; + case ST21NFCB_EVT_WTX_REQUEST: + mod_timer(&info->se_info.bwi_timer, jiffies + + msecs_to_jiffies(info->se_info.wt_timeout)); + break; + } + + kfree_skb(skb); + return r; +} + +/* + * Returns: + * <= 0: driver handled the event, skb consumed + * 1: driver does not handle the event, please do standard processing + */ +static int st21nfcb_hci_connectivity_event_received(struct nci_dev *ndev, + u8 host, u8 event, + struct sk_buff *skb) +{ + int r = 0; + struct device *dev = &ndev->nfc_dev->dev; + struct nfc_evt_transaction *transaction; + + pr_debug("connectivity gate event: %x\n", event); + + switch (event) { + case ST21NFCB_EVT_CONNECTIVITY: + + break; + case ST21NFCB_EVT_TRANSACTION: + if (skb->len < NFC_MIN_AID_LENGTH + 2 && + skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG) + return -EPROTO; + + transaction = (struct nfc_evt_transaction *)devm_kzalloc(dev, + skb->len - 2, GFP_KERNEL); + + transaction->aid_len = skb->data[1]; + memcpy(transaction->aid, &skb->data[2], skb->data[1]); + + if (skb->data[transaction->aid_len + 2] != + NFC_EVT_TRANSACTION_PARAMS_TAG) + return -EPROTO; + + transaction->params_len = skb->data[transaction->aid_len + 3]; + memcpy(transaction->params, skb->data + + transaction->aid_len + 4, transaction->params_len); + + r = nfc_se_transaction(ndev->nfc_dev, host, transaction); + default: + return 1; + } + kfree_skb(skb); + return r; +} + +void st21nfcb_hci_event_received(struct nci_dev *ndev, u8 pipe, + u8 event, struct sk_buff *skb) +{ + u8 gate = ndev->hci_dev->pipes[pipe].gate; + u8 host = ndev->hci_dev->pipes[pipe].host; + + switch (gate) { + case NCI_HCI_ADMIN_GATE: + st21nfcb_hci_admin_event_received(ndev, event, skb); + break; + case ST21NFCB_APDU_READER_GATE: + st21nfcb_hci_apdu_reader_event_received(ndev, event, skb); + break; + case ST21NFCB_CONNECTIVITY_GATE: + st21nfcb_hci_connectivity_event_received(ndev, host, event, + skb); + break; + } +} +EXPORT_SYMBOL_GPL(st21nfcb_hci_event_received); + + +void st21nfcb_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd, + struct sk_buff *skb) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + u8 gate = ndev->hci_dev->pipes[pipe].gate; + + pr_debug("cmd: %x\n", cmd); + + switch (cmd) { + case NCI_HCI_ANY_OPEN_PIPE: + if (gate != ST21NFCB_APDU_READER_GATE && + ndev->hci_dev->pipes[pipe].host != ST21NFCB_UICC_HOST_ID) + ndev->hci_dev->count_pipes++; + + if (ndev->hci_dev->count_pipes == + ndev->hci_dev->expected_pipes) { + del_timer_sync(&info->se_info.se_active_timer); + info->se_info.se_active = false; + ndev->hci_dev->count_pipes = 0; + complete(&info->se_info.req_completion); + } + break; + } +} +EXPORT_SYMBOL_GPL(st21nfcb_hci_cmd_received); + +/* + * Remarks: On some early st21nfcb firmware, nci_nfcee_mode_set(0) + * is rejected + */ +static int st21nfcb_nci_control_se(struct nci_dev *ndev, u8 se_idx, + u8 state) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + int r; + struct sk_buff *sk_host_list; + u8 host_id; + + switch (se_idx) { + case ST21NFCB_UICC_HOST_ID: + ndev->hci_dev->count_pipes = 0; + ndev->hci_dev->expected_pipes = ST21NFCB_SE_COUNT_PIPE_UICC; + break; + case ST21NFCB_ESE_HOST_ID: + ndev->hci_dev->count_pipes = 0; + ndev->hci_dev->expected_pipes = ST21NFCB_SE_COUNT_PIPE_EMBEDDED; + break; + default: + return -EINVAL; + } + + /* + * Wait for an EVT_HOT_PLUG in order to + * retrieve a relevant host list. + */ + reinit_completion(&info->se_info.req_completion); + r = nci_nfcee_mode_set(ndev, se_idx, NCI_NFCEE_ENABLE); + if (r != NCI_STATUS_OK) + return r; + + mod_timer(&info->se_info.se_active_timer, jiffies + + msecs_to_jiffies(ST21NFCB_SE_TO_HOT_PLUG)); + info->se_info.se_active = true; + + /* Ignore return value and check in any case the host_list */ + wait_for_completion_interruptible(&info->se_info.req_completion); + + /* There might be some "collision" after receiving a HOT_PLUG event + * This may cause the CLF to not answer to the next hci command. + * There is no possible synchronization to prevent this. + * Adding a small delay is the only way to solve the issue. + */ + usleep_range(3000, 5000); + + r = nci_hci_get_param(ndev, NCI_HCI_ADMIN_GATE, + NCI_HCI_ADMIN_PARAM_HOST_LIST, &sk_host_list); + if (r != NCI_HCI_ANY_OK) + return r; + + host_id = sk_host_list->data[sk_host_list->len - 1]; + kfree_skb(sk_host_list); + if (state == ST21NFCB_SE_MODE_ON && host_id == se_idx) + return se_idx; + else if (state == ST21NFCB_SE_MODE_OFF && host_id != se_idx) + return se_idx; + + return -1; +} + +int st21nfcb_nci_disable_se(struct nci_dev *ndev, u32 se_idx) +{ + int r; + + pr_debug("st21nfcb_nci_disable_se\n"); + + if (se_idx == NFC_SE_EMBEDDED) { + r = nci_hci_send_event(ndev, ST21NFCB_APDU_READER_GATE, + ST21NFCB_EVT_SE_END_OF_APDU_TRANSFER, NULL, 0); + if (r < 0) + return r; + } + + return 0; +} +EXPORT_SYMBOL_GPL(st21nfcb_nci_disable_se); + +int st21nfcb_nci_enable_se(struct nci_dev *ndev, u32 se_idx) +{ + int r; + + pr_debug("st21nfcb_nci_enable_se\n"); + + if (se_idx == ST21NFCB_HCI_HOST_ID_ESE) { + r = nci_hci_send_event(ndev, ST21NFCB_APDU_READER_GATE, + ST21NFCB_EVT_SE_SOFT_RESET, NULL, 0); + if (r < 0) + return r; + } + + return 0; +} +EXPORT_SYMBOL_GPL(st21nfcb_nci_enable_se); + +static int st21nfcb_hci_network_init(struct nci_dev *ndev) +{ + struct core_conn_create_dest_spec_params *dest_params; + struct dest_spec_params spec_params; + struct nci_conn_info *conn_info; + int r, dev_num; + + dest_params = + kzalloc(sizeof(struct core_conn_create_dest_spec_params) + + sizeof(struct dest_spec_params), GFP_KERNEL); + if (dest_params == NULL) { + r = -ENOMEM; + goto exit; + } + + dest_params->type = NCI_DESTINATION_SPECIFIC_PARAM_NFCEE_TYPE; + dest_params->length = sizeof(struct dest_spec_params); + spec_params.id = ndev->hci_dev->nfcee_id; + spec_params.protocol = NCI_NFCEE_INTERFACE_HCI_ACCESS; + memcpy(dest_params->value, &spec_params, sizeof(struct dest_spec_params)); + r = nci_core_conn_create(ndev, NCI_DESTINATION_NFCEE, 1, + sizeof(struct core_conn_create_dest_spec_params) + + sizeof(struct dest_spec_params), + dest_params); + if (r != NCI_STATUS_OK) + goto free_dest_params; + + conn_info = ndev->hci_dev->conn_info; + if (!conn_info) + goto free_dest_params; + + memcpy(ndev->hci_dev->init_data.gates, st21nfcb_gates, + sizeof(st21nfcb_gates)); + + /* + * Session id must include the driver name + i2c bus addr + * persistent info to discriminate 2 identical chips + */ + dev_num = find_first_zero_bit(dev_mask, ST21NFCB_NUM_DEVICES); + if (dev_num >= ST21NFCB_NUM_DEVICES) { + r = -ENODEV; + goto free_dest_params; + } + + scnprintf(ndev->hci_dev->init_data.session_id, + sizeof(ndev->hci_dev->init_data.session_id), + "%s%2x", "ST21BH", dev_num); + + r = nci_hci_dev_session_init(ndev); + if (r != NCI_HCI_ANY_OK) + goto exit; + + r = nci_nfcee_mode_set(ndev, ndev->hci_dev->conn_info->id, + NCI_NFCEE_ENABLE); + if (r != NCI_STATUS_OK) + goto exit; + + return 0; + +free_dest_params: + kfree(dest_params); + +exit: + return r; +} + +int st21nfcb_nci_discover_se(struct nci_dev *ndev) +{ + u8 param[2]; + int r; + int se_count = 0; + + pr_debug("st21nfcb_nci_discover_se\n"); + + r = st21nfcb_hci_network_init(ndev); + if (r != 0) + return r; + + param[0] = ST21NFCB_UICC_HOST_ID; + param[1] = ST21NFCB_HCI_HOST_ID_ESE; + r = nci_hci_set_param(ndev, NCI_HCI_ADMIN_GATE, + NCI_HCI_ADMIN_PARAM_WHITELIST, + param, sizeof(param)); + if (r != NCI_HCI_ANY_OK) + return r; + + r = st21nfcb_nci_control_se(ndev, ST21NFCB_UICC_HOST_ID, + ST21NFCB_SE_MODE_ON); + if (r == ST21NFCB_UICC_HOST_ID) { + nfc_add_se(ndev->nfc_dev, ST21NFCB_UICC_HOST_ID, NFC_SE_UICC); + se_count++; + } + + /* Try to enable eSE in order to check availability */ + r = st21nfcb_nci_control_se(ndev, ST21NFCB_HCI_HOST_ID_ESE, + ST21NFCB_SE_MODE_ON); + if (r == ST21NFCB_HCI_HOST_ID_ESE) { + nfc_add_se(ndev->nfc_dev, ST21NFCB_HCI_HOST_ID_ESE, + NFC_SE_EMBEDDED); + se_count++; + st21nfcb_se_get_atr(ndev); + } + + return !se_count; +} +EXPORT_SYMBOL_GPL(st21nfcb_nci_discover_se); + +int st21nfcb_nci_se_io(struct nci_dev *ndev, u32 se_idx, + u8 *apdu, size_t apdu_length, + se_io_cb_t cb, void *cb_context) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + + pr_debug("\n"); + + switch (se_idx) { + case ST21NFCB_HCI_HOST_ID_ESE: + info->se_info.cb = cb; + info->se_info.cb_context = cb_context; + mod_timer(&info->se_info.bwi_timer, jiffies + + msecs_to_jiffies(info->se_info.wt_timeout)); + info->se_info.bwi_active = true; + return nci_hci_send_event(ndev, ST21NFCB_APDU_READER_GATE, + ST21NFCB_EVT_TRANSMIT_DATA, apdu, + apdu_length); + default: + return -ENODEV; + } +} +EXPORT_SYMBOL(st21nfcb_nci_se_io); + +static void st21nfcb_se_wt_timeout(unsigned long data) +{ + /* + * No answer from the secure element + * within the defined timeout. + * Let's send a reset request as recovery procedure. + * According to the situation, we first try to send a software reset + * to the secure element. If the next command is still not + * answering in time, we send to the CLF a secure element hardware + * reset request. + */ + /* hardware reset managed through VCC_UICC_OUT power supply */ + u8 param = 0x01; + struct st21nfcb_nci_info *info = (struct st21nfcb_nci_info *) data; + + pr_debug("\n"); + + info->se_info.bwi_active = false; + + if (!info->se_info.xch_error) { + info->se_info.xch_error = true; + nci_hci_send_event(info->ndlc->ndev, ST21NFCB_APDU_READER_GATE, + ST21NFCB_EVT_SE_SOFT_RESET, NULL, 0); + } else { + info->se_info.xch_error = false; + nci_hci_send_event(info->ndlc->ndev, ST21NFCB_DEVICE_MGNT_GATE, + ST21NFCB_EVT_SE_HARD_RESET, ¶m, 1); + } + info->se_info.cb(info->se_info.cb_context, NULL, 0, -ETIME); +} + +static void st21nfcb_se_activation_timeout(unsigned long data) +{ + struct st21nfcb_nci_info *info = (struct st21nfcb_nci_info *) data; + + pr_debug("\n"); + + info->se_info.se_active = false; + + complete(&info->se_info.req_completion); +} + +int st21nfcb_se_init(struct nci_dev *ndev) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + + init_completion(&info->se_info.req_completion); + /* initialize timers */ + init_timer(&info->se_info.bwi_timer); + info->se_info.bwi_timer.data = (unsigned long)info; + info->se_info.bwi_timer.function = st21nfcb_se_wt_timeout; + info->se_info.bwi_active = false; + + init_timer(&info->se_info.se_active_timer); + info->se_info.se_active_timer.data = (unsigned long)info; + info->se_info.se_active_timer.function = + st21nfcb_se_activation_timeout; + info->se_info.se_active = false; + + info->se_info.xch_error = false; + + info->se_info.wt_timeout = + ST21NFCB_BWI_TO_TIMEOUT(ST21NFCB_ATR_DEFAULT_BWI); + + return 0; +} +EXPORT_SYMBOL(st21nfcb_se_init); + +void st21nfcb_se_deinit(struct nci_dev *ndev) +{ + struct st21nfcb_nci_info *info = nci_get_drvdata(ndev); + + if (info->se_info.bwi_active) + del_timer_sync(&info->se_info.bwi_timer); + if (info->se_info.se_active) + del_timer_sync(&info->se_info.se_active_timer); + + info->se_info.se_active = false; + info->se_info.bwi_active = false; +} +EXPORT_SYMBOL(st21nfcb_se_deinit); + diff --git a/drivers/nfc/st21nfcb/st21nfcb_se.h b/drivers/nfc/st21nfcb/st21nfcb_se.h new file mode 100644 index 000000000000..52a323872bea --- /dev/null +++ b/drivers/nfc/st21nfcb/st21nfcb_se.h @@ -0,0 +1,61 @@ +/* + * NCI based Driver for STMicroelectronics NFC Chip + * + * Copyright (C) 2014 STMicroelectronics SAS. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ +#ifndef __LOCAL_ST21NFCB_SE_H_ +#define __LOCAL_ST21NFCB_SE_H_ + +/* + * ref ISO7816-3 chap 8.1. the initial character TS is followed by a + * sequence of at most 32 characters. + */ +#define ST21NFCB_ESE_MAX_LENGTH 33 +#define ST21NFCB_HCI_HOST_ID_ESE 0xc0 + +struct st21nfcb_se_info { + u8 atr[ST21NFCB_ESE_MAX_LENGTH]; + struct completion req_completion; + + struct timer_list bwi_timer; + int wt_timeout; /* in msecs */ + bool bwi_active; + + struct timer_list se_active_timer; + bool se_active; + + bool xch_error; + + se_io_cb_t cb; + void *cb_context; +}; + +int st21nfcb_se_init(struct nci_dev *ndev); +void st21nfcb_se_deinit(struct nci_dev *ndev); + +int st21nfcb_nci_discover_se(struct nci_dev *ndev); +int st21nfcb_nci_enable_se(struct nci_dev *ndev, u32 se_idx); +int st21nfcb_nci_disable_se(struct nci_dev *ndev, u32 se_idx); +int st21nfcb_nci_se_io(struct nci_dev *ndev, u32 se_idx, + u8 *apdu, size_t apdu_length, + se_io_cb_t cb, void *cb_context); +int st21nfcb_hci_load_session(struct nci_dev *ndev); +void st21nfcb_hci_event_received(struct nci_dev *ndev, u8 pipe, + u8 event, struct sk_buff *skb); +void st21nfcb_hci_cmd_received(struct nci_dev *ndev, u8 pipe, u8 cmd, + struct sk_buff *skb); + + +#endif /* __LOCAL_ST21NFCB_NCI_H_ */ diff --git a/drivers/of/of_pci.c b/drivers/of/of_pci.c index 88471d3d98cd..110fece2ff53 100644 --- a/drivers/of/of_pci.c +++ b/drivers/of/of_pci.c @@ -140,6 +140,7 @@ int of_pci_get_host_bridge_resources(struct device_node *dev, unsigned char busno, unsigned char bus_max, struct list_head *resources, resource_size_t *io_base) { + struct resource_entry *window; struct resource *res; struct resource *bus_range; struct of_pci_range range; @@ -225,7 +226,10 @@ int of_pci_get_host_bridge_resources(struct device_node *dev, conversion_failed: kfree(res); parse_failed: + resource_list_for_each_entry(window, resources) + kfree(window->res); pci_free_resource_list(resources); + kfree(bus_range); return err; } EXPORT_SYMBOL_GPL(of_pci_get_host_bridge_resources); diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index ea63fbd228ed..352b4f28f82c 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -114,17 +114,6 @@ static int of_overlay_apply_single_device_node(struct of_overlay *ov, ret = of_overlay_apply_one(ov, tchild, child); if (ret) return ret; - - /* The properties are already copied, now do the child nodes */ - for_each_child_of_node(child, grandchild) { - ret = of_overlay_apply_single_device_node(ov, tchild, grandchild); - if (ret) { - pr_err("%s: Failed to apply single node @%s/%s\n", - __func__, tchild->full_name, - grandchild->name); - return ret; - } - } } return ret; diff --git a/drivers/of/platform.c b/drivers/of/platform.c index 5b33c6a21807..b0d50d70a8a1 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -188,7 +188,7 @@ static void of_dma_configure(struct device *dev) size = dev->coherent_dma_mask; } else { offset = PFN_DOWN(paddr - dma_addr); - dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", dev->dma_pfn_offset); + dev_dbg(dev, "dma_pfn_offset(%#08lx)\n", offset); } dev->dma_pfn_offset = offset; @@ -566,6 +566,10 @@ static int of_platform_notify(struct notifier_block *nb, if (!of_node_check_flag(rd->dn->parent, OF_POPULATED_BUS)) return NOTIFY_OK; /* not for us */ + /* already populated? (driver using of_populate manually) */ + if (of_node_check_flag(rd->dn, OF_POPULATED)) + return NOTIFY_OK; + /* pdev_parent may be NULL when no bus platform device */ pdev_parent = of_find_device_by_node(rd->dn->parent); pdev = of_platform_device_create(rd->dn, NULL, @@ -581,6 +585,11 @@ static int of_platform_notify(struct notifier_block *nb, break; case OF_RECONFIG_CHANGE_REMOVE: + + /* already depopulated? */ + if (!of_node_check_flag(rd->dn, OF_POPULATED)) + return NOTIFY_OK; + /* find our device by node */ pdev = of_find_device_by_node(rd->dn); if (pdev == NULL) diff --git a/drivers/of/unittest-data/tests-overlay.dtsi b/drivers/of/unittest-data/tests-overlay.dtsi index 75976da22b2e..a2b687d5f324 100644 --- a/drivers/of/unittest-data/tests-overlay.dtsi +++ b/drivers/of/unittest-data/tests-overlay.dtsi @@ -176,5 +176,60 @@ }; }; + overlay10 { + fragment@0 { + target-path = "/testcase-data/overlay-node/test-bus"; + __overlay__ { + + /* suppress DTC warning */ + #address-cells = <1>; + #size-cells = <0>; + + test-selftest10 { + compatible = "selftest"; + status = "okay"; + reg = <10>; + + #address-cells = <1>; + #size-cells = <0>; + + test-selftest101 { + compatible = "selftest"; + status = "okay"; + reg = <1>; + }; + + }; + }; + }; + }; + + overlay11 { + fragment@0 { + target-path = "/testcase-data/overlay-node/test-bus"; + __overlay__ { + + /* suppress DTC warning */ + #address-cells = <1>; + #size-cells = <0>; + + test-selftest11 { + compatible = "selftest"; + status = "okay"; + reg = <11>; + + #address-cells = <1>; + #size-cells = <0>; + + test-selftest111 { + compatible = "selftest"; + status = "okay"; + reg = <1>; + }; + + }; + }; + }; + }; }; }; diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 844838e11ef1..41a4a138f53b 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -978,6 +978,9 @@ static int selftest_probe(struct platform_device *pdev) } dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name); + + of_platform_populate(np, NULL, NULL, &pdev->dev); + return 0; } @@ -1385,6 +1388,39 @@ static void of_selftest_overlay_8(void) selftest(1, "overlay test %d passed\n", 8); } +/* test insertion of a bus with parent devices */ +static void of_selftest_overlay_10(void) +{ + int ret; + char *child_path; + + /* device should disable */ + ret = of_selftest_apply_overlay_check(10, 10, 0, 1); + if (selftest(ret == 0, "overlay test %d failed; overlay application\n", 10)) + return; + + child_path = kasprintf(GFP_KERNEL, "%s/test-selftest101", + selftest_path(10)); + if (selftest(child_path, "overlay test %d failed; kasprintf\n", 10)) + return; + + ret = of_path_platform_device_exists(child_path); + kfree(child_path); + if (selftest(ret, "overlay test %d failed; no child device\n", 10)) + return; +} + +/* test insertion of a bus with parent devices (and revert) */ +static void of_selftest_overlay_11(void) +{ + int ret; + + /* device should disable */ + ret = of_selftest_apply_revert_overlay_check(11, 11, 0, 1); + if (selftest(ret == 0, "overlay test %d failed; overlay application\n", 11)) + return; +} + static void __init of_selftest_overlay(void) { struct device_node *bus_np = NULL; @@ -1433,6 +1469,9 @@ static void __init of_selftest_overlay(void) of_selftest_overlay_6(); of_selftest_overlay_8(); + of_selftest_overlay_10(); + of_selftest_overlay_11(); + out: of_node_put(bus_np); } diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c index 37e71ff6408d..dceb9ddfd99a 100644 --- a/drivers/parisc/lba_pci.c +++ b/drivers/parisc/lba_pci.c @@ -694,9 +694,8 @@ lba_fixup_bus(struct pci_bus *bus) int i; /* PCI-PCI Bridge */ pci_read_bridge_bases(bus); - for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { - pci_claim_resource(bus->self, i); - } + for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) + pci_claim_bridge_resource(bus->self, i); } else { /* Host-PCI Bridge */ int err; diff --git a/drivers/parport/parport_atari.c b/drivers/parport/parport_atari.c index 7ad59ac68cf6..a81cd2a2747f 100644 --- a/drivers/parport/parport_atari.c +++ b/drivers/parport/parport_atari.c @@ -192,8 +192,8 @@ static int __init parport_atari_init(void) &parport_atari_ops); if (!p) return -ENODEV; - if (request_irq(IRQ_MFP_BUSY, parport_irq_handler, - IRQ_TYPE_SLOW, p->name, p)) { + if (request_irq(IRQ_MFP_BUSY, parport_irq_handler, 0, p->name, + p)) { parport_put_port (p); return -ENODEV; } diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 49dd766852ba..d9b64a175990 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -67,6 +67,93 @@ EXPORT_SYMBOL(pci_bus_write_config_byte); EXPORT_SYMBOL(pci_bus_write_config_word); EXPORT_SYMBOL(pci_bus_write_config_dword); +int pci_generic_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + void __iomem *addr; + + addr = bus->ops->map_bus(bus, devfn, where); + if (!addr) { + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + if (size == 1) + *val = readb(addr); + else if (size == 2) + *val = readw(addr); + else + *val = readl(addr); + + return PCIBIOS_SUCCESSFUL; +} +EXPORT_SYMBOL_GPL(pci_generic_config_read); + +int pci_generic_config_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + void __iomem *addr; + + addr = bus->ops->map_bus(bus, devfn, where); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (size == 1) + writeb(val, addr); + else if (size == 2) + writew(val, addr); + else + writel(val, addr); + + return PCIBIOS_SUCCESSFUL; +} +EXPORT_SYMBOL_GPL(pci_generic_config_write); + +int pci_generic_config_read32(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + void __iomem *addr; + + addr = bus->ops->map_bus(bus, devfn, where & ~0x3); + if (!addr) { + *val = ~0; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + *val = readl(addr); + + if (size <= 2) + *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1); + + return PCIBIOS_SUCCESSFUL; +} +EXPORT_SYMBOL_GPL(pci_generic_config_read32); + +int pci_generic_config_write32(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + void __iomem *addr; + u32 mask, tmp; + + addr = bus->ops->map_bus(bus, devfn, where & ~0x3); + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + + if (size == 4) { + writel(val, addr); + return PCIBIOS_SUCCESSFUL; + } else { + mask = ~(((1 << (size * 8)) - 1) << ((where & 0x3) * 8)); + } + + tmp = readl(addr) & mask; + tmp |= val << ((where & 0x3) * 8); + writel(tmp, addr); + + return PCIBIOS_SUCCESSFUL; +} +EXPORT_SYMBOL_GPL(pci_generic_config_write32); + /** * pci_bus_set_ops - Set raw operations of pci bus * @bus: pci bus struct diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 73aef51a28f0..90fa3a78fb7c 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -20,17 +20,16 @@ void pci_add_resource_offset(struct list_head *resources, struct resource *res, resource_size_t offset) { - struct pci_host_bridge_window *window; + struct resource_entry *entry; - window = kzalloc(sizeof(struct pci_host_bridge_window), GFP_KERNEL); - if (!window) { + entry = resource_list_create_entry(res, 0); + if (!entry) { printk(KERN_ERR "PCI: can't add host bridge window %pR\n", res); return; } - window->res = res; - window->offset = offset; - list_add_tail(&window->list, resources); + entry->offset = offset; + resource_list_add_tail(entry, resources); } EXPORT_SYMBOL(pci_add_resource_offset); @@ -42,12 +41,7 @@ EXPORT_SYMBOL(pci_add_resource); void pci_free_resource_list(struct list_head *resources) { - struct pci_host_bridge_window *window, *tmp; - - list_for_each_entry_safe(window, tmp, resources, list) { - list_del(&window->list); - kfree(window); - } + resource_list_free(resources); } EXPORT_SYMBOL(pci_free_resource_list); @@ -228,6 +222,49 @@ int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, } EXPORT_SYMBOL(pci_bus_alloc_resource); +/* + * The @idx resource of @dev should be a PCI-PCI bridge window. If this + * resource fits inside a window of an upstream bridge, do nothing. If it + * overlaps an upstream window but extends outside it, clip the resource so + * it fits completely inside. + */ +bool pci_bus_clip_resource(struct pci_dev *dev, int idx) +{ + struct pci_bus *bus = dev->bus; + struct resource *res = &dev->resource[idx]; + struct resource orig_res = *res; + struct resource *r; + int i; + + pci_bus_for_each_resource(bus, r, i) { + resource_size_t start, end; + + if (!r) + continue; + + if (resource_type(res) != resource_type(r)) + continue; + + start = max(r->start, res->start); + end = min(r->end, res->end); + + if (start > end) + continue; /* no overlap */ + + if (res->start == start && res->end == end) + return false; /* no change */ + + res->start = start; + res->end = end; + dev_printk(KERN_DEBUG, &dev->dev, "%pR clipped to %pR\n", + &orig_res, res); + + return true; + } + + return false; +} + void __weak pcibios_resource_survey_bus(struct pci_bus *bus) { } /** diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c index 0e5f3c95af5b..39b2dbe585aa 100644 --- a/drivers/pci/host-bridge.c +++ b/drivers/pci/host-bridge.c @@ -35,10 +35,10 @@ void pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region, struct resource *res) { struct pci_host_bridge *bridge = find_pci_host_bridge(bus); - struct pci_host_bridge_window *window; + struct resource_entry *window; resource_size_t offset = 0; - list_for_each_entry(window, &bridge->windows, list) { + resource_list_for_each_entry(window, &bridge->windows) { if (resource_contains(window->res, res)) { offset = window->offset; break; @@ -60,10 +60,10 @@ void pcibios_bus_to_resource(struct pci_bus *bus, struct resource *res, struct pci_bus_region *region) { struct pci_host_bridge *bridge = find_pci_host_bridge(bus); - struct pci_host_bridge_window *window; + struct resource_entry *window; resource_size_t offset = 0; - list_for_each_entry(window, &bridge->windows, list) { + resource_list_for_each_entry(window, &bridge->windows) { struct pci_bus_region bus_region; if (resource_type(res) != resource_type(window->res)) diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index c4b6568e486d..7b892a9cc4fc 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -102,4 +102,8 @@ config PCI_LAYERSCAPE help Say Y here if you want PCIe controller support on Layerscape SoCs. +config PCI_VERSATILE + bool "ARM Versatile PB PCI controller" + depends on ARCH_VERSATILE + endmenu diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index 44c26998027f..e61d91c92bf1 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_PCI_KEYSTONE) += pci-keystone-dw.o pci-keystone.o obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx.o obj-$(CONFIG_PCI_XGENE) += pci-xgene.o obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o +obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c index 6eb1aa75bd37..ba46e581db99 100644 --- a/drivers/pci/host/pci-host-generic.c +++ b/drivers/pci/host/pci-host-generic.c @@ -76,55 +76,9 @@ static struct gen_pci_cfg_bus_ops gen_pci_cfg_ecam_bus_ops = { .map_bus = gen_pci_map_cfg_bus_ecam, }; -static int gen_pci_config_read(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *val) -{ - void __iomem *addr; - struct pci_sys_data *sys = bus->sysdata; - struct gen_pci *pci = sys->private_data; - - addr = pci->cfg.ops->map_bus(bus, devfn, where); - - switch (size) { - case 1: - *val = readb(addr); - break; - case 2: - *val = readw(addr); - break; - default: - *val = readl(addr); - } - - return PCIBIOS_SUCCESSFUL; -} - -static int gen_pci_config_write(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - void __iomem *addr; - struct pci_sys_data *sys = bus->sysdata; - struct gen_pci *pci = sys->private_data; - - addr = pci->cfg.ops->map_bus(bus, devfn, where); - - switch (size) { - case 1: - writeb(val, addr); - break; - case 2: - writew(val, addr); - break; - default: - writel(val, addr); - } - - return PCIBIOS_SUCCESSFUL; -} - static struct pci_ops gen_pci_ops = { - .read = gen_pci_config_read, - .write = gen_pci_config_write, + .read = pci_generic_config_read, + .write = pci_generic_config_write, }; static const struct of_device_id gen_pci_of_match[] = { @@ -149,14 +103,14 @@ static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci) struct device *dev = pci->host.dev.parent; struct device_node *np = dev->of_node; resource_size_t iobase; - struct pci_host_bridge_window *win; + struct resource_entry *win; err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources, &iobase); if (err) return err; - list_for_each_entry(win, &pci->resources, list) { + resource_list_for_each_entry(win, &pci->resources) { struct resource *parent, *res = win->res; switch (resource_type(res)) { @@ -287,6 +241,7 @@ static int gen_pci_probe(struct platform_device *pdev) of_id = of_match_node(gen_pci_of_match, np); pci->cfg.ops = of_id->data; + gen_pci_ops.map_bus = pci->cfg.ops->map_bus; pci->host.dev.parent = dev; INIT_LIST_HEAD(&pci->host.windows); INIT_LIST_HEAD(&pci->resources); diff --git a/drivers/pci/host/pci-keystone.c b/drivers/pci/host/pci-keystone.c index 78f79e31ac5c..75333b0c4f0a 100644 --- a/drivers/pci/host/pci-keystone.c +++ b/drivers/pci/host/pci-keystone.c @@ -119,7 +119,7 @@ static void ks_pcie_msi_irq_handler(unsigned int irq, struct irq_desc *desc) struct pcie_port *pp = &ks_pcie->pp; struct irq_chip *chip = irq_desc_get_chip(desc); - dev_dbg(pp->dev, "ks_pci_msi_irq_handler, irq %d\n", irq); + dev_dbg(pp->dev, "%s, irq %d\n", __func__, irq); /* * The chained irq handler installation would have replaced normal @@ -197,7 +197,7 @@ static int ks_pcie_get_irq_controller_info(struct keystone_pcie *ks_pcie, */ for (temp = 0; temp < max_host_irqs; temp++) { host_irqs[temp] = irq_of_parse_and_map(*np_temp, temp); - if (host_irqs[temp] < 0) + if (!host_irqs[temp]) break; } if (temp) { diff --git a/drivers/pci/host/pci-layerscape.c b/drivers/pci/host/pci-layerscape.c index 6697b1a4d4fa..68c9e5e9b0a8 100644 --- a/drivers/pci/host/pci-layerscape.c +++ b/drivers/pci/host/pci-layerscape.c @@ -167,7 +167,6 @@ MODULE_DEVICE_TABLE(of, ls_pcie_of_match); static struct platform_driver ls_pcie_driver = { .driver = { .name = "layerscape-pcie", - .owner = THIS_MODULE, .of_match_table = ls_pcie_of_match, }, }; diff --git a/drivers/pci/host/pci-mvebu.c b/drivers/pci/host/pci-mvebu.c index 1dd759596b0a..1309cfbaa719 100644 --- a/drivers/pci/host/pci-mvebu.c +++ b/drivers/pci/host/pci-mvebu.c @@ -101,9 +101,7 @@ struct mvebu_pcie { struct mvebu_pcie_port *ports; struct msi_controller *msi; struct resource io; - char io_name[30]; struct resource realio; - char mem_name[30]; struct resource mem; struct resource busn; int nports; @@ -723,18 +721,9 @@ static int mvebu_pcie_setup(int nr, struct pci_sys_data *sys) { struct mvebu_pcie *pcie = sys_to_pcie(sys); int i; - int domain = 0; -#ifdef CONFIG_PCI_DOMAINS - domain = sys->domain; -#endif - - snprintf(pcie->mem_name, sizeof(pcie->mem_name), "PCI MEM %04x", - domain); - pcie->mem.name = pcie->mem_name; - - snprintf(pcie->io_name, sizeof(pcie->io_name), "PCI I/O %04x", domain); - pcie->realio.name = pcie->io_name; + pcie->mem.name = "PCI MEM"; + pcie->realio.name = "PCI I/O"; if (request_resource(&iomem_resource, &pcie->mem)) return 0; diff --git a/drivers/pci/host/pci-rcar-gen2.c b/drivers/pci/host/pci-rcar-gen2.c index d9c042febb1a..dd6b84e6206c 100644 --- a/drivers/pci/host/pci-rcar-gen2.c +++ b/drivers/pci/host/pci-rcar-gen2.c @@ -131,52 +131,6 @@ static void __iomem *rcar_pci_cfg_base(struct pci_bus *bus, unsigned int devfn, return priv->reg + (slot >> 1) * 0x100 + where; } -static int rcar_pci_read_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *val) -{ - void __iomem *reg = rcar_pci_cfg_base(bus, devfn, where); - - if (!reg) - return PCIBIOS_DEVICE_NOT_FOUND; - - switch (size) { - case 1: - *val = ioread8(reg); - break; - case 2: - *val = ioread16(reg); - break; - default: - *val = ioread32(reg); - break; - } - - return PCIBIOS_SUCCESSFUL; -} - -static int rcar_pci_write_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - void __iomem *reg = rcar_pci_cfg_base(bus, devfn, where); - - if (!reg) - return PCIBIOS_DEVICE_NOT_FOUND; - - switch (size) { - case 1: - iowrite8(val, reg); - break; - case 2: - iowrite16(val, reg); - break; - default: - iowrite32(val, reg); - break; - } - - return PCIBIOS_SUCCESSFUL; -} - /* PCI interrupt mapping */ static int rcar_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) { @@ -325,8 +279,9 @@ static int rcar_pci_setup(int nr, struct pci_sys_data *sys) } static struct pci_ops rcar_pci_ops = { - .read = rcar_pci_read_config, - .write = rcar_pci_write_config, + .map_bus = rcar_pci_cfg_base, + .read = pci_generic_config_read, + .write = pci_generic_config_write, }; static int rcar_pci_probe(struct platform_device *pdev) diff --git a/drivers/pci/host/pci-tegra.c b/drivers/pci/host/pci-tegra.c index a800ae916394..00e92720d7f7 100644 --- a/drivers/pci/host/pci-tegra.c +++ b/drivers/pci/host/pci-tegra.c @@ -480,59 +480,10 @@ static void __iomem *tegra_pcie_conf_address(struct pci_bus *bus, return addr; } -static int tegra_pcie_read_conf(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *value) -{ - void __iomem *addr; - - addr = tegra_pcie_conf_address(bus, devfn, where); - if (!addr) { - *value = 0xffffffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - *value = readl(addr); - - if (size == 1) - *value = (*value >> (8 * (where & 3))) & 0xff; - else if (size == 2) - *value = (*value >> (8 * (where & 3))) & 0xffff; - - return PCIBIOS_SUCCESSFUL; -} - -static int tegra_pcie_write_conf(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 value) -{ - void __iomem *addr; - u32 mask, tmp; - - addr = tegra_pcie_conf_address(bus, devfn, where); - if (!addr) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (size == 4) { - writel(value, addr); - return PCIBIOS_SUCCESSFUL; - } - - if (size == 2) - mask = ~(0xffff << ((where & 0x3) * 8)); - else if (size == 1) - mask = ~(0xff << ((where & 0x3) * 8)); - else - return PCIBIOS_BAD_REGISTER_NUMBER; - - tmp = readl(addr) & mask; - tmp |= value << ((where & 0x3) * 8); - writel(tmp, addr); - - return PCIBIOS_SUCCESSFUL; -} - static struct pci_ops tegra_pcie_ops = { - .read = tegra_pcie_read_conf, - .write = tegra_pcie_write_conf, + .map_bus = tegra_pcie_conf_address, + .read = pci_generic_config_read32, + .write = pci_generic_config_write32, }; static unsigned long tegra_pcie_port_get_pex_ctrl(struct tegra_pcie_port *port) @@ -625,19 +576,6 @@ static void tegra_pcie_port_free(struct tegra_pcie_port *port) devm_kfree(pcie->dev, port); } -static void tegra_pcie_fixup_bridge(struct pci_dev *dev) -{ - u16 reg; - - if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) { - pci_read_config_word(dev, PCI_COMMAND, ®); - reg |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | - PCI_COMMAND_MASTER | PCI_COMMAND_SERR); - pci_write_config_word(dev, PCI_COMMAND, reg); - } -} -DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_fixup_bridge); - /* Tegra PCIE root complex wrongly reports device class */ static void tegra_pcie_fixup_class(struct pci_dev *dev) { diff --git a/drivers/pci/host/pci-versatile.c b/drivers/pci/host/pci-versatile.c new file mode 100644 index 000000000000..1ec694a52379 --- /dev/null +++ b/drivers/pci/host/pci-versatile.c @@ -0,0 +1,237 @@ +/* + * Copyright 2004 Koninklijke Philips Electronics NV + * + * Conversion to platform driver and DT: + * Copyright 2014 Linaro Ltd. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + * 14/04/2005 Initial version, colin.king@philips.com + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_pci.h> +#include <linux/of_platform.h> +#include <linux/pci.h> +#include <linux/platform_device.h> + +static void __iomem *versatile_pci_base; +static void __iomem *versatile_cfg_base[2]; + +#define PCI_IMAP(m) (versatile_pci_base + ((m) * 4)) +#define PCI_SMAP(m) (versatile_pci_base + 0x14 + ((m) * 4)) +#define PCI_SELFID (versatile_pci_base + 0xc) + +#define VP_PCI_DEVICE_ID 0x030010ee +#define VP_PCI_CLASS_ID 0x0b400000 + +static u32 pci_slot_ignore; + +static int __init versatile_pci_slot_ignore(char *str) +{ + int retval; + int slot; + + while ((retval = get_option(&str, &slot))) { + if ((slot < 0) || (slot > 31)) + pr_err("Illegal slot value: %d\n", slot); + else + pci_slot_ignore |= (1 << slot); + } + return 1; +} +__setup("pci_slot_ignore=", versatile_pci_slot_ignore); + + +static void __iomem *versatile_map_bus(struct pci_bus *bus, + unsigned int devfn, int offset) +{ + unsigned int busnr = bus->number; + + if (pci_slot_ignore & (1 << PCI_SLOT(devfn))) + return NULL; + + return versatile_cfg_base[1] + ((busnr << 16) | (devfn << 8) | offset); +} + +static struct pci_ops pci_versatile_ops = { + .map_bus = versatile_map_bus, + .read = pci_generic_config_read32, + .write = pci_generic_config_write, +}; + +static int versatile_pci_parse_request_of_pci_ranges(struct device *dev, + struct list_head *res) +{ + int err, mem = 1, res_valid = 0; + struct device_node *np = dev->of_node; + resource_size_t iobase; + struct resource_entry *win; + + err = of_pci_get_host_bridge_resources(np, 0, 0xff, res, &iobase); + if (err) + return err; + + resource_list_for_each_entry(win, res, list) { + struct resource *parent, *res = win->res; + + switch (resource_type(res)) { + case IORESOURCE_IO: + parent = &ioport_resource; + err = pci_remap_iospace(res, iobase); + if (err) { + dev_warn(dev, "error %d: failed to map resource %pR\n", + err, res); + continue; + } + break; + case IORESOURCE_MEM: + parent = &iomem_resource; + res_valid |= !(res->flags & IORESOURCE_PREFETCH); + + writel(res->start >> 28, PCI_IMAP(mem)); + writel(PHYS_OFFSET >> 28, PCI_SMAP(mem)); + mem++; + + break; + case IORESOURCE_BUS: + default: + continue; + } + + err = devm_request_resource(dev, parent, res); + if (err) + goto out_release_res; + } + + if (!res_valid) { + dev_err(dev, "non-prefetchable memory resource required\n"); + err = -EINVAL; + goto out_release_res; + } + + return 0; + +out_release_res: + pci_free_resource_list(res); + return err; +} + +/* Unused, temporary to satisfy ARM arch code */ +struct pci_sys_data sys; + +static int versatile_pci_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret, i, myslot = -1; + u32 val; + void __iomem *local_pci_cfg_base; + struct pci_bus *bus; + LIST_HEAD(pci_res); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + versatile_pci_base = devm_ioremap_resource(&pdev->dev, res); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) + return -ENODEV; + versatile_cfg_base[0] = devm_ioremap_resource(&pdev->dev, res); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (!res) + return -ENODEV; + versatile_cfg_base[1] = devm_ioremap_resource(&pdev->dev, res); + + ret = versatile_pci_parse_request_of_pci_ranges(&pdev->dev, &pci_res); + if (ret) + return ret; + + /* + * We need to discover the PCI core first to configure itself + * before the main PCI probing is performed + */ + for (i = 0; i < 32; i++) { + if ((readl(versatile_cfg_base[0] + (i << 11) + PCI_VENDOR_ID) == VP_PCI_DEVICE_ID) && + (readl(versatile_cfg_base[0] + (i << 11) + PCI_CLASS_REVISION) == VP_PCI_CLASS_ID)) { + myslot = i; + break; + } + } + if (myslot == -1) { + dev_err(&pdev->dev, "Cannot find PCI core!\n"); + return -EIO; + } + /* + * Do not to map Versatile FPGA PCI device into memory space + */ + pci_slot_ignore |= (1 << myslot); + + dev_info(&pdev->dev, "PCI core found (slot %d)\n", myslot); + + writel(myslot, PCI_SELFID); + local_pci_cfg_base = versatile_cfg_base[1] + (myslot << 11); + + val = readl(local_pci_cfg_base + PCI_COMMAND); + val |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE; + writel(val, local_pci_cfg_base + PCI_COMMAND); + + /* + * Configure the PCI inbound memory windows to be 1:1 mapped to SDRAM + */ + writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_0); + writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_1); + writel(PHYS_OFFSET, local_pci_cfg_base + PCI_BASE_ADDRESS_2); + + /* + * For many years the kernel and QEMU were symbiotically buggy + * in that they both assumed the same broken IRQ mapping. + * QEMU therefore attempts to auto-detect old broken kernels + * so that they still work on newer QEMU as they did on old + * QEMU. Since we now use the correct (ie matching-hardware) + * IRQ mapping we write a definitely different value to a + * PCI_INTERRUPT_LINE register to tell QEMU that we expect + * real hardware behaviour and it need not be backwards + * compatible for us. This write is harmless on real hardware. + */ + writel(0, versatile_cfg_base[0] + PCI_INTERRUPT_LINE); + + pci_add_flags(PCI_ENABLE_PROC_DOMAINS); + pci_add_flags(PCI_REASSIGN_ALL_BUS | PCI_REASSIGN_ALL_RSRC); + + bus = pci_scan_root_bus(&pdev->dev, 0, &pci_versatile_ops, &sys, &pci_res); + if (!bus) + return -ENOMEM; + + pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci); + pci_assign_unassigned_bus_resources(bus); + + return 0; +} + +static const struct of_device_id versatile_pci_of_match[] = { + { .compatible = "arm,versatile-pci", }, + { }, +}; +MODULE_DEVICE_TABLE(of, versatile_pci_of_match); + +static struct platform_driver versatile_pci_driver = { + .driver = { + .name = "versatile-pci", + .of_match_table = versatile_pci_of_match, + }, + .probe = versatile_pci_probe, +}; +module_platform_driver(versatile_pci_driver); + +MODULE_DESCRIPTION("Versatile PCI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c index b1d0596457c5..aab55474dd0d 100644 --- a/drivers/pci/host/pci-xgene.c +++ b/drivers/pci/host/pci-xgene.c @@ -16,7 +16,7 @@ * GNU General Public License for more details. * */ -#include <linux/clk-private.h> +#include <linux/clk.h> #include <linux/delay.h> #include <linux/io.h> #include <linux/jiffies.h> @@ -74,92 +74,6 @@ static inline u32 pcie_bar_low_val(u32 addr, u32 flags) return (addr & PCI_BASE_ADDRESS_MEM_MASK) | flags; } -/* PCIe Configuration Out/In */ -static inline void xgene_pcie_cfg_out32(void __iomem *addr, int offset, u32 val) -{ - writel(val, addr + offset); -} - -static inline void xgene_pcie_cfg_out16(void __iomem *addr, int offset, u16 val) -{ - u32 val32 = readl(addr + (offset & ~0x3)); - - switch (offset & 0x3) { - case 2: - val32 &= ~0xFFFF0000; - val32 |= (u32)val << 16; - break; - case 0: - default: - val32 &= ~0xFFFF; - val32 |= val; - break; - } - writel(val32, addr + (offset & ~0x3)); -} - -static inline void xgene_pcie_cfg_out8(void __iomem *addr, int offset, u8 val) -{ - u32 val32 = readl(addr + (offset & ~0x3)); - - switch (offset & 0x3) { - case 0: - val32 &= ~0xFF; - val32 |= val; - break; - case 1: - val32 &= ~0xFF00; - val32 |= (u32)val << 8; - break; - case 2: - val32 &= ~0xFF0000; - val32 |= (u32)val << 16; - break; - case 3: - default: - val32 &= ~0xFF000000; - val32 |= (u32)val << 24; - break; - } - writel(val32, addr + (offset & ~0x3)); -} - -static inline void xgene_pcie_cfg_in32(void __iomem *addr, int offset, u32 *val) -{ - *val = readl(addr + offset); -} - -static inline void xgene_pcie_cfg_in16(void __iomem *addr, int offset, u32 *val) -{ - *val = readl(addr + (offset & ~0x3)); - - switch (offset & 0x3) { - case 2: - *val >>= 16; - break; - } - - *val &= 0xFFFF; -} - -static inline void xgene_pcie_cfg_in8(void __iomem *addr, int offset, u32 *val) -{ - *val = readl(addr + (offset & ~0x3)); - - switch (offset & 0x3) { - case 3: - *val = *val >> 24; - break; - case 2: - *val = *val >> 16; - break; - case 1: - *val = *val >> 8; - break; - } - *val &= 0xFF; -} - /* * When the address bit [17:16] is 2'b01, the Configuration access will be * treated as Type 1 and it will be forwarded to external PCIe device. @@ -213,69 +127,23 @@ static bool xgene_pcie_hide_rc_bars(struct pci_bus *bus, int offset) return false; } -static int xgene_pcie_read_config(struct pci_bus *bus, unsigned int devfn, - int offset, int len, u32 *val) -{ - struct xgene_pcie_port *port = bus->sysdata; - void __iomem *addr; - - if ((pci_is_root_bus(bus) && devfn != 0) || !port->link_up) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (xgene_pcie_hide_rc_bars(bus, offset)) { - *val = 0; - return PCIBIOS_SUCCESSFUL; - } - - xgene_pcie_set_rtdid_reg(bus, devfn); - addr = xgene_pcie_get_cfg_base(bus); - switch (len) { - case 1: - xgene_pcie_cfg_in8(addr, offset, val); - break; - case 2: - xgene_pcie_cfg_in16(addr, offset, val); - break; - default: - xgene_pcie_cfg_in32(addr, offset, val); - break; - } - - return PCIBIOS_SUCCESSFUL; -} - -static int xgene_pcie_write_config(struct pci_bus *bus, unsigned int devfn, - int offset, int len, u32 val) +static int xgene_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, + int offset) { struct xgene_pcie_port *port = bus->sysdata; - void __iomem *addr; - if ((pci_is_root_bus(bus) && devfn != 0) || !port->link_up) - return PCIBIOS_DEVICE_NOT_FOUND; - - if (xgene_pcie_hide_rc_bars(bus, offset)) - return PCIBIOS_SUCCESSFUL; + if ((pci_is_root_bus(bus) && devfn != 0) || !port->link_up || + xgene_pcie_hide_rc_bars(bus, offset)) + return NULL; xgene_pcie_set_rtdid_reg(bus, devfn); - addr = xgene_pcie_get_cfg_base(bus); - switch (len) { - case 1: - xgene_pcie_cfg_out8(addr, offset, (u8)val); - break; - case 2: - xgene_pcie_cfg_out16(addr, offset, (u16)val); - break; - default: - xgene_pcie_cfg_out32(addr, offset, val); - break; - } - - return PCIBIOS_SUCCESSFUL; + return xgene_pcie_get_cfg_base(bus); } static struct pci_ops xgene_pcie_ops = { - .read = xgene_pcie_read_config, - .write = xgene_pcie_write_config + .map_bus = xgene_pcie_map_bus, + .read = pci_generic_config_read32, + .write = pci_generic_config_write32, }; static u64 xgene_pcie_set_ib_mask(void __iomem *csr_base, u32 addr, @@ -401,11 +269,11 @@ static int xgene_pcie_map_ranges(struct xgene_pcie_port *port, struct list_head *res, resource_size_t io_base) { - struct pci_host_bridge_window *window; + struct resource_entry *window; struct device *dev = port->dev; int ret; - list_for_each_entry(window, res, list) { + resource_list_for_each_entry(window, res) { struct resource *res = window->res; u64 restype = resource_type(res); diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c index df781cdf13c1..1f4ea6f2d910 100644 --- a/drivers/pci/host/pcie-designware.c +++ b/drivers/pci/host/pcie-designware.c @@ -283,6 +283,9 @@ static int dw_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev, struct msi_msg msg; struct pcie_port *pp = sys_to_pcie(pdev->bus->sysdata); + if (desc->msi_attrib.is_msix) + return -EINVAL; + irq = assign_irq(1, desc, &pos); if (irq < 0) return irq; @@ -508,9 +511,6 @@ int __init dw_pcie_host_init(struct pcie_port *pp) dw_pci.private_data = (void **)&pp; pci_common_init_dev(pp->dev, &dw_pci); -#ifdef CONFIG_PCI_DOMAINS - dw_pci.domain++; -#endif return 0; } diff --git a/drivers/pci/host/pcie-rcar.c b/drivers/pci/host/pcie-rcar.c index 748786c402fc..c57bd0ac39a0 100644 --- a/drivers/pci/host/pcie-rcar.c +++ b/drivers/pci/host/pcie-rcar.c @@ -397,9 +397,6 @@ static void rcar_pcie_enable(struct rcar_pcie *pcie) #endif pci_common_init_dev(&pdev->dev, &rcar_pci); -#ifdef CONFIG_PCI_DOMAINS - rcar_pci.domain++; -#endif } static int phy_wait_for_ack(struct rcar_pcie *pcie) @@ -757,7 +754,7 @@ static int rcar_pcie_get_resources(struct platform_device *pdev, goto err_map_reg; i = irq_of_parse_and_map(pdev->dev.of_node, 0); - if (i < 0) { + if (!i) { dev_err(pcie->dev, "cannot get platform resources for msi interrupt\n"); err = -ENOENT; goto err_map_reg; @@ -765,7 +762,7 @@ static int rcar_pcie_get_resources(struct platform_device *pdev, pcie->msi.irq1 = i; i = irq_of_parse_and_map(pdev->dev.of_node, 1); - if (i < 0) { + if (!i) { dev_err(pcie->dev, "cannot get platform resources for msi interrupt\n"); err = -ENOENT; goto err_map_reg; diff --git a/drivers/pci/host/pcie-xilinx.c b/drivers/pci/host/pcie-xilinx.c index ef3ebaf9a738..f1a06a091ccb 100644 --- a/drivers/pci/host/pcie-xilinx.c +++ b/drivers/pci/host/pcie-xilinx.c @@ -148,10 +148,10 @@ static inline bool xilinx_pcie_link_is_up(struct xilinx_pcie_port *port) */ static void xilinx_pcie_clear_err_interrupts(struct xilinx_pcie_port *port) { - u32 val = pcie_read(port, XILINX_PCIE_REG_RPEFR); + unsigned long val = pcie_read(port, XILINX_PCIE_REG_RPEFR); if (val & XILINX_PCIE_RPEFR_ERR_VALID) { - dev_dbg(port->dev, "Requester ID %d\n", + dev_dbg(port->dev, "Requester ID %lu\n", val & XILINX_PCIE_RPEFR_REQ_ID); pcie_write(port, XILINX_PCIE_RPEFR_ALL_MASK, XILINX_PCIE_REG_RPEFR); @@ -189,7 +189,7 @@ static bool xilinx_pcie_valid_device(struct pci_bus *bus, unsigned int devfn) } /** - * xilinx_pcie_config_base - Get configuration base + * xilinx_pcie_map_bus - Get configuration base * @bus: PCI Bus structure * @devfn: Device/function * @where: Offset from base @@ -197,96 +197,26 @@ static bool xilinx_pcie_valid_device(struct pci_bus *bus, unsigned int devfn) * Return: Base address of the configuration space needed to be * accessed. */ -static void __iomem *xilinx_pcie_config_base(struct pci_bus *bus, - unsigned int devfn, int where) +static void __iomem *xilinx_pcie_map_bus(struct pci_bus *bus, + unsigned int devfn, int where) { struct xilinx_pcie_port *port = sys_to_pcie(bus->sysdata); int relbus; + if (!xilinx_pcie_valid_device(bus, devfn)) + return NULL; + relbus = (bus->number << ECAM_BUS_NUM_SHIFT) | (devfn << ECAM_DEV_NUM_SHIFT); return port->reg_base + relbus + where; } -/** - * xilinx_pcie_read_config - Read configuration space - * @bus: PCI Bus structure - * @devfn: Device/function - * @where: Offset from base - * @size: Byte/word/dword - * @val: Value to be read - * - * Return: PCIBIOS_SUCCESSFUL on success - * PCIBIOS_DEVICE_NOT_FOUND on failure - */ -static int xilinx_pcie_read_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *val) -{ - void __iomem *addr; - - if (!xilinx_pcie_valid_device(bus, devfn)) { - *val = 0xFFFFFFFF; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - addr = xilinx_pcie_config_base(bus, devfn, where); - - switch (size) { - case 1: - *val = readb(addr); - break; - case 2: - *val = readw(addr); - break; - default: - *val = readl(addr); - break; - } - - return PCIBIOS_SUCCESSFUL; -} - -/** - * xilinx_pcie_write_config - Write configuration space - * @bus: PCI Bus structure - * @devfn: Device/function - * @where: Offset from base - * @size: Byte/word/dword - * @val: Value to be written to device - * - * Return: PCIBIOS_SUCCESSFUL on success - * PCIBIOS_DEVICE_NOT_FOUND on failure - */ -static int xilinx_pcie_write_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 val) -{ - void __iomem *addr; - - if (!xilinx_pcie_valid_device(bus, devfn)) - return PCIBIOS_DEVICE_NOT_FOUND; - - addr = xilinx_pcie_config_base(bus, devfn, where); - - switch (size) { - case 1: - writeb(val, addr); - break; - case 2: - writew(val, addr); - break; - default: - writel(val, addr); - break; - } - - return PCIBIOS_SUCCESSFUL; -} - /* PCIe operations */ static struct pci_ops xilinx_pcie_ops = { - .read = xilinx_pcie_read_config, - .write = xilinx_pcie_write_config, + .map_bus = xilinx_pcie_map_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, }; /* MSI functions */ @@ -737,7 +667,7 @@ static int xilinx_pcie_parse_and_add_res(struct xilinx_pcie_port *port) resource_size_t offset; struct of_pci_range_parser parser; struct of_pci_range range; - struct pci_host_bridge_window *win; + struct resource_entry *win; int err = 0, mem_resno = 0; /* Get the ranges */ @@ -807,7 +737,7 @@ static int xilinx_pcie_parse_and_add_res(struct xilinx_pcie_port *port) free_resources: release_child_resources(&iomem_resource); - list_for_each_entry(win, &port->resources, list) + resource_list_for_each_entry(win, &port->resources) devm_kfree(dev, win->res); pci_free_resource_list(&port->resources); diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c index a5a7fd8332ac..46db29395a62 100644 --- a/drivers/pci/hotplug/cpci_hotplug_core.c +++ b/drivers/pci/hotplug/cpci_hotplug_core.c @@ -214,8 +214,7 @@ static void release_slot(struct hotplug_slot *hotplug_slot) kfree(slot->hotplug_slot->info); kfree(slot->hotplug_slot); - if (slot->dev) - pci_dev_put(slot->dev); + pci_dev_put(slot->dev); kfree(slot); } diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index ff32e85e1de6..f052e951b23e 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -532,8 +532,6 @@ static void interrupt_event_handler(struct work_struct *work) pciehp_green_led_off(p_slot); break; case INT_PRESENCE_ON: - if (!HP_SUPR_RM(ctrl)) - break; ctrl_dbg(ctrl, "Surprise Insertion\n"); handle_surprise_event(p_slot); break; diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index bada20999870..c32fb786d48e 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -475,7 +475,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot) struct slot *slot = bss_hotplug_slot->private; struct pci_dev *dev, *temp; int rc; - acpi_owner_id ssdt_id = 0; + acpi_handle ssdt_hdl = NULL; /* Acquire update access to the bus */ mutex_lock(&sn_hotplug_mutex); @@ -522,7 +522,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot) if (ACPI_SUCCESS(ret) && (adr>>16) == (slot->device_num + 1)) { /* retain the owner id */ - acpi_get_id(chandle, &ssdt_id); + ssdt_hdl = chandle; ret = acpi_bus_get_device(chandle, &device); @@ -547,12 +547,13 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot) pci_unlock_rescan_remove(); /* Remove the SSDT for the slot from the ACPI namespace */ - if (SN_ACPI_BASE_SUPPORT() && ssdt_id) { + if (SN_ACPI_BASE_SUPPORT() && ssdt_hdl) { acpi_status ret; - ret = acpi_unload_table_id(ssdt_id); + ret = acpi_unload_parent_table(ssdt_hdl); if (ACPI_FAILURE(ret)) { - printk(KERN_ERR "%s: acpi_unload_table_id failed (0x%x) for id %d\n", - __func__, ret, ssdt_id); + acpi_handle_err(ssdt_hdl, + "%s: acpi_unload_parent_table failed (0x%x)\n", + __func__, ret); /* try to continue on */ } } diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index fd60806d3fd0..c3e7dfcf9ff5 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -694,11 +694,16 @@ static void __iomem *msix_map_region(struct pci_dev *dev, unsigned nr_entries) { resource_size_t phys_addr; u32 table_offset; + unsigned long flags; u8 bir; pci_read_config_dword(dev, dev->msix_cap + PCI_MSIX_TABLE, &table_offset); bir = (u8)(table_offset & PCI_MSIX_TABLE_BIR); + flags = pci_resource_flags(dev, bir); + if (!flags || (flags & IORESOURCE_UNSET)) + return NULL; + table_offset &= PCI_MSIX_TABLE_OFFSET; phys_addr = pci_resource_start(dev, bir) + table_offset; diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 3542150fc8a3..489063987325 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -501,12 +501,29 @@ static int acpi_pci_run_wake(struct pci_dev *dev, bool enable) return 0; } +static bool acpi_pci_need_resume(struct pci_dev *dev) +{ + struct acpi_device *adev = ACPI_COMPANION(&dev->dev); + + if (!adev || !acpi_device_power_manageable(adev)) + return false; + + if (device_may_wakeup(&dev->dev) != !!adev->wakeup.prepare_count) + return true; + + if (acpi_target_system_state() == ACPI_STATE_S0) + return false; + + return !!adev->power.flags.dsw_present; +} + static struct pci_platform_pm_ops acpi_pci_platform_pm = { .is_manageable = acpi_pci_power_manageable, .set_state = acpi_pci_set_power_state, .choose_state = acpi_pci_choose_state, .sleep_wake = acpi_pci_sleep_wake, .run_wake = acpi_pci_run_wake, + .need_resume = acpi_pci_need_resume, }; void acpi_pci_add_bus(struct pci_bus *bus) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 887e6bd95af7..3cb2210de553 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -653,7 +653,6 @@ static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) static int pci_pm_prepare(struct device *dev) { struct device_driver *drv = dev->driver; - int error = 0; /* * Devices having power.ignore_children set may still be necessary for @@ -662,10 +661,12 @@ static int pci_pm_prepare(struct device *dev) if (dev->power.ignore_children) pm_runtime_resume(dev); - if (drv && drv->pm && drv->pm->prepare) - error = drv->pm->prepare(dev); - - return error; + if (drv && drv->pm && drv->pm->prepare) { + int error = drv->pm->prepare(dev); + if (error) + return error; + } + return pci_dev_keep_suspended(to_pci_dev(dev)); } @@ -1383,7 +1384,7 @@ static int pci_uevent(struct device *dev, struct kobj_uevent_env *env) if (add_uevent_var(env, "PCI_SLOT_NAME=%s", pci_name(pdev))) return -ENOMEM; - if (add_uevent_var(env, "MODALIAS=pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02x", + if (add_uevent_var(env, "MODALIAS=pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02X", pdev->vendor, pdev->device, pdev->subsystem_vendor, pdev->subsystem_device, (u8)(pdev->class >> 16), (u8)(pdev->class >> 8), diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index cab05f31223f..81f06e8dcc04 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -10,6 +10,8 @@ #include <linux/kernel.h> #include <linux/delay.h> #include <linux/init.h> +#include <linux/of.h> +#include <linux/of_pci.h> #include <linux/pci.h> #include <linux/pm.h> #include <linux/slab.h> @@ -521,6 +523,11 @@ static inline int platform_pci_run_wake(struct pci_dev *dev, bool enable) pci_platform_pm->run_wake(dev, enable) : -ENODEV; } +static inline bool platform_pci_need_resume(struct pci_dev *dev) +{ + return pci_platform_pm ? pci_platform_pm->need_resume(dev) : false; +} + /** * pci_raw_set_power_state - Use PCI PM registers to set the power state of * given PCI device @@ -1999,6 +2006,27 @@ bool pci_dev_run_wake(struct pci_dev *dev) } EXPORT_SYMBOL_GPL(pci_dev_run_wake); +/** + * pci_dev_keep_suspended - Check if the device can stay in the suspended state. + * @pci_dev: Device to check. + * + * Return 'true' if the device is runtime-suspended, it doesn't have to be + * reconfigured due to wakeup settings difference between system and runtime + * suspend and the current power state of it is suitable for the upcoming + * (system) transition. + */ +bool pci_dev_keep_suspended(struct pci_dev *pci_dev) +{ + struct device *dev = &pci_dev->dev; + + if (!pm_runtime_suspended(dev) + || (device_can_wakeup(dev) && !device_may_wakeup(dev)) + || platform_pci_need_resume(pci_dev)) + return false; + + return pci_target_state(pci_dev) == pci_dev->current_state; +} + void pci_config_pm_runtime_get(struct pci_dev *pdev) { struct device *dev = &pdev->dev; @@ -3197,7 +3225,7 @@ static int pci_pm_reset(struct pci_dev *dev, int probe) { u16 csr; - if (!dev->pm_cap) + if (!dev->pm_cap || dev->dev_flags & PCI_DEV_FLAGS_NO_PM_RESET) return -ENOTTY; pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &csr); @@ -3271,7 +3299,8 @@ static int pci_parent_bus_reset(struct pci_dev *dev, int probe) { struct pci_dev *pdev; - if (pci_is_root_bus(dev->bus) || dev->subordinate || !dev->bus->self) + if (pci_is_root_bus(dev->bus) || dev->subordinate || + !dev->bus->self || dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET) return -ENOTTY; list_for_each_entry(pdev, &dev->bus->devices, bus_list) @@ -3305,7 +3334,8 @@ static int pci_dev_reset_slot_function(struct pci_dev *dev, int probe) { struct pci_dev *pdev; - if (dev->subordinate || !dev->slot) + if (dev->subordinate || !dev->slot || + dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET) return -ENOTTY; list_for_each_entry(pdev, &dev->bus->devices, bus_list) @@ -3557,6 +3587,20 @@ int pci_try_reset_function(struct pci_dev *dev) } EXPORT_SYMBOL_GPL(pci_try_reset_function); +/* Do any devices on or below this bus prevent a bus reset? */ +static bool pci_bus_resetable(struct pci_bus *bus) +{ + struct pci_dev *dev; + + list_for_each_entry(dev, &bus->devices, bus_list) { + if (dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET || + (dev->subordinate && !pci_bus_resetable(dev->subordinate))) + return false; + } + + return true; +} + /* Lock devices from the top of the tree down */ static void pci_bus_lock(struct pci_bus *bus) { @@ -3607,6 +3651,22 @@ unlock: return 0; } +/* Do any devices on or below this slot prevent a bus reset? */ +static bool pci_slot_resetable(struct pci_slot *slot) +{ + struct pci_dev *dev; + + list_for_each_entry(dev, &slot->bus->devices, bus_list) { + if (!dev->slot || dev->slot != slot) + continue; + if (dev->dev_flags & PCI_DEV_FLAGS_NO_BUS_RESET || + (dev->subordinate && !pci_bus_resetable(dev->subordinate))) + return false; + } + + return true; +} + /* Lock devices from the top of the tree down */ static void pci_slot_lock(struct pci_slot *slot) { @@ -3728,7 +3788,7 @@ static int pci_slot_reset(struct pci_slot *slot, int probe) { int rc; - if (!slot) + if (!slot || !pci_slot_resetable(slot)) return -ENOTTY; if (!probe) @@ -3820,7 +3880,7 @@ EXPORT_SYMBOL_GPL(pci_try_reset_slot); static int pci_bus_reset(struct pci_bus *bus, int probe) { - if (!bus->self) + if (!bus->self || !pci_bus_resetable(bus)) return -ENOTTY; if (probe) @@ -4439,6 +4499,53 @@ int pci_get_new_domain_nr(void) { return atomic_inc_return(&__domain_nr); } + +#ifdef CONFIG_PCI_DOMAINS_GENERIC +void pci_bus_assign_domain_nr(struct pci_bus *bus, struct device *parent) +{ + static int use_dt_domains = -1; + int domain = of_get_pci_domain_nr(parent->of_node); + + /* + * Check DT domain and use_dt_domains values. + * + * If DT domain property is valid (domain >= 0) and + * use_dt_domains != 0, the DT assignment is valid since this means + * we have not previously allocated a domain number by using + * pci_get_new_domain_nr(); we should also update use_dt_domains to + * 1, to indicate that we have just assigned a domain number from + * DT. + * + * If DT domain property value is not valid (ie domain < 0), and we + * have not previously assigned a domain number from DT + * (use_dt_domains != 1) we should assign a domain number by + * using the: + * + * pci_get_new_domain_nr() + * + * API and update the use_dt_domains value to keep track of method we + * are using to assign domain numbers (use_dt_domains = 0). + * + * All other combinations imply we have a platform that is trying + * to mix domain numbers obtained from DT and pci_get_new_domain_nr(), + * which is a recipe for domain mishandling and it is prevented by + * invalidating the domain value (domain = -1) and printing a + * corresponding error. + */ + if (domain >= 0 && use_dt_domains) { + use_dt_domains = 1; + } else if (domain < 0 && use_dt_domains != 1) { + use_dt_domains = 0; + domain = pci_get_new_domain_nr(); + } else { + dev_err(parent, "Node %s has inconsistent \"linux,pci-domain\" property in DT\n", + parent->of_node->full_name); + domain = -1; + } + + bus->domain_nr = domain; +} +#endif #endif /** diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 8aff29a804ff..4091f82239cd 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -50,6 +50,10 @@ int pci_probe_reset_function(struct pci_dev *dev); * for given device (the device's wake-up capability has to be * enabled by @sleep_wake for this feature to work) * + * @need_resume: returns 'true' if the given device (which is currently + * suspended) needs to be resumed to be configured for system + * wakeup. + * * If given platform is generally capable of power managing PCI devices, all of * these callbacks are mandatory. */ @@ -59,6 +63,7 @@ struct pci_platform_pm_ops { pci_power_t (*choose_state)(struct pci_dev *dev); int (*sleep_wake)(struct pci_dev *dev, bool enable); int (*run_wake)(struct pci_dev *dev, bool enable); + bool (*need_resume)(struct pci_dev *dev); }; int pci_set_platform_pm(struct pci_platform_pm_ops *ops); @@ -67,6 +72,7 @@ void pci_power_up(struct pci_dev *dev); void pci_disable_enabled_device(struct pci_dev *dev); int pci_finish_runtime_suspend(struct pci_dev *dev); int __pci_pme_wakeup(struct pci_dev *dev, void *ign); +bool pci_dev_keep_suspended(struct pci_dev *dev); void pci_config_pm_runtime_get(struct pci_dev *dev); void pci_config_pm_runtime_put(struct pci_dev *dev); void pci_pm_init(struct pci_dev *dev); @@ -208,6 +214,7 @@ void __pci_bus_size_bridges(struct pci_bus *bus, void __pci_bus_assign_resources(const struct pci_bus *bus, struct list_head *realloc_head, struct list_head *fail_head); +bool pci_bus_clip_resource(struct pci_dev *dev, int idx); /** * pci_ari_enabled - query ARI forwarding status diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index e1e7026b838d..820740a22e94 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -859,7 +859,10 @@ static ssize_t link_state_store(struct device *dev, { struct pci_dev *pdev = to_pci_dev(dev); struct pcie_link_state *link, *root = pdev->link_state->root; - u32 val = buf[0] - '0', state = 0; + u32 val, state = 0; + + if (kstrtouint(buf, 10, &val)) + return -EINVAL; if (aspm_disabled) return -EPERM; @@ -900,15 +903,14 @@ static ssize_t clk_ctl_store(struct device *dev, size_t n) { struct pci_dev *pdev = to_pci_dev(dev); - int state; + bool state; - if (n < 1) + if (strtobool(buf, &state)) return -EINVAL; - state = buf[0]-'0'; down_read(&pci_bus_sem); mutex_lock(&aspm_lock); - pcie_set_clkpm_nocheck(pdev->link_state, !!state); + pcie_set_clkpm_nocheck(pdev->link_state, state); mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 23212f8ae09b..8d2f400e96cb 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1895,7 +1895,7 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, int error; struct pci_host_bridge *bridge; struct pci_bus *b, *b2; - struct pci_host_bridge_window *window, *n; + struct resource_entry *window, *n; struct resource *res; resource_size_t offset; char bus_addr[64]; @@ -1959,8 +1959,8 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, printk(KERN_INFO "PCI host bridge to bus %s\n", dev_name(&b->dev)); /* Add initial resources to the bus */ - list_for_each_entry_safe(window, n, resources, list) { - list_move_tail(&window->list, &bridge->windows); + resource_list_for_each_entry_safe(window, n, resources) { + list_move_tail(&window->node, &bridge->windows); res = window->res; offset = window->offset; if (res->flags & IORESOURCE_BUS) @@ -2060,12 +2060,12 @@ void pci_bus_release_busn_res(struct pci_bus *b) struct pci_bus *pci_scan_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources) { - struct pci_host_bridge_window *window; + struct resource_entry *window; bool found = false; struct pci_bus *b; int max; - list_for_each_entry(window, resources, list) + resource_list_for_each_entry(window, resources) if (window->res->flags & IORESOURCE_BUS) { found = true; break; diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index ed6f89b6efe5..85f247e28a80 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -324,18 +324,52 @@ static void quirk_s3_64M(struct pci_dev *dev) DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_868, quirk_s3_64M); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_968, quirk_s3_64M); +static void quirk_io(struct pci_dev *dev, int pos, unsigned size, + const char *name) +{ + u32 region; + struct pci_bus_region bus_region; + struct resource *res = dev->resource + pos; + + pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + (pos << 2), ®ion); + + if (!region) + return; + + res->name = pci_name(dev); + res->flags = region & ~PCI_BASE_ADDRESS_IO_MASK; + res->flags |= + (IORESOURCE_IO | IORESOURCE_PCI_FIXED | IORESOURCE_SIZEALIGN); + region &= ~(size - 1); + + /* Convert from PCI bus to resource space */ + bus_region.start = region; + bus_region.end = region + size - 1; + pcibios_bus_to_resource(dev->bus, res, &bus_region); + + dev_info(&dev->dev, FW_BUG "%s quirk: reg 0x%x: %pR\n", + name, PCI_BASE_ADDRESS_0 + (pos << 2), res); +} + /* * Some CS5536 BIOSes (for example, the Soekris NET5501 board w/ comBIOS * ver. 1.33 20070103) don't set the correct ISA PCI region header info. * BAR0 should be 8 bytes; instead, it may be set to something like 8k * (which conflicts w/ BAR1's memory range). + * + * CS553x's ISA PCI BARs may also be read-only (ref: + * https://bugzilla.kernel.org/show_bug.cgi?id=85991 - Comment #4 forward). */ static void quirk_cs5536_vsa(struct pci_dev *dev) { + static char *name = "CS5536 ISA bridge"; + if (pci_resource_len(dev, 0) != 8) { - struct resource *res = &dev->resource[0]; - res->end = res->start + 8 - 1; - dev_info(&dev->dev, "CS5536 ISA bridge bug detected (incorrect header); workaround applied\n"); + quirk_io(dev, 0, 8, name); /* SMB */ + quirk_io(dev, 1, 256, name); /* GPIO */ + quirk_io(dev, 2, 64, name); /* MFGPT */ + dev_info(&dev->dev, "%s bug detected (incorrect header); workaround applied\n", + name); } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA, quirk_cs5536_vsa); @@ -3028,6 +3062,41 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_REALTEK, 0x8169, DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MELLANOX, PCI_ANY_ID, quirk_broken_intx_masking); +static void quirk_no_bus_reset(struct pci_dev *dev) +{ + dev->dev_flags |= PCI_DEV_FLAGS_NO_BUS_RESET; +} + +/* + * Atheros AR93xx chips do not behave after a bus reset. The device will + * throw a Link Down error on AER-capable systems and regardless of AER, + * config space of the device is never accessible again and typically + * causes the system to hang or reset when access is attempted. + * http://www.spinics.net/lists/linux-pci/msg34797.html + */ +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0030, quirk_no_bus_reset); + +static void quirk_no_pm_reset(struct pci_dev *dev) +{ + /* + * We can't do a bus reset on root bus devices, but an ineffective + * PM reset may be better than nothing. + */ + if (!pci_is_root_bus(dev->bus)) + dev->dev_flags |= PCI_DEV_FLAGS_NO_PM_RESET; +} + +/* + * Some AMD/ATI GPUS (HD8570 - Oland) report that a D3hot->D0 transition + * causes a reset (i.e., they advertise NoSoftRst-). This transition seems + * to have no effect on the device: it retains the framebuffer contents and + * monitor sync. Advertising this support makes other layers, like VFIO, + * assume pci_reset_function() is viable for this device. Mark it as + * unavailable to skip it when testing reset methods. + */ +DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_ATI, PCI_ANY_ID, + PCI_CLASS_DISPLAY_VGA, 8, quirk_no_pm_reset); + #ifdef CONFIG_ACPI /* * Apple: Shutdown Cactus Ridge Thunderbolt controller. @@ -3528,6 +3597,44 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_JMICRON, quirk_dma_func1_alias); /* + * Some devices DMA with the wrong devfn, not just the wrong function. + * quirk_fixed_dma_alias() uses this table to create fixed aliases, where + * the alias is "fixed" and independent of the device devfn. + * + * For example, the Adaptec 3405 is a PCIe card with an Intel 80333 I/O + * processor. To software, this appears as a PCIe-to-PCI/X bridge with a + * single device on the secondary bus. In reality, the single exposed + * device at 0e.0 is the Address Translation Unit (ATU) of the controller + * that provides a bridge to the internal bus of the I/O processor. The + * controller supports private devices, which can be hidden from PCI config + * space. In the case of the Adaptec 3405, a private device at 01.0 + * appears to be the DMA engine, which therefore needs to become a DMA + * alias for the device. + */ +static const struct pci_device_id fixed_dma_alias_tbl[] = { + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x0285, + PCI_VENDOR_ID_ADAPTEC2, 0x02bb), /* Adaptec 3405 */ + .driver_data = PCI_DEVFN(1, 0) }, + { 0 } +}; + +static void quirk_fixed_dma_alias(struct pci_dev *dev) +{ + const struct pci_device_id *id; + + id = pci_match_id(fixed_dma_alias_tbl, dev); + if (id) { + dev->dma_alias_devfn = id->driver_data; + dev->dev_flags |= PCI_DEV_FLAGS_DMA_ALIAS_DEVFN; + dev_info(&dev->dev, "Enabling fixed DMA alias to %02x.%d\n", + PCI_SLOT(dev->dma_alias_devfn), + PCI_FUNC(dev->dma_alias_devfn)); + } +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ADAPTEC2, 0x0285, quirk_fixed_dma_alias); + +/* * A few PCIe-to-PCI bridges fail to expose a PCIe capability, resulting in * using the wrong DMA alias for the device. Some of these devices can be * used as either forward or reverse bridges, so we need to test whether the @@ -3630,6 +3737,9 @@ static const u16 pci_quirk_intel_pch_acs_ids[] = { 0x9c98, 0x9c99, 0x9c9a, 0x9c9b, /* Patsburg (X79) PCH */ 0x1d10, 0x1d12, 0x1d14, 0x1d16, 0x1d18, 0x1d1a, 0x1d1c, 0x1d1e, + /* Wellsburg (X99) PCH */ + 0x8d10, 0x8d11, 0x8d12, 0x8d13, 0x8d14, 0x8d15, 0x8d16, 0x8d17, + 0x8d18, 0x8d19, 0x8d1a, 0x8d1b, 0x8d1c, 0x8d1d, 0x8d1e, }; static bool pci_quirk_intel_pch_acs_match(struct pci_dev *dev) @@ -3713,6 +3823,8 @@ static const struct pci_dev_acs_enabled { { PCI_VENDOR_ID_INTEL, 0x1551, pci_quirk_mf_endpoint_acs }, { PCI_VENDOR_ID_INTEL, 0x1558, pci_quirk_mf_endpoint_acs }, { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, pci_quirk_intel_pch_acs }, + { 0x19a2, 0x710, pci_quirk_mf_endpoint_acs }, /* Emulex BE3-R */ + { 0x10df, 0x720, pci_quirk_mf_endpoint_acs }, /* Emulex Skyhawk-R */ { 0 } }; diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c index f955edb9bea7..eb0ad530dc43 100644 --- a/drivers/pci/rom.c +++ b/drivers/pci/rom.c @@ -71,6 +71,7 @@ size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size) { void __iomem *image; int last_image; + unsigned length; image = rom; do { @@ -93,9 +94,9 @@ size_t pci_get_rom_size(struct pci_dev *pdev, void __iomem *rom, size_t size) if (readb(pds + 3) != 'R') break; last_image = readb(pds + 21) & 0x80; - /* this length is reliable */ - image += readw(pds + 16) * 512; - } while (!last_image); + length = readw(pds + 16); + image += length * 512; + } while (length && !last_image); /* never return a size larger than the PCI resource window */ /* there are known ROMs that get the size wrong */ diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 0482235eee92..e3e17f3c0f0f 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -530,9 +530,8 @@ EXPORT_SYMBOL(pci_setup_cardbus); config space writes, so it's quite possible that an I/O window of the bridge will have some undesirable address (e.g. 0) after the first write. Ditto 64-bit prefetchable MMIO. */ -static void pci_setup_bridge_io(struct pci_bus *bus) +static void pci_setup_bridge_io(struct pci_dev *bridge) { - struct pci_dev *bridge = bus->self; struct resource *res; struct pci_bus_region region; unsigned long io_mask; @@ -545,7 +544,7 @@ static void pci_setup_bridge_io(struct pci_bus *bus) io_mask = PCI_IO_1K_RANGE_MASK; /* Set up the top and bottom of the PCI I/O segment for this bus. */ - res = bus->resource[0]; + res = &bridge->resource[PCI_BRIDGE_RESOURCES + 0]; pcibios_resource_to_bus(bridge->bus, ®ion, res); if (res->flags & IORESOURCE_IO) { pci_read_config_word(bridge, PCI_IO_BASE, &l); @@ -568,15 +567,14 @@ static void pci_setup_bridge_io(struct pci_bus *bus) pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, io_upper16); } -static void pci_setup_bridge_mmio(struct pci_bus *bus) +static void pci_setup_bridge_mmio(struct pci_dev *bridge) { - struct pci_dev *bridge = bus->self; struct resource *res; struct pci_bus_region region; u32 l; /* Set up the top and bottom of the PCI Memory segment for this bus. */ - res = bus->resource[1]; + res = &bridge->resource[PCI_BRIDGE_RESOURCES + 1]; pcibios_resource_to_bus(bridge->bus, ®ion, res); if (res->flags & IORESOURCE_MEM) { l = (region.start >> 16) & 0xfff0; @@ -588,9 +586,8 @@ static void pci_setup_bridge_mmio(struct pci_bus *bus) pci_write_config_dword(bridge, PCI_MEMORY_BASE, l); } -static void pci_setup_bridge_mmio_pref(struct pci_bus *bus) +static void pci_setup_bridge_mmio_pref(struct pci_dev *bridge) { - struct pci_dev *bridge = bus->self; struct resource *res; struct pci_bus_region region; u32 l, bu, lu; @@ -602,7 +599,7 @@ static void pci_setup_bridge_mmio_pref(struct pci_bus *bus) /* Set up PREF base/limit. */ bu = lu = 0; - res = bus->resource[2]; + res = &bridge->resource[PCI_BRIDGE_RESOURCES + 2]; pcibios_resource_to_bus(bridge->bus, ®ion, res); if (res->flags & IORESOURCE_PREFETCH) { l = (region.start >> 16) & 0xfff0; @@ -630,13 +627,13 @@ static void __pci_setup_bridge(struct pci_bus *bus, unsigned long type) &bus->busn_res); if (type & IORESOURCE_IO) - pci_setup_bridge_io(bus); + pci_setup_bridge_io(bridge); if (type & IORESOURCE_MEM) - pci_setup_bridge_mmio(bus); + pci_setup_bridge_mmio(bridge); if (type & IORESOURCE_PREFETCH) - pci_setup_bridge_mmio_pref(bus); + pci_setup_bridge_mmio_pref(bridge); pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl); } @@ -649,6 +646,41 @@ void pci_setup_bridge(struct pci_bus *bus) __pci_setup_bridge(bus, type); } + +int pci_claim_bridge_resource(struct pci_dev *bridge, int i) +{ + if (i < PCI_BRIDGE_RESOURCES || i > PCI_BRIDGE_RESOURCE_END) + return 0; + + if (pci_claim_resource(bridge, i) == 0) + return 0; /* claimed the window */ + + if ((bridge->class >> 8) != PCI_CLASS_BRIDGE_PCI) + return 0; + + if (!pci_bus_clip_resource(bridge, i)) + return -EINVAL; /* clipping didn't change anything */ + + switch (i - PCI_BRIDGE_RESOURCES) { + case 0: + pci_setup_bridge_io(bridge); + break; + case 1: + pci_setup_bridge_mmio(bridge); + break; + case 2: + pci_setup_bridge_mmio_pref(bridge); + break; + default: + return -EINVAL; + } + + if (pci_claim_resource(bridge, i) == 0) + return 0; /* claimed a smaller window */ + + return -EINVAL; +} + /* Check whether the bridge supports optional I/O and prefetchable memory ranges. If not, the respective base/limit registers must be read-only and read as 0. */ diff --git a/drivers/phy/phy-miphy28lp.c b/drivers/phy/phy-miphy28lp.c index e34da13885e8..27fa62ce6136 100644 --- a/drivers/phy/phy-miphy28lp.c +++ b/drivers/phy/phy-miphy28lp.c @@ -1050,7 +1050,8 @@ static int miphy28lp_init(struct phy *phy) ret = miphy28lp_init_usb3(miphy_phy); break; default: - return -EINVAL; + ret = -EINVAL; + break; } mutex_unlock(&miphy_dev->miphy_mutex); diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c index 6ab43a814ad2..6c80154e8bff 100644 --- a/drivers/phy/phy-miphy365x.c +++ b/drivers/phy/phy-miphy365x.c @@ -141,7 +141,7 @@ struct miphy365x_phy { bool pcie_tx_pol_inv; bool sata_tx_pol_inv; u32 sata_gen; - u64 ctrlreg; + u32 ctrlreg; u8 type; }; @@ -179,7 +179,7 @@ static int miphy365x_set_path(struct miphy365x_phy *miphy_phy, bool sata = (miphy_phy->type == MIPHY_TYPE_SATA); return regmap_update_bits(miphy_dev->regmap, - (unsigned int)miphy_phy->ctrlreg, + miphy_phy->ctrlreg, SYSCFG_SELECT_SATA_MASK, sata << SYSCFG_SELECT_SATA_POS); } @@ -445,7 +445,6 @@ int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy, { struct device_node *phynode = miphy_phy->phy->dev.of_node; const char *name; - const __be32 *taddr; int type = miphy_phy->type; int ret; @@ -455,22 +454,6 @@ int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy, return ret; } - if (!strncmp(name, "syscfg", 6)) { - taddr = of_get_address(phynode, index, NULL, NULL); - if (!taddr) { - dev_err(dev, "failed to fetch syscfg address\n"); - return -EINVAL; - } - - miphy_phy->ctrlreg = of_translate_address(phynode, taddr); - if (miphy_phy->ctrlreg == OF_BAD_ADDR) { - dev_err(dev, "failed to translate syscfg address\n"); - return -EINVAL; - } - - return 0; - } - if (!((!strncmp(name, "sata", 4) && type == MIPHY_TYPE_SATA) || (!strncmp(name, "pcie", 4) && type == MIPHY_TYPE_PCIE))) return 0; @@ -606,7 +589,15 @@ static int miphy365x_probe(struct platform_device *pdev) return ret; phy_set_drvdata(phy, miphy_dev->phys[port]); + port++; + /* sysconfig offsets are indexed from 1 */ + ret = of_property_read_u32_index(np, "st,syscfg", port, + &miphy_phy->ctrlreg); + if (ret) { + dev_err(&pdev->dev, "No sysconfig offset found\n"); + return ret; + } } provider = devm_of_phy_provider_register(&pdev->dev, miphy365x_xlate); diff --git a/drivers/phy/phy-omap-control.c b/drivers/phy/phy-omap-control.c index c96e8183a8ff..efe724f97e02 100644 --- a/drivers/phy/phy-omap-control.c +++ b/drivers/phy/phy-omap-control.c @@ -29,10 +29,9 @@ /** * omap_control_pcie_pcs - set the PCS delay count * @dev: the control module device - * @id: index of the pcie PHY (should be 1 or 2) * @delay: 8 bit delay value */ -void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay) +void omap_control_pcie_pcs(struct device *dev, u8 delay) { u32 val; struct omap_control_phy *control_phy; @@ -55,8 +54,8 @@ void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay) val = readl(control_phy->pcie_pcs); val &= ~(OMAP_CTRL_PCIE_PCS_MASK << - (id * OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT)); - val |= delay << (id * OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT); + OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT); + val |= (delay << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT); writel(val, control_phy->pcie_pcs); } EXPORT_SYMBOL_GPL(omap_control_pcie_pcs); diff --git a/drivers/phy/phy-stih407-usb.c b/drivers/phy/phy-stih407-usb.c index 74f0fab3cd8a..1d5ae5f8ef69 100644 --- a/drivers/phy/phy-stih407-usb.c +++ b/drivers/phy/phy-stih407-usb.c @@ -22,6 +22,9 @@ #include <linux/mfd/syscon.h> #include <linux/phy/phy.h> +#define PHYPARAM_REG 1 +#define PHYCTRL_REG 2 + /* Default PHY_SEL and REFCLKSEL configuration */ #define STIH407_USB_PICOPHY_CTRL_PORT_CONF 0x6 #define STIH407_USB_PICOPHY_CTRL_PORT_MASK 0x1f @@ -93,7 +96,7 @@ static int stih407_usb2_picophy_probe(struct platform_device *pdev) struct device_node *np = dev->of_node; struct phy_provider *phy_provider; struct phy *phy; - struct resource *res; + int ret; phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL); if (!phy_dev) @@ -123,19 +126,19 @@ static int stih407_usb2_picophy_probe(struct platform_device *pdev) return PTR_ERR(phy_dev->regmap); } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl"); - if (!res) { - dev_err(dev, "No ctrl reg found\n"); - return -ENXIO; + ret = of_property_read_u32_index(np, "st,syscfg", PHYPARAM_REG, + &phy_dev->param); + if (ret) { + dev_err(dev, "can't get phyparam offset (%d)\n", ret); + return ret; } - phy_dev->ctrl = res->start; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "param"); - if (!res) { - dev_err(dev, "No param reg found\n"); - return -ENXIO; + ret = of_property_read_u32_index(np, "st,syscfg", PHYCTRL_REG, + &phy_dev->ctrl); + if (ret) { + dev_err(dev, "can't get phyctrl offset (%d)\n", ret); + return ret; } - phy_dev->param = res->start; phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data); if (IS_ERR(phy)) { diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c index fb02a67c9181..a2b08f3ccb03 100644 --- a/drivers/phy/phy-sun4i-usb.c +++ b/drivers/phy/phy-sun4i-usb.c @@ -244,7 +244,8 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) else data->num_phys = 3; - if (of_device_is_compatible(np, "allwinner,sun4i-a10-usb-phy")) + if (of_device_is_compatible(np, "allwinner,sun4i-a10-usb-phy") || + of_device_is_compatible(np, "allwinner,sun6i-a31-usb-phy")) data->disc_thresh = 3; else data->disc_thresh = 2; diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c index 1387b4d4afe3..465de2c800f2 100644 --- a/drivers/phy/phy-ti-pipe3.c +++ b/drivers/phy/phy-ti-pipe3.c @@ -82,7 +82,6 @@ struct ti_pipe3 { struct clk *refclk; struct clk *div_clk; struct pipe3_dpll_map *dpll_map; - u8 id; }; static struct pipe3_dpll_map dpll_map_usb[] = { @@ -217,8 +216,13 @@ static int ti_pipe3_init(struct phy *x) u32 val; int ret = 0; + /* + * Set pcie_pcs register to 0x96 for proper functioning of phy + * as recommended in AM572x TRM SPRUHZ6, section 18.5.2.2, table + * 18-1804. + */ if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) { - omap_control_pcie_pcs(phy->control_dev, phy->id, 0xF1); + omap_control_pcie_pcs(phy->control_dev, 0x96); return 0; } @@ -347,8 +351,6 @@ static int ti_pipe3_probe(struct platform_device *pdev) } if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) { - if (of_property_read_u8(node, "id", &phy->id) < 0) - phy->id = 1; clk = devm_clk_get(phy->dev, "dpll_ref"); if (IS_ERR(clk)) { diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index e4f65510c87e..89dca77ca038 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -1801,14 +1801,15 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev) if (pctldev == NULL) return; - mutex_lock(&pinctrldev_list_mutex); mutex_lock(&pctldev->mutex); - pinctrl_remove_device_debugfs(pctldev); + mutex_unlock(&pctldev->mutex); if (!IS_ERR(pctldev->p)) pinctrl_put(pctldev->p); + mutex_lock(&pinctrldev_list_mutex); + mutex_lock(&pctldev->mutex); /* TODO: check that no pinmuxes are still active? */ list_del(&pctldev->node); /* Destroy descriptor tree */ diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index dfd021e8268f..f4cd0b9b2438 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -177,7 +177,7 @@ struct at91_pinctrl { struct device *dev; struct pinctrl_dev *pctl; - int nbanks; + int nactive_banks; uint32_t *mux_mask; int nmux; @@ -653,12 +653,18 @@ static int pin_check_config(struct at91_pinctrl *info, const char *name, int mux; /* check if it's a valid config */ - if (pin->bank >= info->nbanks) { + if (pin->bank >= gpio_banks) { dev_err(info->dev, "%s: pin conf %d bank_id %d >= nbanks %d\n", - name, index, pin->bank, info->nbanks); + name, index, pin->bank, gpio_banks); return -EINVAL; } + if (!gpio_chips[pin->bank]) { + dev_err(info->dev, "%s: pin conf %d bank_id %d not enabled\n", + name, index, pin->bank); + return -ENXIO; + } + if (pin->pin >= MAX_NB_GPIO_PER_BANK) { dev_err(info->dev, "%s: pin conf %d pin_bank_id %d >= %d\n", name, index, pin->pin, MAX_NB_GPIO_PER_BANK); @@ -981,7 +987,8 @@ static void at91_pinctrl_child_count(struct at91_pinctrl *info, for_each_child_of_node(np, child) { if (of_device_is_compatible(child, gpio_compat)) { - info->nbanks++; + if (of_device_is_available(child)) + info->nactive_banks++; } else { info->nfunctions++; info->ngroups += of_get_child_count(child); @@ -1003,11 +1010,11 @@ static int at91_pinctrl_mux_mask(struct at91_pinctrl *info, } size /= sizeof(*list); - if (!size || size % info->nbanks) { - dev_err(info->dev, "wrong mux mask array should be by %d\n", info->nbanks); + if (!size || size % gpio_banks) { + dev_err(info->dev, "wrong mux mask array should be by %d\n", gpio_banks); return -EINVAL; } - info->nmux = size / info->nbanks; + info->nmux = size / gpio_banks; info->mux_mask = devm_kzalloc(info->dev, sizeof(u32) * size, GFP_KERNEL); if (!info->mux_mask) { @@ -1131,7 +1138,7 @@ static int at91_pinctrl_probe_dt(struct platform_device *pdev, of_match_device(at91_pinctrl_of_match, &pdev->dev)->data; at91_pinctrl_child_count(info, np); - if (info->nbanks < 1) { + if (gpio_banks < 1) { dev_err(&pdev->dev, "you need to specify at least one gpio-controller\n"); return -EINVAL; } @@ -1144,7 +1151,7 @@ static int at91_pinctrl_probe_dt(struct platform_device *pdev, dev_dbg(&pdev->dev, "mux-mask\n"); tmp = info->mux_mask; - for (i = 0; i < info->nbanks; i++) { + for (i = 0; i < gpio_banks; i++) { for (j = 0; j < info->nmux; j++, tmp++) { dev_dbg(&pdev->dev, "%d:%d\t0x%x\n", i, j, tmp[0]); } @@ -1162,7 +1169,7 @@ static int at91_pinctrl_probe_dt(struct platform_device *pdev, if (!info->groups) return -ENOMEM; - dev_dbg(&pdev->dev, "nbanks = %d\n", info->nbanks); + dev_dbg(&pdev->dev, "nbanks = %d\n", gpio_banks); dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions); dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups); @@ -1185,7 +1192,7 @@ static int at91_pinctrl_probe(struct platform_device *pdev) { struct at91_pinctrl *info; struct pinctrl_pin_desc *pdesc; - int ret, i, j, k; + int ret, i, j, k, ngpio_chips_enabled = 0; info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) @@ -1200,23 +1207,27 @@ static int at91_pinctrl_probe(struct platform_device *pdev) * to obtain references to the struct gpio_chip * for them, and we * need this to proceed. */ - for (i = 0; i < info->nbanks; i++) { - if (!gpio_chips[i]) { - dev_warn(&pdev->dev, "GPIO chip %d not registered yet\n", i); - devm_kfree(&pdev->dev, info); - return -EPROBE_DEFER; - } + for (i = 0; i < gpio_banks; i++) + if (gpio_chips[i]) + ngpio_chips_enabled++; + + if (ngpio_chips_enabled < info->nactive_banks) { + dev_warn(&pdev->dev, + "All GPIO chips are not registered yet (%d/%d)\n", + ngpio_chips_enabled, info->nactive_banks); + devm_kfree(&pdev->dev, info); + return -EPROBE_DEFER; } at91_pinctrl_desc.name = dev_name(&pdev->dev); - at91_pinctrl_desc.npins = info->nbanks * MAX_NB_GPIO_PER_BANK; + at91_pinctrl_desc.npins = gpio_banks * MAX_NB_GPIO_PER_BANK; at91_pinctrl_desc.pins = pdesc = devm_kzalloc(&pdev->dev, sizeof(*pdesc) * at91_pinctrl_desc.npins, GFP_KERNEL); if (!at91_pinctrl_desc.pins) return -ENOMEM; - for (i = 0 , k = 0; i < info->nbanks; i++) { + for (i = 0, k = 0; i < gpio_banks; i++) { for (j = 0; j < MAX_NB_GPIO_PER_BANK; j++, k++) { pdesc->number = k; pdesc->name = kasprintf(GFP_KERNEL, "pio%c%d", i + 'A', j); @@ -1234,8 +1245,9 @@ static int at91_pinctrl_probe(struct platform_device *pdev) } /* We will handle a range of GPIO pins */ - for (i = 0; i < info->nbanks; i++) - pinctrl_add_gpio_range(info->pctl, &gpio_chips[i]->range); + for (i = 0; i < gpio_banks; i++) + if (gpio_chips[i]) + pinctrl_add_gpio_range(info->pctl, &gpio_chips[i]->range); dev_info(&pdev->dev, "initialized AT91 pinctrl driver\n"); @@ -1613,9 +1625,10 @@ static void gpio_irq_handler(unsigned irq, struct irq_desc *desc) static int at91_gpio_of_irq_setup(struct platform_device *pdev, struct at91_gpio_chip *at91_gpio) { + struct gpio_chip *gpiochip_prev = NULL; struct at91_gpio_chip *prev = NULL; struct irq_data *d = irq_get_irq_data(at91_gpio->pioc_virq); - int ret; + int ret, i; at91_gpio->pioc_hwirq = irqd_to_hwirq(d); @@ -1641,24 +1654,33 @@ static int at91_gpio_of_irq_setup(struct platform_device *pdev, return ret; } - /* Setup chained handler */ - if (at91_gpio->pioc_idx) - prev = gpio_chips[at91_gpio->pioc_idx - 1]; - /* The top level handler handles one bank of GPIOs, except * on some SoC it can handle up to three... * We only set up the handler for the first of the list. */ - if (prev && prev->next == at91_gpio) + gpiochip_prev = irq_get_handler_data(at91_gpio->pioc_virq); + if (!gpiochip_prev) { + /* Then register the chain on the parent IRQ */ + gpiochip_set_chained_irqchip(&at91_gpio->chip, + &gpio_irqchip, + at91_gpio->pioc_virq, + gpio_irq_handler); return 0; + } - /* Then register the chain on the parent IRQ */ - gpiochip_set_chained_irqchip(&at91_gpio->chip, - &gpio_irqchip, - at91_gpio->pioc_virq, - gpio_irq_handler); + prev = container_of(gpiochip_prev, struct at91_gpio_chip, chip); - return 0; + /* we can only have 2 banks before */ + for (i = 0; i < 2; i++) { + if (prev->next) { + prev = prev->next; + } else { + prev->next = at91_gpio; + return 0; + } + } + + return -EINVAL; } /* This structure is replicated for each GPIO block allocated at probe time */ @@ -1675,24 +1697,6 @@ static struct gpio_chip at91_gpio_template = { .ngpio = MAX_NB_GPIO_PER_BANK, }; -static void at91_gpio_probe_fixup(void) -{ - unsigned i; - struct at91_gpio_chip *at91_gpio, *last = NULL; - - for (i = 0; i < gpio_banks; i++) { - at91_gpio = gpio_chips[i]; - - /* - * GPIO controller are grouped on some SoC: - * PIOC, PIOD and PIOE can share the same IRQ line - */ - if (last && last->pioc_virq == at91_gpio->pioc_virq) - last->next = at91_gpio; - last = at91_gpio; - } -} - static struct of_device_id at91_gpio_of_match[] = { { .compatible = "atmel,at91sam9x5-gpio", .data = &at91sam9x5_ops, }, { .compatible = "atmel,at91rm9200-gpio", .data = &at91rm9200_ops }, @@ -1805,8 +1809,6 @@ static int at91_gpio_probe(struct platform_device *pdev) gpio_chips[alias_idx] = at91_chip; gpio_banks = max(gpio_banks, alias_idx + 1); - at91_gpio_probe_fixup(); - ret = at91_gpio_of_irq_setup(pdev, at91_chip); if (ret) goto irq_setup_err; diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c index ba74f0aa60c7..43eacc924b7e 100644 --- a/drivers/pinctrl/pinctrl-rockchip.c +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -89,6 +89,7 @@ struct rockchip_iomux { * @reg_pull: optional separate register for additional pull settings * @clk: clock of the gpio bank * @irq: interrupt of the gpio bank + * @saved_enables: Saved content of GPIO_INTEN at suspend time. * @pin_base: first pin number * @nr_pins: number of pins in this bank * @name: name of the bank @@ -107,6 +108,7 @@ struct rockchip_pin_bank { struct regmap *regmap_pull; struct clk *clk; int irq; + u32 saved_enables; u32 pin_base; u8 nr_pins; char *name; @@ -1396,10 +1398,7 @@ static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc) { struct irq_chip *chip = irq_get_chip(irq); struct rockchip_pin_bank *bank = irq_get_handler_data(irq); - u32 polarity = 0, data = 0; u32 pend; - bool edge_changed = false; - unsigned long flags; dev_dbg(bank->drvdata->dev, "got irq for bank %s\n", bank->name); @@ -1407,12 +1406,6 @@ static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc) pend = readl_relaxed(bank->reg_base + GPIO_INT_STATUS); - if (bank->toggle_edge_mode) { - polarity = readl_relaxed(bank->reg_base + - GPIO_INT_POLARITY); - data = readl_relaxed(bank->reg_base + GPIO_EXT_PORT); - } - while (pend) { unsigned int virq; @@ -1432,27 +1425,31 @@ static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc) * needs manual intervention. */ if (bank->toggle_edge_mode & BIT(irq)) { - if (data & BIT(irq)) - polarity &= ~BIT(irq); - else - polarity |= BIT(irq); + u32 data, data_old, polarity; + unsigned long flags; - edge_changed = true; - } + data = readl_relaxed(bank->reg_base + GPIO_EXT_PORT); + do { + spin_lock_irqsave(&bank->slock, flags); - generic_handle_irq(virq); - } + polarity = readl_relaxed(bank->reg_base + + GPIO_INT_POLARITY); + if (data & BIT(irq)) + polarity &= ~BIT(irq); + else + polarity |= BIT(irq); + writel(polarity, + bank->reg_base + GPIO_INT_POLARITY); - if (bank->toggle_edge_mode && edge_changed) { - /* Interrupt params should only be set with ints disabled */ - spin_lock_irqsave(&bank->slock, flags); + spin_unlock_irqrestore(&bank->slock, flags); - data = readl_relaxed(bank->reg_base + GPIO_INTEN); - writel_relaxed(0, bank->reg_base + GPIO_INTEN); - writel(polarity, bank->reg_base + GPIO_INT_POLARITY); - writel(data, bank->reg_base + GPIO_INTEN); + data_old = data; + data = readl_relaxed(bank->reg_base + + GPIO_EXT_PORT); + } while ((data & BIT(irq)) != (data_old & BIT(irq))); + } - spin_unlock_irqrestore(&bank->slock, flags); + generic_handle_irq(virq); } chained_irq_exit(chip, desc); @@ -1543,6 +1540,51 @@ static int rockchip_irq_set_type(struct irq_data *d, unsigned int type) return 0; } +static void rockchip_irq_suspend(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct rockchip_pin_bank *bank = gc->private; + + bank->saved_enables = irq_reg_readl(gc, GPIO_INTEN); + irq_reg_writel(gc, gc->wake_active, GPIO_INTEN); +} + +static void rockchip_irq_resume(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct rockchip_pin_bank *bank = gc->private; + + irq_reg_writel(gc, bank->saved_enables, GPIO_INTEN); +} + +static void rockchip_irq_disable(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + u32 val; + + irq_gc_lock(gc); + + val = irq_reg_readl(gc, GPIO_INTEN); + val &= ~d->mask; + irq_reg_writel(gc, val, GPIO_INTEN); + + irq_gc_unlock(gc); +} + +static void rockchip_irq_enable(struct irq_data *d) +{ + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + u32 val; + + irq_gc_lock(gc); + + val = irq_reg_readl(gc, GPIO_INTEN); + val |= d->mask; + irq_reg_writel(gc, val, GPIO_INTEN); + + irq_gc_unlock(gc); +} + static int rockchip_interrupts_register(struct platform_device *pdev, struct rockchip_pinctrl *info) { @@ -1581,12 +1623,16 @@ static int rockchip_interrupts_register(struct platform_device *pdev, gc = irq_get_domain_generic_chip(bank->domain, 0); gc->reg_base = bank->reg_base; gc->private = bank; - gc->chip_types[0].regs.mask = GPIO_INTEN; + gc->chip_types[0].regs.mask = GPIO_INTMASK; gc->chip_types[0].regs.ack = GPIO_PORTS_EOI; gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit; - gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; - gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; + gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit; + gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit; + gc->chip_types[0].chip.irq_enable = rockchip_irq_enable; + gc->chip_types[0].chip.irq_disable = rockchip_irq_disable; gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake; + gc->chip_types[0].chip.irq_suspend = rockchip_irq_suspend; + gc->chip_types[0].chip.irq_resume = rockchip_irq_resume; gc->chip_types[0].chip.irq_set_type = rockchip_irq_set_type; gc->wake_enabled = IRQ_MSK(bank->nr_pins); diff --git a/drivers/pinctrl/pinctrl-st.c b/drivers/pinctrl/pinctrl-st.c index 7c9d51382248..9e5ec00084bb 100644 --- a/drivers/pinctrl/pinctrl-st.c +++ b/drivers/pinctrl/pinctrl-st.c @@ -1012,8 +1012,10 @@ static void st_pinconf_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned pin_id) { unsigned long config; - st_pinconf_get(pctldev, pin_id, &config); + mutex_unlock(&pctldev->mutex); + st_pinconf_get(pctldev, pin_id, &config); + mutex_lock(&pctldev->mutex); seq_printf(s, "[OE:%ld,PU:%ld,OD:%ld]\n" "\t\t[retime:%ld,invclk:%ld,clknotdat:%ld," "de:%ld,rt-clk:%ld,rt-delay:%ld]", @@ -1443,6 +1445,7 @@ static struct gpio_chip st_gpio_template = { static struct irq_chip st_gpio_irqchip = { .name = "GPIO", + .irq_disable = st_gpio_irq_mask, .irq_mask = st_gpio_irq_mask, .irq_unmask = st_gpio_irq_unmask, .irq_set_type = st_gpio_irq_set_type, diff --git a/drivers/pinctrl/pinctrl-xway.c b/drivers/pinctrl/pinctrl-xway.c index c5cef59f5965..779950c62e53 100644 --- a/drivers/pinctrl/pinctrl-xway.c +++ b/drivers/pinctrl/pinctrl-xway.c @@ -798,10 +798,8 @@ static int pinmux_xway_probe(struct platform_device *pdev) /* load the gpio chip */ xway_chip.dev = &pdev->dev; - of_gpiochip_add(&xway_chip); ret = gpiochip_add(&xway_chip); if (ret) { - of_gpiochip_remove(&xway_chip); dev_err(&pdev->dev, "Failed to register gpio chip\n"); return ret; } diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index e730935fa457..ed7017df065d 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -865,10 +865,10 @@ static int msm_ps_hold_restart(struct notifier_block *nb, unsigned long action, static void msm_pinctrl_setup_pm_reset(struct msm_pinctrl *pctrl) { - int i = 0; + int i; const struct msm_function *func = pctrl->soc->functions; - for (; i <= pctrl->soc->nfunctions; i++) + for (i = 0; i < pctrl->soc->nfunctions; i++) if (!strcmp(func[i].name, "ps_hold")) { pctrl->restart_nb.notifier_call = msm_ps_hold_restart; pctrl->restart_nb.priority = 128; diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 9411eae39a4e..3d21efe11d7b 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -2,11 +2,9 @@ * Driver for Dell laptop extras * * Copyright (c) Red Hat <mjg@redhat.com> - * Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com> - * Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com> * - * Based on documentation in the libsmbios package: - * Copyright (C) 2005-2014 Dell Inc. + * Based on documentation in the libsmbios package, Copyright (C) 2005 Dell + * 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 @@ -34,13 +32,6 @@ #include "../../firmware/dcdbas.h" #define BRIGHTNESS_TOKEN 0x7d -#define KBD_LED_OFF_TOKEN 0x01E1 -#define KBD_LED_ON_TOKEN 0x01E2 -#define KBD_LED_AUTO_TOKEN 0x01E3 -#define KBD_LED_AUTO_25_TOKEN 0x02EA -#define KBD_LED_AUTO_50_TOKEN 0x02EB -#define KBD_LED_AUTO_75_TOKEN 0x02EC -#define KBD_LED_AUTO_100_TOKEN 0x02F6 /* This structure will be modified by the firmware when we enter * system management mode, hence the volatiles */ @@ -71,13 +62,6 @@ struct calling_interface_structure { struct quirk_entry { u8 touchpad_led; - - int needs_kbd_timeouts; - /* - * Ordered list of timeouts expressed in seconds. - * The list must end with -1 - */ - int kbd_timeouts[]; }; static struct quirk_entry *quirks; @@ -92,15 +76,6 @@ static int __init dmi_matched(const struct dmi_system_id *dmi) return 1; } -/* - * These values come from Windows utility provided by Dell. If any other value - * is used then BIOS silently set timeout to 0 without any error message. - */ -static struct quirk_entry quirk_dell_xps13_9333 = { - .needs_kbd_timeouts = 1, - .kbd_timeouts = { 0, 5, 15, 60, 5 * 60, 15 * 60, -1 }, -}; - static int da_command_address; static int da_command_code; static int da_num_tokens; @@ -292,15 +267,6 @@ static const struct dmi_system_id dell_quirks[] __initconst = { }, .driver_data = &quirk_dell_vostro_v130, }, - { - .callback = dmi_matched, - .ident = "Dell XPS13 9333", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "XPS13 9333"), - }, - .driver_data = &quirk_dell_xps13_9333, - }, { } }; @@ -365,29 +331,17 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy) } } -static int find_token_id(int tokenid) +static int find_token_location(int tokenid) { int i; - for (i = 0; i < da_num_tokens; i++) { if (da_tokens[i].tokenID == tokenid) - return i; + return da_tokens[i].location; } return -1; } -static int find_token_location(int tokenid) -{ - int id; - - id = find_token_id(tokenid); - if (id == -1) - return -1; - - return da_tokens[id].location; -} - static struct calling_interface_buffer * dell_send_request(struct calling_interface_buffer *buffer, int class, int select) @@ -408,20 +362,6 @@ dell_send_request(struct calling_interface_buffer *buffer, int class, return buffer; } -static inline int dell_smi_error(int value) -{ - switch (value) { - case 0: /* Completed successfully */ - return 0; - case -1: /* Completed with error */ - return -EIO; - case -2: /* Function not supported */ - return -ENXIO; - default: /* Unknown error */ - return -EINVAL; - } -} - /* Derived from information in DellWirelessCtl.cpp: Class 17, select 11 is radio control. It returns an array of 32-bit values. @@ -776,7 +716,7 @@ static int dell_send_intensity(struct backlight_device *bd) else dell_send_request(buffer, 1, 1); - out: +out: release_buffer(); return ret; } @@ -800,7 +740,7 @@ static int dell_get_intensity(struct backlight_device *bd) ret = buffer->output[1]; - out: +out: release_buffer(); return ret; } @@ -849,984 +789,6 @@ static void touchpad_led_exit(void) led_classdev_unregister(&touchpad_led); } -/* - * Derived from information in smbios-keyboard-ctl: - * - * cbClass 4 - * cbSelect 11 - * Keyboard illumination - * cbArg1 determines the function to be performed - * - * cbArg1 0x0 = Get Feature Information - * cbRES1 Standard return codes (0, -1, -2) - * cbRES2, word0 Bitmap of user-selectable modes - * bit 0 Always off (All systems) - * bit 1 Always on (Travis ATG, Siberia) - * bit 2 Auto: ALS-based On; ALS-based Off (Travis ATG) - * bit 3 Auto: ALS- and input-activity-based On; input-activity based Off - * bit 4 Auto: Input-activity-based On; input-activity based Off - * bit 5 Auto: Input-activity-based On (illumination level 25%); input-activity based Off - * bit 6 Auto: Input-activity-based On (illumination level 50%); input-activity based Off - * bit 7 Auto: Input-activity-based On (illumination level 75%); input-activity based Off - * bit 8 Auto: Input-activity-based On (illumination level 100%); input-activity based Off - * bits 9-15 Reserved for future use - * cbRES2, byte2 Reserved for future use - * cbRES2, byte3 Keyboard illumination type - * 0 Reserved - * 1 Tasklight - * 2 Backlight - * 3-255 Reserved for future use - * cbRES3, byte0 Supported auto keyboard illumination trigger bitmap. - * bit 0 Any keystroke - * bit 1 Touchpad activity - * bit 2 Pointing stick - * bit 3 Any mouse - * bits 4-7 Reserved for future use - * cbRES3, byte1 Supported timeout unit bitmap - * bit 0 Seconds - * bit 1 Minutes - * bit 2 Hours - * bit 3 Days - * bits 4-7 Reserved for future use - * cbRES3, byte2 Number of keyboard light brightness levels - * cbRES4, byte0 Maximum acceptable seconds value (0 if seconds not supported). - * cbRES4, byte1 Maximum acceptable minutes value (0 if minutes not supported). - * cbRES4, byte2 Maximum acceptable hours value (0 if hours not supported). - * cbRES4, byte3 Maximum acceptable days value (0 if days not supported) - * - * cbArg1 0x1 = Get Current State - * cbRES1 Standard return codes (0, -1, -2) - * cbRES2, word0 Bitmap of current mode state - * bit 0 Always off (All systems) - * bit 1 Always on (Travis ATG, Siberia) - * bit 2 Auto: ALS-based On; ALS-based Off (Travis ATG) - * bit 3 Auto: ALS- and input-activity-based On; input-activity based Off - * bit 4 Auto: Input-activity-based On; input-activity based Off - * bit 5 Auto: Input-activity-based On (illumination level 25%); input-activity based Off - * bit 6 Auto: Input-activity-based On (illumination level 50%); input-activity based Off - * bit 7 Auto: Input-activity-based On (illumination level 75%); input-activity based Off - * bit 8 Auto: Input-activity-based On (illumination level 100%); input-activity based Off - * bits 9-15 Reserved for future use - * Note: Only One bit can be set - * cbRES2, byte2 Currently active auto keyboard illumination triggers. - * bit 0 Any keystroke - * bit 1 Touchpad activity - * bit 2 Pointing stick - * bit 3 Any mouse - * bits 4-7 Reserved for future use - * cbRES2, byte3 Current Timeout - * bits 7:6 Timeout units indicator: - * 00b Seconds - * 01b Minutes - * 10b Hours - * 11b Days - * bits 5:0 Timeout value (0-63) in sec/min/hr/day - * NOTE: A value of 0 means always on (no timeout) if any bits of RES3 byte - * are set upon return from the [Get feature information] call. - * cbRES3, byte0 Current setting of ALS value that turns the light on or off. - * cbRES3, byte1 Current ALS reading - * cbRES3, byte2 Current keyboard light level. - * - * cbArg1 0x2 = Set New State - * cbRES1 Standard return codes (0, -1, -2) - * cbArg2, word0 Bitmap of current mode state - * bit 0 Always off (All systems) - * bit 1 Always on (Travis ATG, Siberia) - * bit 2 Auto: ALS-based On; ALS-based Off (Travis ATG) - * bit 3 Auto: ALS- and input-activity-based On; input-activity based Off - * bit 4 Auto: Input-activity-based On; input-activity based Off - * bit 5 Auto: Input-activity-based On (illumination level 25%); input-activity based Off - * bit 6 Auto: Input-activity-based On (illumination level 50%); input-activity based Off - * bit 7 Auto: Input-activity-based On (illumination level 75%); input-activity based Off - * bit 8 Auto: Input-activity-based On (illumination level 100%); input-activity based Off - * bits 9-15 Reserved for future use - * Note: Only One bit can be set - * cbArg2, byte2 Desired auto keyboard illumination triggers. Must remain inactive to allow - * keyboard to turn off automatically. - * bit 0 Any keystroke - * bit 1 Touchpad activity - * bit 2 Pointing stick - * bit 3 Any mouse - * bits 4-7 Reserved for future use - * cbArg2, byte3 Desired Timeout - * bits 7:6 Timeout units indicator: - * 00b Seconds - * 01b Minutes - * 10b Hours - * 11b Days - * bits 5:0 Timeout value (0-63) in sec/min/hr/day - * cbArg3, byte0 Desired setting of ALS value that turns the light on or off. - * cbArg3, byte2 Desired keyboard light level. - */ - - -enum kbd_timeout_unit { - KBD_TIMEOUT_SECONDS = 0, - KBD_TIMEOUT_MINUTES, - KBD_TIMEOUT_HOURS, - KBD_TIMEOUT_DAYS, -}; - -enum kbd_mode_bit { - KBD_MODE_BIT_OFF = 0, - KBD_MODE_BIT_ON, - KBD_MODE_BIT_ALS, - KBD_MODE_BIT_TRIGGER_ALS, - KBD_MODE_BIT_TRIGGER, - KBD_MODE_BIT_TRIGGER_25, - KBD_MODE_BIT_TRIGGER_50, - KBD_MODE_BIT_TRIGGER_75, - KBD_MODE_BIT_TRIGGER_100, -}; - -#define kbd_is_als_mode_bit(bit) \ - ((bit) == KBD_MODE_BIT_ALS || (bit) == KBD_MODE_BIT_TRIGGER_ALS) -#define kbd_is_trigger_mode_bit(bit) \ - ((bit) >= KBD_MODE_BIT_TRIGGER_ALS && (bit) <= KBD_MODE_BIT_TRIGGER_100) -#define kbd_is_level_mode_bit(bit) \ - ((bit) >= KBD_MODE_BIT_TRIGGER_25 && (bit) <= KBD_MODE_BIT_TRIGGER_100) - -struct kbd_info { - u16 modes; - u8 type; - u8 triggers; - u8 levels; - u8 seconds; - u8 minutes; - u8 hours; - u8 days; -}; - -struct kbd_state { - u8 mode_bit; - u8 triggers; - u8 timeout_value; - u8 timeout_unit; - u8 als_setting; - u8 als_value; - u8 level; -}; - -static const int kbd_tokens[] = { - KBD_LED_OFF_TOKEN, - KBD_LED_AUTO_25_TOKEN, - KBD_LED_AUTO_50_TOKEN, - KBD_LED_AUTO_75_TOKEN, - KBD_LED_AUTO_100_TOKEN, - KBD_LED_ON_TOKEN, -}; - -static u16 kbd_token_bits; - -static struct kbd_info kbd_info; -static bool kbd_als_supported; -static bool kbd_triggers_supported; - -static u8 kbd_mode_levels[16]; -static int kbd_mode_levels_count; - -static u8 kbd_previous_level; -static u8 kbd_previous_mode_bit; - -static bool kbd_led_present; - -/* - * NOTE: there are three ways to set the keyboard backlight level. - * First, via kbd_state.mode_bit (assigning KBD_MODE_BIT_TRIGGER_* value). - * Second, via kbd_state.level (assigning numerical value <= kbd_info.levels). - * Third, via SMBIOS tokens (KBD_LED_* in kbd_tokens) - * - * There are laptops which support only one of these methods. If we want to - * support as many machines as possible we need to implement all three methods. - * The first two methods use the kbd_state structure. The third uses SMBIOS - * tokens. If kbd_info.levels == 0, the machine does not support setting the - * keyboard backlight level via kbd_state.level. - */ - -static int kbd_get_info(struct kbd_info *info) -{ - u8 units; - int ret; - - get_buffer(); - - buffer->input[0] = 0x0; - dell_send_request(buffer, 4, 11); - ret = buffer->output[0]; - - if (ret) { - ret = dell_smi_error(ret); - goto out; - } - - info->modes = buffer->output[1] & 0xFFFF; - info->type = (buffer->output[1] >> 24) & 0xFF; - info->triggers = buffer->output[2] & 0xFF; - units = (buffer->output[2] >> 8) & 0xFF; - info->levels = (buffer->output[2] >> 16) & 0xFF; - - if (units & BIT(0)) - info->seconds = (buffer->output[3] >> 0) & 0xFF; - if (units & BIT(1)) - info->minutes = (buffer->output[3] >> 8) & 0xFF; - if (units & BIT(2)) - info->hours = (buffer->output[3] >> 16) & 0xFF; - if (units & BIT(3)) - info->days = (buffer->output[3] >> 24) & 0xFF; - - out: - release_buffer(); - return ret; -} - -static unsigned int kbd_get_max_level(void) -{ - if (kbd_info.levels != 0) - return kbd_info.levels; - if (kbd_mode_levels_count > 0) - return kbd_mode_levels_count - 1; - return 0; -} - -static int kbd_get_level(struct kbd_state *state) -{ - int i; - - if (kbd_info.levels != 0) - return state->level; - - if (kbd_mode_levels_count > 0) { - for (i = 0; i < kbd_mode_levels_count; ++i) - if (kbd_mode_levels[i] == state->mode_bit) - return i; - return 0; - } - - return -EINVAL; -} - -static int kbd_set_level(struct kbd_state *state, u8 level) -{ - if (kbd_info.levels != 0) { - if (level != 0) - kbd_previous_level = level; - if (state->level == level) - return 0; - state->level = level; - if (level != 0 && state->mode_bit == KBD_MODE_BIT_OFF) - state->mode_bit = kbd_previous_mode_bit; - else if (level == 0 && state->mode_bit != KBD_MODE_BIT_OFF) { - kbd_previous_mode_bit = state->mode_bit; - state->mode_bit = KBD_MODE_BIT_OFF; - } - return 0; - } - - if (kbd_mode_levels_count > 0 && level < kbd_mode_levels_count) { - if (level != 0) - kbd_previous_level = level; - state->mode_bit = kbd_mode_levels[level]; - return 0; - } - - return -EINVAL; -} - -static int kbd_get_state(struct kbd_state *state) -{ - int ret; - - get_buffer(); - - buffer->input[0] = 0x1; - dell_send_request(buffer, 4, 11); - ret = buffer->output[0]; - - if (ret) { - ret = dell_smi_error(ret); - goto out; - } - - state->mode_bit = ffs(buffer->output[1] & 0xFFFF); - if (state->mode_bit != 0) - state->mode_bit--; - - state->triggers = (buffer->output[1] >> 16) & 0xFF; - state->timeout_value = (buffer->output[1] >> 24) & 0x3F; - state->timeout_unit = (buffer->output[1] >> 30) & 0x3; - state->als_setting = buffer->output[2] & 0xFF; - state->als_value = (buffer->output[2] >> 8) & 0xFF; - state->level = (buffer->output[2] >> 16) & 0xFF; - - out: - release_buffer(); - return ret; -} - -static int kbd_set_state(struct kbd_state *state) -{ - int ret; - - get_buffer(); - buffer->input[0] = 0x2; - buffer->input[1] = BIT(state->mode_bit) & 0xFFFF; - buffer->input[1] |= (state->triggers & 0xFF) << 16; - buffer->input[1] |= (state->timeout_value & 0x3F) << 24; - buffer->input[1] |= (state->timeout_unit & 0x3) << 30; - buffer->input[2] = state->als_setting & 0xFF; - buffer->input[2] |= (state->level & 0xFF) << 16; - dell_send_request(buffer, 4, 11); - ret = buffer->output[0]; - release_buffer(); - - return dell_smi_error(ret); -} - -static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old) -{ - int ret; - - ret = kbd_set_state(state); - if (ret == 0) - return 0; - - /* - * When setting the new state fails,try to restore the previous one. - * This is needed on some machines where BIOS sets a default state when - * setting a new state fails. This default state could be all off. - */ - - if (kbd_set_state(old)) - pr_err("Setting old previous keyboard state failed\n"); - - return ret; -} - -static int kbd_set_token_bit(u8 bit) -{ - int id; - int ret; - - if (bit >= ARRAY_SIZE(kbd_tokens)) - return -EINVAL; - - id = find_token_id(kbd_tokens[bit]); - if (id == -1) - return -EINVAL; - - get_buffer(); - buffer->input[0] = da_tokens[id].location; - buffer->input[1] = da_tokens[id].value; - dell_send_request(buffer, 1, 0); - ret = buffer->output[0]; - release_buffer(); - - return dell_smi_error(ret); -} - -static int kbd_get_token_bit(u8 bit) -{ - int id; - int ret; - int val; - - if (bit >= ARRAY_SIZE(kbd_tokens)) - return -EINVAL; - - id = find_token_id(kbd_tokens[bit]); - if (id == -1) - return -EINVAL; - - get_buffer(); - buffer->input[0] = da_tokens[id].location; - dell_send_request(buffer, 0, 0); - ret = buffer->output[0]; - val = buffer->output[1]; - release_buffer(); - - if (ret) - return dell_smi_error(ret); - - return (val == da_tokens[id].value); -} - -static int kbd_get_first_active_token_bit(void) -{ - int i; - int ret; - - for (i = 0; i < ARRAY_SIZE(kbd_tokens); ++i) { - ret = kbd_get_token_bit(i); - if (ret == 1) - return i; - } - - return ret; -} - -static int kbd_get_valid_token_counts(void) -{ - return hweight16(kbd_token_bits); -} - -static inline int kbd_init_info(void) -{ - struct kbd_state state; - int ret; - int i; - - ret = kbd_get_info(&kbd_info); - if (ret) - return ret; - - kbd_get_state(&state); - - /* NOTE: timeout value is stored in 6 bits so max value is 63 */ - if (kbd_info.seconds > 63) - kbd_info.seconds = 63; - if (kbd_info.minutes > 63) - kbd_info.minutes = 63; - if (kbd_info.hours > 63) - kbd_info.hours = 63; - if (kbd_info.days > 63) - kbd_info.days = 63; - - /* NOTE: On tested machines ON mode did not work and caused - * problems (turned backlight off) so do not use it - */ - kbd_info.modes &= ~BIT(KBD_MODE_BIT_ON); - - kbd_previous_level = kbd_get_level(&state); - kbd_previous_mode_bit = state.mode_bit; - - if (kbd_previous_level == 0 && kbd_get_max_level() != 0) - kbd_previous_level = 1; - - if (kbd_previous_mode_bit == KBD_MODE_BIT_OFF) { - kbd_previous_mode_bit = - ffs(kbd_info.modes & ~BIT(KBD_MODE_BIT_OFF)); - if (kbd_previous_mode_bit != 0) - kbd_previous_mode_bit--; - } - - if (kbd_info.modes & (BIT(KBD_MODE_BIT_ALS) | - BIT(KBD_MODE_BIT_TRIGGER_ALS))) - kbd_als_supported = true; - - if (kbd_info.modes & ( - BIT(KBD_MODE_BIT_TRIGGER_ALS) | BIT(KBD_MODE_BIT_TRIGGER) | - BIT(KBD_MODE_BIT_TRIGGER_25) | BIT(KBD_MODE_BIT_TRIGGER_50) | - BIT(KBD_MODE_BIT_TRIGGER_75) | BIT(KBD_MODE_BIT_TRIGGER_100) - )) - kbd_triggers_supported = true; - - /* kbd_mode_levels[0] is reserved, see below */ - for (i = 0; i < 16; ++i) - if (kbd_is_level_mode_bit(i) && (BIT(i) & kbd_info.modes)) - kbd_mode_levels[1 + kbd_mode_levels_count++] = i; - - /* - * Find the first supported mode and assign to kbd_mode_levels[0]. - * This should be 0 (off), but we cannot depend on the BIOS to - * support 0. - */ - if (kbd_mode_levels_count > 0) { - for (i = 0; i < 16; ++i) { - if (BIT(i) & kbd_info.modes) { - kbd_mode_levels[0] = i; - break; - } - } - kbd_mode_levels_count++; - } - - return 0; - -} - -static inline void kbd_init_tokens(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(kbd_tokens); ++i) - if (find_token_id(kbd_tokens[i]) != -1) - kbd_token_bits |= BIT(i); -} - -static void kbd_init(void) -{ - int ret; - - ret = kbd_init_info(); - kbd_init_tokens(); - - if (kbd_token_bits != 0 || ret == 0) - kbd_led_present = true; -} - -static ssize_t kbd_led_timeout_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct kbd_state new_state; - struct kbd_state state; - bool convert; - int value; - int ret; - char ch; - u8 unit; - int i; - - ret = sscanf(buf, "%d %c", &value, &ch); - if (ret < 1) - return -EINVAL; - else if (ret == 1) - ch = 's'; - - if (value < 0) - return -EINVAL; - - convert = false; - - switch (ch) { - case 's': - if (value > kbd_info.seconds) - convert = true; - unit = KBD_TIMEOUT_SECONDS; - break; - case 'm': - if (value > kbd_info.minutes) - convert = true; - unit = KBD_TIMEOUT_MINUTES; - break; - case 'h': - if (value > kbd_info.hours) - convert = true; - unit = KBD_TIMEOUT_HOURS; - break; - case 'd': - if (value > kbd_info.days) - convert = true; - unit = KBD_TIMEOUT_DAYS; - break; - default: - return -EINVAL; - } - - if (quirks && quirks->needs_kbd_timeouts) - convert = true; - - if (convert) { - /* Convert value from current units to seconds */ - switch (unit) { - case KBD_TIMEOUT_DAYS: - value *= 24; - case KBD_TIMEOUT_HOURS: - value *= 60; - case KBD_TIMEOUT_MINUTES: - value *= 60; - unit = KBD_TIMEOUT_SECONDS; - } - - if (quirks && quirks->needs_kbd_timeouts) { - for (i = 0; quirks->kbd_timeouts[i] != -1; i++) { - if (value <= quirks->kbd_timeouts[i]) { - value = quirks->kbd_timeouts[i]; - break; - } - } - } - - if (value <= kbd_info.seconds && kbd_info.seconds) { - unit = KBD_TIMEOUT_SECONDS; - } else if (value / 60 <= kbd_info.minutes && kbd_info.minutes) { - value /= 60; - unit = KBD_TIMEOUT_MINUTES; - } else if (value / (60 * 60) <= kbd_info.hours && kbd_info.hours) { - value /= (60 * 60); - unit = KBD_TIMEOUT_HOURS; - } else if (value / (60 * 60 * 24) <= kbd_info.days && kbd_info.days) { - value /= (60 * 60 * 24); - unit = KBD_TIMEOUT_DAYS; - } else { - return -EINVAL; - } - } - - ret = kbd_get_state(&state); - if (ret) - return ret; - - new_state = state; - new_state.timeout_value = value; - new_state.timeout_unit = unit; - - ret = kbd_set_state_safe(&new_state, &state); - if (ret) - return ret; - - return count; -} - -static ssize_t kbd_led_timeout_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct kbd_state state; - int ret; - int len; - - ret = kbd_get_state(&state); - if (ret) - return ret; - - len = sprintf(buf, "%d", state.timeout_value); - - switch (state.timeout_unit) { - case KBD_TIMEOUT_SECONDS: - return len + sprintf(buf+len, "s\n"); - case KBD_TIMEOUT_MINUTES: - return len + sprintf(buf+len, "m\n"); - case KBD_TIMEOUT_HOURS: - return len + sprintf(buf+len, "h\n"); - case KBD_TIMEOUT_DAYS: - return len + sprintf(buf+len, "d\n"); - default: - return -EINVAL; - } - - return len; -} - -static DEVICE_ATTR(stop_timeout, S_IRUGO | S_IWUSR, - kbd_led_timeout_show, kbd_led_timeout_store); - -static const char * const kbd_led_triggers[] = { - "keyboard", - "touchpad", - /*"trackstick"*/ NULL, /* NOTE: trackstick is just alias for touchpad */ - "mouse", -}; - -static ssize_t kbd_led_triggers_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct kbd_state new_state; - struct kbd_state state; - bool triggers_enabled = false; - bool als_enabled = false; - bool disable_als = false; - bool enable_als = false; - int trigger_bit = -1; - char trigger[21]; - int i, ret; - - ret = sscanf(buf, "%20s", trigger); - if (ret != 1) - return -EINVAL; - - if (trigger[0] != '+' && trigger[0] != '-') - return -EINVAL; - - ret = kbd_get_state(&state); - if (ret) - return ret; - - if (kbd_als_supported) - als_enabled = kbd_is_als_mode_bit(state.mode_bit); - - if (kbd_triggers_supported) - triggers_enabled = kbd_is_trigger_mode_bit(state.mode_bit); - - if (kbd_als_supported) { - if (strcmp(trigger, "+als") == 0) { - if (als_enabled) - return count; - enable_als = true; - } else if (strcmp(trigger, "-als") == 0) { - if (!als_enabled) - return count; - disable_als = true; - } - } - - if (enable_als || disable_als) { - new_state = state; - if (enable_als) { - if (triggers_enabled) - new_state.mode_bit = KBD_MODE_BIT_TRIGGER_ALS; - else - new_state.mode_bit = KBD_MODE_BIT_ALS; - } else { - if (triggers_enabled) { - new_state.mode_bit = KBD_MODE_BIT_TRIGGER; - kbd_set_level(&new_state, kbd_previous_level); - } else { - new_state.mode_bit = KBD_MODE_BIT_ON; - } - } - if (!(kbd_info.modes & BIT(new_state.mode_bit))) - return -EINVAL; - ret = kbd_set_state_safe(&new_state, &state); - if (ret) - return ret; - kbd_previous_mode_bit = new_state.mode_bit; - return count; - } - - if (kbd_triggers_supported) { - for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); ++i) { - if (!(kbd_info.triggers & BIT(i))) - continue; - if (!kbd_led_triggers[i]) - continue; - if (strcmp(trigger+1, kbd_led_triggers[i]) != 0) - continue; - if (trigger[0] == '+' && - triggers_enabled && (state.triggers & BIT(i))) - return count; - if (trigger[0] == '-' && - (!triggers_enabled || !(state.triggers & BIT(i)))) - return count; - trigger_bit = i; - break; - } - } - - if (trigger_bit != -1) { - new_state = state; - if (trigger[0] == '+') - new_state.triggers |= BIT(trigger_bit); - else { - new_state.triggers &= ~BIT(trigger_bit); - /* NOTE: trackstick bit (2) must be disabled when - * disabling touchpad bit (1), otherwise touchpad - * bit (1) will not be disabled */ - if (trigger_bit == 1) - new_state.triggers &= ~BIT(2); - } - if ((kbd_info.triggers & new_state.triggers) != - new_state.triggers) - return -EINVAL; - if (new_state.triggers && !triggers_enabled) { - if (als_enabled) - new_state.mode_bit = KBD_MODE_BIT_TRIGGER_ALS; - else { - new_state.mode_bit = KBD_MODE_BIT_TRIGGER; - kbd_set_level(&new_state, kbd_previous_level); - } - } else if (new_state.triggers == 0) { - if (als_enabled) - new_state.mode_bit = KBD_MODE_BIT_ALS; - else - kbd_set_level(&new_state, 0); - } - if (!(kbd_info.modes & BIT(new_state.mode_bit))) - return -EINVAL; - ret = kbd_set_state_safe(&new_state, &state); - if (ret) - return ret; - if (new_state.mode_bit != KBD_MODE_BIT_OFF) - kbd_previous_mode_bit = new_state.mode_bit; - return count; - } - - return -EINVAL; -} - -static ssize_t kbd_led_triggers_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct kbd_state state; - bool triggers_enabled; - int level, i, ret; - int len = 0; - - ret = kbd_get_state(&state); - if (ret) - return ret; - - len = 0; - - if (kbd_triggers_supported) { - triggers_enabled = kbd_is_trigger_mode_bit(state.mode_bit); - level = kbd_get_level(&state); - for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); ++i) { - if (!(kbd_info.triggers & BIT(i))) - continue; - if (!kbd_led_triggers[i]) - continue; - if ((triggers_enabled || level <= 0) && - (state.triggers & BIT(i))) - buf[len++] = '+'; - else - buf[len++] = '-'; - len += sprintf(buf+len, "%s ", kbd_led_triggers[i]); - } - } - - if (kbd_als_supported) { - if (kbd_is_als_mode_bit(state.mode_bit)) - len += sprintf(buf+len, "+als "); - else - len += sprintf(buf+len, "-als "); - } - - if (len) - buf[len - 1] = '\n'; - - return len; -} - -static DEVICE_ATTR(start_triggers, S_IRUGO | S_IWUSR, - kbd_led_triggers_show, kbd_led_triggers_store); - -static ssize_t kbd_led_als_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct kbd_state state; - struct kbd_state new_state; - u8 setting; - int ret; - - ret = kstrtou8(buf, 10, &setting); - if (ret) - return ret; - - ret = kbd_get_state(&state); - if (ret) - return ret; - - new_state = state; - new_state.als_setting = setting; - - ret = kbd_set_state_safe(&new_state, &state); - if (ret) - return ret; - - return count; -} - -static ssize_t kbd_led_als_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct kbd_state state; - int ret; - - ret = kbd_get_state(&state); - if (ret) - return ret; - - return sprintf(buf, "%d\n", state.als_setting); -} - -static DEVICE_ATTR(als_setting, S_IRUGO | S_IWUSR, - kbd_led_als_show, kbd_led_als_store); - -static struct attribute *kbd_led_attrs[] = { - &dev_attr_stop_timeout.attr, - &dev_attr_start_triggers.attr, - &dev_attr_als_setting.attr, - NULL, -}; -ATTRIBUTE_GROUPS(kbd_led); - -static enum led_brightness kbd_led_level_get(struct led_classdev *led_cdev) -{ - int ret; - u16 num; - struct kbd_state state; - - if (kbd_get_max_level()) { - ret = kbd_get_state(&state); - if (ret) - return 0; - ret = kbd_get_level(&state); - if (ret < 0) - return 0; - return ret; - } - - if (kbd_get_valid_token_counts()) { - ret = kbd_get_first_active_token_bit(); - if (ret < 0) - return 0; - for (num = kbd_token_bits; num != 0 && ret > 0; --ret) - num &= num - 1; /* clear the first bit set */ - if (num == 0) - return 0; - return ffs(num) - 1; - } - - pr_warn("Keyboard brightness level control not supported\n"); - return 0; -} - -static void kbd_led_level_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - struct kbd_state state; - struct kbd_state new_state; - u16 num; - - if (kbd_get_max_level()) { - if (kbd_get_state(&state)) - return; - new_state = state; - if (kbd_set_level(&new_state, value)) - return; - kbd_set_state_safe(&new_state, &state); - return; - } - - if (kbd_get_valid_token_counts()) { - for (num = kbd_token_bits; num != 0 && value > 0; --value) - num &= num - 1; /* clear the first bit set */ - if (num == 0) - return; - kbd_set_token_bit(ffs(num) - 1); - return; - } - - pr_warn("Keyboard brightness level control not supported\n"); -} - -static struct led_classdev kbd_led = { - .name = "dell::kbd_backlight", - .brightness_set = kbd_led_level_set, - .brightness_get = kbd_led_level_get, - .groups = kbd_led_groups, -}; - -static int __init kbd_led_init(struct device *dev) -{ - kbd_init(); - if (!kbd_led_present) - return -ENODEV; - kbd_led.max_brightness = kbd_get_max_level(); - if (!kbd_led.max_brightness) { - kbd_led.max_brightness = kbd_get_valid_token_counts(); - if (kbd_led.max_brightness) - kbd_led.max_brightness--; - } - return led_classdev_register(dev, &kbd_led); -} - -static void brightness_set_exit(struct led_classdev *led_cdev, - enum led_brightness value) -{ - /* Don't change backlight level on exit */ -}; - -static void kbd_led_exit(void) -{ - if (!kbd_led_present) - return; - kbd_led.brightness_set = brightness_set_exit; - led_classdev_unregister(&kbd_led); -} - static int __init dell_init(void) { int max_intensity = 0; @@ -1879,8 +841,6 @@ static int __init dell_init(void) if (quirks && quirks->touchpad_led) touchpad_led_init(&platform_device->dev); - kbd_led_init(&platform_device->dev); - dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); if (dell_laptop_dir != NULL) debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL, @@ -1948,7 +908,6 @@ static void __exit dell_exit(void) debugfs_remove_recursive(dell_laptop_dir); if (quirks && quirks->touchpad_led) touchpad_led_exit(); - kbd_led_exit(); i8042_remove_filter(dell_laptop_i8042_filter); cancel_delayed_work_sync(&dell_rfkill_work); backlight_device_unregister(dell_backlight_device); @@ -1965,7 +924,5 @@ module_init(dell_init); module_exit(dell_exit); MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); -MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>"); -MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>"); MODULE_DESCRIPTION("Dell laptop driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index 66977ebf13b3..ff0356fb378f 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -180,20 +180,21 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res, struct pnp_dev *dev = data; struct acpi_resource_dma *dma; struct acpi_resource_vendor_typed *vendor_typed; - struct resource r = {0}; + struct resource_win win = {{0}, 0}; + struct resource *r = &win.res; int i, flags; - if (acpi_dev_resource_address_space(res, &r) - || acpi_dev_resource_ext_address_space(res, &r)) { - pnp_add_resource(dev, &r); + if (acpi_dev_resource_address_space(res, &win) + || acpi_dev_resource_ext_address_space(res, &win)) { + pnp_add_resource(dev, &win.res); return AE_OK; } - r.flags = 0; - if (acpi_dev_resource_interrupt(res, 0, &r)) { - pnpacpi_add_irqresource(dev, &r); - for (i = 1; acpi_dev_resource_interrupt(res, i, &r); i++) - pnpacpi_add_irqresource(dev, &r); + r->flags = 0; + if (acpi_dev_resource_interrupt(res, 0, r)) { + pnpacpi_add_irqresource(dev, r); + for (i = 1; acpi_dev_resource_interrupt(res, i, r); i++) + pnpacpi_add_irqresource(dev, r); if (i > 1) { /* @@ -209,7 +210,7 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res, } } return AE_OK; - } else if (r.flags & IORESOURCE_DISABLED) { + } else if (r->flags & IORESOURCE_DISABLED) { pnp_add_irq_resource(dev, 0, IORESOURCE_DISABLED); return AE_OK; } @@ -218,13 +219,13 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res, case ACPI_RESOURCE_TYPE_MEMORY24: case ACPI_RESOURCE_TYPE_MEMORY32: case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: - if (acpi_dev_resource_memory(res, &r)) - pnp_add_resource(dev, &r); + if (acpi_dev_resource_memory(res, r)) + pnp_add_resource(dev, r); break; case ACPI_RESOURCE_TYPE_IO: case ACPI_RESOURCE_TYPE_FIXED_IO: - if (acpi_dev_resource_io(res, &r)) - pnp_add_resource(dev, &r); + if (acpi_dev_resource_io(res, r)) + pnp_add_resource(dev, r); break; case ACPI_RESOURCE_TYPE_DMA: dma = &res->data.dma; @@ -410,12 +411,12 @@ static __init void pnpacpi_parse_address_option(struct pnp_dev *dev, if (p->resource_type == ACPI_MEMORY_RANGE) { if (p->info.mem.write_protect == ACPI_READ_WRITE_MEMORY) flags = IORESOURCE_MEM_WRITEABLE; - pnp_register_mem_resource(dev, option_flags, p->minimum, - p->minimum, 0, p->address_length, + pnp_register_mem_resource(dev, option_flags, p->address.minimum, + p->address.minimum, 0, p->address.address_length, flags); } else if (p->resource_type == ACPI_IO_RANGE) - pnp_register_port_resource(dev, option_flags, p->minimum, - p->minimum, 0, p->address_length, + pnp_register_port_resource(dev, option_flags, p->address.minimum, + p->address.minimum, 0, p->address.address_length, IORESOURCE_IO_FIXED); } @@ -429,12 +430,12 @@ static __init void pnpacpi_parse_ext_address_option(struct pnp_dev *dev, if (p->resource_type == ACPI_MEMORY_RANGE) { if (p->info.mem.write_protect == ACPI_READ_WRITE_MEMORY) flags = IORESOURCE_MEM_WRITEABLE; - pnp_register_mem_resource(dev, option_flags, p->minimum, - p->minimum, 0, p->address_length, + pnp_register_mem_resource(dev, option_flags, p->address.minimum, + p->address.minimum, 0, p->address.address_length, flags); } else if (p->resource_type == ACPI_IO_RANGE) - pnp_register_port_resource(dev, option_flags, p->minimum, - p->minimum, 0, p->address_length, + pnp_register_port_resource(dev, option_flags, p->address.minimum, + p->address.minimum, 0, p->address.address_length, IORESOURCE_IO_FIXED); } diff --git a/drivers/power/88pm860x_charger.c b/drivers/power/88pm860x_charger.c index 650930e4fa79..734ec4afa14d 100644 --- a/drivers/power/88pm860x_charger.c +++ b/drivers/power/88pm860x_charger.c @@ -711,6 +711,7 @@ static int pm860x_charger_probe(struct platform_device *pdev) return 0; out_irq: + power_supply_unregister(&info->usb); while (--i >= 0) free_irq(info->irq[i], info); out: diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 0108c2af005b..27b751b995fb 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -315,7 +315,7 @@ config CHARGER_GPIO config CHARGER_MANAGER bool "Battery charger manager for multiple chargers" - depends on REGULATOR && RTC_CLASS + depends on REGULATOR select EXTCON help Say Y to enable charger-manager support, which allows multiple @@ -327,11 +327,16 @@ config CHARGER_MANAGER config CHARGER_MAX14577 tristate "Maxim MAX14577/77836 battery charger driver" depends on MFD_MAX14577 - depends on SYSFS help Say Y to enable support for the battery charger control sysfs and platform data of MAX14577/77836 MUICs. +config CHARGER_MAX77693 + tristate "Maxim MAX77693 battery charger driver" + depends on MFD_MAX77693 + help + Say Y to enable support for the Maxim MAX77693 battery charger. + config CHARGER_MAX8997 tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver" depends on MFD_MAX8997 && REGULATOR_MAX8997 @@ -383,6 +388,14 @@ config CHARGER_TPS65090 Say Y here to enable support for battery charging with TPS65090 PMIC chips. +config BATTERY_GAUGE_LTC2941 + tristate "LTC2941/LTC2943 Battery Gauge Driver" + depends on I2C + help + Say Y here to include support for LTC2941 and LTC2943 Battery + Gauge IC. The driver reports the charge count continuously, and + measures the voltage and temperature every 10 seconds. + config AB8500_BM bool "AB8500 Battery Management Driver" depends on AB8500_CORE && AB8500_GPADC @@ -397,6 +410,14 @@ config BATTERY_GOLDFISH Say Y to enable support for the battery and AC power in the Goldfish emulator. +config BATTERY_RT5033 + tristate "RT5033 fuel gauge support" + depends on MFD_RT5033 + help + This adds support for battery fuel gauge in Richtek RT5033 PMIC. + The fuelgauge calculates and determines the battery state of charge + according to battery open circuit voltage. + source "drivers/power/reset/Kconfig" endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index dfa894273926..36f9e0d10111 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o +obj-$(CONFIG_BATTERY_GAUGE_LTC2941) += ltc2941-battery-gauge.o obj-$(CONFIG_BATTERY_GOLDFISH) += goldfish_battery.o obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o @@ -34,6 +35,7 @@ obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o obj-$(CONFIG_BATTERY_Z2) += z2_battery.o +obj-$(CONFIG_BATTERY_RT5033) += rt5033_battery.o obj-$(CONFIG_BATTERY_S3C_ADC) += s3c_adc_battery.o obj-$(CONFIG_BATTERY_TWL4030_MADC) += twl4030_madc_battery.o obj-$(CONFIG_CHARGER_88PM860X) += 88pm860x_charger.o @@ -50,6 +52,7 @@ obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o +obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o obj-$(CONFIG_CHARGER_BQ2415X) += bq2415x_charger.o diff --git a/drivers/power/ab8500_fg.c b/drivers/power/ab8500_fg.c index 69b80bcaa9e7..c908658aa31a 100644 --- a/drivers/power/ab8500_fg.c +++ b/drivers/power/ab8500_fg.c @@ -2435,20 +2435,6 @@ static void ab8500_fg_reinit_work(struct work_struct *work) } } -/** - * ab8500_fg_reinit() - forces FG algorithm to reinitialize with current values - * - * This function can be used to force the FG algorithm to recalculate a new - * voltage based battery capacity. - */ -void ab8500_fg_reinit(void) -{ - struct ab8500_fg *di = ab8500_fg_get(); - /* User won't be notified if a null pointer returned. */ - if (di != NULL) - queue_delayed_work(di->fg_wq, &di->fg_reinit_work, 0); -} - /* Exposure to the sysfs interface */ struct ab8500_fg_sysfs_entry { diff --git a/drivers/power/bq24190_charger.c b/drivers/power/bq24190_charger.c index ad3ff8fbfbbb..d0e8236a6404 100644 --- a/drivers/power/bq24190_charger.c +++ b/drivers/power/bq24190_charger.c @@ -929,7 +929,7 @@ static void bq24190_charger_init(struct power_supply *charger) charger->properties = bq24190_charger_properties; charger->num_properties = ARRAY_SIZE(bq24190_charger_properties); charger->supplied_to = bq24190_charger_supplied_to; - charger->num_supplies = ARRAY_SIZE(bq24190_charger_supplied_to); + charger->num_supplicants = ARRAY_SIZE(bq24190_charger_supplied_to); charger->get_property = bq24190_charger_get_property; charger->set_property = bq24190_charger_set_property; charger->property_is_writeable = bq24190_charger_property_is_writeable; @@ -1208,7 +1208,7 @@ static irqreturn_t bq24190_irq_handler_thread(int irq, void *data) { struct bq24190_dev_info *bdi = data; bool alert_userspace = false; - u8 ss_reg, f_reg; + u8 ss_reg = 0, f_reg = 0; int ret; pm_runtime_get_sync(bdi->dev); diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index a78ac201828e..b72ba7c1bd69 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -76,7 +76,8 @@ /* bq27425 register addresses are same as bq27x00 addresses minus 4 */ #define BQ27425_REG_OFFSET 0x04 -#define BQ27425_REG_SOC 0x18 /* Register address plus offset */ +#define BQ27425_REG_SOC (0x1C + BQ27425_REG_OFFSET) +#define BQ27425_REG_DCAP (0x3C + BQ27425_REG_OFFSET) #define BQ27000_RS 20 /* Resistor sense */ #define BQ27x00_POWER_CONSTANT (256 * 29200 / 1000) @@ -282,9 +283,12 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di) { int ilmd; - if (bq27xxx_is_chip_version_higher(di)) - ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false); - else + if (bq27xxx_is_chip_version_higher(di)) { + if (di->chip == BQ27425) + ilmd = bq27x00_read(di, BQ27425_REG_DCAP, false); + else + ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false); + } else ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true); if (ilmd < 0) { @@ -493,10 +497,11 @@ static void bq27x00_update(struct bq27x00_device_info *di) di->charge_design_full = bq27x00_battery_read_ilmd(di); } - if (memcmp(&di->cache, &cache, sizeof(cache)) != 0) { - di->cache = cache; + if (di->cache.capacity != cache.capacity) power_supply_changed(&di->bat); - } + + if (memcmp(&di->cache, &cache, sizeof(cache)) != 0) + di->cache = cache; di->last_update = jiffies; } diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c index 649052e1f2d9..14b0d85318eb 100644 --- a/drivers/power/charger-manager.c +++ b/drivers/power/charger-manager.c @@ -69,16 +69,10 @@ static LIST_HEAD(cm_list); static DEFINE_MUTEX(cm_list_mtx); /* About in-suspend (suspend-again) monitoring */ -static struct rtc_device *rtc_dev; -/* - * Backup RTC alarm - * Save the wakeup alarm before entering suspend-to-RAM - */ -static struct rtc_wkalrm rtc_wkalarm_save; -/* Backup RTC alarm time in terms of seconds since 01-01-1970 00:00:00 */ -static unsigned long rtc_wkalarm_save_time; +static struct alarm *cm_timer; + static bool cm_suspended; -static bool cm_rtc_set; +static bool cm_timer_set; static unsigned long cm_suspend_duration_ms; /* About normal (not suspended) monitoring */ @@ -87,9 +81,6 @@ static unsigned long next_polling; /* Next appointed polling time */ static struct workqueue_struct *cm_wq; /* init at driver add */ static struct delayed_work cm_monitor_work; /* init at driver add */ -/* Global charger-manager description */ -static struct charger_global_desc *g_desc; /* init with setup_charger_manager */ - /** * is_batt_present - See if the battery presents in place. * @cm: the Charger Manager representing the battery. @@ -1047,10 +1038,13 @@ static bool cm_setup_timer(void) { struct charger_manager *cm; unsigned int wakeup_ms = UINT_MAX; - bool ret = false; + int timer_req = 0; - mutex_lock(&cm_list_mtx); + if (time_after(next_polling, jiffies)) + CM_MIN_VALID(wakeup_ms, + jiffies_to_msecs(next_polling - jiffies)); + mutex_lock(&cm_list_mtx); list_for_each_entry(cm, &cm_list, entry) { unsigned int fbchk_ms = 0; @@ -1070,162 +1064,38 @@ static bool cm_setup_timer(void) /* Skip if polling is not required for this CM */ if (!is_polling_required(cm) && !cm->emergency_stop) continue; + timer_req++; if (cm->desc->polling_interval_ms == 0) continue; CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms); } - mutex_unlock(&cm_list_mtx); - if (wakeup_ms < UINT_MAX && wakeup_ms > 0) { - pr_info("Charger Manager wakeup timer: %u ms\n", wakeup_ms); - if (rtc_dev) { - struct rtc_wkalrm tmp; - unsigned long time, now; - unsigned long add = DIV_ROUND_UP(wakeup_ms, 1000); - - /* - * Set alarm with the polling interval (wakeup_ms) - * except when rtc_wkalarm_save comes first. - * However, the alarm time should be NOW + - * CM_RTC_SMALL or later. - */ - tmp.enabled = 1; - rtc_read_time(rtc_dev, &tmp.time); - rtc_tm_to_time(&tmp.time, &now); - if (add < CM_RTC_SMALL) - add = CM_RTC_SMALL; - time = now + add; + if (timer_req && cm_timer) { + ktime_t now, add; - ret = true; + /* + * Set alarm with the polling interval (wakeup_ms) + * The alarm time should be NOW + CM_RTC_SMALL or later. + */ + if (wakeup_ms == UINT_MAX || + wakeup_ms < CM_RTC_SMALL * MSEC_PER_SEC) + wakeup_ms = 2 * CM_RTC_SMALL * MSEC_PER_SEC; - if (rtc_wkalarm_save.enabled && - rtc_wkalarm_save_time && - rtc_wkalarm_save_time < time) { - if (rtc_wkalarm_save_time < now + CM_RTC_SMALL) - time = now + CM_RTC_SMALL; - else - time = rtc_wkalarm_save_time; + pr_info("Charger Manager wakeup timer: %u ms\n", wakeup_ms); - /* The timer is not appointed by CM */ - ret = false; - } + now = ktime_get_boottime(); + add = ktime_set(wakeup_ms / MSEC_PER_SEC, + (wakeup_ms % MSEC_PER_SEC) * NSEC_PER_MSEC); + alarm_start(cm_timer, ktime_add(now, add)); - pr_info("Waking up after %lu secs\n", time - now); + cm_suspend_duration_ms = wakeup_ms; - rtc_time_to_tm(time, &tmp.time); - rtc_set_alarm(rtc_dev, &tmp); - cm_suspend_duration_ms += wakeup_ms; - return ret; - } + return true; } - - if (rtc_dev) - rtc_set_alarm(rtc_dev, &rtc_wkalarm_save); return false; } -static void _cm_fbchk_in_suspend(struct charger_manager *cm) -{ - unsigned long jiffy_now = jiffies; - - if (!cm->fullbatt_vchk_jiffies_at) - return; - - if (g_desc && g_desc->assume_timer_stops_in_suspend) - jiffy_now += msecs_to_jiffies(cm_suspend_duration_ms); - - /* Execute now if it's going to be executed not too long after */ - jiffy_now += CM_JIFFIES_SMALL; - - if (time_after_eq(jiffy_now, cm->fullbatt_vchk_jiffies_at)) - fullbatt_vchk(&cm->fullbatt_vchk_work.work); -} - -/** - * cm_suspend_again - Determine whether suspend again or not - * - * Returns true if the system should be suspended again - * Returns false if the system should be woken up - */ -bool cm_suspend_again(void) -{ - struct charger_manager *cm; - bool ret = false; - - if (!g_desc || !g_desc->rtc_only_wakeup || !g_desc->rtc_only_wakeup() || - !cm_rtc_set) - return false; - - if (cm_monitor()) - goto out; - - ret = true; - mutex_lock(&cm_list_mtx); - list_for_each_entry(cm, &cm_list, entry) { - _cm_fbchk_in_suspend(cm); - - if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) || - cm->status_save_batt != is_batt_present(cm)) { - ret = false; - break; - } - } - mutex_unlock(&cm_list_mtx); - - cm_rtc_set = cm_setup_timer(); -out: - /* It's about the time when the non-CM appointed timer goes off */ - if (rtc_wkalarm_save.enabled) { - unsigned long now; - struct rtc_time tmp; - - rtc_read_time(rtc_dev, &tmp); - rtc_tm_to_time(&tmp, &now); - - if (rtc_wkalarm_save_time && - now + CM_RTC_SMALL >= rtc_wkalarm_save_time) - return false; - } - return ret; -} -EXPORT_SYMBOL_GPL(cm_suspend_again); - -/** - * setup_charger_manager - initialize charger_global_desc data - * @gd: pointer to instance of charger_global_desc - */ -int setup_charger_manager(struct charger_global_desc *gd) -{ - if (!gd) - return -EINVAL; - - if (rtc_dev) - rtc_class_close(rtc_dev); - rtc_dev = NULL; - g_desc = NULL; - - if (!gd->rtc_only_wakeup) { - pr_err("The callback rtc_only_wakeup is not given\n"); - return -EINVAL; - } - - if (gd->rtc_name) { - rtc_dev = rtc_class_open(gd->rtc_name); - if (IS_ERR_OR_NULL(rtc_dev)) { - rtc_dev = NULL; - /* Retry at probe. RTC may be not registered yet */ - } - } else { - pr_warn("No wakeup timer is given for charger manager. " - "In-suspend monitoring won't work.\n"); - } - - g_desc = gd; - return 0; -} -EXPORT_SYMBOL_GPL(setup_charger_manager); - /** * charger_extcon_work - enable/diable charger according to the state * of charger cable @@ -1719,6 +1589,12 @@ static inline struct charger_desc *cm_get_drv_data(struct platform_device *pdev) return dev_get_platdata(&pdev->dev); } +static enum alarmtimer_restart cm_timer_func(struct alarm *alarm, ktime_t now) +{ + cm_timer_set = false; + return ALARMTIMER_NORESTART; +} + static int charger_manager_probe(struct platform_device *pdev) { struct charger_desc *desc = cm_get_drv_data(pdev); @@ -1728,16 +1604,6 @@ static int charger_manager_probe(struct platform_device *pdev) union power_supply_propval val; struct power_supply *fuel_gauge; - if (g_desc && !rtc_dev && g_desc->rtc_name) { - rtc_dev = rtc_class_open(g_desc->rtc_name); - if (IS_ERR_OR_NULL(rtc_dev)) { - rtc_dev = NULL; - dev_err(&pdev->dev, "Cannot get RTC %s\n", - g_desc->rtc_name); - return -ENODEV; - } - } - if (IS_ERR(desc)) { dev_err(&pdev->dev, "No platform data (desc) found\n"); return -ENODEV; @@ -1752,6 +1618,12 @@ static int charger_manager_probe(struct platform_device *pdev) cm->dev = &pdev->dev; cm->desc = desc; + /* Initialize alarm timer */ + if (alarmtimer_get_rtcdev()) { + cm_timer = devm_kzalloc(cm->dev, sizeof(*cm_timer), GFP_KERNEL); + alarm_init(cm_timer, ALARM_BOOTTIME, cm_timer_func); + } + /* * The following two do not need to be errors. * Users may intentionally ignore those two features. @@ -1993,38 +1865,41 @@ static int cm_suspend_noirq(struct device *dev) return ret; } +static bool cm_need_to_awake(void) +{ + struct charger_manager *cm; + + if (cm_timer) + return false; + + mutex_lock(&cm_list_mtx); + list_for_each_entry(cm, &cm_list, entry) { + if (is_charging(cm)) { + mutex_unlock(&cm_list_mtx); + return true; + } + } + mutex_unlock(&cm_list_mtx); + + return false; +} + static int cm_suspend_prepare(struct device *dev) { struct charger_manager *cm = dev_get_drvdata(dev); - if (!cm_suspended) { - if (rtc_dev) { - struct rtc_time tmp; - unsigned long now; - - rtc_read_alarm(rtc_dev, &rtc_wkalarm_save); - rtc_read_time(rtc_dev, &tmp); + if (cm_need_to_awake()) + return -EBUSY; - if (rtc_wkalarm_save.enabled) { - rtc_tm_to_time(&rtc_wkalarm_save.time, - &rtc_wkalarm_save_time); - rtc_tm_to_time(&tmp, &now); - if (now > rtc_wkalarm_save_time) - rtc_wkalarm_save_time = 0; - } else { - rtc_wkalarm_save_time = 0; - } - } + if (!cm_suspended) cm_suspended = true; - } - cancel_delayed_work(&cm->fullbatt_vchk_work); - cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm); - cm->status_save_batt = is_batt_present(cm); + cm_timer_set = cm_setup_timer(); - if (!cm_rtc_set) { - cm_suspend_duration_ms = 0; - cm_rtc_set = cm_setup_timer(); + if (cm_timer_set) { + cancel_work_sync(&setup_polling); + cancel_delayed_work_sync(&cm_monitor_work); + cancel_delayed_work(&cm->fullbatt_vchk_work); } return 0; @@ -2034,18 +1909,21 @@ static void cm_suspend_complete(struct device *dev) { struct charger_manager *cm = dev_get_drvdata(dev); - if (cm_suspended) { - if (rtc_dev) { - struct rtc_wkalrm tmp; - - rtc_read_alarm(rtc_dev, &tmp); - rtc_wkalarm_save.pending = tmp.pending; - rtc_set_alarm(rtc_dev, &rtc_wkalarm_save); - } + if (cm_suspended) cm_suspended = false; - cm_rtc_set = false; + + if (cm_timer_set) { + ktime_t remain; + + alarm_cancel(cm_timer); + cm_timer_set = false; + remain = alarm_expires_remaining(cm_timer); + cm_suspend_duration_ms -= ktime_to_ms(remain); + schedule_work(&setup_polling); } + _cm_monitor(cm); + /* Re-enqueue delayed work (fullbatt_vchk_work) */ if (cm->fullbatt_vchk_jiffies_at) { unsigned long delay = 0; @@ -2060,21 +1938,18 @@ static void cm_suspend_complete(struct device *dev) } /* - * Account for cm_suspend_duration_ms if - * assume_timer_stops_in_suspend is active + * Account for cm_suspend_duration_ms with assuming that + * timer stops in suspend. */ - if (g_desc && g_desc->assume_timer_stops_in_suspend) { - if (delay > cm_suspend_duration_ms) - delay -= cm_suspend_duration_ms; - else - delay = 0; - } + if (delay > cm_suspend_duration_ms) + delay -= cm_suspend_duration_ms; + else + delay = 0; queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work, msecs_to_jiffies(delay)); } device_set_wakeup_capable(cm->dev, false); - uevent_notify(cm, NULL); } static const struct dev_pm_ops charger_manager_pm = { diff --git a/drivers/power/collie_battery.c b/drivers/power/collie_battery.c index d02ae02a7590..594e4dbc2d51 100644 --- a/drivers/power/collie_battery.c +++ b/drivers/power/collie_battery.c @@ -26,6 +26,7 @@ static DEFINE_MUTEX(bat_lock); /* protects gpio pins */ static struct work_struct bat_work; static struct ucb1x00 *ucb; +static int wakeup_enabled; struct collie_bat { int status; @@ -291,11 +292,21 @@ static int collie_bat_suspend(struct ucb1x00_dev *dev) { /* flush all pending status updates */ flush_work(&bat_work); + + if (device_may_wakeup(&dev->ucb->dev) && + collie_bat_main.status == POWER_SUPPLY_STATUS_CHARGING) + wakeup_enabled = !enable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO)); + else + wakeup_enabled = 0; + return 0; } static int collie_bat_resume(struct ucb1x00_dev *dev) { + if (wakeup_enabled) + disable_irq_wake(gpio_to_irq(COLLIE_GPIO_CO)); + /* things may have changed while we were away */ schedule_work(&bat_work); return 0; @@ -334,10 +345,15 @@ static int collie_bat_probe(struct ucb1x00_dev *dev) collie_bat_gpio_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "main full", &collie_bat_main); - if (!ret) { - schedule_work(&bat_work); - return 0; - } + if (ret) + goto err_irq; + + device_init_wakeup(&ucb->dev, 1); + schedule_work(&bat_work); + + return 0; + +err_irq: power_supply_unregister(&collie_bat_bu.psy); err_psy_reg_bu: power_supply_unregister(&collie_bat_main.psy); diff --git a/drivers/power/gpio-charger.c b/drivers/power/gpio-charger.c index aef74bdf7ab3..b7424c8501f1 100644 --- a/drivers/power/gpio-charger.c +++ b/drivers/power/gpio-charger.c @@ -229,7 +229,7 @@ static int gpio_charger_suspend(struct device *dev) if (device_may_wakeup(dev)) gpio_charger->wakeup_enabled = - enable_irq_wake(gpio_charger->irq); + !enable_irq_wake(gpio_charger->irq); return 0; } @@ -239,7 +239,7 @@ static int gpio_charger_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); - if (gpio_charger->wakeup_enabled) + if (device_may_wakeup(dev) && gpio_charger->wakeup_enabled) disable_irq_wake(gpio_charger->irq); power_supply_changed(&gpio_charger->charger); diff --git a/drivers/power/ltc2941-battery-gauge.c b/drivers/power/ltc2941-battery-gauge.c new file mode 100644 index 000000000000..e31c927a6d16 --- /dev/null +++ b/drivers/power/ltc2941-battery-gauge.c @@ -0,0 +1,547 @@ +/* + * I2C client/driver for the Linear Technology LTC2941 and LTC2943 + * Battery Gas Gauge IC + * + * Copyright (C) 2014 Topic Embedded Systems + * + * Author: Auryn Verwegen + * Author: Mike Looijmans + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/swab.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/idr.h> +#include <linux/power_supply.h> +#include <linux/slab.h> + +#define I16_MSB(x) ((x >> 8) & 0xFF) +#define I16_LSB(x) (x & 0xFF) + +#define LTC294X_WORK_DELAY 10 /* Update delay in seconds */ + +#define LTC294X_MAX_VALUE 0xFFFF +#define LTC294X_MID_SUPPLY 0x7FFF + +#define LTC2941_MAX_PRESCALER_EXP 7 +#define LTC2943_MAX_PRESCALER_EXP 6 + +enum ltc294x_reg { + LTC294X_REG_STATUS = 0x00, + LTC294X_REG_CONTROL = 0x01, + LTC294X_REG_ACC_CHARGE_MSB = 0x02, + LTC294X_REG_ACC_CHARGE_LSB = 0x03, + LTC294X_REG_THRESH_HIGH_MSB = 0x04, + LTC294X_REG_THRESH_HIGH_LSB = 0x05, + LTC294X_REG_THRESH_LOW_MSB = 0x06, + LTC294X_REG_THRESH_LOW_LSB = 0x07, + LTC294X_REG_VOLTAGE_MSB = 0x08, + LTC294X_REG_VOLTAGE_LSB = 0x09, + LTC294X_REG_CURRENT_MSB = 0x0E, + LTC294X_REG_CURRENT_LSB = 0x0F, + LTC294X_REG_TEMPERATURE_MSB = 0x14, + LTC294X_REG_TEMPERATURE_LSB = 0x15, +}; + +#define LTC2943_REG_CONTROL_MODE_MASK (BIT(7) | BIT(6)) +#define LTC2943_REG_CONTROL_MODE_SCAN BIT(7) +#define LTC294X_REG_CONTROL_PRESCALER_MASK (BIT(5) | BIT(4) | BIT(3)) +#define LTC294X_REG_CONTROL_SHUTDOWN_MASK (BIT(0)) +#define LTC294X_REG_CONTROL_PRESCALER_SET(x) \ + ((x << 3) & LTC294X_REG_CONTROL_PRESCALER_MASK) +#define LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED 0 + +#define LTC2941_NUM_REGS 0x08 +#define LTC2943_NUM_REGS 0x18 + +struct ltc294x_info { + struct i2c_client *client; /* I2C Client pointer */ + struct power_supply supply; /* Supply pointer */ + struct delayed_work work; /* Work scheduler */ + int num_regs; /* Number of registers (chip type) */ + int id; /* Identifier of ltc294x chip */ + int charge; /* Last charge register content */ + int r_sense; /* mOhm */ + int Qlsb; /* nAh */ +}; + +static DEFINE_IDR(ltc294x_id); +static DEFINE_MUTEX(ltc294x_lock); + +static inline int convert_bin_to_uAh( + const struct ltc294x_info *info, int Q) +{ + return ((Q * (info->Qlsb / 10))) / 100; +} + +static inline int convert_uAh_to_bin( + const struct ltc294x_info *info, int uAh) +{ + int Q; + + Q = (uAh * 100) / (info->Qlsb/10); + return (Q < LTC294X_MAX_VALUE) ? Q : LTC294X_MAX_VALUE; +} + +static int ltc294x_read_regs(struct i2c_client *client, + enum ltc294x_reg reg, u8 *buf, int num_regs) +{ + int ret; + struct i2c_msg msgs[2] = { }; + u8 reg_start = reg; + + msgs[0].addr = client->addr; + msgs[0].len = 1; + msgs[0].buf = ®_start; + + msgs[1].addr = client->addr; + msgs[1].len = num_regs; + msgs[1].buf = buf; + msgs[1].flags = I2C_M_RD; + + ret = i2c_transfer(client->adapter, &msgs[0], 2); + if (ret < 0) { + dev_err(&client->dev, "ltc2941 read_reg failed!\n"); + return ret; + } + + dev_dbg(&client->dev, "%s (%#x, %d) -> %#x\n", + __func__, reg, num_regs, *buf); + + return 0; +} + +static int ltc294x_write_regs(struct i2c_client *client, + enum ltc294x_reg reg, const u8 *buf, int num_regs) +{ + int ret; + u8 reg_start = reg; + + ret = i2c_smbus_write_i2c_block_data(client, reg_start, num_regs, buf); + if (ret < 0) { + dev_err(&client->dev, "ltc2941 write_reg failed!\n"); + return ret; + } + + dev_dbg(&client->dev, "%s (%#x, %d) -> %#x\n", + __func__, reg, num_regs, *buf); + + return 0; +} + +static int ltc294x_reset(const struct ltc294x_info *info, int prescaler_exp) +{ + int ret; + u8 value; + u8 control; + + /* Read status and control registers */ + ret = ltc294x_read_regs(info->client, LTC294X_REG_CONTROL, &value, 1); + if (ret < 0) { + dev_err(&info->client->dev, + "Could not read registers from device\n"); + goto error_exit; + } + + control = LTC294X_REG_CONTROL_PRESCALER_SET(prescaler_exp) | + LTC294X_REG_CONTROL_ALCC_CONFIG_DISABLED; + /* Put the 2943 into "monitor" mode, so it measures every 10 sec */ + if (info->num_regs == LTC2943_NUM_REGS) + control |= LTC2943_REG_CONTROL_MODE_SCAN; + + if (value != control) { + ret = ltc294x_write_regs(info->client, + LTC294X_REG_CONTROL, &control, 1); + if (ret < 0) { + dev_err(&info->client->dev, + "Could not write register\n"); + goto error_exit; + } + } + + return 0; + +error_exit: + return ret; +} + +static int ltc294x_read_charge_register(const struct ltc294x_info *info) +{ + int ret; + u8 datar[2]; + + ret = ltc294x_read_regs(info->client, + LTC294X_REG_ACC_CHARGE_MSB, &datar[0], 2); + if (ret < 0) + return ret; + return (datar[0] << 8) + datar[1]; +} + +static int ltc294x_get_charge_now(const struct ltc294x_info *info, int *val) +{ + int value = ltc294x_read_charge_register(info); + + if (value < 0) + return value; + /* When r_sense < 0, this counts up when the battery discharges */ + if (info->Qlsb < 0) + value -= 0xFFFF; + *val = convert_bin_to_uAh(info, value); + return 0; +} + +static int ltc294x_set_charge_now(const struct ltc294x_info *info, int val) +{ + int ret; + u8 dataw[2]; + u8 ctrl_reg; + s32 value; + + value = convert_uAh_to_bin(info, val); + /* Direction depends on how sense+/- were connected */ + if (info->Qlsb < 0) + value += 0xFFFF; + if ((value < 0) || (value > 0xFFFF)) /* input validation */ + return -EINVAL; + + /* Read control register */ + ret = ltc294x_read_regs(info->client, + LTC294X_REG_CONTROL, &ctrl_reg, 1); + if (ret < 0) + return ret; + /* Disable analog section */ + ctrl_reg |= LTC294X_REG_CONTROL_SHUTDOWN_MASK; + ret = ltc294x_write_regs(info->client, + LTC294X_REG_CONTROL, &ctrl_reg, 1); + if (ret < 0) + return ret; + /* Set new charge value */ + dataw[0] = I16_MSB(value); + dataw[1] = I16_LSB(value); + ret = ltc294x_write_regs(info->client, + LTC294X_REG_ACC_CHARGE_MSB, &dataw[0], 2); + if (ret < 0) + goto error_exit; + /* Enable analog section */ +error_exit: + ctrl_reg &= ~LTC294X_REG_CONTROL_SHUTDOWN_MASK; + ret = ltc294x_write_regs(info->client, + LTC294X_REG_CONTROL, &ctrl_reg, 1); + + return ret < 0 ? ret : 0; +} + +static int ltc294x_get_charge_counter( + const struct ltc294x_info *info, int *val) +{ + int value = ltc294x_read_charge_register(info); + + if (value < 0) + return value; + value -= LTC294X_MID_SUPPLY; + *val = convert_bin_to_uAh(info, value); + return 0; +} + +static int ltc294x_get_voltage(const struct ltc294x_info *info, int *val) +{ + int ret; + u8 datar[2]; + u32 value; + + ret = ltc294x_read_regs(info->client, + LTC294X_REG_VOLTAGE_MSB, &datar[0], 2); + value = (datar[0] << 8) | datar[1]; + *val = ((value * 23600) / 0xFFFF) * 1000; /* in uV */ + return ret; +} + +static int ltc294x_get_current(const struct ltc294x_info *info, int *val) +{ + int ret; + u8 datar[2]; + s32 value; + + ret = ltc294x_read_regs(info->client, + LTC294X_REG_CURRENT_MSB, &datar[0], 2); + value = (datar[0] << 8) | datar[1]; + value -= 0x7FFF; + /* Value is in range -32k..+32k, r_sense is usually 10..50 mOhm, + * the formula below keeps everything in s32 range while preserving + * enough digits */ + *val = 1000 * ((60000 * value) / (info->r_sense * 0x7FFF)); /* in uA */ + return ret; +} + +static int ltc294x_get_temperature(const struct ltc294x_info *info, int *val) +{ + int ret; + u8 datar[2]; + u32 value; + + ret = ltc294x_read_regs(info->client, + LTC294X_REG_TEMPERATURE_MSB, &datar[0], 2); + value = (datar[0] << 8) | datar[1]; + /* Full-scale is 510 Kelvin, convert to centidegrees */ + *val = (((51000 * value) / 0xFFFF) - 27215); + return ret; +} + +static int ltc294x_get_property(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *val) +{ + struct ltc294x_info *info = + container_of(psy, struct ltc294x_info, supply); + + switch (prop) { + case POWER_SUPPLY_PROP_CHARGE_NOW: + return ltc294x_get_charge_now(info, &val->intval); + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + return ltc294x_get_charge_counter(info, &val->intval); + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + return ltc294x_get_voltage(info, &val->intval); + case POWER_SUPPLY_PROP_CURRENT_NOW: + return ltc294x_get_current(info, &val->intval); + case POWER_SUPPLY_PROP_TEMP: + return ltc294x_get_temperature(info, &val->intval); + default: + return -EINVAL; + } +} + +static int ltc294x_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct ltc294x_info *info = + container_of(psy, struct ltc294x_info, supply); + + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_NOW: + return ltc294x_set_charge_now(info, val->intval); + default: + return -EPERM; + } +} + +static int ltc294x_property_is_writeable( + struct power_supply *psy, enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_NOW: + return 1; + default: + return 0; + } +} + +static void ltc294x_update(struct ltc294x_info *info) +{ + int charge = ltc294x_read_charge_register(info); + + if (charge != info->charge) { + info->charge = charge; + power_supply_changed(&info->supply); + } +} + +static void ltc294x_work(struct work_struct *work) +{ + struct ltc294x_info *info; + + info = container_of(work, struct ltc294x_info, work.work); + ltc294x_update(info); + schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ); +} + +static enum power_supply_property ltc294x_properties[] = { + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_TEMP, +}; + +static int ltc294x_i2c_remove(struct i2c_client *client) +{ + struct ltc294x_info *info = i2c_get_clientdata(client); + + cancel_delayed_work(&info->work); + power_supply_unregister(&info->supply); + kfree(info->supply.name); + mutex_lock(<c294x_lock); + idr_remove(<c294x_id, info->id); + mutex_unlock(<c294x_lock); + return 0; +} + +static int ltc294x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ltc294x_info *info; + int ret; + int num; + u32 prescaler_exp; + s32 r_sense; + struct device_node *np; + + mutex_lock(<c294x_lock); + ret = idr_alloc(<c294x_id, client, 0, 0, GFP_KERNEL); + mutex_unlock(<c294x_lock); + if (ret < 0) + goto fail_id; + + num = ret; + + info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); + if (info == NULL) { + ret = -ENOMEM; + goto fail_info; + } + + i2c_set_clientdata(client, info); + + info->num_regs = id->driver_data; + info->supply.name = kasprintf(GFP_KERNEL, "%s-%d", client->name, num); + if (!info->supply.name) { + ret = -ENOMEM; + goto fail_name; + } + + np = of_node_get(client->dev.of_node); + + /* r_sense can be negative, when sense+ is connected to the battery + * instead of the sense-. This results in reversed measurements. */ + ret = of_property_read_u32(np, "lltc,resistor-sense", &r_sense); + if (ret < 0) { + dev_err(&client->dev, + "Could not find lltc,resistor-sense in devicetree\n"); + goto fail_name; + } + info->r_sense = r_sense; + + ret = of_property_read_u32(np, "lltc,prescaler-exponent", + &prescaler_exp); + if (ret < 0) { + dev_warn(&client->dev, + "lltc,prescaler-exponent not in devicetree\n"); + prescaler_exp = LTC2941_MAX_PRESCALER_EXP; + } + + if (info->num_regs == LTC2943_NUM_REGS) { + if (prescaler_exp > LTC2943_MAX_PRESCALER_EXP) + prescaler_exp = LTC2943_MAX_PRESCALER_EXP; + info->Qlsb = ((340 * 50000) / r_sense) / + (4096 / (1 << (2*prescaler_exp))); + } else { + if (prescaler_exp > LTC2941_MAX_PRESCALER_EXP) + prescaler_exp = LTC2941_MAX_PRESCALER_EXP; + info->Qlsb = ((58 * 50000) / r_sense) / + (128 / (1 << prescaler_exp)); + } + + info->client = client; + info->id = num; + info->supply.type = POWER_SUPPLY_TYPE_BATTERY; + info->supply.properties = ltc294x_properties; + if (info->num_regs >= LTC294X_REG_TEMPERATURE_LSB) + info->supply.num_properties = + ARRAY_SIZE(ltc294x_properties); + else if (info->num_regs >= LTC294X_REG_CURRENT_LSB) + info->supply.num_properties = + ARRAY_SIZE(ltc294x_properties) - 1; + else if (info->num_regs >= LTC294X_REG_VOLTAGE_LSB) + info->supply.num_properties = + ARRAY_SIZE(ltc294x_properties) - 2; + else + info->supply.num_properties = + ARRAY_SIZE(ltc294x_properties) - 3; + info->supply.get_property = ltc294x_get_property; + info->supply.set_property = ltc294x_set_property; + info->supply.property_is_writeable = ltc294x_property_is_writeable; + info->supply.external_power_changed = NULL; + + INIT_DELAYED_WORK(&info->work, ltc294x_work); + + ret = ltc294x_reset(info, prescaler_exp); + if (ret < 0) { + dev_err(&client->dev, "Communication with chip failed\n"); + goto fail_comm; + } + + ret = power_supply_register(&client->dev, &info->supply); + if (ret) { + dev_err(&client->dev, "failed to register ltc2941\n"); + goto fail_register; + } else { + schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ); + } + + return 0; + +fail_register: + kfree(info->supply.name); +fail_comm: +fail_name: +fail_info: + mutex_lock(<c294x_lock); + idr_remove(<c294x_id, num); + mutex_unlock(<c294x_lock); +fail_id: + return ret; +} + +#ifdef CONFIG_PM_SLEEP + +static int ltc294x_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ltc294x_info *info = i2c_get_clientdata(client); + + cancel_delayed_work(&info->work); + return 0; +} + +static int ltc294x_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ltc294x_info *info = i2c_get_clientdata(client); + + schedule_delayed_work(&info->work, LTC294X_WORK_DELAY * HZ); + return 0; +} + +static SIMPLE_DEV_PM_OPS(ltc294x_pm_ops, ltc294x_suspend, ltc294x_resume); +#define LTC294X_PM_OPS (<c294x_pm_ops) + +#else +#define LTC294X_PM_OPS NULL +#endif /* CONFIG_PM_SLEEP */ + + +static const struct i2c_device_id ltc294x_i2c_id[] = { + {"ltc2941", LTC2941_NUM_REGS}, + {"ltc2943", LTC2943_NUM_REGS}, + { }, +}; +MODULE_DEVICE_TABLE(i2c, ltc294x_i2c_id); + +static struct i2c_driver ltc294x_driver = { + .driver = { + .name = "LTC2941", + .pm = LTC294X_PM_OPS, + }, + .probe = ltc294x_i2c_probe, + .remove = ltc294x_i2c_remove, + .id_table = ltc294x_i2c_id, +}; +module_i2c_driver(ltc294x_driver); + +MODULE_AUTHOR("Auryn Verwegen, Topic Embedded Systems"); +MODULE_AUTHOR("Mike Looijmans, Topic Embedded Products"); +MODULE_DESCRIPTION("LTC2941/LTC2943 Battery Gas Gauge IC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c index 66da691c41cf..1da6c5fbdff5 100644 --- a/drivers/power/max17042_battery.c +++ b/drivers/power/max17042_battery.c @@ -658,7 +658,7 @@ max17042_get_pdata(struct device *dev) } #endif -static struct regmap_config max17042_regmap_config = { +static const struct regmap_config max17042_regmap_config = { .reg_bits = 8, .val_bits = 16, .val_format_endian = REGMAP_ENDIAN_NATIVE, diff --git a/drivers/power/max77693_charger.c b/drivers/power/max77693_charger.c new file mode 100644 index 000000000000..b042970fdeaf --- /dev/null +++ b/drivers/power/max77693_charger.c @@ -0,0 +1,753 @@ +/* + * max77693_charger.c - Battery charger driver for the Maxim 77693 + * + * Copyright (C) 2014 Samsung Electronics + * Krzysztof Kozlowski <k.kozlowski@samsung.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. + * + * 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/module.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/regmap.h> +#include <linux/mfd/max77693.h> +#include <linux/mfd/max77693-private.h> + +static const char *max77693_charger_name = "max77693-charger"; +static const char *max77693_charger_model = "MAX77693"; +static const char *max77693_charger_manufacturer = "Maxim Integrated"; + +struct max77693_charger { + struct device *dev; + struct max77693_dev *max77693; + struct power_supply charger; + + u32 constant_volt; + u32 min_system_volt; + u32 thermal_regulation_temp; + u32 batttery_overcurrent; + u32 charge_input_threshold_volt; +}; + +static int max77693_get_charger_state(struct regmap *regmap) +{ + int state; + unsigned int data; + + if (regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data) < 0) + return POWER_SUPPLY_STATUS_UNKNOWN; + + data &= CHG_DETAILS_01_CHG_MASK; + data >>= CHG_DETAILS_01_CHG_SHIFT; + + switch (data) { + case MAX77693_CHARGING_PREQUALIFICATION: + case MAX77693_CHARGING_FAST_CONST_CURRENT: + case MAX77693_CHARGING_FAST_CONST_VOLTAGE: + case MAX77693_CHARGING_TOP_OFF: + /* In high temp the charging current is reduced, but still charging */ + case MAX77693_CHARGING_HIGH_TEMP: + state = POWER_SUPPLY_STATUS_CHARGING; + break; + case MAX77693_CHARGING_DONE: + state = POWER_SUPPLY_STATUS_FULL; + break; + case MAX77693_CHARGING_TIMER_EXPIRED: + case MAX77693_CHARGING_THERMISTOR_SUSPEND: + state = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case MAX77693_CHARGING_OFF: + case MAX77693_CHARGING_OVER_TEMP: + case MAX77693_CHARGING_WATCHDOG_EXPIRED: + state = POWER_SUPPLY_STATUS_DISCHARGING; + break; + case MAX77693_CHARGING_RESERVED: + default: + state = POWER_SUPPLY_STATUS_UNKNOWN; + } + + return state; +} + +static int max77693_get_charge_type(struct regmap *regmap) +{ + int state; + unsigned int data; + + if (regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data) < 0) + return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + + data &= CHG_DETAILS_01_CHG_MASK; + data >>= CHG_DETAILS_01_CHG_SHIFT; + + switch (data) { + case MAX77693_CHARGING_PREQUALIFICATION: + /* + * Top-off: trickle or fast? In top-off the current varies between + * 100 and 250 mA. It is higher than prequalification current. + */ + case MAX77693_CHARGING_TOP_OFF: + state = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + case MAX77693_CHARGING_FAST_CONST_CURRENT: + case MAX77693_CHARGING_FAST_CONST_VOLTAGE: + /* In high temp the charging current is reduced, but still charging */ + case MAX77693_CHARGING_HIGH_TEMP: + state = POWER_SUPPLY_CHARGE_TYPE_FAST; + break; + case MAX77693_CHARGING_DONE: + case MAX77693_CHARGING_TIMER_EXPIRED: + case MAX77693_CHARGING_THERMISTOR_SUSPEND: + case MAX77693_CHARGING_OFF: + case MAX77693_CHARGING_OVER_TEMP: + case MAX77693_CHARGING_WATCHDOG_EXPIRED: + state = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + case MAX77693_CHARGING_RESERVED: + default: + state = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + } + + return state; +} + +/* + * Supported health statuses: + * - POWER_SUPPLY_HEALTH_DEAD + * - POWER_SUPPLY_HEALTH_GOOD + * - POWER_SUPPLY_HEALTH_OVERVOLTAGE + * - POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE + * - POWER_SUPPLY_HEALTH_UNKNOWN + * - POWER_SUPPLY_HEALTH_UNSPEC_FAILURE + */ +static int max77693_get_battery_health(struct regmap *regmap) +{ + int state; + unsigned int data; + + if (regmap_read(regmap, MAX77693_CHG_REG_CHG_DETAILS_01, &data) < 0) + return POWER_SUPPLY_HEALTH_UNKNOWN; + + data &= CHG_DETAILS_01_BAT_MASK; + data >>= CHG_DETAILS_01_BAT_SHIFT; + + switch (data) { + case MAX77693_BATTERY_NOBAT: + state = POWER_SUPPLY_HEALTH_DEAD; + break; + case MAX77693_BATTERY_PREQUALIFICATION: + case MAX77693_BATTERY_GOOD: + case MAX77693_BATTERY_LOWVOLTAGE: + state = POWER_SUPPLY_HEALTH_GOOD; + break; + case MAX77693_BATTERY_TIMER_EXPIRED: + /* + * Took longer to charge than expected, charging suspended. + * Damaged battery? + */ + state = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; + break; + case MAX77693_BATTERY_OVERVOLTAGE: + state = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + break; + case MAX77693_BATTERY_OVERCURRENT: + state = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + break; + case MAX77693_BATTERY_RESERVED: + default: + state = POWER_SUPPLY_HEALTH_UNKNOWN; + break; + } + + return state; +} + +static int max77693_get_present(struct regmap *regmap) +{ + unsigned int data; + + /* + * Read CHG_INT_OK register. High DETBAT bit here should be + * equal to value 0x0 in CHG_DETAILS_01/BAT field. + */ + regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data); + if (data & CHG_INT_OK_DETBAT_MASK) + return 0; + return 1; +} + +static int max77693_get_online(struct regmap *regmap) +{ + unsigned int data; + + regmap_read(regmap, MAX77693_CHG_REG_CHG_INT_OK, &data); + if (data & CHG_INT_OK_CHGIN_MASK) + return 1; + return 0; +} + +static enum power_supply_property max77693_charger_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + +static int max77693_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct max77693_charger *chg = container_of(psy, + struct max77693_charger, + charger); + struct regmap *regmap = chg->max77693->regmap; + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = max77693_get_charger_state(regmap); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + val->intval = max77693_get_charge_type(regmap); + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = max77693_get_battery_health(regmap); + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = max77693_get_present(regmap); + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = max77693_get_online(regmap); + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = max77693_charger_model; + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = max77693_charger_manufacturer; + break; + default: + return -EINVAL; + } + + return ret; +} + +static ssize_t device_attr_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count, + int (*fn)(struct max77693_charger *, unsigned long)) +{ + struct max77693_charger *chg = dev_get_drvdata(dev); + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + ret = fn(chg, val); + if (ret) + return ret; + + return count; +} + +static ssize_t fast_charge_timer_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct max77693_charger *chg = dev_get_drvdata(dev); + unsigned int data, val; + int ret; + + ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_01, + &data); + if (ret < 0) + return ret; + + data &= CHG_CNFG_01_FCHGTIME_MASK; + data >>= CHG_CNFG_01_FCHGTIME_SHIFT; + switch (data) { + case 0x1 ... 0x7: + /* Starting from 4 hours, step by 2 hours */ + val = 4 + (data - 1) * 2; + break; + case 0x0: + default: + val = 0; + break; + } + + return scnprintf(buf, PAGE_SIZE, "%u\n", val); +} + +static int max77693_set_fast_charge_timer(struct max77693_charger *chg, + unsigned long hours) +{ + unsigned int data; + + /* + * 0x00 - disable + * 0x01 - 4h + * 0x02 - 6h + * ... + * 0x07 - 16h + * Round down odd values. + */ + switch (hours) { + case 4 ... 16: + data = (hours - 4) / 2 + 1; + break; + case 0: + /* Disable */ + data = 0; + break; + default: + return -EINVAL; + } + data <<= CHG_CNFG_01_FCHGTIME_SHIFT; + + return regmap_update_bits(chg->max77693->regmap, + MAX77693_CHG_REG_CHG_CNFG_01, + CHG_CNFG_01_FCHGTIME_MASK, data); +} + +static ssize_t fast_charge_timer_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return device_attr_store(dev, attr, buf, count, + max77693_set_fast_charge_timer); +} + +static ssize_t top_off_threshold_current_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct max77693_charger *chg = dev_get_drvdata(dev); + unsigned int data, val; + int ret; + + ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_03, + &data); + if (ret < 0) + return ret; + + data &= CHG_CNFG_03_TOITH_MASK; + data >>= CHG_CNFG_03_TOITH_SHIFT; + + if (data <= 0x04) + val = 100000 + data * 25000; + else + val = data * 50000; + + return scnprintf(buf, PAGE_SIZE, "%u\n", val); +} + +static int max77693_set_top_off_threshold_current(struct max77693_charger *chg, + unsigned long uamp) +{ + unsigned int data; + + if (uamp < 100000 || uamp > 350000) + return -EINVAL; + + if (uamp <= 200000) + data = (uamp - 100000) / 25000; + else + /* (200000, 350000> */ + data = uamp / 50000; + + data <<= CHG_CNFG_03_TOITH_SHIFT; + + return regmap_update_bits(chg->max77693->regmap, + MAX77693_CHG_REG_CHG_CNFG_03, + CHG_CNFG_03_TOITH_MASK, data); +} + +static ssize_t top_off_threshold_current_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return device_attr_store(dev, attr, buf, count, + max77693_set_top_off_threshold_current); +} + +static ssize_t top_off_timer_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct max77693_charger *chg = dev_get_drvdata(dev); + unsigned int data, val; + int ret; + + ret = regmap_read(chg->max77693->regmap, MAX77693_CHG_REG_CHG_CNFG_03, + &data); + if (ret < 0) + return ret; + + data &= CHG_CNFG_03_TOTIME_MASK; + data >>= CHG_CNFG_03_TOTIME_SHIFT; + + val = data * 10; + + return scnprintf(buf, PAGE_SIZE, "%u\n", val); +} + +static int max77693_set_top_off_timer(struct max77693_charger *chg, + unsigned long minutes) +{ + unsigned int data; + + if (minutes > 70) + return -EINVAL; + + data = minutes / 10; + data <<= CHG_CNFG_03_TOTIME_SHIFT; + + return regmap_update_bits(chg->max77693->regmap, + MAX77693_CHG_REG_CHG_CNFG_03, + CHG_CNFG_03_TOTIME_MASK, data); +} + +static ssize_t top_off_timer_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return device_attr_store(dev, attr, buf, count, + max77693_set_top_off_timer); +} + +static DEVICE_ATTR_RW(fast_charge_timer); +static DEVICE_ATTR_RW(top_off_threshold_current); +static DEVICE_ATTR_RW(top_off_timer); + +static int max77693_set_constant_volt(struct max77693_charger *chg, + unsigned int uvolt) +{ + unsigned int data; + + /* + * 0x00 - 3.650 V + * 0x01 - 3.675 V + * ... + * 0x1b - 4.325 V + * 0x1c - 4.340 V + * 0x1d - 4.350 V + * 0x1e - 4.375 V + * 0x1f - 4.400 V + */ + if (uvolt >= 3650000 && uvolt < 4340000) + data = (uvolt - 3650000) / 25000; + else if (uvolt >= 4340000 && uvolt < 4350000) + data = 0x1c; + else if (uvolt >= 4350000 && uvolt <= 4400000) + data = 0x1d + (uvolt - 4350000) / 25000; + else { + dev_err(chg->dev, "Wrong value for charging constant voltage\n"); + return -EINVAL; + } + + data <<= CHG_CNFG_04_CHGCVPRM_SHIFT; + + dev_dbg(chg->dev, "Charging constant voltage: %u (0x%x)\n", uvolt, + data); + + return regmap_update_bits(chg->max77693->regmap, + MAX77693_CHG_REG_CHG_CNFG_04, + CHG_CNFG_04_CHGCVPRM_MASK, data); +} + +static int max77693_set_min_system_volt(struct max77693_charger *chg, + unsigned int uvolt) +{ + unsigned int data; + + if (uvolt < 3000000 || uvolt > 3700000) { + dev_err(chg->dev, "Wrong value for minimum system regulation voltage\n"); + return -EINVAL; + } + + data = (uvolt - 3000000) / 100000; + + data <<= CHG_CNFG_04_MINVSYS_SHIFT; + + dev_dbg(chg->dev, "Minimum system regulation voltage: %u (0x%x)\n", + uvolt, data); + + return regmap_update_bits(chg->max77693->regmap, + MAX77693_CHG_REG_CHG_CNFG_04, + CHG_CNFG_04_MINVSYS_MASK, data); +} + +static int max77693_set_thermal_regulation_temp(struct max77693_charger *chg, + unsigned int cels) +{ + unsigned int data; + + switch (cels) { + case 70: + case 85: + case 100: + case 115: + data = (cels - 70) / 15; + break; + default: + dev_err(chg->dev, "Wrong value for thermal regulation loop temperature\n"); + return -EINVAL; + } + + data <<= CHG_CNFG_07_REGTEMP_SHIFT; + + dev_dbg(chg->dev, "Thermal regulation loop temperature: %u (0x%x)\n", + cels, data); + + return regmap_update_bits(chg->max77693->regmap, + MAX77693_CHG_REG_CHG_CNFG_07, + CHG_CNFG_07_REGTEMP_MASK, data); +} + +static int max77693_set_batttery_overcurrent(struct max77693_charger *chg, + unsigned int uamp) +{ + unsigned int data; + + if (uamp && (uamp < 2000000 || uamp > 3500000)) { + dev_err(chg->dev, "Wrong value for battery overcurrent\n"); + return -EINVAL; + } + + if (uamp) + data = ((uamp - 2000000) / 250000) + 1; + else + data = 0; /* disable */ + + data <<= CHG_CNFG_12_B2SOVRC_SHIFT; + + dev_dbg(chg->dev, "Battery overcurrent: %u (0x%x)\n", uamp, data); + + return regmap_update_bits(chg->max77693->regmap, + MAX77693_CHG_REG_CHG_CNFG_12, + CHG_CNFG_12_B2SOVRC_MASK, data); +} + +static int max77693_set_charge_input_threshold_volt(struct max77693_charger *chg, + unsigned int uvolt) +{ + unsigned int data; + + switch (uvolt) { + case 4300000: + data = 0x0; + break; + case 4700000: + case 4800000: + case 4900000: + data = (uvolt - 4700000) / 100000; + default: + dev_err(chg->dev, "Wrong value for charge input voltage regulation threshold\n"); + return -EINVAL; + } + + data <<= CHG_CNFG_12_VCHGINREG_SHIFT; + + dev_dbg(chg->dev, "Charge input voltage regulation threshold: %u (0x%x)\n", + uvolt, data); + + return regmap_update_bits(chg->max77693->regmap, + MAX77693_CHG_REG_CHG_CNFG_12, + CHG_CNFG_12_VCHGINREG_MASK, data); +} + +/* + * Sets charger registers to proper and safe default values. + */ +static int max77693_reg_init(struct max77693_charger *chg) +{ + int ret; + unsigned int data; + + /* Unlock charger register protection */ + data = (0x3 << CHG_CNFG_06_CHGPROT_SHIFT); + ret = regmap_update_bits(chg->max77693->regmap, + MAX77693_CHG_REG_CHG_CNFG_06, + CHG_CNFG_06_CHGPROT_MASK, data); + if (ret) { + dev_err(chg->dev, "Error unlocking registers: %d\n", ret); + return ret; + } + + ret = max77693_set_fast_charge_timer(chg, DEFAULT_FAST_CHARGE_TIMER); + if (ret) + return ret; + + ret = max77693_set_top_off_threshold_current(chg, + DEFAULT_TOP_OFF_THRESHOLD_CURRENT); + if (ret) + return ret; + + ret = max77693_set_top_off_timer(chg, DEFAULT_TOP_OFF_TIMER); + if (ret) + return ret; + + ret = max77693_set_constant_volt(chg, chg->constant_volt); + if (ret) + return ret; + + ret = max77693_set_min_system_volt(chg, chg->min_system_volt); + if (ret) + return ret; + + ret = max77693_set_thermal_regulation_temp(chg, + chg->thermal_regulation_temp); + if (ret) + return ret; + + ret = max77693_set_batttery_overcurrent(chg, chg->batttery_overcurrent); + if (ret) + return ret; + + return max77693_set_charge_input_threshold_volt(chg, + chg->charge_input_threshold_volt); +} + +#ifdef CONFIG_OF +static int max77693_dt_init(struct device *dev, struct max77693_charger *chg) +{ + struct device_node *np = dev->of_node; + + if (!np) { + dev_err(dev, "no charger OF node\n"); + return -EINVAL; + } + + if (of_property_read_u32(np, "maxim,constant-microvolt", + &chg->constant_volt)) + chg->constant_volt = DEFAULT_CONSTANT_VOLT; + + if (of_property_read_u32(np, "maxim,min-system-microvolt", + &chg->min_system_volt)) + chg->min_system_volt = DEFAULT_MIN_SYSTEM_VOLT; + + if (of_property_read_u32(np, "maxim,thermal-regulation-celsius", + &chg->thermal_regulation_temp)) + chg->thermal_regulation_temp = DEFAULT_THERMAL_REGULATION_TEMP; + + if (of_property_read_u32(np, "maxim,battery-overcurrent-microamp", + &chg->batttery_overcurrent)) + chg->batttery_overcurrent = DEFAULT_BATTERY_OVERCURRENT; + + if (of_property_read_u32(np, "maxim,charge-input-threshold-microvolt", + &chg->charge_input_threshold_volt)) + chg->charge_input_threshold_volt = + DEFAULT_CHARGER_INPUT_THRESHOLD_VOLT; + + return 0; +} +#else /* CONFIG_OF */ +static int max77693_dt_init(struct device *dev, struct max77693_charger *chg) +{ + return 0; +} +#endif /* CONFIG_OF */ + +static int max77693_charger_probe(struct platform_device *pdev) +{ + struct max77693_charger *chg; + struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent); + int ret; + + chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL); + if (!chg) + return -ENOMEM; + + platform_set_drvdata(pdev, chg); + chg->dev = &pdev->dev; + chg->max77693 = max77693; + + ret = max77693_dt_init(&pdev->dev, chg); + if (ret) + return ret; + + ret = max77693_reg_init(chg); + if (ret) + return ret; + + chg->charger.name = max77693_charger_name; + chg->charger.type = POWER_SUPPLY_TYPE_BATTERY; + chg->charger.properties = max77693_charger_props; + chg->charger.num_properties = ARRAY_SIZE(max77693_charger_props); + chg->charger.get_property = max77693_charger_get_property; + + ret = device_create_file(&pdev->dev, &dev_attr_fast_charge_timer); + if (ret) { + dev_err(&pdev->dev, "failed: create fast charge timer sysfs entry\n"); + goto err; + } + + ret = device_create_file(&pdev->dev, + &dev_attr_top_off_threshold_current); + if (ret) { + dev_err(&pdev->dev, "failed: create top off current sysfs entry\n"); + goto err; + } + + ret = device_create_file(&pdev->dev, &dev_attr_top_off_timer); + if (ret) { + dev_err(&pdev->dev, "failed: create top off timer sysfs entry\n"); + goto err; + } + + ret = power_supply_register(&pdev->dev, &chg->charger); + if (ret) { + dev_err(&pdev->dev, "failed: power supply register\n"); + goto err; + } + + return 0; + +err: + device_remove_file(&pdev->dev, &dev_attr_top_off_timer); + device_remove_file(&pdev->dev, &dev_attr_top_off_threshold_current); + device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer); + + return ret; +} + +static int max77693_charger_remove(struct platform_device *pdev) +{ + struct max77693_charger *chg = platform_get_drvdata(pdev); + + device_remove_file(&pdev->dev, &dev_attr_top_off_timer); + device_remove_file(&pdev->dev, &dev_attr_top_off_threshold_current); + device_remove_file(&pdev->dev, &dev_attr_fast_charge_timer); + + power_supply_unregister(&chg->charger); + + return 0; +} + +static const struct platform_device_id max77693_charger_id[] = { + { "max77693-charger", 0, }, + { } +}; +MODULE_DEVICE_TABLE(platform, max77693_charger_id); + +static struct platform_driver max77693_charger_driver = { + .driver = { + .name = "max77693-charger", + }, + .probe = max77693_charger_probe, + .remove = max77693_charger_remove, + .id_table = max77693_charger_id, +}; +module_platform_driver(max77693_charger_driver); + +MODULE_AUTHOR("Krzysztof Kozlowski <k.kozlowski@samsung.com>"); +MODULE_DESCRIPTION("Maxim 77693 charger driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index 028e76504519..27f6646731b0 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -39,14 +39,13 @@ config POWER_RESET_AXXIA Say Y if you have an Axxia family SoC. config POWER_RESET_BRCMSTB - bool "Broadcom STB reset driver" if COMPILE_TEST - depends on ARM + bool "Broadcom STB reset driver" + depends on ARM || MIPS || COMPILE_TEST default ARCH_BRCMSTB help - This driver provides restart support for ARM-based Broadcom STB - boards. + This driver provides restart support for Broadcom STB boards. - Say Y here if you have an ARM-based Broadcom STB board and you wish + Say Y here if you have a Broadcom STB board and you wish to have restart support. config POWER_RESET_GPIO @@ -104,23 +103,16 @@ config POWER_RESET_QNAP config POWER_RESET_RESTART bool "Restart power-off driver" - depends on ARM help Some boards don't actually have the ability to power off. Instead they restart, and u-boot holds the SoC until the user presses a key. u-boot then boots into Linux. -config POWER_RESET_SUN6I - bool "Allwinner A31 SoC reset driver" - depends on ARCH_SUNXI - help - Reboot support for the Allwinner A31 SoCs. - config POWER_RESET_ST - bool "ST restart power-off driver" + bool "ST restart driver" depends on ARCH_STI help - Power off and reset support for STMicroelectronics boards. + Reset support for STMicroelectronics boards. config POWER_RESET_VERSATILE bool "ARM Versatile family reboot driver" @@ -159,5 +151,11 @@ config POWER_RESET_SYSCON help Reboot support for generic SYSCON mapped register reset. +config POWER_RESET_RMOBILE + tristate "Renesas R-Mobile reset driver" + depends on ARCH_RMOBILE || COMPILE_TEST + help + Reboot support for Renesas R-Mobile and SH-Mobile SoCs. + endif diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 1d4804d6b323..11de15bae52e 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -11,10 +11,10 @@ obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o -obj-$(CONFIG_POWER_RESET_SUN6I) += sun6i-reboot.o obj-$(CONFIG_POWER_RESET_ST) += st-poweroff.o obj-$(CONFIG_POWER_RESET_VERSATILE) += arm-versatile-reboot.o obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o +obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o diff --git a/drivers/power/reset/arm-versatile-reboot.c b/drivers/power/reset/arm-versatile-reboot.c index 5b08bffcf1a8..b208073c887d 100644 --- a/drivers/power/reset/arm-versatile-reboot.c +++ b/drivers/power/reset/arm-versatile-reboot.c @@ -13,16 +13,22 @@ #include <linux/reboot.h> #include <linux/regmap.h> #include <linux/of.h> -#include <asm/system_misc.h> + +#define INTEGRATOR_HDR_CTRL_OFFSET 0x0C +#define INTEGRATOR_HDR_LOCK_OFFSET 0x14 +#define INTEGRATOR_CM_CTRL_RESET (1 << 3) #define REALVIEW_SYS_LOCK_OFFSET 0x20 -#define REALVIEW_SYS_LOCK_VAL 0xA05F #define REALVIEW_SYS_RESETCTL_OFFSET 0x40 +/* Magic unlocking token used on all Versatile boards */ +#define VERSATILE_LOCK_VAL 0xA05F + /* * We detect the different syscon types from the compatible strings. */ enum versatile_reboot { + INTEGRATOR_REBOOT_CM, REALVIEW_REBOOT_EB, REALVIEW_REBOOT_PB1176, REALVIEW_REBOOT_PB11MP, @@ -36,6 +42,10 @@ static enum versatile_reboot versatile_reboot_type; static const struct of_device_id versatile_reboot_of_match[] = { { + .compatible = "arm,core-module-integrator", + .data = (void *)INTEGRATOR_REBOOT_CM + }, + { .compatible = "arm,realview-eb-syscon", .data = (void *)REALVIEW_REBOOT_EB, }, @@ -55,31 +65,47 @@ static const struct of_device_id versatile_reboot_of_match[] = { .compatible = "arm,realview-pbx-syscon", .data = (void *)REALVIEW_REBOOT_PBX, }, + {}, }; -static void versatile_reboot(enum reboot_mode mode, const char *cmd) +static int versatile_reboot(struct notifier_block *this, unsigned long mode, + void *cmd) { /* Unlock the reset register */ - regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, - REALVIEW_SYS_LOCK_VAL); /* Then hit reset on the different machines */ switch (versatile_reboot_type) { + case INTEGRATOR_REBOOT_CM: + regmap_write(syscon_regmap, INTEGRATOR_HDR_LOCK_OFFSET, + VERSATILE_LOCK_VAL); + regmap_update_bits(syscon_regmap, + INTEGRATOR_HDR_CTRL_OFFSET, + INTEGRATOR_CM_CTRL_RESET, + INTEGRATOR_CM_CTRL_RESET); + break; case REALVIEW_REBOOT_EB: + regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, + VERSATILE_LOCK_VAL); regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, 0x0008); break; case REALVIEW_REBOOT_PB1176: + regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, + VERSATILE_LOCK_VAL); regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, 0x0100); break; case REALVIEW_REBOOT_PB11MP: case REALVIEW_REBOOT_PBA8: + regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, + VERSATILE_LOCK_VAL); regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, 0x0000); regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, 0x0004); break; case REALVIEW_REBOOT_PBX: + regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET, + VERSATILE_LOCK_VAL); regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, 0x00f0); regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET, @@ -87,12 +113,20 @@ static void versatile_reboot(enum reboot_mode mode, const char *cmd) break; } dsb(); + + return NOTIFY_DONE; } +static struct notifier_block versatile_reboot_nb = { + .notifier_call = versatile_reboot, + .priority = 192, +}; + static int __init versatile_reboot_probe(void) { const struct of_device_id *reboot_id; struct device_node *np; + int err; np = of_find_matching_node_and_match(NULL, versatile_reboot_of_match, &reboot_id); @@ -104,7 +138,10 @@ static int __init versatile_reboot_probe(void) if (IS_ERR(syscon_regmap)) return PTR_ERR(syscon_regmap); - arm_pm_restart = versatile_reboot; + err = register_restart_handler(&versatile_reboot_nb); + if (err) + return err; + pr_info("versatile reboot driver registered\n"); return 0; } diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c index c61000333bb9..4b72ea51c364 100644 --- a/drivers/power/reset/at91-poweroff.c +++ b/drivers/power/reset/at91-poweroff.c @@ -71,10 +71,11 @@ static void at91_poweroff(void) writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR); } -const enum wakeup_type at91_poweroff_get_wakeup_mode(struct device_node *np) +static int at91_poweroff_get_wakeup_mode(struct device_node *np) { const char *pm; - int err, i; + unsigned int i; + int err; err = of_property_read_string(np, "atmel,wakeup-mode", &pm); if (err < 0) @@ -90,7 +91,7 @@ const enum wakeup_type at91_poweroff_get_wakeup_mode(struct device_node *np) static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; - enum wakeup_type wakeup_mode; + int wakeup_mode; u32 mode = 0, tmp; wakeup_mode = at91_poweroff_get_wakeup_mode(np); diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c index 69a75d99ae92..13584e24736a 100644 --- a/drivers/power/reset/at91-reset.c +++ b/drivers/power/reset/at91-reset.c @@ -17,8 +17,6 @@ #include <linux/platform_device.h> #include <linux/reboot.h> -#include <asm/system_misc.h> - #include <soc/at91/at91sam9_ddrsdr.h> #include <soc/at91/at91sam9_sdramc.h> @@ -54,7 +52,8 @@ static void __iomem *at91_ramc_base[2], *at91_rstc_base; * reset register it can be left driving the data bus and * killing the chance of a subsequent boot from NAND */ -static void at91sam9260_restart(enum reboot_mode mode, const char *cmd) +static int at91sam9260_restart(struct notifier_block *this, unsigned long mode, + void *cmd) { asm volatile( /* Align to cache lines */ @@ -76,9 +75,12 @@ static void at91sam9260_restart(enum reboot_mode mode, const char *cmd) "r" (1), "r" (AT91_SDRAMC_LPCB_POWER_DOWN), "r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST)); + + return NOTIFY_DONE; } -static void at91sam9g45_restart(enum reboot_mode mode, const char *cmd) +static int at91sam9g45_restart(struct notifier_block *this, unsigned long mode, + void *cmd) { asm volatile( /* @@ -117,6 +119,8 @@ static void at91sam9g45_restart(enum reboot_mode mode, const char *cmd) "r" (AT91_DDRSDRC_LPCB_POWER_DOWN), "r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST) : "r0"); + + return NOTIFY_DONE; } static void __init at91_reset_status(struct platform_device *pdev) @@ -161,6 +165,10 @@ static struct of_device_id at91_reset_of_match[] = { { /* sentinel */ } }; +static struct notifier_block at91_restart_nb = { + .priority = 192, +}; + static int at91_reset_of_probe(struct platform_device *pdev) { const struct of_device_id *match; @@ -183,9 +191,8 @@ static int at91_reset_of_probe(struct platform_device *pdev) } match = of_match_node(at91_reset_of_match, pdev->dev.of_node); - arm_pm_restart = match->data; - - return 0; + at91_restart_nb.notifier_call = match->data; + return register_restart_handler(&at91_restart_nb); } static int at91_reset_platform_probe(struct platform_device *pdev) @@ -212,10 +219,11 @@ static int at91_reset_platform_probe(struct platform_device *pdev) } match = platform_get_device_id(pdev); - arm_pm_restart = (void (*)(enum reboot_mode, const char*)) - match->driver_data; + at91_restart_nb.notifier_call = + (int (*)(struct notifier_block *, + unsigned long, void *)) match->driver_data; - return 0; + return register_restart_handler(&at91_restart_nb); } static int at91_reset_probe(struct platform_device *pdev) diff --git a/drivers/power/reset/brcmstb-reboot.c b/drivers/power/reset/brcmstb-reboot.c index 100606f9b3dc..884b53c483c0 100644 --- a/drivers/power/reset/brcmstb-reboot.c +++ b/drivers/power/reset/brcmstb-reboot.c @@ -11,6 +11,7 @@ * GNU General Public License for more details. */ +#include <linux/bitops.h> #include <linux/device.h> #include <linux/errno.h> #include <linux/init.h> @@ -34,13 +35,20 @@ static struct regmap *regmap; static u32 rst_src_en; static u32 sw_mstr_rst; +struct reset_reg_mask { + u32 rst_src_en_mask; + u32 sw_mstr_rst_mask; +}; + +static const struct reset_reg_mask *reset_masks; + static int brcmstb_restart_handler(struct notifier_block *this, unsigned long mode, void *cmd) { int rc; u32 tmp; - rc = regmap_write(regmap, rst_src_en, 1); + rc = regmap_write(regmap, rst_src_en, reset_masks->rst_src_en_mask); if (rc) { pr_err("failed to write rst_src_en (%d)\n", rc); return NOTIFY_DONE; @@ -52,7 +60,7 @@ static int brcmstb_restart_handler(struct notifier_block *this, return NOTIFY_DONE; } - rc = regmap_write(regmap, sw_mstr_rst, 1); + rc = regmap_write(regmap, sw_mstr_rst, reset_masks->sw_mstr_rst_mask); if (rc) { pr_err("failed to write sw_mstr_rst (%d)\n", rc); return NOTIFY_DONE; @@ -75,10 +83,34 @@ static struct notifier_block brcmstb_restart_nb = { .priority = 128, }; +static const struct reset_reg_mask reset_bits_40nm = { + .rst_src_en_mask = BIT(0), + .sw_mstr_rst_mask = BIT(0), +}; + +static const struct reset_reg_mask reset_bits_65nm = { + .rst_src_en_mask = BIT(3), + .sw_mstr_rst_mask = BIT(31), +}; + +static const struct of_device_id of_match[] = { + { .compatible = "brcm,brcmstb-reboot", .data = &reset_bits_40nm }, + { .compatible = "brcm,bcm7038-reboot", .data = &reset_bits_65nm }, + {}, +}; + static int brcmstb_reboot_probe(struct platform_device *pdev) { int rc; struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_id; + + of_id = of_match_node(of_match, np); + if (!of_id) { + pr_err("failed to look up compatible string\n"); + return -EINVAL; + } + reset_masks = of_id->data; regmap = syscon_regmap_lookup_by_phandle(np, "syscon"); if (IS_ERR(regmap)) { @@ -108,11 +140,6 @@ static int brcmstb_reboot_probe(struct platform_device *pdev) return rc; } -static const struct of_device_id of_match[] = { - { .compatible = "brcm,brcmstb-reboot", }, - {}, -}; - static struct platform_driver brcmstb_reboot_driver = { .probe = brcmstb_reboot_probe, .driver = { diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c index 34f38a3dc3ff..7ef193b6f7fe 100644 --- a/drivers/power/reset/ltc2952-poweroff.c +++ b/drivers/power/reset/ltc2952-poweroff.c @@ -32,7 +32,9 @@ * - trigger (input) * A level change indicates the shut-down trigger. If it's state reverts * within the time-out defined by trigger_delay, the shut down is not - * executed. + * executed. If no pin is assigned to this input, the driver will start the + * watchdog toggle immediately. The chip will only power off the system if + * it is requested to do so through the kill line. * * - watchdog (output) * Once a shut down is triggered, the driver will toggle this signal, @@ -63,7 +65,7 @@ #include <linux/gpio/consumer.h> #include <linux/reboot.h> -struct ltc2952_poweroff_data { +struct ltc2952_poweroff { struct hrtimer timer_trigger; struct hrtimer timer_wde; @@ -72,22 +74,21 @@ struct ltc2952_poweroff_data { struct device *dev; - unsigned int virq; + struct gpio_desc *gpio_trigger; + struct gpio_desc *gpio_watchdog; + struct gpio_desc *gpio_kill; - /** - * 0: trigger - * 1: watchdog - * 2: kill - */ - struct gpio_desc *gpio[3]; + bool kernel_panic; + struct notifier_block panic_notifier; }; -static int ltc2952_poweroff_panic; -static struct ltc2952_poweroff_data *ltc2952_data; +#define to_ltc2952(p, m) container_of(p, struct ltc2952_poweroff, m) -#define POWERPATH_IO_TRIGGER 0 -#define POWERPATH_IO_WATCHDOG 1 -#define POWERPATH_IO_KILL 2 +/* + * This global variable is only needed for pm_power_off. We should + * remove it entirely once we don't need the global state anymore. + */ +static struct ltc2952_poweroff *ltc2952_data; /** * ltc2952_poweroff_timer_wde - Timer callback @@ -103,29 +104,24 @@ static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer) ktime_t now; int state; unsigned long overruns; + struct ltc2952_poweroff *data = to_ltc2952(timer, timer_wde); - if (ltc2952_poweroff_panic) + if (data->kernel_panic) return HRTIMER_NORESTART; - state = gpiod_get_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG]); - gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], !state); + state = gpiod_get_value(data->gpio_watchdog); + gpiod_set_value(data->gpio_watchdog, !state); now = hrtimer_cb_get_time(timer); - overruns = hrtimer_forward(timer, now, ltc2952_data->wde_interval); + overruns = hrtimer_forward(timer, now, data->wde_interval); return HRTIMER_RESTART; } -static enum hrtimer_restart ltc2952_poweroff_timer_trigger( - struct hrtimer *timer) +static void ltc2952_poweroff_start_wde(struct ltc2952_poweroff *data) { - int ret; - - ret = hrtimer_start(<c2952_data->timer_wde, - ltc2952_data->wde_interval, HRTIMER_MODE_REL); - - if (ret) { - dev_err(ltc2952_data->dev, "unable to start the timer\n"); + if (hrtimer_start(&data->timer_wde, data->wde_interval, + HRTIMER_MODE_REL)) { /* * The device will not toggle the watchdog reset, * thus shut down is only safe if the PowerPath controller @@ -134,10 +130,17 @@ static enum hrtimer_restart ltc2952_poweroff_timer_trigger( * * Only sending a warning as the system will power-off anyway */ + dev_err(data->dev, "unable to start the timer\n"); } +} - dev_info(ltc2952_data->dev, "executing shutdown\n"); +static enum hrtimer_restart +ltc2952_poweroff_timer_trigger(struct hrtimer *timer) +{ + struct ltc2952_poweroff *data = to_ltc2952(timer, timer_trigger); + ltc2952_poweroff_start_wde(data); + dev_info(data->dev, "executing shutdown\n"); orderly_poweroff(true); return HRTIMER_NORESTART; @@ -154,180 +157,161 @@ static enum hrtimer_restart ltc2952_poweroff_timer_trigger( */ static irqreturn_t ltc2952_poweroff_handler(int irq, void *dev_id) { - int ret; - struct ltc2952_poweroff_data *data = dev_id; + struct ltc2952_poweroff *data = dev_id; - if (ltc2952_poweroff_panic) - goto irq_ok; - - if (hrtimer_active(&data->timer_wde)) { + if (data->kernel_panic || hrtimer_active(&data->timer_wde)) { /* shutdown is already triggered, nothing to do any more */ - goto irq_ok; + return IRQ_HANDLED; } - if (!hrtimer_active(&data->timer_trigger)) { - ret = hrtimer_start(&data->timer_trigger, data->trigger_delay, - HRTIMER_MODE_REL); - - if (ret) + if (gpiod_get_value(data->gpio_trigger)) { + if (hrtimer_start(&data->timer_trigger, data->trigger_delay, + HRTIMER_MODE_REL)) dev_err(data->dev, "unable to start the wait timer\n"); } else { - ret = hrtimer_cancel(&data->timer_trigger); + hrtimer_cancel(&data->timer_trigger); /* omitting return value check, timer should have been valid */ } - -irq_ok: return IRQ_HANDLED; } static void ltc2952_poweroff_kill(void) { - gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_KILL], 1); -} - -static int ltc2952_poweroff_suspend(struct platform_device *pdev, - pm_message_t state) -{ - return -ENOSYS; -} - -static int ltc2952_poweroff_resume(struct platform_device *pdev) -{ - return -ENOSYS; + gpiod_set_value(ltc2952_data->gpio_kill, 1); } -static void ltc2952_poweroff_default(struct ltc2952_poweroff_data *data) +static void ltc2952_poweroff_default(struct ltc2952_poweroff *data) { - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(data->gpio); i++) - data->gpio[i] = NULL; - data->wde_interval = ktime_set(0, 300L*1E6L); data->trigger_delay = ktime_set(2, 500L*1E6L); hrtimer_init(&data->timer_trigger, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - data->timer_trigger.function = <c2952_poweroff_timer_trigger; + data->timer_trigger.function = ltc2952_poweroff_timer_trigger; hrtimer_init(&data->timer_wde, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - data->timer_wde.function = <c2952_poweroff_timer_wde; + data->timer_wde.function = ltc2952_poweroff_timer_wde; } static int ltc2952_poweroff_init(struct platform_device *pdev) { - int ret, virq; - unsigned int i; - struct ltc2952_poweroff_data *data; - - static char *name[] = { - "trigger", - "watchdog", - "kill", - NULL - }; - - data = ltc2952_data; - ltc2952_poweroff_default(ltc2952_data); - - for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) { - ltc2952_data->gpio[i] = gpiod_get(&pdev->dev, name[i]); + int ret; + struct ltc2952_poweroff *data = platform_get_drvdata(pdev); - if (IS_ERR(ltc2952_data->gpio[i])) { - ret = PTR_ERR(ltc2952_data->gpio[i]); - dev_err(&pdev->dev, - "unable to claim the following gpio: %s\n", - name[i]); - goto err_io; - } - } + ltc2952_poweroff_default(data); - ret = gpiod_direction_output( - ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], 0); - if (ret) { - dev_err(&pdev->dev, "unable to use watchdog-gpio as output\n"); - goto err_io; + data->gpio_watchdog = devm_gpiod_get(&pdev->dev, "watchdog", + GPIOD_OUT_LOW); + if (IS_ERR(data->gpio_watchdog)) { + ret = PTR_ERR(data->gpio_watchdog); + dev_err(&pdev->dev, "unable to claim gpio \"watchdog\"\n"); + return ret; } - ret = gpiod_direction_output(ltc2952_data->gpio[POWERPATH_IO_KILL], 0); - if (ret) { - dev_err(&pdev->dev, "unable to use kill-gpio as output\n"); - goto err_io; + data->gpio_kill = devm_gpiod_get(&pdev->dev, "kill", GPIOD_OUT_LOW); + if (IS_ERR(data->gpio_kill)) { + ret = PTR_ERR(data->gpio_kill); + dev_err(&pdev->dev, "unable to claim gpio \"kill\"\n"); + return ret; } - virq = gpiod_to_irq(ltc2952_data->gpio[POWERPATH_IO_TRIGGER]); - if (virq < 0) { - dev_err(&pdev->dev, "cannot map GPIO as interrupt"); - goto err_io; + data->gpio_trigger = devm_gpiod_get(&pdev->dev, "trigger", GPIOD_IN); + if (IS_ERR(data->gpio_trigger)) { + /* + * It's not a problem if the trigger gpio isn't available, but + * it is worth a warning if its use was defined in the device + * tree. + */ + if (PTR_ERR(data->gpio_trigger) != -ENOENT) + dev_err(&pdev->dev, + "unable to claim gpio \"trigger\"\n"); + data->gpio_trigger = NULL; } - ltc2952_data->virq = virq; - ret = request_irq(virq, - ltc2952_poweroff_handler, - (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING), - "ltc2952-poweroff", - ltc2952_data - ); - - if (ret) { - dev_err(&pdev->dev, "cannot configure an interrupt handler\n"); - goto err_io; + if (devm_request_irq(&pdev->dev, gpiod_to_irq(data->gpio_trigger), + ltc2952_poweroff_handler, + (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING), + "ltc2952-poweroff", + data)) { + /* + * Some things may have happened: + * - No trigger input was defined + * - Claiming the GPIO failed + * - We could not map to an IRQ + * - We couldn't register an interrupt handler + * + * None of these really are problems, but all of them + * disqualify the push button from controlling the power. + * + * It is therefore important to note that if the ltc2952 + * detects a button press for long enough, it will still start + * its own powerdown window and cut the power on us if we don't + * start the watchdog trigger. + */ + if (data->gpio_trigger) { + dev_warn(&pdev->dev, + "unable to configure the trigger interrupt\n"); + devm_gpiod_put(&pdev->dev, data->gpio_trigger); + data->gpio_trigger = NULL; + } + dev_info(&pdev->dev, + "power down trigger input will not be used\n"); + ltc2952_poweroff_start_wde(data); } return 0; +} -err_io: - for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) - if (ltc2952_data->gpio[i]) - gpiod_put(ltc2952_data->gpio[i]); +static int ltc2952_poweroff_notify_panic(struct notifier_block *nb, + unsigned long code, void *unused) +{ + struct ltc2952_poweroff *data = to_ltc2952(nb, panic_notifier); - return ret; + data->kernel_panic = true; + return NOTIFY_DONE; } static int ltc2952_poweroff_probe(struct platform_device *pdev) { int ret; + struct ltc2952_poweroff *data; if (pm_power_off) { dev_err(&pdev->dev, "pm_power_off already registered"); return -EBUSY; } - ltc2952_data = kzalloc(sizeof(*ltc2952_data), GFP_KERNEL); - if (!ltc2952_data) + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) return -ENOMEM; - ltc2952_data->dev = &pdev->dev; + data->dev = &pdev->dev; + platform_set_drvdata(pdev, data); ret = ltc2952_poweroff_init(pdev); if (ret) - goto err; + return ret; - pm_power_off = <c2952_poweroff_kill; + /* TODO: remove ltc2952_data */ + ltc2952_data = data; + pm_power_off = ltc2952_poweroff_kill; + data->panic_notifier.notifier_call = ltc2952_poweroff_notify_panic; + atomic_notifier_chain_register(&panic_notifier_list, + &data->panic_notifier); dev_info(&pdev->dev, "probe successful\n"); return 0; - -err: - kfree(ltc2952_data); - return ret; } static int ltc2952_poweroff_remove(struct platform_device *pdev) { - unsigned int i; + struct ltc2952_poweroff *data = platform_get_drvdata(pdev); pm_power_off = NULL; - - if (ltc2952_data) { - free_irq(ltc2952_data->virq, ltc2952_data); - - for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) - gpiod_put(ltc2952_data->gpio[i]); - - kfree(ltc2952_data); - } - + hrtimer_cancel(&data->timer_trigger); + hrtimer_cancel(&data->timer_wde); + atomic_notifier_chain_unregister(&panic_notifier_list, + &data->panic_notifier); return 0; } @@ -344,41 +328,9 @@ static struct platform_driver ltc2952_poweroff_driver = { .name = "ltc2952-poweroff", .of_match_table = of_ltc2952_poweroff_match, }, - .suspend = ltc2952_poweroff_suspend, - .resume = ltc2952_poweroff_resume, -}; - -static int ltc2952_poweroff_notify_panic(struct notifier_block *nb, - unsigned long code, void *unused) -{ - ltc2952_poweroff_panic = 1; - return NOTIFY_DONE; -} - -static struct notifier_block ltc2952_poweroff_panic_nb = { - .notifier_call = ltc2952_poweroff_notify_panic, }; -static int __init ltc2952_poweroff_platform_init(void) -{ - ltc2952_poweroff_panic = 0; - - atomic_notifier_chain_register(&panic_notifier_list, - <c2952_poweroff_panic_nb); - - return platform_driver_register(<c2952_poweroff_driver); -} - -static void __exit ltc2952_poweroff_platform_exit(void) -{ - atomic_notifier_chain_unregister(&panic_notifier_list, - <c2952_poweroff_panic_nb); - - platform_driver_unregister(<c2952_poweroff_driver); -} - -module_init(ltc2952_poweroff_platform_init); -module_exit(ltc2952_poweroff_platform_exit); +module_platform_driver(ltc2952_poweroff_driver); MODULE_AUTHOR("René Moll <rene.moll@xsens.com>"); MODULE_DESCRIPTION("LTC PowerPath power-off driver"); diff --git a/drivers/power/reset/restart-poweroff.c b/drivers/power/reset/restart-poweroff.c index f46f2c2e4648..41b22c4d5236 100644 --- a/drivers/power/reset/restart-poweroff.c +++ b/drivers/power/reset/restart-poweroff.c @@ -16,7 +16,6 @@ #include <linux/of_platform.h> #include <linux/module.h> #include <linux/reboot.h> -#include <asm/system_misc.h> static void restart_poweroff_do_poweroff(void) { diff --git a/drivers/power/reset/rmobile-reset.c b/drivers/power/reset/rmobile-reset.c new file mode 100644 index 000000000000..e6569df76941 --- /dev/null +++ b/drivers/power/reset/rmobile-reset.c @@ -0,0 +1,91 @@ +/* + * Renesas R-Mobile Reset Driver + * + * Copyright (C) 2014 Glider bvba + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/io.h> +#include <linux/module.h> +#include <linux/notifier.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/printk.h> +#include <linux/reboot.h> + +/* SYSC Register Bank 2 */ +#define RESCNT2 0x20 /* Reset Control Register 2 */ + +/* Reset Control Register 2 */ +#define RESCNT2_PRES 0x80000000 /* Soft power-on reset */ + +static void __iomem *sysc_base2; + +static int rmobile_reset_handler(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + pr_debug("%s %lu\n", __func__, mode); + + /* Let's assume we have acquired the HPB semaphore */ + writel(RESCNT2_PRES, sysc_base2 + RESCNT2); + + return NOTIFY_DONE; +} + +static struct notifier_block rmobile_reset_nb = { + .notifier_call = rmobile_reset_handler, + .priority = 192, +}; + +static int rmobile_reset_probe(struct platform_device *pdev) +{ + int error; + + sysc_base2 = of_iomap(pdev->dev.of_node, 1); + if (!sysc_base2) + return -ENODEV; + + error = register_restart_handler(&rmobile_reset_nb); + if (error) { + dev_err(&pdev->dev, + "cannot register restart handler (err=%d)\n", error); + goto fail_unmap; + } + + return 0; + +fail_unmap: + iounmap(sysc_base2); + return error; +} + +static int rmobile_reset_remove(struct platform_device *pdev) +{ + unregister_restart_handler(&rmobile_reset_nb); + iounmap(sysc_base2); + return 0; +} + +static const struct of_device_id rmobile_reset_of_match[] = { + { .compatible = "renesas,sysc-rmobile", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rmobile_reset_of_match); + +static struct platform_driver rmobile_reset_driver = { + .probe = rmobile_reset_probe, + .remove = rmobile_reset_remove, + .driver = { + .name = "rmobile_reset", + .of_match_table = rmobile_reset_of_match, + }, +}; + +module_platform_driver(rmobile_reset_driver); + +MODULE_DESCRIPTION("Renesas R-Mobile Reset Driver"); +MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/reset/st-poweroff.c b/drivers/power/reset/st-poweroff.c index a0acf25ee2a2..27383de9caa8 100644 --- a/drivers/power/reset/st-poweroff.c +++ b/drivers/power/reset/st-poweroff.c @@ -15,10 +15,9 @@ #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/mfd/syscon.h> +#include <linux/reboot.h> #include <linux/regmap.h> -#include <asm/system_misc.h> - struct reset_syscfg { struct regmap *regmap; /* syscfg used for reset */ @@ -75,7 +74,8 @@ static struct reset_syscfg stid127_reset = { static struct reset_syscfg *st_restart_syscfg; -static void st_restart(enum reboot_mode reboot_mode, const char *cmd) +static int st_restart(struct notifier_block *this, unsigned long mode, + void *cmd) { /* reset syscfg updated */ regmap_update_bits(st_restart_syscfg->regmap, @@ -88,8 +88,15 @@ static void st_restart(enum reboot_mode reboot_mode, const char *cmd) st_restart_syscfg->offset_rst_msk, st_restart_syscfg->mask_rst_msk, 0); + + return NOTIFY_DONE; } +static struct notifier_block st_restart_nb = { + .notifier_call = st_restart, + .priority = 192, +}; + static struct of_device_id st_reset_of_match[] = { { .compatible = "st,stih415-restart", @@ -126,9 +133,7 @@ static int st_reset_probe(struct platform_device *pdev) return PTR_ERR(st_restart_syscfg->regmap); } - arm_pm_restart = st_restart; - - return 0; + return register_restart_handler(&st_restart_nb); } static struct platform_driver st_reset_driver = { diff --git a/drivers/power/reset/sun6i-reboot.c b/drivers/power/reset/sun6i-reboot.c deleted file mode 100644 index af2cd7ff2fe8..000000000000 --- a/drivers/power/reset/sun6i-reboot.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Allwinner A31 SoCs reset code - * - * Copyright (C) 2012-2014 Maxime Ripard - * - * Maxime Ripard <maxime.ripard@free-electrons.com> - * - * 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/delay.h> -#include <linux/io.h> -#include <linux/module.h> -#include <linux/of_address.h> -#include <linux/platform_device.h> -#include <linux/reboot.h> - -#include <asm/system_misc.h> - -#define SUN6I_WATCHDOG1_IRQ_REG 0x00 -#define SUN6I_WATCHDOG1_CTRL_REG 0x10 -#define SUN6I_WATCHDOG1_CTRL_RESTART BIT(0) -#define SUN6I_WATCHDOG1_CONFIG_REG 0x14 -#define SUN6I_WATCHDOG1_CONFIG_RESTART BIT(0) -#define SUN6I_WATCHDOG1_CONFIG_IRQ BIT(1) -#define SUN6I_WATCHDOG1_MODE_REG 0x18 -#define SUN6I_WATCHDOG1_MODE_ENABLE BIT(0) - -static void __iomem *wdt_base; - -static void sun6i_wdt_restart(enum reboot_mode mode, const char *cmd) -{ - if (!wdt_base) - return; - - /* Disable interrupts */ - writel(0, wdt_base + SUN6I_WATCHDOG1_IRQ_REG); - - /* We want to disable the IRQ and just reset the whole system */ - writel(SUN6I_WATCHDOG1_CONFIG_RESTART, - wdt_base + SUN6I_WATCHDOG1_CONFIG_REG); - - /* Enable timer. The default and lowest interval value is 0.5s */ - writel(SUN6I_WATCHDOG1_MODE_ENABLE, - wdt_base + SUN6I_WATCHDOG1_MODE_REG); - - /* Restart the watchdog. */ - writel(SUN6I_WATCHDOG1_CTRL_RESTART, - wdt_base + SUN6I_WATCHDOG1_CTRL_REG); - - while (1) { - mdelay(5); - writel(SUN6I_WATCHDOG1_MODE_ENABLE, - wdt_base + SUN6I_WATCHDOG1_MODE_REG); - } -} - -static int sun6i_reboot_probe(struct platform_device *pdev) -{ - wdt_base = of_iomap(pdev->dev.of_node, 0); - if (!wdt_base) { - WARN(1, "failed to map watchdog base address"); - return -ENODEV; - } - - arm_pm_restart = sun6i_wdt_restart; - - return 0; -} - -static struct of_device_id sun6i_reboot_of_match[] = { - { .compatible = "allwinner,sun6i-a31-wdt" }, - {} -}; - -static struct platform_driver sun6i_reboot_driver = { - .probe = sun6i_reboot_probe, - .driver = { - .name = "sun6i-reboot", - .of_match_table = sun6i_reboot_of_match, - }, -}; -module_platform_driver(sun6i_reboot_driver); diff --git a/drivers/power/reset/vexpress-poweroff.c b/drivers/power/reset/vexpress-poweroff.c index 9dfc9cee3232..be12d9b92957 100644 --- a/drivers/power/reset/vexpress-poweroff.c +++ b/drivers/power/reset/vexpress-poweroff.c @@ -111,23 +111,20 @@ static int _vexpress_register_restart_handler(struct device *dev) static int vexpress_reset_probe(struct platform_device *pdev) { - enum vexpress_reset_func func; const struct of_device_id *match = of_match_device(vexpress_reset_of_match, &pdev->dev); struct regmap *regmap; int ret = 0; - if (match) - func = (enum vexpress_reset_func)match->data; - else - func = pdev->id_entry->driver_data; + if (!match) + return -EINVAL; regmap = devm_regmap_init_vexpress_config(&pdev->dev); if (IS_ERR(regmap)) return PTR_ERR(regmap); dev_set_drvdata(&pdev->dev, regmap); - switch (func) { + switch ((enum vexpress_reset_func)match->data) { case FUNC_SHUTDOWN: vexpress_power_off_device = &pdev->dev; pm_power_off = vexpress_power_off; @@ -144,20 +141,12 @@ static int vexpress_reset_probe(struct platform_device *pdev) return ret; } -static const struct platform_device_id vexpress_reset_id_table[] = { - { .name = "vexpress-reset", .driver_data = FUNC_RESET, }, - { .name = "vexpress-shutdown", .driver_data = FUNC_SHUTDOWN, }, - { .name = "vexpress-reboot", .driver_data = FUNC_REBOOT, }, - {} -}; - static struct platform_driver vexpress_reset_driver = { .probe = vexpress_reset_probe, .driver = { .name = "vexpress-reset", .of_match_table = vexpress_reset_of_match, }, - .id_table = vexpress_reset_id_table, }; static int __init vexpress_reset_init(void) diff --git a/drivers/power/rt5033_battery.c b/drivers/power/rt5033_battery.c new file mode 100644 index 000000000000..7b898f41c595 --- /dev/null +++ b/drivers/power/rt5033_battery.c @@ -0,0 +1,177 @@ +/* + * Fuel gauge driver for Richtek RT5033 + * + * Copyright (C) 2014 Samsung Electronics, Co., Ltd. + * Author: Beomho Seo <beomho.seo@samsung.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 bythe Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/mfd/rt5033-private.h> +#include <linux/mfd/rt5033.h> + +static int rt5033_battery_get_capacity(struct i2c_client *client) +{ + struct rt5033_battery *battery = i2c_get_clientdata(client); + u32 msb; + + regmap_read(battery->regmap, RT5033_FUEL_REG_SOC_H, &msb); + + return msb; +} + +static int rt5033_battery_get_present(struct i2c_client *client) +{ + struct rt5033_battery *battery = i2c_get_clientdata(client); + u32 val; + + regmap_read(battery->regmap, RT5033_FUEL_REG_CONFIG_L, &val); + + return (val & RT5033_FUEL_BAT_PRESENT) ? true : false; +} + +static int rt5033_battery_get_watt_prop(struct i2c_client *client, + enum power_supply_property psp) +{ + struct rt5033_battery *battery = i2c_get_clientdata(client); + unsigned int regh, regl; + int ret; + u32 msb, lsb; + + switch (psp) { + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + regh = RT5033_FUEL_REG_VBAT_H; + regl = RT5033_FUEL_REG_VBAT_L; + break; + case POWER_SUPPLY_PROP_VOLTAGE_AVG: + regh = RT5033_FUEL_REG_AVG_VOLT_H; + regl = RT5033_FUEL_REG_AVG_VOLT_L; + break; + case POWER_SUPPLY_PROP_VOLTAGE_OCV: + regh = RT5033_FUEL_REG_OCV_H; + regl = RT5033_FUEL_REG_OCV_L; + break; + default: + return -EINVAL; + } + + regmap_read(battery->regmap, regh, &msb); + regmap_read(battery->regmap, regl, &lsb); + + ret = ((msb << 4) + (lsb >> 4)) * 1250 / 1000; + + return ret; +} + +static int rt5033_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct rt5033_battery *battery = container_of(psy, + struct rt5033_battery, psy); + + switch (psp) { + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + case POWER_SUPPLY_PROP_VOLTAGE_AVG: + case POWER_SUPPLY_PROP_VOLTAGE_OCV: + val->intval = rt5033_battery_get_watt_prop(battery->client, + psp); + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = rt5033_battery_get_present(battery->client); + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = rt5033_battery_get_capacity(battery->client); + break; + default: + return -EINVAL; + } + return 0; +} + +static enum power_supply_property rt5033_battery_props[] = { + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_AVG, + POWER_SUPPLY_PROP_VOLTAGE_OCV, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CAPACITY, +}; + +static struct regmap_config rt5033_battery_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = RT5033_FUEL_REG_END, +}; + +static int rt5033_battery_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct rt5033_battery *battery; + u32 ret; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) + return -EIO; + + battery = devm_kzalloc(&client->dev, sizeof(*battery), GFP_KERNEL); + if (!battery) + return -EINVAL; + + battery->client = client; + battery->regmap = devm_regmap_init_i2c(client, + &rt5033_battery_regmap_config); + if (IS_ERR(battery->regmap)) { + dev_err(&client->dev, "Failed to initialize regmap\n"); + return -EINVAL; + } + + i2c_set_clientdata(client, battery); + + battery->psy.name = "rt5033-battery"; + battery->psy.type = POWER_SUPPLY_TYPE_BATTERY; + battery->psy.get_property = rt5033_battery_get_property; + battery->psy.properties = rt5033_battery_props; + battery->psy.num_properties = ARRAY_SIZE(rt5033_battery_props); + + ret = power_supply_register(&client->dev, &battery->psy); + if (ret) { + dev_err(&client->dev, "Failed to register power supply\n"); + return ret; + } + + return 0; +} + +static int rt5033_battery_remove(struct i2c_client *client) +{ + struct rt5033_battery *battery = i2c_get_clientdata(client); + + power_supply_unregister(&battery->psy); + + return 0; +} + +static const struct i2c_device_id rt5033_battery_id[] = { + { "rt5033-battery", }, + { } +}; +MODULE_DEVICE_TABLE(platform, rt5033_battery_id); + +static struct i2c_driver rt5033_battery_driver = { + .driver = { + .name = "rt5033-battery", + }, + .probe = rt5033_battery_probe, + .remove = rt5033_battery_remove, + .id_table = rt5033_battery_id, +}; +module_i2c_driver(rt5033_battery_driver); + +MODULE_DESCRIPTION("Richtek RT5033 fuel gauge driver"); +MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/test_power.c b/drivers/power/test_power.c index 0152f35dca5c..f26b1fa00fe1 100644 --- a/drivers/power/test_power.c +++ b/drivers/power/test_power.c @@ -21,6 +21,13 @@ #include <linux/delay.h> #include <linux/vermagic.h> +enum test_power_id { + TEST_AC, + TEST_BATTERY, + TEST_USB, + TEST_POWER_NUM, +}; + static int ac_online = 1; static int usb_online = 1; static int battery_status = POWER_SUPPLY_STATUS_DISCHARGING; @@ -147,7 +154,7 @@ static char *test_power_ac_supplied_to[] = { }; static struct power_supply test_power_supplies[] = { - { + [TEST_AC] = { .name = "test_ac", .type = POWER_SUPPLY_TYPE_MAINS, .supplied_to = test_power_ac_supplied_to, @@ -155,13 +162,15 @@ static struct power_supply test_power_supplies[] = { .properties = test_power_ac_props, .num_properties = ARRAY_SIZE(test_power_ac_props), .get_property = test_power_get_ac_property, - }, { + }, + [TEST_BATTERY] = { .name = "test_battery", .type = POWER_SUPPLY_TYPE_BATTERY, .properties = test_power_battery_props, .num_properties = ARRAY_SIZE(test_power_battery_props), .get_property = test_power_get_battery_property, - }, { + }, + [TEST_USB] = { .name = "test_usb", .type = POWER_SUPPLY_TYPE_USB, .supplied_to = test_power_ac_supplied_to, @@ -178,6 +187,8 @@ static int __init test_power_init(void) int i; int ret; + BUILD_BUG_ON(TEST_POWER_NUM != ARRAY_SIZE(test_power_supplies)); + for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++) { ret = power_supply_register(NULL, &test_power_supplies[i]); if (ret) { @@ -309,7 +320,7 @@ static inline void signal_power_supply_changed(struct power_supply *psy) static int param_set_ac_online(const char *key, const struct kernel_param *kp) { ac_online = map_get_value(map_ac_online, key, ac_online); - signal_power_supply_changed(&test_power_supplies[0]); + signal_power_supply_changed(&test_power_supplies[TEST_AC]); return 0; } @@ -322,7 +333,7 @@ static int param_get_ac_online(char *buffer, const struct kernel_param *kp) static int param_set_usb_online(const char *key, const struct kernel_param *kp) { usb_online = map_get_value(map_ac_online, key, usb_online); - signal_power_supply_changed(&test_power_supplies[2]); + signal_power_supply_changed(&test_power_supplies[TEST_USB]); return 0; } @@ -336,7 +347,7 @@ static int param_set_battery_status(const char *key, const struct kernel_param *kp) { battery_status = map_get_value(map_status, key, battery_status); - signal_power_supply_changed(&test_power_supplies[1]); + signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]); return 0; } @@ -350,7 +361,7 @@ static int param_set_battery_health(const char *key, const struct kernel_param *kp) { battery_health = map_get_value(map_health, key, battery_health); - signal_power_supply_changed(&test_power_supplies[1]); + signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]); return 0; } @@ -364,7 +375,7 @@ static int param_set_battery_present(const char *key, const struct kernel_param *kp) { battery_present = map_get_value(map_present, key, battery_present); - signal_power_supply_changed(&test_power_supplies[0]); + signal_power_supply_changed(&test_power_supplies[TEST_AC]); return 0; } @@ -380,7 +391,7 @@ static int param_set_battery_technology(const char *key, { battery_technology = map_get_value(map_technology, key, battery_technology); - signal_power_supply_changed(&test_power_supplies[1]); + signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]); return 0; } @@ -401,7 +412,7 @@ static int param_set_battery_capacity(const char *key, return -EINVAL; battery_capacity = tmp; - signal_power_supply_changed(&test_power_supplies[1]); + signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]); return 0; } @@ -416,7 +427,7 @@ static int param_set_battery_voltage(const char *key, return -EINVAL; battery_voltage = tmp; - signal_power_supply_changed(&test_power_supplies[1]); + signal_power_supply_changed(&test_power_supplies[TEST_BATTERY]); return 0; } diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c index 8bcfecd66281..eeca70ddbf61 100644 --- a/drivers/rapidio/devices/tsi721.c +++ b/drivers/rapidio/devices/tsi721.c @@ -2430,7 +2430,7 @@ static int tsi721_probe(struct pci_dev *pdev, pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_READRQ | PCI_EXP_DEVCTL_RELAX_EN | PCI_EXP_DEVCTL_NOSNOOP_EN, - 0x2 << MAX_READ_REQUEST_SZ_SHIFT); + PCI_EXP_DEVCTL_READRQ_512B); /* Adjust PCIe completion timeout. */ pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL2, 0xf, 0x2); diff --git a/drivers/rapidio/devices/tsi721.h b/drivers/rapidio/devices/tsi721.h index a7b42680a06a..9d2502543ef6 100644 --- a/drivers/rapidio/devices/tsi721.h +++ b/drivers/rapidio/devices/tsi721.h @@ -72,8 +72,6 @@ #define TSI721_MSIXPBA_OFFSET 0x2a000 #define TSI721_PCIECFG_EPCTL 0x400 -#define MAX_READ_REQUEST_SZ_SHIFT 12 - /* * Event Management Registers */ diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index c3a60b57a865..a6f116aa5235 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -414,6 +414,14 @@ config REGULATOR_MAX77802 Exynos5420/Exynos5800 SoCs to control various voltages. It includes support for control of voltage and ramp speed. +config REGULATOR_MAX77843 + tristate "Maxim 77843 regulator" + depends on MFD_MAX77843 + help + This driver controls a Maxim 77843 regulator. + The regulator include two 'SAFEOUT' for USB(Universal Serial Bus) + This is suitable for Exynos5433 SoC chips. + config REGULATOR_MC13XXX_CORE tristate @@ -433,6 +441,15 @@ config REGULATOR_MC13892 Say y here to support the regulators found on the Freescale MC13892 PMIC. +config REGULATOR_MT6397 + tristate "MediaTek MT6397 PMIC" + depends on MFD_MT6397 + help + Say y here to select this option to enable the power regulator of + MediaTek MT6397 PMIC. + This driver supports the control of different power rails of device + through regulator interface. + config REGULATOR_PALMAS tristate "TI Palmas PMIC Regulators" depends on MFD_PALMAS diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 1f28ebfc6f3a..2c4da15e1545 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -55,9 +55,11 @@ obj-$(CONFIG_REGULATOR_MAX8998) += max8998.o obj-$(CONFIG_REGULATOR_MAX77686) += max77686.o obj-$(CONFIG_REGULATOR_MAX77693) += max77693.o obj-$(CONFIG_REGULATOR_MAX77802) += max77802.o +obj-$(CONFIG_REGULATOR_MAX77843) += max77843.o obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o +obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c index f23d7e1f2ee7..e4331f5e5d7d 100644 --- a/drivers/regulator/axp20x-regulator.c +++ b/drivers/regulator/axp20x-regulator.c @@ -32,11 +32,13 @@ #define AXP20X_FREQ_DCDC_MASK 0x0f -#define AXP20X_DESC_IO(_id, _supply, _min, _max, _step, _vreg, _vmask, _ereg, \ - _emask, _enable_val, _disable_val) \ +#define AXP20X_DESC_IO(_id, _match, _supply, _min, _max, _step, _vreg, _vmask, \ + _ereg, _emask, _enable_val, _disable_val) \ [AXP20X_##_id] = { \ .name = #_id, \ .supply_name = (_supply), \ + .of_match = of_match_ptr(_match), \ + .regulators_node = of_match_ptr("regulators"), \ .type = REGULATOR_VOLTAGE, \ .id = AXP20X_##_id, \ .n_voltages = (((_max) - (_min)) / (_step) + 1), \ @@ -52,11 +54,13 @@ .ops = &axp20x_ops, \ } -#define AXP20X_DESC(_id, _supply, _min, _max, _step, _vreg, _vmask, _ereg, \ - _emask) \ +#define AXP20X_DESC(_id, _match, _supply, _min, _max, _step, _vreg, _vmask, \ + _ereg, _emask) \ [AXP20X_##_id] = { \ .name = #_id, \ .supply_name = (_supply), \ + .of_match = of_match_ptr(_match), \ + .regulators_node = of_match_ptr("regulators"), \ .type = REGULATOR_VOLTAGE, \ .id = AXP20X_##_id, \ .n_voltages = (((_max) - (_min)) / (_step) + 1), \ @@ -70,10 +74,12 @@ .ops = &axp20x_ops, \ } -#define AXP20X_DESC_FIXED(_id, _supply, _volt) \ +#define AXP20X_DESC_FIXED(_id, _match, _supply, _volt) \ [AXP20X_##_id] = { \ .name = #_id, \ .supply_name = (_supply), \ + .of_match = of_match_ptr(_match), \ + .regulators_node = of_match_ptr("regulators"), \ .type = REGULATOR_VOLTAGE, \ .id = AXP20X_##_id, \ .n_voltages = 1, \ @@ -82,10 +88,13 @@ .ops = &axp20x_ops_fixed \ } -#define AXP20X_DESC_TABLE(_id, _supply, _table, _vreg, _vmask, _ereg, _emask) \ +#define AXP20X_DESC_TABLE(_id, _match, _supply, _table, _vreg, _vmask, _ereg, \ + _emask) \ [AXP20X_##_id] = { \ .name = #_id, \ .supply_name = (_supply), \ + .of_match = of_match_ptr(_match), \ + .regulators_node = of_match_ptr("regulators"), \ .type = REGULATOR_VOLTAGE, \ .id = AXP20X_##_id, \ .n_voltages = ARRAY_SIZE(_table), \ @@ -127,36 +136,20 @@ static struct regulator_ops axp20x_ops = { }; static const struct regulator_desc axp20x_regulators[] = { - AXP20X_DESC(DCDC2, "vin2", 700, 2275, 25, AXP20X_DCDC2_V_OUT, 0x3f, - AXP20X_PWR_OUT_CTRL, 0x10), - AXP20X_DESC(DCDC3, "vin3", 700, 3500, 25, AXP20X_DCDC3_V_OUT, 0x7f, - AXP20X_PWR_OUT_CTRL, 0x02), - AXP20X_DESC_FIXED(LDO1, "acin", 1300), - AXP20X_DESC(LDO2, "ldo24in", 1800, 3300, 100, AXP20X_LDO24_V_OUT, 0xf0, - AXP20X_PWR_OUT_CTRL, 0x04), - AXP20X_DESC(LDO3, "ldo3in", 700, 3500, 25, AXP20X_LDO3_V_OUT, 0x7f, - AXP20X_PWR_OUT_CTRL, 0x40), - AXP20X_DESC_TABLE(LDO4, "ldo24in", axp20x_ldo4_data, AXP20X_LDO24_V_OUT, 0x0f, - AXP20X_PWR_OUT_CTRL, 0x08), - AXP20X_DESC_IO(LDO5, "ldo5in", 1800, 3300, 100, AXP20X_LDO5_V_OUT, 0xf0, - AXP20X_GPIO0_CTRL, 0x07, AXP20X_IO_ENABLED, - AXP20X_IO_DISABLED), -}; - -#define AXP_MATCH(_name, _id) \ - [AXP20X_##_id] = { \ - .name = #_name, \ - .driver_data = (void *) &axp20x_regulators[AXP20X_##_id], \ - } - -static struct of_regulator_match axp20x_matches[] = { - AXP_MATCH(dcdc2, DCDC2), - AXP_MATCH(dcdc3, DCDC3), - AXP_MATCH(ldo1, LDO1), - AXP_MATCH(ldo2, LDO2), - AXP_MATCH(ldo3, LDO3), - AXP_MATCH(ldo4, LDO4), - AXP_MATCH(ldo5, LDO5), + AXP20X_DESC(DCDC2, "dcdc2", "vin2", 700, 2275, 25, AXP20X_DCDC2_V_OUT, + 0x3f, AXP20X_PWR_OUT_CTRL, 0x10), + AXP20X_DESC(DCDC3, "dcdc3", "vin3", 700, 3500, 25, AXP20X_DCDC3_V_OUT, + 0x7f, AXP20X_PWR_OUT_CTRL, 0x02), + AXP20X_DESC_FIXED(LDO1, "ldo1", "acin", 1300), + AXP20X_DESC(LDO2, "ldo2", "ldo24in", 1800, 3300, 100, + AXP20X_LDO24_V_OUT, 0xf0, AXP20X_PWR_OUT_CTRL, 0x04), + AXP20X_DESC(LDO3, "ldo3", "ldo3in", 700, 3500, 25, AXP20X_LDO3_V_OUT, + 0x7f, AXP20X_PWR_OUT_CTRL, 0x40), + AXP20X_DESC_TABLE(LDO4, "ldo4", "ldo24in", axp20x_ldo4_data, + AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL, 0x08), + AXP20X_DESC_IO(LDO5, "ldo5", "ldo5in", 1800, 3300, 100, + AXP20X_LDO5_V_OUT, 0xf0, AXP20X_GPIO0_CTRL, 0x07, + AXP20X_IO_ENABLED, AXP20X_IO_DISABLED), }; static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq) @@ -193,13 +186,6 @@ static int axp20x_regulator_parse_dt(struct platform_device *pdev) if (!regulators) { dev_warn(&pdev->dev, "regulators node not found\n"); } else { - ret = of_regulator_match(&pdev->dev, regulators, axp20x_matches, - ARRAY_SIZE(axp20x_matches)); - if (ret < 0) { - dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ret); - return ret; - } - dcdcfreq = 1500; of_property_read_u32(regulators, "x-powers,dcdc-freq", &dcdcfreq); ret = axp20x_set_dcdc_freq(pdev, dcdcfreq); @@ -233,23 +219,17 @@ static int axp20x_regulator_probe(struct platform_device *pdev) { struct regulator_dev *rdev; struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); - struct regulator_config config = { }; - struct regulator_init_data *init_data; + struct regulator_config config = { + .dev = pdev->dev.parent, + .regmap = axp20x->regmap, + }; int ret, i; u32 workmode; - ret = axp20x_regulator_parse_dt(pdev); - if (ret) - return ret; + /* This only sets the dcdc freq. Ignore any errors */ + axp20x_regulator_parse_dt(pdev); for (i = 0; i < AXP20X_REG_ID_MAX; i++) { - init_data = axp20x_matches[i].init_data; - - config.dev = pdev->dev.parent; - config.init_data = init_data; - config.regmap = axp20x->regmap; - config.of_node = axp20x_matches[i].of_node; - rdev = devm_regulator_register(&pdev->dev, &axp20x_regulators[i], &config); if (IS_ERR(rdev)) { @@ -259,7 +239,8 @@ static int axp20x_regulator_probe(struct platform_device *pdev) return PTR_ERR(rdev); } - ret = of_property_read_u32(axp20x_matches[i].of_node, "x-powers,dcdc-workmode", + ret = of_property_read_u32(rdev->dev.of_node, + "x-powers,dcdc-workmode", &workmode); if (!ret) { if (axp20x_set_dcdc_workmode(rdev, i, workmode)) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index e225711bb8bc..b899947d839d 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -632,49 +632,34 @@ static ssize_t regulator_bypass_show(struct device *dev, static DEVICE_ATTR(bypass, 0444, regulator_bypass_show, NULL); -/* - * These are the only attributes are present for all regulators. - * Other attributes are a function of regulator functionality. - */ -static struct attribute *regulator_dev_attrs[] = { - &dev_attr_name.attr, - &dev_attr_num_users.attr, - &dev_attr_type.attr, - NULL, -}; -ATTRIBUTE_GROUPS(regulator_dev); - -static void regulator_dev_release(struct device *dev) -{ - struct regulator_dev *rdev = dev_get_drvdata(dev); - kfree(rdev); -} - -static struct class regulator_class = { - .name = "regulator", - .dev_release = regulator_dev_release, - .dev_groups = regulator_dev_groups, -}; - /* Calculate the new optimum regulator operating mode based on the new total * consumer load. All locks held by caller */ -static void drms_uA_update(struct regulator_dev *rdev) +static int drms_uA_update(struct regulator_dev *rdev) { struct regulator *sibling; int current_uA = 0, output_uV, input_uV, err; unsigned int mode; + /* + * first check to see if we can set modes at all, otherwise just + * tell the consumer everything is OK. + */ err = regulator_check_drms(rdev); - if (err < 0 || !rdev->desc->ops->get_optimum_mode || - (!rdev->desc->ops->get_voltage && - !rdev->desc->ops->get_voltage_sel) || - !rdev->desc->ops->set_mode) - return; + if (err < 0) + return 0; + + if (!rdev->desc->ops->get_optimum_mode) + return 0; + + if (!rdev->desc->ops->set_mode) + return -EINVAL; /* get output voltage */ output_uV = _regulator_get_voltage(rdev); - if (output_uV <= 0) - return; + if (output_uV <= 0) { + rdev_err(rdev, "invalid output voltage found\n"); + return -EINVAL; + } /* get input voltage */ input_uV = 0; @@ -682,8 +667,10 @@ static void drms_uA_update(struct regulator_dev *rdev) input_uV = regulator_get_voltage(rdev->supply); if (input_uV <= 0) input_uV = rdev->constraints->input_uV; - if (input_uV <= 0) - return; + if (input_uV <= 0) { + rdev_err(rdev, "invalid input voltage found\n"); + return -EINVAL; + } /* calc total requested load */ list_for_each_entry(sibling, &rdev->consumer_list, list) @@ -695,8 +682,17 @@ static void drms_uA_update(struct regulator_dev *rdev) /* check the new mode is allowed */ err = regulator_mode_constrain(rdev, &mode); - if (err == 0) - rdev->desc->ops->set_mode(rdev, mode); + if (err < 0) { + rdev_err(rdev, "failed to get optimum mode @ %d uA %d -> %d uV\n", + current_uA, input_uV, output_uV); + return err; + } + + err = rdev->desc->ops->set_mode(rdev, mode); + if (err < 0) + rdev_err(rdev, "failed to set optimum mode %x\n", mode); + + return err; } static int suspend_set_state(struct regulator_dev *rdev, @@ -1488,7 +1484,7 @@ struct regulator *regulator_get_optional(struct device *dev, const char *id) } EXPORT_SYMBOL_GPL(regulator_get_optional); -/* Locks held by regulator_put() */ +/* regulator_list_mutex lock held by regulator_put() */ static void _regulator_put(struct regulator *regulator) { struct regulator_dev *rdev; @@ -1503,12 +1499,14 @@ static void _regulator_put(struct regulator *regulator) /* remove any sysfs entries */ if (regulator->dev) sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name); + mutex_lock(&rdev->mutex); kfree(regulator->supply_name); list_del(®ulator->list); kfree(regulator); rdev->open_count--; rdev->exclusive = 0; + mutex_unlock(&rdev->mutex); module_put(rdev->owner); } @@ -3024,75 +3022,13 @@ EXPORT_SYMBOL_GPL(regulator_get_mode); int regulator_set_optimum_mode(struct regulator *regulator, int uA_load) { struct regulator_dev *rdev = regulator->rdev; - struct regulator *consumer; - int ret, output_uV, input_uV = 0, total_uA_load = 0; - unsigned int mode; - - if (rdev->supply) - input_uV = regulator_get_voltage(rdev->supply); + int ret; mutex_lock(&rdev->mutex); - - /* - * first check to see if we can set modes at all, otherwise just - * tell the consumer everything is OK. - */ regulator->uA_load = uA_load; - ret = regulator_check_drms(rdev); - if (ret < 0) { - ret = 0; - goto out; - } - - if (!rdev->desc->ops->get_optimum_mode) - goto out; - - /* - * we can actually do this so any errors are indicators of - * potential real failure. - */ - ret = -EINVAL; - - if (!rdev->desc->ops->set_mode) - goto out; - - /* get output voltage */ - output_uV = _regulator_get_voltage(rdev); - if (output_uV <= 0) { - rdev_err(rdev, "invalid output voltage found\n"); - goto out; - } - - /* No supply? Use constraint voltage */ - if (input_uV <= 0) - input_uV = rdev->constraints->input_uV; - if (input_uV <= 0) { - rdev_err(rdev, "invalid input voltage found\n"); - goto out; - } - - /* calc total requested load for this regulator */ - list_for_each_entry(consumer, &rdev->consumer_list, list) - total_uA_load += consumer->uA_load; - - mode = rdev->desc->ops->get_optimum_mode(rdev, - input_uV, output_uV, - total_uA_load); - ret = regulator_mode_constrain(rdev, &mode); - if (ret < 0) { - rdev_err(rdev, "failed to get optimum mode @ %d uA %d -> %d uV\n", - total_uA_load, input_uV, output_uV); - goto out; - } - - ret = rdev->desc->ops->set_mode(rdev, mode); - if (ret < 0) { - rdev_err(rdev, "failed to set optimum mode %x\n", mode); - goto out; - } - ret = mode; -out: + ret = drms_uA_update(rdev); mutex_unlock(&rdev->mutex); + return ret; } EXPORT_SYMBOL_GPL(regulator_set_optimum_mode); @@ -3434,126 +3370,136 @@ int regulator_mode_to_status(unsigned int mode) } EXPORT_SYMBOL_GPL(regulator_mode_to_status); +static struct attribute *regulator_dev_attrs[] = { + &dev_attr_name.attr, + &dev_attr_num_users.attr, + &dev_attr_type.attr, + &dev_attr_microvolts.attr, + &dev_attr_microamps.attr, + &dev_attr_opmode.attr, + &dev_attr_state.attr, + &dev_attr_status.attr, + &dev_attr_bypass.attr, + &dev_attr_requested_microamps.attr, + &dev_attr_min_microvolts.attr, + &dev_attr_max_microvolts.attr, + &dev_attr_min_microamps.attr, + &dev_attr_max_microamps.attr, + &dev_attr_suspend_standby_state.attr, + &dev_attr_suspend_mem_state.attr, + &dev_attr_suspend_disk_state.attr, + &dev_attr_suspend_standby_microvolts.attr, + &dev_attr_suspend_mem_microvolts.attr, + &dev_attr_suspend_disk_microvolts.attr, + &dev_attr_suspend_standby_mode.attr, + &dev_attr_suspend_mem_mode.attr, + &dev_attr_suspend_disk_mode.attr, + NULL +}; + /* * To avoid cluttering sysfs (and memory) with useless state, only * create attributes that can be meaningfully displayed. */ -static int add_regulator_attributes(struct regulator_dev *rdev) +static umode_t regulator_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) { - struct device *dev = &rdev->dev; + struct device *dev = kobj_to_dev(kobj); + struct regulator_dev *rdev = container_of(dev, struct regulator_dev, dev); const struct regulator_ops *ops = rdev->desc->ops; - int status = 0; + umode_t mode = attr->mode; + + /* these three are always present */ + if (attr == &dev_attr_name.attr || + attr == &dev_attr_num_users.attr || + attr == &dev_attr_type.attr) + return mode; /* some attributes need specific methods to be displayed */ - if ((ops->get_voltage && ops->get_voltage(rdev) >= 0) || - (ops->get_voltage_sel && ops->get_voltage_sel(rdev) >= 0) || - (ops->list_voltage && ops->list_voltage(rdev, 0) >= 0) || - (rdev->desc->fixed_uV && (rdev->desc->n_voltages == 1))) { - status = device_create_file(dev, &dev_attr_microvolts); - if (status < 0) - return status; - } - if (ops->get_current_limit) { - status = device_create_file(dev, &dev_attr_microamps); - if (status < 0) - return status; - } - if (ops->get_mode) { - status = device_create_file(dev, &dev_attr_opmode); - if (status < 0) - return status; - } - if (rdev->ena_pin || ops->is_enabled) { - status = device_create_file(dev, &dev_attr_state); - if (status < 0) - return status; - } - if (ops->get_status) { - status = device_create_file(dev, &dev_attr_status); - if (status < 0) - return status; - } - if (ops->get_bypass) { - status = device_create_file(dev, &dev_attr_bypass); - if (status < 0) - return status; + if (attr == &dev_attr_microvolts.attr) { + if ((ops->get_voltage && ops->get_voltage(rdev) >= 0) || + (ops->get_voltage_sel && ops->get_voltage_sel(rdev) >= 0) || + (ops->list_voltage && ops->list_voltage(rdev, 0) >= 0) || + (rdev->desc->fixed_uV && rdev->desc->n_voltages == 1)) + return mode; + return 0; } + if (attr == &dev_attr_microamps.attr) + return ops->get_current_limit ? mode : 0; + + if (attr == &dev_attr_opmode.attr) + return ops->get_mode ? mode : 0; + + if (attr == &dev_attr_state.attr) + return (rdev->ena_pin || ops->is_enabled) ? mode : 0; + + if (attr == &dev_attr_status.attr) + return ops->get_status ? mode : 0; + + if (attr == &dev_attr_bypass.attr) + return ops->get_bypass ? mode : 0; + /* some attributes are type-specific */ - if (rdev->desc->type == REGULATOR_CURRENT) { - status = device_create_file(dev, &dev_attr_requested_microamps); - if (status < 0) - return status; - } + if (attr == &dev_attr_requested_microamps.attr) + return rdev->desc->type == REGULATOR_CURRENT ? mode : 0; /* all the other attributes exist to support constraints; * don't show them if there are no constraints, or if the * relevant supporting methods are missing. */ if (!rdev->constraints) - return status; + return 0; /* constraints need specific supporting methods */ - if (ops->set_voltage || ops->set_voltage_sel) { - status = device_create_file(dev, &dev_attr_min_microvolts); - if (status < 0) - return status; - status = device_create_file(dev, &dev_attr_max_microvolts); - if (status < 0) - return status; - } - if (ops->set_current_limit) { - status = device_create_file(dev, &dev_attr_min_microamps); - if (status < 0) - return status; - status = device_create_file(dev, &dev_attr_max_microamps); - if (status < 0) - return status; - } - - status = device_create_file(dev, &dev_attr_suspend_standby_state); - if (status < 0) - return status; - status = device_create_file(dev, &dev_attr_suspend_mem_state); - if (status < 0) - return status; - status = device_create_file(dev, &dev_attr_suspend_disk_state); - if (status < 0) - return status; + if (attr == &dev_attr_min_microvolts.attr || + attr == &dev_attr_max_microvolts.attr) + return (ops->set_voltage || ops->set_voltage_sel) ? mode : 0; + + if (attr == &dev_attr_min_microamps.attr || + attr == &dev_attr_max_microamps.attr) + return ops->set_current_limit ? mode : 0; + + if (attr == &dev_attr_suspend_standby_state.attr || + attr == &dev_attr_suspend_mem_state.attr || + attr == &dev_attr_suspend_disk_state.attr) + return mode; + + if (attr == &dev_attr_suspend_standby_microvolts.attr || + attr == &dev_attr_suspend_mem_microvolts.attr || + attr == &dev_attr_suspend_disk_microvolts.attr) + return ops->set_suspend_voltage ? mode : 0; + + if (attr == &dev_attr_suspend_standby_mode.attr || + attr == &dev_attr_suspend_mem_mode.attr || + attr == &dev_attr_suspend_disk_mode.attr) + return ops->set_suspend_mode ? mode : 0; + + return mode; +} + +static const struct attribute_group regulator_dev_group = { + .attrs = regulator_dev_attrs, + .is_visible = regulator_attr_is_visible, +}; + +static const struct attribute_group *regulator_dev_groups[] = { + ®ulator_dev_group, + NULL +}; - if (ops->set_suspend_voltage) { - status = device_create_file(dev, - &dev_attr_suspend_standby_microvolts); - if (status < 0) - return status; - status = device_create_file(dev, - &dev_attr_suspend_mem_microvolts); - if (status < 0) - return status; - status = device_create_file(dev, - &dev_attr_suspend_disk_microvolts); - if (status < 0) - return status; - } - - if (ops->set_suspend_mode) { - status = device_create_file(dev, - &dev_attr_suspend_standby_mode); - if (status < 0) - return status; - status = device_create_file(dev, - &dev_attr_suspend_mem_mode); - if (status < 0) - return status; - status = device_create_file(dev, - &dev_attr_suspend_disk_mode); - if (status < 0) - return status; - } - - return status; +static void regulator_dev_release(struct device *dev) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + kfree(rdev); } +static struct class regulator_class = { + .name = "regulator", + .dev_release = regulator_dev_release, + .dev_groups = regulator_dev_groups, +}; + static void rdev_init_debugfs(struct regulator_dev *rdev) { rdev->debugfs = debugfs_create_dir(rdev_get_name(rdev), debugfs_root); @@ -3573,7 +3519,7 @@ static void rdev_init_debugfs(struct regulator_dev *rdev) /** * regulator_register - register regulator * @regulator_desc: regulator to register - * @config: runtime configuration for regulator + * @cfg: runtime configuration for regulator * * Called by regulator drivers to register a regulator. * Returns a valid pointer to struct regulator_dev on success @@ -3581,20 +3527,21 @@ static void rdev_init_debugfs(struct regulator_dev *rdev) */ struct regulator_dev * regulator_register(const struct regulator_desc *regulator_desc, - const struct regulator_config *config) + const struct regulator_config *cfg) { const struct regulation_constraints *constraints = NULL; const struct regulator_init_data *init_data; - static atomic_t regulator_no = ATOMIC_INIT(0); + struct regulator_config *config = NULL; + static atomic_t regulator_no = ATOMIC_INIT(-1); struct regulator_dev *rdev; struct device *dev; int ret, i; const char *supply = NULL; - if (regulator_desc == NULL || config == NULL) + if (regulator_desc == NULL || cfg == NULL) return ERR_PTR(-EINVAL); - dev = config->dev; + dev = cfg->dev; WARN_ON(!dev); if (regulator_desc->name == NULL || regulator_desc->ops == NULL) @@ -3624,7 +3571,17 @@ regulator_register(const struct regulator_desc *regulator_desc, if (rdev == NULL) return ERR_PTR(-ENOMEM); - init_data = regulator_of_get_init_data(dev, regulator_desc, + /* + * Duplicate the config so the driver could override it after + * parsing init data. + */ + config = kmemdup(cfg, sizeof(*cfg), GFP_KERNEL); + if (config == NULL) { + kfree(rdev); + return ERR_PTR(-ENOMEM); + } + + init_data = regulator_of_get_init_data(dev, regulator_desc, config, &rdev->dev.of_node); if (!init_data) { init_data = config->init_data; @@ -3658,8 +3615,8 @@ regulator_register(const struct regulator_desc *regulator_desc, /* register with sysfs */ rdev->dev.class = ®ulator_class; rdev->dev.parent = dev; - dev_set_name(&rdev->dev, "regulator.%d", - atomic_inc_return(®ulator_no) - 1); + dev_set_name(&rdev->dev, "regulator.%lu", + (unsigned long) atomic_inc_return(®ulator_no)); ret = device_register(&rdev->dev); if (ret != 0) { put_device(&rdev->dev); @@ -3692,11 +3649,6 @@ regulator_register(const struct regulator_desc *regulator_desc, if (ret < 0) goto scrub; - /* add attributes supported by this regulator */ - ret = add_regulator_attributes(rdev); - if (ret < 0) - goto scrub; - if (init_data && init_data->supply_regulator) supply = init_data->supply_regulator; else if (regulator_desc->supply_name) @@ -3752,6 +3704,7 @@ add_dev: rdev_init_debugfs(rdev); out: mutex_unlock(®ulator_list_mutex); + kfree(config); return rdev; unset_supplies: diff --git a/drivers/regulator/da9211-regulator.c b/drivers/regulator/da9211-regulator.c index c78d2106d6cb..01343419555e 100644 --- a/drivers/regulator/da9211-regulator.c +++ b/drivers/regulator/da9211-regulator.c @@ -24,6 +24,7 @@ #include <linux/regmap.h> #include <linux/irq.h> #include <linux/interrupt.h> +#include <linux/of_gpio.h> #include <linux/regulator/of_regulator.h> #include <linux/regulator/da9211.h> #include "da9211-regulator.h" @@ -276,7 +277,10 @@ static struct da9211_pdata *da9211_parse_regulators_dt( continue; pdata->init_data[n] = da9211_matches[i].init_data; - + pdata->reg_node[n] = da9211_matches[i].of_node; + pdata->gpio_ren[n] = + of_get_named_gpio(da9211_matches[i].of_node, + "enable-gpios", 0); n++; } @@ -364,7 +368,15 @@ static int da9211_regulator_init(struct da9211 *chip) config.dev = chip->dev; config.driver_data = chip; config.regmap = chip->regmap; - config.of_node = chip->dev->of_node; + config.of_node = chip->pdata->reg_node[i]; + + if (gpio_is_valid(chip->pdata->gpio_ren[i])) { + config.ena_gpio = chip->pdata->gpio_ren[i]; + config.ena_gpio_initialized = true; + } else { + config.ena_gpio = -EINVAL; + config.ena_gpio_initialized = false; + } chip->rdev[i] = devm_regulator_register(chip->dev, &da9211_regulators[i], &config); diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c index 6c43ab2d5121..3c25db89a021 100644 --- a/drivers/regulator/fan53555.c +++ b/drivers/regulator/fan53555.c @@ -147,7 +147,7 @@ static unsigned int fan53555_get_mode(struct regulator_dev *rdev) return REGULATOR_MODE_NORMAL; } -static int slew_rates[] = { +static const int slew_rates[] = { 64000, 32000, 16000, @@ -296,7 +296,7 @@ static int fan53555_regulator_register(struct fan53555_device_info *di, return PTR_ERR_OR_ZERO(di->rdev); } -static struct regmap_config fan53555_regmap_config = { +static const struct regmap_config fan53555_regmap_config = { .reg_bits = 8, .val_bits = 8, }; diff --git a/drivers/regulator/internal.h b/drivers/regulator/internal.h index 80ba2a35a04b..c74ac8734023 100644 --- a/drivers/regulator/internal.h +++ b/drivers/regulator/internal.h @@ -38,11 +38,13 @@ struct regulator { #ifdef CONFIG_OF struct regulator_init_data *regulator_of_get_init_data(struct device *dev, const struct regulator_desc *desc, + struct regulator_config *config, struct device_node **node); #else static inline struct regulator_init_data * regulator_of_get_init_data(struct device *dev, const struct regulator_desc *desc, + struct regulator_config *config, struct device_node **node) { return NULL; diff --git a/drivers/regulator/isl9305.c b/drivers/regulator/isl9305.c index 92fefd98da58..6e3a15fe00f1 100644 --- a/drivers/regulator/isl9305.c +++ b/drivers/regulator/isl9305.c @@ -177,8 +177,10 @@ static int isl9305_i2c_probe(struct i2c_client *i2c, #ifdef CONFIG_OF static const struct of_device_id isl9305_dt_ids[] = { - { .compatible = "isl,isl9305" }, - { .compatible = "isl,isl9305h" }, + { .compatible = "isl,isl9305" }, /* for backward compat., don't use */ + { .compatible = "isil,isl9305" }, + { .compatible = "isl,isl9305h" }, /* for backward compat., don't use */ + { .compatible = "isil,isl9305h" }, {}, }; #endif diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c index 021d64d856bb..3de328ab41f3 100644 --- a/drivers/regulator/lp872x.c +++ b/drivers/regulator/lp872x.c @@ -106,7 +106,6 @@ struct lp872x { struct device *dev; enum lp872x_id chipid; struct lp872x_platform_data *pdata; - struct regulator_dev **regulators; int num_regulators; enum lp872x_dvs_state dvs_pin; int dvs_gpio; @@ -801,8 +800,6 @@ static int lp872x_regulator_register(struct lp872x *lp) dev_err(lp->dev, "regulator register err"); return PTR_ERR(rdev); } - - *(lp->regulators + i) = rdev; } return 0; @@ -906,7 +903,7 @@ static struct lp872x_platform_data static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) { struct lp872x *lp; - int ret, size, num_regulators; + int ret; const int lp872x_num_regulators[] = { [LP8720] = LP8720_NUM_REGULATORS, [LP8725] = LP8725_NUM_REGULATORS, @@ -918,38 +915,27 @@ static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id) lp = devm_kzalloc(&cl->dev, sizeof(struct lp872x), GFP_KERNEL); if (!lp) - goto err_mem; - - num_regulators = lp872x_num_regulators[id->driver_data]; - size = sizeof(struct regulator_dev *) * num_regulators; + return -ENOMEM; - lp->regulators = devm_kzalloc(&cl->dev, size, GFP_KERNEL); - if (!lp->regulators) - goto err_mem; + lp->num_regulators = lp872x_num_regulators[id->driver_data]; lp->regmap = devm_regmap_init_i2c(cl, &lp872x_regmap_config); if (IS_ERR(lp->regmap)) { ret = PTR_ERR(lp->regmap); dev_err(&cl->dev, "regmap init i2c err: %d\n", ret); - goto err_dev; + return ret; } lp->dev = &cl->dev; lp->pdata = dev_get_platdata(&cl->dev); lp->chipid = id->driver_data; - lp->num_regulators = num_regulators; i2c_set_clientdata(cl, lp); ret = lp872x_config(lp); if (ret) - goto err_dev; + return ret; return lp872x_regulator_register(lp); - -err_mem: - return -ENOMEM; -err_dev: - return ret; } static const struct of_device_id lp872x_dt_ids[] = { diff --git a/drivers/regulator/max14577.c b/drivers/regulator/max14577.c index bf9a44c5fdd2..b3678d289619 100644 --- a/drivers/regulator/max14577.c +++ b/drivers/regulator/max14577.c @@ -103,6 +103,8 @@ static struct regulator_ops max14577_charger_ops = { static const struct regulator_desc max14577_supported_regulators[] = { [MAX14577_SAFEOUT] = { .name = "SAFEOUT", + .of_match = of_match_ptr("SAFEOUT"), + .regulators_node = of_match_ptr("regulators"), .id = MAX14577_SAFEOUT, .ops = &max14577_safeout_ops, .type = REGULATOR_VOLTAGE, @@ -114,6 +116,8 @@ static const struct regulator_desc max14577_supported_regulators[] = { }, [MAX14577_CHARGER] = { .name = "CHARGER", + .of_match = of_match_ptr("CHARGER"), + .regulators_node = of_match_ptr("regulators"), .id = MAX14577_CHARGER, .ops = &max14577_charger_ops, .type = REGULATOR_CURRENT, @@ -137,6 +141,8 @@ static struct regulator_ops max77836_ldo_ops = { static const struct regulator_desc max77836_supported_regulators[] = { [MAX14577_SAFEOUT] = { .name = "SAFEOUT", + .of_match = of_match_ptr("SAFEOUT"), + .regulators_node = of_match_ptr("regulators"), .id = MAX14577_SAFEOUT, .ops = &max14577_safeout_ops, .type = REGULATOR_VOLTAGE, @@ -148,6 +154,8 @@ static const struct regulator_desc max77836_supported_regulators[] = { }, [MAX14577_CHARGER] = { .name = "CHARGER", + .of_match = of_match_ptr("CHARGER"), + .regulators_node = of_match_ptr("regulators"), .id = MAX14577_CHARGER, .ops = &max14577_charger_ops, .type = REGULATOR_CURRENT, @@ -157,6 +165,8 @@ static const struct regulator_desc max77836_supported_regulators[] = { }, [MAX77836_LDO1] = { .name = "LDO1", + .of_match = of_match_ptr("LDO1"), + .regulators_node = of_match_ptr("regulators"), .id = MAX77836_LDO1, .ops = &max77836_ldo_ops, .type = REGULATOR_VOLTAGE, @@ -171,6 +181,8 @@ static const struct regulator_desc max77836_supported_regulators[] = { }, [MAX77836_LDO2] = { .name = "LDO2", + .of_match = of_match_ptr("LDO2"), + .regulators_node = of_match_ptr("regulators"), .id = MAX77836_LDO2, .ops = &max77836_ldo_ops, .type = REGULATOR_VOLTAGE, @@ -198,43 +210,6 @@ static struct of_regulator_match max77836_regulator_matches[] = { { .name = "LDO2", }, }; -static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev, - enum maxim_device_type dev_type) -{ - int ret; - struct device_node *np; - struct of_regulator_match *regulator_matches; - unsigned int regulator_matches_size; - - np = of_get_child_by_name(pdev->dev.parent->of_node, "regulators"); - if (!np) { - dev_err(&pdev->dev, "Failed to get child OF node for regulators\n"); - return -EINVAL; - } - - switch (dev_type) { - case MAXIM_DEVICE_TYPE_MAX77836: - regulator_matches = max77836_regulator_matches; - regulator_matches_size = ARRAY_SIZE(max77836_regulator_matches); - break; - case MAXIM_DEVICE_TYPE_MAX14577: - default: - regulator_matches = max14577_regulator_matches; - regulator_matches_size = ARRAY_SIZE(max14577_regulator_matches); - } - - ret = of_regulator_match(&pdev->dev, np, regulator_matches, - regulator_matches_size); - if (ret < 0) - dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ret); - else - ret = 0; - - of_node_put(np); - - return ret; -} - static inline struct regulator_init_data *match_init_data(int index, enum maxim_device_type dev_type) { @@ -261,11 +236,6 @@ static inline struct device_node *match_of_node(int index, } } #else /* CONFIG_OF */ -static int max14577_regulator_dt_parse_pdata(struct platform_device *pdev, - enum maxim_device_type dev_type) -{ - return 0; -} static inline struct regulator_init_data *match_init_data(int index, enum maxim_device_type dev_type) { @@ -308,16 +278,12 @@ static int max14577_regulator_probe(struct platform_device *pdev) { struct max14577 *max14577 = dev_get_drvdata(pdev->dev.parent); struct max14577_platform_data *pdata = dev_get_platdata(max14577->dev); - int i, ret; + int i, ret = 0; struct regulator_config config = {}; const struct regulator_desc *supported_regulators; unsigned int supported_regulators_size; enum maxim_device_type dev_type = max14577->dev_type; - ret = max14577_regulator_dt_parse_pdata(pdev, dev_type); - if (ret) - return ret; - switch (dev_type) { case MAXIM_DEVICE_TYPE_MAX77836: supported_regulators = max77836_supported_regulators; @@ -329,7 +295,7 @@ static int max14577_regulator_probe(struct platform_device *pdev) supported_regulators_size = ARRAY_SIZE(max14577_supported_regulators); } - config.dev = &pdev->dev; + config.dev = max14577->dev; config.driver_data = max14577; for (i = 0; i < supported_regulators_size; i++) { diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index 10d206266ac2..15fb1416bfbd 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -26,6 +26,7 @@ #include <linux/bug.h> #include <linux/err.h> #include <linux/gpio.h> +#include <linux/of_gpio.h> #include <linux/slab.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> @@ -46,6 +47,11 @@ #define MAX77686_DVS_UVSTEP 12500 /* + * Value for configuring buck[89] and LDO{20,21,22} as GPIO control. + * It is the same as 'off' for other regulators. + */ +#define MAX77686_GPIO_CONTROL 0x0 +/* * Values used for configuring LDOs and bucks. * Forcing low power mode: LDO1, 3-5, 9, 13, 17-26 */ @@ -82,6 +88,8 @@ enum max77686_ramp_rate { }; struct max77686_data { + u64 gpio_enabled:MAX77686_REGULATORS; + /* Array indexed by regulator id */ unsigned int opmode[MAX77686_REGULATORS]; }; @@ -100,6 +108,26 @@ static unsigned int max77686_get_opmode_shift(int id) } } +/* + * When regulator is configured for GPIO control then it + * replaces "normal" mode. Any change from low power mode to normal + * should actually change to GPIO control. + * Map normal mode to proper value for such regulators. + */ +static unsigned int max77686_map_normal_mode(struct max77686_data *max77686, + int id) +{ + switch (id) { + case MAX77686_BUCK8: + case MAX77686_BUCK9: + case MAX77686_LDO20 ... MAX77686_LDO22: + if (max77686->gpio_enabled & (1 << id)) + return MAX77686_GPIO_CONTROL; + } + + return MAX77686_NORMAL; +} + /* Some BUCKs and LDOs supports Normal[ON/OFF] mode during suspend */ static int max77686_set_suspend_disable(struct regulator_dev *rdev) { @@ -136,7 +164,7 @@ static int max77686_set_suspend_mode(struct regulator_dev *rdev, val = MAX77686_LDO_LOWPOWER_PWRREQ; break; case REGULATOR_MODE_NORMAL: /* ON in Normal Mode */ - val = MAX77686_NORMAL; + val = max77686_map_normal_mode(max77686, id); break; default: pr_warn("%s: regulator_suspend_mode : 0x%x not supported\n", @@ -160,7 +188,7 @@ static int max77686_ldo_set_suspend_mode(struct regulator_dev *rdev, { unsigned int val; struct max77686_data *max77686 = rdev_get_drvdata(rdev); - int ret; + int ret, id = rdev_get_id(rdev); switch (mode) { case REGULATOR_MODE_STANDBY: /* switch off */ @@ -170,7 +198,7 @@ static int max77686_ldo_set_suspend_mode(struct regulator_dev *rdev, val = MAX77686_LDO_LOWPOWER_PWRREQ; break; case REGULATOR_MODE_NORMAL: /* ON in Normal Mode */ - val = MAX77686_NORMAL; + val = max77686_map_normal_mode(max77686, id); break; default: pr_warn("%s: regulator_suspend_mode : 0x%x not supported\n", @@ -184,7 +212,7 @@ static int max77686_ldo_set_suspend_mode(struct regulator_dev *rdev, if (ret) return ret; - max77686->opmode[rdev_get_id(rdev)] = val; + max77686->opmode[id] = val; return 0; } @@ -197,7 +225,7 @@ static int max77686_enable(struct regulator_dev *rdev) shift = max77686_get_opmode_shift(id); if (max77686->opmode[id] == MAX77686_OFF_PWRREQ) - max77686->opmode[id] = MAX77686_NORMAL; + max77686->opmode[id] = max77686_map_normal_mode(max77686, id); return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, rdev->desc->enable_mask, @@ -229,6 +257,36 @@ static int max77686_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) MAX77686_RAMP_RATE_MASK, ramp_value << 6); } +static int max77686_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + struct max77686_data *max77686 = config->driver_data; + + switch (desc->id) { + case MAX77686_BUCK8: + case MAX77686_BUCK9: + case MAX77686_LDO20 ... MAX77686_LDO22: + config->ena_gpio = of_get_named_gpio(np, + "maxim,ena-gpios", 0); + config->ena_gpio_flags = GPIOF_OUT_INIT_HIGH; + config->ena_gpio_initialized = true; + break; + default: + return 0; + } + + if (gpio_is_valid(config->ena_gpio)) { + max77686->gpio_enabled |= (1 << desc->id); + + return regmap_update_bits(config->regmap, desc->enable_reg, + desc->enable_mask, + MAX77686_GPIO_CONTROL); + } + + return 0; +} + static struct regulator_ops max77686_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, @@ -283,6 +341,7 @@ static struct regulator_ops max77686_buck_dvs_ops = { .name = "LDO"#num, \ .of_match = of_match_ptr("LDO"#num), \ .regulators_node = of_match_ptr("voltage-regulators"), \ + .of_parse_cb = max77686_of_parse_cb, \ .id = MAX77686_LDO##num, \ .ops = &max77686_ops, \ .type = REGULATOR_VOLTAGE, \ @@ -355,6 +414,7 @@ static struct regulator_ops max77686_buck_dvs_ops = { .name = "BUCK"#num, \ .of_match = of_match_ptr("BUCK"#num), \ .regulators_node = of_match_ptr("voltage-regulators"), \ + .of_parse_cb = max77686_of_parse_cb, \ .id = MAX77686_BUCK##num, \ .ops = &max77686_ops, \ .type = REGULATOR_VOLTAGE, \ diff --git a/drivers/regulator/max77843.c b/drivers/regulator/max77843.c new file mode 100644 index 000000000000..c132ef527cdd --- /dev/null +++ b/drivers/regulator/max77843.c @@ -0,0 +1,227 @@ +/* + * max77843.c - Regulator driver for the Maxim MAX77843 + * + * Copyright (C) 2015 Samsung Electronics + * Author: Jaewon Kim <jaewon02.kim@samsung.com> + * Author: Beomho Seo <beomho.seo@samsung.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/module.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/mfd/max77843-private.h> +#include <linux/regulator/of_regulator.h> + +enum max77843_regulator_type { + MAX77843_SAFEOUT1 = 0, + MAX77843_SAFEOUT2, + MAX77843_CHARGER, + + MAX77843_NUM, +}; + +static const unsigned int max77843_safeout_voltage_table[] = { + 4850000, + 4900000, + 4950000, + 3300000, +}; + +static int max77843_reg_is_enabled(struct regulator_dev *rdev) +{ + struct regmap *regmap = rdev->regmap; + int ret; + unsigned int reg; + + ret = regmap_read(regmap, rdev->desc->enable_reg, ®); + if (ret) { + dev_err(&rdev->dev, "Fialed to read charger register\n"); + return ret; + } + + return (reg & rdev->desc->enable_mask) == rdev->desc->enable_mask; +} + +static int max77843_reg_get_current_limit(struct regulator_dev *rdev) +{ + struct regmap *regmap = rdev->regmap; + unsigned int chg_min_uA = rdev->constraints->min_uA; + unsigned int chg_max_uA = rdev->constraints->max_uA; + unsigned int val; + int ret; + unsigned int reg, sel; + + ret = regmap_read(regmap, MAX77843_CHG_REG_CHG_CNFG_02, ®); + if (ret) { + dev_err(&rdev->dev, "Failed to read charger register\n"); + return ret; + } + + sel = reg & MAX77843_CHG_FAST_CHG_CURRENT_MASK; + + if (sel < 0x03) + sel = 0; + else + sel -= 2; + + val = chg_min_uA + MAX77843_CHG_FAST_CHG_CURRENT_STEP * sel; + if (val > chg_max_uA) + return -EINVAL; + + return val; +} + +static int max77843_reg_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + struct regmap *regmap = rdev->regmap; + unsigned int chg_min_uA = rdev->constraints->min_uA; + int sel = 0; + + while (chg_min_uA + MAX77843_CHG_FAST_CHG_CURRENT_STEP * sel < min_uA) + sel++; + + if (chg_min_uA + MAX77843_CHG_FAST_CHG_CURRENT_STEP * sel > max_uA) + return -EINVAL; + + sel += 2; + + return regmap_write(regmap, MAX77843_CHG_REG_CHG_CNFG_02, sel); +} + +static struct regulator_ops max77843_charger_ops = { + .is_enabled = max77843_reg_is_enabled, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .get_current_limit = max77843_reg_get_current_limit, + .set_current_limit = max77843_reg_set_current_limit, +}; + +static struct regulator_ops max77843_regulator_ops = { + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .list_voltage = regulator_list_voltage_table, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, +}; + +static const struct regulator_desc max77843_supported_regulators[] = { + [MAX77843_SAFEOUT1] = { + .name = "SAFEOUT1", + .id = MAX77843_SAFEOUT1, + .ops = &max77843_regulator_ops, + .of_match = of_match_ptr("SAFEOUT1"), + .regulators_node = of_match_ptr("regulators"), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(max77843_safeout_voltage_table), + .volt_table = max77843_safeout_voltage_table, + .enable_reg = MAX77843_SYS_REG_SAFEOUTCTRL, + .enable_mask = MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT1, + .vsel_reg = MAX77843_SYS_REG_SAFEOUTCTRL, + .vsel_mask = MAX77843_REG_SAFEOUTCTRL_SAFEOUT1_MASK, + }, + [MAX77843_SAFEOUT2] = { + .name = "SAFEOUT2", + .id = MAX77843_SAFEOUT2, + .ops = &max77843_regulator_ops, + .of_match = of_match_ptr("SAFEOUT2"), + .regulators_node = of_match_ptr("regulators"), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .n_voltages = ARRAY_SIZE(max77843_safeout_voltage_table), + .volt_table = max77843_safeout_voltage_table, + .enable_reg = MAX77843_SYS_REG_SAFEOUTCTRL, + .enable_mask = MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT2, + .vsel_reg = MAX77843_SYS_REG_SAFEOUTCTRL, + .vsel_mask = MAX77843_REG_SAFEOUTCTRL_SAFEOUT2_MASK, + }, + [MAX77843_CHARGER] = { + .name = "CHARGER", + .id = MAX77843_CHARGER, + .ops = &max77843_charger_ops, + .of_match = of_match_ptr("CHARGER"), + .regulators_node = of_match_ptr("regulators"), + .type = REGULATOR_CURRENT, + .owner = THIS_MODULE, + .enable_reg = MAX77843_CHG_REG_CHG_CNFG_00, + .enable_mask = MAX77843_CHG_MASK, + }, +}; + +static struct regmap *max77843_get_regmap(struct max77843 *max77843, int reg_id) +{ + switch (reg_id) { + case MAX77843_SAFEOUT1: + case MAX77843_SAFEOUT2: + return max77843->regmap; + case MAX77843_CHARGER: + return max77843->regmap_chg; + default: + return max77843->regmap; + } +} + +static int max77843_regulator_probe(struct platform_device *pdev) +{ + struct max77843 *max77843 = dev_get_drvdata(pdev->dev.parent); + struct regulator_config config = {}; + int i; + + config.dev = max77843->dev; + config.driver_data = max77843; + + for (i = 0; i < ARRAY_SIZE(max77843_supported_regulators); i++) { + struct regulator_dev *regulator; + + config.regmap = max77843_get_regmap(max77843, + max77843_supported_regulators[i].id); + + regulator = devm_regulator_register(&pdev->dev, + &max77843_supported_regulators[i], &config); + if (IS_ERR(regulator)) { + dev_err(&pdev->dev, + "Failed to regiser regulator-%d\n", i); + return PTR_ERR(regulator); + } + } + + return 0; +} + +static const struct platform_device_id max77843_regulator_id[] = { + { "max77843-regulator", }, + { /* sentinel */ }, +}; + +static struct platform_driver max77843_regulator_driver = { + .driver = { + .name = "max77843-regulator", + }, + .probe = max77843_regulator_probe, + .id_table = max77843_regulator_id, +}; + +static int __init max77843_regulator_init(void) +{ + return platform_driver_register(&max77843_regulator_driver); +} +subsys_initcall(max77843_regulator_init); + +static void __exit max77843_regulator_exit(void) +{ + platform_driver_unregister(&max77843_regulator_driver); +} +module_exit(max77843_regulator_exit); + +MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>"); +MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>"); +MODULE_DESCRIPTION("Maxim MAX77843 regulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/max8649.c b/drivers/regulator/max8649.c index c8bddcc8f911..81229579ece9 100644 --- a/drivers/regulator/max8649.c +++ b/drivers/regulator/max8649.c @@ -115,7 +115,7 @@ static unsigned int max8649_get_mode(struct regulator_dev *rdev) return REGULATOR_MODE_NORMAL; } -static struct regulator_ops max8649_dcdc_ops = { +static const struct regulator_ops max8649_dcdc_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .list_voltage = regulator_list_voltage_linear, @@ -143,7 +143,7 @@ static struct regulator_desc dcdc_desc = { .enable_is_inverted = true, }; -static struct regmap_config max8649_regmap_config = { +static const struct regmap_config max8649_regmap_config = { .reg_bits = 8, .val_bits = 8, }; diff --git a/drivers/regulator/mt6397-regulator.c b/drivers/regulator/mt6397-regulator.c new file mode 100644 index 000000000000..a5b2f4762677 --- /dev/null +++ b/drivers/regulator/mt6397-regulator.c @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Flora Fu <flora.fu@mediatek.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. + * + * 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/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/mfd/mt6397/core.h> +#include <linux/mfd/mt6397/registers.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/mt6397-regulator.h> +#include <linux/regulator/of_regulator.h> + +/* + * MT6397 regulators' information + * + * @desc: standard fields of regulator description. + * @qi: Mask for query enable signal status of regulators + * @vselon_reg: Register sections for hardware control mode of bucks + * @vselctrl_reg: Register for controlling the buck control mode. + * @vselctrl_mask: Mask for query buck's voltage control mode. + */ +struct mt6397_regulator_info { + struct regulator_desc desc; + u32 qi; + u32 vselon_reg; + u32 vselctrl_reg; + u32 vselctrl_mask; +}; + +#define MT6397_BUCK(match, vreg, min, max, step, volt_ranges, enreg, \ + vosel, vosel_mask, voselon, vosel_ctrl) \ +[MT6397_ID_##vreg] = { \ + .desc = { \ + .name = #vreg, \ + .of_match = of_match_ptr(match), \ + .ops = &mt6397_volt_range_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = MT6397_ID_##vreg, \ + .owner = THIS_MODULE, \ + .n_voltages = (max - min)/step + 1, \ + .linear_ranges = volt_ranges, \ + .n_linear_ranges = ARRAY_SIZE(volt_ranges), \ + .vsel_reg = vosel, \ + .vsel_mask = vosel_mask, \ + .enable_reg = enreg, \ + .enable_mask = BIT(0), \ + }, \ + .qi = BIT(13), \ + .vselon_reg = voselon, \ + .vselctrl_reg = vosel_ctrl, \ + .vselctrl_mask = BIT(1), \ +} + +#define MT6397_LDO(match, vreg, ldo_volt_table, enreg, enbit, vosel, \ + vosel_mask) \ +[MT6397_ID_##vreg] = { \ + .desc = { \ + .name = #vreg, \ + .of_match = of_match_ptr(match), \ + .ops = &mt6397_volt_table_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = MT6397_ID_##vreg, \ + .owner = THIS_MODULE, \ + .n_voltages = ARRAY_SIZE(ldo_volt_table), \ + .volt_table = ldo_volt_table, \ + .vsel_reg = vosel, \ + .vsel_mask = vosel_mask, \ + .enable_reg = enreg, \ + .enable_mask = BIT(enbit), \ + }, \ + .qi = BIT(15), \ +} + +#define MT6397_REG_FIXED(match, vreg, enreg, enbit, volt) \ +[MT6397_ID_##vreg] = { \ + .desc = { \ + .name = #vreg, \ + .of_match = of_match_ptr(match), \ + .ops = &mt6397_volt_fixed_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = MT6397_ID_##vreg, \ + .owner = THIS_MODULE, \ + .n_voltages = 1, \ + .enable_reg = enreg, \ + .enable_mask = BIT(enbit), \ + .min_uV = volt, \ + }, \ + .qi = BIT(15), \ +} + +static const struct regulator_linear_range buck_volt_range1[] = { + REGULATOR_LINEAR_RANGE(700000, 0, 0x7f, 6250), +}; + +static const struct regulator_linear_range buck_volt_range2[] = { + REGULATOR_LINEAR_RANGE(800000, 0, 0x7f, 6250), +}; + +static const struct regulator_linear_range buck_volt_range3[] = { + REGULATOR_LINEAR_RANGE(1500000, 0, 0x1f, 20000), +}; + +static const u32 ldo_volt_table1[] = { + 1500000, 1800000, 2500000, 2800000, +}; + +static const u32 ldo_volt_table2[] = { + 1800000, 3300000, +}; + +static const u32 ldo_volt_table3[] = { + 3000000, 3300000, +}; + +static const u32 ldo_volt_table4[] = { + 1220000, 1300000, 1500000, 1800000, 2500000, 2800000, 3000000, 3300000, +}; + +static const u32 ldo_volt_table5[] = { + 1200000, 1300000, 1500000, 1800000, 2500000, 2800000, 3000000, 3300000, +}; + +static const u32 ldo_volt_table5_v2[] = { + 1200000, 1000000, 1500000, 1800000, 2500000, 2800000, 3000000, 3300000, +}; + +static const u32 ldo_volt_table6[] = { + 1200000, 1300000, 1500000, 1800000, 2500000, 2800000, 3000000, 2000000, +}; + +static const u32 ldo_volt_table7[] = { + 1300000, 1500000, 1800000, 2000000, 2500000, 2800000, 3000000, 3300000, +}; + +static int mt6397_get_status(struct regulator_dev *rdev) +{ + int ret; + u32 regval; + struct mt6397_regulator_info *info = rdev_get_drvdata(rdev); + + ret = regmap_read(rdev->regmap, info->desc.enable_reg, ®val); + if (ret != 0) { + dev_err(&rdev->dev, "Failed to get enable reg: %d\n", ret); + return ret; + } + + return (regval & info->qi) ? REGULATOR_STATUS_ON : REGULATOR_STATUS_OFF; +} + +static struct regulator_ops mt6397_volt_range_ops = { + .list_voltage = regulator_list_voltage_linear_range, + .map_voltage = regulator_map_voltage_linear_range, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_status = mt6397_get_status, +}; + +static struct regulator_ops mt6397_volt_table_ops = { + .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_iterate, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_status = mt6397_get_status, +}; + +static struct regulator_ops mt6397_volt_fixed_ops = { + .list_voltage = regulator_list_voltage_linear, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_status = mt6397_get_status, +}; + +/* The array is indexed by id(MT6397_ID_XXX) */ +static struct mt6397_regulator_info mt6397_regulators[] = { + MT6397_BUCK("buck_vpca15", VPCA15, 700000, 1493750, 6250, + buck_volt_range1, MT6397_VCA15_CON7, MT6397_VCA15_CON9, 0x7f, + MT6397_VCA15_CON10, MT6397_VCA15_CON5), + MT6397_BUCK("buck_vpca7", VPCA7, 700000, 1493750, 6250, + buck_volt_range1, MT6397_VPCA7_CON7, MT6397_VPCA7_CON9, 0x7f, + MT6397_VPCA7_CON10, MT6397_VPCA7_CON5), + MT6397_BUCK("buck_vsramca15", VSRAMCA15, 700000, 1493750, 6250, + buck_volt_range1, MT6397_VSRMCA15_CON7, MT6397_VSRMCA15_CON9, + 0x7f, MT6397_VSRMCA15_CON10, MT6397_VSRMCA15_CON5), + MT6397_BUCK("buck_vsramca7", VSRAMCA7, 700000, 1493750, 6250, + buck_volt_range1, MT6397_VSRMCA7_CON7, MT6397_VSRMCA7_CON9, + 0x7f, MT6397_VSRMCA7_CON10, MT6397_VSRMCA7_CON5), + MT6397_BUCK("buck_vcore", VCORE, 700000, 1493750, 6250, + buck_volt_range1, MT6397_VCORE_CON7, MT6397_VCORE_CON9, 0x7f, + MT6397_VCORE_CON10, MT6397_VCORE_CON5), + MT6397_BUCK("buck_vgpu", VGPU, 700000, 1493750, 6250, buck_volt_range1, + MT6397_VGPU_CON7, MT6397_VGPU_CON9, 0x7f, + MT6397_VGPU_CON10, MT6397_VGPU_CON5), + MT6397_BUCK("buck_vdrm", VDRM, 800000, 1593750, 6250, buck_volt_range2, + MT6397_VDRM_CON7, MT6397_VDRM_CON9, 0x7f, + MT6397_VDRM_CON10, MT6397_VDRM_CON5), + MT6397_BUCK("buck_vio18", VIO18, 1500000, 2120000, 20000, + buck_volt_range3, MT6397_VIO18_CON7, MT6397_VIO18_CON9, 0x1f, + MT6397_VIO18_CON10, MT6397_VIO18_CON5), + MT6397_REG_FIXED("ldo_vtcxo", VTCXO, MT6397_ANALDO_CON0, 10, 2800000), + MT6397_REG_FIXED("ldo_va28", VA28, MT6397_ANALDO_CON1, 14, 2800000), + MT6397_LDO("ldo_vcama", VCAMA, ldo_volt_table1, + MT6397_ANALDO_CON2, 15, MT6397_ANALDO_CON6, 0xC0), + MT6397_REG_FIXED("ldo_vio28", VIO28, MT6397_DIGLDO_CON0, 14, 2800000), + MT6397_REG_FIXED("ldo_vusb", VUSB, MT6397_DIGLDO_CON1, 14, 3300000), + MT6397_LDO("ldo_vmc", VMC, ldo_volt_table2, + MT6397_DIGLDO_CON2, 12, MT6397_DIGLDO_CON29, 0x10), + MT6397_LDO("ldo_vmch", VMCH, ldo_volt_table3, + MT6397_DIGLDO_CON3, 14, MT6397_DIGLDO_CON17, 0x80), + MT6397_LDO("ldo_vemc3v3", VEMC3V3, ldo_volt_table3, + MT6397_DIGLDO_CON4, 14, MT6397_DIGLDO_CON18, 0x10), + MT6397_LDO("ldo_vgp1", VGP1, ldo_volt_table4, + MT6397_DIGLDO_CON5, 15, MT6397_DIGLDO_CON19, 0xE0), + MT6397_LDO("ldo_vgp2", VGP2, ldo_volt_table5, + MT6397_DIGLDO_CON6, 15, MT6397_DIGLDO_CON20, 0xE0), + MT6397_LDO("ldo_vgp3", VGP3, ldo_volt_table5, + MT6397_DIGLDO_CON7, 15, MT6397_DIGLDO_CON21, 0xE0), + MT6397_LDO("ldo_vgp4", VGP4, ldo_volt_table5, + MT6397_DIGLDO_CON8, 15, MT6397_DIGLDO_CON22, 0xE0), + MT6397_LDO("ldo_vgp5", VGP5, ldo_volt_table6, + MT6397_DIGLDO_CON9, 15, MT6397_DIGLDO_CON23, 0xE0), + MT6397_LDO("ldo_vgp6", VGP6, ldo_volt_table5, + MT6397_DIGLDO_CON10, 15, MT6397_DIGLDO_CON33, 0xE0), + MT6397_LDO("ldo_vibr", VIBR, ldo_volt_table7, + MT6397_DIGLDO_CON24, 15, MT6397_DIGLDO_CON25, 0xE00), +}; + +static int mt6397_set_buck_vosel_reg(struct platform_device *pdev) +{ + struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent); + int i; + u32 regval; + + for (i = 0; i < MT6397_MAX_REGULATOR; i++) { + if (mt6397_regulators[i].vselctrl_reg) { + if (regmap_read(mt6397->regmap, + mt6397_regulators[i].vselctrl_reg, + ®val) < 0) { + dev_err(&pdev->dev, + "Failed to read buck ctrl\n"); + return -EIO; + } + + if (regval & mt6397_regulators[i].vselctrl_mask) { + mt6397_regulators[i].desc.vsel_reg = + mt6397_regulators[i].vselon_reg; + } + } + } + + return 0; +} + +static int mt6397_regulator_probe(struct platform_device *pdev) +{ + struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent); + struct regulator_config config = {}; + struct regulator_dev *rdev; + int i; + u32 reg_value, version; + + /* Query buck controller to select activated voltage register part */ + if (mt6397_set_buck_vosel_reg(pdev)) + return -EIO; + + /* Read PMIC chip revision to update constraints and voltage table */ + if (regmap_read(mt6397->regmap, MT6397_CID, ®_value) < 0) { + dev_err(&pdev->dev, "Failed to read Chip ID\n"); + return -EIO; + } + dev_info(&pdev->dev, "Chip ID = 0x%x\n", reg_value); + + version = (reg_value & 0xFF); + switch (version) { + case MT6397_REGULATOR_ID91: + mt6397_regulators[MT6397_ID_VGP2].desc.volt_table = + ldo_volt_table5_v2; + break; + default: + break; + } + + for (i = 0; i < MT6397_MAX_REGULATOR; i++) { + config.dev = &pdev->dev; + config.driver_data = &mt6397_regulators[i]; + config.regmap = mt6397->regmap; + rdev = devm_regulator_register(&pdev->dev, + &mt6397_regulators[i].desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to register %s\n", + mt6397_regulators[i].desc.name); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static struct platform_driver mt6397_regulator_driver = { + .driver = { + .name = "mt6397-regulator", + }, + .probe = mt6397_regulator_probe, +}; + +module_platform_driver(mt6397_regulator_driver); + +MODULE_AUTHOR("Flora Fu <flora.fu@mediatek.com>"); +MODULE_DESCRIPTION("Regulator Driver for MediaTek MT6397 PMIC"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:mt6397-regulator"); diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 91eaaf010524..24e812c48d93 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -270,6 +270,7 @@ EXPORT_SYMBOL_GPL(of_regulator_match); struct regulator_init_data *regulator_of_get_init_data(struct device *dev, const struct regulator_desc *desc, + struct regulator_config *config, struct device_node **node) { struct device_node *search, *child; @@ -307,6 +308,16 @@ struct regulator_init_data *regulator_of_get_init_data(struct device *dev, break; } + if (desc->of_parse_cb) { + if (desc->of_parse_cb(child, desc, config)) { + dev_err(dev, + "driver callback failed to parse DT for regulator %s\n", + child->name); + init_data = NULL; + break; + } + } + of_node_get(child); *node = child; break; diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c index c879dff597ee..8cc8d1877c44 100644 --- a/drivers/regulator/pfuze100-regulator.c +++ b/drivers/regulator/pfuze100-regulator.c @@ -56,7 +56,7 @@ #define PFUZE100_VGEN5VOL 0x70 #define PFUZE100_VGEN6VOL 0x71 -enum chips { PFUZE100, PFUZE200 }; +enum chips { PFUZE100, PFUZE200, PFUZE3000 = 3 }; struct pfuze_regulator { struct regulator_desc desc; @@ -80,9 +80,18 @@ static const int pfuze100_vsnvs[] = { 1000000, 1100000, 1200000, 1300000, 1500000, 1800000, 3000000, }; +static const int pfuze3000_sw2lo[] = { + 1500000, 1550000, 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, +}; + +static const int pfuze3000_sw2hi[] = { + 2500000, 2800000, 2850000, 3000000, 3100000, 3150000, 3200000, 3300000, +}; + static const struct i2c_device_id pfuze_device_id[] = { {.name = "pfuze100", .driver_data = PFUZE100}, {.name = "pfuze200", .driver_data = PFUZE200}, + {.name = "pfuze3000", .driver_data = PFUZE3000}, { } }; MODULE_DEVICE_TABLE(i2c, pfuze_device_id); @@ -90,6 +99,7 @@ MODULE_DEVICE_TABLE(i2c, pfuze_device_id); static const struct of_device_id pfuze_dt_ids[] = { { .compatible = "fsl,pfuze100", .data = (void *)PFUZE100}, { .compatible = "fsl,pfuze200", .data = (void *)PFUZE200}, + { .compatible = "fsl,pfuze3000", .data = (void *)PFUZE3000}, { } }; MODULE_DEVICE_TABLE(of, pfuze_dt_ids); @@ -219,6 +229,60 @@ static struct regulator_ops pfuze100_swb_regulator_ops = { .stby_mask = 0x20, \ } +#define PFUZE3000_VCC_REG(_chip, _name, base, min, max, step) { \ + .desc = { \ + .name = #_name, \ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .ops = &pfuze100_ldo_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = _chip ## _ ## _name, \ + .owner = THIS_MODULE, \ + .min_uV = (min), \ + .uV_step = (step), \ + .vsel_reg = (base), \ + .vsel_mask = 0x3, \ + .enable_reg = (base), \ + .enable_mask = 0x10, \ + }, \ + .stby_reg = (base), \ + .stby_mask = 0x20, \ +} + + +#define PFUZE3000_SW2_REG(_chip, _name, base, min, max, step) { \ + .desc = { \ + .name = #_name,\ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .ops = &pfuze100_sw_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = _chip ## _ ## _name, \ + .owner = THIS_MODULE, \ + .min_uV = (min), \ + .uV_step = (step), \ + .vsel_reg = (base) + PFUZE100_VOL_OFFSET, \ + .vsel_mask = 0x7, \ + }, \ + .stby_reg = (base) + PFUZE100_STANDBY_OFFSET, \ + .stby_mask = 0x7, \ +} + +#define PFUZE3000_SW3_REG(_chip, _name, base, min, max, step) { \ + .desc = { \ + .name = #_name,\ + .n_voltages = ((max) - (min)) / (step) + 1, \ + .ops = &pfuze100_sw_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = _chip ## _ ## _name, \ + .owner = THIS_MODULE, \ + .min_uV = (min), \ + .uV_step = (step), \ + .vsel_reg = (base) + PFUZE100_VOL_OFFSET, \ + .vsel_mask = 0xf, \ + }, \ + .stby_reg = (base) + PFUZE100_STANDBY_OFFSET, \ + .stby_mask = 0xf, \ +} + /* PFUZE100 */ static struct pfuze_regulator pfuze100_regulators[] = { PFUZE100_SW_REG(PFUZE100, SW1AB, PFUZE100_SW1ABVOL, 300000, 1875000, 25000), @@ -254,6 +318,22 @@ static struct pfuze_regulator pfuze200_regulators[] = { PFUZE100_VGEN_REG(PFUZE200, VGEN6, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), }; +static struct pfuze_regulator pfuze3000_regulators[] = { + PFUZE100_SW_REG(PFUZE3000, SW1A, PFUZE100_SW1ABVOL, 700000, 1475000, 25000), + PFUZE100_SW_REG(PFUZE3000, SW1B, PFUZE100_SW1CVOL, 700000, 1475000, 25000), + PFUZE100_SWB_REG(PFUZE3000, SW2, PFUZE100_SW2VOL, 0x7, pfuze3000_sw2lo), + PFUZE3000_SW3_REG(PFUZE3000, SW3, PFUZE100_SW3AVOL, 900000, 1650000, 50000), + PFUZE100_SWB_REG(PFUZE3000, SWBST, PFUZE100_SWBSTCON1, 0x3, pfuze100_swbst), + PFUZE100_SWB_REG(PFUZE3000, VSNVS, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs), + PFUZE100_FIXED_REG(PFUZE3000, VREFDDR, PFUZE100_VREFDDRCON, 750000), + PFUZE100_VGEN_REG(PFUZE3000, VLDO1, PFUZE100_VGEN1VOL, 1800000, 3300000, 100000), + PFUZE100_VGEN_REG(PFUZE3000, VLDO2, PFUZE100_VGEN2VOL, 800000, 1550000, 50000), + PFUZE3000_VCC_REG(PFUZE3000, VCCSD, PFUZE100_VGEN3VOL, 2850000, 3300000, 150000), + PFUZE3000_VCC_REG(PFUZE3000, V33, PFUZE100_VGEN4VOL, 2850000, 3300000, 150000), + PFUZE100_VGEN_REG(PFUZE3000, VLDO3, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000), + PFUZE100_VGEN_REG(PFUZE3000, VLDO4, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), +}; + static struct pfuze_regulator *pfuze_regulators; #ifdef CONFIG_OF @@ -294,6 +374,24 @@ static struct of_regulator_match pfuze200_matches[] = { { .name = "vgen6", }, }; +/* PFUZE3000 */ +static struct of_regulator_match pfuze3000_matches[] = { + + { .name = "sw1a", }, + { .name = "sw1b", }, + { .name = "sw2", }, + { .name = "sw3", }, + { .name = "swbst", }, + { .name = "vsnvs", }, + { .name = "vrefddr", }, + { .name = "vldo1", }, + { .name = "vldo2", }, + { .name = "vccsd", }, + { .name = "v33", }, + { .name = "vldo3", }, + { .name = "vldo4", }, +}; + static struct of_regulator_match *pfuze_matches; static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) @@ -313,6 +411,11 @@ static int pfuze_parse_regulators_dt(struct pfuze_chip *chip) } switch (chip->chip_id) { + case PFUZE3000: + pfuze_matches = pfuze3000_matches; + ret = of_regulator_match(dev, parent, pfuze3000_matches, + ARRAY_SIZE(pfuze3000_matches)); + break; case PFUZE200: pfuze_matches = pfuze200_matches; ret = of_regulator_match(dev, parent, pfuze200_matches, @@ -378,7 +481,8 @@ static int pfuze_identify(struct pfuze_chip *pfuze_chip) * as ID=8 in PFUZE100 */ dev_info(pfuze_chip->dev, "Assuming misprogrammed ID=0x8"); - } else if ((value & 0x0f) != pfuze_chip->chip_id) { + } else if ((value & 0x0f) != pfuze_chip->chip_id && + (value & 0xf0) >> 4 != pfuze_chip->chip_id) { /* device id NOT match with your setting */ dev_warn(pfuze_chip->dev, "Illegal ID: %x\n", value); return -ENODEV; @@ -417,7 +521,7 @@ static int pfuze100_regulator_probe(struct i2c_client *client, int i, ret; const struct of_device_id *match; u32 regulator_num; - u32 sw_check_start, sw_check_end; + u32 sw_check_start, sw_check_end, sw_hi = 0x40; pfuze_chip = devm_kzalloc(&client->dev, sizeof(*pfuze_chip), GFP_KERNEL); @@ -458,13 +562,19 @@ static int pfuze100_regulator_probe(struct i2c_client *client, /* use the right regulators after identify the right device */ switch (pfuze_chip->chip_id) { + case PFUZE3000: + pfuze_regulators = pfuze3000_regulators; + regulator_num = ARRAY_SIZE(pfuze3000_regulators); + sw_check_start = PFUZE3000_SW2; + sw_check_end = PFUZE3000_SW2; + sw_hi = 1 << 3; + break; case PFUZE200: pfuze_regulators = pfuze200_regulators; regulator_num = ARRAY_SIZE(pfuze200_regulators); sw_check_start = PFUZE200_SW2; sw_check_end = PFUZE200_SW3B; break; - case PFUZE100: default: pfuze_regulators = pfuze100_regulators; @@ -474,7 +584,8 @@ static int pfuze100_regulator_probe(struct i2c_client *client, break; } dev_info(&client->dev, "pfuze%s found.\n", - (pfuze_chip->chip_id == PFUZE100) ? "100" : "200"); + (pfuze_chip->chip_id == PFUZE100) ? "100" : + ((pfuze_chip->chip_id == PFUZE200) ? "200" : "3000")); memcpy(pfuze_chip->regulator_descs, pfuze_regulators, sizeof(pfuze_chip->regulator_descs)); @@ -498,10 +609,15 @@ static int pfuze100_regulator_probe(struct i2c_client *client, /* SW2~SW4 high bit check and modify the voltage value table */ if (i >= sw_check_start && i <= sw_check_end) { regmap_read(pfuze_chip->regmap, desc->vsel_reg, &val); - if (val & 0x40) { - desc->min_uV = 800000; - desc->uV_step = 50000; - desc->n_voltages = 51; + if (val & sw_hi) { + if (pfuze_chip->chip_id == PFUZE3000) { + desc->volt_table = pfuze3000_sw2hi; + desc->n_voltages = ARRAY_SIZE(pfuze3000_sw2hi); + } else { + desc->min_uV = 800000; + desc->uV_step = 50000; + desc->n_voltages = 51; + } } } diff --git a/drivers/regulator/qcom_rpm-regulator.c b/drivers/regulator/qcom_rpm-regulator.c index 8364ff331a81..e8647f7cf25e 100644 --- a/drivers/regulator/qcom_rpm-regulator.c +++ b/drivers/regulator/qcom_rpm-regulator.c @@ -227,9 +227,11 @@ static int rpm_reg_set_mV_sel(struct regulator_dev *rdev, return uV; mutex_lock(&vreg->lock); - vreg->uV = uV; if (vreg->is_enabled) - ret = rpm_reg_write(vreg, req, vreg->uV / 1000); + ret = rpm_reg_write(vreg, req, uV / 1000); + + if (!ret) + vreg->uV = uV; mutex_unlock(&vreg->lock); return ret; @@ -252,9 +254,11 @@ static int rpm_reg_set_uV_sel(struct regulator_dev *rdev, return uV; mutex_lock(&vreg->lock); - vreg->uV = uV; if (vreg->is_enabled) - ret = rpm_reg_write(vreg, req, vreg->uV); + ret = rpm_reg_write(vreg, req, uV); + + if (!ret) + vreg->uV = uV; mutex_unlock(&vreg->lock); return ret; @@ -674,6 +678,7 @@ static int rpm_reg_probe(struct platform_device *pdev) vreg->desc.owner = THIS_MODULE; vreg->desc.type = REGULATOR_VOLTAGE; vreg->desc.name = pdev->dev.of_node->name; + vreg->desc.supply_name = "vin"; vreg->rpm = dev_get_drvdata(pdev->dev.parent); if (!vreg->rpm) { @@ -768,7 +773,7 @@ static int rpm_reg_probe(struct platform_device *pdev) break; } - if (force_mode < 0) { + if (force_mode == -1) { dev_err(&pdev->dev, "invalid force mode\n"); return -EINVAL; } diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c index c94a3e0f3b91..1f93b752a81c 100644 --- a/drivers/regulator/rk808-regulator.c +++ b/drivers/regulator/rk808-regulator.c @@ -97,7 +97,7 @@ static int rk808_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) RK808_RAMP_RATE_MASK, ramp_value); } -int rk808_set_suspend_voltage(struct regulator_dev *rdev, int uv) +static int rk808_set_suspend_voltage(struct regulator_dev *rdev, int uv) { unsigned int reg; int sel = regulator_map_voltage_linear_range(rdev, uv, uv); @@ -112,7 +112,7 @@ int rk808_set_suspend_voltage(struct regulator_dev *rdev, int uv) sel); } -int rk808_set_suspend_enable(struct regulator_dev *rdev) +static int rk808_set_suspend_enable(struct regulator_dev *rdev) { unsigned int reg; @@ -123,7 +123,7 @@ int rk808_set_suspend_enable(struct regulator_dev *rdev) 0); } -int rk808_set_suspend_disable(struct regulator_dev *rdev) +static int rk808_set_suspend_disable(struct regulator_dev *rdev) { unsigned int reg; diff --git a/drivers/regulator/rt5033-regulator.c b/drivers/regulator/rt5033-regulator.c index 870cc49438db..96d2c18e051a 100644 --- a/drivers/regulator/rt5033-regulator.c +++ b/drivers/regulator/rt5033-regulator.c @@ -36,6 +36,8 @@ static struct regulator_ops rt5033_buck_ops = { static const struct regulator_desc rt5033_supported_regulators[] = { [RT5033_BUCK] = { .name = "BUCK", + .of_match = of_match_ptr("BUCK"), + .regulators_node = of_match_ptr("regulators"), .id = RT5033_BUCK, .ops = &rt5033_buck_ops, .type = REGULATOR_VOLTAGE, @@ -50,6 +52,8 @@ static const struct regulator_desc rt5033_supported_regulators[] = { }, [RT5033_LDO] = { .name = "LDO", + .of_match = of_match_ptr("LDO"), + .regulators_node = of_match_ptr("regulators"), .id = RT5033_LDO, .ops = &rt5033_buck_ops, .type = REGULATOR_VOLTAGE, @@ -64,6 +68,8 @@ static const struct regulator_desc rt5033_supported_regulators[] = { }, [RT5033_SAFE_LDO] = { .name = "SAFE_LDO", + .of_match = of_match_ptr("SAFE_LDO"), + .regulators_node = of_match_ptr("regulators"), .id = RT5033_SAFE_LDO, .ops = &rt5033_safe_ldo_ops, .type = REGULATOR_VOLTAGE, @@ -81,7 +87,7 @@ static int rt5033_regulator_probe(struct platform_device *pdev) int ret, i; struct regulator_config config = {}; - config.dev = &pdev->dev; + config.dev = rt5033->dev; config.driver_data = rt5033; for (i = 0; i < ARRAY_SIZE(rt5033_supported_regulators); i++) { diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index 2809ae0d6bcd..ff828117798f 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -405,6 +405,40 @@ static struct regulator_ops s2mps14_reg_ops; .enable_mask = S2MPS14_ENABLE_MASK \ } +#define regulator_desc_s2mps13_buck7(num, min, step, min_sel) { \ + .name = "BUCK"#num, \ + .id = S2MPS13_BUCK##num, \ + .ops = &s2mps14_reg_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = min, \ + .uV_step = step, \ + .linear_min_sel = min_sel, \ + .n_voltages = S2MPS14_BUCK_N_VOLTAGES, \ + .ramp_delay = S2MPS13_BUCK_RAMP_DELAY, \ + .vsel_reg = S2MPS13_REG_B1OUT + (num) * 2 - 1, \ + .vsel_mask = S2MPS14_BUCK_VSEL_MASK, \ + .enable_reg = S2MPS13_REG_B1CTRL + (num - 1) * 2, \ + .enable_mask = S2MPS14_ENABLE_MASK \ +} + +#define regulator_desc_s2mps13_buck8_10(num, min, step, min_sel) { \ + .name = "BUCK"#num, \ + .id = S2MPS13_BUCK##num, \ + .ops = &s2mps14_reg_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .min_uV = min, \ + .uV_step = step, \ + .linear_min_sel = min_sel, \ + .n_voltages = S2MPS14_BUCK_N_VOLTAGES, \ + .ramp_delay = S2MPS13_BUCK_RAMP_DELAY, \ + .vsel_reg = S2MPS13_REG_B1OUT + (num) * 2 - 1, \ + .vsel_mask = S2MPS14_BUCK_VSEL_MASK, \ + .enable_reg = S2MPS13_REG_B1CTRL + (num) * 2 - 1, \ + .enable_mask = S2MPS14_ENABLE_MASK \ +} + static const struct regulator_desc s2mps13_regulators[] = { regulator_desc_s2mps13_ldo(1, MIN_800_MV, STEP_12_5_MV, 0x00), regulator_desc_s2mps13_ldo(2, MIN_1400_MV, STEP_50_MV, 0x0C), @@ -452,10 +486,10 @@ static const struct regulator_desc s2mps13_regulators[] = { regulator_desc_s2mps13_buck(4, MIN_500_MV, STEP_6_25_MV, 0x10), regulator_desc_s2mps13_buck(5, MIN_500_MV, STEP_6_25_MV, 0x10), regulator_desc_s2mps13_buck(6, MIN_500_MV, STEP_6_25_MV, 0x10), - regulator_desc_s2mps13_buck(7, MIN_500_MV, STEP_6_25_MV, 0x10), - regulator_desc_s2mps13_buck(8, MIN_1000_MV, STEP_12_5_MV, 0x20), - regulator_desc_s2mps13_buck(9, MIN_1000_MV, STEP_12_5_MV, 0x20), - regulator_desc_s2mps13_buck(10, MIN_500_MV, STEP_6_25_MV, 0x10), + regulator_desc_s2mps13_buck7(7, MIN_500_MV, STEP_6_25_MV, 0x10), + regulator_desc_s2mps13_buck8_10(8, MIN_1000_MV, STEP_12_5_MV, 0x20), + regulator_desc_s2mps13_buck8_10(9, MIN_1000_MV, STEP_12_5_MV, 0x20), + regulator_desc_s2mps13_buck8_10(10, MIN_500_MV, STEP_6_25_MV, 0x10), }; static int s2mps14_regulator_enable(struct regulator_dev *rdev) diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index 7380af8bd50d..b941e564b3f3 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -173,7 +173,7 @@ static int tps65023_dcdc_set_voltage_sel(struct regulator_dev *dev, } /* Operations permitted on VDCDCx */ -static struct regulator_ops tps65023_dcdc_ops = { +static const struct regulator_ops tps65023_dcdc_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -184,7 +184,7 @@ static struct regulator_ops tps65023_dcdc_ops = { }; /* Operations permitted on LDOx */ -static struct regulator_ops tps65023_ldo_ops = { +static const struct regulator_ops tps65023_ldo_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -194,7 +194,7 @@ static struct regulator_ops tps65023_ldo_ops = { .map_voltage = regulator_map_voltage_ascend, }; -static struct regmap_config tps65023_regmap_config = { +static const struct regmap_config tps65023_regmap_config = { .reg_bits = 8, .val_bits = 8, }; diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c index eebc52cb6984..3d95c87160b3 100644 --- a/drivers/reset/reset-sunxi.c +++ b/drivers/reset/reset-sunxi.c @@ -102,6 +102,8 @@ static int sunxi_reset_init(struct device_node *np) goto err_alloc; } + spin_lock_init(&data->lock); + data->rcdev.owner = THIS_MODULE; data->rcdev.nr_resets = size * 32; data->rcdev.ops = &sunxi_reset_ops; @@ -157,6 +159,8 @@ static int sunxi_reset_probe(struct platform_device *pdev) if (IS_ERR(data->membase)) return PTR_ERR(data->membase); + spin_lock_init(&data->lock); + data->rcdev.owner = THIS_MODULE; data->rcdev.nr_resets = resource_size(res) * 32; data->rcdev.ops = &sunxi_reset_ops; diff --git a/drivers/rtc/hctosys.c b/drivers/rtc/hctosys.c index 4aa60d74004e..6c719f23520a 100644 --- a/drivers/rtc/hctosys.c +++ b/drivers/rtc/hctosys.c @@ -26,7 +26,7 @@ static int __init rtc_hctosys(void) { int err = -ENODEV; struct rtc_time tm; - struct timespec tv = { + struct timespec64 tv64 = { .tv_nsec = NSEC_PER_SEC >> 1, }; struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); @@ -45,25 +45,17 @@ static int __init rtc_hctosys(void) } - err = rtc_valid_tm(&tm); - if (err) { - dev_err(rtc->dev.parent, - "hctosys: invalid date/time\n"); - goto err_invalid; - } - - rtc_tm_to_time(&tm, &tv.tv_sec); + tv64.tv_sec = rtc_tm_to_time64(&tm); - err = do_settimeofday(&tv); + err = do_settimeofday64(&tv64); dev_info(rtc->dev.parent, "setting system clock to " - "%d-%02d-%02d %02d:%02d:%02d UTC (%u)\n", + "%d-%02d-%02d %02d:%02d:%02d UTC (%lld)\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, - (unsigned int) tv.tv_sec); + (long long) tv64.tv_sec); -err_invalid: err_read: rtc_class_close(rtc); diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 45bfc28ee3aa..37215cf983e9 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -73,10 +73,8 @@ int rtc_set_time(struct rtc_device *rtc, struct rtc_time *tm) else if (rtc->ops->set_time) err = rtc->ops->set_time(rtc->dev.parent, tm); else if (rtc->ops->set_mmss) { - unsigned long secs; - err = rtc_tm_to_time(tm, &secs); - if (err == 0) - err = rtc->ops->set_mmss(rtc->dev.parent, secs); + time64_t secs64 = rtc_tm_to_time64(tm); + err = rtc->ops->set_mmss(rtc->dev.parent, secs64); } else err = -EINVAL; @@ -105,7 +103,7 @@ int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs) err = rtc->ops->read_time(rtc->dev.parent, &old); if (err == 0) { - rtc_time_to_tm(secs, &new); + rtc_time64_to_tm(secs, &new); /* * avoid writing when we're going to change the day of @@ -157,7 +155,7 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) int err; struct rtc_time before, now; int first_time = 1; - unsigned long t_now, t_alm; + time64_t t_now, t_alm; enum { none, day, month, year } missing = none; unsigned days; @@ -258,8 +256,8 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) } /* with luck, no rollover is needed */ - rtc_tm_to_time(&now, &t_now); - rtc_tm_to_time(&alarm->time, &t_alm); + t_now = rtc_tm_to_time64(&now); + t_alm = rtc_tm_to_time64(&alarm->time); if (t_now < t_alm) goto done; @@ -273,7 +271,7 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) case day: dev_dbg(&rtc->dev, "alarm rollover: %s\n", "day"); t_alm += 24 * 60 * 60; - rtc_time_to_tm(t_alm, &alarm->time); + rtc_time64_to_tm(t_alm, &alarm->time); break; /* Month rollover ... if it's the 31th, an alarm on the 3rd will @@ -346,19 +344,19 @@ EXPORT_SYMBOL_GPL(rtc_read_alarm); static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) { struct rtc_time tm; - long now, scheduled; + time64_t now, scheduled; int err; err = rtc_valid_tm(&alarm->time); if (err) return err; - rtc_tm_to_time(&alarm->time, &scheduled); + scheduled = rtc_tm_to_time64(&alarm->time); /* Make sure we're not setting alarms in the past */ err = __rtc_read_time(rtc, &tm); if (err) return err; - rtc_tm_to_time(&tm, &now); + now = rtc_tm_to_time64(&tm); if (scheduled <= now) return -ETIME; /* diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index d04939369251..799c34bcb26f 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c @@ -304,12 +304,12 @@ static long rtc_dev_ioctl(struct file *file, * Not supported here. */ { - unsigned long now, then; + time64_t now, then; err = rtc_read_time(rtc, &tm); if (err < 0) return err; - rtc_tm_to_time(&tm, &now); + now = rtc_tm_to_time64(&tm); alarm.time.tm_mday = tm.tm_mday; alarm.time.tm_mon = tm.tm_mon; @@ -317,11 +317,11 @@ static long rtc_dev_ioctl(struct file *file, err = rtc_valid_tm(&alarm.time); if (err < 0) return err; - rtc_tm_to_time(&alarm.time, &then); + then = rtc_tm_to_time64(&alarm.time); /* alarm may need to wrap into tomorrow */ if (then < now) { - rtc_time_to_tm(now + 24 * 60 * 60, &tm); + rtc_time64_to_tm(now + 24 * 60 * 60, &tm); alarm.time.tm_mday = tm.tm_mday; alarm.time.tm_mon = tm.tm_mon; alarm.time.tm_year = tm.tm_year; diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c index b37b0c80bd5a..cb989cd00b14 100644 --- a/drivers/rtc/rtc-efi.c +++ b/drivers/rtc/rtc-efi.c @@ -218,6 +218,7 @@ static int __init efi_rtc_probe(struct platform_device *dev) if (IS_ERR(rtc)) return PTR_ERR(rtc); + rtc->uie_unsupported = 1; platform_set_drvdata(dev, rtc); return 0; diff --git a/drivers/rtc/rtc-s5m.c b/drivers/rtc/rtc-s5m.c index b5e7c4670205..89ac1d5083c6 100644 --- a/drivers/rtc/rtc-s5m.c +++ b/drivers/rtc/rtc-s5m.c @@ -832,6 +832,7 @@ static SIMPLE_DEV_PM_OPS(s5m_rtc_pm_ops, s5m_rtc_suspend, s5m_rtc_resume); static const struct platform_device_id s5m_rtc_id[] = { { "s5m-rtc", S5M8767X }, { "s2mps14-rtc", S2MPS14X }, + { }, }; static struct platform_driver s5m_rtc_driver = { diff --git a/drivers/rtc/systohc.c b/drivers/rtc/systohc.c index bf3e242ccc5c..eb71872d0361 100644 --- a/drivers/rtc/systohc.c +++ b/drivers/rtc/systohc.c @@ -20,16 +20,16 @@ * * If temporary failure is indicated the caller should try again 'soon' */ -int rtc_set_ntp_time(struct timespec now) +int rtc_set_ntp_time(struct timespec64 now) { struct rtc_device *rtc; struct rtc_time tm; int err = -ENODEV; if (now.tv_nsec < (NSEC_PER_SEC >> 1)) - rtc_time_to_tm(now.tv_sec, &tm); + rtc_time64_to_tm(now.tv_sec, &tm); else - rtc_time_to_tm(now.tv_sec + 1, &tm); + rtc_time64_to_tm(now.tv_sec + 1, &tm); rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); if (rtc) { diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 91e97ec01418..4d41bf75c233 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -1163,9 +1163,13 @@ static inline int ap_test_config_card_id(unsigned int id) */ static inline int ap_test_config_domain(unsigned int domain) { - if (!ap_configuration) - return 1; - return ap_test_config(ap_configuration->aqm, domain); + if (!ap_configuration) /* QCI not supported */ + if (domain < 16) + return 1; /* then domains 0...15 are configured */ + else + return 0; + else + return ap_test_config(ap_configuration->aqm, domain); } /** diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c index 213e54ee8a66..d609ca09aa94 100644 --- a/drivers/s390/net/claw.c +++ b/drivers/s390/net/claw.c @@ -109,10 +109,8 @@ static debug_info_t *claw_dbf_trace; static void claw_unregister_debug_facility(void) { - if (claw_dbf_setup) - debug_unregister(claw_dbf_setup); - if (claw_dbf_trace) - debug_unregister(claw_dbf_trace); + debug_unregister(claw_dbf_setup); + debug_unregister(claw_dbf_trace); } static int diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c index fb92524d24ef..fd5944bbe224 100644 --- a/drivers/s390/net/ctcm_fsms.c +++ b/drivers/s390/net/ctcm_fsms.c @@ -251,13 +251,11 @@ static void chx_txdone(fsm_instance *fi, int event, void *arg) int first = 1; int i; unsigned long duration; - struct timespec done_stamp = current_kernel_time(); /* xtime */ + unsigned long done_stamp = jiffies; CTCM_PR_DEBUG("%s(%s): %s\n", __func__, ch->id, dev->name); - duration = - (done_stamp.tv_sec - ch->prof.send_stamp.tv_sec) * 1000000 + - (done_stamp.tv_nsec - ch->prof.send_stamp.tv_nsec) / 1000; + duration = done_stamp - ch->prof.send_stamp; if (duration > ch->prof.tx_time) ch->prof.tx_time = duration; @@ -307,7 +305,7 @@ static void chx_txdone(fsm_instance *fi, int event, void *arg) spin_unlock(&ch->collect_lock); ch->ccw[1].count = ch->trans_skb->len; fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch); - ch->prof.send_stamp = current_kernel_time(); /* xtime */ + ch->prof.send_stamp = jiffies; rc = ccw_device_start(ch->cdev, &ch->ccw[0], (unsigned long)ch, 0xff, 0); ch->prof.doios_multi++; @@ -1229,14 +1227,12 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg) int rc; struct th_header *header; struct pdu *p_header; - struct timespec done_stamp = current_kernel_time(); /* xtime */ + unsigned long done_stamp = jiffies; CTCM_PR_DEBUG("Enter %s: %s cp:%i\n", __func__, dev->name, smp_processor_id()); - duration = - (done_stamp.tv_sec - ch->prof.send_stamp.tv_sec) * 1000000 + - (done_stamp.tv_nsec - ch->prof.send_stamp.tv_nsec) / 1000; + duration = done_stamp - ch->prof.send_stamp; if (duration > ch->prof.tx_time) ch->prof.tx_time = duration; @@ -1361,7 +1357,7 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg) ch->ccw[1].count = ch->trans_skb->len; fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch); - ch->prof.send_stamp = current_kernel_time(); /* xtime */ + ch->prof.send_stamp = jiffies; if (do_debug_ccw) ctcmpc_dumpit((char *)&ch->ccw[0], sizeof(struct ccw1) * 3); rc = ccw_device_start(ch->cdev, &ch->ccw[0], @@ -1827,7 +1823,7 @@ static void ctcmpc_chx_send_sweep(fsm_instance *fsm, int event, void *arg) fsm_newstate(wch->fsm, CTC_STATE_TX); spin_lock_irqsave(get_ccwdev_lock(wch->cdev), saveflags); - wch->prof.send_stamp = current_kernel_time(); /* xtime */ + wch->prof.send_stamp = jiffies; rc = ccw_device_start(wch->cdev, &wch->ccw[3], (unsigned long) wch, 0xff, 0); spin_unlock_irqrestore(get_ccwdev_lock(wch->cdev), saveflags); diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index e056dd4fe44d..05c37d6d4afe 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -567,7 +567,7 @@ static int ctcm_transmit_skb(struct channel *ch, struct sk_buff *skb) fsm_newstate(ch->fsm, CTC_STATE_TX); fsm_addtimer(&ch->timer, CTCM_TIME_5_SEC, CTC_EVENT_TIMER, ch); spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags); - ch->prof.send_stamp = current_kernel_time(); /* xtime */ + ch->prof.send_stamp = jiffies; rc = ccw_device_start(ch->cdev, &ch->ccw[ccw_idx], (unsigned long)ch, 0xff, 0); spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags); @@ -831,7 +831,7 @@ static int ctcmpc_transmit_skb(struct channel *ch, struct sk_buff *skb) sizeof(struct ccw1) * 3); spin_lock_irqsave(get_ccwdev_lock(ch->cdev), saveflags); - ch->prof.send_stamp = current_kernel_time(); /* xtime */ + ch->prof.send_stamp = jiffies; rc = ccw_device_start(ch->cdev, &ch->ccw[ccw_idx], (unsigned long)ch, 0xff, 0); spin_unlock_irqrestore(get_ccwdev_lock(ch->cdev), saveflags); diff --git a/drivers/s390/net/ctcm_main.h b/drivers/s390/net/ctcm_main.h index 477c933685f3..6f4417c80247 100644 --- a/drivers/s390/net/ctcm_main.h +++ b/drivers/s390/net/ctcm_main.h @@ -121,7 +121,7 @@ struct ctcm_profile { unsigned long doios_multi; unsigned long txlen; unsigned long tx_time; - struct timespec send_stamp; + unsigned long send_stamp; }; /* diff --git a/drivers/s390/net/ctcm_sysfs.c b/drivers/s390/net/ctcm_sysfs.c index 47773c4d235a..ddb0aa321339 100644 --- a/drivers/s390/net/ctcm_sysfs.c +++ b/drivers/s390/net/ctcm_sysfs.c @@ -100,8 +100,8 @@ static void ctcm_print_statistics(struct ctcm_priv *priv) priv->channel[WRITE]->prof.doios_multi); p += sprintf(p, " Netto bytes written: %ld\n", priv->channel[WRITE]->prof.txlen); - p += sprintf(p, " Max. TX IO-time: %ld\n", - priv->channel[WRITE]->prof.tx_time); + p += sprintf(p, " Max. TX IO-time: %u\n", + jiffies_to_usecs(priv->channel[WRITE]->prof.tx_time)); printk(KERN_INFO "Statistics for %s:\n%s", priv->channel[CTCM_WRITE]->netdev->name, sbuf); diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index 92190aa20b9f..00b7d9c9fe48 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -88,10 +88,8 @@ static debug_info_t *lcs_dbf_trace; static void lcs_unregister_debug_facility(void) { - if (lcs_dbf_setup) - debug_unregister(lcs_dbf_setup); - if (lcs_dbf_trace) - debug_unregister(lcs_dbf_trace); + debug_unregister(lcs_dbf_setup); + debug_unregister(lcs_dbf_trace); } static int diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 0a87809c8af7..33f7040d711d 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -178,7 +178,7 @@ struct connection_profile { unsigned long doios_multi; unsigned long txlen; unsigned long tx_time; - struct timespec send_stamp; + unsigned long send_stamp; unsigned long tx_pending; unsigned long tx_max_pending; }; @@ -487,12 +487,9 @@ DEFINE_PER_CPU(char[256], iucv_dbf_txt_buf); static void iucv_unregister_dbf_views(void) { - if (iucv_dbf_setup) - debug_unregister(iucv_dbf_setup); - if (iucv_dbf_data) - debug_unregister(iucv_dbf_data); - if (iucv_dbf_trace) - debug_unregister(iucv_dbf_trace); + debug_unregister(iucv_dbf_setup); + debug_unregister(iucv_dbf_data); + debug_unregister(iucv_dbf_trace); } static int iucv_register_dbf_views(void) { @@ -786,7 +783,7 @@ static void conn_action_txdone(fsm_instance *fi, int event, void *arg) header.next = 0; memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN); - conn->prof.send_stamp = current_kernel_time(); + conn->prof.send_stamp = jiffies; txmsg.class = 0; txmsg.tag = 0; rc = iucv_message_send(conn->path, &txmsg, 0, 0, @@ -1220,7 +1217,7 @@ static int netiucv_transmit_skb(struct iucv_connection *conn, memcpy(skb_put(nskb, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN); fsm_newstate(conn->fsm, CONN_STATE_TX); - conn->prof.send_stamp = current_kernel_time(); + conn->prof.send_stamp = jiffies; msg.tag = 1; msg.class = 0; diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h index 7a8bb9f78e76..3abac028899f 100644 --- a/drivers/s390/net/qeth_core.h +++ b/drivers/s390/net/qeth_core.h @@ -596,7 +596,6 @@ struct qeth_channel { struct ccw1 ccw; spinlock_t iob_lock; wait_queue_head_t wait_q; - struct tasklet_struct irq_tasklet; struct ccw_device *ccwdev; /*command buffer for control data*/ struct qeth_cmd_buffer iob[QETH_CMD_BUFFER_NO]; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index f407e3763432..642c77c76b84 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -1784,6 +1784,8 @@ static int qeth_idx_activate_get_answer(struct qeth_channel *channel, QETH_DBF_TEXT(SETUP, 2, "idxanswr"); card = CARD_FROM_CDEV(channel->ccwdev); iob = qeth_get_buffer(channel); + if (!iob) + return -ENOMEM; iob->callback = idx_reply_cb; memcpy(&channel->ccw, READ_CCW, sizeof(struct ccw1)); channel->ccw.count = QETH_BUFSIZE; @@ -1834,6 +1836,8 @@ static int qeth_idx_activate_channel(struct qeth_channel *channel, QETH_DBF_TEXT(SETUP, 2, "idxactch"); iob = qeth_get_buffer(channel); + if (!iob) + return -ENOMEM; iob->callback = idx_reply_cb; memcpy(&channel->ccw, WRITE_CCW, sizeof(struct ccw1)); channel->ccw.count = IDX_ACTIVATE_SIZE; @@ -2021,10 +2025,36 @@ void qeth_prepare_control_data(struct qeth_card *card, int len, } EXPORT_SYMBOL_GPL(qeth_prepare_control_data); +/** + * qeth_send_control_data() - send control command to the card + * @card: qeth_card structure pointer + * @len: size of the command buffer + * @iob: qeth_cmd_buffer pointer + * @reply_cb: callback function pointer + * @cb_card: pointer to the qeth_card structure + * @cb_reply: pointer to the qeth_reply structure + * @cb_cmd: pointer to the original iob for non-IPA + * commands, or to the qeth_ipa_cmd structure + * for the IPA commands. + * @reply_param: private pointer passed to the callback + * + * Returns the value of the `return_code' field of the response + * block returned from the hardware, or other error indication. + * Value of zero indicates successful execution of the command. + * + * Callback function gets called one or more times, with cb_cmd + * pointing to the response returned by the hardware. Callback + * function must return non-zero if more reply blocks are expected, + * and zero if the last or only reply block is received. Callback + * function can get the value of the reply_param pointer from the + * field 'param' of the structure qeth_reply. + */ + int qeth_send_control_data(struct qeth_card *card, int len, struct qeth_cmd_buffer *iob, - int (*reply_cb)(struct qeth_card *, struct qeth_reply *, - unsigned long), + int (*reply_cb)(struct qeth_card *cb_card, + struct qeth_reply *cb_reply, + unsigned long cb_cmd), void *reply_param) { int rc; @@ -2914,9 +2944,16 @@ struct qeth_cmd_buffer *qeth_get_ipacmd_buffer(struct qeth_card *card, struct qeth_cmd_buffer *iob; struct qeth_ipa_cmd *cmd; - iob = qeth_wait_for_buffer(&card->write); - cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); - qeth_fill_ipacmd_header(card, cmd, ipacmd, prot); + iob = qeth_get_buffer(&card->write); + if (iob) { + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + qeth_fill_ipacmd_header(card, cmd, ipacmd, prot); + } else { + dev_warn(&card->gdev->dev, + "The qeth driver ran out of channel command buffers\n"); + QETH_DBF_MESSAGE(1, "%s The qeth driver ran out of channel command buffers", + dev_name(&card->gdev->dev)); + } return iob; } @@ -2932,6 +2969,12 @@ void qeth_prepare_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob, } EXPORT_SYMBOL_GPL(qeth_prepare_ipa_cmd); +/** + * qeth_send_ipa_cmd() - send an IPA command + * + * See qeth_send_control_data() for explanation of the arguments. + */ + int qeth_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob, int (*reply_cb)(struct qeth_card *, struct qeth_reply*, unsigned long), @@ -2968,6 +3011,8 @@ int qeth_send_startlan(struct qeth_card *card) QETH_DBF_TEXT(SETUP, 2, "strtlan"); iob = qeth_get_ipacmd_buffer(card, IPA_CMD_STARTLAN, 0); + if (!iob) + return -ENOMEM; rc = qeth_send_ipa_cmd(card, iob, NULL, NULL); return rc; } @@ -3013,11 +3058,13 @@ static struct qeth_cmd_buffer *qeth_get_adapter_cmd(struct qeth_card *card, iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETADAPTERPARMS, QETH_PROT_IPV4); - cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); - cmd->data.setadapterparms.hdr.cmdlength = cmdlen; - cmd->data.setadapterparms.hdr.command_code = command; - cmd->data.setadapterparms.hdr.used_total = 1; - cmd->data.setadapterparms.hdr.seq_no = 1; + if (iob) { + cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); + cmd->data.setadapterparms.hdr.cmdlength = cmdlen; + cmd->data.setadapterparms.hdr.command_code = command; + cmd->data.setadapterparms.hdr.used_total = 1; + cmd->data.setadapterparms.hdr.seq_no = 1; + } return iob; } @@ -3030,6 +3077,8 @@ int qeth_query_setadapterparms(struct qeth_card *card) QETH_CARD_TEXT(card, 3, "queryadp"); iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_COMMANDS_SUPPORTED, sizeof(struct qeth_ipacmd_setadpparms)); + if (!iob) + return -ENOMEM; rc = qeth_send_ipa_cmd(card, iob, qeth_query_setadapterparms_cb, NULL); return rc; } @@ -3080,6 +3129,8 @@ int qeth_query_ipassists(struct qeth_card *card, enum qeth_prot_versions prot) QETH_DBF_TEXT_(SETUP, 2, "qipassi%i", prot); iob = qeth_get_ipacmd_buffer(card, IPA_CMD_QIPASSIST, prot); + if (!iob) + return -ENOMEM; rc = qeth_send_ipa_cmd(card, iob, qeth_query_ipassists_cb, NULL); return rc; } @@ -3119,6 +3170,8 @@ int qeth_query_switch_attributes(struct qeth_card *card, return -ENOMEDIUM; iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_SWITCH_ATTRIBUTES, sizeof(struct qeth_ipacmd_setadpparms_hdr)); + if (!iob) + return -ENOMEM; return qeth_send_ipa_cmd(card, iob, qeth_query_switch_attributes_cb, sw_info); } @@ -3146,6 +3199,8 @@ static int qeth_query_setdiagass(struct qeth_card *card) QETH_DBF_TEXT(SETUP, 2, "qdiagass"); iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0); + if (!iob) + return -ENOMEM; cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); cmd->data.diagass.subcmd_len = 16; cmd->data.diagass.subcmd = QETH_DIAGS_CMD_QUERY; @@ -3197,6 +3252,8 @@ int qeth_hw_trap(struct qeth_card *card, enum qeth_diags_trap_action action) QETH_DBF_TEXT(SETUP, 2, "diagtrap"); iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0); + if (!iob) + return -ENOMEM; cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); cmd->data.diagass.subcmd_len = 80; cmd->data.diagass.subcmd = QETH_DIAGS_CMD_TRAP; @@ -4162,6 +4219,8 @@ void qeth_setadp_promisc_mode(struct qeth_card *card) iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_PROMISC_MODE, sizeof(struct qeth_ipacmd_setadpparms)); + if (!iob) + return; cmd = (struct qeth_ipa_cmd *)(iob->data + IPA_PDU_HEADER_SIZE); cmd->data.setadapterparms.data.mode = mode; qeth_send_ipa_cmd(card, iob, qeth_setadp_promisc_mode_cb, NULL); @@ -4232,6 +4291,8 @@ int qeth_setadpparms_change_macaddr(struct qeth_card *card) iob = qeth_get_adapter_cmd(card, IPA_SETADP_ALTER_MAC_ADDRESS, sizeof(struct qeth_ipacmd_setadpparms)); + if (!iob) + return -ENOMEM; cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); cmd->data.setadapterparms.data.change_addr.cmd = CHANGE_ADDR_READ_MAC; cmd->data.setadapterparms.data.change_addr.addr_size = OSA_ADDR_LEN; @@ -4345,6 +4406,8 @@ static int qeth_setadpparms_set_access_ctrl(struct qeth_card *card, iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_ACCESS_CONTROL, sizeof(struct qeth_ipacmd_setadpparms_hdr) + sizeof(struct qeth_set_access_ctrl)); + if (!iob) + return -ENOMEM; cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); access_ctrl_req = &cmd->data.setadapterparms.data.set_access_ctrl; access_ctrl_req->subcmd_code = isolation; @@ -4588,6 +4651,10 @@ int qeth_snmp_command(struct qeth_card *card, char __user *udata) iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_SNMP_CONTROL, QETH_SNMP_SETADP_CMDLENGTH + req_len); + if (!iob) { + rc = -ENOMEM; + goto out; + } cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); memcpy(&cmd->data.setadapterparms.data.snmp, &ureq->cmd, req_len); rc = qeth_send_ipa_snmp_cmd(card, iob, QETH_SETADP_BASE_LEN + req_len, @@ -4599,7 +4666,7 @@ int qeth_snmp_command(struct qeth_card *card, char __user *udata) if (copy_to_user(udata, qinfo.udata, qinfo.udata_len)) rc = -EFAULT; } - +out: kfree(ureq); kfree(qinfo.udata); return rc; @@ -4670,6 +4737,10 @@ int qeth_query_oat_command(struct qeth_card *card, char __user *udata) iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_OAT, sizeof(struct qeth_ipacmd_setadpparms_hdr) + sizeof(struct qeth_query_oat)); + if (!iob) { + rc = -ENOMEM; + goto out_free; + } cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); oat_req = &cmd->data.setadapterparms.data.query_oat; oat_req->subcmd_code = oat_data.command; @@ -4735,6 +4806,8 @@ static int qeth_query_card_info(struct qeth_card *card, return -EOPNOTSUPP; iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_CARD_INFO, sizeof(struct qeth_ipacmd_setadpparms_hdr)); + if (!iob) + return -ENOMEM; return qeth_send_ipa_cmd(card, iob, qeth_query_card_info_cb, (void *)carrier_info); } @@ -5060,11 +5133,23 @@ retriable: card->options.adp.supported_funcs = 0; card->options.sbp.supported_funcs = 0; card->info.diagass_support = 0; - qeth_query_ipassists(card, QETH_PROT_IPV4); - if (qeth_is_supported(card, IPA_SETADAPTERPARMS)) - qeth_query_setadapterparms(card); - if (qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) - qeth_query_setdiagass(card); + rc = qeth_query_ipassists(card, QETH_PROT_IPV4); + if (rc == -ENOMEM) + goto out; + if (qeth_is_supported(card, IPA_SETADAPTERPARMS)) { + rc = qeth_query_setadapterparms(card); + if (rc < 0) { + QETH_DBF_TEXT_(SETUP, 2, "6err%d", rc); + goto out; + } + } + if (qeth_adp_supported(card, IPA_SETADP_SET_DIAG_ASSIST)) { + rc = qeth_query_setdiagass(card); + if (rc < 0) { + QETH_DBF_TEXT_(SETUP, 2, "7err%d", rc); + goto out; + } + } return 0; out: dev_warn(&card->gdev->dev, "The qeth device driver failed to recover " diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c index 15523f0e4c03..423bec56cffa 100644 --- a/drivers/s390/net/qeth_core_sys.c +++ b/drivers/s390/net/qeth_core_sys.c @@ -231,7 +231,6 @@ static ssize_t qeth_dev_prioqing_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 rc = 0; if (!card) @@ -253,36 +252,35 @@ static ssize_t qeth_dev_prioqing_store(struct device *dev, goto out; } - tmp = strsep((char **) &buf, "\n"); - if (!strcmp(tmp, "prio_queueing_prec")) { + if (sysfs_streq(buf, "prio_queueing_prec")) { card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_PREC; card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; - } else if (!strcmp(tmp, "prio_queueing_skb")) { + } else if (sysfs_streq(buf, "prio_queueing_skb")) { card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_SKB; card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; - } else if (!strcmp(tmp, "prio_queueing_tos")) { + } else if (sysfs_streq(buf, "prio_queueing_tos")) { card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_TOS; card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; - } else if (!strcmp(tmp, "prio_queueing_vlan")) { + } else if (sysfs_streq(buf, "prio_queueing_vlan")) { if (!card->options.layer2) { rc = -ENOTSUPP; goto out; } card->qdio.do_prio_queueing = QETH_PRIO_Q_ING_VLAN; card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; - } else if (!strcmp(tmp, "no_prio_queueing:0")) { + } else if (sysfs_streq(buf, "no_prio_queueing:0")) { card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING; card->qdio.default_out_queue = 0; - } else if (!strcmp(tmp, "no_prio_queueing:1")) { + } else if (sysfs_streq(buf, "no_prio_queueing:1")) { card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING; card->qdio.default_out_queue = 1; - } else if (!strcmp(tmp, "no_prio_queueing:2")) { + } else if (sysfs_streq(buf, "no_prio_queueing:2")) { card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING; card->qdio.default_out_queue = 2; - } else if (!strcmp(tmp, "no_prio_queueing:3")) { + } else if (sysfs_streq(buf, "no_prio_queueing:3")) { card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING; card->qdio.default_out_queue = 3; - } else if (!strcmp(tmp, "no_prio_queueing")) { + } else if (sysfs_streq(buf, "no_prio_queueing")) { card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING; card->qdio.default_out_queue = QETH_DEFAULT_QUEUE; } else @@ -497,8 +495,6 @@ static ssize_t qeth_dev_isolation_store(struct device *dev, struct qeth_card *card = dev_get_drvdata(dev); enum qeth_ipa_isolation_modes isolation; int rc = 0; - char *tmp, *curtoken; - curtoken = (char *) buf; if (!card) return -EINVAL; @@ -515,12 +511,11 @@ static ssize_t qeth_dev_isolation_store(struct device *dev, } /* parse input into isolation mode */ - tmp = strsep(&curtoken, "\n"); - if (!strcmp(tmp, ATTR_QETH_ISOLATION_NONE)) { + if (sysfs_streq(buf, ATTR_QETH_ISOLATION_NONE)) { isolation = ISOLATION_MODE_NONE; - } else if (!strcmp(tmp, ATTR_QETH_ISOLATION_FWD)) { + } else if (sysfs_streq(buf, ATTR_QETH_ISOLATION_FWD)) { isolation = ISOLATION_MODE_FWD; - } else if (!strcmp(tmp, ATTR_QETH_ISOLATION_DROP)) { + } else if (sysfs_streq(buf, ATTR_QETH_ISOLATION_DROP)) { isolation = ISOLATION_MODE_DROP; } else { rc = -EINVAL; @@ -531,8 +526,7 @@ static ssize_t qeth_dev_isolation_store(struct device *dev, /* defer IP assist if device is offline (until discipline->set_online)*/ card->options.prev_isolation = card->options.isolation; card->options.isolation = isolation; - if (card->state == CARD_STATE_SOFTSETUP || - card->state == CARD_STATE_UP) { + if (qeth_card_hw_is_reachable(card)) { int ipa_rc = qeth_set_access_ctrl_online(card, 1); if (ipa_rc != 0) rc = ipa_rc; @@ -555,7 +549,7 @@ static ssize_t qeth_dev_switch_attrs_show(struct device *dev, if (!card) return -EINVAL; - if (card->state != CARD_STATE_SOFTSETUP && card->state != CARD_STATE_UP) + if (!qeth_card_hw_is_reachable(card)) return sprintf(buf, "n/a\n"); rc = qeth_query_switch_attributes(card, &sw_info); @@ -598,19 +592,16 @@ static ssize_t qeth_hw_trap_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); int rc = 0; - char *tmp, *curtoken; int state = 0; - curtoken = (char *)buf; if (!card) return -EINVAL; mutex_lock(&card->conf_mutex); - if (card->state == CARD_STATE_SOFTSETUP || card->state == CARD_STATE_UP) + if (qeth_card_hw_is_reachable(card)) state = 1; - tmp = strsep(&curtoken, "\n"); - if (!strcmp(tmp, "arm") && !card->info.hwtrap) { + if (sysfs_streq(buf, "arm") && !card->info.hwtrap) { if (state) { if (qeth_is_diagass_supported(card, QETH_DIAGS_CMD_TRAP)) { @@ -621,14 +612,14 @@ static ssize_t qeth_hw_trap_store(struct device *dev, rc = -EINVAL; } else card->info.hwtrap = 1; - } else if (!strcmp(tmp, "disarm") && card->info.hwtrap) { + } else if (sysfs_streq(buf, "disarm") && card->info.hwtrap) { if (state) { rc = qeth_hw_trap(card, QETH_DIAGS_TRAP_DISARM); if (!rc) card->info.hwtrap = 0; } else card->info.hwtrap = 0; - } else if (!strcmp(tmp, "trap") && state && card->info.hwtrap) + } else if (sysfs_streq(buf, "trap") && state && card->info.hwtrap) rc = qeth_hw_trap(card, QETH_DIAGS_TRAP_CAPTURE); else rc = -EINVAL; diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c index d02cd1a67943..0ea0869120cf 100644 --- a/drivers/s390/net/qeth_l2_main.c +++ b/drivers/s390/net/qeth_l2_main.c @@ -27,10 +27,7 @@ static int qeth_l2_set_offline(struct ccwgroup_device *); 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, - int (*reply_cb) (struct qeth_card *, - struct qeth_reply*, - unsigned long)); + enum qeth_ipa_cmds); static void qeth_l2_set_multicast_list(struct net_device *); static int qeth_l2_recover(void *); static void qeth_bridgeport_query_support(struct qeth_card *card); @@ -48,8 +45,7 @@ static int qeth_l2_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) if (!card) return -ENODEV; - if ((card->state != CARD_STATE_UP) && - (card->state != CARD_STATE_SOFTSETUP)) + if (!qeth_card_hw_is_reachable(card)) return -ENODEV; if (card->info.type == QETH_CARD_TYPE_OSN) @@ -130,56 +126,71 @@ static struct net_device *qeth_l2_netdev_by_devno(unsigned char *read_dev_no) return ndev; } -static int qeth_l2_send_setgroupmac_cb(struct qeth_card *card, - struct qeth_reply *reply, - unsigned long data) +static int qeth_setdel_makerc(struct qeth_card *card, int retcode) { - struct qeth_ipa_cmd *cmd; - __u8 *mac; + int rc; - QETH_CARD_TEXT(card, 2, "L2Sgmacb"); - cmd = (struct qeth_ipa_cmd *) data; - mac = &cmd->data.setdelmac.mac[0]; - /* MAC already registered, needed in couple/uncouple case */ - if (cmd->hdr.return_code == IPA_RC_L2_DUP_MAC) { - QETH_DBF_MESSAGE(2, "Group MAC %pM already existing on %s \n", - mac, QETH_CARD_IFNAME(card)); - cmd->hdr.return_code = 0; + if (retcode) + QETH_CARD_TEXT_(card, 2, "err%04x", retcode); + switch (retcode) { + case IPA_RC_SUCCESS: + rc = 0; + break; + case IPA_RC_L2_UNSUPPORTED_CMD: + rc = -ENOSYS; + break; + case IPA_RC_L2_ADDR_TABLE_FULL: + rc = -ENOSPC; + break; + case IPA_RC_L2_DUP_MAC: + case IPA_RC_L2_DUP_LAYER3_MAC: + rc = -EEXIST; + break; + case IPA_RC_L2_MAC_NOT_AUTH_BY_HYP: + case IPA_RC_L2_MAC_NOT_AUTH_BY_ADP: + rc = -EPERM; + break; + case IPA_RC_L2_MAC_NOT_FOUND: + rc = -ENOENT; + break; + case -ENOMEM: + rc = -ENOMEM; + break; + default: + rc = -EIO; + break; } - if (cmd->hdr.return_code) - QETH_DBF_MESSAGE(2, "Could not set group MAC %pM on %s: %x\n", - mac, QETH_CARD_IFNAME(card), cmd->hdr.return_code); - return 0; + return rc; } static int qeth_l2_send_setgroupmac(struct qeth_card *card, __u8 *mac) { - QETH_CARD_TEXT(card, 2, "L2Sgmac"); - return qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETGMAC, - qeth_l2_send_setgroupmac_cb); -} - -static int qeth_l2_send_delgroupmac_cb(struct qeth_card *card, - struct qeth_reply *reply, - unsigned long data) -{ - struct qeth_ipa_cmd *cmd; - __u8 *mac; + int rc; - QETH_CARD_TEXT(card, 2, "L2Dgmacb"); - cmd = (struct qeth_ipa_cmd *) data; - mac = &cmd->data.setdelmac.mac[0]; - if (cmd->hdr.return_code) - QETH_DBF_MESSAGE(2, "Could not delete group MAC %pM on %s: %x\n", - mac, QETH_CARD_IFNAME(card), cmd->hdr.return_code); - return 0; + QETH_CARD_TEXT(card, 2, "L2Sgmac"); + rc = qeth_setdel_makerc(card, qeth_l2_send_setdelmac(card, mac, + IPA_CMD_SETGMAC)); + if (rc == -EEXIST) + QETH_DBF_MESSAGE(2, "Group MAC %pM already existing on %s\n", + mac, QETH_CARD_IFNAME(card)); + else if (rc) + QETH_DBF_MESSAGE(2, "Could not set group MAC %pM on %s: %d\n", + mac, QETH_CARD_IFNAME(card), rc); + return rc; } static int qeth_l2_send_delgroupmac(struct qeth_card *card, __u8 *mac) { + int rc; + QETH_CARD_TEXT(card, 2, "L2Dgmac"); - return qeth_l2_send_setdelmac(card, mac, IPA_CMD_DELGMAC, - qeth_l2_send_delgroupmac_cb); + rc = qeth_setdel_makerc(card, qeth_l2_send_setdelmac(card, mac, + IPA_CMD_DELGMAC)); + if (rc) + QETH_DBF_MESSAGE(2, + "Could not delete group MAC %pM on %s: %d\n", + mac, QETH_CARD_IFNAME(card), rc); + return rc; } static void qeth_l2_add_mc(struct qeth_card *card, __u8 *mac, int vmac) @@ -197,10 +208,11 @@ static void qeth_l2_add_mc(struct qeth_card *card, __u8 *mac, int vmac) mc->is_vmac = vmac; if (vmac) { - rc = qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC, - NULL); + rc = qeth_setdel_makerc(card, + qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC)); } else { - rc = qeth_l2_send_setgroupmac(card, mac); + rc = qeth_setdel_makerc(card, + qeth_l2_send_setgroupmac(card, mac)); } if (!rc) @@ -218,7 +230,7 @@ static void qeth_l2_del_all_mc(struct qeth_card *card, int del) if (del) { if (mc->is_vmac) qeth_l2_send_setdelmac(card, mc->mc_addr, - IPA_CMD_DELVMAC, NULL); + IPA_CMD_DELVMAC); else qeth_l2_send_delgroupmac(card, mc->mc_addr); } @@ -291,6 +303,8 @@ static int qeth_l2_send_setdelvlan(struct qeth_card *card, __u16 i, QETH_CARD_TEXT_(card, 4, "L2sdv%x", ipacmd); iob = qeth_get_ipacmd_buffer(card, ipacmd, QETH_PROT_IPV4); + if (!iob) + return -ENOMEM; cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); cmd->data.setdelvlan.vlan_id = i; return qeth_send_ipa_cmd(card, iob, @@ -313,6 +327,7 @@ static int qeth_l2_vlan_rx_add_vid(struct net_device *dev, { struct qeth_card *card = dev->ml_priv; struct qeth_vlan_vid *id; + int rc; QETH_CARD_TEXT_(card, 4, "aid:%d", vid); if (!vid) @@ -328,7 +343,11 @@ static int qeth_l2_vlan_rx_add_vid(struct net_device *dev, id = kmalloc(sizeof(struct qeth_vlan_vid), GFP_ATOMIC); if (id) { id->vid = vid; - qeth_l2_send_setdelvlan(card, vid, IPA_CMD_SETVLAN); + rc = qeth_l2_send_setdelvlan(card, vid, IPA_CMD_SETVLAN); + if (rc) { + kfree(id); + return rc; + } spin_lock_bh(&card->vlanlock); list_add_tail(&id->list, &card->vid_list); spin_unlock_bh(&card->vlanlock); @@ -343,6 +362,7 @@ static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev, { struct qeth_vlan_vid *id, *tmpid = NULL; struct qeth_card *card = dev->ml_priv; + int rc = 0; QETH_CARD_TEXT_(card, 4, "kid:%d", vid); if (card->info.type == QETH_CARD_TYPE_OSM) { @@ -363,11 +383,11 @@ static int qeth_l2_vlan_rx_kill_vid(struct net_device *dev, } spin_unlock_bh(&card->vlanlock); if (tmpid) { - qeth_l2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN); + rc = qeth_l2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN); kfree(tmpid); } qeth_l2_set_multicast_list(card->dev); - return 0; + return rc; } static int qeth_l2_stop_card(struct qeth_card *card, int recovery_mode) @@ -539,91 +559,62 @@ out: } static int qeth_l2_send_setdelmac(struct qeth_card *card, __u8 *mac, - enum qeth_ipa_cmds ipacmd, - int (*reply_cb) (struct qeth_card *, - struct qeth_reply*, - unsigned long)) + enum qeth_ipa_cmds ipacmd) { struct qeth_ipa_cmd *cmd; struct qeth_cmd_buffer *iob; QETH_CARD_TEXT(card, 2, "L2sdmac"); iob = qeth_get_ipacmd_buffer(card, ipacmd, QETH_PROT_IPV4); + if (!iob) + return -ENOMEM; cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); cmd->data.setdelmac.mac_length = OSA_ADDR_LEN; memcpy(&cmd->data.setdelmac.mac, mac, OSA_ADDR_LEN); - return qeth_send_ipa_cmd(card, iob, reply_cb, NULL); + return qeth_send_ipa_cmd(card, iob, NULL, NULL); } -static int qeth_l2_send_setmac_cb(struct qeth_card *card, - struct qeth_reply *reply, - unsigned long data) +static int qeth_l2_send_setmac(struct qeth_card *card, __u8 *mac) { - struct qeth_ipa_cmd *cmd; + int rc; - QETH_CARD_TEXT(card, 2, "L2Smaccb"); - cmd = (struct qeth_ipa_cmd *) data; - if (cmd->hdr.return_code) { - QETH_CARD_TEXT_(card, 2, "L2er%x", cmd->hdr.return_code); + QETH_CARD_TEXT(card, 2, "L2Setmac"); + rc = qeth_setdel_makerc(card, qeth_l2_send_setdelmac(card, mac, + IPA_CMD_SETVMAC)); + if (rc == 0) { + card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED; + memcpy(card->dev->dev_addr, mac, OSA_ADDR_LEN); + dev_info(&card->gdev->dev, + "MAC address %pM successfully registered on device %s\n", + card->dev->dev_addr, card->dev->name); + } else { card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED; - switch (cmd->hdr.return_code) { - case IPA_RC_L2_DUP_MAC: - case IPA_RC_L2_DUP_LAYER3_MAC: + switch (rc) { + case -EEXIST: dev_warn(&card->gdev->dev, - "MAC address %pM already exists\n", - cmd->data.setdelmac.mac); + "MAC address %pM already exists\n", mac); break; - case IPA_RC_L2_MAC_NOT_AUTH_BY_HYP: - case IPA_RC_L2_MAC_NOT_AUTH_BY_ADP: + case -EPERM: dev_warn(&card->gdev->dev, - "MAC address %pM is not authorized\n", - cmd->data.setdelmac.mac); - break; - default: + "MAC address %pM is not authorized\n", mac); break; } - } else { - card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED; - memcpy(card->dev->dev_addr, cmd->data.setdelmac.mac, - OSA_ADDR_LEN); - dev_info(&card->gdev->dev, - "MAC address %pM successfully registered on device %s\n", - card->dev->dev_addr, card->dev->name); - } - return 0; -} - -static int qeth_l2_send_setmac(struct qeth_card *card, __u8 *mac) -{ - QETH_CARD_TEXT(card, 2, "L2Setmac"); - return qeth_l2_send_setdelmac(card, mac, IPA_CMD_SETVMAC, - qeth_l2_send_setmac_cb); -} - -static int qeth_l2_send_delmac_cb(struct qeth_card *card, - struct qeth_reply *reply, - unsigned long data) -{ - struct qeth_ipa_cmd *cmd; - - QETH_CARD_TEXT(card, 2, "L2Dmaccb"); - cmd = (struct qeth_ipa_cmd *) data; - if (cmd->hdr.return_code) { - QETH_CARD_TEXT_(card, 2, "err%d", cmd->hdr.return_code); - return 0; } - card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED; - - return 0; + return rc; } static int qeth_l2_send_delmac(struct qeth_card *card, __u8 *mac) { + int rc; + QETH_CARD_TEXT(card, 2, "L2Delmac"); if (!(card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED)) return 0; - return qeth_l2_send_setdelmac(card, mac, IPA_CMD_DELVMAC, - qeth_l2_send_delmac_cb); + rc = qeth_setdel_makerc(card, qeth_l2_send_setdelmac(card, mac, + IPA_CMD_DELVMAC)); + if (rc == 0) + card->info.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED; + return rc; } static int qeth_l2_request_initial_mac(struct qeth_card *card) @@ -651,7 +642,7 @@ static int qeth_l2_request_initial_mac(struct qeth_card *card) if (rc) { QETH_DBF_MESSAGE(2, "couldn't get MAC address on " "device %s: x%x\n", CARD_BUS_ID(card), rc); - QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc); + QETH_DBF_TEXT_(SETUP, 2, "1err%04x", rc); return rc; } QETH_DBF_HEX(SETUP, 2, card->dev->dev_addr, OSA_ADDR_LEN); @@ -687,7 +678,7 @@ static int qeth_l2_set_mac_address(struct net_device *dev, void *p) return -ERESTARTSYS; } rc = qeth_l2_send_delmac(card, &card->dev->dev_addr[0]); - if (!rc || (rc == IPA_RC_L2_MAC_NOT_FOUND)) + if (!rc || (rc == -ENOENT)) rc = qeth_l2_send_setmac(card, addr->sa_data); return rc ? -EINVAL : 0; } @@ -996,7 +987,7 @@ static int __qeth_l2_set_online(struct ccwgroup_device *gdev, int recovery_mode) recover_flag = card->state; rc = qeth_core_hardsetup_card(card); if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); + QETH_DBF_TEXT_(SETUP, 2, "2err%04x", rc); rc = -ENODEV; goto out_remove; } @@ -1344,8 +1335,7 @@ int qeth_osn_assist(struct net_device *dev, void *data, int data_len) if (!card) return -ENODEV; QETH_CARD_TEXT(card, 2, "osnsdmc"); - if ((card->state != CARD_STATE_UP) && - (card->state != CARD_STATE_SOFTSETUP)) + if (!qeth_card_hw_is_reachable(card)) return -ENODEV; iob = qeth_wait_for_buffer(&card->write); memcpy(iob->data+IPA_PDU_HEADER_SIZE, data, data_len); @@ -1730,6 +1720,8 @@ static void qeth_bridgeport_query_support(struct qeth_card *card) QETH_CARD_TEXT(card, 2, "brqsuppo"); iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); + if (!iob) + return; cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); cmd->data.sbp.hdr.cmdlength = sizeof(struct qeth_ipacmd_sbp_hdr) + @@ -1805,6 +1797,8 @@ int qeth_bridgeport_query_ports(struct qeth_card *card, if (!(card->options.sbp.supported_funcs & IPA_SBP_QUERY_BRIDGE_PORTS)) return -EOPNOTSUPP; iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); + if (!iob) + return -ENOMEM; cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); cmd->data.sbp.hdr.cmdlength = sizeof(struct qeth_ipacmd_sbp_hdr); @@ -1817,9 +1811,7 @@ int qeth_bridgeport_query_ports(struct qeth_card *card, if (rc) return rc; rc = qeth_bridgeport_makerc(card, &cbctl, IPA_SBP_QUERY_BRIDGE_PORTS); - if (rc) - return rc; - return 0; + return rc; } EXPORT_SYMBOL_GPL(qeth_bridgeport_query_ports); @@ -1873,6 +1865,8 @@ int qeth_bridgeport_setrole(struct qeth_card *card, enum qeth_sbp_roles role) if (!(card->options.sbp.supported_funcs & setcmd)) return -EOPNOTSUPP; iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETBRIDGEPORT, 0); + if (!iob) + return -ENOMEM; cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); cmd->data.sbp.hdr.cmdlength = cmdlength; cmd->data.sbp.hdr.command_code = setcmd; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 625227ad16ee..04e42c649134 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -432,10 +432,8 @@ void qeth_l3_set_ip_addr_list(struct qeth_card *card) QETH_CARD_TEXT(card, 2, "sdiplist"); QETH_CARD_HEX(card, 2, &card, sizeof(void *)); - if ((card->state != CARD_STATE_UP && - card->state != CARD_STATE_SOFTSETUP) || card->options.sniffer) { + if (!qeth_card_hw_is_reachable(card) || card->options.sniffer) return; - } spin_lock_irqsave(&card->ip_lock, flags); tbd_list = card->ip_tbd_list; @@ -549,6 +547,8 @@ static int qeth_l3_send_setdelmc(struct qeth_card *card, QETH_CARD_TEXT(card, 4, "setdelmc"); iob = qeth_get_ipacmd_buffer(card, ipacmd, addr->proto); + if (!iob) + return -ENOMEM; cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); memcpy(&cmd->data.setdelipm.mac, addr->mac, OSA_ADDR_LEN); if (addr->proto == QETH_PROT_IPV6) @@ -588,6 +588,8 @@ static int qeth_l3_send_setdelip(struct qeth_card *card, QETH_CARD_TEXT_(card, 4, "flags%02X", flags); iob = qeth_get_ipacmd_buffer(card, ipacmd, addr->proto); + if (!iob) + return -ENOMEM; cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); if (addr->proto == QETH_PROT_IPV6) { memcpy(cmd->data.setdelip6.ip_addr, &addr->u.a6.addr, @@ -616,6 +618,8 @@ static int qeth_l3_send_setrouting(struct qeth_card *card, QETH_CARD_TEXT(card, 4, "setroutg"); iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETRTG, prot); + if (!iob) + return -ENOMEM; cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); cmd->data.setrtg.type = (type); rc = qeth_send_ipa_cmd(card, iob, NULL, NULL); @@ -1049,12 +1053,14 @@ static struct qeth_cmd_buffer *qeth_l3_get_setassparms_cmd( QETH_CARD_TEXT(card, 4, "getasscm"); iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETASSPARMS, prot); - 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; + 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; } @@ -1090,6 +1096,8 @@ static int qeth_l3_send_simple_setassparms_ipv6(struct qeth_card *card, QETH_CARD_TEXT(card, 4, "simassp6"); iob = qeth_l3_get_setassparms_cmd(card, ipa_func, cmd_code, 0, QETH_PROT_IPV6); + if (!iob) + return -ENOMEM; rc = qeth_l3_send_setassparms(card, iob, 0, 0, qeth_l3_default_setassparms_cb, NULL); return rc; @@ -1108,6 +1116,8 @@ static int qeth_l3_send_simple_setassparms(struct qeth_card *card, 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; @@ -1494,6 +1504,8 @@ static int qeth_l3_iqd_read_initial_mac(struct qeth_card *card) iob = qeth_get_ipacmd_buffer(card, IPA_CMD_CREATE_ADDR, QETH_PROT_IPV6); + if (!iob) + return -ENOMEM; cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); *((__u16 *) &cmd->data.create_destroy_addr.unique_id[6]) = card->info.unique_id; @@ -1537,6 +1549,8 @@ static int qeth_l3_get_unique_id(struct qeth_card *card) iob = qeth_get_ipacmd_buffer(card, IPA_CMD_CREATE_ADDR, QETH_PROT_IPV6); + if (!iob) + return -ENOMEM; cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); *((__u16 *) &cmd->data.create_destroy_addr.unique_id[6]) = card->info.unique_id; @@ -1611,6 +1625,8 @@ qeth_diags_trace(struct qeth_card *card, enum qeth_diags_trace_cmds diags_cmd) QETH_DBF_TEXT(SETUP, 2, "diagtrac"); iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SET_DIAG_ASS, 0); + if (!iob) + return -ENOMEM; cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); cmd->data.diagass.subcmd_len = 16; cmd->data.diagass.subcmd = QETH_DIAGS_CMD_TRACE; @@ -2442,6 +2458,8 @@ static int qeth_l3_query_arp_cache_info(struct qeth_card *card, IPA_CMD_ASS_ARP_QUERY_INFO, sizeof(struct qeth_arp_query_data) - sizeof(char), prot); + if (!iob) + return -ENOMEM; cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE); cmd->data.setassparms.data.query_arp.request_bits = 0x000F; cmd->data.setassparms.data.query_arp.reply_bits = 0; @@ -2535,6 +2553,8 @@ static int qeth_l3_arp_add_entry(struct qeth_card *card, IPA_CMD_ASS_ARP_ADD_ENTRY, sizeof(struct qeth_arp_cache_entry), QETH_PROT_IPV4); + if (!iob) + return -ENOMEM; rc = qeth_l3_send_setassparms(card, iob, sizeof(struct qeth_arp_cache_entry), (unsigned long) entry, @@ -2574,6 +2594,8 @@ static int qeth_l3_arp_remove_entry(struct qeth_card *card, IPA_CMD_ASS_ARP_REMOVE_ENTRY, 12, QETH_PROT_IPV4); + if (!iob) + return -ENOMEM; rc = qeth_l3_send_setassparms(card, iob, 12, (unsigned long)buf, qeth_l3_default_setassparms_cb, NULL); @@ -2626,8 +2648,7 @@ static int qeth_l3_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) if (!card) return -ENODEV; - if ((card->state != CARD_STATE_UP) && - (card->state != CARD_STATE_SOFTSETUP)) + if (!qeth_card_hw_is_reachable(card)) return -ENODEV; switch (cmd) { @@ -2800,12 +2821,12 @@ static void qeth_l3_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, * before we're going to overwrite this location with next hop ip. * v6 uses passthrough, v4 sets the tag in the QDIO header. */ - if (vlan_tx_tag_present(skb)) { + if (skb_vlan_tag_present(skb)) { if ((ipv == 4) || (card->info.type == QETH_CARD_TYPE_IQD)) hdr->hdr.l3.ext_flags = QETH_HDR_EXT_VLAN_FRAME; else hdr->hdr.l3.ext_flags = QETH_HDR_EXT_INCLUDE_VLAN_TAG; - hdr->hdr.l3.vlan_id = vlan_tx_tag_get(skb); + hdr->hdr.l3.vlan_id = skb_vlan_tag_get(skb); } hdr->hdr.l3.length = skb->len - sizeof(struct qeth_hdr); @@ -2986,7 +3007,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) skb_pull(new_skb, ETH_HLEN); } - if (ipv != 4 && vlan_tx_tag_present(new_skb)) { + if (ipv != 4 && skb_vlan_tag_present(new_skb)) { skb_push(new_skb, VLAN_HLEN); skb_copy_to_linear_data(new_skb, new_skb->data + 4, 4); skb_copy_to_linear_data_offset(new_skb, 4, @@ -2995,7 +3016,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) new_skb->data + 12, 4); tag = (u16 *)(new_skb->data + 12); *tag = __constant_htons(ETH_P_8021Q); - *(tag + 1) = htons(vlan_tx_tag_get(new_skb)); + *(tag + 1) = htons(skb_vlan_tag_get(new_skb)); } } @@ -3262,6 +3283,8 @@ static const struct net_device_ops qeth_l3_osa_netdev_ops = { static int qeth_l3_setup_netdev(struct qeth_card *card) { + int rc; + if (card->info.type == QETH_CARD_TYPE_OSD || card->info.type == QETH_CARD_TYPE_OSX) { if ((card->info.link_type == QETH_LINK_TYPE_LANE_TR) || @@ -3293,7 +3316,9 @@ static int qeth_l3_setup_netdev(struct qeth_card *card) return -ENODEV; card->dev->flags |= IFF_NOARP; card->dev->netdev_ops = &qeth_l3_netdev_ops; - qeth_l3_iqd_read_initial_mac(card); + rc = qeth_l3_iqd_read_initial_mac(card); + if (rc) + return rc; if (card->options.hsuid[0]) memcpy(card->dev->perm_addr, card->options.hsuid, 9); } else @@ -3360,7 +3385,7 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) recover_flag = card->state; rc = qeth_core_hardsetup_card(card); if (rc) { - QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); + QETH_DBF_TEXT_(SETUP, 2, "2err%04x", rc); rc = -ENODEV; goto out_remove; } @@ -3401,7 +3426,7 @@ static int __qeth_l3_set_online(struct ccwgroup_device *gdev, int recovery_mode) contin: rc = qeth_l3_setadapter_parms(card); if (rc) - QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc); + QETH_DBF_TEXT_(SETUP, 2, "2err%04x", rc); if (!card->options.sniffer) { rc = qeth_l3_start_ipassists(card); if (rc) { @@ -3410,10 +3435,10 @@ contin: } rc = qeth_l3_setrouting_v4(card); if (rc) - QETH_DBF_TEXT_(SETUP, 2, "4err%d", rc); + QETH_DBF_TEXT_(SETUP, 2, "4err%04x", rc); rc = qeth_l3_setrouting_v6(card); if (rc) - QETH_DBF_TEXT_(SETUP, 2, "5err%d", rc); + QETH_DBF_TEXT_(SETUP, 2, "5err%04x", rc); } netif_tx_disable(card->dev); diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c index adef5f5de118..386eb7b89b1e 100644 --- a/drivers/s390/net/qeth_l3_sys.c +++ b/drivers/s390/net/qeth_l3_sys.c @@ -57,29 +57,26 @@ static ssize_t qeth_l3_dev_route_store(struct qeth_card *card, const char *buf, size_t count) { enum qeth_routing_types old_route_type = route->type; - char *tmp; int rc = 0; - tmp = strsep((char **) &buf, "\n"); mutex_lock(&card->conf_mutex); - if (!strcmp(tmp, "no_router")) { + if (sysfs_streq(buf, "no_router")) { route->type = NO_ROUTER; - } else if (!strcmp(tmp, "primary_connector")) { + } else if (sysfs_streq(buf, "primary_connector")) { route->type = PRIMARY_CONNECTOR; - } else if (!strcmp(tmp, "secondary_connector")) { + } else if (sysfs_streq(buf, "secondary_connector")) { route->type = SECONDARY_CONNECTOR; - } else if (!strcmp(tmp, "primary_router")) { + } else if (sysfs_streq(buf, "primary_router")) { route->type = PRIMARY_ROUTER; - } else if (!strcmp(tmp, "secondary_router")) { + } else if (sysfs_streq(buf, "secondary_router")) { route->type = SECONDARY_ROUTER; - } else if (!strcmp(tmp, "multicast_router")) { + } else if (sysfs_streq(buf, "multicast_router")) { route->type = MULTICAST_ROUTER; } else { rc = -EINVAL; goto out; } - if (((card->state == CARD_STATE_SOFTSETUP) || - (card->state == CARD_STATE_UP)) && + if (qeth_card_hw_is_reachable(card) && (old_route_type != route->type)) { if (prot == QETH_PROT_IPV4) rc = qeth_l3_setrouting_v4(card); @@ -371,7 +368,6 @@ static ssize_t qeth_l3_dev_ipato_enable_store(struct device *dev, { struct qeth_card *card = dev_get_drvdata(dev); struct qeth_ipaddr *tmpipa, *t; - char *tmp; int rc = 0; if (!card) @@ -384,10 +380,9 @@ static ssize_t qeth_l3_dev_ipato_enable_store(struct device *dev, goto out; } - tmp = strsep((char **) &buf, "\n"); - if (!strcmp(tmp, "toggle")) { + if (sysfs_streq(buf, "toggle")) { card->ipato.enabled = (card->ipato.enabled)? 0 : 1; - } else if (!strcmp(tmp, "1")) { + } else if (sysfs_streq(buf, "1")) { card->ipato.enabled = 1; list_for_each_entry_safe(tmpipa, t, card->ip_tbd_list, entry) { if ((tmpipa->type == QETH_IP_TYPE_NORMAL) && @@ -396,7 +391,7 @@ static ssize_t qeth_l3_dev_ipato_enable_store(struct device *dev, QETH_IPA_SETIP_TAKEOVER_FLAG; } - } else if (!strcmp(tmp, "0")) { + } else if (sysfs_streq(buf, "0")) { card->ipato.enabled = 0; list_for_each_entry_safe(tmpipa, t, card->ip_tbd_list, entry) { if (tmpipa->set_flags & @@ -431,21 +426,19 @@ static ssize_t qeth_l3_dev_ipato_invert4_store(struct device *dev, const char *buf, size_t count) { struct qeth_card *card = dev_get_drvdata(dev); - char *tmp; int rc = 0; if (!card) return -EINVAL; mutex_lock(&card->conf_mutex); - tmp = strsep((char **) &buf, "\n"); - if (!strcmp(tmp, "toggle")) { + if (sysfs_streq(buf, "toggle")) card->ipato.invert4 = (card->ipato.invert4)? 0 : 1; - } else if (!strcmp(tmp, "1")) { + else if (sysfs_streq(buf, "1")) card->ipato.invert4 = 1; - } else if (!strcmp(tmp, "0")) { + else if (sysfs_streq(buf, "0")) card->ipato.invert4 = 0; - } else + else rc = -EINVAL; mutex_unlock(&card->conf_mutex); return rc ? rc : count; @@ -613,21 +606,19 @@ static ssize_t qeth_l3_dev_ipato_invert6_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 rc = 0; if (!card) return -EINVAL; mutex_lock(&card->conf_mutex); - tmp = strsep((char **) &buf, "\n"); - if (!strcmp(tmp, "toggle")) { + if (sysfs_streq(buf, "toggle")) card->ipato.invert6 = (card->ipato.invert6)? 0 : 1; - } else if (!strcmp(tmp, "1")) { + else if (sysfs_streq(buf, "1")) card->ipato.invert6 = 1; - } else if (!strcmp(tmp, "0")) { + else if (sysfs_streq(buf, "0")) card->ipato.invert6 = 0; - } else + else rc = -EINVAL; mutex_unlock(&card->conf_mutex); return rc ? rc : count; diff --git a/drivers/scsi/aic7xxx/aic79xx_core.c b/drivers/scsi/aic7xxx/aic79xx_core.c index 0bcacf71aef8..97f2accd3dbb 100644 --- a/drivers/scsi/aic7xxx/aic79xx_core.c +++ b/drivers/scsi/aic7xxx/aic79xx_core.c @@ -1298,7 +1298,7 @@ rescan_fifos: /* * Wait for any inprogress DMA to complete and clear DMA state - * if this if for an SCB in the qinfifo. + * if this is for an SCB in the qinfifo. */ while (((ccscbctl = ahd_inb(ahd, CCSCBCTL)) & (CCARREN|CCSCBEN)) != 0) { diff --git a/drivers/scsi/csiostor/Makefile b/drivers/scsi/csiostor/Makefile index 913b9a92fb06..3681a3fbd499 100644 --- a/drivers/scsi/csiostor/Makefile +++ b/drivers/scsi/csiostor/Makefile @@ -8,5 +8,5 @@ ccflags-y += -I$(srctree)/drivers/net/ethernet/chelsio/cxgb4 obj-$(CONFIG_SCSI_CHELSIO_FCOE) += csiostor.o csiostor-objs := csio_attr.o csio_init.o csio_lnode.o csio_scsi.o \ - csio_hw.o csio_hw_t4.o csio_hw_t5.o csio_isr.o \ + csio_hw.o csio_hw_t5.o csio_isr.o \ csio_mb.o csio_rnode.o csio_wr.o diff --git a/drivers/scsi/csiostor/csio_hw.c b/drivers/scsi/csiostor/csio_hw.c index 9ab997e18b20..2e66f34ebb79 100644 --- a/drivers/scsi/csiostor/csio_hw.c +++ b/drivers/scsi/csiostor/csio_hw.c @@ -60,37 +60,10 @@ int csio_msi = 2; static int dev_num; /* FCoE Adapter types & its description */ -static const struct csio_adap_desc csio_t4_fcoe_adapters[] = { - {"T440-Dbg 10G", "Chelsio T440-Dbg 10G [FCoE]"}, - {"T420-CR 10G", "Chelsio T420-CR 10G [FCoE]"}, - {"T422-CR 10G/1G", "Chelsio T422-CR 10G/1G [FCoE]"}, - {"T440-CR 10G", "Chelsio T440-CR 10G [FCoE]"}, - {"T420-BCH 10G", "Chelsio T420-BCH 10G [FCoE]"}, - {"T440-BCH 10G", "Chelsio T440-BCH 10G [FCoE]"}, - {"T440-CH 10G", "Chelsio T440-CH 10G [FCoE]"}, - {"T420-SO 10G", "Chelsio T420-SO 10G [FCoE]"}, - {"T420-CX4 10G", "Chelsio T420-CX4 10G [FCoE]"}, - {"T420-BT 10G", "Chelsio T420-BT 10G [FCoE]"}, - {"T404-BT 1G", "Chelsio T404-BT 1G [FCoE]"}, - {"B420-SR 10G", "Chelsio B420-SR 10G [FCoE]"}, - {"B404-BT 1G", "Chelsio B404-BT 1G [FCoE]"}, - {"T480-CR 10G", "Chelsio T480-CR 10G [FCoE]"}, - {"T440-LP-CR 10G", "Chelsio T440-LP-CR 10G [FCoE]"}, - {"AMSTERDAM 10G", "Chelsio AMSTERDAM 10G [FCoE]"}, - {"HUAWEI T480 10G", "Chelsio HUAWEI T480 10G [FCoE]"}, - {"HUAWEI T440 10G", "Chelsio HUAWEI T440 10G [FCoE]"}, - {"HUAWEI STG 10G", "Chelsio HUAWEI STG 10G [FCoE]"}, - {"ACROMAG XAUI 10G", "Chelsio ACROMAG XAUI 10G [FCoE]"}, - {"ACROMAG SFP+ 10G", "Chelsio ACROMAG SFP+ 10G [FCoE]"}, - {"QUANTA SFP+ 10G", "Chelsio QUANTA SFP+ 10G [FCoE]"}, - {"HUAWEI 10Gbase-T", "Chelsio HUAWEI 10Gbase-T [FCoE]"}, - {"HUAWEI T4TOE 10G", "Chelsio HUAWEI T4TOE 10G [FCoE]"} -}; - static const struct csio_adap_desc csio_t5_fcoe_adapters[] = { {"T580-Dbg 10G", "Chelsio T580-Dbg 10G [FCoE]"}, {"T520-CR 10G", "Chelsio T520-CR 10G [FCoE]"}, - {"T522-CR 10G/1G", "Chelsio T452-CR 10G/1G [FCoE]"}, + {"T522-CR 10G/1G", "Chelsio T522-CR 10G/1G [FCoE]"}, {"T540-CR 10G", "Chelsio T540-CR 10G [FCoE]"}, {"T520-BCH 10G", "Chelsio T520-BCH 10G [FCoE]"}, {"T540-BCH 10G", "Chelsio T540-BCH 10G [FCoE]"}, @@ -107,7 +80,9 @@ static const struct csio_adap_desc csio_t5_fcoe_adapters[] = { {"T580-LP-CR 40G", "Chelsio T580-LP-CR 40G [FCoE]"}, {"T520-LL-CR 10G", "Chelsio T520-LL-CR 10G [FCoE]"}, {"T560-CR 40G", "Chelsio T560-CR 40G [FCoE]"}, - {"T580-CR 40G", "Chelsio T580-CR 40G [FCoE]"} + {"T580-CR 40G", "Chelsio T580-CR 40G [FCoE]"}, + {"T580-SO 40G", "Chelsio T580-SO 40G [FCoE]"}, + {"T502-BT 1G", "Chelsio T502-BT 1G [FCoE]"} }; static void csio_mgmtm_cleanup(struct csio_mgmtm *); @@ -188,9 +163,9 @@ void csio_hw_tp_wr_bits_indirect(struct csio_hw *hw, unsigned int addr, unsigned int mask, unsigned int val) { - csio_wr_reg32(hw, addr, TP_PIO_ADDR); - val |= csio_rd_reg32(hw, TP_PIO_DATA) & ~mask; - csio_wr_reg32(hw, val, TP_PIO_DATA); + csio_wr_reg32(hw, addr, TP_PIO_ADDR_A); + val |= csio_rd_reg32(hw, TP_PIO_DATA_A) & ~mask; + csio_wr_reg32(hw, val, TP_PIO_DATA_A); } void @@ -256,7 +231,7 @@ csio_hw_seeprom_read(struct csio_hw *hw, uint32_t addr, uint32_t *data) } pci_read_config_dword(hw->pdev, base + PCI_VPD_DATA, data); - *data = le32_to_cpu(*data); + *data = le32_to_cpu(*(__le32 *)data); return 0; } @@ -421,17 +396,15 @@ csio_hw_sf1_read(struct csio_hw *hw, uint32_t byte_cnt, int32_t cont, if (!byte_cnt || byte_cnt > 4) return -EINVAL; - if (csio_rd_reg32(hw, SF_OP) & SF_BUSY) + if (csio_rd_reg32(hw, SF_OP_A) & SF_BUSY_F) return -EBUSY; - cont = cont ? SF_CONT : 0; - lock = lock ? SF_LOCK : 0; - - csio_wr_reg32(hw, lock | cont | BYTECNT(byte_cnt - 1), SF_OP); - ret = csio_hw_wait_op_done_val(hw, SF_OP, SF_BUSY, 0, SF_ATTEMPTS, - 10, NULL); + csio_wr_reg32(hw, SF_LOCK_V(lock) | SF_CONT_V(cont) | + BYTECNT_V(byte_cnt - 1), SF_OP_A); + ret = csio_hw_wait_op_done_val(hw, SF_OP_A, SF_BUSY_F, 0, SF_ATTEMPTS, + 10, NULL); if (!ret) - *valp = csio_rd_reg32(hw, SF_DATA); + *valp = csio_rd_reg32(hw, SF_DATA_A); return ret; } @@ -453,16 +426,14 @@ csio_hw_sf1_write(struct csio_hw *hw, uint32_t byte_cnt, uint32_t cont, { if (!byte_cnt || byte_cnt > 4) return -EINVAL; - if (csio_rd_reg32(hw, SF_OP) & SF_BUSY) + if (csio_rd_reg32(hw, SF_OP_A) & SF_BUSY_F) return -EBUSY; - cont = cont ? SF_CONT : 0; - lock = lock ? SF_LOCK : 0; - - csio_wr_reg32(hw, val, SF_DATA); - csio_wr_reg32(hw, cont | BYTECNT(byte_cnt - 1) | OP_WR | lock, SF_OP); + csio_wr_reg32(hw, val, SF_DATA_A); + csio_wr_reg32(hw, SF_CONT_V(cont) | BYTECNT_V(byte_cnt - 1) | + OP_V(1) | SF_LOCK_V(lock), SF_OP_A); - return csio_hw_wait_op_done_val(hw, SF_OP, SF_BUSY, 0, SF_ATTEMPTS, + return csio_hw_wait_op_done_val(hw, SF_OP_A, SF_BUSY_F, 0, SF_ATTEMPTS, 10, NULL); } @@ -533,11 +504,11 @@ csio_hw_read_flash(struct csio_hw *hw, uint32_t addr, uint32_t nwords, for ( ; nwords; nwords--, data++) { ret = csio_hw_sf1_read(hw, 4, nwords > 1, nwords == 1, data); if (nwords == 1) - csio_wr_reg32(hw, 0, SF_OP); /* unlock SF */ + csio_wr_reg32(hw, 0, SF_OP_A); /* unlock SF */ if (ret) return ret; if (byte_oriented) - *data = htonl(*data); + *data = (__force __u32) htonl(*data); } return 0; } @@ -586,7 +557,7 @@ csio_hw_write_flash(struct csio_hw *hw, uint32_t addr, if (ret) goto unlock; - csio_wr_reg32(hw, 0, SF_OP); /* unlock SF */ + csio_wr_reg32(hw, 0, SF_OP_A); /* unlock SF */ /* Read the page to verify the write succeeded */ ret = csio_hw_read_flash(hw, addr & ~0xff, ARRAY_SIZE(buf), buf, 1); @@ -603,7 +574,7 @@ csio_hw_write_flash(struct csio_hw *hw, uint32_t addr, return 0; unlock: - csio_wr_reg32(hw, 0, SF_OP); /* unlock SF */ + csio_wr_reg32(hw, 0, SF_OP_A); /* unlock SF */ return ret; } @@ -641,7 +612,7 @@ out: if (ret) csio_err(hw, "erase of flash sector %d failed, error %d\n", start, ret); - csio_wr_reg32(hw, 0, SF_OP); /* unlock SF */ + csio_wr_reg32(hw, 0, SF_OP_A); /* unlock SF */ return 0; } @@ -665,7 +636,7 @@ csio_hw_print_fw_version(struct csio_hw *hw, char *str) static int csio_hw_get_fw_version(struct csio_hw *hw, uint32_t *vers) { - return csio_hw_read_flash(hw, FW_IMG_START + + return csio_hw_read_flash(hw, FLASH_FW_START + offsetof(struct fw_hdr, fw_ver), 1, vers, 0); } @@ -686,43 +657,6 @@ csio_hw_get_tp_version(struct csio_hw *hw, u32 *vers) } /* - * csio_hw_check_fw_version - check if the FW is compatible with - * this driver - * @hw: HW module - * - * Checks if an adapter's FW is compatible with the driver. Returns 0 - * if there's exact match, a negative error if the version could not be - * read or there's a major/minor version mismatch/minor. - */ -static int -csio_hw_check_fw_version(struct csio_hw *hw) -{ - int ret, major, minor, micro; - - ret = csio_hw_get_fw_version(hw, &hw->fwrev); - if (!ret) - ret = csio_hw_get_tp_version(hw, &hw->tp_vers); - if (ret) - return ret; - - major = FW_HDR_FW_VER_MAJOR_G(hw->fwrev); - minor = FW_HDR_FW_VER_MINOR_G(hw->fwrev); - micro = FW_HDR_FW_VER_MICRO_G(hw->fwrev); - - if (major != FW_VERSION_MAJOR(hw)) { /* major mismatch - fail */ - csio_err(hw, "card FW has major version %u, driver wants %u\n", - major, FW_VERSION_MAJOR(hw)); - return -EINVAL; - } - - if (minor == FW_VERSION_MINOR(hw) && micro == FW_VERSION_MICRO(hw)) - return 0; /* perfect match */ - - /* Minor/micro version mismatch */ - return -EINVAL; -} - -/* * csio_hw_fw_dload - download firmware. * @hw: HW module * @fw_data: firmware image to write. @@ -762,9 +696,9 @@ csio_hw_fw_dload(struct csio_hw *hw, uint8_t *fw_data, uint32_t size) return -EINVAL; } - if (size > FW_MAX_SIZE) { + if (size > FLASH_FW_MAX_SIZE) { csio_err(hw, "FW image too large, max is %u bytes\n", - FW_MAX_SIZE); + FLASH_FW_MAX_SIZE); return -EINVAL; } @@ -780,10 +714,10 @@ csio_hw_fw_dload(struct csio_hw *hw, uint8_t *fw_data, uint32_t size) i = DIV_ROUND_UP(size, sf_sec_size); /* # of sectors spanned */ csio_dbg(hw, "Erasing sectors... start:%d end:%d\n", - FW_START_SEC, FW_START_SEC + i - 1); + FLASH_FW_START_SEC, FLASH_FW_START_SEC + i - 1); - ret = csio_hw_flash_erase_sectors(hw, FW_START_SEC, - FW_START_SEC + i - 1); + ret = csio_hw_flash_erase_sectors(hw, FLASH_FW_START_SEC, + FLASH_FW_START_SEC + i - 1); if (ret) { csio_err(hw, "Flash Erase failed\n"); goto out; @@ -796,14 +730,14 @@ csio_hw_fw_dload(struct csio_hw *hw, uint8_t *fw_data, uint32_t size) */ memcpy(first_page, fw_data, SF_PAGE_SIZE); ((struct fw_hdr *)first_page)->fw_ver = htonl(0xffffffff); - ret = csio_hw_write_flash(hw, FW_IMG_START, SF_PAGE_SIZE, first_page); + ret = csio_hw_write_flash(hw, FLASH_FW_START, SF_PAGE_SIZE, first_page); if (ret) goto out; csio_dbg(hw, "Writing Flash .. start:%d end:%d\n", FW_IMG_START, FW_IMG_START + size); - addr = FW_IMG_START; + addr = FLASH_FW_START; for (size -= SF_PAGE_SIZE; size; size -= SF_PAGE_SIZE) { addr += SF_PAGE_SIZE; fw_data += SF_PAGE_SIZE; @@ -813,7 +747,7 @@ csio_hw_fw_dload(struct csio_hw *hw, uint8_t *fw_data, uint32_t size) } ret = csio_hw_write_flash(hw, - FW_IMG_START + + FLASH_FW_START + offsetof(struct fw_hdr, fw_ver), sizeof(hdr->fw_ver), (const uint8_t *)&hdr->fw_ver); @@ -833,7 +767,7 @@ csio_hw_get_flash_params(struct csio_hw *hw) ret = csio_hw_sf1_write(hw, 1, 1, 0, SF_RD_ID); if (!ret) ret = csio_hw_sf1_read(hw, 3, 0, 1, &info); - csio_wr_reg32(hw, 0, SF_OP); /* unlock SF */ + csio_wr_reg32(hw, 0, SF_OP_A); /* unlock SF */ if (ret != 0) return ret; @@ -861,17 +795,17 @@ csio_hw_dev_ready(struct csio_hw *hw) uint32_t reg; int cnt = 6; - while (((reg = csio_rd_reg32(hw, PL_WHOAMI)) == 0xFFFFFFFF) && - (--cnt != 0)) + while (((reg = csio_rd_reg32(hw, PL_WHOAMI_A)) == 0xFFFFFFFF) && + (--cnt != 0)) mdelay(100); - if ((cnt == 0) && (((int32_t)(SOURCEPF_GET(reg)) < 0) || - (SOURCEPF_GET(reg) >= CSIO_MAX_PFN))) { + if ((cnt == 0) && (((int32_t)(SOURCEPF_G(reg)) < 0) || + (SOURCEPF_G(reg) >= CSIO_MAX_PFN))) { csio_err(hw, "PL_WHOAMI returned 0x%x, cnt:%d\n", reg, cnt); return -EIO; } - hw->pfn = SOURCEPF_GET(reg); + hw->pfn = SOURCEPF_G(reg); return 0; } @@ -959,8 +893,8 @@ retry: * timeout ... and then retry if we haven't exhausted * our retries ... */ - pcie_fw = csio_rd_reg32(hw, PCIE_FW); - if (!(pcie_fw & (PCIE_FW_ERR|PCIE_FW_INIT))) { + pcie_fw = csio_rd_reg32(hw, PCIE_FW_A); + if (!(pcie_fw & (PCIE_FW_ERR_F|PCIE_FW_INIT_F))) { if (waiting <= 0) { if (retries-- > 0) goto retry; @@ -976,10 +910,10 @@ retry: * report errors preferentially. */ if (state) { - if (pcie_fw & PCIE_FW_ERR) { + if (pcie_fw & PCIE_FW_ERR_F) { *state = CSIO_DEV_STATE_ERR; rv = -ETIMEDOUT; - } else if (pcie_fw & PCIE_FW_INIT) + } else if (pcie_fw & PCIE_FW_INIT_F) *state = CSIO_DEV_STATE_INIT; } @@ -988,9 +922,9 @@ retry: * there's not a valid Master PF, grab its identity * for our caller. */ - if (mpfn == PCIE_FW_MASTER_MASK && - (pcie_fw & PCIE_FW_MASTER_VLD)) - mpfn = PCIE_FW_MASTER_GET(pcie_fw); + if (mpfn == PCIE_FW_MASTER_M && + (pcie_fw & PCIE_FW_MASTER_VLD_F)) + mpfn = PCIE_FW_MASTER_G(pcie_fw); break; } hw->flags &= ~CSIO_HWF_MASTER; @@ -1078,7 +1012,7 @@ csio_do_reset(struct csio_hw *hw, bool fw_rst) if (!fw_rst) { /* PIO reset */ - csio_wr_reg32(hw, PIORSTMODE | PIORST, PL_RST); + csio_wr_reg32(hw, PIORSTMODE_F | PIORST_F, PL_RST_A); mdelay(2000); return 0; } @@ -1090,7 +1024,7 @@ csio_do_reset(struct csio_hw *hw, bool fw_rst) } csio_mb_reset(hw, mbp, CSIO_MB_DEFAULT_TMO, - PIORSTMODE | PIORST, 0, NULL); + PIORSTMODE_F | PIORST_F, 0, NULL); if (csio_mb_issue(hw, mbp)) { csio_err(hw, "Issue of RESET command failed.n"); @@ -1156,7 +1090,7 @@ csio_hw_fw_halt(struct csio_hw *hw, uint32_t mbox, int32_t force) * If a legitimate mailbox is provided, issue a RESET command * with a HALT indication. */ - if (mbox <= PCIE_FW_MASTER_MASK) { + if (mbox <= PCIE_FW_MASTER_M) { struct csio_mb *mbp; mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC); @@ -1166,7 +1100,7 @@ csio_hw_fw_halt(struct csio_hw *hw, uint32_t mbox, int32_t force) } csio_mb_reset(hw, mbp, CSIO_MB_DEFAULT_TMO, - PIORSTMODE | PIORST, FW_RESET_CMD_HALT_F, + PIORSTMODE_F | PIORST_F, FW_RESET_CMD_HALT_F, NULL); if (csio_mb_issue(hw, mbp)) { @@ -1193,8 +1127,9 @@ csio_hw_fw_halt(struct csio_hw *hw, uint32_t mbox, int32_t force) * rather than a RESET ... if it's new enough to understand that ... */ if (retval == 0 || force) { - csio_set_reg_field(hw, CIM_BOOT_CFG, UPCRST, UPCRST); - csio_set_reg_field(hw, PCIE_FW, PCIE_FW_HALT, PCIE_FW_HALT); + csio_set_reg_field(hw, CIM_BOOT_CFG_A, UPCRST_F, UPCRST_F); + csio_set_reg_field(hw, PCIE_FW_A, PCIE_FW_HALT_F, + PCIE_FW_HALT_F); } /* @@ -1234,7 +1169,7 @@ csio_hw_fw_restart(struct csio_hw *hw, uint32_t mbox, int32_t reset) * doing it automatically, we need to clear the PCIE_FW.HALT * bit. */ - csio_set_reg_field(hw, PCIE_FW, PCIE_FW_HALT, 0); + csio_set_reg_field(hw, PCIE_FW_A, PCIE_FW_HALT_F, 0); /* * If we've been given a valid mailbox, first try to get the @@ -1243,21 +1178,21 @@ csio_hw_fw_restart(struct csio_hw *hw, uint32_t mbox, int32_t reset) * valid mailbox or the RESET command failed, fall back to * hitting the chip with a hammer. */ - if (mbox <= PCIE_FW_MASTER_MASK) { - csio_set_reg_field(hw, CIM_BOOT_CFG, UPCRST, 0); + if (mbox <= PCIE_FW_MASTER_M) { + csio_set_reg_field(hw, CIM_BOOT_CFG_A, UPCRST_F, 0); msleep(100); if (csio_do_reset(hw, true) == 0) return 0; } - csio_wr_reg32(hw, PIORSTMODE | PIORST, PL_RST); + csio_wr_reg32(hw, PIORSTMODE_F | PIORST_F, PL_RST_A); msleep(2000); } else { int ms; - csio_set_reg_field(hw, CIM_BOOT_CFG, UPCRST, 0); + csio_set_reg_field(hw, CIM_BOOT_CFG_A, UPCRST_F, 0); for (ms = 0; ms < FW_CMD_MAX_TIMEOUT; ) { - if (!(csio_rd_reg32(hw, PCIE_FW) & PCIE_FW_HALT)) + if (!(csio_rd_reg32(hw, PCIE_FW_A) & PCIE_FW_HALT_F)) return 0; msleep(100); ms += 100; @@ -1315,116 +1250,6 @@ csio_hw_fw_upgrade(struct csio_hw *hw, uint32_t mbox, return csio_hw_fw_restart(hw, mbox, reset); } - -/* - * csio_hw_fw_config_file - setup an adapter via a Configuration File - * @hw: the HW module - * @mbox: mailbox to use for the FW command - * @mtype: the memory type where the Configuration File is located - * @maddr: the memory address where the Configuration File is located - * @finiver: return value for CF [fini] version - * @finicsum: return value for CF [fini] checksum - * @cfcsum: return value for CF computed checksum - * - * Issue a command to get the firmware to process the Configuration - * File located at the specified mtype/maddress. If the Configuration - * File is processed successfully and return value pointers are - * provided, the Configuration File "[fini] section version and - * checksum values will be returned along with the computed checksum. - * It's up to the caller to decide how it wants to respond to the - * checksums not matching but it recommended that a prominant warning - * be emitted in order to help people rapidly identify changed or - * corrupted Configuration Files. - * - * Also note that it's possible to modify things like "niccaps", - * "toecaps",etc. between processing the Configuration File and telling - * the firmware to use the new configuration. Callers which want to - * do this will need to "hand-roll" their own CAPS_CONFIGS commands for - * Configuration Files if they want to do this. - */ -static int -csio_hw_fw_config_file(struct csio_hw *hw, - unsigned int mtype, unsigned int maddr, - uint32_t *finiver, uint32_t *finicsum, uint32_t *cfcsum) -{ - struct csio_mb *mbp; - struct fw_caps_config_cmd *caps_cmd; - int rv = -EINVAL; - enum fw_retval ret; - - mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC); - if (!mbp) { - CSIO_INC_STATS(hw, n_err_nomem); - return -ENOMEM; - } - /* - * Tell the firmware to process the indicated Configuration File. - * If there are no errors and the caller has provided return value - * pointers for the [fini] section version, checksum and computed - * checksum, pass those back to the caller. - */ - caps_cmd = (struct fw_caps_config_cmd *)(mbp->mb); - CSIO_INIT_MBP(mbp, caps_cmd, CSIO_MB_DEFAULT_TMO, hw, NULL, 1); - caps_cmd->op_to_write = - htonl(FW_CMD_OP_V(FW_CAPS_CONFIG_CMD) | - FW_CMD_REQUEST_F | - FW_CMD_READ_F); - caps_cmd->cfvalid_to_len16 = - htonl(FW_CAPS_CONFIG_CMD_CFVALID_F | - FW_CAPS_CONFIG_CMD_MEMTYPE_CF_V(mtype) | - FW_CAPS_CONFIG_CMD_MEMADDR64K_CF_V(maddr >> 16) | - FW_LEN16(*caps_cmd)); - - if (csio_mb_issue(hw, mbp)) { - csio_err(hw, "Issue of FW_CAPS_CONFIG_CMD failed!\n"); - goto out; - } - - ret = csio_mb_fw_retval(mbp); - if (ret != FW_SUCCESS) { - csio_dbg(hw, "FW_CAPS_CONFIG_CMD returned %d!\n", rv); - goto out; - } - - if (finiver) - *finiver = ntohl(caps_cmd->finiver); - if (finicsum) - *finicsum = ntohl(caps_cmd->finicsum); - if (cfcsum) - *cfcsum = ntohl(caps_cmd->cfcsum); - - /* Validate device capabilities */ - if (csio_hw_validate_caps(hw, mbp)) { - rv = -ENOENT; - goto out; - } - - /* - * And now tell the firmware to use the configuration we just loaded. - */ - caps_cmd->op_to_write = - htonl(FW_CMD_OP_V(FW_CAPS_CONFIG_CMD) | - FW_CMD_REQUEST_F | - FW_CMD_WRITE_F); - caps_cmd->cfvalid_to_len16 = htonl(FW_LEN16(*caps_cmd)); - - if (csio_mb_issue(hw, mbp)) { - csio_err(hw, "Issue of FW_CAPS_CONFIG_CMD failed!\n"); - goto out; - } - - ret = csio_mb_fw_retval(mbp); - if (ret != FW_SUCCESS) { - csio_dbg(hw, "FW_CAPS_CONFIG_CMD returned %d!\n", rv); - goto out; - } - - rv = 0; -out: - mempool_free(mbp, hw->mb_mempool); - return rv; -} - /* * csio_get_device_params - Get device parameters. * @hw: HW module @@ -1547,7 +1372,8 @@ csio_config_device_caps(struct csio_hw *hw) } /* Validate device capabilities */ - if (csio_hw_validate_caps(hw, mbp)) + rv = csio_hw_validate_caps(hw, mbp); + if (rv != 0) goto out; /* Don't config device capabilities if already configured */ @@ -1756,9 +1582,9 @@ csio_hw_flash_config(struct csio_hw *hw, u32 *fw_cfg_param, char *path) uint32_t *cfg_data; int value_to_add = 0; - if (request_firmware(&cf, CSIO_CF_FNAME(hw), dev) < 0) { + if (request_firmware(&cf, FW_CFG_NAME_T5, dev) < 0) { csio_err(hw, "could not find config file %s, err: %d\n", - CSIO_CF_FNAME(hw), ret); + FW_CFG_NAME_T5, ret); return -ENOENT; } @@ -1798,8 +1624,8 @@ csio_hw_flash_config(struct csio_hw *hw, u32 *fw_cfg_param, char *path) } if (ret == 0) { csio_info(hw, "config file upgraded to %s\n", - CSIO_CF_FNAME(hw)); - snprintf(path, 64, "%s%s", "/lib/firmware/", CSIO_CF_FNAME(hw)); + FW_CFG_NAME_T5); + snprintf(path, 64, "%s%s", "/lib/firmware/", FW_CFG_NAME_T5); } leave: @@ -1827,11 +1653,13 @@ leave: static int csio_hw_use_fwconfig(struct csio_hw *hw, int reset, u32 *fw_cfg_param) { + struct csio_mb *mbp = NULL; + struct fw_caps_config_cmd *caps_cmd; unsigned int mtype, maddr; - int rv; + int rv = -EINVAL; uint32_t finiver = 0, finicsum = 0, cfcsum = 0; - int using_flash; char path[64]; + char *config_name = NULL; /* * Reset device if necessary @@ -1851,51 +1679,107 @@ csio_hw_use_fwconfig(struct csio_hw *hw, int reset, u32 *fw_cfg_param) rv = csio_hw_flash_config(hw, fw_cfg_param, path); spin_lock_irq(&hw->lock); if (rv != 0) { - if (rv == -ENOENT) { - /* - * config file was not found. Use default - * config file from flash. - */ - mtype = FW_MEMTYPE_CF_FLASH; - maddr = hw->chip_ops->chip_flash_cfg_addr(hw); - using_flash = 1; - } else { - /* - * we revert back to the hardwired config if - * flashing failed. - */ - goto bye; - } + /* + * config file was not found. Use default + * config file from flash. + */ + config_name = "On FLASH"; + mtype = FW_MEMTYPE_CF_FLASH; + maddr = hw->chip_ops->chip_flash_cfg_addr(hw); } else { + config_name = path; mtype = FW_PARAMS_PARAM_Y_G(*fw_cfg_param); maddr = FW_PARAMS_PARAM_Z_G(*fw_cfg_param) << 16; - using_flash = 0; } - hw->cfg_store = (uint8_t)mtype; + mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC); + if (!mbp) { + CSIO_INC_STATS(hw, n_err_nomem); + return -ENOMEM; + } + /* + * Tell the firmware to process the indicated Configuration File. + * If there are no errors and the caller has provided return value + * pointers for the [fini] section version, checksum and computed + * checksum, pass those back to the caller. + */ + caps_cmd = (struct fw_caps_config_cmd *)(mbp->mb); + CSIO_INIT_MBP(mbp, caps_cmd, CSIO_MB_DEFAULT_TMO, hw, NULL, 1); + caps_cmd->op_to_write = + htonl(FW_CMD_OP_V(FW_CAPS_CONFIG_CMD) | + FW_CMD_REQUEST_F | + FW_CMD_READ_F); + caps_cmd->cfvalid_to_len16 = + htonl(FW_CAPS_CONFIG_CMD_CFVALID_F | + FW_CAPS_CONFIG_CMD_MEMTYPE_CF_V(mtype) | + FW_CAPS_CONFIG_CMD_MEMADDR64K_CF_V(maddr >> 16) | + FW_LEN16(*caps_cmd)); + + if (csio_mb_issue(hw, mbp)) { + rv = -EINVAL; + goto bye; + } + + rv = csio_mb_fw_retval(mbp); + /* If the CAPS_CONFIG failed with an ENOENT (for a Firmware + * Configuration File in FLASH), our last gasp effort is to use the + * Firmware Configuration File which is embedded in the + * firmware. A very few early versions of the firmware didn't + * have one embedded but we can ignore those. + */ + if (rv == ENOENT) { + CSIO_INIT_MBP(mbp, caps_cmd, CSIO_MB_DEFAULT_TMO, hw, NULL, 1); + caps_cmd->op_to_write = htonl(FW_CMD_OP_V(FW_CAPS_CONFIG_CMD) | + FW_CMD_REQUEST_F | + FW_CMD_READ_F); + caps_cmd->cfvalid_to_len16 = htonl(FW_LEN16(*caps_cmd)); + + if (csio_mb_issue(hw, mbp)) { + rv = -EINVAL; + goto bye; + } + + rv = csio_mb_fw_retval(mbp); + config_name = "Firmware Default"; + } + if (rv != FW_SUCCESS) + goto bye; + + finiver = ntohl(caps_cmd->finiver); + finicsum = ntohl(caps_cmd->finicsum); + cfcsum = ntohl(caps_cmd->cfcsum); /* - * Issue a Capability Configuration command to the firmware to get it - * to parse the Configuration File. + * And now tell the firmware to use the configuration we just loaded. */ - rv = csio_hw_fw_config_file(hw, mtype, maddr, &finiver, - &finicsum, &cfcsum); - if (rv != 0) + caps_cmd->op_to_write = + htonl(FW_CMD_OP_V(FW_CAPS_CONFIG_CMD) | + FW_CMD_REQUEST_F | + FW_CMD_WRITE_F); + caps_cmd->cfvalid_to_len16 = htonl(FW_LEN16(*caps_cmd)); + + if (csio_mb_issue(hw, mbp)) { + rv = -EINVAL; goto bye; + } - hw->cfg_finiver = finiver; - hw->cfg_finicsum = finicsum; - hw->cfg_cfcsum = cfcsum; - hw->cfg_csum_status = true; + rv = csio_mb_fw_retval(mbp); + if (rv != FW_SUCCESS) { + csio_dbg(hw, "FW_CAPS_CONFIG_CMD returned %d!\n", rv); + goto bye; + } + mempool_free(mbp, hw->mb_mempool); if (finicsum != cfcsum) { csio_warn(hw, "Config File checksum mismatch: csum=%#x, computed=%#x\n", finicsum, cfcsum); - - hw->cfg_csum_status = false; } + /* Validate device capabilities */ + rv = csio_hw_validate_caps(hw, mbp); + if (rv != 0) + goto bye; /* * Note that we're operating with parameters * not supplied by the driver, rather than from hard-wired @@ -1918,56 +1802,184 @@ csio_hw_use_fwconfig(struct csio_hw *hw, int reset, u32 *fw_cfg_param) /* Post event to notify completion of configuration */ csio_post_event(&hw->sm, CSIO_HWE_INIT); - csio_info(hw, - "Firmware Configuration File %s, version %#x, computed checksum %#x\n", - (using_flash ? "in device FLASH" : path), finiver, cfcsum); - + csio_info(hw, "Successfully configure using Firmware " + "Configuration File %s, version %#x, computed checksum %#x\n", + config_name, finiver, cfcsum); return 0; /* * Something bad happened. Return the error ... */ bye: + if (mbp) + mempool_free(mbp, hw->mb_mempool); hw->flags &= ~CSIO_HWF_USING_SOFT_PARAMS; - csio_dbg(hw, "Configuration file error %d\n", rv); + csio_warn(hw, "Configuration file error %d\n", rv); return rv; } -/* - * Attempt to initialize the adapter via hard-coded, driver supplied - * parameters ... +/* Is the given firmware API compatible with the one the driver was compiled + * with? */ -static int -csio_hw_no_fwconfig(struct csio_hw *hw, int reset) +static int fw_compatible(const struct fw_hdr *hdr1, const struct fw_hdr *hdr2) { - int rv; - /* - * Reset device if necessary - */ - if (reset) { - rv = csio_do_reset(hw, true); - if (rv != 0) - goto out; + + /* short circuit if it's the exact same firmware version */ + if (hdr1->chip == hdr2->chip && hdr1->fw_ver == hdr2->fw_ver) + return 1; + +#define SAME_INTF(x) (hdr1->intfver_##x == hdr2->intfver_##x) + if (hdr1->chip == hdr2->chip && SAME_INTF(nic) && SAME_INTF(vnic) && + SAME_INTF(ri) && SAME_INTF(iscsi) && SAME_INTF(fcoe)) + return 1; +#undef SAME_INTF + + return 0; +} + +/* The firmware in the filesystem is usable, but should it be installed? + * This routine explains itself in detail if it indicates the filesystem + * firmware should be installed. + */ +static int csio_should_install_fs_fw(struct csio_hw *hw, int card_fw_usable, + int k, int c) +{ + const char *reason; + + if (!card_fw_usable) { + reason = "incompatible or unusable"; + goto install; } - /* Get and set device capabilities */ - rv = csio_config_device_caps(hw); - if (rv != 0) - goto out; + if (k > c) { + reason = "older than the version supported with this driver"; + goto install; + } - /* device parameters */ - rv = csio_get_device_params(hw); - if (rv != 0) - goto out; + return 0; - /* Configure SGE */ - csio_wr_sge_init(hw); +install: + csio_err(hw, "firmware on card (%u.%u.%u.%u) is %s, " + "installing firmware %u.%u.%u.%u on card.\n", + FW_HDR_FW_VER_MAJOR_G(c), FW_HDR_FW_VER_MINOR_G(c), + FW_HDR_FW_VER_MICRO_G(c), FW_HDR_FW_VER_BUILD_G(c), reason, + FW_HDR_FW_VER_MAJOR_G(k), FW_HDR_FW_VER_MINOR_G(k), + FW_HDR_FW_VER_MICRO_G(k), FW_HDR_FW_VER_BUILD_G(k)); - /* Post event to notify completion of configuration */ - csio_post_event(&hw->sm, CSIO_HWE_INIT); + return 1; +} -out: - return rv; +static struct fw_info fw_info_array[] = { + { + .chip = CHELSIO_T5, + .fs_name = FW_CFG_NAME_T5, + .fw_mod_name = FW_FNAME_T5, + .fw_hdr = { + .chip = FW_HDR_CHIP_T5, + .fw_ver = __cpu_to_be32(FW_VERSION(T5)), + .intfver_nic = FW_INTFVER(T5, NIC), + .intfver_vnic = FW_INTFVER(T5, VNIC), + .intfver_ri = FW_INTFVER(T5, RI), + .intfver_iscsi = FW_INTFVER(T5, ISCSI), + .intfver_fcoe = FW_INTFVER(T5, FCOE), + }, + } +}; + +static struct fw_info *find_fw_info(int chip) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fw_info_array); i++) { + if (fw_info_array[i].chip == chip) + return &fw_info_array[i]; + } + return NULL; +} + +static int csio_hw_prep_fw(struct csio_hw *hw, struct fw_info *fw_info, + const u8 *fw_data, unsigned int fw_size, + struct fw_hdr *card_fw, enum csio_dev_state state, + int *reset) +{ + int ret, card_fw_usable, fs_fw_usable; + const struct fw_hdr *fs_fw; + const struct fw_hdr *drv_fw; + + drv_fw = &fw_info->fw_hdr; + + /* Read the header of the firmware on the card */ + ret = csio_hw_read_flash(hw, FLASH_FW_START, + sizeof(*card_fw) / sizeof(uint32_t), + (uint32_t *)card_fw, 1); + if (ret == 0) { + card_fw_usable = fw_compatible(drv_fw, (const void *)card_fw); + } else { + csio_err(hw, + "Unable to read card's firmware header: %d\n", ret); + card_fw_usable = 0; + } + + if (fw_data != NULL) { + fs_fw = (const void *)fw_data; + fs_fw_usable = fw_compatible(drv_fw, fs_fw); + } else { + fs_fw = NULL; + fs_fw_usable = 0; + } + + if (card_fw_usable && card_fw->fw_ver == drv_fw->fw_ver && + (!fs_fw_usable || fs_fw->fw_ver == drv_fw->fw_ver)) { + /* Common case: the firmware on the card is an exact match and + * the filesystem one is an exact match too, or the filesystem + * one is absent/incompatible. + */ + } else if (fs_fw_usable && state == CSIO_DEV_STATE_UNINIT && + csio_should_install_fs_fw(hw, card_fw_usable, + be32_to_cpu(fs_fw->fw_ver), + be32_to_cpu(card_fw->fw_ver))) { + ret = csio_hw_fw_upgrade(hw, hw->pfn, fw_data, + fw_size, 0); + if (ret != 0) { + csio_err(hw, + "failed to install firmware: %d\n", ret); + goto bye; + } + + /* Installed successfully, update the cached header too. */ + memcpy(card_fw, fs_fw, sizeof(*card_fw)); + card_fw_usable = 1; + *reset = 0; /* already reset as part of load_fw */ + } + + if (!card_fw_usable) { + uint32_t d, c, k; + + d = be32_to_cpu(drv_fw->fw_ver); + c = be32_to_cpu(card_fw->fw_ver); + k = fs_fw ? be32_to_cpu(fs_fw->fw_ver) : 0; + + csio_err(hw, "Cannot find a usable firmware: " + "chip state %d, " + "driver compiled with %d.%d.%d.%d, " + "card has %d.%d.%d.%d, filesystem has %d.%d.%d.%d\n", + state, + FW_HDR_FW_VER_MAJOR_G(d), FW_HDR_FW_VER_MINOR_G(d), + FW_HDR_FW_VER_MICRO_G(d), FW_HDR_FW_VER_BUILD_G(d), + FW_HDR_FW_VER_MAJOR_G(c), FW_HDR_FW_VER_MINOR_G(c), + FW_HDR_FW_VER_MICRO_G(c), FW_HDR_FW_VER_BUILD_G(c), + FW_HDR_FW_VER_MAJOR_G(k), FW_HDR_FW_VER_MINOR_G(k), + FW_HDR_FW_VER_MICRO_G(k), FW_HDR_FW_VER_BUILD_G(k)); + ret = EINVAL; + goto bye; + } + + /* We're using whatever's on the card and it's known to be good. */ + hw->fwrev = be32_to_cpu(card_fw->fw_ver); + hw->tp_vers = be32_to_cpu(card_fw->tp_microcode_ver); + +bye: + return ret; } /* @@ -1977,48 +1989,52 @@ out: * latest firmware ECANCELED is returned */ static int -csio_hw_flash_fw(struct csio_hw *hw) +csio_hw_flash_fw(struct csio_hw *hw, int *reset) { int ret = -ECANCELED; const struct firmware *fw; - const struct fw_hdr *hdr; - u32 fw_ver; + struct fw_info *fw_info; + struct fw_hdr *card_fw; struct pci_dev *pci_dev = hw->pdev; struct device *dev = &pci_dev->dev ; + const u8 *fw_data = NULL; + unsigned int fw_size = 0; - if (request_firmware(&fw, CSIO_FW_FNAME(hw), dev) < 0) { - csio_err(hw, "could not find firmware image %s, err: %d\n", - CSIO_FW_FNAME(hw), ret); + /* This is the firmware whose headers the driver was compiled + * against + */ + fw_info = find_fw_info(CHELSIO_CHIP_VERSION(hw->chip_id)); + if (fw_info == NULL) { + csio_err(hw, + "unable to get firmware info for chip %d.\n", + CHELSIO_CHIP_VERSION(hw->chip_id)); return -EINVAL; } - hdr = (const struct fw_hdr *)fw->data; - fw_ver = ntohl(hdr->fw_ver); - if (FW_HDR_FW_VER_MAJOR_G(fw_ver) != FW_VERSION_MAJOR(hw)) - return -EINVAL; /* wrong major version, won't do */ + if (request_firmware(&fw, FW_FNAME_T5, dev) < 0) { + csio_err(hw, "could not find firmware image %s, err: %d\n", + FW_FNAME_T5, ret); + } else { + fw_data = fw->data; + fw_size = fw->size; + } - /* - * If the flash FW is unusable or we found something newer, load it. + /* allocate memory to read the header of the firmware on the + * card */ - if (FW_HDR_FW_VER_MAJOR_G(hw->fwrev) != FW_VERSION_MAJOR(hw) || - fw_ver > hw->fwrev) { - ret = csio_hw_fw_upgrade(hw, hw->pfn, fw->data, fw->size, - /*force=*/false); - if (!ret) - csio_info(hw, - "firmware upgraded to version %pI4 from %s\n", - &hdr->fw_ver, CSIO_FW_FNAME(hw)); - else - csio_err(hw, "firmware upgrade failed! err=%d\n", ret); - } else - ret = -EINVAL; + card_fw = kmalloc(sizeof(*card_fw), GFP_KERNEL); - release_firmware(fw); + /* upgrade FW logic */ + ret = csio_hw_prep_fw(hw, fw_info, fw_data, fw_size, card_fw, + hw->fw_state, reset); + /* Cleaning up */ + if (fw != NULL) + release_firmware(fw); + kfree(card_fw); return ret; } - /* * csio_hw_configure - Configure HW * @hw - HW module @@ -2039,7 +2055,7 @@ csio_hw_configure(struct csio_hw *hw) } /* HW version */ - hw->chip_ver = (char)csio_rd_reg32(hw, PL_REV); + hw->chip_ver = (char)csio_rd_reg32(hw, PL_REV_A); /* Needed for FW download */ rv = csio_hw_get_flash_params(hw); @@ -2074,50 +2090,43 @@ csio_hw_configure(struct csio_hw *hw) if (rv != 0) goto out; + csio_hw_get_fw_version(hw, &hw->fwrev); + csio_hw_get_tp_version(hw, &hw->tp_vers); if (csio_is_hw_master(hw) && hw->fw_state != CSIO_DEV_STATE_INIT) { - rv = csio_hw_check_fw_version(hw); - if (rv == -EINVAL) { /* Do firmware update */ - spin_unlock_irq(&hw->lock); - rv = csio_hw_flash_fw(hw); - spin_lock_irq(&hw->lock); + spin_unlock_irq(&hw->lock); + rv = csio_hw_flash_fw(hw, &reset); + spin_lock_irq(&hw->lock); - if (rv == 0) { - reset = 0; - /* - * Note that the chip was reset as part of the - * firmware upgrade so we don't reset it again - * below and grab the new firmware version. - */ - rv = csio_hw_check_fw_version(hw); - } - } - /* - * If the firmware doesn't support Configuration - * Files, use the old Driver-based, hard-wired - * initialization. Otherwise, try using the - * Configuration File support and fall back to the - * Driver-based initialization if there's no - * Configuration File found. + if (rv != 0) + goto out; + + /* If the firmware doesn't support Configuration Files, + * return an error. */ - if (csio_hw_check_fwconfig(hw, param) == 0) { - rv = csio_hw_use_fwconfig(hw, reset, param); - if (rv == -ENOENT) - goto out; - if (rv != 0) { - csio_info(hw, - "No Configuration File present " - "on adapter. Using hard-wired " - "configuration parameters.\n"); - rv = csio_hw_no_fwconfig(hw, reset); - } - } else { - rv = csio_hw_no_fwconfig(hw, reset); + rv = csio_hw_check_fwconfig(hw, param); + if (rv != 0) { + csio_info(hw, "Firmware doesn't support " + "Firmware Configuration files\n"); + goto out; } - if (rv != 0) + /* The firmware provides us with a memory buffer where we can + * load a Configuration File from the host if we want to + * override the Configuration File in flash. + */ + rv = csio_hw_use_fwconfig(hw, reset, param); + if (rv == -ENOENT) { + csio_info(hw, "Could not initialize " + "adapter, error%d\n", rv); + goto out; + } + if (rv != 0) { + csio_info(hw, "Could not initialize " + "adapter, error%d\n", rv); goto out; + } } else { if (hw->fw_state == CSIO_DEV_STATE_INIT) { @@ -2217,7 +2226,7 @@ out: return; } -#define PF_INTR_MASK (PFSW | PFCIM) +#define PF_INTR_MASK (PFSW_F | PFCIM_F) /* * csio_hw_intr_enable - Enable HW interrupts @@ -2229,21 +2238,21 @@ static void csio_hw_intr_enable(struct csio_hw *hw) { uint16_t vec = (uint16_t)csio_get_mb_intr_idx(csio_hw_to_mbm(hw)); - uint32_t pf = SOURCEPF_GET(csio_rd_reg32(hw, PL_WHOAMI)); - uint32_t pl = csio_rd_reg32(hw, PL_INT_ENABLE); + uint32_t pf = SOURCEPF_G(csio_rd_reg32(hw, PL_WHOAMI_A)); + uint32_t pl = csio_rd_reg32(hw, PL_INT_ENABLE_A); /* * Set aivec for MSI/MSIX. PCIE_PF_CFG.INTXType is set up * by FW, so do nothing for INTX. */ if (hw->intr_mode == CSIO_IM_MSIX) - csio_set_reg_field(hw, MYPF_REG(PCIE_PF_CFG), - AIVEC(AIVEC_MASK), vec); + csio_set_reg_field(hw, MYPF_REG(PCIE_PF_CFG_A), + AIVEC_V(AIVEC_M), vec); else if (hw->intr_mode == CSIO_IM_MSI) - csio_set_reg_field(hw, MYPF_REG(PCIE_PF_CFG), - AIVEC(AIVEC_MASK), 0); + csio_set_reg_field(hw, MYPF_REG(PCIE_PF_CFG_A), + AIVEC_V(AIVEC_M), 0); - csio_wr_reg32(hw, PF_INTR_MASK, MYPF_REG(PL_PF_INT_ENABLE)); + csio_wr_reg32(hw, PF_INTR_MASK, MYPF_REG(PL_PF_INT_ENABLE_A)); /* Turn on MB interrupts - this will internally flush PIO as well */ csio_mb_intr_enable(hw); @@ -2253,19 +2262,19 @@ csio_hw_intr_enable(struct csio_hw *hw) /* * Disable the Serial FLASH interrupt, if enabled! */ - pl &= (~SF); - csio_wr_reg32(hw, pl, PL_INT_ENABLE); + pl &= (~SF_F); + csio_wr_reg32(hw, pl, PL_INT_ENABLE_A); - csio_wr_reg32(hw, ERR_CPL_EXCEED_IQE_SIZE | - EGRESS_SIZE_ERR | ERR_INVALID_CIDX_INC | - ERR_CPL_OPCODE_0 | ERR_DROPPED_DB | - ERR_DATA_CPL_ON_HIGH_QID1 | - ERR_DATA_CPL_ON_HIGH_QID0 | ERR_BAD_DB_PIDX3 | - ERR_BAD_DB_PIDX2 | ERR_BAD_DB_PIDX1 | - ERR_BAD_DB_PIDX0 | ERR_ING_CTXT_PRIO | - ERR_EGR_CTXT_PRIO | INGRESS_SIZE_ERR, - SGE_INT_ENABLE3); - csio_set_reg_field(hw, PL_INT_MAP0, 0, 1 << pf); + csio_wr_reg32(hw, ERR_CPL_EXCEED_IQE_SIZE_F | + EGRESS_SIZE_ERR_F | ERR_INVALID_CIDX_INC_F | + ERR_CPL_OPCODE_0_F | ERR_DROPPED_DB_F | + ERR_DATA_CPL_ON_HIGH_QID1_F | + ERR_DATA_CPL_ON_HIGH_QID0_F | ERR_BAD_DB_PIDX3_F | + ERR_BAD_DB_PIDX2_F | ERR_BAD_DB_PIDX1_F | + ERR_BAD_DB_PIDX0_F | ERR_ING_CTXT_PRIO_F | + ERR_EGR_CTXT_PRIO_F | INGRESS_SIZE_ERR_F, + SGE_INT_ENABLE3_A); + csio_set_reg_field(hw, PL_INT_MAP0_A, 0, 1 << pf); } hw->flags |= CSIO_HWF_HW_INTR_ENABLED; @@ -2281,16 +2290,16 @@ csio_hw_intr_enable(struct csio_hw *hw) void csio_hw_intr_disable(struct csio_hw *hw) { - uint32_t pf = SOURCEPF_GET(csio_rd_reg32(hw, PL_WHOAMI)); + uint32_t pf = SOURCEPF_G(csio_rd_reg32(hw, PL_WHOAMI_A)); if (!(hw->flags & CSIO_HWF_HW_INTR_ENABLED)) return; hw->flags &= ~CSIO_HWF_HW_INTR_ENABLED; - csio_wr_reg32(hw, 0, MYPF_REG(PL_PF_INT_ENABLE)); + csio_wr_reg32(hw, 0, MYPF_REG(PL_PF_INT_ENABLE_A)); if (csio_is_hw_master(hw)) - csio_set_reg_field(hw, PL_INT_MAP0, 1 << pf, 0); + csio_set_reg_field(hw, PL_INT_MAP0_A, 1 << pf, 0); /* Turn off MB interrupts */ csio_mb_intr_disable(hw); @@ -2300,7 +2309,7 @@ csio_hw_intr_disable(struct csio_hw *hw) void csio_hw_fatal_err(struct csio_hw *hw) { - csio_set_reg_field(hw, SGE_CONTROL, GLOBALENABLE, 0); + csio_set_reg_field(hw, SGE_CONTROL_A, GLOBALENABLE_F, 0); csio_hw_intr_disable(hw); /* Do not reset HW, we may need FW state for debugging */ @@ -2594,7 +2603,7 @@ csio_hws_removing(struct csio_hw *hw, enum csio_hw_ev evt) * register directly. */ csio_err(hw, "Resetting HW and waiting 2 seconds...\n"); - csio_wr_reg32(hw, PIORSTMODE | PIORST, PL_RST); + csio_wr_reg32(hw, PIORSTMODE_F | PIORST_F, PL_RST_A); mdelay(2000); break; @@ -2682,11 +2691,11 @@ static void csio_tp_intr_handler(struct csio_hw *hw) { static struct intr_info tp_intr_info[] = { { 0x3fffffff, "TP parity error", -1, 1 }, - { FLMTXFLSTEMPTY, "TP out of Tx pages", -1, 1 }, + { FLMTXFLSTEMPTY_F, "TP out of Tx pages", -1, 1 }, { 0, NULL, 0, 0 } }; - if (csio_handle_intr_status(hw, TP_INT_CAUSE, tp_intr_info)) + if (csio_handle_intr_status(hw, TP_INT_CAUSE_A, tp_intr_info)) csio_hw_fatal_err(hw); } @@ -2698,52 +2707,52 @@ static void csio_sge_intr_handler(struct csio_hw *hw) uint64_t v; static struct intr_info sge_intr_info[] = { - { ERR_CPL_EXCEED_IQE_SIZE, + { ERR_CPL_EXCEED_IQE_SIZE_F, "SGE received CPL exceeding IQE size", -1, 1 }, - { ERR_INVALID_CIDX_INC, + { ERR_INVALID_CIDX_INC_F, "SGE GTS CIDX increment too large", -1, 0 }, - { ERR_CPL_OPCODE_0, "SGE received 0-length CPL", -1, 0 }, - { ERR_DROPPED_DB, "SGE doorbell dropped", -1, 0 }, - { ERR_DATA_CPL_ON_HIGH_QID1 | ERR_DATA_CPL_ON_HIGH_QID0, + { ERR_CPL_OPCODE_0_F, "SGE received 0-length CPL", -1, 0 }, + { ERR_DROPPED_DB_F, "SGE doorbell dropped", -1, 0 }, + { ERR_DATA_CPL_ON_HIGH_QID1_F | ERR_DATA_CPL_ON_HIGH_QID0_F, "SGE IQID > 1023 received CPL for FL", -1, 0 }, - { ERR_BAD_DB_PIDX3, "SGE DBP 3 pidx increment too large", -1, + { ERR_BAD_DB_PIDX3_F, "SGE DBP 3 pidx increment too large", -1, 0 }, - { ERR_BAD_DB_PIDX2, "SGE DBP 2 pidx increment too large", -1, + { ERR_BAD_DB_PIDX2_F, "SGE DBP 2 pidx increment too large", -1, 0 }, - { ERR_BAD_DB_PIDX1, "SGE DBP 1 pidx increment too large", -1, + { ERR_BAD_DB_PIDX1_F, "SGE DBP 1 pidx increment too large", -1, 0 }, - { ERR_BAD_DB_PIDX0, "SGE DBP 0 pidx increment too large", -1, + { ERR_BAD_DB_PIDX0_F, "SGE DBP 0 pidx increment too large", -1, 0 }, - { ERR_ING_CTXT_PRIO, + { ERR_ING_CTXT_PRIO_F, "SGE too many priority ingress contexts", -1, 0 }, - { ERR_EGR_CTXT_PRIO, + { ERR_EGR_CTXT_PRIO_F, "SGE too many priority egress contexts", -1, 0 }, - { INGRESS_SIZE_ERR, "SGE illegal ingress QID", -1, 0 }, - { EGRESS_SIZE_ERR, "SGE illegal egress QID", -1, 0 }, + { INGRESS_SIZE_ERR_F, "SGE illegal ingress QID", -1, 0 }, + { EGRESS_SIZE_ERR_F, "SGE illegal egress QID", -1, 0 }, { 0, NULL, 0, 0 } }; - v = (uint64_t)csio_rd_reg32(hw, SGE_INT_CAUSE1) | - ((uint64_t)csio_rd_reg32(hw, SGE_INT_CAUSE2) << 32); + v = (uint64_t)csio_rd_reg32(hw, SGE_INT_CAUSE1_A) | + ((uint64_t)csio_rd_reg32(hw, SGE_INT_CAUSE2_A) << 32); if (v) { csio_fatal(hw, "SGE parity error (%#llx)\n", (unsigned long long)v); csio_wr_reg32(hw, (uint32_t)(v & 0xFFFFFFFF), - SGE_INT_CAUSE1); - csio_wr_reg32(hw, (uint32_t)(v >> 32), SGE_INT_CAUSE2); + SGE_INT_CAUSE1_A); + csio_wr_reg32(hw, (uint32_t)(v >> 32), SGE_INT_CAUSE2_A); } - v |= csio_handle_intr_status(hw, SGE_INT_CAUSE3, sge_intr_info); + v |= csio_handle_intr_status(hw, SGE_INT_CAUSE3_A, sge_intr_info); - if (csio_handle_intr_status(hw, SGE_INT_CAUSE3, sge_intr_info) || + if (csio_handle_intr_status(hw, SGE_INT_CAUSE3_A, sge_intr_info) || v != 0) csio_hw_fatal_err(hw); } -#define CIM_OBQ_INTR (OBQULP0PARERR | OBQULP1PARERR | OBQULP2PARERR |\ - OBQULP3PARERR | OBQSGEPARERR | OBQNCSIPARERR) -#define CIM_IBQ_INTR (IBQTP0PARERR | IBQTP1PARERR | IBQULPPARERR |\ - IBQSGEHIPARERR | IBQSGELOPARERR | IBQNCSIPARERR) +#define CIM_OBQ_INTR (OBQULP0PARERR_F | OBQULP1PARERR_F | OBQULP2PARERR_F |\ + OBQULP3PARERR_F | OBQSGEPARERR_F | OBQNCSIPARERR_F) +#define CIM_IBQ_INTR (IBQTP0PARERR_F | IBQTP1PARERR_F | IBQULPPARERR_F |\ + IBQSGEHIPARERR_F | IBQSGELOPARERR_F | IBQNCSIPARERR_F) /* * CIM interrupt handler. @@ -2751,53 +2760,53 @@ static void csio_sge_intr_handler(struct csio_hw *hw) static void csio_cim_intr_handler(struct csio_hw *hw) { static struct intr_info cim_intr_info[] = { - { PREFDROPINT, "CIM control register prefetch drop", -1, 1 }, + { PREFDROPINT_F, "CIM control register prefetch drop", -1, 1 }, { CIM_OBQ_INTR, "CIM OBQ parity error", -1, 1 }, { CIM_IBQ_INTR, "CIM IBQ parity error", -1, 1 }, - { MBUPPARERR, "CIM mailbox uP parity error", -1, 1 }, - { MBHOSTPARERR, "CIM mailbox host parity error", -1, 1 }, - { TIEQINPARERRINT, "CIM TIEQ outgoing parity error", -1, 1 }, - { TIEQOUTPARERRINT, "CIM TIEQ incoming parity error", -1, 1 }, + { MBUPPARERR_F, "CIM mailbox uP parity error", -1, 1 }, + { MBHOSTPARERR_F, "CIM mailbox host parity error", -1, 1 }, + { TIEQINPARERRINT_F, "CIM TIEQ outgoing parity error", -1, 1 }, + { TIEQOUTPARERRINT_F, "CIM TIEQ incoming parity error", -1, 1 }, { 0, NULL, 0, 0 } }; static struct intr_info cim_upintr_info[] = { - { RSVDSPACEINT, "CIM reserved space access", -1, 1 }, - { ILLTRANSINT, "CIM illegal transaction", -1, 1 }, - { ILLWRINT, "CIM illegal write", -1, 1 }, - { ILLRDINT, "CIM illegal read", -1, 1 }, - { ILLRDBEINT, "CIM illegal read BE", -1, 1 }, - { ILLWRBEINT, "CIM illegal write BE", -1, 1 }, - { SGLRDBOOTINT, "CIM single read from boot space", -1, 1 }, - { SGLWRBOOTINT, "CIM single write to boot space", -1, 1 }, - { BLKWRBOOTINT, "CIM block write to boot space", -1, 1 }, - { SGLRDFLASHINT, "CIM single read from flash space", -1, 1 }, - { SGLWRFLASHINT, "CIM single write to flash space", -1, 1 }, - { BLKWRFLASHINT, "CIM block write to flash space", -1, 1 }, - { SGLRDEEPROMINT, "CIM single EEPROM read", -1, 1 }, - { SGLWREEPROMINT, "CIM single EEPROM write", -1, 1 }, - { BLKRDEEPROMINT, "CIM block EEPROM read", -1, 1 }, - { BLKWREEPROMINT, "CIM block EEPROM write", -1, 1 }, - { SGLRDCTLINT , "CIM single read from CTL space", -1, 1 }, - { SGLWRCTLINT , "CIM single write to CTL space", -1, 1 }, - { BLKRDCTLINT , "CIM block read from CTL space", -1, 1 }, - { BLKWRCTLINT , "CIM block write to CTL space", -1, 1 }, - { SGLRDPLINT , "CIM single read from PL space", -1, 1 }, - { SGLWRPLINT , "CIM single write to PL space", -1, 1 }, - { BLKRDPLINT , "CIM block read from PL space", -1, 1 }, - { BLKWRPLINT , "CIM block write to PL space", -1, 1 }, - { REQOVRLOOKUPINT , "CIM request FIFO overwrite", -1, 1 }, - { RSPOVRLOOKUPINT , "CIM response FIFO overwrite", -1, 1 }, - { TIMEOUTINT , "CIM PIF timeout", -1, 1 }, - { TIMEOUTMAINT , "CIM PIF MA timeout", -1, 1 }, + { RSVDSPACEINT_F, "CIM reserved space access", -1, 1 }, + { ILLTRANSINT_F, "CIM illegal transaction", -1, 1 }, + { ILLWRINT_F, "CIM illegal write", -1, 1 }, + { ILLRDINT_F, "CIM illegal read", -1, 1 }, + { ILLRDBEINT_F, "CIM illegal read BE", -1, 1 }, + { ILLWRBEINT_F, "CIM illegal write BE", -1, 1 }, + { SGLRDBOOTINT_F, "CIM single read from boot space", -1, 1 }, + { SGLWRBOOTINT_F, "CIM single write to boot space", -1, 1 }, + { BLKWRBOOTINT_F, "CIM block write to boot space", -1, 1 }, + { SGLRDFLASHINT_F, "CIM single read from flash space", -1, 1 }, + { SGLWRFLASHINT_F, "CIM single write to flash space", -1, 1 }, + { BLKWRFLASHINT_F, "CIM block write to flash space", -1, 1 }, + { SGLRDEEPROMINT_F, "CIM single EEPROM read", -1, 1 }, + { SGLWREEPROMINT_F, "CIM single EEPROM write", -1, 1 }, + { BLKRDEEPROMINT_F, "CIM block EEPROM read", -1, 1 }, + { BLKWREEPROMINT_F, "CIM block EEPROM write", -1, 1 }, + { SGLRDCTLINT_F, "CIM single read from CTL space", -1, 1 }, + { SGLWRCTLINT_F, "CIM single write to CTL space", -1, 1 }, + { BLKRDCTLINT_F, "CIM block read from CTL space", -1, 1 }, + { BLKWRCTLINT_F, "CIM block write to CTL space", -1, 1 }, + { SGLRDPLINT_F, "CIM single read from PL space", -1, 1 }, + { SGLWRPLINT_F, "CIM single write to PL space", -1, 1 }, + { BLKRDPLINT_F, "CIM block read from PL space", -1, 1 }, + { BLKWRPLINT_F, "CIM block write to PL space", -1, 1 }, + { REQOVRLOOKUPINT_F, "CIM request FIFO overwrite", -1, 1 }, + { RSPOVRLOOKUPINT_F, "CIM response FIFO overwrite", -1, 1 }, + { TIMEOUTINT_F, "CIM PIF timeout", -1, 1 }, + { TIMEOUTMAINT_F, "CIM PIF MA timeout", -1, 1 }, { 0, NULL, 0, 0 } }; int fat; - fat = csio_handle_intr_status(hw, CIM_HOST_INT_CAUSE, - cim_intr_info) + - csio_handle_intr_status(hw, CIM_HOST_UPACC_INT_CAUSE, - cim_upintr_info); + fat = csio_handle_intr_status(hw, CIM_HOST_INT_CAUSE_A, + cim_intr_info) + + csio_handle_intr_status(hw, CIM_HOST_UPACC_INT_CAUSE_A, + cim_upintr_info); if (fat) csio_hw_fatal_err(hw); } @@ -2813,7 +2822,7 @@ static void csio_ulprx_intr_handler(struct csio_hw *hw) { 0, NULL, 0, 0 } }; - if (csio_handle_intr_status(hw, ULP_RX_INT_CAUSE, ulprx_intr_info)) + if (csio_handle_intr_status(hw, ULP_RX_INT_CAUSE_A, ulprx_intr_info)) csio_hw_fatal_err(hw); } @@ -2823,19 +2832,19 @@ static void csio_ulprx_intr_handler(struct csio_hw *hw) static void csio_ulptx_intr_handler(struct csio_hw *hw) { static struct intr_info ulptx_intr_info[] = { - { PBL_BOUND_ERR_CH3, "ULPTX channel 3 PBL out of bounds", -1, + { PBL_BOUND_ERR_CH3_F, "ULPTX channel 3 PBL out of bounds", -1, 0 }, - { PBL_BOUND_ERR_CH2, "ULPTX channel 2 PBL out of bounds", -1, + { PBL_BOUND_ERR_CH2_F, "ULPTX channel 2 PBL out of bounds", -1, 0 }, - { PBL_BOUND_ERR_CH1, "ULPTX channel 1 PBL out of bounds", -1, + { PBL_BOUND_ERR_CH1_F, "ULPTX channel 1 PBL out of bounds", -1, 0 }, - { PBL_BOUND_ERR_CH0, "ULPTX channel 0 PBL out of bounds", -1, + { PBL_BOUND_ERR_CH0_F, "ULPTX channel 0 PBL out of bounds", -1, 0 }, { 0xfffffff, "ULPTX parity error", -1, 1 }, { 0, NULL, 0, 0 } }; - if (csio_handle_intr_status(hw, ULP_TX_INT_CAUSE, ulptx_intr_info)) + if (csio_handle_intr_status(hw, ULP_TX_INT_CAUSE_A, ulptx_intr_info)) csio_hw_fatal_err(hw); } @@ -2845,20 +2854,20 @@ static void csio_ulptx_intr_handler(struct csio_hw *hw) static void csio_pmtx_intr_handler(struct csio_hw *hw) { static struct intr_info pmtx_intr_info[] = { - { PCMD_LEN_OVFL0, "PMTX channel 0 pcmd too large", -1, 1 }, - { PCMD_LEN_OVFL1, "PMTX channel 1 pcmd too large", -1, 1 }, - { PCMD_LEN_OVFL2, "PMTX channel 2 pcmd too large", -1, 1 }, - { ZERO_C_CMD_ERROR, "PMTX 0-length pcmd", -1, 1 }, + { PCMD_LEN_OVFL0_F, "PMTX channel 0 pcmd too large", -1, 1 }, + { PCMD_LEN_OVFL1_F, "PMTX channel 1 pcmd too large", -1, 1 }, + { PCMD_LEN_OVFL2_F, "PMTX channel 2 pcmd too large", -1, 1 }, + { ZERO_C_CMD_ERROR_F, "PMTX 0-length pcmd", -1, 1 }, { 0xffffff0, "PMTX framing error", -1, 1 }, - { OESPI_PAR_ERROR, "PMTX oespi parity error", -1, 1 }, - { DB_OPTIONS_PAR_ERROR, "PMTX db_options parity error", -1, + { OESPI_PAR_ERROR_F, "PMTX oespi parity error", -1, 1 }, + { DB_OPTIONS_PAR_ERROR_F, "PMTX db_options parity error", -1, 1 }, - { ICSPI_PAR_ERROR, "PMTX icspi parity error", -1, 1 }, - { C_PCMD_PAR_ERROR, "PMTX c_pcmd parity error", -1, 1}, + { ICSPI_PAR_ERROR_F, "PMTX icspi parity error", -1, 1 }, + { PMTX_C_PCMD_PAR_ERROR_F, "PMTX c_pcmd parity error", -1, 1}, { 0, NULL, 0, 0 } }; - if (csio_handle_intr_status(hw, PM_TX_INT_CAUSE, pmtx_intr_info)) + if (csio_handle_intr_status(hw, PM_TX_INT_CAUSE_A, pmtx_intr_info)) csio_hw_fatal_err(hw); } @@ -2868,17 +2877,17 @@ static void csio_pmtx_intr_handler(struct csio_hw *hw) static void csio_pmrx_intr_handler(struct csio_hw *hw) { static struct intr_info pmrx_intr_info[] = { - { ZERO_E_CMD_ERROR, "PMRX 0-length pcmd", -1, 1 }, + { ZERO_E_CMD_ERROR_F, "PMRX 0-length pcmd", -1, 1 }, { 0x3ffff0, "PMRX framing error", -1, 1 }, - { OCSPI_PAR_ERROR, "PMRX ocspi parity error", -1, 1 }, - { DB_OPTIONS_PAR_ERROR, "PMRX db_options parity error", -1, + { OCSPI_PAR_ERROR_F, "PMRX ocspi parity error", -1, 1 }, + { DB_OPTIONS_PAR_ERROR_F, "PMRX db_options parity error", -1, 1 }, - { IESPI_PAR_ERROR, "PMRX iespi parity error", -1, 1 }, - { E_PCMD_PAR_ERROR, "PMRX e_pcmd parity error", -1, 1}, + { IESPI_PAR_ERROR_F, "PMRX iespi parity error", -1, 1 }, + { PMRX_E_PCMD_PAR_ERROR_F, "PMRX e_pcmd parity error", -1, 1}, { 0, NULL, 0, 0 } }; - if (csio_handle_intr_status(hw, PM_RX_INT_CAUSE, pmrx_intr_info)) + if (csio_handle_intr_status(hw, PM_RX_INT_CAUSE_A, pmrx_intr_info)) csio_hw_fatal_err(hw); } @@ -2888,16 +2897,16 @@ static void csio_pmrx_intr_handler(struct csio_hw *hw) static void csio_cplsw_intr_handler(struct csio_hw *hw) { static struct intr_info cplsw_intr_info[] = { - { CIM_OP_MAP_PERR, "CPLSW CIM op_map parity error", -1, 1 }, - { CIM_OVFL_ERROR, "CPLSW CIM overflow", -1, 1 }, - { TP_FRAMING_ERROR, "CPLSW TP framing error", -1, 1 }, - { SGE_FRAMING_ERROR, "CPLSW SGE framing error", -1, 1 }, - { CIM_FRAMING_ERROR, "CPLSW CIM framing error", -1, 1 }, - { ZERO_SWITCH_ERROR, "CPLSW no-switch error", -1, 1 }, + { CIM_OP_MAP_PERR_F, "CPLSW CIM op_map parity error", -1, 1 }, + { CIM_OVFL_ERROR_F, "CPLSW CIM overflow", -1, 1 }, + { TP_FRAMING_ERROR_F, "CPLSW TP framing error", -1, 1 }, + { SGE_FRAMING_ERROR_F, "CPLSW SGE framing error", -1, 1 }, + { CIM_FRAMING_ERROR_F, "CPLSW CIM framing error", -1, 1 }, + { ZERO_SWITCH_ERROR_F, "CPLSW no-switch error", -1, 1 }, { 0, NULL, 0, 0 } }; - if (csio_handle_intr_status(hw, CPL_INTR_CAUSE, cplsw_intr_info)) + if (csio_handle_intr_status(hw, CPL_INTR_CAUSE_A, cplsw_intr_info)) csio_hw_fatal_err(hw); } @@ -2907,15 +2916,15 @@ static void csio_cplsw_intr_handler(struct csio_hw *hw) static void csio_le_intr_handler(struct csio_hw *hw) { static struct intr_info le_intr_info[] = { - { LIPMISS, "LE LIP miss", -1, 0 }, - { LIP0, "LE 0 LIP error", -1, 0 }, - { PARITYERR, "LE parity error", -1, 1 }, - { UNKNOWNCMD, "LE unknown command", -1, 1 }, - { REQQPARERR, "LE request queue parity error", -1, 1 }, + { LIPMISS_F, "LE LIP miss", -1, 0 }, + { LIP0_F, "LE 0 LIP error", -1, 0 }, + { PARITYERR_F, "LE parity error", -1, 1 }, + { UNKNOWNCMD_F, "LE unknown command", -1, 1 }, + { REQQPARERR_F, "LE request queue parity error", -1, 1 }, { 0, NULL, 0, 0 } }; - if (csio_handle_intr_status(hw, LE_DB_INT_CAUSE, le_intr_info)) + if (csio_handle_intr_status(hw, LE_DB_INT_CAUSE_A, le_intr_info)) csio_hw_fatal_err(hw); } @@ -2929,19 +2938,22 @@ static void csio_mps_intr_handler(struct csio_hw *hw) { 0, NULL, 0, 0 } }; static struct intr_info mps_tx_intr_info[] = { - { TPFIFO, "MPS Tx TP FIFO parity error", -1, 1 }, - { NCSIFIFO, "MPS Tx NC-SI FIFO parity error", -1, 1 }, - { TXDATAFIFO, "MPS Tx data FIFO parity error", -1, 1 }, - { TXDESCFIFO, "MPS Tx desc FIFO parity error", -1, 1 }, - { BUBBLE, "MPS Tx underflow", -1, 1 }, - { SECNTERR, "MPS Tx SOP/EOP error", -1, 1 }, - { FRMERR, "MPS Tx framing error", -1, 1 }, + { TPFIFO_V(TPFIFO_M), "MPS Tx TP FIFO parity error", -1, 1 }, + { NCSIFIFO_F, "MPS Tx NC-SI FIFO parity error", -1, 1 }, + { TXDATAFIFO_V(TXDATAFIFO_M), "MPS Tx data FIFO parity error", + -1, 1 }, + { TXDESCFIFO_V(TXDESCFIFO_M), "MPS Tx desc FIFO parity error", + -1, 1 }, + { BUBBLE_F, "MPS Tx underflow", -1, 1 }, + { SECNTERR_F, "MPS Tx SOP/EOP error", -1, 1 }, + { FRMERR_F, "MPS Tx framing error", -1, 1 }, { 0, NULL, 0, 0 } }; static struct intr_info mps_trc_intr_info[] = { - { FILTMEM, "MPS TRC filter parity error", -1, 1 }, - { PKTFIFO, "MPS TRC packet FIFO parity error", -1, 1 }, - { MISCPERR, "MPS TRC misc parity error", -1, 1 }, + { FILTMEM_V(FILTMEM_M), "MPS TRC filter parity error", -1, 1 }, + { PKTFIFO_V(PKTFIFO_M), "MPS TRC packet FIFO parity error", + -1, 1 }, + { MISCPERR_F, "MPS TRC misc parity error", -1, 1 }, { 0, NULL, 0, 0 } }; static struct intr_info mps_stat_sram_intr_info[] = { @@ -2957,36 +2969,37 @@ static void csio_mps_intr_handler(struct csio_hw *hw) { 0, NULL, 0, 0 } }; static struct intr_info mps_cls_intr_info[] = { - { MATCHSRAM, "MPS match SRAM parity error", -1, 1 }, - { MATCHTCAM, "MPS match TCAM parity error", -1, 1 }, - { HASHSRAM, "MPS hash SRAM parity error", -1, 1 }, + { MATCHSRAM_F, "MPS match SRAM parity error", -1, 1 }, + { MATCHTCAM_F, "MPS match TCAM parity error", -1, 1 }, + { HASHSRAM_F, "MPS hash SRAM parity error", -1, 1 }, { 0, NULL, 0, 0 } }; int fat; - fat = csio_handle_intr_status(hw, MPS_RX_PERR_INT_CAUSE, - mps_rx_intr_info) + - csio_handle_intr_status(hw, MPS_TX_INT_CAUSE, - mps_tx_intr_info) + - csio_handle_intr_status(hw, MPS_TRC_INT_CAUSE, - mps_trc_intr_info) + - csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_SRAM, - mps_stat_sram_intr_info) + - csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_TX_FIFO, - mps_stat_tx_intr_info) + - csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_RX_FIFO, - mps_stat_rx_intr_info) + - csio_handle_intr_status(hw, MPS_CLS_INT_CAUSE, - mps_cls_intr_info); - - csio_wr_reg32(hw, 0, MPS_INT_CAUSE); - csio_rd_reg32(hw, MPS_INT_CAUSE); /* flush */ + fat = csio_handle_intr_status(hw, MPS_RX_PERR_INT_CAUSE_A, + mps_rx_intr_info) + + csio_handle_intr_status(hw, MPS_TX_INT_CAUSE_A, + mps_tx_intr_info) + + csio_handle_intr_status(hw, MPS_TRC_INT_CAUSE_A, + mps_trc_intr_info) + + csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_SRAM_A, + mps_stat_sram_intr_info) + + csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_TX_FIFO_A, + mps_stat_tx_intr_info) + + csio_handle_intr_status(hw, MPS_STAT_PERR_INT_CAUSE_RX_FIFO_A, + mps_stat_rx_intr_info) + + csio_handle_intr_status(hw, MPS_CLS_INT_CAUSE_A, + mps_cls_intr_info); + + csio_wr_reg32(hw, 0, MPS_INT_CAUSE_A); + csio_rd_reg32(hw, MPS_INT_CAUSE_A); /* flush */ if (fat) csio_hw_fatal_err(hw); } -#define MEM_INT_MASK (PERR_INT_CAUSE | ECC_CE_INT_CAUSE | ECC_UE_INT_CAUSE) +#define MEM_INT_MASK (PERR_INT_CAUSE_F | ECC_CE_INT_CAUSE_F | \ + ECC_UE_INT_CAUSE_F) /* * EDC/MC interrupt handler. @@ -2998,28 +3011,28 @@ static void csio_mem_intr_handler(struct csio_hw *hw, int idx) unsigned int addr, cnt_addr, v; if (idx <= MEM_EDC1) { - addr = EDC_REG(EDC_INT_CAUSE, idx); - cnt_addr = EDC_REG(EDC_ECC_STATUS, idx); + addr = EDC_REG(EDC_INT_CAUSE_A, idx); + cnt_addr = EDC_REG(EDC_ECC_STATUS_A, idx); } else { - addr = MC_INT_CAUSE; - cnt_addr = MC_ECC_STATUS; + addr = MC_INT_CAUSE_A; + cnt_addr = MC_ECC_STATUS_A; } v = csio_rd_reg32(hw, addr) & MEM_INT_MASK; - if (v & PERR_INT_CAUSE) + if (v & PERR_INT_CAUSE_F) csio_fatal(hw, "%s FIFO parity error\n", name[idx]); - if (v & ECC_CE_INT_CAUSE) { - uint32_t cnt = ECC_CECNT_GET(csio_rd_reg32(hw, cnt_addr)); + if (v & ECC_CE_INT_CAUSE_F) { + uint32_t cnt = ECC_CECNT_G(csio_rd_reg32(hw, cnt_addr)); - csio_wr_reg32(hw, ECC_CECNT_MASK, cnt_addr); + csio_wr_reg32(hw, ECC_CECNT_V(ECC_CECNT_M), cnt_addr); csio_warn(hw, "%u %s correctable ECC data error%s\n", cnt, name[idx], cnt > 1 ? "s" : ""); } - if (v & ECC_UE_INT_CAUSE) + if (v & ECC_UE_INT_CAUSE_F) csio_fatal(hw, "%s uncorrectable ECC data error\n", name[idx]); csio_wr_reg32(hw, v, addr); - if (v & (PERR_INT_CAUSE | ECC_UE_INT_CAUSE)) + if (v & (PERR_INT_CAUSE_F | ECC_UE_INT_CAUSE_F)) csio_hw_fatal_err(hw); } @@ -3028,18 +3041,18 @@ static void csio_mem_intr_handler(struct csio_hw *hw, int idx) */ static void csio_ma_intr_handler(struct csio_hw *hw) { - uint32_t v, status = csio_rd_reg32(hw, MA_INT_CAUSE); + uint32_t v, status = csio_rd_reg32(hw, MA_INT_CAUSE_A); - if (status & MEM_PERR_INT_CAUSE) + if (status & MEM_PERR_INT_CAUSE_F) csio_fatal(hw, "MA parity error, parity status %#x\n", - csio_rd_reg32(hw, MA_PARITY_ERROR_STATUS)); - if (status & MEM_WRAP_INT_CAUSE) { - v = csio_rd_reg32(hw, MA_INT_WRAP_STATUS); + csio_rd_reg32(hw, MA_PARITY_ERROR_STATUS_A)); + if (status & MEM_WRAP_INT_CAUSE_F) { + v = csio_rd_reg32(hw, MA_INT_WRAP_STATUS_A); csio_fatal(hw, "MA address wrap-around error by client %u to address %#x\n", - MEM_WRAP_CLIENT_NUM_GET(v), MEM_WRAP_ADDRESS_GET(v) << 4); + MEM_WRAP_CLIENT_NUM_G(v), MEM_WRAP_ADDRESS_G(v) << 4); } - csio_wr_reg32(hw, status, MA_INT_CAUSE); + csio_wr_reg32(hw, status, MA_INT_CAUSE_A); csio_hw_fatal_err(hw); } @@ -3049,13 +3062,13 @@ static void csio_ma_intr_handler(struct csio_hw *hw) static void csio_smb_intr_handler(struct csio_hw *hw) { static struct intr_info smb_intr_info[] = { - { MSTTXFIFOPARINT, "SMB master Tx FIFO parity error", -1, 1 }, - { MSTRXFIFOPARINT, "SMB master Rx FIFO parity error", -1, 1 }, - { SLVFIFOPARINT, "SMB slave FIFO parity error", -1, 1 }, + { MSTTXFIFOPARINT_F, "SMB master Tx FIFO parity error", -1, 1 }, + { MSTRXFIFOPARINT_F, "SMB master Rx FIFO parity error", -1, 1 }, + { SLVFIFOPARINT_F, "SMB slave FIFO parity error", -1, 1 }, { 0, NULL, 0, 0 } }; - if (csio_handle_intr_status(hw, SMB_INT_CAUSE, smb_intr_info)) + if (csio_handle_intr_status(hw, SMB_INT_CAUSE_A, smb_intr_info)) csio_hw_fatal_err(hw); } @@ -3065,14 +3078,14 @@ static void csio_smb_intr_handler(struct csio_hw *hw) static void csio_ncsi_intr_handler(struct csio_hw *hw) { static struct intr_info ncsi_intr_info[] = { - { CIM_DM_PRTY_ERR, "NC-SI CIM parity error", -1, 1 }, - { MPS_DM_PRTY_ERR, "NC-SI MPS parity error", -1, 1 }, - { TXFIFO_PRTY_ERR, "NC-SI Tx FIFO parity error", -1, 1 }, - { RXFIFO_PRTY_ERR, "NC-SI Rx FIFO parity error", -1, 1 }, + { CIM_DM_PRTY_ERR_F, "NC-SI CIM parity error", -1, 1 }, + { MPS_DM_PRTY_ERR_F, "NC-SI MPS parity error", -1, 1 }, + { TXFIFO_PRTY_ERR_F, "NC-SI Tx FIFO parity error", -1, 1 }, + { RXFIFO_PRTY_ERR_F, "NC-SI Rx FIFO parity error", -1, 1 }, { 0, NULL, 0, 0 } }; - if (csio_handle_intr_status(hw, NCSI_INT_CAUSE, ncsi_intr_info)) + if (csio_handle_intr_status(hw, NCSI_INT_CAUSE_A, ncsi_intr_info)) csio_hw_fatal_err(hw); } @@ -3081,17 +3094,17 @@ static void csio_ncsi_intr_handler(struct csio_hw *hw) */ static void csio_xgmac_intr_handler(struct csio_hw *hw, int port) { - uint32_t v = csio_rd_reg32(hw, CSIO_MAC_INT_CAUSE_REG(hw, port)); + uint32_t v = csio_rd_reg32(hw, T5_PORT_REG(port, MAC_PORT_INT_CAUSE_A)); - v &= TXFIFO_PRTY_ERR | RXFIFO_PRTY_ERR; + v &= TXFIFO_PRTY_ERR_F | RXFIFO_PRTY_ERR_F; if (!v) return; - if (v & TXFIFO_PRTY_ERR) + if (v & TXFIFO_PRTY_ERR_F) csio_fatal(hw, "XGMAC %d Tx FIFO parity error\n", port); - if (v & RXFIFO_PRTY_ERR) + if (v & RXFIFO_PRTY_ERR_F) csio_fatal(hw, "XGMAC %d Rx FIFO parity error\n", port); - csio_wr_reg32(hw, v, CSIO_MAC_INT_CAUSE_REG(hw, port)); + csio_wr_reg32(hw, v, T5_PORT_REG(port, MAC_PORT_INT_CAUSE_A)); csio_hw_fatal_err(hw); } @@ -3101,12 +3114,12 @@ static void csio_xgmac_intr_handler(struct csio_hw *hw, int port) static void csio_pl_intr_handler(struct csio_hw *hw) { static struct intr_info pl_intr_info[] = { - { FATALPERR, "T4 fatal parity error", -1, 1 }, - { PERRVFID, "PL VFID_MAP parity error", -1, 1 }, + { FATALPERR_F, "T4 fatal parity error", -1, 1 }, + { PERRVFID_F, "PL VFID_MAP parity error", -1, 1 }, { 0, NULL, 0, 0 } }; - if (csio_handle_intr_status(hw, PL_PL_INT_CAUSE, pl_intr_info)) + if (csio_handle_intr_status(hw, PL_PL_INT_CAUSE_A, pl_intr_info)) csio_hw_fatal_err(hw); } @@ -3121,7 +3134,7 @@ static void csio_pl_intr_handler(struct csio_hw *hw) int csio_hw_slow_intr_handler(struct csio_hw *hw) { - uint32_t cause = csio_rd_reg32(hw, PL_INT_CAUSE); + uint32_t cause = csio_rd_reg32(hw, PL_INT_CAUSE_A); if (!(cause & CSIO_GLBL_INTR_MASK)) { CSIO_INC_STATS(hw, n_plint_unexp); @@ -3132,75 +3145,75 @@ csio_hw_slow_intr_handler(struct csio_hw *hw) CSIO_INC_STATS(hw, n_plint_cnt); - if (cause & CIM) + if (cause & CIM_F) csio_cim_intr_handler(hw); - if (cause & MPS) + if (cause & MPS_F) csio_mps_intr_handler(hw); - if (cause & NCSI) + if (cause & NCSI_F) csio_ncsi_intr_handler(hw); - if (cause & PL) + if (cause & PL_F) csio_pl_intr_handler(hw); - if (cause & SMB) + if (cause & SMB_F) csio_smb_intr_handler(hw); - if (cause & XGMAC0) + if (cause & XGMAC0_F) csio_xgmac_intr_handler(hw, 0); - if (cause & XGMAC1) + if (cause & XGMAC1_F) csio_xgmac_intr_handler(hw, 1); - if (cause & XGMAC_KR0) + if (cause & XGMAC_KR0_F) csio_xgmac_intr_handler(hw, 2); - if (cause & XGMAC_KR1) + if (cause & XGMAC_KR1_F) csio_xgmac_intr_handler(hw, 3); - if (cause & PCIE) + if (cause & PCIE_F) hw->chip_ops->chip_pcie_intr_handler(hw); - if (cause & MC) + if (cause & MC_F) csio_mem_intr_handler(hw, MEM_MC); - if (cause & EDC0) + if (cause & EDC0_F) csio_mem_intr_handler(hw, MEM_EDC0); - if (cause & EDC1) + if (cause & EDC1_F) csio_mem_intr_handler(hw, MEM_EDC1); - if (cause & LE) + if (cause & LE_F) csio_le_intr_handler(hw); - if (cause & TP) + if (cause & TP_F) csio_tp_intr_handler(hw); - if (cause & MA) + if (cause & MA_F) csio_ma_intr_handler(hw); - if (cause & PM_TX) + if (cause & PM_TX_F) csio_pmtx_intr_handler(hw); - if (cause & PM_RX) + if (cause & PM_RX_F) csio_pmrx_intr_handler(hw); - if (cause & ULP_RX) + if (cause & ULP_RX_F) csio_ulprx_intr_handler(hw); - if (cause & CPL_SWITCH) + if (cause & CPL_SWITCH_F) csio_cplsw_intr_handler(hw); - if (cause & SGE) + if (cause & SGE_F) csio_sge_intr_handler(hw); - if (cause & ULP_TX) + if (cause & ULP_TX_F) csio_ulptx_intr_handler(hw); /* Clear the interrupts just processed for which we are the master. */ - csio_wr_reg32(hw, cause & CSIO_GLBL_INTR_MASK, PL_INT_CAUSE); - csio_rd_reg32(hw, PL_INT_CAUSE); /* flush */ + csio_wr_reg32(hw, cause & CSIO_GLBL_INTR_MASK, PL_INT_CAUSE_A); + csio_rd_reg32(hw, PL_INT_CAUSE_A); /* flush */ return 1; } @@ -3840,13 +3853,7 @@ csio_hw_set_description(struct csio_hw *hw, uint16_t ven_id, uint16_t dev_id) prot_type = (dev_id & CSIO_ASIC_DEVID_PROTO_MASK); adap_type = (dev_id & CSIO_ASIC_DEVID_TYPE_MASK); - if (prot_type == CSIO_T4_FCOE_ASIC) { - memcpy(hw->hw_ver, - csio_t4_fcoe_adapters[adap_type].model_no, 16); - memcpy(hw->model_desc, - csio_t4_fcoe_adapters[adap_type].description, - 32); - } else if (prot_type == CSIO_T5_FCOE_ASIC) { + if (prot_type == CSIO_T5_FCOE_ASIC) { memcpy(hw->hw_ver, csio_t5_fcoe_adapters[adap_type].model_no, 16); memcpy(hw->model_desc, @@ -3883,8 +3890,8 @@ csio_hw_init(struct csio_hw *hw) strcpy(hw->name, CSIO_HW_NAME); - /* Initialize the HW chip ops with T4/T5 specific ops */ - hw->chip_ops = csio_is_t4(hw->chip_id) ? &t4_ops : &t5_ops; + /* Initialize the HW chip ops T5 specific ops */ + hw->chip_ops = &t5_ops; /* Set the model & its description */ diff --git a/drivers/scsi/csiostor/csio_hw.h b/drivers/scsi/csiostor/csio_hw.h index 68248da1b9af..029bef82c057 100644 --- a/drivers/scsi/csiostor/csio_hw.h +++ b/drivers/scsi/csiostor/csio_hw.h @@ -48,6 +48,7 @@ #include <scsi/scsi_device.h> #include <scsi/scsi_transport_fc.h> +#include "t4_hw.h" #include "csio_hw_chip.h" #include "csio_wr.h" #include "csio_mb.h" @@ -117,10 +118,10 @@ extern int csio_msi; #define CSIO_ASIC_DEVID_PROTO_MASK 0xFF00 #define CSIO_ASIC_DEVID_TYPE_MASK 0x00FF -#define CSIO_GLBL_INTR_MASK (CIM | MPS | PL | PCIE | MC | EDC0 | \ - EDC1 | LE | TP | MA | PM_TX | PM_RX | \ - ULP_RX | CPL_SWITCH | SGE | \ - ULP_TX | SF) +#define CSIO_GLBL_INTR_MASK (CIM_F | MPS_F | PL_F | PCIE_F | MC_F | \ + EDC0_F | EDC1_F | LE_F | TP_F | MA_F | \ + PM_TX_F | PM_RX_F | ULP_RX_F | \ + CPL_SWITCH_F | SGE_F | ULP_TX_F | SF_F) /* * Hard parameters used to initialize the card in the absence of a @@ -174,16 +175,12 @@ struct csio_evt_msg { }; enum { - EEPROMVSIZE = 32768, /* Serial EEPROM virtual address space size */ SERNUM_LEN = 16, /* Serial # length */ EC_LEN = 16, /* E/C length */ ID_LEN = 16, /* ID length */ - TRACE_LEN = 112, /* length of trace data and mask */ }; enum { - SF_PAGE_SIZE = 256, /* serial flash page size */ - SF_SEC_SIZE = 64 * 1024, /* serial flash sector size */ SF_SIZE = SF_SEC_SIZE * 16, /* serial flash size */ }; @@ -199,39 +196,8 @@ enum { SF_RD_DATA_FAST = 0xb, /* read flash */ SF_RD_ID = 0x9f, /* read ID */ SF_ERASE_SECTOR = 0xd8, /* erase sector */ - - FW_START_SEC = 8, /* first flash sector for FW */ - FW_END_SEC = 15, /* last flash sector for FW */ - FW_IMG_START = FW_START_SEC * SF_SEC_SIZE, - FW_MAX_SIZE = (FW_END_SEC - FW_START_SEC + 1) * SF_SEC_SIZE, - - FLASH_CFG_MAX_SIZE = 0x10000 , /* max size of the flash config file*/ - FLASH_CFG_OFFSET = 0x1f0000, - FLASH_CFG_START_SEC = FLASH_CFG_OFFSET / SF_SEC_SIZE, }; -/* - * Flash layout. - */ -#define FLASH_START(start) ((start) * SF_SEC_SIZE) -#define FLASH_MAX_SIZE(nsecs) ((nsecs) * SF_SEC_SIZE) - -enum { - /* - * Location of firmware image in FLASH. - */ - FLASH_FW_START_SEC = 8, - FLASH_FW_NSECS = 8, - FLASH_FW_START = FLASH_START(FLASH_FW_START_SEC), - FLASH_FW_MAX_SIZE = FLASH_MAX_SIZE(FLASH_FW_NSECS), - - /* Location of Firmware Configuration File in FLASH. */ - FLASH_CFG_START = FLASH_START(FLASH_CFG_START_SEC), -}; - -#undef FLASH_START -#undef FLASH_MAX_SIZE - /* Management module */ enum { CSIO_MGMT_EQ_WRSIZE = 512, @@ -482,11 +448,6 @@ struct csio_hw { uint32_t tp_vers; char chip_ver; uint16_t chip_id; /* Tells T4/T5 chip */ - uint32_t cfg_finiver; - uint32_t cfg_finicsum; - uint32_t cfg_cfcsum; - uint8_t cfg_csum_status; - uint8_t cfg_store; enum csio_dev_state fw_state; struct csio_vpd vpd; diff --git a/drivers/scsi/csiostor/csio_hw_chip.h b/drivers/scsi/csiostor/csio_hw_chip.h index 4752fed476df..b56a11d817be 100644 --- a/drivers/scsi/csiostor/csio_hw_chip.h +++ b/drivers/scsi/csiostor/csio_hw_chip.h @@ -37,24 +37,27 @@ #include "csio_defs.h" /* Define MACRO values */ -#define CSIO_HW_T4 0x4000 -#define CSIO_T4_FCOE_ASIC 0x4600 #define CSIO_HW_T5 0x5000 #define CSIO_T5_FCOE_ASIC 0x5600 #define CSIO_HW_CHIP_MASK 0xF000 -#define T4_REGMAP_SIZE (160 * 1024) #define T5_REGMAP_SIZE (332 * 1024) -#define FW_FNAME_T4 "cxgb4/t4fw.bin" #define FW_FNAME_T5 "cxgb4/t5fw.bin" -#define FW_CFG_NAME_T4 "cxgb4/t4-config.txt" #define FW_CFG_NAME_T5 "cxgb4/t5-config.txt" -/* Define static functions */ -static inline int csio_is_t4(uint16_t chip) -{ - return (chip == CSIO_HW_T4); -} +#define CHELSIO_CHIP_CODE(version, revision) (((version) << 4) | (revision)) +#define CHELSIO_CHIP_FPGA 0x100 +#define CHELSIO_CHIP_VERSION(code) (((code) >> 12) & 0xf) +#define CHELSIO_CHIP_RELEASE(code) ((code) & 0xf) + +#define CHELSIO_T5 0x5 + +enum chip_type { + T5_A0 = CHELSIO_CHIP_CODE(CHELSIO_T5, 0), + T5_A1 = CHELSIO_CHIP_CODE(CHELSIO_T5, 1), + T5_FIRST_REV = T5_A0, + T5_LAST_REV = T5_A1, +}; static inline int csio_is_t5(uint16_t chip) { @@ -65,30 +68,22 @@ static inline int csio_is_t5(uint16_t chip) #define CSIO_DEVICE(devid, idx) \ { PCI_VENDOR_ID_CHELSIO, (devid), PCI_ANY_ID, PCI_ANY_ID, 0, 0, (idx) } -#define CSIO_HW_PIDX(hw, index) \ - (csio_is_t4(hw->chip_id) ? (PIDX(index)) : \ - (PIDX_T5(index) | DBTYPE(1U))) - -#define CSIO_HW_LP_INT_THRESH(hw, val) \ - (csio_is_t4(hw->chip_id) ? (LP_INT_THRESH(val)) : \ - (V_LP_INT_THRESH_T5(val))) - -#define CSIO_HW_M_LP_INT_THRESH(hw) \ - (csio_is_t4(hw->chip_id) ? (LP_INT_THRESH_MASK) : (M_LP_INT_THRESH_T5)) - -#define CSIO_MAC_INT_CAUSE_REG(hw, port) \ - (csio_is_t4(hw->chip_id) ? (PORT_REG(port, XGMAC_PORT_INT_CAUSE)) : \ - (T5_PORT_REG(port, MAC_PORT_INT_CAUSE))) - -#define FW_VERSION_MAJOR(hw) (csio_is_t4(hw->chip_id) ? 1 : 0) -#define FW_VERSION_MINOR(hw) (csio_is_t4(hw->chip_id) ? 2 : 0) -#define FW_VERSION_MICRO(hw) (csio_is_t4(hw->chip_id) ? 8 : 0) - -#define CSIO_FW_FNAME(hw) \ - (csio_is_t4(hw->chip_id) ? FW_FNAME_T4 : FW_FNAME_T5) - -#define CSIO_CF_FNAME(hw) \ - (csio_is_t4(hw->chip_id) ? FW_CFG_NAME_T4 : FW_CFG_NAME_T5) +#include "t4fw_api.h" +#include "t4fw_version.h" + +#define FW_VERSION(chip) ( \ + FW_HDR_FW_VER_MAJOR_G(chip##FW_VERSION_MAJOR) | \ + FW_HDR_FW_VER_MINOR_G(chip##FW_VERSION_MINOR) | \ + FW_HDR_FW_VER_MICRO_G(chip##FW_VERSION_MICRO) | \ + FW_HDR_FW_VER_BUILD_G(chip##FW_VERSION_BUILD)) +#define FW_INTFVER(chip, intf) (FW_HDR_INTFVER_##intf) + +struct fw_info { + u8 chip; + char *fs_name; + char *fw_mod_name; + struct fw_hdr fw_hdr; +}; /* Declare ENUMS */ enum { MEM_EDC0, MEM_EDC1, MEM_MC, MEM_MC0 = MEM_MC, MEM_MC1 }; @@ -96,7 +91,6 @@ enum { MEM_EDC0, MEM_EDC1, MEM_MC, MEM_MC0 = MEM_MC, MEM_MC1 }; enum { MEMWIN_APERTURE = 2048, MEMWIN_BASE = 0x1b800, - MEMWIN_CSIOSTOR = 6, /* PCI-e Memory Window access */ }; /* Slow path handlers */ @@ -122,7 +116,6 @@ struct csio_hw_chip_ops { void (*chip_dfs_create_ext_mem)(struct csio_hw *); }; -extern struct csio_hw_chip_ops t4_ops; extern struct csio_hw_chip_ops t5_ops; #endif /* #ifndef __CSIO_HW_CHIP_H__ */ diff --git a/drivers/scsi/csiostor/csio_hw_t4.c b/drivers/scsi/csiostor/csio_hw_t4.c deleted file mode 100644 index 95d831857640..000000000000 --- a/drivers/scsi/csiostor/csio_hw_t4.c +++ /dev/null @@ -1,404 +0,0 @@ -/* - * This file is part of the Chelsio FCoE driver for Linux. - * - * Copyright (c) 2008-2013 Chelsio Communications, Inc. All rights reserved. - * - * This software is available to you under a choice of one of two - * licenses. You may choose to be licensed under the terms of the GNU - * General Public License (GPL) Version 2, available from the file - * OpenIB.org BSD license below: - * - * Redistribution and use in source and binary forms, with or - * without modification, are permitted provided that the following - * conditions are met: - * - * - Redistributions of source code must retain the above - * copyright notice, this list of conditions and the following - * - Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "csio_hw.h" -#include "csio_init.h" - -/* - * Return the specified PCI-E Configuration Space register from our Physical - * Function. We try first via a Firmware LDST Command since we prefer to let - * the firmware own all of these registers, but if that fails we go for it - * directly ourselves. - */ -static uint32_t -csio_t4_read_pcie_cfg4(struct csio_hw *hw, int reg) -{ - u32 val = 0; - struct csio_mb *mbp; - int rv; - struct fw_ldst_cmd *ldst_cmd; - - mbp = mempool_alloc(hw->mb_mempool, GFP_ATOMIC); - if (!mbp) { - CSIO_INC_STATS(hw, n_err_nomem); - pci_read_config_dword(hw->pdev, reg, &val); - return val; - } - - csio_mb_ldst(hw, mbp, CSIO_MB_DEFAULT_TMO, reg); - rv = csio_mb_issue(hw, mbp); - - /* - * If the LDST Command suucceeded, exctract the returned register - * value. Otherwise read it directly ourself. - */ - if (rv == 0) { - ldst_cmd = (struct fw_ldst_cmd *)(mbp->mb); - val = ntohl(ldst_cmd->u.pcie.data[0]); - } else - pci_read_config_dword(hw->pdev, reg, &val); - - mempool_free(mbp, hw->mb_mempool); - - return val; -} - -static int -csio_t4_set_mem_win(struct csio_hw *hw, uint32_t win) -{ - u32 bar0; - u32 mem_win_base; - - /* - * Truncation intentional: we only read the bottom 32-bits of the - * 64-bit BAR0/BAR1 ... We use the hardware backdoor mechanism to - * read BAR0 instead of using pci_resource_start() because we could be - * operating from within a Virtual Machine which is trapping our - * accesses to our Configuration Space and we need to set up the PCI-E - * Memory Window decoders with the actual addresses which will be - * coming across the PCI-E link. - */ - bar0 = csio_t4_read_pcie_cfg4(hw, PCI_BASE_ADDRESS_0); - bar0 &= PCI_BASE_ADDRESS_MEM_MASK; - - mem_win_base = bar0 + MEMWIN_BASE; - - /* - * Set up memory window for accessing adapter memory ranges. (Read - * back MA register to ensure that changes propagate before we attempt - * to use the new values.) - */ - csio_wr_reg32(hw, mem_win_base | BIR(0) | - WINDOW(ilog2(MEMWIN_APERTURE) - 10), - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, win)); - csio_rd_reg32(hw, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, win)); - return 0; -} - -/* - * Interrupt handler for the PCIE module. - */ -static void -csio_t4_pcie_intr_handler(struct csio_hw *hw) -{ - static struct intr_info sysbus_intr_info[] = { - { RNPP, "RXNP array parity error", -1, 1 }, - { RPCP, "RXPC array parity error", -1, 1 }, - { RCIP, "RXCIF array parity error", -1, 1 }, - { RCCP, "Rx completions control array parity error", -1, 1 }, - { RFTP, "RXFT array parity error", -1, 1 }, - { 0, NULL, 0, 0 } - }; - static struct intr_info pcie_port_intr_info[] = { - { TPCP, "TXPC array parity error", -1, 1 }, - { TNPP, "TXNP array parity error", -1, 1 }, - { TFTP, "TXFT array parity error", -1, 1 }, - { TCAP, "TXCA array parity error", -1, 1 }, - { TCIP, "TXCIF array parity error", -1, 1 }, - { RCAP, "RXCA array parity error", -1, 1 }, - { OTDD, "outbound request TLP discarded", -1, 1 }, - { RDPE, "Rx data parity error", -1, 1 }, - { TDUE, "Tx uncorrectable data error", -1, 1 }, - { 0, NULL, 0, 0 } - }; - - static struct intr_info pcie_intr_info[] = { - { MSIADDRLPERR, "MSI AddrL parity error", -1, 1 }, - { MSIADDRHPERR, "MSI AddrH parity error", -1, 1 }, - { MSIDATAPERR, "MSI data parity error", -1, 1 }, - { MSIXADDRLPERR, "MSI-X AddrL parity error", -1, 1 }, - { MSIXADDRHPERR, "MSI-X AddrH parity error", -1, 1 }, - { MSIXDATAPERR, "MSI-X data parity error", -1, 1 }, - { MSIXDIPERR, "MSI-X DI parity error", -1, 1 }, - { PIOCPLPERR, "PCI PIO completion FIFO parity error", -1, 1 }, - { PIOREQPERR, "PCI PIO request FIFO parity error", -1, 1 }, - { TARTAGPERR, "PCI PCI target tag FIFO parity error", -1, 1 }, - { CCNTPERR, "PCI CMD channel count parity error", -1, 1 }, - { CREQPERR, "PCI CMD channel request parity error", -1, 1 }, - { CRSPPERR, "PCI CMD channel response parity error", -1, 1 }, - { DCNTPERR, "PCI DMA channel count parity error", -1, 1 }, - { DREQPERR, "PCI DMA channel request parity error", -1, 1 }, - { DRSPPERR, "PCI DMA channel response parity error", -1, 1 }, - { HCNTPERR, "PCI HMA channel count parity error", -1, 1 }, - { HREQPERR, "PCI HMA channel request parity error", -1, 1 }, - { HRSPPERR, "PCI HMA channel response parity error", -1, 1 }, - { CFGSNPPERR, "PCI config snoop FIFO parity error", -1, 1 }, - { FIDPERR, "PCI FID parity error", -1, 1 }, - { INTXCLRPERR, "PCI INTx clear parity error", -1, 1 }, - { MATAGPERR, "PCI MA tag parity error", -1, 1 }, - { PIOTAGPERR, "PCI PIO tag parity error", -1, 1 }, - { RXCPLPERR, "PCI Rx completion parity error", -1, 1 }, - { RXWRPERR, "PCI Rx write parity error", -1, 1 }, - { RPLPERR, "PCI replay buffer parity error", -1, 1 }, - { PCIESINT, "PCI core secondary fault", -1, 1 }, - { PCIEPINT, "PCI core primary fault", -1, 1 }, - { UNXSPLCPLERR, "PCI unexpected split completion error", -1, - 0 }, - { 0, NULL, 0, 0 } - }; - - int fat; - fat = csio_handle_intr_status(hw, - PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS, - sysbus_intr_info) + - csio_handle_intr_status(hw, - PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS, - pcie_port_intr_info) + - csio_handle_intr_status(hw, PCIE_INT_CAUSE, pcie_intr_info); - if (fat) - csio_hw_fatal_err(hw); -} - -/* - * csio_t4_flash_cfg_addr - return the address of the flash configuration file - * @hw: the HW module - * - * Return the address within the flash where the Firmware Configuration - * File is stored. - */ -static unsigned int -csio_t4_flash_cfg_addr(struct csio_hw *hw) -{ - return FLASH_CFG_OFFSET; -} - -/* - * csio_t4_mc_read - read from MC through backdoor accesses - * @hw: the hw module - * @idx: not used for T4 adapter - * @addr: address of first byte requested - * @data: 64 bytes of data containing the requested address - * @ecc: where to store the corresponding 64-bit ECC word - * - * Read 64 bytes of data from MC starting at a 64-byte-aligned address - * that covers the requested address @addr. If @parity is not %NULL it - * is assigned the 64-bit ECC word for the read data. - */ -static int -csio_t4_mc_read(struct csio_hw *hw, int idx, uint32_t addr, __be32 *data, - uint64_t *ecc) -{ - int i; - - if (csio_rd_reg32(hw, MC_BIST_CMD) & START_BIST) - return -EBUSY; - csio_wr_reg32(hw, addr & ~0x3fU, MC_BIST_CMD_ADDR); - csio_wr_reg32(hw, 64, MC_BIST_CMD_LEN); - csio_wr_reg32(hw, 0xc, MC_BIST_DATA_PATTERN); - csio_wr_reg32(hw, BIST_OPCODE(1) | START_BIST | BIST_CMD_GAP(1), - MC_BIST_CMD); - i = csio_hw_wait_op_done_val(hw, MC_BIST_CMD, START_BIST, - 0, 10, 1, NULL); - if (i) - return i; - -#define MC_DATA(i) MC_BIST_STATUS_REG(MC_BIST_STATUS_RDATA, i) - - for (i = 15; i >= 0; i--) - *data++ = htonl(csio_rd_reg32(hw, MC_DATA(i))); - if (ecc) - *ecc = csio_rd_reg64(hw, MC_DATA(16)); -#undef MC_DATA - return 0; -} - -/* - * csio_t4_edc_read - read from EDC through backdoor accesses - * @hw: the hw module - * @idx: which EDC to access - * @addr: address of first byte requested - * @data: 64 bytes of data containing the requested address - * @ecc: where to store the corresponding 64-bit ECC word - * - * Read 64 bytes of data from EDC starting at a 64-byte-aligned address - * that covers the requested address @addr. If @parity is not %NULL it - * is assigned the 64-bit ECC word for the read data. - */ -static int -csio_t4_edc_read(struct csio_hw *hw, int idx, uint32_t addr, __be32 *data, - uint64_t *ecc) -{ - int i; - - idx *= EDC_STRIDE; - if (csio_rd_reg32(hw, EDC_BIST_CMD + idx) & START_BIST) - return -EBUSY; - csio_wr_reg32(hw, addr & ~0x3fU, EDC_BIST_CMD_ADDR + idx); - csio_wr_reg32(hw, 64, EDC_BIST_CMD_LEN + idx); - csio_wr_reg32(hw, 0xc, EDC_BIST_DATA_PATTERN + idx); - csio_wr_reg32(hw, BIST_OPCODE(1) | BIST_CMD_GAP(1) | START_BIST, - EDC_BIST_CMD + idx); - i = csio_hw_wait_op_done_val(hw, EDC_BIST_CMD + idx, START_BIST, - 0, 10, 1, NULL); - if (i) - return i; - -#define EDC_DATA(i) (EDC_BIST_STATUS_REG(EDC_BIST_STATUS_RDATA, i) + idx) - - for (i = 15; i >= 0; i--) - *data++ = htonl(csio_rd_reg32(hw, EDC_DATA(i))); - if (ecc) - *ecc = csio_rd_reg64(hw, EDC_DATA(16)); -#undef EDC_DATA - return 0; -} - -/* - * csio_t4_memory_rw - read/write EDC 0, EDC 1 or MC via PCIE memory window - * @hw: the csio_hw - * @win: PCI-E memory Window to use - * @mtype: memory type: MEM_EDC0, MEM_EDC1, MEM_MC0 (or MEM_MC) or MEM_MC1 - * @addr: address within indicated memory type - * @len: amount of memory to transfer - * @buf: host memory buffer - * @dir: direction of transfer 1 => read, 0 => write - * - * Reads/writes an [almost] arbitrary memory region in the firmware: the - * firmware memory address, length and host buffer must be aligned on - * 32-bit boudaries. The memory is transferred as a raw byte sequence - * from/to the firmware's memory. If this memory contains data - * structures which contain multi-byte integers, it's the callers - * responsibility to perform appropriate byte order conversions. - */ -static int -csio_t4_memory_rw(struct csio_hw *hw, u32 win, int mtype, u32 addr, - u32 len, uint32_t *buf, int dir) -{ - u32 pos, start, offset, memoffset, bar0; - u32 edc_size, mc_size, mem_reg, mem_aperture, mem_base; - - /* - * Argument sanity checks ... - */ - if ((addr & 0x3) || (len & 0x3)) - return -EINVAL; - - /* Offset into the region of memory which is being accessed - * MEM_EDC0 = 0 - * MEM_EDC1 = 1 - * MEM_MC = 2 -- T4 - */ - edc_size = EDRAM0_SIZE_G(csio_rd_reg32(hw, MA_EDRAM0_BAR_A)); - if (mtype != MEM_MC1) - memoffset = (mtype * (edc_size * 1024 * 1024)); - else { - mc_size = EXT_MEM_SIZE_G(csio_rd_reg32(hw, - MA_EXT_MEMORY_BAR_A)); - memoffset = (MEM_MC0 * edc_size + mc_size) * 1024 * 1024; - } - - /* Determine the PCIE_MEM_ACCESS_OFFSET */ - addr = addr + memoffset; - - /* - * Each PCI-E Memory Window is programmed with a window size -- or - * "aperture" -- which controls the granularity of its mapping onto - * adapter memory. We need to grab that aperture in order to know - * how to use the specified window. The window is also programmed - * with the base address of the Memory Window in BAR0's address - * space. For T4 this is an absolute PCI-E Bus Address. For T5 - * the address is relative to BAR0. - */ - mem_reg = csio_rd_reg32(hw, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, win)); - mem_aperture = 1 << (WINDOW(mem_reg) + 10); - mem_base = GET_PCIEOFST(mem_reg) << 10; - - bar0 = csio_t4_read_pcie_cfg4(hw, PCI_BASE_ADDRESS_0); - bar0 &= PCI_BASE_ADDRESS_MEM_MASK; - mem_base -= bar0; - - start = addr & ~(mem_aperture-1); - offset = addr - start; - - csio_dbg(hw, "csio_t4_memory_rw: mem_reg: 0x%x, mem_aperture: 0x%x\n", - mem_reg, mem_aperture); - csio_dbg(hw, "csio_t4_memory_rw: mem_base: 0x%x, mem_offset: 0x%x\n", - mem_base, memoffset); - csio_dbg(hw, "csio_t4_memory_rw: bar0: 0x%x, start:0x%x, offset:0x%x\n", - bar0, start, offset); - csio_dbg(hw, "csio_t4_memory_rw: mtype: %d, addr: 0x%x, len: %d\n", - mtype, addr, len); - - for (pos = start; len > 0; pos += mem_aperture, offset = 0) { - /* - * Move PCI-E Memory Window to our current transfer - * position. Read it back to ensure that changes propagate - * before we attempt to use the new value. - */ - csio_wr_reg32(hw, pos, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, win)); - csio_rd_reg32(hw, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, win)); - - while (offset < mem_aperture && len > 0) { - if (dir) - *buf++ = csio_rd_reg32(hw, mem_base + offset); - else - csio_wr_reg32(hw, *buf++, mem_base + offset); - - offset += sizeof(__be32); - len -= sizeof(__be32); - } - } - return 0; -} - -/* - * csio_t4_dfs_create_ext_mem - setup debugfs for MC to read the values - * @hw: the csio_hw - * - * This function creates files in the debugfs with external memory region MC. - */ -static void -csio_t4_dfs_create_ext_mem(struct csio_hw *hw) -{ - u32 size; - int i = csio_rd_reg32(hw, MA_TARGET_MEM_ENABLE_A); - - if (i & EXT_MEM_ENABLE_F) { - size = csio_rd_reg32(hw, MA_EXT_MEMORY_BAR_A); - csio_add_debugfs_mem(hw, "mc", MEM_MC, - EXT_MEM_SIZE_G(size)); - } -} - -/* T4 adapter specific function */ -struct csio_hw_chip_ops t4_ops = { - .chip_set_mem_win = csio_t4_set_mem_win, - .chip_pcie_intr_handler = csio_t4_pcie_intr_handler, - .chip_flash_cfg_addr = csio_t4_flash_cfg_addr, - .chip_mc_read = csio_t4_mc_read, - .chip_edc_read = csio_t4_edc_read, - .chip_memory_rw = csio_t4_memory_rw, - .chip_dfs_create_ext_mem = csio_t4_dfs_create_ext_mem, -}; diff --git a/drivers/scsi/csiostor/csio_hw_t5.c b/drivers/scsi/csiostor/csio_hw_t5.c index 66e180a58718..3267f4f627c9 100644 --- a/drivers/scsi/csiostor/csio_hw_t5.c +++ b/drivers/scsi/csiostor/csio_hw_t5.c @@ -56,11 +56,11 @@ csio_t5_set_mem_win(struct csio_hw *hw, uint32_t win) * back MA register to ensure that changes propagate before we attempt * to use the new values.) */ - csio_wr_reg32(hw, mem_win_base | BIR(0) | - WINDOW(ilog2(MEMWIN_APERTURE) - 10), - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, win)); + csio_wr_reg32(hw, mem_win_base | BIR_V(0) | + WINDOW_V(ilog2(MEMWIN_APERTURE) - 10), + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, win)); csio_rd_reg32(hw, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, win)); + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, win)); return 0; } @@ -72,74 +72,74 @@ static void csio_t5_pcie_intr_handler(struct csio_hw *hw) { static struct intr_info sysbus_intr_info[] = { - { RNPP, "RXNP array parity error", -1, 1 }, - { RPCP, "RXPC array parity error", -1, 1 }, - { RCIP, "RXCIF array parity error", -1, 1 }, - { RCCP, "Rx completions control array parity error", -1, 1 }, - { RFTP, "RXFT array parity error", -1, 1 }, + { RNPP_F, "RXNP array parity error", -1, 1 }, + { RPCP_F, "RXPC array parity error", -1, 1 }, + { RCIP_F, "RXCIF array parity error", -1, 1 }, + { RCCP_F, "Rx completions control array parity error", -1, 1 }, + { RFTP_F, "RXFT array parity error", -1, 1 }, { 0, NULL, 0, 0 } }; static struct intr_info pcie_port_intr_info[] = { - { TPCP, "TXPC array parity error", -1, 1 }, - { TNPP, "TXNP array parity error", -1, 1 }, - { TFTP, "TXFT array parity error", -1, 1 }, - { TCAP, "TXCA array parity error", -1, 1 }, - { TCIP, "TXCIF array parity error", -1, 1 }, - { RCAP, "RXCA array parity error", -1, 1 }, - { OTDD, "outbound request TLP discarded", -1, 1 }, - { RDPE, "Rx data parity error", -1, 1 }, - { TDUE, "Tx uncorrectable data error", -1, 1 }, + { TPCP_F, "TXPC array parity error", -1, 1 }, + { TNPP_F, "TXNP array parity error", -1, 1 }, + { TFTP_F, "TXFT array parity error", -1, 1 }, + { TCAP_F, "TXCA array parity error", -1, 1 }, + { TCIP_F, "TXCIF array parity error", -1, 1 }, + { RCAP_F, "RXCA array parity error", -1, 1 }, + { OTDD_F, "outbound request TLP discarded", -1, 1 }, + { RDPE_F, "Rx data parity error", -1, 1 }, + { TDUE_F, "Tx uncorrectable data error", -1, 1 }, { 0, NULL, 0, 0 } }; static struct intr_info pcie_intr_info[] = { - { MSTGRPPERR, "Master Response Read Queue parity error", + { MSTGRPPERR_F, "Master Response Read Queue parity error", -1, 1 }, - { MSTTIMEOUTPERR, "Master Timeout FIFO parity error", -1, 1 }, - { MSIXSTIPERR, "MSI-X STI SRAM parity error", -1, 1 }, - { MSIXADDRLPERR, "MSI-X AddrL parity error", -1, 1 }, - { MSIXADDRHPERR, "MSI-X AddrH parity error", -1, 1 }, - { MSIXDATAPERR, "MSI-X data parity error", -1, 1 }, - { MSIXDIPERR, "MSI-X DI parity error", -1, 1 }, - { PIOCPLGRPPERR, "PCI PIO completion Group FIFO parity error", + { MSTTIMEOUTPERR_F, "Master Timeout FIFO parity error", -1, 1 }, + { MSIXSTIPERR_F, "MSI-X STI SRAM parity error", -1, 1 }, + { MSIXADDRLPERR_F, "MSI-X AddrL parity error", -1, 1 }, + { MSIXADDRHPERR_F, "MSI-X AddrH parity error", -1, 1 }, + { MSIXDATAPERR_F, "MSI-X data parity error", -1, 1 }, + { MSIXDIPERR_F, "MSI-X DI parity error", -1, 1 }, + { PIOCPLGRPPERR_F, "PCI PIO completion Group FIFO parity error", -1, 1 }, - { PIOREQGRPPERR, "PCI PIO request Group FIFO parity error", + { PIOREQGRPPERR_F, "PCI PIO request Group FIFO parity error", -1, 1 }, - { TARTAGPERR, "PCI PCI target tag FIFO parity error", -1, 1 }, - { MSTTAGQPERR, "PCI master tag queue parity error", -1, 1 }, - { CREQPERR, "PCI CMD channel request parity error", -1, 1 }, - { CRSPPERR, "PCI CMD channel response parity error", -1, 1 }, - { DREQWRPERR, "PCI DMA channel write request parity error", + { TARTAGPERR_F, "PCI PCI target tag FIFO parity error", -1, 1 }, + { MSTTAGQPERR_F, "PCI master tag queue parity error", -1, 1 }, + { CREQPERR_F, "PCI CMD channel request parity error", -1, 1 }, + { CRSPPERR_F, "PCI CMD channel response parity error", -1, 1 }, + { DREQWRPERR_F, "PCI DMA channel write request parity error", -1, 1 }, - { DREQPERR, "PCI DMA channel request parity error", -1, 1 }, - { DRSPPERR, "PCI DMA channel response parity error", -1, 1 }, - { HREQWRPERR, "PCI HMA channel count parity error", -1, 1 }, - { HREQPERR, "PCI HMA channel request parity error", -1, 1 }, - { HRSPPERR, "PCI HMA channel response parity error", -1, 1 }, - { CFGSNPPERR, "PCI config snoop FIFO parity error", -1, 1 }, - { FIDPERR, "PCI FID parity error", -1, 1 }, - { VFIDPERR, "PCI INTx clear parity error", -1, 1 }, - { MAGRPPERR, "PCI MA group FIFO parity error", -1, 1 }, - { PIOTAGPERR, "PCI PIO tag parity error", -1, 1 }, - { IPRXHDRGRPPERR, "PCI IP Rx header group parity error", + { DREQPERR_F, "PCI DMA channel request parity error", -1, 1 }, + { DRSPPERR_F, "PCI DMA channel response parity error", -1, 1 }, + { HREQWRPERR_F, "PCI HMA channel count parity error", -1, 1 }, + { HREQPERR_F, "PCI HMA channel request parity error", -1, 1 }, + { HRSPPERR_F, "PCI HMA channel response parity error", -1, 1 }, + { CFGSNPPERR_F, "PCI config snoop FIFO parity error", -1, 1 }, + { FIDPERR_F, "PCI FID parity error", -1, 1 }, + { VFIDPERR_F, "PCI INTx clear parity error", -1, 1 }, + { MAGRPPERR_F, "PCI MA group FIFO parity error", -1, 1 }, + { PIOTAGPERR_F, "PCI PIO tag parity error", -1, 1 }, + { IPRXHDRGRPPERR_F, "PCI IP Rx header group parity error", -1, 1 }, - { IPRXDATAGRPPERR, "PCI IP Rx data group parity error", + { IPRXDATAGRPPERR_F, "PCI IP Rx data group parity error", -1, 1 }, - { RPLPERR, "PCI IP replay buffer parity error", -1, 1 }, - { IPSOTPERR, "PCI IP SOT buffer parity error", -1, 1 }, - { TRGT1GRPPERR, "PCI TRGT1 group FIFOs parity error", -1, 1 }, - { READRSPERR, "Outbound read error", -1, 0 }, + { RPLPERR_F, "PCI IP replay buffer parity error", -1, 1 }, + { IPSOTPERR_F, "PCI IP SOT buffer parity error", -1, 1 }, + { TRGT1GRPPERR_F, "PCI TRGT1 group FIFOs parity error", -1, 1 }, + { READRSPERR_F, "Outbound read error", -1, 0 }, { 0, NULL, 0, 0 } }; int fat; fat = csio_handle_intr_status(hw, - PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS, + PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS_A, sysbus_intr_info) + csio_handle_intr_status(hw, - PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS, + PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS_A, pcie_port_intr_info) + - csio_handle_intr_status(hw, PCIE_INT_CAUSE, pcie_intr_info); + csio_handle_intr_status(hw, PCIE_INT_CAUSE_A, pcie_intr_info); if (fat) csio_hw_fatal_err(hw); } @@ -177,25 +177,25 @@ csio_t5_mc_read(struct csio_hw *hw, int idx, uint32_t addr, __be32 *data, uint32_t mc_bist_cmd_reg, mc_bist_cmd_addr_reg, mc_bist_cmd_len_reg; uint32_t mc_bist_status_rdata_reg, mc_bist_data_pattern_reg; - mc_bist_cmd_reg = MC_REG(MC_P_BIST_CMD, idx); - mc_bist_cmd_addr_reg = MC_REG(MC_P_BIST_CMD_ADDR, idx); - mc_bist_cmd_len_reg = MC_REG(MC_P_BIST_CMD_LEN, idx); - mc_bist_status_rdata_reg = MC_REG(MC_P_BIST_STATUS_RDATA, idx); - mc_bist_data_pattern_reg = MC_REG(MC_P_BIST_DATA_PATTERN, idx); + mc_bist_cmd_reg = MC_REG(MC_P_BIST_CMD_A, idx); + mc_bist_cmd_addr_reg = MC_REG(MC_P_BIST_CMD_ADDR_A, idx); + mc_bist_cmd_len_reg = MC_REG(MC_P_BIST_CMD_LEN_A, idx); + mc_bist_status_rdata_reg = MC_REG(MC_P_BIST_STATUS_RDATA_A, idx); + mc_bist_data_pattern_reg = MC_REG(MC_P_BIST_DATA_PATTERN_A, idx); - if (csio_rd_reg32(hw, mc_bist_cmd_reg) & START_BIST) + if (csio_rd_reg32(hw, mc_bist_cmd_reg) & START_BIST_F) return -EBUSY; csio_wr_reg32(hw, addr & ~0x3fU, mc_bist_cmd_addr_reg); csio_wr_reg32(hw, 64, mc_bist_cmd_len_reg); csio_wr_reg32(hw, 0xc, mc_bist_data_pattern_reg); - csio_wr_reg32(hw, BIST_OPCODE(1) | START_BIST | BIST_CMD_GAP(1), + csio_wr_reg32(hw, BIST_OPCODE_V(1) | START_BIST_F | BIST_CMD_GAP_V(1), mc_bist_cmd_reg); - i = csio_hw_wait_op_done_val(hw, mc_bist_cmd_reg, START_BIST, + i = csio_hw_wait_op_done_val(hw, mc_bist_cmd_reg, START_BIST_F, 0, 10, 1, NULL); if (i) return i; -#define MC_DATA(i) MC_BIST_STATUS_REG(MC_BIST_STATUS_RDATA, i) +#define MC_DATA(i) MC_BIST_STATUS_REG(MC_BIST_STATUS_RDATA_A, i) for (i = 15; i >= 0; i--) *data++ = htonl(csio_rd_reg32(hw, MC_DATA(i))); @@ -231,27 +231,27 @@ csio_t5_edc_read(struct csio_hw *hw, int idx, uint32_t addr, __be32 *data, #define EDC_STRIDE_T5 (EDC_T51_BASE_ADDR - EDC_T50_BASE_ADDR) #define EDC_REG_T5(reg, idx) (reg + EDC_STRIDE_T5 * idx) - edc_bist_cmd_reg = EDC_REG_T5(EDC_H_BIST_CMD, idx); - edc_bist_cmd_addr_reg = EDC_REG_T5(EDC_H_BIST_CMD_ADDR, idx); - edc_bist_cmd_len_reg = EDC_REG_T5(EDC_H_BIST_CMD_LEN, idx); - edc_bist_cmd_data_pattern = EDC_REG_T5(EDC_H_BIST_DATA_PATTERN, idx); - edc_bist_status_rdata_reg = EDC_REG_T5(EDC_H_BIST_STATUS_RDATA, idx); + edc_bist_cmd_reg = EDC_REG_T5(EDC_H_BIST_CMD_A, idx); + edc_bist_cmd_addr_reg = EDC_REG_T5(EDC_H_BIST_CMD_ADDR_A, idx); + edc_bist_cmd_len_reg = EDC_REG_T5(EDC_H_BIST_CMD_LEN_A, idx); + edc_bist_cmd_data_pattern = EDC_REG_T5(EDC_H_BIST_DATA_PATTERN_A, idx); + edc_bist_status_rdata_reg = EDC_REG_T5(EDC_H_BIST_STATUS_RDATA_A, idx); #undef EDC_REG_T5 #undef EDC_STRIDE_T5 - if (csio_rd_reg32(hw, edc_bist_cmd_reg) & START_BIST) + if (csio_rd_reg32(hw, edc_bist_cmd_reg) & START_BIST_F) return -EBUSY; csio_wr_reg32(hw, addr & ~0x3fU, edc_bist_cmd_addr_reg); csio_wr_reg32(hw, 64, edc_bist_cmd_len_reg); csio_wr_reg32(hw, 0xc, edc_bist_cmd_data_pattern); - csio_wr_reg32(hw, BIST_OPCODE(1) | START_BIST | BIST_CMD_GAP(1), + csio_wr_reg32(hw, BIST_OPCODE_V(1) | START_BIST_F | BIST_CMD_GAP_V(1), edc_bist_cmd_reg); - i = csio_hw_wait_op_done_val(hw, edc_bist_cmd_reg, START_BIST, + i = csio_hw_wait_op_done_val(hw, edc_bist_cmd_reg, START_BIST_F, 0, 10, 1, NULL); if (i) return i; -#define EDC_DATA(i) (EDC_BIST_STATUS_REG(EDC_BIST_STATUS_RDATA, i) + idx) +#define EDC_DATA(i) (EDC_BIST_STATUS_REG(EDC_BIST_STATUS_RDATA_A, i) + idx) for (i = 15; i >= 0; i--) *data++ = htonl(csio_rd_reg32(hw, EDC_DATA(i))); @@ -320,13 +320,13 @@ csio_t5_memory_rw(struct csio_hw *hw, u32 win, int mtype, u32 addr, * the address is relative to BAR0. */ mem_reg = csio_rd_reg32(hw, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, win)); - mem_aperture = 1 << (WINDOW(mem_reg) + 10); - mem_base = GET_PCIEOFST(mem_reg) << 10; + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN_A, win)); + mem_aperture = 1 << (WINDOW_V(mem_reg) + 10); + mem_base = PCIEOFST_G(mem_reg) << 10; start = addr & ~(mem_aperture-1); offset = addr - start; - win_pf = V_PFNUM(hw->pfn); + win_pf = PFNUM_V(hw->pfn); csio_dbg(hw, "csio_t5_memory_rw: mem_reg: 0x%x, mem_aperture: 0x%x\n", mem_reg, mem_aperture); @@ -344,9 +344,9 @@ csio_t5_memory_rw(struct csio_hw *hw, u32 win, int mtype, u32 addr, * before we attempt to use the new value. */ csio_wr_reg32(hw, pos | win_pf, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, win)); + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, win)); csio_rd_reg32(hw, - PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET, win)); + PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_OFFSET_A, win)); while (offset < mem_aperture && len > 0) { if (dir) diff --git a/drivers/scsi/csiostor/csio_init.c b/drivers/scsi/csiostor/csio_init.c index 34d20cc3e110..9b9794d42ffe 100644 --- a/drivers/scsi/csiostor/csio_init.c +++ b/drivers/scsi/csiostor/csio_init.c @@ -1176,9 +1176,8 @@ static struct pci_error_handlers csio_err_handler = { */ #define CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN \ static struct pci_device_id csio_pci_tbl[] = { -/* Define for iSCSI uses PF5, FCoE uses PF6 */ -#define CH_PCI_DEVICE_ID_FUNCTION 0x5 -#define CH_PCI_DEVICE_ID_FUNCTION2 0x6 +/* Define for FCoE uses PF6 */ +#define CH_PCI_DEVICE_ID_FUNCTION 0x6 #define CH_PCI_ID_TABLE_ENTRY(devid) \ { PCI_VDEVICE(CHELSIO, (devid)), 0 } @@ -1256,5 +1255,4 @@ MODULE_DESCRIPTION(CSIO_DRV_DESC); MODULE_LICENSE(CSIO_DRV_LICENSE); MODULE_DEVICE_TABLE(pci, csio_pci_tbl); MODULE_VERSION(CSIO_DRV_VERSION); -MODULE_FIRMWARE(FW_FNAME_T4); MODULE_FIRMWARE(FW_FNAME_T5); diff --git a/drivers/scsi/csiostor/csio_isr.c b/drivers/scsi/csiostor/csio_isr.c index a8c748a35f9c..2fb71c6c3b37 100644 --- a/drivers/scsi/csiostor/csio_isr.c +++ b/drivers/scsi/csiostor/csio_isr.c @@ -317,7 +317,7 @@ csio_fcoe_isr(int irq, void *dev_id) /* Disable the interrupt for this PCI function. */ if (hw->intr_mode == CSIO_IM_INTX) - csio_wr_reg32(hw, 0, MYPF_REG(PCIE_PF_CLI)); + csio_wr_reg32(hw, 0, MYPF_REG(PCIE_PF_CLI_A)); /* * The read in the following function will flush the diff --git a/drivers/scsi/csiostor/csio_lnode.c b/drivers/scsi/csiostor/csio_lnode.c index 87f9280d9b43..c00b2ff72b55 100644 --- a/drivers/scsi/csiostor/csio_lnode.c +++ b/drivers/scsi/csiostor/csio_lnode.c @@ -1758,7 +1758,7 @@ csio_ln_mgmt_submit_wr(struct csio_mgmtm *mgmtm, struct csio_ioreq *io_req, else { /* Program DSGL to dma payload */ dsgl.cmd_nsge = htonl(ULPTX_CMD_V(ULP_TX_SC_DSGL) | - ULPTX_MORE | ULPTX_NSGE(1)); + ULPTX_MORE_F | ULPTX_NSGE_V(1)); dsgl.len0 = cpu_to_be32(pld_len); dsgl.addr0 = cpu_to_be64(pld->paddr); csio_wr_copy_to_wrp(&dsgl, &wrp, ALIGN(wr_off, 8), diff --git a/drivers/scsi/csiostor/csio_mb.c b/drivers/scsi/csiostor/csio_mb.c index 08c265c0f353..9451787ca7f2 100644 --- a/drivers/scsi/csiostor/csio_mb.c +++ b/drivers/scsi/csiostor/csio_mb.c @@ -327,7 +327,8 @@ csio_mb_caps_config(struct csio_hw *hw, struct csio_mb *mbp, uint32_t tmo, } #define CSIO_ADVERT_MASK (FW_PORT_CAP_SPEED_100M | FW_PORT_CAP_SPEED_1G |\ - FW_PORT_CAP_SPEED_10G | FW_PORT_CAP_ANEG) + FW_PORT_CAP_SPEED_10G | FW_PORT_CAP_SPEED_40G |\ + FW_PORT_CAP_ANEG) /* * csio_mb_port- FW PORT command helper @@ -1104,8 +1105,8 @@ csio_mb_process_portparams_rsp(struct csio_hw *hw, void csio_mb_intr_enable(struct csio_hw *hw) { - csio_wr_reg32(hw, MBMSGRDYINTEN(1), MYPF_REG(CIM_PF_HOST_INT_ENABLE)); - csio_rd_reg32(hw, MYPF_REG(CIM_PF_HOST_INT_ENABLE)); + csio_wr_reg32(hw, MBMSGRDYINTEN_F, MYPF_REG(CIM_PF_HOST_INT_ENABLE_A)); + csio_rd_reg32(hw, MYPF_REG(CIM_PF_HOST_INT_ENABLE_A)); } /* @@ -1117,8 +1118,9 @@ csio_mb_intr_enable(struct csio_hw *hw) void csio_mb_intr_disable(struct csio_hw *hw) { - csio_wr_reg32(hw, MBMSGRDYINTEN(0), MYPF_REG(CIM_PF_HOST_INT_ENABLE)); - csio_rd_reg32(hw, MYPF_REG(CIM_PF_HOST_INT_ENABLE)); + csio_wr_reg32(hw, MBMSGRDYINTEN_V(0), + MYPF_REG(CIM_PF_HOST_INT_ENABLE_A)); + csio_rd_reg32(hw, MYPF_REG(CIM_PF_HOST_INT_ENABLE_A)); } static void @@ -1153,8 +1155,8 @@ csio_mb_debug_cmd_handler(struct csio_hw *hw) { int i; __be64 cmd[CSIO_MB_MAX_REGS]; - uint32_t ctl_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_CTRL); - uint32_t data_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_DATA); + uint32_t ctl_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_CTRL_A); + uint32_t data_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_DATA_A); int size = sizeof(struct fw_debug_cmd); /* Copy mailbox data */ @@ -1164,8 +1166,8 @@ csio_mb_debug_cmd_handler(struct csio_hw *hw) csio_mb_dump_fw_dbg(hw, cmd); /* Notify FW of mailbox by setting owner as UP */ - csio_wr_reg32(hw, MBMSGVALID | MBINTREQ | MBOWNER(CSIO_MBOWNER_FW), - ctl_reg); + csio_wr_reg32(hw, MBMSGVALID_F | MBINTREQ_F | + MBOWNER_V(CSIO_MBOWNER_FW), ctl_reg); csio_rd_reg32(hw, ctl_reg); wmb(); @@ -1187,8 +1189,8 @@ csio_mb_issue(struct csio_hw *hw, struct csio_mb *mbp) __be64 *cmd = mbp->mb; __be64 hdr; struct csio_mbm *mbm = &hw->mbm; - uint32_t ctl_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_CTRL); - uint32_t data_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_DATA); + uint32_t ctl_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_CTRL_A); + uint32_t data_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_DATA_A); int size = mbp->mb_size; int rv = -EINVAL; struct fw_cmd_hdr *fw_hdr; @@ -1224,12 +1226,12 @@ csio_mb_issue(struct csio_hw *hw, struct csio_mb *mbp) } /* Now get ownership of mailbox */ - owner = MBOWNER_GET(csio_rd_reg32(hw, ctl_reg)); + owner = MBOWNER_G(csio_rd_reg32(hw, ctl_reg)); if (!csio_mb_is_host_owner(owner)) { for (i = 0; (owner == CSIO_MBOWNER_NONE) && (i < 3); i++) - owner = MBOWNER_GET(csio_rd_reg32(hw, ctl_reg)); + owner = MBOWNER_G(csio_rd_reg32(hw, ctl_reg)); /* * Mailbox unavailable. In immediate mode, fail the command. * In other modes, enqueue the request. @@ -1271,10 +1273,10 @@ csio_mb_issue(struct csio_hw *hw, struct csio_mb *mbp) if (mbp->mb_cbfn != NULL) { mbm->mcurrent = mbp; mod_timer(&mbm->timer, jiffies + msecs_to_jiffies(mbp->tmo)); - csio_wr_reg32(hw, MBMSGVALID | MBINTREQ | - MBOWNER(CSIO_MBOWNER_FW), ctl_reg); + csio_wr_reg32(hw, MBMSGVALID_F | MBINTREQ_F | + MBOWNER_V(CSIO_MBOWNER_FW), ctl_reg); } else - csio_wr_reg32(hw, MBMSGVALID | MBOWNER(CSIO_MBOWNER_FW), + csio_wr_reg32(hw, MBMSGVALID_F | MBOWNER_V(CSIO_MBOWNER_FW), ctl_reg); /* Flush posted writes */ @@ -1294,9 +1296,9 @@ csio_mb_issue(struct csio_hw *hw, struct csio_mb *mbp) /* Check for response */ ctl = csio_rd_reg32(hw, ctl_reg); - if (csio_mb_is_host_owner(MBOWNER_GET(ctl))) { + if (csio_mb_is_host_owner(MBOWNER_G(ctl))) { - if (!(ctl & MBMSGVALID)) { + if (!(ctl & MBMSGVALID_F)) { csio_wr_reg32(hw, 0, ctl_reg); continue; } @@ -1457,16 +1459,16 @@ csio_mb_isr_handler(struct csio_hw *hw) __be64 *cmd; uint32_t ctl, cim_cause, pl_cause; int i; - uint32_t ctl_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_CTRL); - uint32_t data_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_DATA); + uint32_t ctl_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_CTRL_A); + uint32_t data_reg = PF_REG(hw->pfn, CIM_PF_MAILBOX_DATA_A); int size; __be64 hdr; struct fw_cmd_hdr *fw_hdr; - pl_cause = csio_rd_reg32(hw, MYPF_REG(PL_PF_INT_CAUSE)); - cim_cause = csio_rd_reg32(hw, MYPF_REG(CIM_PF_HOST_INT_CAUSE)); + pl_cause = csio_rd_reg32(hw, MYPF_REG(PL_PF_INT_CAUSE_A)); + cim_cause = csio_rd_reg32(hw, MYPF_REG(CIM_PF_HOST_INT_CAUSE_A)); - if (!(pl_cause & PFCIM) || !(cim_cause & MBMSGRDYINT)) { + if (!(pl_cause & PFCIM_F) || !(cim_cause & MBMSGRDYINT_F)) { CSIO_INC_STATS(hw, n_mbint_unexp); return -EINVAL; } @@ -1477,16 +1479,16 @@ csio_mb_isr_handler(struct csio_hw *hw) * the upper level cause register. In other words, CIM-cause * first followed by PL-Cause next. */ - csio_wr_reg32(hw, MBMSGRDYINT, MYPF_REG(CIM_PF_HOST_INT_CAUSE)); - csio_wr_reg32(hw, PFCIM, MYPF_REG(PL_PF_INT_CAUSE)); + csio_wr_reg32(hw, MBMSGRDYINT_F, MYPF_REG(CIM_PF_HOST_INT_CAUSE_A)); + csio_wr_reg32(hw, PFCIM_F, MYPF_REG(PL_PF_INT_CAUSE_A)); ctl = csio_rd_reg32(hw, ctl_reg); - if (csio_mb_is_host_owner(MBOWNER_GET(ctl))) { + if (csio_mb_is_host_owner(MBOWNER_G(ctl))) { CSIO_DUMP_MB(hw, hw->pfn, data_reg); - if (!(ctl & MBMSGVALID)) { + if (!(ctl & MBMSGVALID_F)) { csio_warn(hw, "Stray mailbox interrupt recvd," " mailbox data not valid\n"); diff --git a/drivers/scsi/csiostor/csio_scsi.c b/drivers/scsi/csiostor/csio_scsi.c index 3987284e0d2a..2c4562d82dc0 100644 --- a/drivers/scsi/csiostor/csio_scsi.c +++ b/drivers/scsi/csiostor/csio_scsi.c @@ -298,8 +298,8 @@ csio_scsi_init_ultptx_dsgl(struct csio_hw *hw, struct csio_ioreq *req, struct csio_dma_buf *dma_buf; struct scsi_cmnd *scmnd = csio_scsi_cmnd(req); - sgl->cmd_nsge = htonl(ULPTX_CMD_V(ULP_TX_SC_DSGL) | ULPTX_MORE | - ULPTX_NSGE(req->nsge)); + sgl->cmd_nsge = htonl(ULPTX_CMD_V(ULP_TX_SC_DSGL) | ULPTX_MORE_F | + ULPTX_NSGE_V(req->nsge)); /* Now add the data SGLs */ if (likely(!req->dcopy)) { scsi_for_each_sg(scmnd, sgel, req->nsge, i) { diff --git a/drivers/scsi/csiostor/csio_wr.c b/drivers/scsi/csiostor/csio_wr.c index 773da14cfa14..e8f18174f2e9 100644 --- a/drivers/scsi/csiostor/csio_wr.c +++ b/drivers/scsi/csiostor/csio_wr.c @@ -51,12 +51,12 @@ int csio_intr_coalesce_time = 10; /* value:SGE_TIMER_VALUE_1 */ static int csio_sge_timer_reg = 1; #define CSIO_SET_FLBUF_SIZE(_hw, _reg, _val) \ - csio_wr_reg32((_hw), (_val), SGE_FL_BUFFER_SIZE##_reg) + csio_wr_reg32((_hw), (_val), SGE_FL_BUFFER_SIZE##_reg##_A) static void csio_get_flbuf_size(struct csio_hw *hw, struct csio_sge *sge, uint32_t reg) { - sge->sge_fl_buf_size[reg] = csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE0 + + sge->sge_fl_buf_size[reg] = csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE0_A + reg * sizeof(uint32_t)); } @@ -71,7 +71,7 @@ csio_wr_fl_bufsz(struct csio_sge *sge, struct csio_dma_buf *buf) static inline uint32_t csio_wr_qstat_pgsz(struct csio_hw *hw) { - return (hw->wrm.sge.sge_control & EGRSTATUSPAGESIZE(1)) ? 128 : 64; + return (hw->wrm.sge.sge_control & EGRSTATUSPAGESIZE_F) ? 128 : 64; } /* Ring freelist doorbell */ @@ -84,9 +84,9 @@ csio_wr_ring_fldb(struct csio_hw *hw, struct csio_q *flq) * 8 freelist buffer pointers (since each pointer is 8 bytes). */ if (flq->inc_idx >= 8) { - csio_wr_reg32(hw, DBPRIO(1) | QID(flq->un.fl.flid) | - CSIO_HW_PIDX(hw, flq->inc_idx / 8), - MYPF_REG(SGE_PF_KDOORBELL)); + csio_wr_reg32(hw, DBPRIO_F | QID_V(flq->un.fl.flid) | + PIDX_T5_V(flq->inc_idx / 8) | DBTYPE_F, + MYPF_REG(SGE_PF_KDOORBELL_A)); flq->inc_idx &= 7; } } @@ -95,10 +95,10 @@ csio_wr_ring_fldb(struct csio_hw *hw, struct csio_q *flq) static void csio_wr_sge_intr_enable(struct csio_hw *hw, uint16_t iqid) { - csio_wr_reg32(hw, CIDXINC(0) | - INGRESSQID(iqid) | - TIMERREG(X_TIMERREG_RESTART_COUNTER), - MYPF_REG(SGE_PF_GTS)); + csio_wr_reg32(hw, CIDXINC_V(0) | + INGRESSQID_V(iqid) | + TIMERREG_V(X_TIMERREG_RESTART_COUNTER), + MYPF_REG(SGE_PF_GTS_A)); } /* @@ -982,9 +982,9 @@ csio_wr_issue(struct csio_hw *hw, int qidx, bool prio) wmb(); /* Ring SGE Doorbell writing q->pidx into it */ - csio_wr_reg32(hw, DBPRIO(prio) | QID(q->un.eq.physeqid) | - CSIO_HW_PIDX(hw, q->inc_idx), - MYPF_REG(SGE_PF_KDOORBELL)); + csio_wr_reg32(hw, DBPRIO_V(prio) | QID_V(q->un.eq.physeqid) | + PIDX_T5_V(q->inc_idx) | DBTYPE_F, + MYPF_REG(SGE_PF_KDOORBELL_A)); q->inc_idx = 0; return 0; @@ -1242,10 +1242,10 @@ csio_wr_process_iq(struct csio_hw *hw, struct csio_q *q, restart: /* Now inform SGE about our incremental index value */ - csio_wr_reg32(hw, CIDXINC(q->inc_idx) | - INGRESSQID(q->un.iq.physiqid) | - TIMERREG(csio_sge_timer_reg), - MYPF_REG(SGE_PF_GTS)); + csio_wr_reg32(hw, CIDXINC_V(q->inc_idx) | + INGRESSQID_V(q->un.iq.physiqid) | + TIMERREG_V(csio_sge_timer_reg), + MYPF_REG(SGE_PF_GTS_A)); q->stats.n_tot_rsps += q->inc_idx; q->inc_idx = 0; @@ -1310,22 +1310,23 @@ csio_wr_fixup_host_params(struct csio_hw *hw) uint32_t ingpad = 0; uint32_t stat_len = clsz > 64 ? 128 : 64; - csio_wr_reg32(hw, HOSTPAGESIZEPF0(s_hps) | HOSTPAGESIZEPF1(s_hps) | - HOSTPAGESIZEPF2(s_hps) | HOSTPAGESIZEPF3(s_hps) | - HOSTPAGESIZEPF4(s_hps) | HOSTPAGESIZEPF5(s_hps) | - HOSTPAGESIZEPF6(s_hps) | HOSTPAGESIZEPF7(s_hps), - SGE_HOST_PAGE_SIZE); + csio_wr_reg32(hw, HOSTPAGESIZEPF0_V(s_hps) | HOSTPAGESIZEPF1_V(s_hps) | + HOSTPAGESIZEPF2_V(s_hps) | HOSTPAGESIZEPF3_V(s_hps) | + HOSTPAGESIZEPF4_V(s_hps) | HOSTPAGESIZEPF5_V(s_hps) | + HOSTPAGESIZEPF6_V(s_hps) | HOSTPAGESIZEPF7_V(s_hps), + SGE_HOST_PAGE_SIZE_A); sge->csio_fl_align = clsz < 32 ? 32 : clsz; ingpad = ilog2(sge->csio_fl_align) - 5; - csio_set_reg_field(hw, SGE_CONTROL, INGPADBOUNDARY_MASK | - EGRSTATUSPAGESIZE(1), - INGPADBOUNDARY(ingpad) | - EGRSTATUSPAGESIZE(stat_len != 64)); + csio_set_reg_field(hw, SGE_CONTROL_A, + INGPADBOUNDARY_V(INGPADBOUNDARY_M) | + EGRSTATUSPAGESIZE_F, + INGPADBOUNDARY_V(ingpad) | + EGRSTATUSPAGESIZE_V(stat_len != 64)); /* FL BUFFER SIZE#0 is Page size i,e already aligned to cache line */ - csio_wr_reg32(hw, PAGE_SIZE, SGE_FL_BUFFER_SIZE0); + csio_wr_reg32(hw, PAGE_SIZE, SGE_FL_BUFFER_SIZE0_A); /* * If using hard params, the following will get set correctly @@ -1333,23 +1334,24 @@ csio_wr_fixup_host_params(struct csio_hw *hw) */ if (hw->flags & CSIO_HWF_USING_SOFT_PARAMS) { csio_wr_reg32(hw, - (csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE2) + + (csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE2_A) + sge->csio_fl_align - 1) & ~(sge->csio_fl_align - 1), - SGE_FL_BUFFER_SIZE2); + SGE_FL_BUFFER_SIZE2_A); csio_wr_reg32(hw, - (csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE3) + + (csio_rd_reg32(hw, SGE_FL_BUFFER_SIZE3_A) + sge->csio_fl_align - 1) & ~(sge->csio_fl_align - 1), - SGE_FL_BUFFER_SIZE3); + SGE_FL_BUFFER_SIZE3_A); } - csio_wr_reg32(hw, HPZ0(PAGE_SHIFT - 12), ULP_RX_TDDP_PSZ); + csio_wr_reg32(hw, HPZ0_V(PAGE_SHIFT - 12), ULP_RX_TDDP_PSZ_A); /* default value of rx_dma_offset of the NIC driver */ - csio_set_reg_field(hw, SGE_CONTROL, PKTSHIFT_MASK, - PKTSHIFT(CSIO_SGE_RX_DMA_OFFSET)); + csio_set_reg_field(hw, SGE_CONTROL_A, + PKTSHIFT_V(PKTSHIFT_M), + PKTSHIFT_V(CSIO_SGE_RX_DMA_OFFSET)); - csio_hw_tp_wr_bits_indirect(hw, TP_INGRESS_CONFIG, - CSUM_HAS_PSEUDO_HDR, 0); + csio_hw_tp_wr_bits_indirect(hw, TP_INGRESS_CONFIG_A, + CSUM_HAS_PSEUDO_HDR_F, 0); } static void @@ -1384,9 +1386,9 @@ csio_wr_get_sge(struct csio_hw *hw) u32 timer_value_0_and_1, timer_value_2_and_3, timer_value_4_and_5; u32 ingress_rx_threshold; - sge->sge_control = csio_rd_reg32(hw, SGE_CONTROL); + sge->sge_control = csio_rd_reg32(hw, SGE_CONTROL_A); - ingpad = INGPADBOUNDARY_GET(sge->sge_control); + ingpad = INGPADBOUNDARY_G(sge->sge_control); switch (ingpad) { case X_INGPCIEBOUNDARY_32B: @@ -1410,28 +1412,28 @@ csio_wr_get_sge(struct csio_hw *hw) for (i = 0; i < CSIO_SGE_FL_SIZE_REGS; i++) csio_get_flbuf_size(hw, sge, i); - timer_value_0_and_1 = csio_rd_reg32(hw, SGE_TIMER_VALUE_0_AND_1); - timer_value_2_and_3 = csio_rd_reg32(hw, SGE_TIMER_VALUE_2_AND_3); - timer_value_4_and_5 = csio_rd_reg32(hw, SGE_TIMER_VALUE_4_AND_5); + timer_value_0_and_1 = csio_rd_reg32(hw, SGE_TIMER_VALUE_0_AND_1_A); + timer_value_2_and_3 = csio_rd_reg32(hw, SGE_TIMER_VALUE_2_AND_3_A); + timer_value_4_and_5 = csio_rd_reg32(hw, SGE_TIMER_VALUE_4_AND_5_A); sge->timer_val[0] = (uint16_t)csio_core_ticks_to_us(hw, - TIMERVALUE0_GET(timer_value_0_and_1)); + TIMERVALUE0_G(timer_value_0_and_1)); sge->timer_val[1] = (uint16_t)csio_core_ticks_to_us(hw, - TIMERVALUE1_GET(timer_value_0_and_1)); + TIMERVALUE1_G(timer_value_0_and_1)); sge->timer_val[2] = (uint16_t)csio_core_ticks_to_us(hw, - TIMERVALUE2_GET(timer_value_2_and_3)); + TIMERVALUE2_G(timer_value_2_and_3)); sge->timer_val[3] = (uint16_t)csio_core_ticks_to_us(hw, - TIMERVALUE3_GET(timer_value_2_and_3)); + TIMERVALUE3_G(timer_value_2_and_3)); sge->timer_val[4] = (uint16_t)csio_core_ticks_to_us(hw, - TIMERVALUE4_GET(timer_value_4_and_5)); + TIMERVALUE4_G(timer_value_4_and_5)); sge->timer_val[5] = (uint16_t)csio_core_ticks_to_us(hw, - TIMERVALUE5_GET(timer_value_4_and_5)); + TIMERVALUE5_G(timer_value_4_and_5)); - ingress_rx_threshold = csio_rd_reg32(hw, SGE_INGRESS_RX_THRESHOLD); - sge->counter_val[0] = THRESHOLD_0_GET(ingress_rx_threshold); - sge->counter_val[1] = THRESHOLD_1_GET(ingress_rx_threshold); - sge->counter_val[2] = THRESHOLD_2_GET(ingress_rx_threshold); - sge->counter_val[3] = THRESHOLD_3_GET(ingress_rx_threshold); + ingress_rx_threshold = csio_rd_reg32(hw, SGE_INGRESS_RX_THRESHOLD_A); + sge->counter_val[0] = THRESHOLD_0_G(ingress_rx_threshold); + sge->counter_val[1] = THRESHOLD_1_G(ingress_rx_threshold); + sge->counter_val[2] = THRESHOLD_2_G(ingress_rx_threshold); + sge->counter_val[3] = THRESHOLD_3_G(ingress_rx_threshold); csio_init_intr_coalesce_parms(hw); } @@ -1454,9 +1456,9 @@ csio_wr_set_sge(struct csio_hw *hw) * Set up our basic SGE mode to deliver CPL messages to our Ingress * Queue and Packet Date to the Free List. */ - csio_set_reg_field(hw, SGE_CONTROL, RXPKTCPLMODE(1), RXPKTCPLMODE(1)); + csio_set_reg_field(hw, SGE_CONTROL_A, RXPKTCPLMODE_F, RXPKTCPLMODE_F); - sge->sge_control = csio_rd_reg32(hw, SGE_CONTROL); + sge->sge_control = csio_rd_reg32(hw, SGE_CONTROL_A); /* sge->csio_fl_align is set up by csio_wr_fixup_host_params(). */ @@ -1464,22 +1466,23 @@ csio_wr_set_sge(struct csio_hw *hw) * Set up to drop DOORBELL writes when the DOORBELL FIFO overflows * and generate an interrupt when this occurs so we can recover. */ - csio_set_reg_field(hw, SGE_DBFIFO_STATUS, - HP_INT_THRESH(HP_INT_THRESH_MASK) | - CSIO_HW_LP_INT_THRESH(hw, CSIO_HW_M_LP_INT_THRESH(hw)), - HP_INT_THRESH(CSIO_SGE_DBFIFO_INT_THRESH) | - CSIO_HW_LP_INT_THRESH(hw, CSIO_SGE_DBFIFO_INT_THRESH)); + csio_set_reg_field(hw, SGE_DBFIFO_STATUS_A, + LP_INT_THRESH_T5_V(LP_INT_THRESH_T5_M), + LP_INT_THRESH_T5_V(CSIO_SGE_DBFIFO_INT_THRESH)); + csio_set_reg_field(hw, SGE_DBFIFO_STATUS2_A, + HP_INT_THRESH_T5_V(LP_INT_THRESH_T5_M), + HP_INT_THRESH_T5_V(CSIO_SGE_DBFIFO_INT_THRESH)); - csio_set_reg_field(hw, SGE_DOORBELL_CONTROL, ENABLE_DROP, - ENABLE_DROP); + csio_set_reg_field(hw, SGE_DOORBELL_CONTROL_A, ENABLE_DROP_F, + ENABLE_DROP_F); /* SGE_FL_BUFFER_SIZE0 is set up by csio_wr_fixup_host_params(). */ CSIO_SET_FLBUF_SIZE(hw, 1, CSIO_SGE_FLBUF_SIZE1); csio_wr_reg32(hw, (CSIO_SGE_FLBUF_SIZE2 + sge->csio_fl_align - 1) - & ~(sge->csio_fl_align - 1), SGE_FL_BUFFER_SIZE2); + & ~(sge->csio_fl_align - 1), SGE_FL_BUFFER_SIZE2_A); csio_wr_reg32(hw, (CSIO_SGE_FLBUF_SIZE3 + sge->csio_fl_align - 1) - & ~(sge->csio_fl_align - 1), SGE_FL_BUFFER_SIZE3); + & ~(sge->csio_fl_align - 1), SGE_FL_BUFFER_SIZE3_A); CSIO_SET_FLBUF_SIZE(hw, 4, CSIO_SGE_FLBUF_SIZE4); CSIO_SET_FLBUF_SIZE(hw, 5, CSIO_SGE_FLBUF_SIZE5); CSIO_SET_FLBUF_SIZE(hw, 6, CSIO_SGE_FLBUF_SIZE6); @@ -1502,26 +1505,26 @@ csio_wr_set_sge(struct csio_hw *hw) sge->counter_val[2] = CSIO_SGE_INT_CNT_VAL_2; sge->counter_val[3] = CSIO_SGE_INT_CNT_VAL_3; - csio_wr_reg32(hw, THRESHOLD_0(sge->counter_val[0]) | - THRESHOLD_1(sge->counter_val[1]) | - THRESHOLD_2(sge->counter_val[2]) | - THRESHOLD_3(sge->counter_val[3]), - SGE_INGRESS_RX_THRESHOLD); + csio_wr_reg32(hw, THRESHOLD_0_V(sge->counter_val[0]) | + THRESHOLD_1_V(sge->counter_val[1]) | + THRESHOLD_2_V(sge->counter_val[2]) | + THRESHOLD_3_V(sge->counter_val[3]), + SGE_INGRESS_RX_THRESHOLD_A); csio_wr_reg32(hw, - TIMERVALUE0(csio_us_to_core_ticks(hw, sge->timer_val[0])) | - TIMERVALUE1(csio_us_to_core_ticks(hw, sge->timer_val[1])), - SGE_TIMER_VALUE_0_AND_1); + TIMERVALUE0_V(csio_us_to_core_ticks(hw, sge->timer_val[0])) | + TIMERVALUE1_V(csio_us_to_core_ticks(hw, sge->timer_val[1])), + SGE_TIMER_VALUE_0_AND_1_A); csio_wr_reg32(hw, - TIMERVALUE2(csio_us_to_core_ticks(hw, sge->timer_val[2])) | - TIMERVALUE3(csio_us_to_core_ticks(hw, sge->timer_val[3])), - SGE_TIMER_VALUE_2_AND_3); + TIMERVALUE2_V(csio_us_to_core_ticks(hw, sge->timer_val[2])) | + TIMERVALUE3_V(csio_us_to_core_ticks(hw, sge->timer_val[3])), + SGE_TIMER_VALUE_2_AND_3_A); csio_wr_reg32(hw, - TIMERVALUE4(csio_us_to_core_ticks(hw, sge->timer_val[4])) | - TIMERVALUE5(csio_us_to_core_ticks(hw, sge->timer_val[5])), - SGE_TIMER_VALUE_4_AND_5); + TIMERVALUE4_V(csio_us_to_core_ticks(hw, sge->timer_val[4])) | + TIMERVALUE5_V(csio_us_to_core_ticks(hw, sge->timer_val[5])), + SGE_TIMER_VALUE_4_AND_5_A); csio_init_intr_coalesce_parms(hw); } diff --git a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c index a83d2ceded83..dd00e5fe4a5e 100644 --- a/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c +++ b/drivers/scsi/cxgbi/cxgb4i/cxgb4i.c @@ -28,6 +28,7 @@ #include "t4fw_api.h" #include "l2t.h" #include "cxgb4i.h" +#include "clip_tbl.h" static unsigned int dbg_level; @@ -704,7 +705,7 @@ static void do_act_establish(struct cxgbi_device *cdev, struct sk_buff *skb) struct cpl_act_establish *req = (struct cpl_act_establish *)skb->data; unsigned short tcp_opt = ntohs(req->tcp_opt); unsigned int tid = GET_TID(req); - unsigned int atid = GET_TID_TID(ntohl(req->tos_atid)); + unsigned int atid = TID_TID_G(ntohl(req->tos_atid)); struct cxgb4_lld_info *lldi = cxgbi_cdev_priv(cdev); struct tid_info *t = lldi->tids; u32 rcv_isn = be32_to_cpu(req->rcv_isn); @@ -752,15 +753,15 @@ static void do_act_establish(struct cxgbi_device *cdev, struct sk_buff *skb) if (cxgb4i_rcv_win > (RCV_BUFSIZ_MASK << 10)) csk->rcv_wup -= cxgb4i_rcv_win - (RCV_BUFSIZ_MASK << 10); - csk->advmss = lldi->mtus[GET_TCPOPT_MSS(tcp_opt)] - 40; - if (GET_TCPOPT_TSTAMP(tcp_opt)) + csk->advmss = lldi->mtus[TCPOPT_MSS_G(tcp_opt)] - 40; + if (TCPOPT_TSTAMP_G(tcp_opt)) csk->advmss -= 12; if (csk->advmss < 128) csk->advmss = 128; log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, "csk 0x%p, mss_idx %u, advmss %u.\n", - csk, GET_TCPOPT_MSS(tcp_opt), csk->advmss); + csk, TCPOPT_MSS_G(tcp_opt), csk->advmss); cxgbi_sock_established(csk, ntohl(req->snd_isn), ntohs(req->tcp_opt)); @@ -856,8 +857,8 @@ static void do_act_open_rpl(struct cxgbi_device *cdev, struct sk_buff *skb) struct cpl_act_open_rpl *rpl = (struct cpl_act_open_rpl *)skb->data; unsigned int tid = GET_TID(rpl); unsigned int atid = - GET_TID_TID(GET_AOPEN_ATID(be32_to_cpu(rpl->atid_status))); - unsigned int status = GET_AOPEN_STATUS(be32_to_cpu(rpl->atid_status)); + TID_TID_G(AOPEN_ATID_G(be32_to_cpu(rpl->atid_status))); + unsigned int status = AOPEN_STATUS_G(be32_to_cpu(rpl->atid_status)); struct cxgb4_lld_info *lldi = cxgbi_cdev_priv(cdev); struct tid_info *t = lldi->tids; @@ -1112,7 +1113,7 @@ static void do_rx_iscsi_hdr(struct cxgbi_device *cdev, struct sk_buff *skb) hlen = ntohs(cpl->len); dlen = ntohl(*(unsigned int *)(bhs + 4)) & 0xFFFFFF; - plen = ISCSI_PDU_LEN(pdu_len_ddp); + plen = ISCSI_PDU_LEN_G(pdu_len_ddp); if (is_t4(lldi->adapter_type)) plen -= 40; @@ -1322,6 +1323,9 @@ static inline void l2t_put(struct cxgbi_sock *csk) static void release_offload_resources(struct cxgbi_sock *csk) { struct cxgb4_lld_info *lldi; +#if IS_ENABLED(CONFIG_IPV6) + struct net_device *ndev = csk->cdev->ports[csk->port_id]; +#endif log_debug(1 << CXGBI_DBG_TOE | 1 << CXGBI_DBG_SOCK, "csk 0x%p,%u,0x%lx,%u.\n", @@ -1334,6 +1338,12 @@ static void release_offload_resources(struct cxgbi_sock *csk) } l2t_put(csk); +#if IS_ENABLED(CONFIG_IPV6) + if (csk->csk_family == AF_INET6) + cxgb4_clip_release(ndev, + (const u32 *)&csk->saddr6.sin6_addr, 1); +#endif + if (cxgbi_sock_flag(csk, CTPF_HAS_ATID)) free_atid(csk); else if (cxgbi_sock_flag(csk, CTPF_HAS_TID)) { @@ -1391,10 +1401,15 @@ static int init_act_open(struct cxgbi_sock *csk) csk->l2t = cxgb4_l2t_get(lldi->l2t, n, ndev, 0); if (!csk->l2t) { pr_err("%s, cannot alloc l2t.\n", ndev->name); - goto rel_resource; + goto rel_resource_without_clip; } cxgbi_sock_get(csk); +#if IS_ENABLED(CONFIG_IPV6) + if (csk->csk_family == AF_INET6) + cxgb4_clip_get(ndev, (const u32 *)&csk->saddr6.sin6_addr, 1); +#endif + if (t4) { size = sizeof(struct cpl_act_open_req); size6 = sizeof(struct cpl_act_open_req6); @@ -1451,6 +1466,12 @@ static int init_act_open(struct cxgbi_sock *csk) return 0; rel_resource: +#if IS_ENABLED(CONFIG_IPV6) + if (csk->csk_family == AF_INET6) + cxgb4_clip_release(ndev, + (const u32 *)&csk->saddr6.sin6_addr, 1); +#endif +rel_resource_without_clip: if (n) neigh_release(n); if (skb) @@ -1619,7 +1640,7 @@ static int ddp_setup_conn_pgidx(struct cxgbi_sock *csk, unsigned int tid, req = (struct cpl_set_tcb_field *)skb->head; INIT_TP_WR(req, csk->tid); OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, csk->tid)); - req->reply_ctrl = htons(NO_REPLY(reply) | QUEUENO(csk->rss_qid)); + req->reply_ctrl = htons(NO_REPLY_V(reply) | QUEUENO_V(csk->rss_qid)); req->word_cookie = htons(0); req->mask = cpu_to_be64(0x3 << 8); req->val = cpu_to_be64(pg_idx << 8); @@ -1651,7 +1672,7 @@ static int ddp_setup_conn_digest(struct cxgbi_sock *csk, unsigned int tid, req = (struct cpl_set_tcb_field *)skb->head; INIT_TP_WR(req, tid); OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, tid)); - req->reply_ctrl = htons(NO_REPLY(reply) | QUEUENO(csk->rss_qid)); + req->reply_ctrl = htons(NO_REPLY_V(reply) | QUEUENO_V(csk->rss_qid)); req->word_cookie = htons(0); req->mask = cpu_to_be64(0x3 << 4); req->val = cpu_to_be64(((hcrc ? ULP_CRC_HEADER : 0) | diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c index 1dba62c5cf6a..1efebc9eedfb 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c @@ -136,11 +136,12 @@ static void __detach_handler (struct kref *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); - scsi_dh->detach(sdev); sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", scsi_dh->name); module_put(scsi_dh->module); } diff --git a/drivers/scsi/esas2r/esas2r_init.c b/drivers/scsi/esas2r/esas2r_init.c index 6776931e25d4..78ce4d61a69b 100644 --- a/drivers/scsi/esas2r/esas2r_init.c +++ b/drivers/scsi/esas2r/esas2r_init.c @@ -813,12 +813,13 @@ static void esas2r_init_pci_cfg_space(struct esas2r_adapter *a) pci_read_config_word(a->pcid, pcie_cap_reg + PCI_EXP_DEVCTL, &devcontrol); - if ((devcontrol & PCI_EXP_DEVCTL_READRQ) > 0x2000) { + if ((devcontrol & PCI_EXP_DEVCTL_READRQ) > + PCI_EXP_DEVCTL_READRQ_512B) { esas2r_log(ESAS2R_LOG_INFO, "max read request size > 512B"); devcontrol &= ~PCI_EXP_DEVCTL_READRQ; - devcontrol |= 0x2000; + devcontrol |= PCI_EXP_DEVCTL_READRQ_512B; pci_write_config_word(a->pcid, pcie_cap_reg + PCI_EXP_DEVCTL, devcontrol); diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index df4e27cd996a..9219953ee949 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -683,6 +683,7 @@ static void ipr_init_ipr_cmnd(struct ipr_cmnd *ipr_cmd, ipr_reinit_ipr_cmnd(ipr_cmd); ipr_cmd->u.scratch = 0; ipr_cmd->sibling = NULL; + ipr_cmd->eh_comp = NULL; ipr_cmd->fast_done = fast_done; init_timer(&ipr_cmd->timer); } @@ -848,6 +849,8 @@ static void ipr_scsi_eh_done(struct ipr_cmnd *ipr_cmd) scsi_dma_unmap(ipr_cmd->scsi_cmd); scsi_cmd->scsi_done(scsi_cmd); + if (ipr_cmd->eh_comp) + complete(ipr_cmd->eh_comp); list_add_tail(&ipr_cmd->queue, &ipr_cmd->hrrq->hrrq_free_q); } @@ -4811,6 +4814,84 @@ static int ipr_slave_alloc(struct scsi_device *sdev) return rc; } +/** + * ipr_match_lun - Match function for specified LUN + * @ipr_cmd: ipr command struct + * @device: device to match (sdev) + * + * Returns: + * 1 if command matches sdev / 0 if command does not match sdev + **/ +static int ipr_match_lun(struct ipr_cmnd *ipr_cmd, void *device) +{ + if (ipr_cmd->scsi_cmd && ipr_cmd->scsi_cmd->device == device) + return 1; + return 0; +} + +/** + * ipr_wait_for_ops - Wait for matching commands to complete + * @ipr_cmd: ipr command struct + * @device: device to match (sdev) + * @match: match function to use + * + * Returns: + * SUCCESS / FAILED + **/ +static int ipr_wait_for_ops(struct ipr_ioa_cfg *ioa_cfg, void *device, + int (*match)(struct ipr_cmnd *, void *)) +{ + struct ipr_cmnd *ipr_cmd; + int wait; + unsigned long flags; + struct ipr_hrr_queue *hrrq; + signed long timeout = IPR_ABORT_TASK_TIMEOUT; + DECLARE_COMPLETION_ONSTACK(comp); + + ENTER; + do { + wait = 0; + + for_each_hrrq(hrrq, ioa_cfg) { + spin_lock_irqsave(hrrq->lock, flags); + list_for_each_entry(ipr_cmd, &hrrq->hrrq_pending_q, queue) { + if (match(ipr_cmd, device)) { + ipr_cmd->eh_comp = ∁ + wait++; + } + } + spin_unlock_irqrestore(hrrq->lock, flags); + } + + if (wait) { + timeout = wait_for_completion_timeout(&comp, timeout); + + if (!timeout) { + wait = 0; + + for_each_hrrq(hrrq, ioa_cfg) { + spin_lock_irqsave(hrrq->lock, flags); + list_for_each_entry(ipr_cmd, &hrrq->hrrq_pending_q, queue) { + if (match(ipr_cmd, device)) { + ipr_cmd->eh_comp = NULL; + wait++; + } + } + spin_unlock_irqrestore(hrrq->lock, flags); + } + + if (wait) + dev_err(&ioa_cfg->pdev->dev, "Timed out waiting for aborted commands\n"); + LEAVE; + return wait ? FAILED : SUCCESS; + } + } + } while (wait); + + LEAVE; + return SUCCESS; +} + static int ipr_eh_host_reset(struct scsi_cmnd *cmd) { struct ipr_ioa_cfg *ioa_cfg; @@ -5030,11 +5111,17 @@ static int __ipr_eh_dev_reset(struct scsi_cmnd *scsi_cmd) static int ipr_eh_dev_reset(struct scsi_cmnd *cmd) { int rc; + struct ipr_ioa_cfg *ioa_cfg; + + ioa_cfg = (struct ipr_ioa_cfg *) cmd->device->host->hostdata; spin_lock_irq(cmd->device->host->host_lock); rc = __ipr_eh_dev_reset(cmd); spin_unlock_irq(cmd->device->host->host_lock); + if (rc == SUCCESS) + rc = ipr_wait_for_ops(ioa_cfg, cmd->device, ipr_match_lun); + return rc; } @@ -5234,13 +5321,18 @@ static int ipr_eh_abort(struct scsi_cmnd *scsi_cmd) { unsigned long flags; int rc; + struct ipr_ioa_cfg *ioa_cfg; ENTER; + ioa_cfg = (struct ipr_ioa_cfg *) scsi_cmd->device->host->hostdata; + spin_lock_irqsave(scsi_cmd->device->host->host_lock, flags); rc = ipr_cancel_op(scsi_cmd); spin_unlock_irqrestore(scsi_cmd->device->host->host_lock, flags); + if (rc == SUCCESS) + rc = ipr_wait_for_ops(ioa_cfg, scsi_cmd->device, ipr_match_lun); LEAVE; return rc; } diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index b4f3eec51bc9..ec03b42fa2b9 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -1606,6 +1606,7 @@ struct ipr_cmnd { struct scsi_device *sdev; } u; + struct completion *eh_comp; struct ipr_hrr_queue *hrrq; struct ipr_ioa_cfg *ioa_cfg; }; diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index 8c27b6a77ec4..ed31d8cc6266 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -1473,13 +1473,7 @@ static int pmcraid_notify_aen( } /* send genetlink multicast message to notify appplications */ - result = genlmsg_end(skb, msg_header); - - if (result < 0) { - pmcraid_err("genlmsg_end failed\n"); - nlmsg_free(skb); - return result; - } + genlmsg_end(skb, msg_header); result = genlmsg_multicast(&pmcraid_event_family, skb, 0, 0, GFP_ATOMIC); @@ -4223,7 +4217,7 @@ static ssize_t pmcraid_show_adapter_id( static struct device_attribute pmcraid_adapter_id_attr = { .attr = { .name = "adapter_id", - .mode = S_IRUGO | S_IWUSR, + .mode = S_IRUGO, }, .show = pmcraid_show_adapter_id, }; diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 08c90a711556..c9c3b579eece 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -979,9 +979,9 @@ int scsi_device_get(struct scsi_device *sdev) return -ENXIO; if (!get_device(&sdev->sdev_gendev)) return -ENXIO; - /* We can fail this if we're doing SCSI operations + /* We can fail try_module_get if we're doing SCSI operations * from module exit (like cache flush) */ - try_module_get(sdev->host->hostt->module); + __module_get(sdev->host->hostt->module); return 0; } @@ -997,14 +997,7 @@ EXPORT_SYMBOL(scsi_device_get); */ void scsi_device_put(struct scsi_device *sdev) { -#ifdef CONFIG_MODULE_UNLOAD - struct module *module = sdev->host->hostt->module; - - /* The module refcount will be zero if scsi_device_get() - * was called from a module removal routine */ - if (module && module_refcount(module) != 0) - module_put(module); -#endif + module_put(sdev->host->hostt->module); put_device(&sdev->sdev_gendev); } EXPORT_SYMBOL(scsi_device_put); diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 8bcf6ad0d765..113232135d27 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -1678,7 +1678,7 @@ resp_rsup_opcodes(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) req_opcode = cmd[3]; req_sa = get_unaligned_be16(cmd + 4); alloc_len = get_unaligned_be32(cmd + 6); - if (alloc_len < 4 && alloc_len > 0xffff) { + if (alloc_len < 4 || alloc_len > 0xffff) { mk_sense_invalid_fld(scp, SDEB_IN_CDB, 6, -1); return check_condition_result; } @@ -1686,7 +1686,7 @@ resp_rsup_opcodes(struct scsi_cmnd *scp, struct sdebug_dev_info *devip) a_len = 8192; else a_len = alloc_len; - arr = kzalloc((a_len < 256) ? 320 : a_len + 64, GFP_KERNEL); + arr = kzalloc((a_len < 256) ? 320 : a_len + 64, GFP_ATOMIC); if (NULL == arr) { mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, INSUFF_RES_ASCQ); diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 6d5c0b8cb0bb..17bb541f7cc2 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1143,7 +1143,17 @@ int scsi_init_io(struct scsi_cmnd *cmd) struct scsi_data_buffer *prot_sdb = cmd->prot_sdb; int ivecs, count; - BUG_ON(prot_sdb == NULL); + if (prot_sdb == NULL) { + /* + * This can happen if someone (e.g. multipath) + * queues a command to a device on an adapter + * that does not support DIX. + */ + WARN_ON_ONCE(1); + error = BLKPREP_KILL; + goto err_exit; + } + ivecs = blk_rq_count_integrity_sg(rq->q, rq->bio); if (scsi_alloc_sgtable(prot_sdb, ivecs, is_mq)) { diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index ebf35cb64216..6b78476d04bb 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -2800,9 +2800,11 @@ static int sd_revalidate_disk(struct gendisk *disk) */ sd_set_flush_flag(sdkp); - max_xfer = min_not_zero(queue_max_hw_sectors(sdkp->disk->queue), - sdkp->max_xfer_blocks); + max_xfer = sdkp->max_xfer_blocks; max_xfer <<= ilog2(sdp->sector_size) - 9; + + max_xfer = min_not_zero(queue_max_hw_sectors(sdkp->disk->queue), + max_xfer); blk_queue_max_hw_sectors(sdkp->disk->queue, max_xfer); set_capacity(disk, sdkp->capacity); sd_config_write_same(sdkp); diff --git a/drivers/sfi/sfi_core.c b/drivers/sfi/sfi_core.c index 1e824fb1649b..296db7a69c27 100644 --- a/drivers/sfi/sfi_core.c +++ b/drivers/sfi/sfi_core.c @@ -161,7 +161,7 @@ static int sfi_verify_table(struct sfi_table_header *table) * Check for common case that we can re-use mapping to SYST, * which requires syst_pa, syst_va to be initialized. */ -struct sfi_table_header *sfi_map_table(u64 pa) +static struct sfi_table_header *sfi_map_table(u64 pa) { struct sfi_table_header *th; u32 length; @@ -189,7 +189,7 @@ struct sfi_table_header *sfi_map_table(u64 pa) * Undoes effect of sfi_map_table() by unmapping table * if it did not completely fit on same page as SYST. */ -void sfi_unmap_table(struct sfi_table_header *th) +static void sfi_unmap_table(struct sfi_table_header *th) { if (!TABLE_ON_PAGE(syst_va, th, th->len)) sfi_unmap_memory(th, TABLE_ON_PAGE(th, th, th->len) ? diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 99829985c1a1..95ccedabba4f 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -185,6 +185,16 @@ config SPI_DAVINCI help SPI master controller for DaVinci/DA8x/OMAP-L/AM1x SPI modules. +config SPI_DLN2 + tristate "Diolan DLN-2 USB SPI adapter" + depends on MFD_DLN2 + help + If you say yes to this option, support will be included for Diolan + DLN2, a USB to SPI interface. + + This driver can also be built as a module. If so, the module + will be called spi-dln2. + config SPI_EFM32 tristate "EFM32 SPI controller" depends on OF && ARM && (ARCH_EFM32 || COMPILE_TEST) @@ -279,7 +289,7 @@ config SPI_FSL_CPM depends on FSL_SOC config SPI_FSL_SPI - bool "Freescale SPI controller and Aeroflex Gaisler GRLIB SPI controller" + tristate "Freescale SPI controller and Aeroflex Gaisler GRLIB SPI controller" depends on OF select SPI_FSL_LIB select SPI_FSL_CPM if FSL_SOC @@ -292,7 +302,6 @@ config SPI_FSL_SPI config SPI_FSL_DSPI tristate "Freescale DSPI controller" - select SPI_BITBANG select REGMAP_MMIO depends on SOC_VF610 || COMPILE_TEST help @@ -300,7 +309,7 @@ config SPI_FSL_DSPI mode. VF610 platform uses the controller. config SPI_FSL_ESPI - bool "Freescale eSPI controller" + tristate "Freescale eSPI controller" depends on FSL_SOC select SPI_FSL_LIB help @@ -460,7 +469,6 @@ config SPI_S3C24XX_FIQ config SPI_S3C64XX tristate "Samsung S3C64XX series type SPI" depends on (PLAT_SAMSUNG || ARCH_EXYNOS) - select S3C64XX_PL080 if ARCH_S3C64XX help SPI driver for Samsung S3C64XX and newer SoCs. @@ -503,6 +511,13 @@ config SPI_SIRF help SPI driver for CSR SiRFprimaII SoCs +config SPI_ST_SSC4 + tristate "STMicroelectronics SPI SSC-based driver" + depends on ARCH_STI + help + STMicroelectronics SoCs support for SPI. If you say yes to + this option, support will be included for the SSC driven SPI. + config SPI_SUN4I tristate "Allwinner A10 SoCs SPI controller" depends on ARCH_SUNXI || COMPILE_TEST @@ -595,7 +610,6 @@ config SPI_XTENSA_XTFPGA 16 bit words in SPI mode 0, automatically asserting CS on transfer start and deasserting on end. - config SPI_NUC900 tristate "Nuvoton NUC900 series SPI" depends on ARCH_W90X900 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 6b9d2ac629cc..d8cbf654976b 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_SPI_CADENCE) += spi-cadence.o obj-$(CONFIG_SPI_CLPS711X) += spi-clps711x.o obj-$(CONFIG_SPI_COLDFIRE_QSPI) += spi-coldfire-qspi.o obj-$(CONFIG_SPI_DAVINCI) += spi-davinci.o +obj-$(CONFIG_SPI_DLN2) += spi-dln2.o obj-$(CONFIG_SPI_DESIGNWARE) += spi-dw.o obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o @@ -76,6 +77,7 @@ obj-$(CONFIG_SPI_SH_HSPI) += spi-sh-hspi.o obj-$(CONFIG_SPI_SH_MSIOF) += spi-sh-msiof.o obj-$(CONFIG_SPI_SH_SCI) += spi-sh-sci.o obj-$(CONFIG_SPI_SIRF) += spi-sirf.o +obj-$(CONFIG_SPI_ST_SSC4) += spi-st-ssc4.o obj-$(CONFIG_SPI_SUN4I) += spi-sun4i.o obj-$(CONFIG_SPI_SUN6I) += spi-sun6i.o obj-$(CONFIG_SPI_TEGRA114) += spi-tegra114.o diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 23d8f5f56579..9af7841f2e8c 100644 --- a/drivers/spi/spi-atmel.c +++ b/drivers/spi/spi-atmel.c @@ -1046,6 +1046,7 @@ static int atmel_spi_one_transfer(struct spi_master *master, struct atmel_spi_device *asd; int timeout; int ret; + unsigned long dma_timeout; as = spi_master_get_devdata(master); @@ -1103,15 +1104,12 @@ static int atmel_spi_one_transfer(struct spi_master *master, /* interrupts are disabled, so free the lock for schedule */ atmel_spi_unlock(as); - ret = wait_for_completion_timeout(&as->xfer_completion, - SPI_DMA_TIMEOUT); + dma_timeout = wait_for_completion_timeout(&as->xfer_completion, + SPI_DMA_TIMEOUT); atmel_spi_lock(as); - if (WARN_ON(ret == 0)) { - dev_err(&spi->dev, - "spi trasfer timeout, err %d\n", ret); + if (WARN_ON(dma_timeout == 0)) { + dev_err(&spi->dev, "spi transfer timeout\n"); as->done_status = -EIO; - } else { - ret = 0; } if (as->done_status) diff --git a/drivers/spi/spi-au1550.c b/drivers/spi/spi-au1550.c index 326f47973684..f45e085c01a6 100644 --- a/drivers/spi/spi-au1550.c +++ b/drivers/spi/spi-au1550.c @@ -15,10 +15,6 @@ * 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 */ #include <linux/init.h> diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index 98aab457b24d..419a782ab6d5 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -17,10 +17,6 @@ * 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. */ #include <linux/clk.h> diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c index c20530982e26..e73e2b052c9c 100644 --- a/drivers/spi/spi-bcm63xx.c +++ b/drivers/spi/spi-bcm63xx.c @@ -13,10 +13,6 @@ * 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., 51 Franklin Street, Fifth Floor, */ #include <linux/kernel.h> diff --git a/drivers/spi/spi-bitbang.c b/drivers/spi/spi-bitbang.c index dc7d2c2d643e..5ef6638d5e8a 100644 --- a/drivers/spi/spi-bitbang.c +++ b/drivers/spi/spi-bitbang.c @@ -10,10 +10,6 @@ * 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 */ #include <linux/spinlock.h> diff --git a/drivers/spi/spi-butterfly.c b/drivers/spi/spi-butterfly.c index ee4f91ccd8fd..9a95862986c8 100644 --- a/drivers/spi/spi-butterfly.c +++ b/drivers/spi/spi-butterfly.c @@ -12,10 +12,6 @@ * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/kernel.h> #include <linux/init.h> diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c index 41b5dc4445f6..688956ff5095 100644 --- a/drivers/spi/spi-coldfire-qspi.c +++ b/drivers/spi/spi-coldfire-qspi.c @@ -12,11 +12,6 @@ * 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., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA - * */ #include <linux/kernel.h> diff --git a/drivers/spi/spi-davinci.c b/drivers/spi/spi-davinci.c index b3707badb1e5..5e991065f5b0 100644 --- a/drivers/spi/spi-davinci.c +++ b/drivers/spi/spi-davinci.c @@ -11,10 +11,6 @@ * 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 */ #include <linux/interrupt.h> diff --git a/drivers/spi/spi-dln2.c b/drivers/spi/spi-dln2.c new file mode 100644 index 000000000000..3b7d91d94fea --- /dev/null +++ b/drivers/spi/spi-dln2.c @@ -0,0 +1,881 @@ +/* + * Driver for the Diolan DLN-2 USB-SPI adapter + * + * Copyright (c) 2014 Intel 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/mfd/dln2.h> +#include <linux/spi/spi.h> +#include <linux/pm_runtime.h> +#include <asm/unaligned.h> + +#define DLN2_SPI_MODULE_ID 0x02 +#define DLN2_SPI_CMD(cmd) DLN2_CMD(cmd, DLN2_SPI_MODULE_ID) + +/* SPI commands */ +#define DLN2_SPI_GET_PORT_COUNT DLN2_SPI_CMD(0x00) +#define DLN2_SPI_ENABLE DLN2_SPI_CMD(0x11) +#define DLN2_SPI_DISABLE DLN2_SPI_CMD(0x12) +#define DLN2_SPI_IS_ENABLED DLN2_SPI_CMD(0x13) +#define DLN2_SPI_SET_MODE DLN2_SPI_CMD(0x14) +#define DLN2_SPI_GET_MODE DLN2_SPI_CMD(0x15) +#define DLN2_SPI_SET_FRAME_SIZE DLN2_SPI_CMD(0x16) +#define DLN2_SPI_GET_FRAME_SIZE DLN2_SPI_CMD(0x17) +#define DLN2_SPI_SET_FREQUENCY DLN2_SPI_CMD(0x18) +#define DLN2_SPI_GET_FREQUENCY DLN2_SPI_CMD(0x19) +#define DLN2_SPI_READ_WRITE DLN2_SPI_CMD(0x1A) +#define DLN2_SPI_READ DLN2_SPI_CMD(0x1B) +#define DLN2_SPI_WRITE DLN2_SPI_CMD(0x1C) +#define DLN2_SPI_SET_DELAY_BETWEEN_SS DLN2_SPI_CMD(0x20) +#define DLN2_SPI_GET_DELAY_BETWEEN_SS DLN2_SPI_CMD(0x21) +#define DLN2_SPI_SET_DELAY_AFTER_SS DLN2_SPI_CMD(0x22) +#define DLN2_SPI_GET_DELAY_AFTER_SS DLN2_SPI_CMD(0x23) +#define DLN2_SPI_SET_DELAY_BETWEEN_FRAMES DLN2_SPI_CMD(0x24) +#define DLN2_SPI_GET_DELAY_BETWEEN_FRAMES DLN2_SPI_CMD(0x25) +#define DLN2_SPI_SET_SS DLN2_SPI_CMD(0x26) +#define DLN2_SPI_GET_SS DLN2_SPI_CMD(0x27) +#define DLN2_SPI_RELEASE_SS DLN2_SPI_CMD(0x28) +#define DLN2_SPI_SS_VARIABLE_ENABLE DLN2_SPI_CMD(0x2B) +#define DLN2_SPI_SS_VARIABLE_DISABLE DLN2_SPI_CMD(0x2C) +#define DLN2_SPI_SS_VARIABLE_IS_ENABLED DLN2_SPI_CMD(0x2D) +#define DLN2_SPI_SS_AAT_ENABLE DLN2_SPI_CMD(0x2E) +#define DLN2_SPI_SS_AAT_DISABLE DLN2_SPI_CMD(0x2F) +#define DLN2_SPI_SS_AAT_IS_ENABLED DLN2_SPI_CMD(0x30) +#define DLN2_SPI_SS_BETWEEN_FRAMES_ENABLE DLN2_SPI_CMD(0x31) +#define DLN2_SPI_SS_BETWEEN_FRAMES_DISABLE DLN2_SPI_CMD(0x32) +#define DLN2_SPI_SS_BETWEEN_FRAMES_IS_ENABLED DLN2_SPI_CMD(0x33) +#define DLN2_SPI_SET_CPHA DLN2_SPI_CMD(0x34) +#define DLN2_SPI_GET_CPHA DLN2_SPI_CMD(0x35) +#define DLN2_SPI_SET_CPOL DLN2_SPI_CMD(0x36) +#define DLN2_SPI_GET_CPOL DLN2_SPI_CMD(0x37) +#define DLN2_SPI_SS_MULTI_ENABLE DLN2_SPI_CMD(0x38) +#define DLN2_SPI_SS_MULTI_DISABLE DLN2_SPI_CMD(0x39) +#define DLN2_SPI_SS_MULTI_IS_ENABLED DLN2_SPI_CMD(0x3A) +#define DLN2_SPI_GET_SUPPORTED_MODES DLN2_SPI_CMD(0x40) +#define DLN2_SPI_GET_SUPPORTED_CPHA_VALUES DLN2_SPI_CMD(0x41) +#define DLN2_SPI_GET_SUPPORTED_CPOL_VALUES DLN2_SPI_CMD(0x42) +#define DLN2_SPI_GET_SUPPORTED_FRAME_SIZES DLN2_SPI_CMD(0x43) +#define DLN2_SPI_GET_SS_COUNT DLN2_SPI_CMD(0x44) +#define DLN2_SPI_GET_MIN_FREQUENCY DLN2_SPI_CMD(0x45) +#define DLN2_SPI_GET_MAX_FREQUENCY DLN2_SPI_CMD(0x46) +#define DLN2_SPI_GET_MIN_DELAY_BETWEEN_SS DLN2_SPI_CMD(0x47) +#define DLN2_SPI_GET_MAX_DELAY_BETWEEN_SS DLN2_SPI_CMD(0x48) +#define DLN2_SPI_GET_MIN_DELAY_AFTER_SS DLN2_SPI_CMD(0x49) +#define DLN2_SPI_GET_MAX_DELAY_AFTER_SS DLN2_SPI_CMD(0x4A) +#define DLN2_SPI_GET_MIN_DELAY_BETWEEN_FRAMES DLN2_SPI_CMD(0x4B) +#define DLN2_SPI_GET_MAX_DELAY_BETWEEN_FRAMES DLN2_SPI_CMD(0x4C) + +#define DLN2_SPI_MAX_XFER_SIZE 256 +#define DLN2_SPI_BUF_SIZE (DLN2_SPI_MAX_XFER_SIZE + 16) +#define DLN2_SPI_ATTR_LEAVE_SS_LOW BIT(0) +#define DLN2_TRANSFERS_WAIT_COMPLETE 1 +#define DLN2_TRANSFERS_CANCEL 0 +#define DLN2_RPM_AUTOSUSPEND_TIMEOUT 2000 + +struct dln2_spi { + struct platform_device *pdev; + struct spi_master *master; + u8 port; + + /* + * This buffer will be used mainly for read/write operations. Since + * they're quite large, we cannot use the stack. Protection is not + * needed because all SPI communication is serialized by the SPI core. + */ + void *buf; + + u8 bpw; + u32 speed; + u16 mode; + u8 cs; +}; + +/* + * Enable/Disable SPI module. The disable command will wait for transfers to + * complete first. + */ +static int dln2_spi_enable(struct dln2_spi *dln2, bool enable) +{ + u16 cmd; + struct { + u8 port; + u8 wait_for_completion; + } tx; + unsigned len = sizeof(tx); + + tx.port = dln2->port; + + if (enable) { + cmd = DLN2_SPI_ENABLE; + len -= sizeof(tx.wait_for_completion); + } else { + tx.wait_for_completion = DLN2_TRANSFERS_WAIT_COMPLETE; + cmd = DLN2_SPI_DISABLE; + } + + return dln2_transfer_tx(dln2->pdev, cmd, &tx, len); +} + +/* + * Select/unselect multiple CS lines. The selected lines will be automatically + * toggled LOW/HIGH by the board firmware during transfers, provided they're + * enabled first. + * + * Ex: cs_mask = 0x03 -> CS0 & CS1 will be selected and the next WR/RD operation + * will toggle the lines LOW/HIGH automatically. + */ +static int dln2_spi_cs_set(struct dln2_spi *dln2, u8 cs_mask) +{ + struct { + u8 port; + u8 cs; + } tx; + + tx.port = dln2->port; + + /* + * According to Diolan docs, "a slave device can be selected by changing + * the corresponding bit value to 0". The rest must be set to 1. Hence + * the bitwise NOT in front. + */ + tx.cs = ~cs_mask; + + return dln2_transfer_tx(dln2->pdev, DLN2_SPI_SET_SS, &tx, sizeof(tx)); +} + +/* + * Select one CS line. The other lines will be un-selected. + */ +static int dln2_spi_cs_set_one(struct dln2_spi *dln2, u8 cs) +{ + return dln2_spi_cs_set(dln2, BIT(cs)); +} + +/* + * Enable/disable CS lines for usage. The module has to be disabled first. + */ +static int dln2_spi_cs_enable(struct dln2_spi *dln2, u8 cs_mask, bool enable) +{ + struct { + u8 port; + u8 cs; + } tx; + u16 cmd; + + tx.port = dln2->port; + tx.cs = cs_mask; + cmd = enable ? DLN2_SPI_SS_MULTI_ENABLE : DLN2_SPI_SS_MULTI_DISABLE; + + return dln2_transfer_tx(dln2->pdev, cmd, &tx, sizeof(tx)); +} + +static int dln2_spi_cs_enable_all(struct dln2_spi *dln2, bool enable) +{ + u8 cs_mask = GENMASK(dln2->master->num_chipselect - 1, 0); + + return dln2_spi_cs_enable(dln2, cs_mask, enable); +} + +static int dln2_spi_get_cs_num(struct dln2_spi *dln2, u16 *cs_num) +{ + int ret; + struct { + u8 port; + } tx; + struct { + __le16 cs_count; + } rx; + unsigned rx_len = sizeof(rx); + + tx.port = dln2->port; + ret = dln2_transfer(dln2->pdev, DLN2_SPI_GET_SS_COUNT, &tx, sizeof(tx), + &rx, &rx_len); + if (ret < 0) + return ret; + if (rx_len < sizeof(rx)) + return -EPROTO; + + *cs_num = le16_to_cpu(rx.cs_count); + + dev_dbg(&dln2->pdev->dev, "cs_num = %d\n", *cs_num); + + return 0; +} + +static int dln2_spi_get_speed(struct dln2_spi *dln2, u16 cmd, u32 *freq) +{ + int ret; + struct { + u8 port; + } tx; + struct { + __le32 speed; + } rx; + unsigned rx_len = sizeof(rx); + + tx.port = dln2->port; + + ret = dln2_transfer(dln2->pdev, cmd, &tx, sizeof(tx), &rx, &rx_len); + if (ret < 0) + return ret; + if (rx_len < sizeof(rx)) + return -EPROTO; + + *freq = le32_to_cpu(rx.speed); + + return 0; +} + +/* + * Get bus min/max frequencies. + */ +static int dln2_spi_get_speed_range(struct dln2_spi *dln2, u32 *fmin, u32 *fmax) +{ + int ret; + + ret = dln2_spi_get_speed(dln2, DLN2_SPI_GET_MIN_FREQUENCY, fmin); + if (ret < 0) + return ret; + + ret = dln2_spi_get_speed(dln2, DLN2_SPI_GET_MAX_FREQUENCY, fmax); + if (ret < 0) + return ret; + + dev_dbg(&dln2->pdev->dev, "freq_min = %d, freq_max = %d\n", + *fmin, *fmax); + + return 0; +} + +/* + * Set the bus speed. The module will automatically round down to the closest + * available frequency and returns it. The module has to be disabled first. + */ +static int dln2_spi_set_speed(struct dln2_spi *dln2, u32 speed) +{ + int ret; + struct { + u8 port; + __le32 speed; + } __packed tx; + struct { + __le32 speed; + } rx; + int rx_len = sizeof(rx); + + tx.port = dln2->port; + tx.speed = cpu_to_le32(speed); + + ret = dln2_transfer(dln2->pdev, DLN2_SPI_SET_FREQUENCY, &tx, sizeof(tx), + &rx, &rx_len); + if (ret < 0) + return ret; + if (rx_len < sizeof(rx)) + return -EPROTO; + + return 0; +} + +/* + * Change CPOL & CPHA. The module has to be disabled first. + */ +static int dln2_spi_set_mode(struct dln2_spi *dln2, u8 mode) +{ + struct { + u8 port; + u8 mode; + } tx; + + tx.port = dln2->port; + tx.mode = mode; + + return dln2_transfer_tx(dln2->pdev, DLN2_SPI_SET_MODE, &tx, sizeof(tx)); +} + +/* + * Change frame size. The module has to be disabled first. + */ +static int dln2_spi_set_bpw(struct dln2_spi *dln2, u8 bpw) +{ + struct { + u8 port; + u8 bpw; + } tx; + + tx.port = dln2->port; + tx.bpw = bpw; + + return dln2_transfer_tx(dln2->pdev, DLN2_SPI_SET_FRAME_SIZE, + &tx, sizeof(tx)); +} + +static int dln2_spi_get_supported_frame_sizes(struct dln2_spi *dln2, + u32 *bpw_mask) +{ + int ret; + struct { + u8 port; + } tx; + struct { + u8 count; + u8 frame_sizes[36]; + } *rx = dln2->buf; + unsigned rx_len = sizeof(*rx); + int i; + + tx.port = dln2->port; + + ret = dln2_transfer(dln2->pdev, DLN2_SPI_GET_SUPPORTED_FRAME_SIZES, + &tx, sizeof(tx), rx, &rx_len); + if (ret < 0) + return ret; + if (rx_len < sizeof(*rx)) + return -EPROTO; + if (rx->count > ARRAY_SIZE(rx->frame_sizes)) + return -EPROTO; + + *bpw_mask = 0; + for (i = 0; i < rx->count; i++) + *bpw_mask |= BIT(rx->frame_sizes[i] - 1); + + dev_dbg(&dln2->pdev->dev, "bpw_mask = 0x%X\n", *bpw_mask); + + return 0; +} + +/* + * Copy the data to DLN2 buffer and change the byte order to LE, requested by + * DLN2 module. SPI core makes sure that the data length is a multiple of word + * size. + */ +static int dln2_spi_copy_to_buf(u8 *dln2_buf, const u8 *src, u16 len, u8 bpw) +{ +#ifdef __LITTLE_ENDIAN + memcpy(dln2_buf, src, len); +#else + if (bpw <= 8) { + memcpy(dln2_buf, src, len); + } else if (bpw <= 16) { + __le16 *d = (__le16 *)dln2_buf; + u16 *s = (u16 *)src; + + len = len / 2; + while (len--) + *d++ = cpu_to_le16p(s++); + } else { + __le32 *d = (__le32 *)dln2_buf; + u32 *s = (u32 *)src; + + len = len / 4; + while (len--) + *d++ = cpu_to_le32p(s++); + } +#endif + + return 0; +} + +/* + * Copy the data from DLN2 buffer and convert to CPU byte order since the DLN2 + * buffer is LE ordered. SPI core makes sure that the data length is a multiple + * of word size. The RX dln2_buf is 2 byte aligned so, for BE, we have to make + * sure we avoid unaligned accesses for 32 bit case. + */ +static int dln2_spi_copy_from_buf(u8 *dest, const u8 *dln2_buf, u16 len, u8 bpw) +{ +#ifdef __LITTLE_ENDIAN + memcpy(dest, dln2_buf, len); +#else + if (bpw <= 8) { + memcpy(dest, dln2_buf, len); + } else if (bpw <= 16) { + u16 *d = (u16 *)dest; + __le16 *s = (__le16 *)dln2_buf; + + len = len / 2; + while (len--) + *d++ = le16_to_cpup(s++); + } else { + u32 *d = (u32 *)dest; + __le32 *s = (__le32 *)dln2_buf; + + len = len / 4; + while (len--) + *d++ = get_unaligned_le32(s++); + } +#endif + + return 0; +} + +/* + * Perform one write operation. + */ +static int dln2_spi_write_one(struct dln2_spi *dln2, const u8 *data, + u16 data_len, u8 attr) +{ + struct { + u8 port; + __le16 size; + u8 attr; + u8 buf[DLN2_SPI_MAX_XFER_SIZE]; + } __packed *tx = dln2->buf; + unsigned tx_len; + + BUILD_BUG_ON(sizeof(*tx) > DLN2_SPI_BUF_SIZE); + + if (data_len > DLN2_SPI_MAX_XFER_SIZE) + return -EINVAL; + + tx->port = dln2->port; + tx->size = cpu_to_le16(data_len); + tx->attr = attr; + + dln2_spi_copy_to_buf(tx->buf, data, data_len, dln2->bpw); + + tx_len = sizeof(*tx) + data_len - DLN2_SPI_MAX_XFER_SIZE; + return dln2_transfer_tx(dln2->pdev, DLN2_SPI_WRITE, tx, tx_len); +} + +/* + * Perform one read operation. + */ +static int dln2_spi_read_one(struct dln2_spi *dln2, u8 *data, + u16 data_len, u8 attr) +{ + int ret; + struct { + u8 port; + __le16 size; + u8 attr; + } __packed tx; + struct { + __le16 size; + u8 buf[DLN2_SPI_MAX_XFER_SIZE]; + } __packed *rx = dln2->buf; + unsigned rx_len = sizeof(*rx); + + BUILD_BUG_ON(sizeof(*rx) > DLN2_SPI_BUF_SIZE); + + if (data_len > DLN2_SPI_MAX_XFER_SIZE) + return -EINVAL; + + tx.port = dln2->port; + tx.size = cpu_to_le16(data_len); + tx.attr = attr; + + ret = dln2_transfer(dln2->pdev, DLN2_SPI_READ, &tx, sizeof(tx), + rx, &rx_len); + if (ret < 0) + return ret; + if (rx_len < sizeof(rx->size) + data_len) + return -EPROTO; + if (le16_to_cpu(rx->size) != data_len) + return -EPROTO; + + dln2_spi_copy_from_buf(data, rx->buf, data_len, dln2->bpw); + + return 0; +} + +/* + * Perform one write & read operation. + */ +static int dln2_spi_read_write_one(struct dln2_spi *dln2, const u8 *tx_data, + u8 *rx_data, u16 data_len, u8 attr) +{ + int ret; + struct { + u8 port; + __le16 size; + u8 attr; + u8 buf[DLN2_SPI_MAX_XFER_SIZE]; + } __packed *tx; + struct { + __le16 size; + u8 buf[DLN2_SPI_MAX_XFER_SIZE]; + } __packed *rx; + unsigned tx_len, rx_len; + + BUILD_BUG_ON(sizeof(*tx) > DLN2_SPI_BUF_SIZE || + sizeof(*rx) > DLN2_SPI_BUF_SIZE); + + if (data_len > DLN2_SPI_MAX_XFER_SIZE) + return -EINVAL; + + /* + * Since this is a pseudo full-duplex communication, we're perfectly + * safe to use the same buffer for both tx and rx. When DLN2 sends the + * response back, with the rx data, we don't need the tx buffer anymore. + */ + tx = dln2->buf; + rx = dln2->buf; + + tx->port = dln2->port; + tx->size = cpu_to_le16(data_len); + tx->attr = attr; + + dln2_spi_copy_to_buf(tx->buf, tx_data, data_len, dln2->bpw); + + tx_len = sizeof(*tx) + data_len - DLN2_SPI_MAX_XFER_SIZE; + rx_len = sizeof(*rx); + + ret = dln2_transfer(dln2->pdev, DLN2_SPI_READ_WRITE, tx, tx_len, + rx, &rx_len); + if (ret < 0) + return ret; + if (rx_len < sizeof(rx->size) + data_len) + return -EPROTO; + if (le16_to_cpu(rx->size) != data_len) + return -EPROTO; + + dln2_spi_copy_from_buf(rx_data, rx->buf, data_len, dln2->bpw); + + return 0; +} + +/* + * Read/Write wrapper. It will automatically split an operation into multiple + * single ones due to device buffer constraints. + */ +static int dln2_spi_rdwr(struct dln2_spi *dln2, const u8 *tx_data, + u8 *rx_data, u16 data_len, u8 attr) { + int ret; + u16 len; + u8 temp_attr; + u16 remaining = data_len; + u16 offset; + + do { + if (remaining > DLN2_SPI_MAX_XFER_SIZE) { + len = DLN2_SPI_MAX_XFER_SIZE; + temp_attr = DLN2_SPI_ATTR_LEAVE_SS_LOW; + } else { + len = remaining; + temp_attr = attr; + } + + offset = data_len - remaining; + + if (tx_data && rx_data) { + ret = dln2_spi_read_write_one(dln2, + tx_data + offset, + rx_data + offset, + len, temp_attr); + } else if (tx_data) { + ret = dln2_spi_write_one(dln2, + tx_data + offset, + len, temp_attr); + } else if (rx_data) { + ret = dln2_spi_read_one(dln2, + rx_data + offset, + len, temp_attr); + } else { + return -EINVAL; + } + + if (ret < 0) + return ret; + + remaining -= len; + } while (remaining); + + return 0; +} + +static int dln2_spi_prepare_message(struct spi_master *master, + struct spi_message *message) +{ + int ret; + struct dln2_spi *dln2 = spi_master_get_devdata(master); + struct spi_device *spi = message->spi; + + if (dln2->cs != spi->chip_select) { + ret = dln2_spi_cs_set_one(dln2, spi->chip_select); + if (ret < 0) + return ret; + + dln2->cs = spi->chip_select; + } + + return 0; +} + +static int dln2_spi_transfer_setup(struct dln2_spi *dln2, u32 speed, + u8 bpw, u8 mode) +{ + int ret; + bool bus_setup_change; + + bus_setup_change = dln2->speed != speed || dln2->mode != mode || + dln2->bpw != bpw; + + if (!bus_setup_change) + return 0; + + ret = dln2_spi_enable(dln2, false); + if (ret < 0) + return ret; + + if (dln2->speed != speed) { + ret = dln2_spi_set_speed(dln2, speed); + if (ret < 0) + return ret; + + dln2->speed = speed; + } + + if (dln2->mode != mode) { + ret = dln2_spi_set_mode(dln2, mode & 0x3); + if (ret < 0) + return ret; + + dln2->mode = mode; + } + + if (dln2->bpw != bpw) { + ret = dln2_spi_set_bpw(dln2, bpw); + if (ret < 0) + return ret; + + dln2->bpw = bpw; + } + + return dln2_spi_enable(dln2, true); +} + +static int dln2_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct dln2_spi *dln2 = spi_master_get_devdata(master); + int status; + u8 attr = 0; + + status = dln2_spi_transfer_setup(dln2, xfer->speed_hz, + xfer->bits_per_word, + spi->mode); + if (status < 0) { + dev_err(&dln2->pdev->dev, "Cannot setup transfer\n"); + return status; + } + + if (!xfer->cs_change && !spi_transfer_is_last(master, xfer)) + attr = DLN2_SPI_ATTR_LEAVE_SS_LOW; + + status = dln2_spi_rdwr(dln2, xfer->tx_buf, xfer->rx_buf, + xfer->len, attr); + if (status < 0) + dev_err(&dln2->pdev->dev, "write/read failed!\n"); + + return status; +} + +static int dln2_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct dln2_spi *dln2; + struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev); + int ret; + + master = spi_alloc_master(&pdev->dev, sizeof(*dln2)); + if (!master) + return -ENOMEM; + + platform_set_drvdata(pdev, master); + + dln2 = spi_master_get_devdata(master); + + dln2->buf = devm_kmalloc(&pdev->dev, DLN2_SPI_BUF_SIZE, GFP_KERNEL); + if (!dln2->buf) { + ret = -ENOMEM; + goto exit_free_master; + } + + dln2->master = master; + dln2->pdev = pdev; + dln2->port = pdata->port; + /* cs/mode can never be 0xff, so the first transfer will set them */ + dln2->cs = 0xff; + dln2->mode = 0xff; + + /* disable SPI module before continuing with the setup */ + ret = dln2_spi_enable(dln2, false); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to disable SPI module\n"); + goto exit_free_master; + } + + ret = dln2_spi_get_cs_num(dln2, &master->num_chipselect); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to get number of CS pins\n"); + goto exit_free_master; + } + + ret = dln2_spi_get_speed_range(dln2, + &master->min_speed_hz, + &master->max_speed_hz); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to read bus min/max freqs\n"); + goto exit_free_master; + } + + ret = dln2_spi_get_supported_frame_sizes(dln2, + &master->bits_per_word_mask); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to read supported frame sizes\n"); + goto exit_free_master; + } + + ret = dln2_spi_cs_enable_all(dln2, true); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to enable CS pins\n"); + goto exit_free_master; + } + + master->bus_num = -1; + master->mode_bits = SPI_CPOL | SPI_CPHA; + master->prepare_message = dln2_spi_prepare_message; + master->transfer_one = dln2_spi_transfer_one; + master->auto_runtime_pm = true; + + /* enable SPI module, we're good to go */ + ret = dln2_spi_enable(dln2, true); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to enable SPI module\n"); + goto exit_free_master; + } + + pm_runtime_set_autosuspend_delay(&pdev->dev, + DLN2_RPM_AUTOSUSPEND_TIMEOUT); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + ret = devm_spi_register_master(&pdev->dev, master); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register master\n"); + goto exit_register; + } + + return ret; + +exit_register: + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + + if (dln2_spi_enable(dln2, false) < 0) + dev_err(&pdev->dev, "Failed to disable SPI module\n"); +exit_free_master: + spi_master_put(master); + + return ret; +} + +static int dln2_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); + struct dln2_spi *dln2 = spi_master_get_devdata(master); + + pm_runtime_disable(&pdev->dev); + + if (dln2_spi_enable(dln2, false) < 0) + dev_err(&pdev->dev, "Failed to disable SPI module\n"); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int dln2_spi_suspend(struct device *dev) +{ + int ret; + struct spi_master *master = dev_get_drvdata(dev); + struct dln2_spi *dln2 = spi_master_get_devdata(master); + + ret = spi_master_suspend(master); + if (ret < 0) + return ret; + + if (!pm_runtime_suspended(dev)) { + ret = dln2_spi_enable(dln2, false); + if (ret < 0) + return ret; + } + + /* + * USB power may be cut off during sleep. Resetting the following + * parameters will force the board to be set up before first transfer. + */ + dln2->cs = 0xff; + dln2->speed = 0; + dln2->bpw = 0; + dln2->mode = 0xff; + + return 0; +} + +static int dln2_spi_resume(struct device *dev) +{ + int ret; + struct spi_master *master = dev_get_drvdata(dev); + struct dln2_spi *dln2 = spi_master_get_devdata(master); + + if (!pm_runtime_suspended(dev)) { + ret = dln2_spi_cs_enable_all(dln2, true); + if (ret < 0) + return ret; + + ret = dln2_spi_enable(dln2, true); + if (ret < 0) + return ret; + } + + return spi_master_resume(master); +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM +static int dln2_spi_runtime_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct dln2_spi *dln2 = spi_master_get_devdata(master); + + return dln2_spi_enable(dln2, false); +} + +static int dln2_spi_runtime_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct dln2_spi *dln2 = spi_master_get_devdata(master); + + return dln2_spi_enable(dln2, true); +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops dln2_spi_pm = { + SET_SYSTEM_SLEEP_PM_OPS(dln2_spi_suspend, dln2_spi_resume) + SET_RUNTIME_PM_OPS(dln2_spi_runtime_suspend, + dln2_spi_runtime_resume, NULL) +}; + +static struct platform_driver spi_dln2_driver = { + .driver = { + .name = "dln2-spi", + .pm = &dln2_spi_pm, + }, + .probe = dln2_spi_probe, + .remove = dln2_spi_remove, +}; +module_platform_driver(spi_dln2_driver); + +MODULE_DESCRIPTION("Driver for the Diolan DLN2 SPI master interface"); +MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:dln2-spi"); diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 7281316a5ecb..a0197fd4e95c 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -247,9 +247,9 @@ static struct dw_spi_dma_ops mid_dma_ops = { /* Some specific info for SPI0 controller on Intel MID */ -/* HW info for MRST CLk Control Unit, one 32b reg */ +/* HW info for MRST Clk Control Unit, 32b reg per controller */ #define MRST_SPI_CLK_BASE 100000000 /* 100m */ -#define MRST_CLK_SPI0_REG 0xff11d86c +#define MRST_CLK_SPI_REG 0xff11d86c #define CLK_SPI_BDIV_OFFSET 0 #define CLK_SPI_BDIV_MASK 0x00000007 #define CLK_SPI_CDIV_OFFSET 9 @@ -261,17 +261,17 @@ int dw_spi_mid_init(struct dw_spi *dws) void __iomem *clk_reg; u32 clk_cdiv; - clk_reg = ioremap_nocache(MRST_CLK_SPI0_REG, 16); + clk_reg = ioremap_nocache(MRST_CLK_SPI_REG, 16); if (!clk_reg) return -ENOMEM; - /* get SPI controller operating freq info */ - clk_cdiv = (readl(clk_reg) & CLK_SPI_CDIV_MASK) >> CLK_SPI_CDIV_OFFSET; + /* Get SPI controller operating freq info */ + clk_cdiv = readl(clk_reg + dws->bus_num * sizeof(u32)); + clk_cdiv &= CLK_SPI_CDIV_MASK; + clk_cdiv >>= CLK_SPI_CDIV_OFFSET; dws->max_freq = MRST_SPI_CLK_BASE / (clk_cdiv + 1); - iounmap(clk_reg); - dws->num_cs = 16; - dws->fifo_len = 40; /* FIFO has 40 words buffer */ + iounmap(clk_reg); #ifdef CONFIG_SPI_DW_MID_DMA dws->dma_priv = kzalloc(sizeof(struct mid_dma), GFP_KERNEL); diff --git a/drivers/spi/spi-dw-pci.c b/drivers/spi/spi-dw-pci.c index ba68da12cdf0..5ba331047cbe 100644 --- a/drivers/spi/spi-dw-pci.c +++ b/drivers/spi/spi-dw-pci.c @@ -30,10 +30,20 @@ struct dw_spi_pci { struct spi_pci_desc { int (*setup)(struct dw_spi *); + u16 num_cs; + u16 bus_num; }; -static struct spi_pci_desc spi_pci_mid_desc = { +static struct spi_pci_desc spi_pci_mid_desc_1 = { .setup = dw_spi_mid_init, + .num_cs = 32, + .bus_num = 0, +}; + +static struct spi_pci_desc spi_pci_mid_desc_2 = { + .setup = dw_spi_mid_init, + .num_cs = 4, + .bus_num = 1, }; static int spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) @@ -65,18 +75,23 @@ static int spi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) dws->regs = pcim_iomap_table(pdev)[pci_bar]; - dws->bus_num = 0; - dws->num_cs = 4; dws->irq = pdev->irq; /* * Specific handling for paltforms, like dma setup, * clock rate, FIFO depth. */ - if (desc && desc->setup) { - ret = desc->setup(dws); - if (ret) - return ret; + if (desc) { + dws->num_cs = desc->num_cs; + dws->bus_num = desc->bus_num; + + if (desc->setup) { + ret = desc->setup(dws); + if (ret) + return ret; + } + } else { + return -ENODEV; } ret = dw_spi_add_host(&pdev->dev, dws); @@ -121,7 +136,14 @@ static SIMPLE_DEV_PM_OPS(dw_spi_pm_ops, spi_suspend, spi_resume); static const struct pci_device_id pci_ids[] = { /* Intel MID platform SPI controller 0 */ - { PCI_VDEVICE(INTEL, 0x0800), (kernel_ulong_t)&spi_pci_mid_desc}, + /* + * The access to the device 8086:0801 is disabled by HW, since it's + * exclusively used by SCU to communicate with MSIC. + */ + /* Intel MID platform SPI controller 1 */ + { PCI_VDEVICE(INTEL, 0x0800), (kernel_ulong_t)&spi_pci_mid_desc_1}, + /* Intel MID platform SPI controller 2 */ + { PCI_VDEVICE(INTEL, 0x0812), (kernel_ulong_t)&spi_pci_mid_desc_2}, {}, }; diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c index d0d5542efc06..5a97a62b298a 100644 --- a/drivers/spi/spi-dw.c +++ b/drivers/spi/spi-dw.c @@ -608,7 +608,7 @@ static void dw_spi_cleanup(struct spi_device *spi) } /* Restart the controller, disable all interrupts, clean rx fifo */ -static void spi_hw_init(struct dw_spi *dws) +static void spi_hw_init(struct device *dev, struct dw_spi *dws) { spi_enable_chip(dws, 0); spi_mask_intr(dws, 0xff); @@ -621,14 +621,15 @@ static void spi_hw_init(struct dw_spi *dws) if (!dws->fifo_len) { u32 fifo; - for (fifo = 2; fifo <= 257; fifo++) { + for (fifo = 2; fifo <= 256; fifo++) { dw_writew(dws, DW_SPI_TXFLTR, fifo); if (fifo != dw_readw(dws, DW_SPI_TXFLTR)) break; } - - dws->fifo_len = (fifo == 257) ? 0 : fifo; dw_writew(dws, DW_SPI_TXFLTR, 0); + + dws->fifo_len = (fifo == 2) ? 0 : fifo - 1; + dev_dbg(dev, "Detected FIFO size: %u bytes\n", dws->fifo_len); } } @@ -668,12 +669,12 @@ int dw_spi_add_host(struct device *dev, struct dw_spi *dws) master->dev.of_node = dev->of_node; /* Basic HW init */ - spi_hw_init(dws); + spi_hw_init(dev, dws); if (dws->dma_ops && dws->dma_ops->dma_init) { ret = dws->dma_ops->dma_init(dws); if (ret) { - dev_warn(&master->dev, "DMA init failed\n"); + dev_warn(dev, "DMA init failed\n"); dws->dma_inited = 0; } } @@ -731,7 +732,7 @@ int dw_spi_resume_host(struct dw_spi *dws) { int ret; - spi_hw_init(dws); + spi_hw_init(&dws->master->dev, dws); ret = spi_master_resume(dws->master); if (ret) dev_err(&dws->master->dev, "fail to start queue (%d)\n", ret); diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c index 912b9037e9cf..286b2c81fc6b 100644 --- a/drivers/spi/spi-falcon.c +++ b/drivers/spi/spi-falcon.c @@ -353,16 +353,6 @@ static int falcon_sflash_setup(struct spi_device *spi) return 0; } -static int falcon_sflash_prepare_xfer(struct spi_master *master) -{ - return 0; -} - -static int falcon_sflash_unprepare_xfer(struct spi_master *master) -{ - return 0; -} - static int falcon_sflash_xfer_one(struct spi_master *master, struct spi_message *m) { @@ -420,9 +410,7 @@ static int falcon_sflash_probe(struct platform_device *pdev) master->mode_bits = SPI_MODE_3; master->flags = SPI_MASTER_HALF_DUPLEX; master->setup = falcon_sflash_setup; - master->prepare_transfer_hardware = falcon_sflash_prepare_xfer; master->transfer_one_message = falcon_sflash_xfer_one; - master->unprepare_transfer_hardware = falcon_sflash_unprepare_xfer; master->dev.of_node = pdev->dev.of_node; ret = devm_spi_register_master(&pdev->dev, master); diff --git a/drivers/spi/spi-fsl-cpm.c b/drivers/spi/spi-fsl-cpm.c index e85ab1cb17a2..9c46a3058743 100644 --- a/drivers/spi/spi-fsl-cpm.c +++ b/drivers/spi/spi-fsl-cpm.c @@ -20,6 +20,7 @@ #include <linux/dma-mapping.h> #include <linux/fsl_devices.h> #include <linux/kernel.h> +#include <linux/module.h> #include <linux/of_address.h> #include <linux/spi/spi.h> #include <linux/types.h> @@ -68,6 +69,7 @@ void fsl_spi_cpm_reinit_txrx(struct mpc8xxx_spi *mspi) } } } +EXPORT_SYMBOL_GPL(fsl_spi_cpm_reinit_txrx); static void fsl_spi_cpm_bufs_start(struct mpc8xxx_spi *mspi) { @@ -162,6 +164,7 @@ err_rx_dma: dma_unmap_single(dev, mspi->tx_dma, t->len, DMA_TO_DEVICE); return -ENOMEM; } +EXPORT_SYMBOL_GPL(fsl_spi_cpm_bufs); void fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi) { @@ -174,6 +177,7 @@ void fsl_spi_cpm_bufs_complete(struct mpc8xxx_spi *mspi) dma_unmap_single(dev, mspi->rx_dma, t->len, DMA_FROM_DEVICE); mspi->xfer_in_progress = NULL; } +EXPORT_SYMBOL_GPL(fsl_spi_cpm_bufs_complete); void fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events) { @@ -198,6 +202,7 @@ void fsl_spi_cpm_irq(struct mpc8xxx_spi *mspi, u32 events) else complete(&mspi->done); } +EXPORT_SYMBOL_GPL(fsl_spi_cpm_irq); static void *fsl_spi_alloc_dummy_rx(void) { @@ -375,6 +380,7 @@ err_pram: fsl_spi_free_dummy_rx(); return -ENOMEM; } +EXPORT_SYMBOL_GPL(fsl_spi_cpm_init); void fsl_spi_cpm_free(struct mpc8xxx_spi *mspi) { @@ -389,3 +395,6 @@ void fsl_spi_cpm_free(struct mpc8xxx_spi *mspi) cpm_muram_free(cpm_muram_offset(mspi->pram)); fsl_spi_free_dummy_rx(); } +EXPORT_SYMBOL_GPL(fsl_spi_cpm_free); + +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 4cda994d3f40..d1a39249704a 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -106,7 +106,7 @@ struct chip_data { }; struct fsl_dspi { - struct spi_bitbang bitbang; + struct spi_master *master; struct platform_device *pdev; struct regmap *regmap; @@ -114,6 +114,7 @@ struct fsl_dspi { struct clk *clk; struct spi_transfer *cur_transfer; + struct spi_message *cur_msg; struct chip_data *cur_chip; size_t len; void *tx; @@ -123,6 +124,7 @@ struct fsl_dspi { char dataflags; u8 cs; u16 void_write_data; + u32 cs_change; wait_queue_head_t waitq; u32 waitflags; @@ -225,6 +227,8 @@ static int dspi_transfer_write(struct fsl_dspi *dspi) if (dspi->len == 0 || tx_count == DSPI_FIFO_SIZE - 1) { /* last transfer in the transfer */ dspi_pushr |= SPI_PUSHR_EOQ; + if ((dspi->cs_change) && (!dspi->len)) + dspi_pushr &= ~SPI_PUSHR_CONT; } else if (tx_word && (dspi->len == 1)) dspi_pushr |= SPI_PUSHR_EOQ; @@ -246,6 +250,7 @@ static int dspi_transfer_read(struct fsl_dspi *dspi) int rx_count = 0; int rx_word = is_double_byte_mode(dspi); u16 d; + while ((dspi->rx < dspi->rx_end) && (rx_count < DSPI_FIFO_SIZE)) { if (rx_word) { @@ -276,86 +281,89 @@ static int dspi_transfer_read(struct fsl_dspi *dspi) return rx_count; } -static int dspi_txrx_transfer(struct spi_device *spi, struct spi_transfer *t) +static int dspi_transfer_one_message(struct spi_master *master, + struct spi_message *message) { - struct fsl_dspi *dspi = spi_master_get_devdata(spi->master); - dspi->cur_transfer = t; - dspi->cur_chip = spi_get_ctldata(spi); - dspi->cs = spi->chip_select; - dspi->void_write_data = dspi->cur_chip->void_write_data; - - dspi->dataflags = 0; - dspi->tx = (void *)t->tx_buf; - dspi->tx_end = dspi->tx + t->len; - dspi->rx = t->rx_buf; - dspi->rx_end = dspi->rx + t->len; - dspi->len = t->len; - - if (!dspi->rx) - dspi->dataflags |= TRAN_STATE_RX_VOID; - - if (!dspi->tx) - dspi->dataflags |= TRAN_STATE_TX_VOID; - - regmap_write(dspi->regmap, SPI_MCR, dspi->cur_chip->mcr_val); - regmap_write(dspi->regmap, SPI_CTAR(dspi->cs), dspi->cur_chip->ctar_val); - regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE); - - if (t->speed_hz) + struct fsl_dspi *dspi = spi_master_get_devdata(master); + struct spi_device *spi = message->spi; + struct spi_transfer *transfer; + int status = 0; + message->actual_length = 0; + + list_for_each_entry(transfer, &message->transfers, transfer_list) { + dspi->cur_transfer = transfer; + dspi->cur_msg = message; + dspi->cur_chip = spi_get_ctldata(spi); + dspi->cs = spi->chip_select; + if (dspi->cur_transfer->transfer_list.next + == &dspi->cur_msg->transfers) + transfer->cs_change = 1; + dspi->cs_change = transfer->cs_change; + dspi->void_write_data = dspi->cur_chip->void_write_data; + + dspi->dataflags = 0; + dspi->tx = (void *)transfer->tx_buf; + dspi->tx_end = dspi->tx + transfer->len; + dspi->rx = transfer->rx_buf; + dspi->rx_end = dspi->rx + transfer->len; + dspi->len = transfer->len; + + if (!dspi->rx) + dspi->dataflags |= TRAN_STATE_RX_VOID; + + if (!dspi->tx) + dspi->dataflags |= TRAN_STATE_TX_VOID; + + regmap_write(dspi->regmap, SPI_MCR, dspi->cur_chip->mcr_val); + regmap_update_bits(dspi->regmap, SPI_MCR, + SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF, + SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF); regmap_write(dspi->regmap, SPI_CTAR(dspi->cs), dspi->cur_chip->ctar_val); + if (transfer->speed_hz) + regmap_write(dspi->regmap, SPI_CTAR(dspi->cs), + dspi->cur_chip->ctar_val); - dspi_transfer_write(dspi); + regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_EOQFE); + message->actual_length += dspi_transfer_write(dspi); - if (wait_event_interruptible(dspi->waitq, dspi->waitflags)) - dev_err(&dspi->pdev->dev, "wait transfer complete fail!\n"); - dspi->waitflags = 0; - - return t->len - dspi->len; -} + if (wait_event_interruptible(dspi->waitq, dspi->waitflags)) + dev_err(&dspi->pdev->dev, "wait transfer complete fail!\n"); + dspi->waitflags = 0; -static void dspi_chipselect(struct spi_device *spi, int value) -{ - struct fsl_dspi *dspi = spi_master_get_devdata(spi->master); - unsigned int pushr; - - regmap_read(dspi->regmap, SPI_PUSHR, &pushr); - - switch (value) { - case BITBANG_CS_ACTIVE: - pushr |= SPI_PUSHR_CONT; - break; - case BITBANG_CS_INACTIVE: - pushr &= ~SPI_PUSHR_CONT; - break; + if (transfer->delay_usecs) + udelay(transfer->delay_usecs); } - regmap_write(dspi->regmap, SPI_PUSHR, pushr); + message->status = status; + spi_finalize_current_message(master); + + return status; } -static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) +static int dspi_setup(struct spi_device *spi) { struct chip_data *chip; struct fsl_dspi *dspi = spi_master_get_devdata(spi->master); unsigned char br = 0, pbr = 0, fmsz = 0; + if ((spi->bits_per_word >= 4) && (spi->bits_per_word <= 16)) { + fmsz = spi->bits_per_word - 1; + } else { + pr_err("Invalid wordsize\n"); + return -ENODEV; + } + /* Only alloc on first setup */ chip = spi_get_ctldata(spi); if (chip == NULL) { - chip = devm_kzalloc(&spi->dev, sizeof(struct chip_data), - GFP_KERNEL); + chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); if (!chip) return -ENOMEM; } chip->mcr_val = SPI_MCR_MASTER | SPI_MCR_PCSIS | SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF; - if ((spi->bits_per_word >= 4) && (spi->bits_per_word <= 16)) { - fmsz = spi->bits_per_word - 1; - } else { - pr_err("Invalid wordsize\n"); - return -ENODEV; - } chip->void_write_data = 0; @@ -374,34 +382,34 @@ static int dspi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) return 0; } -static int dspi_setup(struct spi_device *spi) +static void dspi_cleanup(struct spi_device *spi) { - if (!spi->max_speed_hz) - return -EINVAL; + struct chip_data *chip = spi_get_ctldata((struct spi_device *)spi); + + dev_dbg(&spi->dev, "spi_device %u.%u cleanup\n", + spi->master->bus_num, spi->chip_select); - return dspi_setup_transfer(spi, NULL); + kfree(chip); } static irqreturn_t dspi_interrupt(int irq, void *dev_id) { struct fsl_dspi *dspi = (struct fsl_dspi *)dev_id; - regmap_write(dspi->regmap, SPI_SR, SPI_SR_EOQF); + struct spi_message *msg = dspi->cur_msg; + regmap_write(dspi->regmap, SPI_SR, SPI_SR_EOQF); dspi_transfer_read(dspi); if (!dspi->len) { if (dspi->dataflags & TRAN_STATE_WORD_ODD_NUM) regmap_update_bits(dspi->regmap, SPI_CTAR(dspi->cs), - SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(16)); + SPI_FRAME_BITS_MASK, SPI_FRAME_BITS(16)); dspi->waitflags = 1; wake_up_interruptible(&dspi->waitq); - } else { - dspi_transfer_write(dspi); - - return IRQ_HANDLED; - } + } else + msg->actual_length += dspi_transfer_write(dspi); return IRQ_HANDLED; } @@ -460,13 +468,14 @@ static int dspi_probe(struct platform_device *pdev) dspi = spi_master_get_devdata(master); dspi->pdev = pdev; - dspi->bitbang.master = master; - dspi->bitbang.chipselect = dspi_chipselect; - dspi->bitbang.setup_transfer = dspi_setup_transfer; - dspi->bitbang.txrx_bufs = dspi_txrx_transfer; - dspi->bitbang.master->setup = dspi_setup; - dspi->bitbang.master->dev.of_node = pdev->dev.of_node; + dspi->master = master; + + master->transfer = NULL; + master->setup = dspi_setup; + master->transfer_one_message = dspi_transfer_one_message; + master->dev.of_node = pdev->dev.of_node; + master->cleanup = dspi_cleanup; master->mode_bits = SPI_CPOL | SPI_CPHA; master->bits_per_word_mask = SPI_BPW_MASK(4) | SPI_BPW_MASK(8) | SPI_BPW_MASK(16); @@ -525,7 +534,7 @@ static int dspi_probe(struct platform_device *pdev) init_waitqueue_head(&dspi->waitq); platform_set_drvdata(pdev, master); - ret = spi_bitbang_start(&dspi->bitbang); + ret = spi_register_master(master); if (ret != 0) { dev_err(&pdev->dev, "Problem registering DSPI master\n"); goto out_clk_put; @@ -547,9 +556,9 @@ static int dspi_remove(struct platform_device *pdev) struct fsl_dspi *dspi = spi_master_get_devdata(master); /* Disconnect from the SPI framework */ - spi_bitbang_stop(&dspi->bitbang); clk_disable_unprepare(dspi->clk); - spi_master_put(dspi->bitbang.master); + spi_unregister_master(dspi->master); + spi_master_put(dspi->master); return 0; } diff --git a/drivers/spi/spi-fsl-lib.c b/drivers/spi/spi-fsl-lib.c index 446b737e1532..cb35d2f0d0e6 100644 --- a/drivers/spi/spi-fsl-lib.c +++ b/drivers/spi/spi-fsl-lib.c @@ -21,6 +21,7 @@ #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/mm.h> +#include <linux/module.h> #include <linux/of_platform.h> #include <linux/spi/spi.h> #ifdef CONFIG_FSL_SOC @@ -35,7 +36,8 @@ void mpc8xxx_spi_rx_buf_##type(u32 data, struct mpc8xxx_spi *mpc8xxx_spi) \ type *rx = mpc8xxx_spi->rx; \ *rx++ = (type)(data >> mpc8xxx_spi->rx_shift); \ mpc8xxx_spi->rx = rx; \ -} +} \ +EXPORT_SYMBOL_GPL(mpc8xxx_spi_rx_buf_##type); #define MPC8XXX_SPI_TX_BUF(type) \ u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi) \ @@ -47,7 +49,8 @@ u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi) \ data = *tx++ << mpc8xxx_spi->tx_shift; \ mpc8xxx_spi->tx = tx; \ return data; \ -} +} \ +EXPORT_SYMBOL_GPL(mpc8xxx_spi_tx_buf_##type); MPC8XXX_SPI_RX_BUF(u8) MPC8XXX_SPI_RX_BUF(u16) @@ -60,6 +63,7 @@ struct mpc8xxx_spi_probe_info *to_of_pinfo(struct fsl_spi_platform_data *pdata) { return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata); } +EXPORT_SYMBOL_GPL(to_of_pinfo); const char *mpc8xxx_spi_strmode(unsigned int flags) { @@ -75,6 +79,7 @@ const char *mpc8xxx_spi_strmode(unsigned int flags) } return "CPU"; } +EXPORT_SYMBOL_GPL(mpc8xxx_spi_strmode); void mpc8xxx_spi_probe(struct device *dev, struct resource *mem, unsigned int irq) @@ -102,13 +107,12 @@ void mpc8xxx_spi_probe(struct device *dev, struct resource *mem, mpc8xxx_spi->rx_shift = 0; mpc8xxx_spi->tx_shift = 0; - init_completion(&mpc8xxx_spi->done); - master->bus_num = pdata->bus_num; master->num_chipselect = pdata->max_chipselect; init_completion(&mpc8xxx_spi->done); } +EXPORT_SYMBOL_GPL(mpc8xxx_spi_probe); int mpc8xxx_spi_remove(struct device *dev) { @@ -127,6 +131,7 @@ int mpc8xxx_spi_remove(struct device *dev) return 0; } +EXPORT_SYMBOL_GPL(mpc8xxx_spi_remove); int of_mpc8xxx_spi_probe(struct platform_device *ofdev) { @@ -173,3 +178,6 @@ int of_mpc8xxx_spi_probe(struct platform_device *ofdev) return 0; } +EXPORT_SYMBOL_GPL(of_mpc8xxx_spi_probe); + +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-fsl-lib.h b/drivers/spi/spi-fsl-lib.h index b4ed04e8862f..1326a392adca 100644 --- a/drivers/spi/spi-fsl-lib.h +++ b/drivers/spi/spi-fsl-lib.h @@ -28,7 +28,7 @@ struct mpc8xxx_spi { /* rx & tx bufs from the spi_transfer */ const void *tx; void *rx; -#ifdef CONFIG_SPI_FSL_ESPI +#if IS_ENABLED(CONFIG_SPI_FSL_ESPI) int len; #endif @@ -68,7 +68,7 @@ struct mpc8xxx_spi { unsigned int flags; -#ifdef CONFIG_SPI_FSL_SPI +#if IS_ENABLED(CONFIG_SPI_FSL_SPI) int type; int native_chipselects; u8 max_bits_per_word; diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index aee4e7589568..1c34c9314c8a 100644 --- a/drivers/spi/spi-gpio.c +++ b/drivers/spi/spi-gpio.c @@ -12,10 +12,6 @@ * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/kernel.h> #include <linux/module.h> @@ -92,7 +88,7 @@ struct spi_gpio { /*----------------------------------------------------------------------*/ -static inline struct spi_gpio * __pure +static inline struct spi_gpio *__pure spi_to_spi_gpio(const struct spi_device *spi) { const struct spi_bitbang *bang; @@ -103,7 +99,7 @@ spi_to_spi_gpio(const struct spi_device *spi) return spi_gpio; } -static inline struct spi_gpio_platform_data * __pure +static inline struct spi_gpio_platform_data *__pure spi_to_pdata(const struct spi_device *spi) { return &spi_to_spi_gpio(spi)->pdata; diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c index aad6683db81b..c01567d53581 100644 --- a/drivers/spi/spi-img-spfi.c +++ b/drivers/spi/spi-img-spfi.c @@ -160,16 +160,16 @@ static unsigned int spfi_pio_write32(struct img_spfi *spfi, const u32 *buf, unsigned int count = 0; u32 status; - while (count < max) { + while (count < max / 4) { spfi_writel(spfi, SPFI_INTERRUPT_SDFUL, SPFI_INTERRUPT_CLEAR); status = spfi_readl(spfi, SPFI_INTERRUPT_STATUS); if (status & SPFI_INTERRUPT_SDFUL) break; - spfi_writel(spfi, buf[count / 4], SPFI_TX_32BIT_VALID_DATA); - count += 4; + spfi_writel(spfi, buf[count], SPFI_TX_32BIT_VALID_DATA); + count++; } - return count; + return count * 4; } static unsigned int spfi_pio_write8(struct img_spfi *spfi, const u8 *buf, @@ -196,17 +196,17 @@ static unsigned int spfi_pio_read32(struct img_spfi *spfi, u32 *buf, unsigned int count = 0; u32 status; - while (count < max) { + while (count < max / 4) { spfi_writel(spfi, SPFI_INTERRUPT_GDEX32BIT, SPFI_INTERRUPT_CLEAR); status = spfi_readl(spfi, SPFI_INTERRUPT_STATUS); if (!(status & SPFI_INTERRUPT_GDEX32BIT)) break; - buf[count / 4] = spfi_readl(spfi, SPFI_RX_32BIT_VALID_DATA); - count += 4; + buf[count] = spfi_readl(spfi, SPFI_RX_32BIT_VALID_DATA); + count++; } - return count; + return count * 4; } static unsigned int spfi_pio_read8(struct img_spfi *spfi, u8 *buf, @@ -251,17 +251,15 @@ static int img_spfi_start_pio(struct spi_master *master, time_before(jiffies, timeout)) { unsigned int tx_count, rx_count; - switch (xfer->bits_per_word) { - case 32: + if (tx_bytes >= 4) tx_count = spfi_pio_write32(spfi, tx_buf, tx_bytes); - rx_count = spfi_pio_read32(spfi, rx_buf, rx_bytes); - break; - case 8: - default: + else tx_count = spfi_pio_write8(spfi, tx_buf, tx_bytes); + + if (rx_bytes >= 4) + rx_count = spfi_pio_read32(spfi, rx_buf, rx_bytes); + else rx_count = spfi_pio_read8(spfi, rx_buf, rx_bytes); - break; - } tx_buf += tx_count; rx_buf += rx_count; @@ -331,14 +329,11 @@ static int img_spfi_start_dma(struct spi_master *master, if (xfer->rx_buf) { rxconf.direction = DMA_DEV_TO_MEM; - switch (xfer->bits_per_word) { - case 32: + if (xfer->len % 4 == 0) { rxconf.src_addr = spfi->phys + SPFI_RX_32BIT_VALID_DATA; rxconf.src_addr_width = 4; rxconf.src_maxburst = 4; - break; - case 8: - default: + } else { rxconf.src_addr = spfi->phys + SPFI_RX_8BIT_VALID_DATA; rxconf.src_addr_width = 1; rxconf.src_maxburst = 4; @@ -358,18 +353,14 @@ static int img_spfi_start_dma(struct spi_master *master, if (xfer->tx_buf) { txconf.direction = DMA_MEM_TO_DEV; - switch (xfer->bits_per_word) { - case 32: + if (xfer->len % 4 == 0) { txconf.dst_addr = spfi->phys + SPFI_TX_32BIT_VALID_DATA; txconf.dst_addr_width = 4; txconf.dst_maxburst = 4; - break; - case 8: - default: + } else { txconf.dst_addr = spfi->phys + SPFI_TX_8BIT_VALID_DATA; txconf.dst_addr_width = 1; txconf.dst_maxburst = 4; - break; } dmaengine_slave_config(spfi->tx_ch, &txconf); @@ -508,9 +499,7 @@ static void img_spfi_set_cs(struct spi_device *spi, bool enable) static bool img_spfi_can_dma(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { - if (xfer->bits_per_word == 8 && xfer->len > SPFI_8BIT_FIFO_SIZE) - return true; - if (xfer->bits_per_word == 32 && xfer->len > SPFI_32BIT_FIFO_SIZE) + if (xfer->len > SPFI_32BIT_FIFO_SIZE) return true; return false; } diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 961b97d43b43..6fea4af51c41 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -89,7 +89,6 @@ struct spi_imx_data { struct completion xfer_done; void __iomem *base; - int irq; struct clk *clk_per; struct clk *clk_ipg; unsigned long spi_clk; @@ -823,6 +822,10 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx, struct dma_slave_config slave_config = {}; int ret; + /* use pio mode for i.mx6dl chip TKT238285 */ + if (of_machine_is_compatible("fsl,imx6dl")) + return 0; + /* Prepare for TX DMA: */ master->dma_tx = dma_request_slave_channel(dev, "tx"); if (!master->dma_tx) { @@ -892,6 +895,7 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx, { struct dma_async_tx_descriptor *desc_tx = NULL, *desc_rx = NULL; int ret; + unsigned long timeout; u32 dma; int left; struct spi_master *master = spi_imx->bitbang.master; @@ -939,17 +943,17 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx, dma_async_issue_pending(master->dma_tx); dma_async_issue_pending(master->dma_rx); /* Wait SDMA to finish the data transfer.*/ - ret = wait_for_completion_timeout(&spi_imx->dma_tx_completion, + timeout = wait_for_completion_timeout(&spi_imx->dma_tx_completion, IMX_DMA_TIMEOUT); - if (!ret) { + if (!timeout) { pr_warn("%s %s: I/O Error in DMA TX\n", dev_driver_string(&master->dev), dev_name(&master->dev)); dmaengine_terminate_all(master->dma_tx); } else { - ret = wait_for_completion_timeout(&spi_imx->dma_rx_completion, - IMX_DMA_TIMEOUT); - if (!ret) { + timeout = wait_for_completion_timeout( + &spi_imx->dma_rx_completion, IMX_DMA_TIMEOUT); + if (!timeout) { pr_warn("%s %s: I/O Error in DMA RX\n", dev_driver_string(&master->dev), dev_name(&master->dev)); @@ -964,9 +968,9 @@ static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx, spi_imx->dma_finished = 1; spi_imx->devtype_data->trigger(spi_imx); - if (!ret) + if (!timeout) ret = -ETIMEDOUT; - else if (ret > 0) + else ret = transfer->len; return ret; @@ -1076,7 +1080,7 @@ static int spi_imx_probe(struct platform_device *pdev) struct spi_master *master; struct spi_imx_data *spi_imx; struct resource *res; - int i, ret, num_cs; + int i, ret, num_cs, irq; if (!np && !mxc_platform_info) { dev_err(&pdev->dev, "can't get the platform data\n"); @@ -1143,16 +1147,16 @@ static int spi_imx_probe(struct platform_device *pdev) goto out_master_put; } - spi_imx->irq = platform_get_irq(pdev, 0); - if (spi_imx->irq < 0) { - ret = spi_imx->irq; + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = irq; goto out_master_put; } - ret = devm_request_irq(&pdev->dev, spi_imx->irq, spi_imx_isr, 0, + ret = devm_request_irq(&pdev->dev, irq, spi_imx_isr, 0, dev_name(&pdev->dev), spi_imx); if (ret) { - dev_err(&pdev->dev, "can't get irq%d: %d\n", spi_imx->irq, ret); + dev_err(&pdev->dev, "can't get irq%d: %d\n", irq, ret); goto out_master_put; } diff --git a/drivers/spi/spi-lm70llp.c b/drivers/spi/spi-lm70llp.c index 41c5765be746..ba72347cb99d 100644 --- a/drivers/spi/spi-lm70llp.c +++ b/drivers/spi/spi-lm70llp.c @@ -12,10 +12,6 @@ * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/init.h> diff --git a/drivers/spi/spi-meson-spifc.c b/drivers/spi/spi-meson-spifc.c index 1bbac0378bf7..5468fc70dbf8 100644 --- a/drivers/spi/spi-meson-spifc.c +++ b/drivers/spi/spi-meson-spifc.c @@ -85,7 +85,7 @@ struct meson_spifc { struct device *dev; }; -static struct regmap_config spifc_regmap_config = { +static const struct regmap_config spifc_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, diff --git a/drivers/spi/spi-mxs.c b/drivers/spi/spi-mxs.c index 4045a1e580e1..5b0e9a3e83f6 100644 --- a/drivers/spi/spi-mxs.c +++ b/drivers/spi/spi-mxs.c @@ -282,9 +282,8 @@ static int mxs_spi_txrx_dma(struct mxs_spi *spi, dmaengine_submit(desc); dma_async_issue_pending(ssp->dmach); - ret = wait_for_completion_timeout(&spi->c, - msecs_to_jiffies(SSP_TIMEOUT)); - if (!ret) { + if (!wait_for_completion_timeout(&spi->c, + msecs_to_jiffies(SSP_TIMEOUT))) { dev_err(ssp->dev, "DMA transfer timeout\n"); ret = -ETIMEDOUT; dmaengine_terminate_all(ssp->dmach); diff --git a/drivers/spi/spi-omap-100k.c b/drivers/spi/spi-omap-100k.c index 79399ae9c84c..d890d309dff9 100644 --- a/drivers/spi/spi-omap-100k.c +++ b/drivers/spi/spi-omap-100k.c @@ -16,11 +16,6 @@ * 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 - * */ #include <linux/kernel.h> #include <linux/init.h> diff --git a/drivers/spi/spi-omap-uwire.c b/drivers/spi/spi-omap-uwire.c index daf1ada5cd11..3c0844457c07 100644 --- a/drivers/spi/spi-omap-uwire.c +++ b/drivers/spi/spi-omap-uwire.c @@ -28,10 +28,6 @@ * 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. - * - * 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., - * 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/kernel.h> #include <linux/init.h> diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 3bc3cbabbbc0..4df8942058de 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -14,11 +14,6 @@ * 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 - * */ #include <linux/kernel.h> diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index 3dec9e0b99b8..861664776672 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -28,7 +28,12 @@ /* Runtime PM autosuspend timeout: PM is fairly light on this driver */ #define SPI_AUTOSUSPEND_TIMEOUT 200 -#define ORION_NUM_CHIPSELECTS 1 /* only one slave is supported*/ +/* Some SoCs using this driver support up to 8 chip selects. + * It is up to the implementer to only use the chip selects + * that are available. + */ +#define ORION_NUM_CHIPSELECTS 8 + #define ORION_SPI_WAIT_RDY_MAX_LOOP 2000 /* in usec */ #define ORION_SPI_IF_CTRL_REG 0x00 @@ -44,6 +49,10 @@ #define ARMADA_SPI_CLK_PRESCALE_MASK 0xDF #define ORION_SPI_MODE_MASK (ORION_SPI_MODE_CPOL | \ ORION_SPI_MODE_CPHA) +#define ORION_SPI_CS_MASK 0x1C +#define ORION_SPI_CS_SHIFT 2 +#define ORION_SPI_CS(cs) ((cs << ORION_SPI_CS_SHIFT) & \ + ORION_SPI_CS_MASK) enum orion_spi_type { ORION_SPI, @@ -215,9 +224,18 @@ orion_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) return 0; } -static void orion_spi_set_cs(struct orion_spi *orion_spi, int enable) +static void orion_spi_set_cs(struct spi_device *spi, bool enable) { - if (enable) + struct orion_spi *orion_spi; + + orion_spi = spi_master_get_devdata(spi->master); + + orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, ORION_SPI_CS_MASK); + orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG, + ORION_SPI_CS(spi->chip_select)); + + /* Chip select logic is inverted from spi_set_cs */ + if (!enable) orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1); else orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1); @@ -332,64 +350,31 @@ out: return xfer->len - count; } -static int orion_spi_transfer_one_message(struct spi_master *master, - struct spi_message *m) +static int orion_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) { - struct orion_spi *orion_spi = spi_master_get_devdata(master); - struct spi_device *spi = m->spi; - struct spi_transfer *t = NULL; - int par_override = 0; int status = 0; - int cs_active = 0; - - /* Load defaults */ - status = orion_spi_setup_transfer(spi, NULL); + status = orion_spi_setup_transfer(spi, t); if (status < 0) - goto msg_done; - - list_for_each_entry(t, &m->transfers, transfer_list) { - if (par_override || t->speed_hz || t->bits_per_word) { - par_override = 1; - status = orion_spi_setup_transfer(spi, t); - if (status < 0) - break; - if (!t->speed_hz && !t->bits_per_word) - par_override = 0; - } - - if (!cs_active) { - orion_spi_set_cs(orion_spi, 1); - cs_active = 1; - } + return status; - if (t->len) - m->actual_length += orion_spi_write_read(spi, t); + if (t->len) + orion_spi_write_read(spi, t); - if (t->delay_usecs) - udelay(t->delay_usecs); - - if (t->cs_change) { - orion_spi_set_cs(orion_spi, 0); - cs_active = 0; - } - } - -msg_done: - if (cs_active) - orion_spi_set_cs(orion_spi, 0); - - m->status = status; - spi_finalize_current_message(master); + return status; +} - return 0; +static int orion_spi_setup(struct spi_device *spi) +{ + return orion_spi_setup_transfer(spi, NULL); } static int orion_spi_reset(struct orion_spi *orion_spi) { /* Verify that the CS is deasserted */ - orion_spi_set_cs(orion_spi, 0); - + orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1); return 0; } @@ -442,9 +427,10 @@ static int orion_spi_probe(struct platform_device *pdev) /* we support only mode 0, and no options */ master->mode_bits = SPI_CPHA | SPI_CPOL; - - master->transfer_one_message = orion_spi_transfer_one_message; + master->set_cs = orion_spi_set_cs; + master->transfer_one = orion_spi_transfer_one; master->num_chipselect = ORION_NUM_CHIPSELECTS; + master->setup = orion_spi_setup; master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); master->auto_runtime_pm = true; diff --git a/drivers/spi/spi-pxa2xx-dma.c b/drivers/spi/spi-pxa2xx-dma.c index 62a9297e96ac..66a173939be8 100644 --- a/drivers/spi/spi-pxa2xx-dma.c +++ b/drivers/spi/spi-pxa2xx-dma.c @@ -111,23 +111,24 @@ static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data, * by using ->dma_running. */ if (atomic_dec_and_test(&drv_data->dma_running)) { - void __iomem *reg = drv_data->ioaddr; - /* * If the other CPU is still handling the ROR interrupt we * might not know about the error yet. So we re-check the * ROR bit here before we clear the status register. */ if (!error) { - u32 status = read_SSSR(reg) & drv_data->mask_sr; + u32 status = pxa2xx_spi_read(drv_data, SSSR) + & drv_data->mask_sr; error = status & SSSR_ROR; } /* Clear status & disable interrupts */ - write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); + pxa2xx_spi_write(drv_data, SSCR1, + pxa2xx_spi_read(drv_data, SSCR1) + & ~drv_data->dma_cr1); write_SSSR_CS(drv_data, drv_data->clear_sr); if (!pxa25x_ssp_comp(drv_data)) - write_SSTO(0, reg); + pxa2xx_spi_write(drv_data, SSTO, 0); if (!error) { pxa2xx_spi_unmap_dma_buffers(drv_data); @@ -139,7 +140,9 @@ static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data, msg->state = pxa2xx_spi_next_transfer(drv_data); } else { /* In case we got an error we disable the SSP now */ - write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); + pxa2xx_spi_write(drv_data, SSCR0, + pxa2xx_spi_read(drv_data, SSCR0) + & ~SSCR0_SSE); msg->state = ERROR_STATE; } @@ -247,7 +250,7 @@ irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data) { u32 status; - status = read_SSSR(drv_data->ioaddr) & drv_data->mask_sr; + status = pxa2xx_spi_read(drv_data, SSSR) & drv_data->mask_sr; if (status & SSSR_ROR) { dev_err(&drv_data->pdev->dev, "FIFO overrun\n"); diff --git a/drivers/spi/spi-pxa2xx-pxadma.c b/drivers/spi/spi-pxa2xx-pxadma.c index e8a26f25d5c0..2e0796a0003f 100644 --- a/drivers/spi/spi-pxa2xx-pxadma.c +++ b/drivers/spi/spi-pxa2xx-pxadma.c @@ -12,10 +12,6 @@ * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/delay.h> @@ -25,6 +21,7 @@ #include <linux/spi/spi.h> #include <linux/spi/pxa2xx_spi.h> +#include <mach/dma.h> #include "spi-pxa2xx.h" #define DMA_INT_MASK (DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR) @@ -118,11 +115,11 @@ static void pxa2xx_spi_unmap_dma_buffers(struct driver_data *drv_data) drv_data->dma_mapped = 0; } -static int wait_ssp_rx_stall(void const __iomem *ioaddr) +static int wait_ssp_rx_stall(struct driver_data *drv_data) { unsigned long limit = loops_per_jiffy << 1; - while ((read_SSSR(ioaddr) & SSSR_BSY) && --limit) + while ((pxa2xx_spi_read(drv_data, SSSR) & SSSR_BSY) && --limit) cpu_relax(); return limit; @@ -141,17 +138,18 @@ static int wait_dma_channel_stop(int channel) static void pxa2xx_spi_dma_error_stop(struct driver_data *drv_data, const char *msg) { - void __iomem *reg = drv_data->ioaddr; - /* Stop and reset */ DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; write_SSSR_CS(drv_data, drv_data->clear_sr); - write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); + pxa2xx_spi_write(drv_data, SSCR1, + pxa2xx_spi_read(drv_data, SSCR1) + & ~drv_data->dma_cr1); if (!pxa25x_ssp_comp(drv_data)) - write_SSTO(0, reg); + pxa2xx_spi_write(drv_data, SSTO, 0); pxa2xx_spi_flush(drv_data); - write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); + pxa2xx_spi_write(drv_data, SSCR0, + pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE); pxa2xx_spi_unmap_dma_buffers(drv_data); @@ -163,11 +161,12 @@ static void pxa2xx_spi_dma_error_stop(struct driver_data *drv_data, static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; struct spi_message *msg = drv_data->cur_msg; /* Clear and disable interrupts on SSP and DMA channels*/ - write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); + pxa2xx_spi_write(drv_data, SSCR1, + pxa2xx_spi_read(drv_data, SSCR1) + & ~drv_data->dma_cr1); write_SSSR_CS(drv_data, drv_data->clear_sr); DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; @@ -228,7 +227,7 @@ void pxa2xx_spi_dma_handler(int channel, void *data) && (drv_data->ssp_type == PXA25x_SSP)) { /* Wait for rx to stall */ - if (wait_ssp_rx_stall(drv_data->ioaddr) == 0) + if (wait_ssp_rx_stall(drv_data) == 0) dev_err(&drv_data->pdev->dev, "dma_handler: ssp rx stall failed\n"); @@ -240,9 +239,8 @@ void pxa2xx_spi_dma_handler(int channel, void *data) irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data) { u32 irq_status; - void __iomem *reg = drv_data->ioaddr; - irq_status = read_SSSR(reg) & drv_data->mask_sr; + irq_status = pxa2xx_spi_read(drv_data, SSSR) & drv_data->mask_sr; if (irq_status & SSSR_ROR) { pxa2xx_spi_dma_error_stop(drv_data, "dma_transfer: fifo overrun"); @@ -252,7 +250,7 @@ irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data) /* Check for false positive timeout */ if ((irq_status & SSSR_TINT) && (DCSR(drv_data->tx_channel) & DCSR_RUN)) { - write_SSSR(SSSR_TINT, reg); + pxa2xx_spi_write(drv_data, SSSR, SSSR_TINT); return IRQ_HANDLED; } @@ -261,7 +259,7 @@ irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data) /* Clear and disable timeout interrupt, do the rest in * dma_transfer_complete */ if (!pxa25x_ssp_comp(drv_data)) - write_SSTO(0, reg); + pxa2xx_spi_write(drv_data, SSTO, 0); /* finish this transfer, start the next */ pxa2xx_spi_dma_transfer_complete(drv_data); diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index 05c623cfb078..6f72ad01e041 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -11,10 +11,6 @@ * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/init.h> @@ -45,8 +41,6 @@ MODULE_DESCRIPTION("PXA2xx SSP SPI Controller"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:pxa2xx-spi"); -#define MAX_BUSES 3 - #define TIMOUT_DFLT 1000 /* @@ -162,7 +156,6 @@ pxa2xx_spi_get_rx_default_thre(const struct driver_data *drv_data) static bool pxa2xx_spi_txfifo_full(const struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; u32 mask; switch (drv_data->ssp_type) { @@ -174,7 +167,7 @@ static bool pxa2xx_spi_txfifo_full(const struct driver_data *drv_data) break; } - return (read_SSSR(reg) & mask) == mask; + return (pxa2xx_spi_read(drv_data, SSSR) & mask) == mask; } static void pxa2xx_spi_clear_rx_thre(const struct driver_data *drv_data, @@ -253,9 +246,6 @@ static void lpss_ssp_setup(struct driver_data *drv_data) unsigned offset = 0x400; u32 value, orig; - if (!is_lpss_ssp(drv_data)) - return; - /* * Perform auto-detection of the LPSS SSP private registers. They * can be either at 1k or 2k offset from the base address. @@ -304,9 +294,6 @@ static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable) { u32 value; - if (!is_lpss_ssp(drv_data)) - return; - value = __lpss_ssp_read_priv(drv_data, SPI_CS_CONTROL); if (enable) value &= ~SPI_CS_CONTROL_CS_HIGH; @@ -320,7 +307,7 @@ static void cs_assert(struct driver_data *drv_data) struct chip_data *chip = drv_data->cur_chip; if (drv_data->ssp_type == CE4100_SSP) { - write_SSSR(drv_data->cur_chip->frm, drv_data->ioaddr); + pxa2xx_spi_write(drv_data, SSSR, drv_data->cur_chip->frm); return; } @@ -334,7 +321,8 @@ static void cs_assert(struct driver_data *drv_data) return; } - lpss_ssp_cs_control(drv_data, true); + if (is_lpss_ssp(drv_data)) + lpss_ssp_cs_control(drv_data, true); } static void cs_deassert(struct driver_data *drv_data) @@ -354,20 +342,18 @@ static void cs_deassert(struct driver_data *drv_data) return; } - lpss_ssp_cs_control(drv_data, false); + if (is_lpss_ssp(drv_data)) + lpss_ssp_cs_control(drv_data, false); } int pxa2xx_spi_flush(struct driver_data *drv_data) { unsigned long limit = loops_per_jiffy << 1; - void __iomem *reg = drv_data->ioaddr; - do { - while (read_SSSR(reg) & SSSR_RNE) { - read_SSDR(reg); - } - } while ((read_SSSR(reg) & SSSR_BSY) && --limit); + while (pxa2xx_spi_read(drv_data, SSSR) & SSSR_RNE) + pxa2xx_spi_read(drv_data, SSDR); + } while ((pxa2xx_spi_read(drv_data, SSSR) & SSSR_BSY) && --limit); write_SSSR_CS(drv_data, SSSR_ROR); return limit; @@ -375,14 +361,13 @@ int pxa2xx_spi_flush(struct driver_data *drv_data) static int null_writer(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; u8 n_bytes = drv_data->n_bytes; if (pxa2xx_spi_txfifo_full(drv_data) || (drv_data->tx == drv_data->tx_end)) return 0; - write_SSDR(0, reg); + pxa2xx_spi_write(drv_data, SSDR, 0); drv_data->tx += n_bytes; return 1; @@ -390,12 +375,11 @@ static int null_writer(struct driver_data *drv_data) static int null_reader(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; u8 n_bytes = drv_data->n_bytes; - while ((read_SSSR(reg) & SSSR_RNE) - && (drv_data->rx < drv_data->rx_end)) { - read_SSDR(reg); + while ((pxa2xx_spi_read(drv_data, SSSR) & SSSR_RNE) + && (drv_data->rx < drv_data->rx_end)) { + pxa2xx_spi_read(drv_data, SSDR); drv_data->rx += n_bytes; } @@ -404,13 +388,11 @@ static int null_reader(struct driver_data *drv_data) static int u8_writer(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; - if (pxa2xx_spi_txfifo_full(drv_data) || (drv_data->tx == drv_data->tx_end)) return 0; - write_SSDR(*(u8 *)(drv_data->tx), reg); + pxa2xx_spi_write(drv_data, SSDR, *(u8 *)(drv_data->tx)); ++drv_data->tx; return 1; @@ -418,11 +400,9 @@ static int u8_writer(struct driver_data *drv_data) static int u8_reader(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; - - while ((read_SSSR(reg) & SSSR_RNE) - && (drv_data->rx < drv_data->rx_end)) { - *(u8 *)(drv_data->rx) = read_SSDR(reg); + while ((pxa2xx_spi_read(drv_data, SSSR) & SSSR_RNE) + && (drv_data->rx < drv_data->rx_end)) { + *(u8 *)(drv_data->rx) = pxa2xx_spi_read(drv_data, SSDR); ++drv_data->rx; } @@ -431,13 +411,11 @@ static int u8_reader(struct driver_data *drv_data) static int u16_writer(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; - if (pxa2xx_spi_txfifo_full(drv_data) || (drv_data->tx == drv_data->tx_end)) return 0; - write_SSDR(*(u16 *)(drv_data->tx), reg); + pxa2xx_spi_write(drv_data, SSDR, *(u16 *)(drv_data->tx)); drv_data->tx += 2; return 1; @@ -445,11 +423,9 @@ static int u16_writer(struct driver_data *drv_data) static int u16_reader(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; - - while ((read_SSSR(reg) & SSSR_RNE) - && (drv_data->rx < drv_data->rx_end)) { - *(u16 *)(drv_data->rx) = read_SSDR(reg); + while ((pxa2xx_spi_read(drv_data, SSSR) & SSSR_RNE) + && (drv_data->rx < drv_data->rx_end)) { + *(u16 *)(drv_data->rx) = pxa2xx_spi_read(drv_data, SSDR); drv_data->rx += 2; } @@ -458,13 +434,11 @@ static int u16_reader(struct driver_data *drv_data) static int u32_writer(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; - if (pxa2xx_spi_txfifo_full(drv_data) || (drv_data->tx == drv_data->tx_end)) return 0; - write_SSDR(*(u32 *)(drv_data->tx), reg); + pxa2xx_spi_write(drv_data, SSDR, *(u32 *)(drv_data->tx)); drv_data->tx += 4; return 1; @@ -472,11 +446,9 @@ static int u32_writer(struct driver_data *drv_data) static int u32_reader(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; - - while ((read_SSSR(reg) & SSSR_RNE) - && (drv_data->rx < drv_data->rx_end)) { - *(u32 *)(drv_data->rx) = read_SSDR(reg); + while ((pxa2xx_spi_read(drv_data, SSSR) & SSSR_RNE) + && (drv_data->rx < drv_data->rx_end)) { + *(u32 *)(drv_data->rx) = pxa2xx_spi_read(drv_data, SSDR); drv_data->rx += 4; } @@ -546,33 +518,31 @@ static void giveback(struct driver_data *drv_data) cs_deassert(drv_data); } - spi_finalize_current_message(drv_data->master); drv_data->cur_chip = NULL; + spi_finalize_current_message(drv_data->master); } static void reset_sccr1(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; struct chip_data *chip = drv_data->cur_chip; u32 sccr1_reg; - sccr1_reg = read_SSCR1(reg) & ~drv_data->int_cr1; + sccr1_reg = pxa2xx_spi_read(drv_data, SSCR1) & ~drv_data->int_cr1; sccr1_reg &= ~SSCR1_RFT; sccr1_reg |= chip->threshold; - write_SSCR1(sccr1_reg, reg); + pxa2xx_spi_write(drv_data, SSCR1, sccr1_reg); } static void int_error_stop(struct driver_data *drv_data, const char* msg) { - void __iomem *reg = drv_data->ioaddr; - /* Stop and reset SSP */ write_SSSR_CS(drv_data, drv_data->clear_sr); reset_sccr1(drv_data); if (!pxa25x_ssp_comp(drv_data)) - write_SSTO(0, reg); + pxa2xx_spi_write(drv_data, SSTO, 0); pxa2xx_spi_flush(drv_data); - write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); + pxa2xx_spi_write(drv_data, SSCR0, + pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE); dev_err(&drv_data->pdev->dev, "%s\n", msg); @@ -582,13 +552,11 @@ static void int_error_stop(struct driver_data *drv_data, const char* msg) static void int_transfer_complete(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; - /* Stop SSP */ write_SSSR_CS(drv_data, drv_data->clear_sr); reset_sccr1(drv_data); if (!pxa25x_ssp_comp(drv_data)) - write_SSTO(0, reg); + pxa2xx_spi_write(drv_data, SSTO, 0); /* Update total byte transferred return count actual bytes read */ drv_data->cur_msg->actual_length += drv_data->len - @@ -607,12 +575,10 @@ static void int_transfer_complete(struct driver_data *drv_data) static irqreturn_t interrupt_transfer(struct driver_data *drv_data) { - void __iomem *reg = drv_data->ioaddr; + u32 irq_mask = (pxa2xx_spi_read(drv_data, SSCR1) & SSCR1_TIE) ? + drv_data->mask_sr : drv_data->mask_sr & ~SSSR_TFS; - u32 irq_mask = (read_SSCR1(reg) & SSCR1_TIE) ? - drv_data->mask_sr : drv_data->mask_sr & ~SSSR_TFS; - - u32 irq_status = read_SSSR(reg) & irq_mask; + u32 irq_status = pxa2xx_spi_read(drv_data, SSSR) & irq_mask; if (irq_status & SSSR_ROR) { int_error_stop(drv_data, "interrupt_transfer: fifo overrun"); @@ -620,7 +586,7 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) } if (irq_status & SSSR_TINT) { - write_SSSR(SSSR_TINT, reg); + pxa2xx_spi_write(drv_data, SSSR, SSSR_TINT); if (drv_data->read(drv_data)) { int_transfer_complete(drv_data); return IRQ_HANDLED; @@ -644,7 +610,7 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) u32 bytes_left; u32 sccr1_reg; - sccr1_reg = read_SSCR1(reg); + sccr1_reg = pxa2xx_spi_read(drv_data, SSCR1); sccr1_reg &= ~SSCR1_TIE; /* @@ -670,7 +636,7 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) pxa2xx_spi_set_rx_thre(drv_data, &sccr1_reg, rx_thre); } - write_SSCR1(sccr1_reg, reg); + pxa2xx_spi_write(drv_data, SSCR1, sccr1_reg); } /* We did something */ @@ -680,7 +646,6 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data) static irqreturn_t ssp_int(int irq, void *dev_id) { struct driver_data *drv_data = dev_id; - void __iomem *reg = drv_data->ioaddr; u32 sccr1_reg; u32 mask = drv_data->mask_sr; u32 status; @@ -700,11 +665,11 @@ static irqreturn_t ssp_int(int irq, void *dev_id) * are all set to one. That means that the device is already * powered off. */ - status = read_SSSR(reg); + status = pxa2xx_spi_read(drv_data, SSSR); if (status == ~0) return IRQ_NONE; - sccr1_reg = read_SSCR1(reg); + sccr1_reg = pxa2xx_spi_read(drv_data, SSCR1); /* Ignore possible writes if we don't need to write */ if (!(sccr1_reg & SSCR1_TIE)) @@ -715,10 +680,14 @@ static irqreturn_t ssp_int(int irq, void *dev_id) if (!drv_data->cur_msg) { - write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); - write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg); + pxa2xx_spi_write(drv_data, SSCR0, + pxa2xx_spi_read(drv_data, SSCR0) + & ~SSCR0_SSE); + pxa2xx_spi_write(drv_data, SSCR1, + pxa2xx_spi_read(drv_data, SSCR1) + & ~drv_data->int_cr1); if (!pxa25x_ssp_comp(drv_data)) - write_SSTO(0, reg); + pxa2xx_spi_write(drv_data, SSTO, 0); write_SSSR_CS(drv_data, drv_data->clear_sr); dev_err(&drv_data->pdev->dev, @@ -787,7 +756,6 @@ static void pump_transfers(unsigned long data) struct spi_transfer *transfer = NULL; struct spi_transfer *previous = NULL; struct chip_data *chip = NULL; - void __iomem *reg = drv_data->ioaddr; u32 clk_div = 0; u8 bits = 0; u32 speed = 0; @@ -931,7 +899,7 @@ static void pump_transfers(unsigned long data) /* Clear status and start DMA engine */ cr1 = chip->cr1 | dma_thresh | drv_data->dma_cr1; - write_SSSR(drv_data->clear_sr, reg); + pxa2xx_spi_write(drv_data, SSSR, drv_data->clear_sr); pxa2xx_spi_dma_start(drv_data); } else { @@ -944,39 +912,43 @@ static void pump_transfers(unsigned long data) } if (is_lpss_ssp(drv_data)) { - if ((read_SSIRF(reg) & 0xff) != chip->lpss_rx_threshold) - write_SSIRF(chip->lpss_rx_threshold, reg); - if ((read_SSITF(reg) & 0xffff) != chip->lpss_tx_threshold) - write_SSITF(chip->lpss_tx_threshold, reg); + if ((pxa2xx_spi_read(drv_data, SSIRF) & 0xff) + != chip->lpss_rx_threshold) + pxa2xx_spi_write(drv_data, SSIRF, + chip->lpss_rx_threshold); + if ((pxa2xx_spi_read(drv_data, SSITF) & 0xffff) + != chip->lpss_tx_threshold) + pxa2xx_spi_write(drv_data, SSITF, + chip->lpss_tx_threshold); } if (is_quark_x1000_ssp(drv_data) && - (read_DDS_RATE(reg) != chip->dds_rate)) - write_DDS_RATE(chip->dds_rate, reg); + (pxa2xx_spi_read(drv_data, DDS_RATE) != chip->dds_rate)) + pxa2xx_spi_write(drv_data, DDS_RATE, chip->dds_rate); /* see if we need to reload the config registers */ - if ((read_SSCR0(reg) != cr0) || - (read_SSCR1(reg) & change_mask) != (cr1 & change_mask)) { - + if ((pxa2xx_spi_read(drv_data, SSCR0) != cr0) + || (pxa2xx_spi_read(drv_data, SSCR1) & change_mask) + != (cr1 & change_mask)) { /* stop the SSP, and update the other bits */ - write_SSCR0(cr0 & ~SSCR0_SSE, reg); + pxa2xx_spi_write(drv_data, SSCR0, cr0 & ~SSCR0_SSE); if (!pxa25x_ssp_comp(drv_data)) - write_SSTO(chip->timeout, reg); + pxa2xx_spi_write(drv_data, SSTO, chip->timeout); /* first set CR1 without interrupt and service enables */ - write_SSCR1(cr1 & change_mask, reg); + pxa2xx_spi_write(drv_data, SSCR1, cr1 & change_mask); /* restart the SSP */ - write_SSCR0(cr0, reg); + pxa2xx_spi_write(drv_data, SSCR0, cr0); } else { if (!pxa25x_ssp_comp(drv_data)) - write_SSTO(chip->timeout, reg); + pxa2xx_spi_write(drv_data, SSTO, chip->timeout); } cs_assert(drv_data); /* after chip select, release the data by enabling service * requests and interrupts, without changing any mode bits */ - write_SSCR1(cr1, reg); + pxa2xx_spi_write(drv_data, SSCR1, cr1); } static int pxa2xx_spi_transfer_one_message(struct spi_master *master, @@ -1005,8 +977,8 @@ static int pxa2xx_spi_unprepare_transfer(struct spi_master *master) struct driver_data *drv_data = spi_master_get_devdata(master); /* Disable the SSP now */ - write_SSCR0(read_SSCR0(drv_data->ioaddr) & ~SSCR0_SSE, - drv_data->ioaddr); + pxa2xx_spi_write(drv_data, SSCR0, + pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE); return 0; } @@ -1289,6 +1261,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) struct driver_data *drv_data; struct ssp_device *ssp; int status; + u32 tmp; platform_info = dev_get_platdata(dev); if (!platform_info) { @@ -1386,38 +1359,35 @@ static int pxa2xx_spi_probe(struct platform_device *pdev) drv_data->max_clk_rate = clk_get_rate(ssp->clk); /* Load default SSP configuration */ - write_SSCR0(0, drv_data->ioaddr); + pxa2xx_spi_write(drv_data, SSCR0, 0); switch (drv_data->ssp_type) { case QUARK_X1000_SSP: - write_SSCR1(QUARK_X1000_SSCR1_RxTresh( - RX_THRESH_QUARK_X1000_DFLT) | - QUARK_X1000_SSCR1_TxTresh( - TX_THRESH_QUARK_X1000_DFLT), - drv_data->ioaddr); + tmp = QUARK_X1000_SSCR1_RxTresh(RX_THRESH_QUARK_X1000_DFLT) + | QUARK_X1000_SSCR1_TxTresh(TX_THRESH_QUARK_X1000_DFLT); + pxa2xx_spi_write(drv_data, SSCR1, tmp); /* using the Motorola SPI protocol and use 8 bit frame */ - write_SSCR0(QUARK_X1000_SSCR0_Motorola - | QUARK_X1000_SSCR0_DataSize(8), - drv_data->ioaddr); + pxa2xx_spi_write(drv_data, SSCR0, + QUARK_X1000_SSCR0_Motorola + | QUARK_X1000_SSCR0_DataSize(8)); break; default: - write_SSCR1(SSCR1_RxTresh(RX_THRESH_DFLT) | - SSCR1_TxTresh(TX_THRESH_DFLT), - drv_data->ioaddr); - write_SSCR0(SSCR0_SCR(2) - | SSCR0_Motorola - | SSCR0_DataSize(8), - drv_data->ioaddr); + tmp = SSCR1_RxTresh(RX_THRESH_DFLT) | + SSCR1_TxTresh(TX_THRESH_DFLT); + pxa2xx_spi_write(drv_data, SSCR1, tmp); + tmp = SSCR0_SCR(2) | SSCR0_Motorola | SSCR0_DataSize(8); + pxa2xx_spi_write(drv_data, SSCR0, tmp); break; } if (!pxa25x_ssp_comp(drv_data)) - write_SSTO(0, drv_data->ioaddr); + pxa2xx_spi_write(drv_data, SSTO, 0); if (!is_quark_x1000_ssp(drv_data)) - write_SSPSP(0, drv_data->ioaddr); + pxa2xx_spi_write(drv_data, SSPSP, 0); - lpss_ssp_setup(drv_data); + if (is_lpss_ssp(drv_data)) + lpss_ssp_setup(drv_data); tasklet_init(&drv_data->pump_transfers, pump_transfers, (unsigned long)drv_data); @@ -1460,7 +1430,7 @@ static int pxa2xx_spi_remove(struct platform_device *pdev) pm_runtime_get_sync(&pdev->dev); /* Disable the SSP at the peripheral and SOC level */ - write_SSCR0(0, drv_data->ioaddr); + pxa2xx_spi_write(drv_data, SSCR0, 0); clk_disable_unprepare(ssp->clk); /* Release DMA */ @@ -1497,7 +1467,7 @@ static int pxa2xx_spi_suspend(struct device *dev) status = spi_master_suspend(drv_data->master); if (status != 0) return status; - write_SSCR0(0, drv_data->ioaddr); + pxa2xx_spi_write(drv_data, SSCR0, 0); if (!pm_runtime_suspended(dev)) clk_disable_unprepare(ssp->clk); @@ -1518,7 +1488,8 @@ static int pxa2xx_spi_resume(struct device *dev) clk_prepare_enable(ssp->clk); /* Restore LPSS private register bits */ - lpss_ssp_setup(drv_data); + if (is_lpss_ssp(drv_data)) + lpss_ssp_setup(drv_data); /* Start the queue running */ status = spi_master_resume(drv_data->master); diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h index 6bec59c90cd4..85a58c906869 100644 --- a/drivers/spi/spi-pxa2xx.h +++ b/drivers/spi/spi-pxa2xx.h @@ -115,23 +115,17 @@ struct chip_data { void (*cs_control)(u32 command); }; -#define DEFINE_SSP_REG(reg, off) \ -static inline u32 read_##reg(void const __iomem *p) \ -{ return __raw_readl(p + (off)); } \ -\ -static inline void write_##reg(u32 v, void __iomem *p) \ -{ __raw_writel(v, p + (off)); } - -DEFINE_SSP_REG(SSCR0, 0x00) -DEFINE_SSP_REG(SSCR1, 0x04) -DEFINE_SSP_REG(SSSR, 0x08) -DEFINE_SSP_REG(SSITR, 0x0c) -DEFINE_SSP_REG(SSDR, 0x10) -DEFINE_SSP_REG(DDS_RATE, 0x28) /* DDS Clock Rate */ -DEFINE_SSP_REG(SSTO, 0x28) -DEFINE_SSP_REG(SSPSP, 0x2c) -DEFINE_SSP_REG(SSITF, SSITF) -DEFINE_SSP_REG(SSIRF, SSIRF) +static inline u32 pxa2xx_spi_read(const struct driver_data *drv_data, + unsigned reg) +{ + return __raw_readl(drv_data->ioaddr + reg); +} + +static inline void pxa2xx_spi_write(const struct driver_data *drv_data, + unsigned reg, u32 val) +{ + __raw_writel(val, drv_data->ioaddr + reg); +} #define START_STATE ((void *)0) #define RUNNING_STATE ((void *)1) @@ -155,13 +149,11 @@ static inline int pxa25x_ssp_comp(struct driver_data *drv_data) static inline void write_SSSR_CS(struct driver_data *drv_data, u32 val) { - void __iomem *reg = drv_data->ioaddr; - if (drv_data->ssp_type == CE4100_SSP || drv_data->ssp_type == QUARK_X1000_SSP) - val |= read_SSSR(reg) & SSSR_ALT_FRM_MASK; + val |= pxa2xx_spi_read(drv_data, SSSR) & SSSR_ALT_FRM_MASK; - write_SSSR(val, reg); + pxa2xx_spi_write(drv_data, SSSR, val); } extern int pxa2xx_spi_flush(struct driver_data *drv_data); diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c index e7fb5a0d2e8d..ff9cdbdb6672 100644 --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -337,7 +337,7 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) { struct spi_qup *controller = spi_master_get_devdata(spi->master); - u32 config, iomode, mode; + u32 config, iomode, mode, control; int ret, n_words, w_size; if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) { @@ -392,6 +392,15 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer) writel_relaxed(iomode, controller->base + QUP_IO_M_MODES); + control = readl_relaxed(controller->base + SPI_IO_CONTROL); + + if (spi->mode & SPI_CPOL) + control |= SPI_IO_C_CLK_IDLE_HIGH; + else + control &= ~SPI_IO_C_CLK_IDLE_HIGH; + + writel_relaxed(control, controller->base + SPI_IO_CONTROL); + config = readl_relaxed(controller->base + SPI_CONFIG); if (spi->mode & SPI_LOOP) diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index daabbabd26b0..1a777dc261d6 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -437,6 +437,7 @@ static void rockchip_spi_prepare_dma(struct rockchip_spi *rs) rs->state &= ~TXBUSY; spin_unlock_irqrestore(&rs->lock, flags); + rxdesc = NULL; if (rs->rx) { rxconf.direction = rs->dma_rx.direction; rxconf.src_addr = rs->dma_rx.addr; @@ -453,6 +454,7 @@ static void rockchip_spi_prepare_dma(struct rockchip_spi *rs) rxdesc->callback_param = rs; } + txdesc = NULL; if (rs->tx) { txconf.direction = rs->dma_tx.direction; txconf.dst_addr = rs->dma_tx.addr; @@ -470,7 +472,7 @@ static void rockchip_spi_prepare_dma(struct rockchip_spi *rs) } /* rx must be started before tx due to spi instinct */ - if (rs->rx) { + if (rxdesc) { spin_lock_irqsave(&rs->lock, flags); rs->state |= RXBUSY; spin_unlock_irqrestore(&rs->lock, flags); @@ -478,7 +480,7 @@ static void rockchip_spi_prepare_dma(struct rockchip_spi *rs) dma_async_issue_pending(rs->dma_rx.ch); } - if (rs->tx) { + if (txdesc) { spin_lock_irqsave(&rs->lock, flags); rs->state |= TXBUSY; spin_unlock_irqrestore(&rs->lock, flags); diff --git a/drivers/spi/spi-rspi.c b/drivers/spi/spi-rspi.c index 2071f788c6fb..46ce47076e63 100644 --- a/drivers/spi/spi-rspi.c +++ b/drivers/spi/spi-rspi.c @@ -15,11 +15,6 @@ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include <linux/module.h> diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 37b19836f5cb..9231c34b5a5c 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -11,10 +11,6 @@ * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/init.h> diff --git a/drivers/spi/spi-sc18is602.c b/drivers/spi/spi-sc18is602.c index 237f2e7a7179..5a56acf8a43e 100644 --- a/drivers/spi/spi-sc18is602.c +++ b/drivers/spi/spi-sc18is602.c @@ -12,10 +12,6 @@ * 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 */ #include <linux/kernel.h> diff --git a/drivers/spi/spi-sh-hspi.c b/drivers/spi/spi-sh-hspi.c index fc29233d0650..20e800e70442 100644 --- a/drivers/spi/spi-sh-hspi.c +++ b/drivers/spi/spi-sh-hspi.c @@ -16,11 +16,6 @@ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include <linux/clk.h> diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 96a5fc0878d8..e57eec0b2f46 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c @@ -82,7 +82,9 @@ struct sh_msiof_spi_priv { #define MDR1_SYNCMD_LR 0x30000000 /* L/R mode */ #define MDR1_SYNCAC_SHIFT 25 /* Sync Polarity (1 = Active-low) */ #define MDR1_BITLSB_SHIFT 24 /* MSB/LSB First (1 = LSB first) */ -#define MDR1_FLD_MASK 0x000000c0 /* Frame Sync Signal Interval (0-3) */ +#define MDR1_DTDL_SHIFT 20 /* Data Pin Bit Delay for MSIOF_SYNC */ +#define MDR1_SYNCDL_SHIFT 16 /* Frame Sync Signal Timing Delay */ +#define MDR1_FLD_MASK 0x0000000c /* Frame Sync Signal Interval (0-3) */ #define MDR1_FLD_SHIFT 2 #define MDR1_XXSTP 0x00000001 /* Transmission/Reception Stop on FIFO */ /* TMDR1 */ @@ -241,42 +243,80 @@ static irqreturn_t sh_msiof_spi_irq(int irq, void *data) static struct { unsigned short div; - unsigned short scr; -} const sh_msiof_spi_clk_table[] = { - { 1, SCR_BRPS( 1) | SCR_BRDV_DIV_1 }, - { 2, SCR_BRPS( 1) | SCR_BRDV_DIV_2 }, - { 4, SCR_BRPS( 1) | SCR_BRDV_DIV_4 }, - { 8, SCR_BRPS( 1) | SCR_BRDV_DIV_8 }, - { 16, SCR_BRPS( 1) | SCR_BRDV_DIV_16 }, - { 32, SCR_BRPS( 1) | SCR_BRDV_DIV_32 }, - { 64, SCR_BRPS(32) | SCR_BRDV_DIV_2 }, - { 128, SCR_BRPS(32) | SCR_BRDV_DIV_4 }, - { 256, SCR_BRPS(32) | SCR_BRDV_DIV_8 }, - { 512, SCR_BRPS(32) | SCR_BRDV_DIV_16 }, - { 1024, SCR_BRPS(32) | SCR_BRDV_DIV_32 }, + unsigned short brdv; +} const sh_msiof_spi_div_table[] = { + { 1, SCR_BRDV_DIV_1 }, + { 2, SCR_BRDV_DIV_2 }, + { 4, SCR_BRDV_DIV_4 }, + { 8, SCR_BRDV_DIV_8 }, + { 16, SCR_BRDV_DIV_16 }, + { 32, SCR_BRDV_DIV_32 }, }; static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p, unsigned long parent_rate, u32 spi_hz) { unsigned long div = 1024; + u32 brps, scr; size_t k; if (!WARN_ON(!spi_hz || !parent_rate)) div = DIV_ROUND_UP(parent_rate, spi_hz); - /* TODO: make more fine grained */ - - for (k = 0; k < ARRAY_SIZE(sh_msiof_spi_clk_table); k++) { - if (sh_msiof_spi_clk_table[k].div >= div) + for (k = 0; k < ARRAY_SIZE(sh_msiof_spi_div_table); k++) { + brps = DIV_ROUND_UP(div, sh_msiof_spi_div_table[k].div); + if (brps <= 32) /* max of brdv is 32 */ break; } - k = min_t(int, k, ARRAY_SIZE(sh_msiof_spi_clk_table) - 1); + k = min_t(int, k, ARRAY_SIZE(sh_msiof_spi_div_table) - 1); - sh_msiof_write(p, TSCR, sh_msiof_spi_clk_table[k].scr); + scr = sh_msiof_spi_div_table[k].brdv | SCR_BRPS(brps); + sh_msiof_write(p, TSCR, scr); if (!(p->chipdata->master_flags & SPI_MASTER_MUST_TX)) - sh_msiof_write(p, RSCR, sh_msiof_spi_clk_table[k].scr); + sh_msiof_write(p, RSCR, scr); +} + +static u32 sh_msiof_get_delay_bit(u32 dtdl_or_syncdl) +{ + /* + * DTDL/SYNCDL bit : p->info->dtdl or p->info->syncdl + * b'000 : 0 + * b'001 : 100 + * b'010 : 200 + * b'011 (SYNCDL only) : 300 + * b'101 : 50 + * b'110 : 150 + */ + if (dtdl_or_syncdl % 100) + return dtdl_or_syncdl / 100 + 5; + else + return dtdl_or_syncdl / 100; +} + +static u32 sh_msiof_spi_get_dtdl_and_syncdl(struct sh_msiof_spi_priv *p) +{ + u32 val; + + if (!p->info) + return 0; + + /* check if DTDL and SYNCDL is allowed value */ + if (p->info->dtdl > 200 || p->info->syncdl > 300) { + dev_warn(&p->pdev->dev, "DTDL or SYNCDL is too large\n"); + return 0; + } + + /* check if the sum of DTDL and SYNCDL becomes an integer value */ + if ((p->info->dtdl + p->info->syncdl) % 100) { + dev_warn(&p->pdev->dev, "the sum of DTDL/SYNCDL is not good\n"); + return 0; + } + + val = sh_msiof_get_delay_bit(p->info->dtdl) << MDR1_DTDL_SHIFT; + val |= sh_msiof_get_delay_bit(p->info->syncdl) << MDR1_SYNCDL_SHIFT; + + return val; } static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p, @@ -296,6 +336,7 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p, tmp = MDR1_SYNCMD_SPI | 1 << MDR1_FLD_SHIFT | MDR1_XXSTP; tmp |= !cs_high << MDR1_SYNCAC_SHIFT; tmp |= lsb_first << MDR1_BITLSB_SHIFT; + tmp |= sh_msiof_spi_get_dtdl_and_syncdl(p); sh_msiof_write(p, TMDR1, tmp | MDR1_TRMD | TMDR1_PCON); if (p->chipdata->master_flags & SPI_MASTER_MUST_TX) { /* These bits are reserved if RX needs TX */ @@ -501,7 +542,7 @@ static int sh_msiof_spi_setup(struct spi_device *spi) gpio_set_value(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH)); - pm_runtime_put_sync(&p->pdev->dev); + pm_runtime_put(&p->pdev->dev); return 0; } @@ -595,8 +636,7 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p, } /* wait for tx fifo to be emptied / rx fifo to be filled */ - ret = wait_for_completion_timeout(&p->done, HZ); - if (!ret) { + if (!wait_for_completion_timeout(&p->done, HZ)) { dev_err(&p->pdev->dev, "PIO timeout\n"); ret = -ETIMEDOUT; goto stop_reset; @@ -706,8 +746,7 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx, } /* wait for tx fifo to be emptied / rx fifo to be filled */ - ret = wait_for_completion_timeout(&p->done, HZ); - if (!ret) { + if (!wait_for_completion_timeout(&p->done, HZ)) { dev_err(&p->pdev->dev, "DMA timeout\n"); ret = -ETIMEDOUT; goto stop_reset; @@ -957,6 +996,8 @@ static struct sh_msiof_spi_info *sh_msiof_spi_parse_dt(struct device *dev) &info->tx_fifo_override); of_property_read_u32(np, "renesas,rx-fifo-size", &info->rx_fifo_override); + of_property_read_u32(np, "renesas,dtdl", &info->dtdl); + of_property_read_u32(np, "renesas,syncdl", &info->syncdl); info->num_chipselect = num_cs; diff --git a/drivers/spi/spi-sh.c b/drivers/spi/spi-sh.c index 1cfc906dd174..502501187c9e 100644 --- a/drivers/spi/spi-sh.c +++ b/drivers/spi/spi-sh.c @@ -14,11 +14,6 @@ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include <linux/module.h> diff --git a/drivers/spi/spi-sirf.c b/drivers/spi/spi-sirf.c index d075191476f0..f5715c9f68b0 100644 --- a/drivers/spi/spi-sirf.c +++ b/drivers/spi/spi-sirf.c @@ -818,7 +818,6 @@ static SIMPLE_DEV_PM_OPS(spi_sirfsoc_pm_ops, spi_sirfsoc_suspend, static const struct of_device_id spi_sirfsoc_of_match[] = { { .compatible = "sirf,prima2-spi", }, - { .compatible = "sirf,marco-spi", }, {} }; MODULE_DEVICE_TABLE(of, spi_sirfsoc_of_match); diff --git a/drivers/spi/spi-st-ssc4.c b/drivers/spi/spi-st-ssc4.c new file mode 100644 index 000000000000..2faeaa7b57a8 --- /dev/null +++ b/drivers/spi/spi-st-ssc4.c @@ -0,0 +1,504 @@ +/* + * Copyright (c) 2008-2014 STMicroelectronics Limited + * + * Author: Angus Clark <Angus.Clark@st.com> + * Patrice Chotard <patrice.chotard@st.com> + * Lee Jones <lee.jones@linaro.org> + * + * SPI master mode controller driver, used in STMicroelectronics devices. + * + * May be copied or modified under the terms of the GNU General Public + * License Version 2.0 only. See linux/COPYING for more information. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> +#include <linux/pm_runtime.h> +#include <linux/spi/spi.h> +#include <linux/spi/spi_bitbang.h> + +/* SSC registers */ +#define SSC_BRG 0x000 +#define SSC_TBUF 0x004 +#define SSC_RBUF 0x008 +#define SSC_CTL 0x00C +#define SSC_IEN 0x010 +#define SSC_I2C 0x018 + +/* SSC Control */ +#define SSC_CTL_DATA_WIDTH_9 0x8 +#define SSC_CTL_DATA_WIDTH_MSK 0xf +#define SSC_CTL_BM 0xf +#define SSC_CTL_HB BIT(4) +#define SSC_CTL_PH BIT(5) +#define SSC_CTL_PO BIT(6) +#define SSC_CTL_SR BIT(7) +#define SSC_CTL_MS BIT(8) +#define SSC_CTL_EN BIT(9) +#define SSC_CTL_LPB BIT(10) +#define SSC_CTL_EN_TX_FIFO BIT(11) +#define SSC_CTL_EN_RX_FIFO BIT(12) +#define SSC_CTL_EN_CLST_RX BIT(13) + +/* SSC Interrupt Enable */ +#define SSC_IEN_TEEN BIT(2) + +#define FIFO_SIZE 8 + +struct spi_st { + /* SSC SPI Controller */ + void __iomem *base; + struct clk *clk; + struct device *dev; + + /* SSC SPI current transaction */ + const u8 *tx_ptr; + u8 *rx_ptr; + u16 bytes_per_word; + unsigned int words_remaining; + unsigned int baud; + struct completion done; +}; + +static int spi_st_clk_enable(struct spi_st *spi_st) +{ + /* + * Current platforms use one of the core clocks for SPI and I2C. + * If we attempt to disable the clock, the system will hang. + * + * TODO: Remove this when platform supports power domains. + */ + return 0; + + return clk_prepare_enable(spi_st->clk); +} + +static void spi_st_clk_disable(struct spi_st *spi_st) +{ + /* + * Current platforms use one of the core clocks for SPI and I2C. + * If we attempt to disable the clock, the system will hang. + * + * TODO: Remove this when platform supports power domains. + */ + return; + + clk_disable_unprepare(spi_st->clk); +} + +/* Load the TX FIFO */ +static void ssc_write_tx_fifo(struct spi_st *spi_st) +{ + unsigned int count, i; + uint32_t word = 0; + + if (spi_st->words_remaining > FIFO_SIZE) + count = FIFO_SIZE; + else + count = spi_st->words_remaining; + + for (i = 0; i < count; i++) { + if (spi_st->tx_ptr) { + if (spi_st->bytes_per_word == 1) { + word = *spi_st->tx_ptr++; + } else { + word = *spi_st->tx_ptr++; + word = *spi_st->tx_ptr++ | (word << 8); + } + } + writel_relaxed(word, spi_st->base + SSC_TBUF); + } +} + +/* Read the RX FIFO */ +static void ssc_read_rx_fifo(struct spi_st *spi_st) +{ + unsigned int count, i; + uint32_t word = 0; + + if (spi_st->words_remaining > FIFO_SIZE) + count = FIFO_SIZE; + else + count = spi_st->words_remaining; + + for (i = 0; i < count; i++) { + word = readl_relaxed(spi_st->base + SSC_RBUF); + + if (spi_st->rx_ptr) { + if (spi_st->bytes_per_word == 1) { + *spi_st->rx_ptr++ = (uint8_t)word; + } else { + *spi_st->rx_ptr++ = (word >> 8); + *spi_st->rx_ptr++ = word & 0xff; + } + } + } + spi_st->words_remaining -= count; +} + +static int spi_st_transfer_one(struct spi_master *master, + struct spi_device *spi, struct spi_transfer *t) +{ + struct spi_st *spi_st = spi_master_get_devdata(master); + uint32_t ctl = 0; + + /* Setup transfer */ + spi_st->tx_ptr = t->tx_buf; + spi_st->rx_ptr = t->rx_buf; + + if (spi->bits_per_word > 8) { + /* + * Anything greater than 8 bits-per-word requires 2 + * bytes-per-word in the RX/TX buffers + */ + spi_st->bytes_per_word = 2; + spi_st->words_remaining = t->len / 2; + + } else if (spi->bits_per_word == 8 && !(t->len & 0x1)) { + /* + * If transfer is even-length, and 8 bits-per-word, then + * implement as half-length 16 bits-per-word transfer + */ + spi_st->bytes_per_word = 2; + spi_st->words_remaining = t->len / 2; + + /* Set SSC_CTL to 16 bits-per-word */ + ctl = readl_relaxed(spi_st->base + SSC_CTL); + writel_relaxed((ctl | 0xf), spi_st->base + SSC_CTL); + + readl_relaxed(spi_st->base + SSC_RBUF); + + } else { + spi_st->bytes_per_word = 1; + spi_st->words_remaining = t->len; + } + + reinit_completion(&spi_st->done); + + /* Start transfer by writing to the TX FIFO */ + ssc_write_tx_fifo(spi_st); + writel_relaxed(SSC_IEN_TEEN, spi_st->base + SSC_IEN); + + /* Wait for transfer to complete */ + wait_for_completion(&spi_st->done); + + /* Restore SSC_CTL if necessary */ + if (ctl) + writel_relaxed(ctl, spi_st->base + SSC_CTL); + + spi_finalize_current_transfer(spi->master); + + return t->len; +} + +static void spi_st_cleanup(struct spi_device *spi) +{ + int cs = spi->cs_gpio; + + if (gpio_is_valid(cs)) + devm_gpio_free(&spi->dev, cs); +} + +/* the spi->mode bits understood by this driver: */ +#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_LOOP | SPI_CS_HIGH) +static int spi_st_setup(struct spi_device *spi) +{ + struct spi_st *spi_st = spi_master_get_devdata(spi->master); + u32 spi_st_clk, sscbrg, var; + u32 hz = spi->max_speed_hz; + int cs = spi->cs_gpio; + int ret; + + if (!hz) { + dev_err(&spi->dev, "max_speed_hz unspecified\n"); + return -EINVAL; + } + + if (!gpio_is_valid(cs)) { + dev_err(&spi->dev, "%d is not a valid gpio\n", cs); + return -EINVAL; + } + + if (devm_gpio_request(&spi->dev, cs, dev_name(&spi->dev))) { + dev_err(&spi->dev, "could not request gpio:%d\n", cs); + return -EINVAL; + } + + ret = gpio_direction_output(cs, spi->mode & SPI_CS_HIGH); + if (ret) + return ret; + + spi_st_clk = clk_get_rate(spi_st->clk); + + /* Set SSC_BRF */ + sscbrg = spi_st_clk / (2 * hz); + if (sscbrg < 0x07 || sscbrg > BIT(16)) { + dev_err(&spi->dev, + "baudrate %d outside valid range %d\n", sscbrg, hz); + return -EINVAL; + } + + spi_st->baud = spi_st_clk / (2 * sscbrg); + if (sscbrg == BIT(16)) /* 16-bit counter wraps */ + sscbrg = 0x0; + + writel_relaxed(sscbrg, spi_st->base + SSC_BRG); + + dev_dbg(&spi->dev, + "setting baudrate:target= %u hz, actual= %u hz, sscbrg= %u\n", + hz, spi_st->baud, sscbrg); + + /* Set SSC_CTL and enable SSC */ + var = readl_relaxed(spi_st->base + SSC_CTL); + var |= SSC_CTL_MS; + + if (spi->mode & SPI_CPOL) + var |= SSC_CTL_PO; + else + var &= ~SSC_CTL_PO; + + if (spi->mode & SPI_CPHA) + var |= SSC_CTL_PH; + else + var &= ~SSC_CTL_PH; + + if ((spi->mode & SPI_LSB_FIRST) == 0) + var |= SSC_CTL_HB; + else + var &= ~SSC_CTL_HB; + + if (spi->mode & SPI_LOOP) + var |= SSC_CTL_LPB; + else + var &= ~SSC_CTL_LPB; + + var &= ~SSC_CTL_DATA_WIDTH_MSK; + var |= (spi->bits_per_word - 1); + + var |= SSC_CTL_EN_TX_FIFO | SSC_CTL_EN_RX_FIFO; + var |= SSC_CTL_EN; + + writel_relaxed(var, spi_st->base + SSC_CTL); + + /* Clear the status register */ + readl_relaxed(spi_st->base + SSC_RBUF); + + return 0; +} + +/* Interrupt fired when TX shift register becomes empty */ +static irqreturn_t spi_st_irq(int irq, void *dev_id) +{ + struct spi_st *spi_st = (struct spi_st *)dev_id; + + /* Read RX FIFO */ + ssc_read_rx_fifo(spi_st); + + /* Fill TX FIFO */ + if (spi_st->words_remaining) { + ssc_write_tx_fifo(spi_st); + } else { + /* TX/RX complete */ + writel_relaxed(0x0, spi_st->base + SSC_IEN); + /* + * read SSC_IEN to ensure that this bit is set + * before re-enabling interrupt + */ + readl(spi_st->base + SSC_IEN); + complete(&spi_st->done); + } + + return IRQ_HANDLED; +} + +static int spi_st_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct spi_master *master; + struct resource *res; + struct spi_st *spi_st; + int irq, ret = 0; + u32 var; + + master = spi_alloc_master(&pdev->dev, sizeof(*spi_st)); + if (!master) + return -ENOMEM; + + master->dev.of_node = np; + master->mode_bits = MODEBITS; + master->setup = spi_st_setup; + master->cleanup = spi_st_cleanup; + master->transfer_one = spi_st_transfer_one; + master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); + master->auto_runtime_pm = true; + master->bus_num = pdev->id; + spi_st = spi_master_get_devdata(master); + + spi_st->clk = devm_clk_get(&pdev->dev, "ssc"); + if (IS_ERR(spi_st->clk)) { + dev_err(&pdev->dev, "Unable to request clock\n"); + return PTR_ERR(spi_st->clk); + } + + ret = spi_st_clk_enable(spi_st); + if (ret) + return ret; + + init_completion(&spi_st->done); + + /* Get resources */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + spi_st->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(spi_st->base)) { + ret = PTR_ERR(spi_st->base); + goto clk_disable; + } + + /* Disable I2C and Reset SSC */ + writel_relaxed(0x0, spi_st->base + SSC_I2C); + var = readw_relaxed(spi_st->base + SSC_CTL); + var |= SSC_CTL_SR; + writel_relaxed(var, spi_st->base + SSC_CTL); + + udelay(1); + var = readl_relaxed(spi_st->base + SSC_CTL); + var &= ~SSC_CTL_SR; + writel_relaxed(var, spi_st->base + SSC_CTL); + + /* Set SSC into slave mode before reconfiguring PIO pins */ + var = readl_relaxed(spi_st->base + SSC_CTL); + var &= ~SSC_CTL_MS; + writel_relaxed(var, spi_st->base + SSC_CTL); + + irq = irq_of_parse_and_map(np, 0); + if (!irq) { + dev_err(&pdev->dev, "IRQ missing or invalid\n"); + ret = -EINVAL; + goto clk_disable; + } + + ret = devm_request_irq(&pdev->dev, irq, spi_st_irq, 0, + pdev->name, spi_st); + if (ret) { + dev_err(&pdev->dev, "Failed to request irq %d\n", irq); + goto clk_disable; + } + + /* by default the device is on */ + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + + platform_set_drvdata(pdev, master); + + ret = devm_spi_register_master(&pdev->dev, master); + if (ret) { + dev_err(&pdev->dev, "Failed to register master\n"); + goto clk_disable; + } + + return 0; + +clk_disable: + spi_st_clk_disable(spi_st); + + return ret; +} + +static int spi_st_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct spi_st *spi_st = spi_master_get_devdata(master); + + spi_st_clk_disable(spi_st); + + pinctrl_pm_select_sleep_state(&pdev->dev); + + return 0; +} + +#ifdef CONFIG_PM +static int spi_st_runtime_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct spi_st *spi_st = spi_master_get_devdata(master); + + writel_relaxed(0, spi_st->base + SSC_IEN); + pinctrl_pm_select_sleep_state(dev); + + spi_st_clk_disable(spi_st); + + return 0; +} + +static int spi_st_runtime_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + struct spi_st *spi_st = spi_master_get_devdata(master); + int ret; + + ret = spi_st_clk_enable(spi_st); + pinctrl_pm_select_default_state(dev); + + return ret; +} +#endif + +#ifdef CONFIG_PM_SLEEP +static int spi_st_suspend(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + int ret; + + ret = spi_master_suspend(master); + if (ret) + return ret; + + return pm_runtime_force_suspend(dev); +} + +static int spi_st_resume(struct device *dev) +{ + struct spi_master *master = dev_get_drvdata(dev); + int ret; + + ret = spi_master_resume(master); + if (ret) + return ret; + + return pm_runtime_force_resume(dev); +} +#endif + +static const struct dev_pm_ops spi_st_pm = { + SET_SYSTEM_SLEEP_PM_OPS(spi_st_suspend, spi_st_resume) + SET_RUNTIME_PM_OPS(spi_st_runtime_suspend, spi_st_runtime_resume, NULL) +}; + +static struct of_device_id stm_spi_match[] = { + { .compatible = "st,comms-ssc4-spi", }, + {}, +}; +MODULE_DEVICE_TABLE(of, stm_spi_match); + +static struct platform_driver spi_st_driver = { + .driver = { + .name = "spi-st", + .pm = &spi_st_pm, + .of_match_table = of_match_ptr(stm_spi_match), + }, + .probe = spi_st_probe, + .remove = spi_st_remove, +}; +module_platform_driver(spi_st_driver); + +MODULE_AUTHOR("Patrice Chotard <patrice.chotard@st.com>"); +MODULE_DESCRIPTION("STM SSC SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index 6146c4cd6583..884a716e50cb 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -201,7 +201,7 @@ static void ti_qspi_restore_ctx(struct ti_qspi *qspi) static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t) { - int wlen, count, ret; + int wlen, count; unsigned int cmd; const u8 *txbuf; @@ -230,9 +230,8 @@ static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t) } ti_qspi_write(qspi, cmd, QSPI_SPI_CMD_REG); - ret = wait_for_completion_timeout(&qspi->transfer_complete, - QSPI_COMPLETION_TIMEOUT); - if (ret == 0) { + if (!wait_for_completion_timeout(&qspi->transfer_complete, + QSPI_COMPLETION_TIMEOUT)) { dev_err(qspi->dev, "write timed out\n"); return -ETIMEDOUT; } @@ -245,7 +244,7 @@ static int qspi_write_msg(struct ti_qspi *qspi, struct spi_transfer *t) static int qspi_read_msg(struct ti_qspi *qspi, struct spi_transfer *t) { - int wlen, count, ret; + int wlen, count; unsigned int cmd; u8 *rxbuf; @@ -268,9 +267,8 @@ static int qspi_read_msg(struct ti_qspi *qspi, struct spi_transfer *t) while (count) { dev_dbg(qspi->dev, "rx cmd %08x dc %08x\n", cmd, qspi->dc); ti_qspi_write(qspi, cmd, QSPI_SPI_CMD_REG); - ret = wait_for_completion_timeout(&qspi->transfer_complete, - QSPI_COMPLETION_TIMEOUT); - if (ret == 0) { + if (!wait_for_completion_timeout(&qspi->transfer_complete, + QSPI_COMPLETION_TIMEOUT)) { dev_err(qspi->dev, "read timed out\n"); return -ETIMEDOUT; } diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c index be692ad50442..93dfcee0f987 100644 --- a/drivers/spi/spi-topcliff-pch.c +++ b/drivers/spi/spi-topcliff-pch.c @@ -11,10 +11,6 @@ * 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. */ #include <linux/delay.h> diff --git a/drivers/spi/spi-xilinx.c b/drivers/spi/spi-xilinx.c index 79bd84f43430..133f53a9c1d4 100644 --- a/drivers/spi/spi-xilinx.c +++ b/drivers/spi/spi-xilinx.c @@ -22,6 +22,8 @@ #include <linux/spi/xilinx_spi.h> #include <linux/io.h> +#define XILINX_SPI_MAX_CS 32 + #define XILINX_SPI_NAME "xilinx_spi" /* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e) @@ -34,7 +36,8 @@ #define XSPI_CR_MASTER_MODE 0x04 #define XSPI_CR_CPOL 0x08 #define XSPI_CR_CPHA 0x10 -#define XSPI_CR_MODE_MASK (XSPI_CR_CPHA | XSPI_CR_CPOL) +#define XSPI_CR_MODE_MASK (XSPI_CR_CPHA | XSPI_CR_CPOL | \ + XSPI_CR_LSB_FIRST | XSPI_CR_LOOP) #define XSPI_CR_TXFIFO_RESET 0x20 #define XSPI_CR_RXFIFO_RESET 0x40 #define XSPI_CR_MANUAL_SSELECT 0x80 @@ -85,12 +88,11 @@ struct xilinx_spi { u8 *rx_ptr; /* pointer in the Tx buffer */ const u8 *tx_ptr; /* pointer in the Rx buffer */ - int remaining_bytes; /* the number of bytes left to transfer */ - u8 bits_per_word; + u8 bytes_per_word; + int buffer_size; /* buffer size in words */ + u32 cs_inactive; /* Level of the CS pins when inactive*/ unsigned int (*read_fn)(void __iomem *); void (*write_fn)(u32, void __iomem *); - void (*tx_fn)(struct xilinx_spi *); - void (*rx_fn)(struct xilinx_spi *); }; static void xspi_write32(u32 val, void __iomem *addr) @@ -113,49 +115,51 @@ static unsigned int xspi_read32_be(void __iomem *addr) return ioread32be(addr); } -static void xspi_tx8(struct xilinx_spi *xspi) +static void xilinx_spi_tx(struct xilinx_spi *xspi) { - xspi->write_fn(*xspi->tx_ptr, xspi->regs + XSPI_TXD_OFFSET); - xspi->tx_ptr++; -} - -static void xspi_tx16(struct xilinx_spi *xspi) -{ - xspi->write_fn(*(u16 *)(xspi->tx_ptr), xspi->regs + XSPI_TXD_OFFSET); - xspi->tx_ptr += 2; -} + u32 data = 0; -static void xspi_tx32(struct xilinx_spi *xspi) -{ - xspi->write_fn(*(u32 *)(xspi->tx_ptr), xspi->regs + XSPI_TXD_OFFSET); - xspi->tx_ptr += 4; -} - -static void xspi_rx8(struct xilinx_spi *xspi) -{ - u32 data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET); - if (xspi->rx_ptr) { - *xspi->rx_ptr = data & 0xff; - xspi->rx_ptr++; + if (!xspi->tx_ptr) { + xspi->write_fn(0, xspi->regs + XSPI_TXD_OFFSET); + return; } -} -static void xspi_rx16(struct xilinx_spi *xspi) -{ - u32 data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET); - if (xspi->rx_ptr) { - *(u16 *)(xspi->rx_ptr) = data & 0xffff; - xspi->rx_ptr += 2; + switch (xspi->bytes_per_word) { + case 1: + data = *(u8 *)(xspi->tx_ptr); + break; + case 2: + data = *(u16 *)(xspi->tx_ptr); + break; + case 4: + data = *(u32 *)(xspi->tx_ptr); + break; } + + xspi->write_fn(data, xspi->regs + XSPI_TXD_OFFSET); + xspi->tx_ptr += xspi->bytes_per_word; } -static void xspi_rx32(struct xilinx_spi *xspi) +static void xilinx_spi_rx(struct xilinx_spi *xspi) { u32 data = xspi->read_fn(xspi->regs + XSPI_RXD_OFFSET); - if (xspi->rx_ptr) { + + if (!xspi->rx_ptr) + return; + + switch (xspi->bytes_per_word) { + case 1: + *(u8 *)(xspi->rx_ptr) = data; + break; + case 2: + *(u16 *)(xspi->rx_ptr) = data; + break; + case 4: *(u32 *)(xspi->rx_ptr) = data; - xspi->rx_ptr += 4; + break; } + + xspi->rx_ptr += xspi->bytes_per_word; } static void xspi_init_hw(struct xilinx_spi *xspi) @@ -165,46 +169,56 @@ static void xspi_init_hw(struct xilinx_spi *xspi) /* Reset the SPI device */ xspi->write_fn(XIPIF_V123B_RESET_MASK, regs_base + XIPIF_V123B_RESETR_OFFSET); - /* Disable all the interrupts just in case */ - xspi->write_fn(0, regs_base + XIPIF_V123B_IIER_OFFSET); - /* Enable the global IPIF interrupt */ - xspi->write_fn(XIPIF_V123B_GINTR_ENABLE, - regs_base + XIPIF_V123B_DGIER_OFFSET); + /* Enable the transmit empty interrupt, which we use to determine + * progress on the transmission. + */ + xspi->write_fn(XSPI_INTR_TX_EMPTY, + regs_base + XIPIF_V123B_IIER_OFFSET); + /* Disable the global IPIF interrupt */ + xspi->write_fn(0, regs_base + XIPIF_V123B_DGIER_OFFSET); /* Deselect the slave on the SPI bus */ xspi->write_fn(0xffff, regs_base + XSPI_SSR_OFFSET); /* Disable the transmitter, enable Manual Slave Select Assertion, * put SPI controller into master mode, and enable it */ - xspi->write_fn(XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT | - XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE | XSPI_CR_TXFIFO_RESET | - XSPI_CR_RXFIFO_RESET, regs_base + XSPI_CR_OFFSET); + xspi->write_fn(XSPI_CR_MANUAL_SSELECT | XSPI_CR_MASTER_MODE | + XSPI_CR_ENABLE | XSPI_CR_TXFIFO_RESET | XSPI_CR_RXFIFO_RESET, + regs_base + XSPI_CR_OFFSET); } static void xilinx_spi_chipselect(struct spi_device *spi, int is_on) { struct xilinx_spi *xspi = spi_master_get_devdata(spi->master); + u16 cr; + u32 cs; if (is_on == BITBANG_CS_INACTIVE) { /* Deselect the slave on the SPI bus */ - xspi->write_fn(0xffff, xspi->regs + XSPI_SSR_OFFSET); - } else if (is_on == BITBANG_CS_ACTIVE) { - /* Set the SPI clock phase and polarity */ - u16 cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET) - & ~XSPI_CR_MODE_MASK; - if (spi->mode & SPI_CPHA) - cr |= XSPI_CR_CPHA; - if (spi->mode & SPI_CPOL) - cr |= XSPI_CR_CPOL; - xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET); - - /* We do not check spi->max_speed_hz here as the SPI clock - * frequency is not software programmable (the IP block design - * parameter) - */ - - /* Activate the chip select */ - xspi->write_fn(~(0x0001 << spi->chip_select), - xspi->regs + XSPI_SSR_OFFSET); + xspi->write_fn(xspi->cs_inactive, xspi->regs + XSPI_SSR_OFFSET); + return; } + + /* Set the SPI clock phase and polarity */ + cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET) & ~XSPI_CR_MODE_MASK; + if (spi->mode & SPI_CPHA) + cr |= XSPI_CR_CPHA; + if (spi->mode & SPI_CPOL) + cr |= XSPI_CR_CPOL; + if (spi->mode & SPI_LSB_FIRST) + cr |= XSPI_CR_LSB_FIRST; + if (spi->mode & SPI_LOOP) + cr |= XSPI_CR_LOOP; + xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET); + + /* We do not check spi->max_speed_hz here as the SPI clock + * frequency is not software programmable (the IP block design + * parameter) + */ + + cs = xspi->cs_inactive; + cs ^= BIT(spi->chip_select); + + /* Activate the chip select */ + xspi->write_fn(cs, xspi->regs + XSPI_SSR_OFFSET); } /* spi_bitbang requires custom setup_transfer() to be defined if there is a @@ -213,85 +227,85 @@ static void xilinx_spi_chipselect(struct spi_device *spi, int is_on) static int xilinx_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { - return 0; -} + struct xilinx_spi *xspi = spi_master_get_devdata(spi->master); -static void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi) -{ - u8 sr; + if (spi->mode & SPI_CS_HIGH) + xspi->cs_inactive &= ~BIT(spi->chip_select); + else + xspi->cs_inactive |= BIT(spi->chip_select); - /* Fill the Tx FIFO with as many bytes as possible */ - sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); - while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && xspi->remaining_bytes > 0) { - if (xspi->tx_ptr) - xspi->tx_fn(xspi); - else - xspi->write_fn(0, xspi->regs + XSPI_TXD_OFFSET); - xspi->remaining_bytes -= xspi->bits_per_word / 8; - sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); - } + return 0; } static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) { struct xilinx_spi *xspi = spi_master_get_devdata(spi->master); - u32 ipif_ier; + int remaining_words; /* the number of words left to transfer */ + bool use_irq = false; + u16 cr = 0; /* We get here with transmitter inhibited */ xspi->tx_ptr = t->tx_buf; xspi->rx_ptr = t->rx_buf; - xspi->remaining_bytes = t->len; + remaining_words = t->len / xspi->bytes_per_word; reinit_completion(&xspi->done); + if (xspi->irq >= 0 && remaining_words > xspi->buffer_size) { + use_irq = true; + xspi->write_fn(XSPI_INTR_TX_EMPTY, + xspi->regs + XIPIF_V123B_IISR_OFFSET); + /* Enable the global IPIF interrupt */ + xspi->write_fn(XIPIF_V123B_GINTR_ENABLE, + xspi->regs + XIPIF_V123B_DGIER_OFFSET); + /* Inhibit irq to avoid spurious irqs on tx_empty*/ + cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET); + xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT, + xspi->regs + XSPI_CR_OFFSET); + } - /* Enable the transmit empty interrupt, which we use to determine - * progress on the transmission. - */ - ipif_ier = xspi->read_fn(xspi->regs + XIPIF_V123B_IIER_OFFSET); - xspi->write_fn(ipif_ier | XSPI_INTR_TX_EMPTY, - xspi->regs + XIPIF_V123B_IIER_OFFSET); + while (remaining_words) { + int n_words, tx_words, rx_words; - for (;;) { - u16 cr; - u8 sr; + n_words = min(remaining_words, xspi->buffer_size); - xilinx_spi_fill_tx_fifo(xspi); + tx_words = n_words; + while (tx_words--) + xilinx_spi_tx(xspi); /* Start the transfer by not inhibiting the transmitter any * longer */ - cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET) & - ~XSPI_CR_TRANS_INHIBIT; - xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET); - wait_for_completion(&xspi->done); + if (use_irq) { + xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET); + wait_for_completion(&xspi->done); + } else + while (!(xspi->read_fn(xspi->regs + XSPI_SR_OFFSET) & + XSPI_SR_TX_EMPTY_MASK)) + ; /* A transmit has just completed. Process received data and * check for more data to transmit. Always inhibit the * transmitter while the Isr refills the transmit register/FIFO, * or make sure it is stopped if we're done. */ - cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET); - xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT, + if (use_irq) + xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT, xspi->regs + XSPI_CR_OFFSET); /* Read out all the data from the Rx FIFO */ - sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); - while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) { - xspi->rx_fn(xspi); - sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); - } - - /* See if there is more data to send */ - if (xspi->remaining_bytes <= 0) - break; + rx_words = n_words; + while (rx_words--) + xilinx_spi_rx(xspi); + + remaining_words -= n_words; } - /* Disable the transmit empty interrupt */ - xspi->write_fn(ipif_ier, xspi->regs + XIPIF_V123B_IIER_OFFSET); + if (use_irq) + xspi->write_fn(0, xspi->regs + XIPIF_V123B_DGIER_OFFSET); - return t->len - xspi->remaining_bytes; + return t->len; } @@ -316,6 +330,28 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static int xilinx_spi_find_buffer_size(struct xilinx_spi *xspi) +{ + u8 sr; + int n_words = 0; + + /* + * Before the buffer_size detection we reset the core + * to make sure we start with a clean state. + */ + xspi->write_fn(XIPIF_V123B_RESET_MASK, + xspi->regs + XIPIF_V123B_RESETR_OFFSET); + + /* Fill the Tx FIFO with as many words as possible */ + do { + xspi->write_fn(0, xspi->regs + XSPI_TXD_OFFSET); + sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET); + n_words++; + } while (!(sr & XSPI_SR_TX_FULL_MASK)); + + return n_words; +} + static const struct of_device_id xilinx_spi_of_match[] = { { .compatible = "xlnx,xps-spi-2.00.a", }, { .compatible = "xlnx,xps-spi-2.00.b", }, @@ -348,14 +384,21 @@ static int xilinx_spi_probe(struct platform_device *pdev) return -EINVAL; } + if (num_cs > XILINX_SPI_MAX_CS) { + dev_err(&pdev->dev, "Invalid number of spi slaves\n"); + return -EINVAL; + } + master = spi_alloc_master(&pdev->dev, sizeof(struct xilinx_spi)); if (!master) return -ENODEV; /* the spi->mode bits understood by this driver: */ - master->mode_bits = SPI_CPOL | SPI_CPHA; + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_LOOP | + SPI_CS_HIGH; xspi = spi_master_get_devdata(master); + xspi->cs_inactive = 0xffffffff; xspi->bitbang.master = master; xspi->bitbang.chipselect = xilinx_spi_chipselect; xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer; @@ -392,35 +435,20 @@ static int xilinx_spi_probe(struct platform_device *pdev) } master->bits_per_word_mask = SPI_BPW_MASK(bits_per_word); - xspi->bits_per_word = bits_per_word; - if (xspi->bits_per_word == 8) { - xspi->tx_fn = xspi_tx8; - xspi->rx_fn = xspi_rx8; - } else if (xspi->bits_per_word == 16) { - xspi->tx_fn = xspi_tx16; - xspi->rx_fn = xspi_rx16; - } else if (xspi->bits_per_word == 32) { - xspi->tx_fn = xspi_tx32; - xspi->rx_fn = xspi_rx32; - } else { - ret = -EINVAL; - goto put_master; - } - - /* SPI controller initializations */ - xspi_init_hw(xspi); + xspi->bytes_per_word = bits_per_word / 8; + xspi->buffer_size = xilinx_spi_find_buffer_size(xspi); xspi->irq = platform_get_irq(pdev, 0); - if (xspi->irq < 0) { - ret = xspi->irq; - goto put_master; + if (xspi->irq >= 0) { + /* Register for SPI Interrupt */ + ret = devm_request_irq(&pdev->dev, xspi->irq, xilinx_spi_irq, 0, + dev_name(&pdev->dev), xspi); + if (ret) + goto put_master; } - /* Register for SPI Interrupt */ - ret = devm_request_irq(&pdev->dev, xspi->irq, xilinx_spi_irq, 0, - dev_name(&pdev->dev), xspi); - if (ret) - goto put_master; + /* SPI controller initializations */ + xspi_init_hw(xspi); ret = spi_bitbang_start(&xspi->bitbang); if (ret) { diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 66a70e9bc743..c64a3e59fce3 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -13,10 +13,6 @@ * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/kernel.h> @@ -788,7 +784,7 @@ static int spi_transfer_one_message(struct spi_master *master, struct spi_transfer *xfer; bool keep_cs = false; int ret = 0; - int ms = 1; + unsigned long ms = 1; spi_set_cs(msg->spi, true); @@ -875,31 +871,59 @@ void spi_finalize_current_transfer(struct spi_master *master) EXPORT_SYMBOL_GPL(spi_finalize_current_transfer); /** - * spi_pump_messages - kthread work function which processes spi message queue - * @work: pointer to kthread work struct contained in the master struct + * __spi_pump_messages - function which processes spi message queue + * @master: master to process queue for + * @in_kthread: true if we are in the context of the message pump thread * * This function checks if there is any spi message in the queue that * needs processing and if so call out to the driver to initialize hardware * and transfer each message. * + * Note that it is called both from the kthread itself and also from + * inside spi_sync(); the queue extraction handling at the top of the + * function should deal with this safely. */ -static void spi_pump_messages(struct kthread_work *work) +static void __spi_pump_messages(struct spi_master *master, bool in_kthread) { - struct spi_master *master = - container_of(work, struct spi_master, pump_messages); unsigned long flags; bool was_busy = false; int ret; - /* Lock queue and check for queue work */ + /* Lock queue */ spin_lock_irqsave(&master->queue_lock, flags); + + /* Make sure we are not already running a message */ + if (master->cur_msg) { + spin_unlock_irqrestore(&master->queue_lock, flags); + return; + } + + /* If another context is idling the device then defer */ + if (master->idling) { + queue_kthread_work(&master->kworker, &master->pump_messages); + spin_unlock_irqrestore(&master->queue_lock, flags); + return; + } + + /* Check if the queue is idle */ if (list_empty(&master->queue) || !master->running) { if (!master->busy) { spin_unlock_irqrestore(&master->queue_lock, flags); return; } + + /* Only do teardown in the thread */ + if (!in_kthread) { + queue_kthread_work(&master->kworker, + &master->pump_messages); + spin_unlock_irqrestore(&master->queue_lock, flags); + return; + } + master->busy = false; + master->idling = true; spin_unlock_irqrestore(&master->queue_lock, flags); + kfree(master->dummy_rx); master->dummy_rx = NULL; kfree(master->dummy_tx); @@ -913,14 +937,13 @@ static void spi_pump_messages(struct kthread_work *work) pm_runtime_put_autosuspend(master->dev.parent); } trace_spi_master_idle(master); - return; - } - /* Make sure we are not already running a message */ - if (master->cur_msg) { + spin_lock_irqsave(&master->queue_lock, flags); + master->idling = false; spin_unlock_irqrestore(&master->queue_lock, flags); return; } + /* Extract head of queue */ master->cur_msg = list_first_entry(&master->queue, struct spi_message, queue); @@ -985,13 +1008,22 @@ static void spi_pump_messages(struct kthread_work *work) } } +/** + * spi_pump_messages - kthread work function which processes spi message queue + * @work: pointer to kthread work struct contained in the master struct + */ +static void spi_pump_messages(struct kthread_work *work) +{ + struct spi_master *master = + container_of(work, struct spi_master, pump_messages); + + __spi_pump_messages(master, true); +} + static int spi_init_queue(struct spi_master *master) { struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 }; - INIT_LIST_HEAD(&master->queue); - spin_lock_init(&master->queue_lock); - master->running = false; master->busy = false; @@ -1161,12 +1193,9 @@ static int spi_destroy_queue(struct spi_master *master) return 0; } -/** - * spi_queued_transfer - transfer function for queued transfers - * @spi: spi device which is requesting transfer - * @msg: spi message which is to handled is queued to driver queue - */ -static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg) +static int __spi_queued_transfer(struct spi_device *spi, + struct spi_message *msg, + bool need_pump) { struct spi_master *master = spi->master; unsigned long flags; @@ -1181,13 +1210,23 @@ static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg) msg->status = -EINPROGRESS; list_add_tail(&msg->queue, &master->queue); - if (!master->busy) + if (!master->busy && need_pump) queue_kthread_work(&master->kworker, &master->pump_messages); spin_unlock_irqrestore(&master->queue_lock, flags); return 0; } +/** + * spi_queued_transfer - transfer function for queued transfers + * @spi: spi device which is requesting transfer + * @msg: spi message which is to handled is queued to driver queue + */ +static int spi_queued_transfer(struct spi_device *spi, struct spi_message *msg) +{ + return __spi_queued_transfer(spi, msg, true); +} + static int spi_master_initialize_queue(struct spi_master *master) { int ret; @@ -1609,6 +1648,8 @@ int spi_register_master(struct spi_master *master) dynamic = 1; } + INIT_LIST_HEAD(&master->queue); + spin_lock_init(&master->queue_lock); spin_lock_init(&master->bus_lock_spinlock); mutex_init(&master->bus_lock_mutex); master->bus_lock_flag = 0; @@ -2114,19 +2155,46 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message, DECLARE_COMPLETION_ONSTACK(done); int status; struct spi_master *master = spi->master; + unsigned long flags; + + status = __spi_validate(spi, message); + if (status != 0) + return status; message->complete = spi_complete; message->context = &done; + message->spi = spi; if (!bus_locked) mutex_lock(&master->bus_lock_mutex); - status = spi_async_locked(spi, message); + /* If we're not using the legacy transfer method then we will + * try to transfer in the calling context so special case. + * This code would be less tricky if we could remove the + * support for driver implemented message queues. + */ + if (master->transfer == spi_queued_transfer) { + spin_lock_irqsave(&master->bus_lock_spinlock, flags); + + trace_spi_message_submit(message); + + status = __spi_queued_transfer(spi, message, false); + + spin_unlock_irqrestore(&master->bus_lock_spinlock, flags); + } else { + status = spi_async_locked(spi, message); + } if (!bus_locked) mutex_unlock(&master->bus_lock_mutex); if (status == 0) { + /* Push out the messages in the calling context if we + * can. + */ + if (master->transfer == spi_queued_transfer) + __spi_pump_messages(master, false); + wait_for_completion(&done); status = message->status; } diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 6941e04afb8c..4eb7a980e670 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -14,10 +14,6 @@ * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/init.h> @@ -317,6 +313,37 @@ done: return status; } +static struct spi_ioc_transfer * +spidev_get_ioc_message(unsigned int cmd, struct spi_ioc_transfer __user *u_ioc, + unsigned *n_ioc) +{ + struct spi_ioc_transfer *ioc; + u32 tmp; + + /* Check type, command number and direction */ + if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC + || _IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0)) + || _IOC_DIR(cmd) != _IOC_WRITE) + return ERR_PTR(-ENOTTY); + + tmp = _IOC_SIZE(cmd); + if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) + return ERR_PTR(-EINVAL); + *n_ioc = tmp / sizeof(struct spi_ioc_transfer); + if (*n_ioc == 0) + return NULL; + + /* copy into scratch area */ + ioc = kmalloc(tmp, GFP_KERNEL); + if (!ioc) + return ERR_PTR(-ENOMEM); + if (__copy_from_user(ioc, u_ioc, tmp)) { + kfree(ioc); + return ERR_PTR(-EFAULT); + } + return ioc; +} + static long spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { @@ -456,32 +483,15 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) default: /* segmented and/or full-duplex I/O request */ - if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0)) - || _IOC_DIR(cmd) != _IOC_WRITE) { - retval = -ENOTTY; - break; - } - - tmp = _IOC_SIZE(cmd); - if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) { - retval = -EINVAL; - break; - } - n_ioc = tmp / sizeof(struct spi_ioc_transfer); - if (n_ioc == 0) - break; - - /* copy into scratch area */ - ioc = kmalloc(tmp, GFP_KERNEL); - if (!ioc) { - retval = -ENOMEM; - break; - } - if (__copy_from_user(ioc, (void __user *)arg, tmp)) { - kfree(ioc); - retval = -EFAULT; + /* Check message and copy into scratch area */ + ioc = spidev_get_ioc_message(cmd, + (struct spi_ioc_transfer __user *)arg, &n_ioc); + if (IS_ERR(ioc)) { + retval = PTR_ERR(ioc); break; } + if (!ioc) + break; /* n_ioc is also 0 */ /* translate to spi_message, execute */ retval = spidev_message(spidev, ioc, n_ioc); @@ -496,8 +506,67 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) #ifdef CONFIG_COMPAT static long +spidev_compat_ioc_message(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct spi_ioc_transfer __user *u_ioc; + int retval = 0; + struct spidev_data *spidev; + struct spi_device *spi; + unsigned n_ioc, n; + struct spi_ioc_transfer *ioc; + + u_ioc = (struct spi_ioc_transfer __user *) compat_ptr(arg); + if (!access_ok(VERIFY_READ, u_ioc, _IOC_SIZE(cmd))) + return -EFAULT; + + /* guard against device removal before, or while, + * we issue this ioctl. + */ + spidev = filp->private_data; + spin_lock_irq(&spidev->spi_lock); + spi = spi_dev_get(spidev->spi); + spin_unlock_irq(&spidev->spi_lock); + + if (spi == NULL) + return -ESHUTDOWN; + + /* SPI_IOC_MESSAGE needs the buffer locked "normally" */ + mutex_lock(&spidev->buf_lock); + + /* Check message and copy into scratch area */ + ioc = spidev_get_ioc_message(cmd, u_ioc, &n_ioc); + if (IS_ERR(ioc)) { + retval = PTR_ERR(ioc); + goto done; + } + if (!ioc) + goto done; /* n_ioc is also 0 */ + + /* Convert buffer pointers */ + for (n = 0; n < n_ioc; n++) { + ioc[n].rx_buf = (uintptr_t) compat_ptr(ioc[n].rx_buf); + ioc[n].tx_buf = (uintptr_t) compat_ptr(ioc[n].tx_buf); + } + + /* translate to spi_message, execute */ + retval = spidev_message(spidev, ioc, n_ioc); + kfree(ioc); + +done: + mutex_unlock(&spidev->buf_lock); + spi_dev_put(spi); + return retval; +} + +static long spidev_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { + if (_IOC_TYPE(cmd) == SPI_IOC_MAGIC + && _IOC_NR(cmd) == _IOC_NR(SPI_IOC_MESSAGE(0)) + && _IOC_DIR(cmd) == _IOC_WRITE) + return spidev_compat_ioc_message(filp, cmd, arg); + return spidev_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); } #else diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index 2fead3820849..1e180c400f17 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c @@ -90,25 +90,6 @@ found: } #endif /* CONFIG_SSB_PCMCIAHOST */ -#ifdef CONFIG_SSB_SDIOHOST -struct ssb_bus *ssb_sdio_func_to_bus(struct sdio_func *func) -{ - struct ssb_bus *bus; - - ssb_buses_lock(); - list_for_each_entry(bus, &buses, list) { - if (bus->bustype == SSB_BUSTYPE_SDIO && - bus->host_sdio == func) - goto found; - } - bus = NULL; -found: - ssb_buses_unlock(); - - return bus; -} -#endif /* CONFIG_SSB_SDIOHOST */ - int ssb_for_each_bus_call(unsigned long data, int (*func)(struct ssb_bus *bus, unsigned long data)) { diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 815de379a130..9049dd91b569 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -46,8 +46,6 @@ source "drivers/staging/rtl8723au/Kconfig" source "drivers/staging/rts5208/Kconfig" -source "drivers/staging/line6/Kconfig" - source "drivers/staging/octeon/Kconfig" source "drivers/staging/octeon-usb/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 33c640b49566..fe26ff162b42 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_R8712U) += rtl8712/ obj-$(CONFIG_R8188EU) += rtl8188eu/ obj-$(CONFIG_R8723AU) += rtl8723au/ obj-$(CONFIG_RTS5208) += rts5208/ -obj-$(CONFIG_LINE6_USB) += line6/ obj-$(CONFIG_NETLOGIC_XLR_NET) += netlogic/ obj-$(CONFIG_OCTEON_ETHERNET) += octeon/ obj-$(CONFIG_OCTEON_USB) += octeon-usb/ diff --git a/drivers/staging/line6/Kconfig b/drivers/staging/line6/Kconfig deleted file mode 100644 index 4f1219b4c692..000000000000 --- a/drivers/staging/line6/Kconfig +++ /dev/null @@ -1,38 +0,0 @@ -menuconfig LINE6_USB - tristate "Line6 USB support" - depends on USB && SND - select SND_RAWMIDI - select SND_PCM - help - This is a driver for the guitar amp, cab, and effects modeller - PODxt Pro by Line6 (and similar devices), supporting the - following features: - * Reading/writing individual parameters - * Reading/writing complete channel, effects setup, and amp - setup data - * Channel switching - * Virtual MIDI interface - * Tuner access - * Playback/capture/mixer device for any ALSA-compatible PCM - audio application - * Signal routing (record clean/processed guitar signal, - re-amping) - - Preliminary support for the Variax Workbench and TonePort - devices is included. - -if LINE6_USB - -config LINE6_USB_IMPULSE_RESPONSE - bool "measure impulse response" - default n - help - Say Y here to add code to measure the impulse response of a Line6 - device. This is more accurate than user-space methods since it - bypasses any PCM data buffering (e.g., by ALSA or jack). This is - useful for assessing the performance of new devices, but is not - required for normal operation. - - If unsure, say N. - -endif # LINE6_USB diff --git a/drivers/staging/line6/Makefile b/drivers/staging/line6/Makefile deleted file mode 100644 index ae5c374b0f87..000000000000 --- a/drivers/staging/line6/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -obj-$(CONFIG_LINE6_USB) += line6usb.o - -line6usb-y := \ - audio.o \ - capture.o \ - driver.o \ - midi.o \ - midibuf.o \ - pcm.o \ - playback.o \ - pod.o \ - toneport.o \ - variax.o \ - podhd.o diff --git a/drivers/staging/line6/audio.c b/drivers/staging/line6/audio.c deleted file mode 100644 index 171d80c1b020..000000000000 --- a/drivers/staging/line6/audio.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * 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. - * - */ - -#include <sound/core.h> -#include <sound/initval.h> -#include <linux/export.h> - -#include "driver.h" -#include "audio.h" - -/* - Initialize the Line6 USB audio system. -*/ -int line6_init_audio(struct usb_line6 *line6) -{ - struct snd_card *card; - int err; - - err = snd_card_new(line6->ifcdev, - SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, - THIS_MODULE, 0, &card); - if (err < 0) - return err; - - line6->card = card; - - strcpy(card->id, line6->properties->id); - strcpy(card->driver, DRIVER_NAME); - strcpy(card->shortname, line6->properties->name); - /* longname is 80 chars - see asound.h */ - sprintf(card->longname, "Line6 %s at USB %s", line6->properties->name, - dev_name(line6->ifcdev)); - return 0; -} - -/* - Register the Line6 USB audio system. -*/ -int line6_register_audio(struct usb_line6 *line6) -{ - int err; - - err = snd_card_register(line6->card); - if (err < 0) - return err; - - return 0; -} - -/* - Cleanup the Line6 USB audio system. -*/ -void line6_cleanup_audio(struct usb_line6 *line6) -{ - struct snd_card *card = line6->card; - - if (card == NULL) - return; - - snd_card_disconnect(card); - snd_card_free(card); - line6->card = NULL; -} diff --git a/drivers/staging/line6/audio.h b/drivers/staging/line6/audio.h deleted file mode 100644 index 5f8a09a0fa95..000000000000 --- a/drivers/staging/line6/audio.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * 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. - * - */ - -#ifndef AUDIO_H -#define AUDIO_H - -#include "driver.h" - -extern void line6_cleanup_audio(struct usb_line6 *); -extern int line6_init_audio(struct usb_line6 *); -extern int line6_register_audio(struct usb_line6 *); - -#endif diff --git a/drivers/staging/line6/capture.c b/drivers/staging/line6/capture.c deleted file mode 100644 index e6ca631e3f79..000000000000 --- a/drivers/staging/line6/capture.c +++ /dev/null @@ -1,432 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * 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. - * - */ - -#include <linux/slab.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> - -#include "audio.h" -#include "capture.h" -#include "driver.h" -#include "pcm.h" -#include "pod.h" - -/* - Find a free URB and submit it. -*/ -static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm) -{ - int index; - unsigned long flags; - int i, urb_size; - int ret; - struct urb *urb_in; - - spin_lock_irqsave(&line6pcm->lock_audio_in, flags); - index = - find_first_zero_bit(&line6pcm->active_urb_in, LINE6_ISO_BUFFERS); - - if (index < 0 || index >= LINE6_ISO_BUFFERS) { - spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); - dev_err(line6pcm->line6->ifcdev, "no free URB found\n"); - return -EINVAL; - } - - urb_in = line6pcm->urb_audio_in[index]; - urb_size = 0; - - for (i = 0; i < LINE6_ISO_PACKETS; ++i) { - struct usb_iso_packet_descriptor *fin = - &urb_in->iso_frame_desc[i]; - fin->offset = urb_size; - fin->length = line6pcm->max_packet_size; - urb_size += line6pcm->max_packet_size; - } - - urb_in->transfer_buffer = - line6pcm->buffer_in + - index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; - urb_in->transfer_buffer_length = urb_size; - urb_in->context = line6pcm; - - ret = usb_submit_urb(urb_in, GFP_ATOMIC); - - if (ret == 0) - set_bit(index, &line6pcm->active_urb_in); - else - dev_err(line6pcm->line6->ifcdev, - "URB in #%d submission failed (%d)\n", index, ret); - - spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); - return 0; -} - -/* - Submit all currently available capture URBs. -*/ -int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm) -{ - int ret, i; - - for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { - ret = submit_audio_in_urb(line6pcm); - if (ret < 0) - return ret; - } - - return 0; -} - -/* - Unlink all currently active capture URBs. -*/ -void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm) -{ - unsigned int i; - - for (i = LINE6_ISO_BUFFERS; i--;) { - if (test_bit(i, &line6pcm->active_urb_in)) { - if (!test_and_set_bit(i, &line6pcm->unlink_urb_in)) { - struct urb *u = line6pcm->urb_audio_in[i]; - - usb_unlink_urb(u); - } - } - } -} - -/* - Wait until unlinking of all currently active capture URBs has been - finished. -*/ -void line6_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) -{ - int timeout = HZ; - unsigned int i; - int alive; - - do { - alive = 0; - for (i = LINE6_ISO_BUFFERS; i--;) { - if (test_bit(i, &line6pcm->active_urb_in)) - alive++; - } - if (!alive) - break; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } while (--timeout > 0); - if (alive) - snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); -} - -/* - Unlink all currently active capture URBs, and wait for finishing. -*/ -void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) -{ - line6_unlink_audio_in_urbs(line6pcm); - line6_wait_clear_audio_in_urbs(line6pcm); -} - -/* - Copy data into ALSA capture buffer. -*/ -void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize) -{ - struct snd_pcm_substream *substream = - get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE); - struct snd_pcm_runtime *runtime = substream->runtime; - const int bytes_per_frame = line6pcm->properties->bytes_per_frame; - int frames = fsize / bytes_per_frame; - - if (runtime == NULL) - return; - - if (line6pcm->pos_in_done + frames > runtime->buffer_size) { - /* - The transferred area goes over buffer boundary, - copy two separate chunks. - */ - int len; - - len = runtime->buffer_size - line6pcm->pos_in_done; - - if (len > 0) { - memcpy(runtime->dma_area + - line6pcm->pos_in_done * bytes_per_frame, fbuf, - len * bytes_per_frame); - memcpy(runtime->dma_area, fbuf + len * bytes_per_frame, - (frames - len) * bytes_per_frame); - } else { - /* this is somewhat paranoid */ - dev_err(line6pcm->line6->ifcdev, - "driver bug: len = %d\n", len); - } - } else { - /* copy single chunk */ - memcpy(runtime->dma_area + - line6pcm->pos_in_done * bytes_per_frame, fbuf, fsize); - } - - line6pcm->pos_in_done += frames; - if (line6pcm->pos_in_done >= runtime->buffer_size) - line6pcm->pos_in_done -= runtime->buffer_size; -} - -void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length) -{ - struct snd_pcm_substream *substream = - get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE); - - line6pcm->bytes_in += length; - if (line6pcm->bytes_in >= line6pcm->period_in) { - line6pcm->bytes_in %= line6pcm->period_in; - snd_pcm_period_elapsed(substream); - } -} - -void line6_free_capture_buffer(struct snd_line6_pcm *line6pcm) -{ - kfree(line6pcm->buffer_in); - line6pcm->buffer_in = NULL; -} - -/* - * Callback for completed capture URB. - */ -static void audio_in_callback(struct urb *urb) -{ - int i, index, length = 0, shutdown = 0; - unsigned long flags; - - struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context; - - line6pcm->last_frame_in = urb->start_frame; - - /* find index of URB */ - for (index = 0; index < LINE6_ISO_BUFFERS; ++index) - if (urb == line6pcm->urb_audio_in[index]) - break; - - spin_lock_irqsave(&line6pcm->lock_audio_in, flags); - - for (i = 0; i < LINE6_ISO_PACKETS; ++i) { - char *fbuf; - int fsize; - struct usb_iso_packet_descriptor *fin = &urb->iso_frame_desc[i]; - - if (fin->status == -EXDEV) { - shutdown = 1; - break; - } - - fbuf = urb->transfer_buffer + fin->offset; - fsize = fin->actual_length; - - if (fsize > line6pcm->max_packet_size) { - dev_err(line6pcm->line6->ifcdev, - "driver and/or device bug: packet too large (%d > %d)\n", - fsize, line6pcm->max_packet_size); - } - - length += fsize; - - /* the following assumes LINE6_ISO_PACKETS == 1: */ - line6pcm->prev_fbuf = fbuf; - line6pcm->prev_fsize = fsize; - -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - if (!(line6pcm->flags & LINE6_BITS_PCM_IMPULSE)) -#endif - if (test_bit(LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, - &line6pcm->flags) && (fsize > 0)) - line6_capture_copy(line6pcm, fbuf, fsize); - } - - clear_bit(index, &line6pcm->active_urb_in); - - if (test_and_clear_bit(index, &line6pcm->unlink_urb_in)) - shutdown = 1; - - spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); - - if (!shutdown) { - submit_audio_in_urb(line6pcm); - -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - if (!(line6pcm->flags & LINE6_BITS_PCM_IMPULSE)) -#endif - if (test_bit(LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, - &line6pcm->flags)) - line6_capture_check_period(line6pcm, length); - } -} - -/* open capture callback */ -static int snd_line6_capture_open(struct snd_pcm_substream *substream) -{ - int err; - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - err = snd_pcm_hw_constraint_ratdens(runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - (&line6pcm-> - properties->snd_line6_rates)); - if (err < 0) - return err; - - runtime->hw = line6pcm->properties->snd_line6_capture_hw; - return 0; -} - -/* close capture callback */ -static int snd_line6_capture_close(struct snd_pcm_substream *substream) -{ - return 0; -} - -/* hw_params capture callback */ -static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - int ret; - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - /* -- Florian Demski [FD] */ - /* don't ask me why, but this fixes the bug on my machine */ - if (line6pcm == NULL) { - if (substream->pcm == NULL) - return -ENOMEM; - if (substream->pcm->private_data == NULL) - return -ENOMEM; - substream->private_data = substream->pcm->private_data; - line6pcm = snd_pcm_substream_chip(substream); - } - /* -- [FD] end */ - - ret = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); - - if (ret < 0) - return ret; - - ret = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - if (ret < 0) { - line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); - return ret; - } - - line6pcm->period_in = params_period_bytes(hw_params); - return 0; -} - -/* hw_free capture callback */ -static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); - return snd_pcm_lib_free_pages(substream); -} - -/* trigger callback */ -int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd) -{ - int err; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: -#ifdef CONFIG_PM - case SNDRV_PCM_TRIGGER_RESUME: -#endif - err = line6_pcm_acquire(line6pcm, - LINE6_BIT_PCM_ALSA_CAPTURE_STREAM); - - if (err < 0) - return err; - - break; - - case SNDRV_PCM_TRIGGER_STOP: -#ifdef CONFIG_PM - case SNDRV_PCM_TRIGGER_SUSPEND: -#endif - err = line6_pcm_release(line6pcm, - LINE6_BIT_PCM_ALSA_CAPTURE_STREAM); - - if (err < 0) - return err; - - break; - - default: - return -EINVAL; - } - - return 0; -} - -/* capture pointer callback */ -static snd_pcm_uframes_t -snd_line6_capture_pointer(struct snd_pcm_substream *substream) -{ - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - return line6pcm->pos_in_done; -} - -/* capture operators */ -struct snd_pcm_ops snd_line6_capture_ops = { - .open = snd_line6_capture_open, - .close = snd_line6_capture_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_line6_capture_hw_params, - .hw_free = snd_line6_capture_hw_free, - .prepare = snd_line6_prepare, - .trigger = snd_line6_trigger, - .pointer = snd_line6_capture_pointer, -}; - -int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm) -{ - int i; - - /* create audio URBs and fill in constant values: */ - for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { - struct urb *urb; - - /* URB for audio in: */ - urb = line6pcm->urb_audio_in[i] = - usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL); - - if (urb == NULL) { - dev_err(line6pcm->line6->ifcdev, "Out of memory\n"); - return -ENOMEM; - } - - urb->dev = line6pcm->line6->usbdev; - urb->pipe = - usb_rcvisocpipe(line6pcm->line6->usbdev, - line6pcm->ep_audio_read & - USB_ENDPOINT_NUMBER_MASK); - urb->transfer_flags = URB_ISO_ASAP; - urb->start_frame = -1; - urb->number_of_packets = LINE6_ISO_PACKETS; - urb->interval = LINE6_ISO_INTERVAL; - urb->error_count = 0; - urb->complete = audio_in_callback; - } - - return 0; -} diff --git a/drivers/staging/line6/capture.h b/drivers/staging/line6/capture.h deleted file mode 100644 index 4157bcb598a9..000000000000 --- a/drivers/staging/line6/capture.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * 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. - * - */ - -#ifndef CAPTURE_H -#define CAPTURE_H - -#include <sound/pcm.h> - -#include "driver.h" -#include "pcm.h" - -extern struct snd_pcm_ops snd_line6_capture_ops; - -extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, - int fsize); -extern void line6_capture_check_period(struct snd_line6_pcm *line6pcm, - int length); -extern int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm); -extern void line6_free_capture_buffer(struct snd_line6_pcm *line6pcm); -extern int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm); -extern void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm); -extern void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm - *line6pcm); -extern void line6_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm); -extern int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd); - -#endif diff --git a/drivers/staging/line6/driver.c b/drivers/staging/line6/driver.c deleted file mode 100644 index 503b2d763595..000000000000 --- a/drivers/staging/line6/driver.c +++ /dev/null @@ -1,1162 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * 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. - * - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/usb.h> - -#include "audio.h" -#include "capture.h" -#include "driver.h" -#include "midi.h" -#include "playback.h" -#include "pod.h" -#include "podhd.h" -#include "revision.h" -#include "toneport.h" -#include "usbdefs.h" -#include "variax.h" - -#define DRIVER_AUTHOR "Markus Grabner <grabner@icg.tugraz.at>" -#define DRIVER_DESC "Line6 USB Driver" -#define DRIVER_VERSION "0.9.1beta" DRIVER_REVISION - -/* table of devices that work with this driver */ -static const struct usb_device_id line6_id_table[] = { - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXT)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXTLIVE)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_BASSPODXTPRO)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_GUITARPORT)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_POCKETPOD)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODHD300)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODHD400)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODHD500)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_GX)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_UX1)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODSTUDIO_UX2)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODX3)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODX3LIVE)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXT)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXTLIVE)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_PODXTPRO)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_TONEPORT_GX)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_TONEPORT_UX1)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_TONEPORT_UX2)}, - {USB_DEVICE(LINE6_VENDOR_ID, LINE6_DEVID_VARIAX)}, - {}, -}; - -MODULE_DEVICE_TABLE(usb, line6_id_table); - -#define L6PROP(dev_bit, dev_id, dev_name, dev_cap)\ - {.device_bit = LINE6_BIT_##dev_bit, .id = dev_id,\ - .name = dev_name, .capabilities = LINE6_BIT_##dev_cap} - -/* *INDENT-OFF* */ -static const struct line6_properties line6_properties_table[] = { - L6PROP(BASSPODXT, "BassPODxt", "BassPODxt", CTRL_PCM_HW), - L6PROP(BASSPODXTLIVE, "BassPODxtLive", "BassPODxt Live", CTRL_PCM_HW), - L6PROP(BASSPODXTPRO, "BassPODxtPro", "BassPODxt Pro", CTRL_PCM_HW), - L6PROP(GUITARPORT, "GuitarPort", "GuitarPort", PCM), - L6PROP(POCKETPOD, "PocketPOD", "Pocket POD", CONTROL), - L6PROP(PODHD300, "PODHD300", "POD HD300", CTRL_PCM_HW), - L6PROP(PODHD400, "PODHD400", "POD HD400", CTRL_PCM_HW), - L6PROP(PODHD500, "PODHD500", "POD HD500", CTRL_PCM_HW), - L6PROP(PODSTUDIO_GX, "PODStudioGX", "POD Studio GX", PCM), - L6PROP(PODSTUDIO_UX1, "PODStudioUX1", "POD Studio UX1", PCM), - L6PROP(PODSTUDIO_UX2, "PODStudioUX2", "POD Studio UX2", PCM), - L6PROP(PODX3, "PODX3", "POD X3", PCM), - L6PROP(PODX3LIVE, "PODX3Live", "POD X3 Live", PCM), - L6PROP(PODXT, "PODxt", "PODxt", CTRL_PCM_HW), - L6PROP(PODXTLIVE, "PODxtLive", "PODxt Live", CTRL_PCM_HW), - L6PROP(PODXTPRO, "PODxtPro", "PODxt Pro", CTRL_PCM_HW), - L6PROP(TONEPORT_GX, "TonePortGX", "TonePort GX", PCM), - L6PROP(TONEPORT_UX1, "TonePortUX1", "TonePort UX1", PCM), - L6PROP(TONEPORT_UX2, "TonePortUX2", "TonePort UX2", PCM), - L6PROP(VARIAX, "Variax", "Variax Workbench", CONTROL), -}; -/* *INDENT-ON* */ - -/* - This is Line6's MIDI manufacturer ID. -*/ -const unsigned char line6_midi_id[] = { - 0x00, 0x01, 0x0c -}; - -/* - Code to request version of POD, Variax interface - (and maybe other devices). -*/ -static const char line6_request_version[] = { - 0xf0, 0x7e, 0x7f, 0x06, 0x01, 0xf7 -}; - -/** - Class for asynchronous messages. -*/ -struct message { - struct usb_line6 *line6; - const char *buffer; - int size; - int done; -}; - -/* - Forward declarations. -*/ -static void line6_data_received(struct urb *urb); -static int line6_send_raw_message_async_part(struct message *msg, - struct urb *urb); - -/* - Start to listen on endpoint. -*/ -static int line6_start_listen(struct usb_line6 *line6) -{ - int err; - - usb_fill_int_urb(line6->urb_listen, line6->usbdev, - usb_rcvintpipe(line6->usbdev, line6->ep_control_read), - line6->buffer_listen, LINE6_BUFSIZE_LISTEN, - line6_data_received, line6, line6->interval); - line6->urb_listen->actual_length = 0; - err = usb_submit_urb(line6->urb_listen, GFP_ATOMIC); - return err; -} - -/* - Stop listening on endpoint. -*/ -static void line6_stop_listen(struct usb_line6 *line6) -{ - usb_kill_urb(line6->urb_listen); -} - -/* - Send raw message in pieces of wMaxPacketSize bytes. -*/ -int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, - int size) -{ - int i, done = 0; - - for (i = 0; i < size; i += line6->max_packet_size) { - int partial; - const char *frag_buf = buffer + i; - int frag_size = min(line6->max_packet_size, size - i); - int retval; - - retval = usb_interrupt_msg(line6->usbdev, - usb_sndintpipe(line6->usbdev, - line6->ep_control_write), - (char *)frag_buf, frag_size, - &partial, LINE6_TIMEOUT * HZ); - - if (retval) { - dev_err(line6->ifcdev, - "usb_interrupt_msg failed (%d)\n", retval); - break; - } - - done += frag_size; - } - - return done; -} - -/* - Notification of completion of asynchronous request transmission. -*/ -static void line6_async_request_sent(struct urb *urb) -{ - struct message *msg = (struct message *)urb->context; - - if (msg->done >= msg->size) { - usb_free_urb(urb); - kfree(msg); - } else - line6_send_raw_message_async_part(msg, urb); -} - -/* - Asynchronously send part of a raw message. -*/ -static int line6_send_raw_message_async_part(struct message *msg, - struct urb *urb) -{ - int retval; - struct usb_line6 *line6 = msg->line6; - int done = msg->done; - int bytes = min(msg->size - done, line6->max_packet_size); - - usb_fill_int_urb(urb, line6->usbdev, - usb_sndintpipe(line6->usbdev, line6->ep_control_write), - (char *)msg->buffer + done, bytes, - line6_async_request_sent, msg, line6->interval); - - msg->done += bytes; - retval = usb_submit_urb(urb, GFP_ATOMIC); - - if (retval < 0) { - dev_err(line6->ifcdev, "%s: usb_submit_urb failed (%d)\n", - __func__, retval); - usb_free_urb(urb); - kfree(msg); - return retval; - } - - return 0; -} - -/* - Setup and start timer. -*/ -void line6_start_timer(struct timer_list *timer, unsigned int msecs, - void (*function)(unsigned long), unsigned long data) -{ - setup_timer(timer, function, data); - timer->expires = jiffies + msecs * HZ / 1000; - add_timer(timer); -} - -/* - Asynchronously send raw message. -*/ -int line6_send_raw_message_async(struct usb_line6 *line6, const char *buffer, - int size) -{ - struct message *msg; - struct urb *urb; - - /* create message: */ - msg = kmalloc(sizeof(struct message), GFP_ATOMIC); - if (msg == NULL) - return -ENOMEM; - - /* create URB: */ - urb = usb_alloc_urb(0, GFP_ATOMIC); - - if (urb == NULL) { - kfree(msg); - dev_err(line6->ifcdev, "Out of memory\n"); - return -ENOMEM; - } - - /* set message data: */ - msg->line6 = line6; - msg->buffer = buffer; - msg->size = size; - msg->done = 0; - - /* start sending: */ - return line6_send_raw_message_async_part(msg, urb); -} - -/* - Send asynchronous device version request. -*/ -int line6_version_request_async(struct usb_line6 *line6) -{ - char *buffer; - int retval; - - buffer = kmemdup(line6_request_version, - sizeof(line6_request_version), GFP_ATOMIC); - if (buffer == NULL) { - dev_err(line6->ifcdev, "Out of memory"); - return -ENOMEM; - } - - retval = line6_send_raw_message_async(line6, buffer, - sizeof(line6_request_version)); - kfree(buffer); - return retval; -} - -/* - Send sysex message in pieces of wMaxPacketSize bytes. -*/ -int line6_send_sysex_message(struct usb_line6 *line6, const char *buffer, - int size) -{ - return line6_send_raw_message(line6, buffer, - size + SYSEX_EXTRA_SIZE) - - SYSEX_EXTRA_SIZE; -} - -/* - Allocate buffer for sysex message and prepare header. - @param code sysex message code - @param size number of bytes between code and sysex end -*/ -char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, int code2, - int size) -{ - char *buffer = kmalloc(size + SYSEX_EXTRA_SIZE, GFP_ATOMIC); - - if (!buffer) - return NULL; - - buffer[0] = LINE6_SYSEX_BEGIN; - memcpy(buffer + 1, line6_midi_id, sizeof(line6_midi_id)); - buffer[sizeof(line6_midi_id) + 1] = code1; - buffer[sizeof(line6_midi_id) + 2] = code2; - buffer[sizeof(line6_midi_id) + 3 + size] = LINE6_SYSEX_END; - return buffer; -} - -/* - Notification of data received from the Line6 device. -*/ -static void line6_data_received(struct urb *urb) -{ - struct usb_line6 *line6 = (struct usb_line6 *)urb->context; - struct midi_buffer *mb = &line6->line6midi->midibuf_in; - int done; - - if (urb->status == -ESHUTDOWN) - return; - - done = - line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length); - - if (done < urb->actual_length) { - line6_midibuf_ignore(mb, done); - dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n", - done, urb->actual_length); - } - - for (;;) { - done = - line6_midibuf_read(mb, line6->buffer_message, - LINE6_MESSAGE_MAXLEN); - - if (done == 0) - break; - - line6->message_length = done; - line6_midi_receive(line6, line6->buffer_message, done); - - switch (le16_to_cpu(line6->usbdev->descriptor.idProduct)) { - case LINE6_DEVID_BASSPODXT: - case LINE6_DEVID_BASSPODXTLIVE: - case LINE6_DEVID_BASSPODXTPRO: - case LINE6_DEVID_PODXT: - case LINE6_DEVID_PODXTPRO: - case LINE6_DEVID_POCKETPOD: - line6_pod_process_message((struct usb_line6_pod *) - line6); - break; - - case LINE6_DEVID_PODHD300: - case LINE6_DEVID_PODHD400: - case LINE6_DEVID_PODHD500: - break; /* let userspace handle MIDI */ - - case LINE6_DEVID_PODXTLIVE: - switch (line6->interface_number) { - case PODXTLIVE_INTERFACE_POD: - line6_pod_process_message((struct usb_line6_pod - *)line6); - break; - - case PODXTLIVE_INTERFACE_VARIAX: - line6_variax_process_message((struct - usb_line6_variax - *)line6); - break; - - default: - dev_err(line6->ifcdev, - "PODxt Live interface %d not supported\n", - line6->interface_number); - } - break; - - case LINE6_DEVID_VARIAX: - line6_variax_process_message((struct usb_line6_variax *) - line6); - break; - - default: - MISSING_CASE; - } - } - - line6_start_listen(line6); -} - -/* - Send channel number (i.e., switch to a different sound). -*/ -int line6_send_program(struct usb_line6 *line6, u8 value) -{ - int retval; - unsigned char *buffer; - int partial; - - buffer = kmalloc(2, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - buffer[0] = LINE6_PROGRAM_CHANGE | LINE6_CHANNEL_HOST; - buffer[1] = value; - - retval = usb_interrupt_msg(line6->usbdev, - usb_sndintpipe(line6->usbdev, - line6->ep_control_write), - buffer, 2, &partial, LINE6_TIMEOUT * HZ); - - if (retval) - dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", - retval); - - kfree(buffer); - return retval; -} - -/* - Transmit Line6 control parameter. -*/ -int line6_transmit_parameter(struct usb_line6 *line6, int param, u8 value) -{ - int retval; - unsigned char *buffer; - int partial; - - buffer = kmalloc(3, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - buffer[0] = LINE6_PARAM_CHANGE | LINE6_CHANNEL_HOST; - buffer[1] = param; - buffer[2] = value; - - retval = usb_interrupt_msg(line6->usbdev, - usb_sndintpipe(line6->usbdev, - line6->ep_control_write), - buffer, 3, &partial, LINE6_TIMEOUT * HZ); - - if (retval) - dev_err(line6->ifcdev, "usb_interrupt_msg failed (%d)\n", - retval); - - kfree(buffer); - return retval; -} - -/* - Read data from device. -*/ -int line6_read_data(struct usb_line6 *line6, int address, void *data, - size_t datalen) -{ - struct usb_device *usbdev = line6->usbdev; - int ret; - unsigned char len; - - /* query the serial number: */ - ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, - (datalen << 8) | 0x21, address, - NULL, 0, LINE6_TIMEOUT * HZ); - - if (ret < 0) { - dev_err(line6->ifcdev, "read request failed (error %d)\n", ret); - return ret; - } - - /* Wait for data length. We'll get 0xff until length arrives. */ - do { - ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | - USB_DIR_IN, - 0x0012, 0x0000, &len, 1, - LINE6_TIMEOUT * HZ); - if (ret < 0) { - dev_err(line6->ifcdev, - "receive length failed (error %d)\n", ret); - return ret; - } - } while (len == 0xff); - - if (len != datalen) { - /* should be equal or something went wrong */ - dev_err(line6->ifcdev, - "length mismatch (expected %d, got %d)\n", - (int)datalen, (int)len); - return -EINVAL; - } - - /* receive the result: */ - ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x0013, 0x0000, data, datalen, - LINE6_TIMEOUT * HZ); - - if (ret < 0) { - dev_err(line6->ifcdev, "read failed (error %d)\n", ret); - return ret; - } - - return 0; -} - -/* - Write data to device. -*/ -int line6_write_data(struct usb_line6 *line6, int address, void *data, - size_t datalen) -{ - struct usb_device *usbdev = line6->usbdev; - int ret; - unsigned char status; - - ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, - 0x0022, address, data, datalen, - LINE6_TIMEOUT * HZ); - - if (ret < 0) { - dev_err(line6->ifcdev, - "write request failed (error %d)\n", ret); - return ret; - } - - do { - ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), - 0x67, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | - USB_DIR_IN, - 0x0012, 0x0000, - &status, 1, LINE6_TIMEOUT * HZ); - - if (ret < 0) { - dev_err(line6->ifcdev, - "receiving status failed (error %d)\n", ret); - return ret; - } - } while (status == 0xff); - - if (status != 0) { - dev_err(line6->ifcdev, "write failed (error %d)\n", ret); - return -EINVAL; - } - - return 0; -} - -/* - Read Line6 device serial number. - (POD, TonePort, GuitarPort) -*/ -int line6_read_serial_number(struct usb_line6 *line6, int *serial_number) -{ - return line6_read_data(line6, 0x80d0, serial_number, - sizeof(*serial_number)); -} - -/* - No operation (i.e., unsupported). -*/ -ssize_t line6_nop_read(struct device *dev, struct device_attribute *attr, - char *buf) -{ - return 0; -} - -/* - Generic destructor. -*/ -static void line6_destruct(struct usb_interface *interface) -{ - struct usb_line6 *line6; - - if (interface == NULL) - return; - line6 = usb_get_intfdata(interface); - if (line6 == NULL) - return; - - /* free buffer memory first: */ - kfree(line6->buffer_message); - kfree(line6->buffer_listen); - - /* then free URBs: */ - usb_free_urb(line6->urb_listen); - - /* make sure the device isn't destructed twice: */ - usb_set_intfdata(interface, NULL); - - /* free interface data: */ - kfree(line6); -} - -/* - Probe USB device. -*/ -static int line6_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - int devtype; - struct usb_device *usbdev; - struct usb_line6 *line6; - const struct line6_properties *properties; - int interface_number, alternate = 0; - int product; - int size = 0; - int ep_read = 0, ep_write = 0; - int ret; - - if (interface == NULL) - return -ENODEV; - usbdev = interface_to_usbdev(interface); - if (usbdev == NULL) - return -ENODEV; - - /* we don't handle multiple configurations */ - if (usbdev->descriptor.bNumConfigurations != 1) { - ret = -ENODEV; - goto err_put; - } - - /* check vendor and product id */ - for (devtype = ARRAY_SIZE(line6_id_table) - 1; devtype--;) { - u16 idVendor = le16_to_cpu(usbdev->descriptor.idVendor); - u16 idProduct = le16_to_cpu(usbdev->descriptor.idProduct); - - if (idVendor == line6_id_table[devtype].idVendor && - idProduct == line6_id_table[devtype].idProduct) - break; - } - - if (devtype < 0) { - ret = -ENODEV; - goto err_put; - } - - /* initialize device info: */ - properties = &line6_properties_table[devtype]; - dev_info(&interface->dev, "Line6 %s found\n", properties->name); - product = le16_to_cpu(usbdev->descriptor.idProduct); - - /* query interface number */ - interface_number = interface->cur_altsetting->desc.bInterfaceNumber; - - switch (product) { - case LINE6_DEVID_BASSPODXTLIVE: - case LINE6_DEVID_PODXTLIVE: - case LINE6_DEVID_VARIAX: - alternate = 1; - break; - - case LINE6_DEVID_POCKETPOD: - switch (interface_number) { - case 0: - return -ENODEV; /* this interface has no endpoints */ - case 1: - alternate = 0; - break; - default: - MISSING_CASE; - } - break; - - case LINE6_DEVID_PODHD500: - case LINE6_DEVID_PODX3: - case LINE6_DEVID_PODX3LIVE: - switch (interface_number) { - case 0: - alternate = 1; - break; - case 1: - alternate = 0; - break; - default: - MISSING_CASE; - } - break; - - case LINE6_DEVID_BASSPODXT: - case LINE6_DEVID_BASSPODXTPRO: - case LINE6_DEVID_PODXT: - case LINE6_DEVID_PODXTPRO: - case LINE6_DEVID_PODHD300: - case LINE6_DEVID_PODHD400: - alternate = 5; - break; - - case LINE6_DEVID_GUITARPORT: - case LINE6_DEVID_PODSTUDIO_GX: - case LINE6_DEVID_PODSTUDIO_UX1: - case LINE6_DEVID_TONEPORT_GX: - case LINE6_DEVID_TONEPORT_UX1: - alternate = 2; /* 1..4 seem to be ok */ - break; - - case LINE6_DEVID_TONEPORT_UX2: - case LINE6_DEVID_PODSTUDIO_UX2: - switch (interface_number) { - case 0: - /* defaults to 44.1kHz, 16-bit */ - alternate = 2; - break; - case 1: - /* don't know yet what this is ... - alternate = 1; - break; - */ - return -ENODEV; - default: - MISSING_CASE; - } - break; - - default: - MISSING_CASE; - ret = -ENODEV; - goto err_put; - } - - ret = usb_set_interface(usbdev, interface_number, alternate); - if (ret < 0) { - dev_err(&interface->dev, "set_interface failed\n"); - goto err_put; - } - - /* initialize device data based on product id: */ - switch (product) { - case LINE6_DEVID_BASSPODXT: - case LINE6_DEVID_BASSPODXTLIVE: - case LINE6_DEVID_BASSPODXTPRO: - case LINE6_DEVID_PODXT: - case LINE6_DEVID_PODXTPRO: - size = sizeof(struct usb_line6_pod); - ep_read = 0x84; - ep_write = 0x03; - break; - - case LINE6_DEVID_PODHD300: - case LINE6_DEVID_PODHD400: - size = sizeof(struct usb_line6_podhd); - ep_read = 0x84; - ep_write = 0x03; - break; - - case LINE6_DEVID_PODHD500: - size = sizeof(struct usb_line6_podhd); - ep_read = 0x81; - ep_write = 0x01; - break; - - case LINE6_DEVID_POCKETPOD: - size = sizeof(struct usb_line6_pod); - ep_read = 0x82; - ep_write = 0x02; - break; - - case LINE6_DEVID_PODX3: - case LINE6_DEVID_PODX3LIVE: - /* currently unused! */ - size = sizeof(struct usb_line6_pod); - ep_read = 0x81; - ep_write = 0x01; - break; - - case LINE6_DEVID_PODSTUDIO_GX: - case LINE6_DEVID_PODSTUDIO_UX1: - case LINE6_DEVID_PODSTUDIO_UX2: - case LINE6_DEVID_TONEPORT_GX: - case LINE6_DEVID_TONEPORT_UX1: - case LINE6_DEVID_TONEPORT_UX2: - case LINE6_DEVID_GUITARPORT: - size = sizeof(struct usb_line6_toneport); - /* these don't have a control channel */ - break; - - case LINE6_DEVID_PODXTLIVE: - switch (interface_number) { - case PODXTLIVE_INTERFACE_POD: - size = sizeof(struct usb_line6_pod); - ep_read = 0x84; - ep_write = 0x03; - break; - - case PODXTLIVE_INTERFACE_VARIAX: - size = sizeof(struct usb_line6_variax); - ep_read = 0x86; - ep_write = 0x05; - break; - - default: - ret = -ENODEV; - goto err_put; - } - break; - - case LINE6_DEVID_VARIAX: - size = sizeof(struct usb_line6_variax); - ep_read = 0x82; - ep_write = 0x01; - break; - - default: - MISSING_CASE; - ret = -ENODEV; - goto err_put; - } - - if (size == 0) { - dev_err(&interface->dev, - "driver bug: interface data size not set\n"); - ret = -ENODEV; - goto err_put; - } - - line6 = kzalloc(size, GFP_KERNEL); - if (line6 == NULL) { - ret = -ENODEV; - goto err_put; - } - - /* store basic data: */ - line6->interface_number = interface_number; - line6->properties = properties; - line6->usbdev = usbdev; - line6->ifcdev = &interface->dev; - line6->ep_control_read = ep_read; - line6->ep_control_write = ep_write; - line6->product = product; - - /* get data from endpoint descriptor (see usb_maxpacket): */ - { - struct usb_host_endpoint *ep; - unsigned epnum = - usb_pipeendpoint(usb_rcvintpipe(usbdev, ep_read)); - ep = usbdev->ep_in[epnum]; - - if (ep != NULL) { - line6->interval = ep->desc.bInterval; - line6->max_packet_size = - le16_to_cpu(ep->desc.wMaxPacketSize); - } else { - line6->interval = LINE6_FALLBACK_INTERVAL; - line6->max_packet_size = LINE6_FALLBACK_MAXPACKETSIZE; - dev_err(line6->ifcdev, - "endpoint not available, using fallback values"); - } - } - - usb_set_intfdata(interface, line6); - - if (properties->capabilities & LINE6_BIT_CONTROL) { - /* initialize USB buffers: */ - line6->buffer_listen = - kmalloc(LINE6_BUFSIZE_LISTEN, GFP_KERNEL); - if (line6->buffer_listen == NULL) { - ret = -ENOMEM; - goto err_destruct; - } - - line6->buffer_message = - kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL); - if (line6->buffer_message == NULL) { - ret = -ENOMEM; - goto err_destruct; - } - - line6->urb_listen = usb_alloc_urb(0, GFP_KERNEL); - - if (line6->urb_listen == NULL) { - dev_err(&interface->dev, "Out of memory\n"); - line6_destruct(interface); - ret = -ENOMEM; - goto err_destruct; - } - - ret = line6_start_listen(line6); - if (ret < 0) { - dev_err(&interface->dev, "%s: usb_submit_urb failed\n", - __func__); - goto err_destruct; - } - } - - /* initialize device data based on product id: */ - switch (product) { - case LINE6_DEVID_BASSPODXT: - case LINE6_DEVID_BASSPODXTLIVE: - case LINE6_DEVID_BASSPODXTPRO: - case LINE6_DEVID_POCKETPOD: - case LINE6_DEVID_PODX3: - case LINE6_DEVID_PODX3LIVE: - case LINE6_DEVID_PODXT: - case LINE6_DEVID_PODXTPRO: - ret = line6_pod_init(interface, (struct usb_line6_pod *)line6); - break; - - case LINE6_DEVID_PODHD300: - case LINE6_DEVID_PODHD400: - case LINE6_DEVID_PODHD500: - ret = line6_podhd_init(interface, - (struct usb_line6_podhd *)line6); - break; - - case LINE6_DEVID_PODXTLIVE: - switch (interface_number) { - case PODXTLIVE_INTERFACE_POD: - ret = - line6_pod_init(interface, - (struct usb_line6_pod *)line6); - break; - - case PODXTLIVE_INTERFACE_VARIAX: - ret = - line6_variax_init(interface, - (struct usb_line6_variax *)line6); - break; - - default: - dev_err(&interface->dev, - "PODxt Live interface %d not supported\n", - interface_number); - ret = -ENODEV; - } - - break; - - case LINE6_DEVID_VARIAX: - ret = - line6_variax_init(interface, - (struct usb_line6_variax *)line6); - break; - - case LINE6_DEVID_PODSTUDIO_GX: - case LINE6_DEVID_PODSTUDIO_UX1: - case LINE6_DEVID_PODSTUDIO_UX2: - case LINE6_DEVID_TONEPORT_GX: - case LINE6_DEVID_TONEPORT_UX1: - case LINE6_DEVID_TONEPORT_UX2: - case LINE6_DEVID_GUITARPORT: - ret = - line6_toneport_init(interface, - (struct usb_line6_toneport *)line6); - break; - - default: - MISSING_CASE; - ret = -ENODEV; - } - - if (ret < 0) - goto err_destruct; - - ret = sysfs_create_link(&interface->dev.kobj, &usbdev->dev.kobj, - "usb_device"); - if (ret < 0) - goto err_destruct; - - /* creation of additional special files should go here */ - - dev_info(&interface->dev, "Line6 %s now attached\n", - line6->properties->name); - - switch (product) { - case LINE6_DEVID_PODX3: - case LINE6_DEVID_PODX3LIVE: - dev_info(&interface->dev, - "NOTE: the Line6 %s is detected, but not yet supported\n", - line6->properties->name); - } - - /* increment reference counters: */ - usb_get_intf(interface); - usb_get_dev(usbdev); - - return 0; - -err_destruct: - line6_destruct(interface); -err_put: - return ret; -} - -/* - Line6 device disconnected. -*/ -static void line6_disconnect(struct usb_interface *interface) -{ - struct usb_line6 *line6; - struct usb_device *usbdev; - int interface_number; - - if (interface == NULL) - return; - usbdev = interface_to_usbdev(interface); - if (usbdev == NULL) - return; - - /* removal of additional special files should go here */ - - sysfs_remove_link(&interface->dev.kobj, "usb_device"); - - interface_number = interface->cur_altsetting->desc.bInterfaceNumber; - line6 = usb_get_intfdata(interface); - - if (line6 != NULL) { - if (line6->urb_listen != NULL) - line6_stop_listen(line6); - - if (usbdev != line6->usbdev) - dev_err(line6->ifcdev, - "driver bug: inconsistent usb device\n"); - - switch (le16_to_cpu(line6->usbdev->descriptor.idProduct)) { - case LINE6_DEVID_BASSPODXT: - case LINE6_DEVID_BASSPODXTLIVE: - case LINE6_DEVID_BASSPODXTPRO: - case LINE6_DEVID_POCKETPOD: - case LINE6_DEVID_PODX3: - case LINE6_DEVID_PODX3LIVE: - case LINE6_DEVID_PODXT: - case LINE6_DEVID_PODXTPRO: - line6_pod_disconnect(interface); - break; - - case LINE6_DEVID_PODHD300: - case LINE6_DEVID_PODHD400: - case LINE6_DEVID_PODHD500: - line6_podhd_disconnect(interface); - break; - - case LINE6_DEVID_PODXTLIVE: - switch (interface_number) { - case PODXTLIVE_INTERFACE_POD: - line6_pod_disconnect(interface); - break; - - case PODXTLIVE_INTERFACE_VARIAX: - line6_variax_disconnect(interface); - break; - } - - break; - - case LINE6_DEVID_VARIAX: - line6_variax_disconnect(interface); - break; - - case LINE6_DEVID_PODSTUDIO_GX: - case LINE6_DEVID_PODSTUDIO_UX1: - case LINE6_DEVID_PODSTUDIO_UX2: - case LINE6_DEVID_TONEPORT_GX: - case LINE6_DEVID_TONEPORT_UX1: - case LINE6_DEVID_TONEPORT_UX2: - case LINE6_DEVID_GUITARPORT: - line6_toneport_disconnect(interface); - break; - - default: - MISSING_CASE; - } - - dev_info(&interface->dev, "Line6 %s now disconnected\n", - line6->properties->name); - } - - line6_destruct(interface); - - /* decrement reference counters: */ - usb_put_intf(interface); - usb_put_dev(usbdev); -} - -#ifdef CONFIG_PM - -/* - Suspend Line6 device. -*/ -static int line6_suspend(struct usb_interface *interface, pm_message_t message) -{ - struct usb_line6 *line6 = usb_get_intfdata(interface); - struct snd_line6_pcm *line6pcm = line6->line6pcm; - - snd_power_change_state(line6->card, SNDRV_CTL_POWER_D3hot); - - if (line6->properties->capabilities & LINE6_BIT_CONTROL) - line6_stop_listen(line6); - - if (line6pcm != NULL) { - snd_pcm_suspend_all(line6pcm->pcm); - line6_pcm_disconnect(line6pcm); - line6pcm->flags = 0; - } - - return 0; -} - -/* - Resume Line6 device. -*/ -static int line6_resume(struct usb_interface *interface) -{ - struct usb_line6 *line6 = usb_get_intfdata(interface); - - if (line6->properties->capabilities & LINE6_BIT_CONTROL) - line6_start_listen(line6); - - snd_power_change_state(line6->card, SNDRV_CTL_POWER_D0); - return 0; -} - -/* - Resume Line6 device after reset. -*/ -static int line6_reset_resume(struct usb_interface *interface) -{ - struct usb_line6 *line6 = usb_get_intfdata(interface); - - switch (le16_to_cpu(line6->usbdev->descriptor.idProduct)) { - case LINE6_DEVID_PODSTUDIO_GX: - case LINE6_DEVID_PODSTUDIO_UX1: - case LINE6_DEVID_PODSTUDIO_UX2: - case LINE6_DEVID_TONEPORT_GX: - case LINE6_DEVID_TONEPORT_UX1: - case LINE6_DEVID_TONEPORT_UX2: - case LINE6_DEVID_GUITARPORT: - line6_toneport_reset_resume((struct usb_line6_toneport *)line6); - } - - return line6_resume(interface); -} - -#endif /* CONFIG_PM */ - -static struct usb_driver line6_driver = { - .name = DRIVER_NAME, - .probe = line6_probe, - .disconnect = line6_disconnect, -#ifdef CONFIG_PM - .suspend = line6_suspend, - .resume = line6_resume, - .reset_resume = line6_reset_resume, -#endif - .id_table = line6_id_table, -}; - -module_usb_driver(line6_driver); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/staging/line6/driver.h b/drivers/staging/line6/driver.h deleted file mode 100644 index 16e3fc2f1f15..000000000000 --- a/drivers/staging/line6/driver.h +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * 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. - * - */ - -#ifndef DRIVER_H -#define DRIVER_H - -#include <linux/spinlock.h> -#include <linux/usb.h> -#include <sound/core.h> - -#include "midi.h" - -#define DRIVER_NAME "line6usb" - -#define LINE6_TIMEOUT 1 -#define LINE6_BUFSIZE_LISTEN 32 -#define LINE6_MESSAGE_MAXLEN 256 - -/* - Line6 MIDI control commands -*/ -#define LINE6_PARAM_CHANGE 0xb0 -#define LINE6_PROGRAM_CHANGE 0xc0 -#define LINE6_SYSEX_BEGIN 0xf0 -#define LINE6_SYSEX_END 0xf7 -#define LINE6_RESET 0xff - -/* - MIDI channel for messages initiated by the host - (and eventually echoed back by the device) -*/ -#define LINE6_CHANNEL_HOST 0x00 - -/* - MIDI channel for messages initiated by the device -*/ -#define LINE6_CHANNEL_DEVICE 0x02 - -#define LINE6_CHANNEL_UNKNOWN 5 /* don't know yet what this is good for */ - -#define LINE6_CHANNEL_MASK 0x0f - -#define MISSING_CASE \ - pr_err("line6usb driver bug: missing case in %s:%d\n", \ - __FILE__, __LINE__) - -#define CHECK_RETURN(x) \ -do { \ - err = x; \ - if (err < 0) \ - return err; \ -} while (0) - -#define CHECK_STARTUP_PROGRESS(x, n) \ -do { \ - if ((x) >= (n)) \ - return; \ - x = (n); \ -} while (0) - -extern const unsigned char line6_midi_id[3]; - -static const int SYSEX_DATA_OFS = sizeof(line6_midi_id) + 3; -static const int SYSEX_EXTRA_SIZE = sizeof(line6_midi_id) + 4; - -/** - Common properties of Line6 devices. -*/ -struct line6_properties { - /** - Bit identifying this device in the line6usb driver. - */ - int device_bit; - - /** - Card id string (maximum 16 characters). - This can be used to address the device in ALSA programs as - "default:CARD=<id>" - */ - const char *id; - - /** - Card short name (maximum 32 characters). - */ - const char *name; - - /** - Bit vector defining this device's capabilities in the - line6usb driver. - */ - int capabilities; -}; - -/** - Common data shared by all Line6 devices. - Corresponds to a pair of USB endpoints. -*/ -struct usb_line6 { - /** - USB device. - */ - struct usb_device *usbdev; - - /** - Product id. - */ - int product; - - /** - Properties. - */ - const struct line6_properties *properties; - - /** - Interface number. - */ - int interface_number; - - /** - Interval (ms). - */ - int interval; - - /** - Maximum size of USB packet. - */ - int max_packet_size; - - /** - Device representing the USB interface. - */ - struct device *ifcdev; - - /** - Line6 sound card data structure. - Each device has at least MIDI or PCM. - */ - struct snd_card *card; - - /** - Line6 PCM device data structure. - */ - struct snd_line6_pcm *line6pcm; - - /** - Line6 MIDI device data structure. - */ - struct snd_line6_midi *line6midi; - - /** - USB endpoint for listening to control commands. - */ - int ep_control_read; - - /** - USB endpoint for writing control commands. - */ - int ep_control_write; - - /** - URB for listening to PODxt Pro control endpoint. - */ - struct urb *urb_listen; - - /** - Buffer for listening to PODxt Pro control endpoint. - */ - unsigned char *buffer_listen; - - /** - Buffer for message to be processed. - */ - unsigned char *buffer_message; - - /** - Length of message to be processed. - */ - int message_length; -}; - -extern char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, - int code2, int size); -extern ssize_t line6_nop_read(struct device *dev, - struct device_attribute *attr, char *buf); -extern int line6_read_data(struct usb_line6 *line6, int address, void *data, - size_t datalen); -extern int line6_read_serial_number(struct usb_line6 *line6, - int *serial_number); -extern int line6_send_program(struct usb_line6 *line6, u8 value); -extern int line6_send_raw_message(struct usb_line6 *line6, const char *buffer, - int size); -extern int line6_send_raw_message_async(struct usb_line6 *line6, - const char *buffer, int size); -extern int line6_send_sysex_message(struct usb_line6 *line6, - const char *buffer, int size); -extern ssize_t line6_set_raw(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count); -extern void line6_start_timer(struct timer_list *timer, unsigned int msecs, - void (*function)(unsigned long), - unsigned long data); -extern int line6_transmit_parameter(struct usb_line6 *line6, int param, - u8 value); -extern int line6_version_request_async(struct usb_line6 *line6); -extern int line6_write_data(struct usb_line6 *line6, int address, void *data, - size_t datalen); - -#endif diff --git a/drivers/staging/line6/midi.c b/drivers/staging/line6/midi.c deleted file mode 100644 index 1ac343b649c1..000000000000 --- a/drivers/staging/line6/midi.c +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * 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. - * - */ - -#include <linux/slab.h> -#include <linux/usb.h> -#include <sound/core.h> -#include <sound/rawmidi.h> - -#include "audio.h" -#include "driver.h" -#include "midi.h" -#include "pod.h" -#include "usbdefs.h" - -#define line6_rawmidi_substream_midi(substream) \ - ((struct snd_line6_midi *)((substream)->rmidi->private_data)) - -static int send_midi_async(struct usb_line6 *line6, unsigned char *data, - int length); - -/* - Pass data received via USB to MIDI. -*/ -void line6_midi_receive(struct usb_line6 *line6, unsigned char *data, - int length) -{ - if (line6->line6midi->substream_receive) - snd_rawmidi_receive(line6->line6midi->substream_receive, - data, length); -} - -/* - Read data from MIDI buffer and transmit them via USB. -*/ -static void line6_midi_transmit(struct snd_rawmidi_substream *substream) -{ - struct usb_line6 *line6 = - line6_rawmidi_substream_midi(substream)->line6; - struct snd_line6_midi *line6midi = line6->line6midi; - struct midi_buffer *mb = &line6midi->midibuf_out; - unsigned long flags; - unsigned char chunk[LINE6_FALLBACK_MAXPACKETSIZE]; - int req, done; - - spin_lock_irqsave(&line6->line6midi->midi_transmit_lock, flags); - - for (;;) { - req = min(line6_midibuf_bytes_free(mb), line6->max_packet_size); - done = snd_rawmidi_transmit_peek(substream, chunk, req); - - if (done == 0) - break; - - line6_midibuf_write(mb, chunk, done); - snd_rawmidi_transmit_ack(substream, done); - } - - for (;;) { - done = line6_midibuf_read(mb, chunk, - LINE6_FALLBACK_MAXPACKETSIZE); - - if (done == 0) - break; - - send_midi_async(line6, chunk, done); - } - - spin_unlock_irqrestore(&line6->line6midi->midi_transmit_lock, flags); -} - -/* - Notification of completion of MIDI transmission. -*/ -static void midi_sent(struct urb *urb) -{ - unsigned long flags; - int status; - int num; - struct usb_line6 *line6 = (struct usb_line6 *)urb->context; - - status = urb->status; - kfree(urb->transfer_buffer); - usb_free_urb(urb); - - if (status == -ESHUTDOWN) - return; - - spin_lock_irqsave(&line6->line6midi->send_urb_lock, flags); - num = --line6->line6midi->num_active_send_urbs; - - if (num == 0) { - line6_midi_transmit(line6->line6midi->substream_transmit); - num = line6->line6midi->num_active_send_urbs; - } - - if (num == 0) - wake_up(&line6->line6midi->send_wait); - - spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags); -} - -/* - Send an asynchronous MIDI message. - Assumes that line6->line6midi->send_urb_lock is held - (i.e., this function is serialized). -*/ -static int send_midi_async(struct usb_line6 *line6, unsigned char *data, - int length) -{ - struct urb *urb; - int retval; - unsigned char *transfer_buffer; - - urb = usb_alloc_urb(0, GFP_ATOMIC); - - if (urb == NULL) { - dev_err(line6->ifcdev, "Out of memory\n"); - return -ENOMEM; - } - - transfer_buffer = kmemdup(data, length, GFP_ATOMIC); - - if (transfer_buffer == NULL) { - usb_free_urb(urb); - dev_err(line6->ifcdev, "Out of memory\n"); - return -ENOMEM; - } - - usb_fill_int_urb(urb, line6->usbdev, - usb_sndbulkpipe(line6->usbdev, - line6->ep_control_write), - transfer_buffer, length, midi_sent, line6, - line6->interval); - urb->actual_length = 0; - retval = usb_submit_urb(urb, GFP_ATOMIC); - - if (retval < 0) { - dev_err(line6->ifcdev, "usb_submit_urb failed\n"); - usb_free_urb(urb); - return retval; - } - - ++line6->line6midi->num_active_send_urbs; - return 0; -} - -static int line6_midi_output_open(struct snd_rawmidi_substream *substream) -{ - return 0; -} - -static int line6_midi_output_close(struct snd_rawmidi_substream *substream) -{ - return 0; -} - -static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream, - int up) -{ - unsigned long flags; - struct usb_line6 *line6 = - line6_rawmidi_substream_midi(substream)->line6; - - line6->line6midi->substream_transmit = substream; - spin_lock_irqsave(&line6->line6midi->send_urb_lock, flags); - - if (line6->line6midi->num_active_send_urbs == 0) - line6_midi_transmit(substream); - - spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags); -} - -static void line6_midi_output_drain(struct snd_rawmidi_substream *substream) -{ - struct usb_line6 *line6 = - line6_rawmidi_substream_midi(substream)->line6; - struct snd_line6_midi *midi = line6->line6midi; - - wait_event_interruptible(midi->send_wait, - midi->num_active_send_urbs == 0); -} - -static int line6_midi_input_open(struct snd_rawmidi_substream *substream) -{ - return 0; -} - -static int line6_midi_input_close(struct snd_rawmidi_substream *substream) -{ - return 0; -} - -static void line6_midi_input_trigger(struct snd_rawmidi_substream *substream, - int up) -{ - struct usb_line6 *line6 = - line6_rawmidi_substream_midi(substream)->line6; - - if (up) - line6->line6midi->substream_receive = substream; - else - line6->line6midi->substream_receive = NULL; -} - -static struct snd_rawmidi_ops line6_midi_output_ops = { - .open = line6_midi_output_open, - .close = line6_midi_output_close, - .trigger = line6_midi_output_trigger, - .drain = line6_midi_output_drain, -}; - -static struct snd_rawmidi_ops line6_midi_input_ops = { - .open = line6_midi_input_open, - .close = line6_midi_input_close, - .trigger = line6_midi_input_trigger, -}; - -/* - Cleanup the Line6 MIDI device. -*/ -static void line6_cleanup_midi(struct snd_rawmidi *rmidi) -{ -} - -/* Create a MIDI device */ -static int snd_line6_new_midi(struct snd_line6_midi *line6midi) -{ - struct snd_rawmidi *rmidi; - int err; - - err = snd_rawmidi_new(line6midi->line6->card, "Line6 MIDI", 0, 1, 1, - &rmidi); - if (err < 0) - return err; - - rmidi->private_data = line6midi; - rmidi->private_free = line6_cleanup_midi; - strcpy(rmidi->id, line6midi->line6->properties->id); - strcpy(rmidi->name, line6midi->line6->properties->name); - - rmidi->info_flags = - SNDRV_RAWMIDI_INFO_OUTPUT | - SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX; - - snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, - &line6_midi_output_ops); - snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, - &line6_midi_input_ops); - return 0; -} - -/* MIDI device destructor */ -static int snd_line6_midi_free(struct snd_device *device) -{ - struct snd_line6_midi *line6midi = device->device_data; - - line6_midibuf_destroy(&line6midi->midibuf_in); - line6_midibuf_destroy(&line6midi->midibuf_out); - return 0; -} - -/* - Initialize the Line6 MIDI subsystem. -*/ -int line6_init_midi(struct usb_line6 *line6) -{ - static struct snd_device_ops midi_ops = { - .dev_free = snd_line6_midi_free, - }; - - int err; - struct snd_line6_midi *line6midi; - - if (!(line6->properties->capabilities & LINE6_BIT_CONTROL)) { - /* skip MIDI initialization and report success */ - return 0; - } - - line6midi = kzalloc(sizeof(struct snd_line6_midi), GFP_KERNEL); - - if (line6midi == NULL) - return -ENOMEM; - - err = line6_midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0); - if (err < 0) { - kfree(line6midi); - return err; - } - - err = line6_midibuf_init(&line6midi->midibuf_out, MIDI_BUFFER_SIZE, 1); - if (err < 0) { - kfree(line6midi->midibuf_in.buf); - kfree(line6midi); - return err; - } - - line6midi->line6 = line6; - line6->line6midi = line6midi; - - err = snd_device_new(line6->card, SNDRV_DEV_RAWMIDI, line6midi, - &midi_ops); - if (err < 0) - return err; - - err = snd_line6_new_midi(line6midi); - if (err < 0) - return err; - - init_waitqueue_head(&line6midi->send_wait); - spin_lock_init(&line6midi->send_urb_lock); - spin_lock_init(&line6midi->midi_transmit_lock); - return 0; -} diff --git a/drivers/staging/line6/midi.h b/drivers/staging/line6/midi.h deleted file mode 100644 index 78f903fb4d41..000000000000 --- a/drivers/staging/line6/midi.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * 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. - * - */ - -#ifndef MIDI_H -#define MIDI_H - -#include <sound/rawmidi.h> - -#include "midibuf.h" - -#define MIDI_BUFFER_SIZE 1024 - -struct snd_line6_midi { - /** - Pointer back to the Line6 driver data structure. - */ - struct usb_line6 *line6; - - /** - MIDI substream for receiving (or NULL if not active). - */ - struct snd_rawmidi_substream *substream_receive; - - /** - MIDI substream for transmitting (or NULL if not active). - */ - struct snd_rawmidi_substream *substream_transmit; - - /** - Number of currently active MIDI send URBs. - */ - int num_active_send_urbs; - - /** - Spin lock to protect updates of send_urb. - */ - spinlock_t send_urb_lock; - - /** - Spin lock to protect MIDI buffer handling. - */ - spinlock_t midi_transmit_lock; - - /** - Wait queue for MIDI transmission. - */ - wait_queue_head_t send_wait; - - /** - Buffer for incoming MIDI stream. - */ - struct midi_buffer midibuf_in; - - /** - Buffer for outgoing MIDI stream. - */ - struct midi_buffer midibuf_out; -}; - -extern int line6_init_midi(struct usb_line6 *line6); -extern void line6_midi_receive(struct usb_line6 *line6, unsigned char *data, - int length); - -#endif diff --git a/drivers/staging/line6/midibuf.c b/drivers/staging/line6/midibuf.c deleted file mode 100644 index 1ff856989fd6..000000000000 --- a/drivers/staging/line6/midibuf.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * 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. - * - */ - -#include <linux/slab.h> - -#include "midibuf.h" - -static int midibuf_message_length(unsigned char code) -{ - int message_length; - - if (code < 0x80) - message_length = -1; - else if (code < 0xf0) { - static const int length[] = { 3, 3, 3, 3, 2, 2, 3 }; - - message_length = length[(code >> 4) - 8]; - } else { - /* - Note that according to the MIDI specification 0xf2 is - the "Song Position Pointer", but this is used by Line6 - to send sysex messages to the host. - */ - static const int length[] = { -1, 2, -1, 2, -1, -1, 1, 1, 1, 1, - 1, 1, 1, -1, 1, 1 - }; - message_length = length[code & 0x0f]; - } - - return message_length; -} - -static int midibuf_is_empty(struct midi_buffer *this) -{ - return (this->pos_read == this->pos_write) && !this->full; -} - -static int midibuf_is_full(struct midi_buffer *this) -{ - return this->full; -} - -void line6_midibuf_reset(struct midi_buffer *this) -{ - this->pos_read = this->pos_write = this->full = 0; - this->command_prev = -1; -} - -int line6_midibuf_init(struct midi_buffer *this, int size, int split) -{ - this->buf = kmalloc(size, GFP_KERNEL); - - if (this->buf == NULL) - return -ENOMEM; - - this->size = size; - this->split = split; - line6_midibuf_reset(this); - return 0; -} - -void line6_midibuf_status(struct midi_buffer *this) -{ - pr_debug("midibuf size=%d split=%d pos_read=%d pos_write=%d full=%d command_prev=%02x\n", - this->size, this->split, this->pos_read, this->pos_write, - this->full, this->command_prev); -} - -int line6_midibuf_bytes_free(struct midi_buffer *this) -{ - return - midibuf_is_full(this) ? - 0 : - (this->pos_read - this->pos_write + this->size - 1) % this->size + - 1; -} - -int line6_midibuf_bytes_used(struct midi_buffer *this) -{ - return - midibuf_is_empty(this) ? - 0 : - (this->pos_write - this->pos_read + this->size - 1) % this->size + - 1; -} - -int line6_midibuf_write(struct midi_buffer *this, unsigned char *data, - int length) -{ - int bytes_free; - int length1, length2; - int skip_active_sense = 0; - - if (midibuf_is_full(this) || (length <= 0)) - return 0; - - /* skip trailing active sense */ - if (data[length - 1] == 0xfe) { - --length; - skip_active_sense = 1; - } - - bytes_free = line6_midibuf_bytes_free(this); - - if (length > bytes_free) - length = bytes_free; - - if (length > 0) { - length1 = this->size - this->pos_write; - - if (length < length1) { - /* no buffer wraparound */ - memcpy(this->buf + this->pos_write, data, length); - this->pos_write += length; - } else { - /* buffer wraparound */ - length2 = length - length1; - memcpy(this->buf + this->pos_write, data, length1); - memcpy(this->buf, data + length1, length2); - this->pos_write = length2; - } - - if (this->pos_write == this->pos_read) - this->full = 1; - } - - return length + skip_active_sense; -} - -int line6_midibuf_read(struct midi_buffer *this, unsigned char *data, - int length) -{ - int bytes_used; - int length1, length2; - int command; - int midi_length; - int repeat = 0; - int i; - - /* we need to be able to store at least a 3 byte MIDI message */ - if (length < 3) - return -EINVAL; - - if (midibuf_is_empty(this)) - return 0; - - bytes_used = line6_midibuf_bytes_used(this); - - if (length > bytes_used) - length = bytes_used; - - length1 = this->size - this->pos_read; - - /* check MIDI command length */ - command = this->buf[this->pos_read]; - - if (command & 0x80) { - midi_length = midibuf_message_length(command); - this->command_prev = command; - } else { - if (this->command_prev > 0) { - int midi_length_prev = - midibuf_message_length(this->command_prev); - - if (midi_length_prev > 0) { - midi_length = midi_length_prev - 1; - repeat = 1; - } else - midi_length = -1; - } else - midi_length = -1; - } - - if (midi_length < 0) { - /* search for end of message */ - if (length < length1) { - /* no buffer wraparound */ - for (i = 1; i < length; ++i) - if (this->buf[this->pos_read + i] & 0x80) - break; - - midi_length = i; - } else { - /* buffer wraparound */ - length2 = length - length1; - - for (i = 1; i < length1; ++i) - if (this->buf[this->pos_read + i] & 0x80) - break; - - if (i < length1) - midi_length = i; - else { - for (i = 0; i < length2; ++i) - if (this->buf[i] & 0x80) - break; - - midi_length = length1 + i; - } - } - - if (midi_length == length) - midi_length = -1; /* end of message not found */ - } - - if (midi_length < 0) { - if (!this->split) - return 0; /* command is not yet complete */ - } else { - if (length < midi_length) - return 0; /* command is not yet complete */ - - length = midi_length; - } - - if (length < length1) { - /* no buffer wraparound */ - memcpy(data + repeat, this->buf + this->pos_read, length); - this->pos_read += length; - } else { - /* buffer wraparound */ - length2 = length - length1; - memcpy(data + repeat, this->buf + this->pos_read, length1); - memcpy(data + repeat + length1, this->buf, length2); - this->pos_read = length2; - } - - if (repeat) - data[0] = this->command_prev; - - this->full = 0; - return length + repeat; -} - -int line6_midibuf_ignore(struct midi_buffer *this, int length) -{ - int bytes_used = line6_midibuf_bytes_used(this); - - if (length > bytes_used) - length = bytes_used; - - this->pos_read = (this->pos_read + length) % this->size; - this->full = 0; - return length; -} - -int line6_midibuf_skip_message(struct midi_buffer *this, unsigned short mask) -{ - int cmd = this->command_prev; - - if ((cmd >= 0x80) && (cmd < 0xf0)) - if ((mask & (1 << (cmd & 0x0f))) == 0) - return 1; - - return 0; -} - -void line6_midibuf_destroy(struct midi_buffer *this) -{ - kfree(this->buf); - this->buf = NULL; -} diff --git a/drivers/staging/line6/midibuf.h b/drivers/staging/line6/midibuf.h deleted file mode 100644 index 707482b940e4..000000000000 --- a/drivers/staging/line6/midibuf.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * 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. - * - */ - -#ifndef MIDIBUF_H -#define MIDIBUF_H - -struct midi_buffer { - unsigned char *buf; - int size; - int split; - int pos_read, pos_write; - int full; - int command_prev; -}; - -extern int line6_midibuf_bytes_used(struct midi_buffer *mb); -extern int line6_midibuf_bytes_free(struct midi_buffer *mb); -extern void line6_midibuf_destroy(struct midi_buffer *mb); -extern int line6_midibuf_ignore(struct midi_buffer *mb, int length); -extern int line6_midibuf_init(struct midi_buffer *mb, int size, int split); -extern int line6_midibuf_read(struct midi_buffer *mb, unsigned char *data, - int length); -extern void line6_midibuf_reset(struct midi_buffer *mb); -extern int line6_midibuf_skip_message(struct midi_buffer *mb, - unsigned short mask); -extern void line6_midibuf_status(struct midi_buffer *mb); -extern int line6_midibuf_write(struct midi_buffer *mb, unsigned char *data, - int length); - -#endif diff --git a/drivers/staging/line6/pcm.c b/drivers/staging/line6/pcm.c deleted file mode 100644 index a3136b189ee5..000000000000 --- a/drivers/staging/line6/pcm.c +++ /dev/null @@ -1,576 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * 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. - * - */ - -#include <linux/slab.h> -#include <sound/core.h> -#include <sound/control.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> - -#include "audio.h" -#include "capture.h" -#include "driver.h" -#include "playback.h" -#include "pod.h" - -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - -static struct snd_line6_pcm *dev2pcm(struct device *dev) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6 *line6 = usb_get_intfdata(interface); - struct snd_line6_pcm *line6pcm = line6->line6pcm; - return line6pcm; -} - -/* - "read" request on "impulse_volume" special file. -*/ -static ssize_t impulse_volume_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_volume); -} - -/* - "write" request on "impulse_volume" special file. -*/ -static ssize_t impulse_volume_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct snd_line6_pcm *line6pcm = dev2pcm(dev); - int value; - int ret; - - ret = kstrtoint(buf, 10, &value); - if (ret < 0) - return ret; - - line6pcm->impulse_volume = value; - - if (value > 0) - line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_IMPULSE); - else - line6_pcm_release(line6pcm, LINE6_BITS_PCM_IMPULSE); - - return count; -} -static DEVICE_ATTR_RW(impulse_volume); - -/* - "read" request on "impulse_period" special file. -*/ -static ssize_t impulse_period_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sprintf(buf, "%d\n", dev2pcm(dev)->impulse_period); -} - -/* - "write" request on "impulse_period" special file. -*/ -static ssize_t impulse_period_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - int value; - int ret; - - ret = kstrtoint(buf, 10, &value); - if (ret < 0) - return ret; - - dev2pcm(dev)->impulse_period = value; - return count; -} -static DEVICE_ATTR_RW(impulse_period); - -#endif - -static bool test_flags(unsigned long flags0, unsigned long flags1, - unsigned long mask) -{ - return ((flags0 & mask) == 0) && ((flags1 & mask) != 0); -} - -int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int channels) -{ - unsigned long flags_old, flags_new, flags_final; - int err; - - do { - flags_old = ACCESS_ONCE(line6pcm->flags); - flags_new = flags_old | channels; - } while (cmpxchg(&line6pcm->flags, flags_old, flags_new) != flags_old); - - flags_final = flags_old; - - line6pcm->prev_fbuf = NULL; - - if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_BUFFER)) { - /* Invoked multiple times in a row so allocate once only */ - if (!line6pcm->buffer_in) { - line6pcm->buffer_in = - kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * - line6pcm->max_packet_size, GFP_KERNEL); - if (!line6pcm->buffer_in) { - err = -ENOMEM; - goto pcm_acquire_error; - } - - flags_final |= channels & LINE6_BITS_CAPTURE_BUFFER; - } - } - - if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_STREAM)) { - /* - Waiting for completion of active URBs in the stop handler is - a bug, we therefore report an error if capturing is restarted - too soon. - */ - if (line6pcm->active_urb_in | line6pcm->unlink_urb_in) { - dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n"); - return -EBUSY; - } - - line6pcm->count_in = 0; - line6pcm->prev_fsize = 0; - err = line6_submit_audio_in_all_urbs(line6pcm); - - if (err < 0) - goto pcm_acquire_error; - - flags_final |= channels & LINE6_BITS_CAPTURE_STREAM; - } - - if (test_flags(flags_old, flags_new, LINE6_BITS_PLAYBACK_BUFFER)) { - /* Invoked multiple times in a row so allocate once only */ - if (!line6pcm->buffer_out) { - line6pcm->buffer_out = - kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * - line6pcm->max_packet_size, GFP_KERNEL); - if (!line6pcm->buffer_out) { - err = -ENOMEM; - goto pcm_acquire_error; - } - - flags_final |= channels & LINE6_BITS_PLAYBACK_BUFFER; - } - } - - if (test_flags(flags_old, flags_new, LINE6_BITS_PLAYBACK_STREAM)) { - /* - See comment above regarding PCM restart. - */ - if (line6pcm->active_urb_out | line6pcm->unlink_urb_out) { - dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n"); - return -EBUSY; - } - - line6pcm->count_out = 0; - err = line6_submit_audio_out_all_urbs(line6pcm); - - if (err < 0) - goto pcm_acquire_error; - - flags_final |= channels & LINE6_BITS_PLAYBACK_STREAM; - } - - return 0; - -pcm_acquire_error: - /* - If not all requested resources/streams could be obtained, release - those which were successfully obtained (if any). - */ - line6_pcm_release(line6pcm, flags_final & channels); - return err; -} - -int line6_pcm_release(struct snd_line6_pcm *line6pcm, int channels) -{ - unsigned long flags_old, flags_new; - - do { - flags_old = ACCESS_ONCE(line6pcm->flags); - flags_new = flags_old & ~channels; - } while (cmpxchg(&line6pcm->flags, flags_old, flags_new) != flags_old); - - if (test_flags(flags_new, flags_old, LINE6_BITS_CAPTURE_STREAM)) - line6_unlink_audio_in_urbs(line6pcm); - - if (test_flags(flags_new, flags_old, LINE6_BITS_CAPTURE_BUFFER)) { - line6_wait_clear_audio_in_urbs(line6pcm); - line6_free_capture_buffer(line6pcm); - } - - if (test_flags(flags_new, flags_old, LINE6_BITS_PLAYBACK_STREAM)) - line6_unlink_audio_out_urbs(line6pcm); - - if (test_flags(flags_new, flags_old, LINE6_BITS_PLAYBACK_BUFFER)) { - line6_wait_clear_audio_out_urbs(line6pcm); - line6_free_playback_buffer(line6pcm); - } - - return 0; -} - -/* trigger callback */ -int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - struct snd_pcm_substream *s; - int err; - unsigned long flags; - - spin_lock_irqsave(&line6pcm->lock_trigger, flags); - clear_bit(LINE6_INDEX_PREPARED, &line6pcm->flags); - - snd_pcm_group_for_each_entry(s, substream) { - switch (s->stream) { - case SNDRV_PCM_STREAM_PLAYBACK: - err = snd_line6_playback_trigger(line6pcm, cmd); - - if (err < 0) { - spin_unlock_irqrestore(&line6pcm->lock_trigger, - flags); - return err; - } - - break; - - case SNDRV_PCM_STREAM_CAPTURE: - err = snd_line6_capture_trigger(line6pcm, cmd); - - if (err < 0) { - spin_unlock_irqrestore(&line6pcm->lock_trigger, - flags); - return err; - } - - break; - - default: - dev_err(line6pcm->line6->ifcdev, - "Unknown stream direction %d\n", s->stream); - } - } - - spin_unlock_irqrestore(&line6pcm->lock_trigger, flags); - return 0; -} - -/* control info callback */ -static int snd_line6_control_playback_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 256; - return 0; -} - -/* control get callback */ -static int snd_line6_control_playback_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - int i; - struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - - for (i = 2; i--;) - ucontrol->value.integer.value[i] = line6pcm->volume_playback[i]; - - return 0; -} - -/* control put callback */ -static int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - int i, changed = 0; - struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - - for (i = 2; i--;) - if (line6pcm->volume_playback[i] != - ucontrol->value.integer.value[i]) { - line6pcm->volume_playback[i] = - ucontrol->value.integer.value[i]; - changed = 1; - } - - return changed; -} - -/* control definition */ -static struct snd_kcontrol_new line6_control_playback = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Playback Volume", - .index = 0, - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = snd_line6_control_playback_info, - .get = snd_line6_control_playback_get, - .put = snd_line6_control_playback_put -}; - -/* - Cleanup the PCM device. -*/ -static void line6_cleanup_pcm(struct snd_pcm *pcm) -{ - int i; - struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm); - -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - device_remove_file(line6pcm->line6->ifcdev, &dev_attr_impulse_volume); - device_remove_file(line6pcm->line6->ifcdev, &dev_attr_impulse_period); -#endif - - for (i = LINE6_ISO_BUFFERS; i--;) { - if (line6pcm->urb_audio_out[i]) { - usb_kill_urb(line6pcm->urb_audio_out[i]); - usb_free_urb(line6pcm->urb_audio_out[i]); - } - if (line6pcm->urb_audio_in[i]) { - usb_kill_urb(line6pcm->urb_audio_in[i]); - usb_free_urb(line6pcm->urb_audio_in[i]); - } - } -} - -/* create a PCM device */ -static int snd_line6_new_pcm(struct snd_line6_pcm *line6pcm) -{ - struct snd_pcm *pcm; - int err; - - err = snd_pcm_new(line6pcm->line6->card, - (char *)line6pcm->line6->properties->name, - 0, 1, 1, &pcm); - if (err < 0) - return err; - - pcm->private_data = line6pcm; - pcm->private_free = line6_cleanup_pcm; - line6pcm->pcm = pcm; - strcpy(pcm->name, line6pcm->line6->properties->name); - - /* set operators */ - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, - &snd_line6_playback_ops); - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_line6_capture_ops); - - /* pre-allocation of buffers */ - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data - (GFP_KERNEL), 64 * 1024, - 128 * 1024); - - return 0; -} - -/* PCM device destructor */ -static int snd_line6_pcm_free(struct snd_device *device) -{ - return 0; -} - -/* - Stop substream if still running. -*/ -static void pcm_disconnect_substream(struct snd_pcm_substream *substream) -{ - if (substream->runtime && snd_pcm_running(substream)) { - snd_pcm_stream_lock_irq(substream); - snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); - snd_pcm_stream_unlock_irq(substream); - } -} - -/* - Stop PCM stream. -*/ -void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm) -{ - pcm_disconnect_substream(get_substream - (line6pcm, SNDRV_PCM_STREAM_CAPTURE)); - pcm_disconnect_substream(get_substream - (line6pcm, SNDRV_PCM_STREAM_PLAYBACK)); - line6_unlink_wait_clear_audio_out_urbs(line6pcm); - line6_unlink_wait_clear_audio_in_urbs(line6pcm); -} - -/* - Create and register the PCM device and mixer entries. - Create URBs for playback and capture. -*/ -int line6_init_pcm(struct usb_line6 *line6, - struct line6_pcm_properties *properties) -{ - static struct snd_device_ops pcm_ops = { - .dev_free = snd_line6_pcm_free, - }; - - int err; - int ep_read = 0, ep_write = 0; - struct snd_line6_pcm *line6pcm; - - if (!(line6->properties->capabilities & LINE6_BIT_PCM)) - return 0; /* skip PCM initialization and report success */ - - /* initialize PCM subsystem based on product id: */ - switch (line6->product) { - case LINE6_DEVID_BASSPODXT: - case LINE6_DEVID_BASSPODXTLIVE: - case LINE6_DEVID_BASSPODXTPRO: - case LINE6_DEVID_PODXT: - case LINE6_DEVID_PODXTLIVE: - case LINE6_DEVID_PODXTPRO: - case LINE6_DEVID_PODHD300: - case LINE6_DEVID_PODHD400: - ep_read = 0x82; - ep_write = 0x01; - break; - - case LINE6_DEVID_PODHD500: - case LINE6_DEVID_PODX3: - case LINE6_DEVID_PODX3LIVE: - ep_read = 0x86; - ep_write = 0x02; - break; - - case LINE6_DEVID_POCKETPOD: - ep_read = 0x82; - ep_write = 0x02; - break; - - case LINE6_DEVID_GUITARPORT: - case LINE6_DEVID_PODSTUDIO_GX: - case LINE6_DEVID_PODSTUDIO_UX1: - case LINE6_DEVID_PODSTUDIO_UX2: - case LINE6_DEVID_TONEPORT_GX: - case LINE6_DEVID_TONEPORT_UX1: - case LINE6_DEVID_TONEPORT_UX2: - ep_read = 0x82; - ep_write = 0x01; - break; - - /* this is for interface_number == 1: - case LINE6_DEVID_TONEPORT_UX2: - case LINE6_DEVID_PODSTUDIO_UX2: - ep_read = 0x87; - ep_write = 0x00; - break; */ - - default: - MISSING_CASE; - } - - line6pcm = kzalloc(sizeof(*line6pcm), GFP_KERNEL); - - if (line6pcm == NULL) - return -ENOMEM; - - line6pcm->volume_playback[0] = line6pcm->volume_playback[1] = 255; - line6pcm->volume_monitor = 255; - line6pcm->line6 = line6; - line6pcm->ep_audio_read = ep_read; - line6pcm->ep_audio_write = ep_write; - - /* Read and write buffers are sized identically, so choose minimum */ - line6pcm->max_packet_size = min( - usb_maxpacket(line6->usbdev, - usb_rcvisocpipe(line6->usbdev, ep_read), 0), - usb_maxpacket(line6->usbdev, - usb_sndisocpipe(line6->usbdev, ep_write), 1)); - - line6pcm->properties = properties; - line6->line6pcm = line6pcm; - - /* PCM device: */ - err = snd_device_new(line6->card, SNDRV_DEV_PCM, line6, &pcm_ops); - if (err < 0) - return err; - - err = snd_line6_new_pcm(line6pcm); - if (err < 0) - return err; - - spin_lock_init(&line6pcm->lock_audio_out); - spin_lock_init(&line6pcm->lock_audio_in); - spin_lock_init(&line6pcm->lock_trigger); - - err = line6_create_audio_out_urbs(line6pcm); - if (err < 0) - return err; - - err = line6_create_audio_in_urbs(line6pcm); - if (err < 0) - return err; - - /* mixer: */ - err = - snd_ctl_add(line6->card, - snd_ctl_new1(&line6_control_playback, line6pcm)); - if (err < 0) - return err; - -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - /* impulse response test: */ - err = device_create_file(line6->ifcdev, &dev_attr_impulse_volume); - if (err < 0) - return err; - - err = device_create_file(line6->ifcdev, &dev_attr_impulse_period); - if (err < 0) - return err; - - line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD; -#endif - - return 0; -} - -/* prepare pcm callback */ -int snd_line6_prepare(struct snd_pcm_substream *substream) -{ - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - switch (substream->stream) { - case SNDRV_PCM_STREAM_PLAYBACK: - if ((line6pcm->flags & LINE6_BITS_PLAYBACK_STREAM) == 0) - line6_unlink_wait_clear_audio_out_urbs(line6pcm); - - break; - - case SNDRV_PCM_STREAM_CAPTURE: - if ((line6pcm->flags & LINE6_BITS_CAPTURE_STREAM) == 0) - line6_unlink_wait_clear_audio_in_urbs(line6pcm); - - break; - - default: - MISSING_CASE; - } - - if (!test_and_set_bit(LINE6_INDEX_PREPARED, &line6pcm->flags)) { - line6pcm->count_out = 0; - line6pcm->pos_out = 0; - line6pcm->pos_out_done = 0; - line6pcm->bytes_out = 0; - line6pcm->count_in = 0; - line6pcm->pos_in_done = 0; - line6pcm->bytes_in = 0; - } - - return 0; -} diff --git a/drivers/staging/line6/pcm.h b/drivers/staging/line6/pcm.h deleted file mode 100644 index 6aa0d46a2890..000000000000 --- a/drivers/staging/line6/pcm.h +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * 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. - * - */ - -/* - PCM interface to POD series devices. -*/ - -#ifndef PCM_H -#define PCM_H - -#include <sound/pcm.h> - -#include "driver.h" -#include "usbdefs.h" - -/* number of URBs */ -#define LINE6_ISO_BUFFERS 2 - -/* - number of USB frames per URB - The Line6 Windows driver always transmits two frames per packet, but - the Linux driver performs significantly better (i.e., lower latency) - with only one frame per packet. -*/ -#define LINE6_ISO_PACKETS 1 - -/* in a "full speed" device (such as the PODxt Pro) this means 1ms */ -#define LINE6_ISO_INTERVAL 1 - -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE -#define LINE6_IMPULSE_DEFAULT_PERIOD 100 -#endif - -/* - Get substream from Line6 PCM data structure -*/ -#define get_substream(line6pcm, stream) \ - (line6pcm->pcm->streams[stream].substream) - -/* - PCM mode bits. - - There are several features of the Line6 USB driver which require PCM - data to be exchanged with the device: - *) PCM playback and capture via ALSA - *) software monitoring (for devices without hardware monitoring) - *) optional impulse response measurement - However, from the device's point of view, there is just a single - capture and playback stream, which must be shared between these - subsystems. It is therefore necessary to maintain the state of the - subsystems with respect to PCM usage. We define several constants of - the form LINE6_BIT_PCM_<subsystem>_<direction>_<resource> with the - following meanings: - *) <subsystem> is one of - -) ALSA: PCM playback and capture via ALSA - -) MONITOR: software monitoring - -) IMPULSE: optional impulse response measurement - *) <direction> is one of - -) PLAYBACK: audio output (from host to device) - -) CAPTURE: audio input (from device to host) - *) <resource> is one of - -) BUFFER: buffer required by PCM data stream - -) STREAM: actual PCM data stream - - The subsystems call line6_pcm_acquire() to acquire the (shared) - resources needed for a particular operation (e.g., allocate the buffer - for ALSA playback or start the capture stream for software monitoring). - When a resource is no longer needed, it is released by calling - line6_pcm_release(). Buffer allocation and stream startup are handled - separately to allow the ALSA kernel driver to perform them at - appropriate places (since the callback which starts a PCM stream is not - allowed to sleep). -*/ -enum { - /* individual bit indices: */ - LINE6_INDEX_PCM_ALSA_PLAYBACK_BUFFER, - LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, - LINE6_INDEX_PCM_ALSA_CAPTURE_BUFFER, - LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, - LINE6_INDEX_PCM_MONITOR_PLAYBACK_BUFFER, - LINE6_INDEX_PCM_MONITOR_PLAYBACK_STREAM, - LINE6_INDEX_PCM_MONITOR_CAPTURE_BUFFER, - LINE6_INDEX_PCM_MONITOR_CAPTURE_STREAM, -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - LINE6_INDEX_PCM_IMPULSE_PLAYBACK_BUFFER, - LINE6_INDEX_PCM_IMPULSE_PLAYBACK_STREAM, - LINE6_INDEX_PCM_IMPULSE_CAPTURE_BUFFER, - LINE6_INDEX_PCM_IMPULSE_CAPTURE_STREAM, -#endif - LINE6_INDEX_PAUSE_PLAYBACK, - LINE6_INDEX_PREPARED, - - /* individual bit masks: */ - LINE6_BIT(PCM_ALSA_PLAYBACK_BUFFER), - LINE6_BIT(PCM_ALSA_PLAYBACK_STREAM), - LINE6_BIT(PCM_ALSA_CAPTURE_BUFFER), - LINE6_BIT(PCM_ALSA_CAPTURE_STREAM), - LINE6_BIT(PCM_MONITOR_PLAYBACK_BUFFER), - LINE6_BIT(PCM_MONITOR_PLAYBACK_STREAM), - LINE6_BIT(PCM_MONITOR_CAPTURE_BUFFER), - LINE6_BIT(PCM_MONITOR_CAPTURE_STREAM), -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - LINE6_BIT(PCM_IMPULSE_PLAYBACK_BUFFER), - LINE6_BIT(PCM_IMPULSE_PLAYBACK_STREAM), - LINE6_BIT(PCM_IMPULSE_CAPTURE_BUFFER), - LINE6_BIT(PCM_IMPULSE_CAPTURE_STREAM), -#endif - LINE6_BIT(PAUSE_PLAYBACK), - LINE6_BIT(PREPARED), - - /* combined bit masks (by operation): */ - LINE6_BITS_PCM_ALSA_BUFFER = - LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER | - LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER, - - LINE6_BITS_PCM_ALSA_STREAM = - LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM | - LINE6_BIT_PCM_ALSA_CAPTURE_STREAM, - - LINE6_BITS_PCM_MONITOR = - LINE6_BIT_PCM_MONITOR_PLAYBACK_BUFFER | - LINE6_BIT_PCM_MONITOR_PLAYBACK_STREAM | - LINE6_BIT_PCM_MONITOR_CAPTURE_BUFFER | - LINE6_BIT_PCM_MONITOR_CAPTURE_STREAM, - -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - LINE6_BITS_PCM_IMPULSE = - LINE6_BIT_PCM_IMPULSE_PLAYBACK_BUFFER | - LINE6_BIT_PCM_IMPULSE_PLAYBACK_STREAM | - LINE6_BIT_PCM_IMPULSE_CAPTURE_BUFFER | - LINE6_BIT_PCM_IMPULSE_CAPTURE_STREAM, -#endif - - /* combined bit masks (by direction): */ - LINE6_BITS_PLAYBACK_BUFFER = -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - LINE6_BIT_PCM_IMPULSE_PLAYBACK_BUFFER | -#endif - LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER | - LINE6_BIT_PCM_MONITOR_PLAYBACK_BUFFER , - - LINE6_BITS_PLAYBACK_STREAM = -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - LINE6_BIT_PCM_IMPULSE_PLAYBACK_STREAM | -#endif - LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM | - LINE6_BIT_PCM_MONITOR_PLAYBACK_STREAM , - - LINE6_BITS_CAPTURE_BUFFER = -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - LINE6_BIT_PCM_IMPULSE_CAPTURE_BUFFER | -#endif - LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER | - LINE6_BIT_PCM_MONITOR_CAPTURE_BUFFER , - - LINE6_BITS_CAPTURE_STREAM = -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - LINE6_BIT_PCM_IMPULSE_CAPTURE_STREAM | -#endif - LINE6_BIT_PCM_ALSA_CAPTURE_STREAM | - LINE6_BIT_PCM_MONITOR_CAPTURE_STREAM, - - LINE6_BITS_STREAM = - LINE6_BITS_PLAYBACK_STREAM | - LINE6_BITS_CAPTURE_STREAM -}; - -struct line6_pcm_properties { - struct snd_pcm_hardware snd_line6_playback_hw, snd_line6_capture_hw; - struct snd_pcm_hw_constraint_ratdens snd_line6_rates; - int bytes_per_frame; -}; - -struct snd_line6_pcm { - /** - Pointer back to the Line6 driver data structure. - */ - struct usb_line6 *line6; - - /** - Properties. - */ - struct line6_pcm_properties *properties; - - /** - ALSA pcm stream - */ - struct snd_pcm *pcm; - - /** - URBs for audio playback. - */ - struct urb *urb_audio_out[LINE6_ISO_BUFFERS]; - - /** - URBs for audio capture. - */ - struct urb *urb_audio_in[LINE6_ISO_BUFFERS]; - - /** - Temporary buffer for playback. - Since the packet size is not known in advance, this buffer is - large enough to store maximum size packets. - */ - unsigned char *buffer_out; - - /** - Temporary buffer for capture. - Since the packet size is not known in advance, this buffer is - large enough to store maximum size packets. - */ - unsigned char *buffer_in; - - /** - Previously captured frame (for software monitoring). - */ - unsigned char *prev_fbuf; - - /** - Size of previously captured frame (for software monitoring). - */ - int prev_fsize; - - /** - Free frame position in the playback buffer. - */ - snd_pcm_uframes_t pos_out; - - /** - Count processed bytes for playback. - This is modulo period size (to determine when a period is - finished). - */ - unsigned bytes_out; - - /** - Counter to create desired playback sample rate. - */ - unsigned count_out; - - /** - Playback period size in bytes - */ - unsigned period_out; - - /** - Processed frame position in the playback buffer. - The contents of the output ring buffer have been consumed by - the USB subsystem (i.e., sent to the USB device) up to this - position. - */ - snd_pcm_uframes_t pos_out_done; - - /** - Count processed bytes for capture. - This is modulo period size (to determine when a period is - finished). - */ - unsigned bytes_in; - - /** - Counter to create desired capture sample rate. - */ - unsigned count_in; - - /** - Capture period size in bytes - */ - unsigned period_in; - - /** - Processed frame position in the capture buffer. - The contents of the output ring buffer have been consumed by - the USB subsystem (i.e., sent to the USB device) up to this - position. - */ - snd_pcm_uframes_t pos_in_done; - - /** - Bit mask of active playback URBs. - */ - unsigned long active_urb_out; - - /** - Maximum size of USB packet. - */ - int max_packet_size; - - /** - USB endpoint for listening to audio data. - */ - int ep_audio_read; - - /** - USB endpoint for writing audio data. - */ - int ep_audio_write; - - /** - Bit mask of active capture URBs. - */ - unsigned long active_urb_in; - - /** - Bit mask of playback URBs currently being unlinked. - */ - unsigned long unlink_urb_out; - - /** - Bit mask of capture URBs currently being unlinked. - */ - unsigned long unlink_urb_in; - - /** - Spin lock to protect updates of the playback buffer positions (not - contents!) - */ - spinlock_t lock_audio_out; - - /** - Spin lock to protect updates of the capture buffer positions (not - contents!) - */ - spinlock_t lock_audio_in; - - /** - Spin lock to protect trigger. - */ - spinlock_t lock_trigger; - - /** - PCM playback volume (left and right). - */ - int volume_playback[2]; - - /** - PCM monitor volume. - */ - int volume_monitor; - -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - /** - Volume of impulse response test signal (if zero, test is disabled). - */ - int impulse_volume; - - /** - Period of impulse response test signal. - */ - int impulse_period; - - /** - Counter for impulse response test signal. - */ - int impulse_count; -#endif - - /** - Several status bits (see LINE6_BIT_*). - */ - unsigned long flags; - - int last_frame_in, last_frame_out; -}; - -extern int line6_init_pcm(struct usb_line6 *line6, - struct line6_pcm_properties *properties); -extern int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd); -extern int snd_line6_prepare(struct snd_pcm_substream *substream); -extern void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm); -extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int channels); -extern int line6_pcm_release(struct snd_line6_pcm *line6pcm, int channels); - -#endif diff --git a/drivers/staging/line6/playback.c b/drivers/staging/line6/playback.c deleted file mode 100644 index 2ca8900e68c3..000000000000 --- a/drivers/staging/line6/playback.c +++ /dev/null @@ -1,592 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * 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. - * - */ - -#include <linux/slab.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> - -#include "audio.h" -#include "capture.h" -#include "driver.h" -#include "pcm.h" -#include "pod.h" -#include "playback.h" - -/* - Software stereo volume control. -*/ -static void change_volume(struct urb *urb_out, int volume[], - int bytes_per_frame) -{ - int chn = 0; - - if (volume[0] == 256 && volume[1] == 256) - return; /* maximum volume - no change */ - - if (bytes_per_frame == 4) { - short *p, *buf_end; - - p = (short *)urb_out->transfer_buffer; - buf_end = p + urb_out->transfer_buffer_length / sizeof(*p); - - for (; p < buf_end; ++p) { - *p = (*p * volume[chn & 1]) >> 8; - ++chn; - } - } else if (bytes_per_frame == 6) { - unsigned char *p, *buf_end; - - p = (unsigned char *)urb_out->transfer_buffer; - buf_end = p + urb_out->transfer_buffer_length; - - for (; p < buf_end; p += 3) { - int val; - - val = p[0] + (p[1] << 8) + ((signed char)p[2] << 16); - val = (val * volume[chn & 1]) >> 8; - p[0] = val; - p[1] = val >> 8; - p[2] = val >> 16; - ++chn; - } - } -} - -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - -/* - Create signal for impulse response test. -*/ -static void create_impulse_test_signal(struct snd_line6_pcm *line6pcm, - struct urb *urb_out, int bytes_per_frame) -{ - int frames = urb_out->transfer_buffer_length / bytes_per_frame; - - if (bytes_per_frame == 4) { - int i; - short *pi = (short *)line6pcm->prev_fbuf; - short *po = (short *)urb_out->transfer_buffer; - - for (i = 0; i < frames; ++i) { - po[0] = pi[0]; - po[1] = 0; - pi += 2; - po += 2; - } - } else if (bytes_per_frame == 6) { - int i, j; - unsigned char *pi = line6pcm->prev_fbuf; - unsigned char *po = urb_out->transfer_buffer; - - for (i = 0; i < frames; ++i) { - for (j = 0; j < bytes_per_frame / 2; ++j) - po[j] = pi[j]; - - for (; j < bytes_per_frame; ++j) - po[j] = 0; - - pi += bytes_per_frame; - po += bytes_per_frame; - } - } - if (--line6pcm->impulse_count <= 0) { - ((unsigned char *)(urb_out->transfer_buffer))[bytes_per_frame - - 1] = - line6pcm->impulse_volume; - line6pcm->impulse_count = line6pcm->impulse_period; - } -} - -#endif - -/* - Add signal to buffer for software monitoring. -*/ -static void add_monitor_signal(struct urb *urb_out, unsigned char *signal, - int volume, int bytes_per_frame) -{ - if (volume == 0) - return; /* zero volume - no change */ - - if (bytes_per_frame == 4) { - short *pi, *po, *buf_end; - - pi = (short *)signal; - po = (short *)urb_out->transfer_buffer; - buf_end = po + urb_out->transfer_buffer_length / sizeof(*po); - - for (; po < buf_end; ++pi, ++po) - *po += (*pi * volume) >> 8; - } - - /* - We don't need to handle devices with 6 bytes per frame here - since they all support hardware monitoring. - */ -} - -/* - Find a free URB, prepare audio data, and submit URB. -*/ -static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) -{ - int index; - unsigned long flags; - int i, urb_size, urb_frames; - int ret; - const int bytes_per_frame = line6pcm->properties->bytes_per_frame; - const int frame_increment = - line6pcm->properties->snd_line6_rates.rats[0].num_min; - const int frame_factor = - line6pcm->properties->snd_line6_rates.rats[0].den * - (USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL); - struct urb *urb_out; - - spin_lock_irqsave(&line6pcm->lock_audio_out, flags); - index = - find_first_zero_bit(&line6pcm->active_urb_out, LINE6_ISO_BUFFERS); - - if (index < 0 || index >= LINE6_ISO_BUFFERS) { - spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); - dev_err(line6pcm->line6->ifcdev, "no free URB found\n"); - return -EINVAL; - } - - urb_out = line6pcm->urb_audio_out[index]; - urb_size = 0; - - for (i = 0; i < LINE6_ISO_PACKETS; ++i) { - /* compute frame size for given sampling rate */ - int fsize = 0; - struct usb_iso_packet_descriptor *fout = - &urb_out->iso_frame_desc[i]; - - if (line6pcm->flags & LINE6_BITS_CAPTURE_STREAM) - fsize = line6pcm->prev_fsize; - - if (fsize == 0) { - int n; - - line6pcm->count_out += frame_increment; - n = line6pcm->count_out / frame_factor; - line6pcm->count_out -= n * frame_factor; - fsize = n * bytes_per_frame; - } - - fout->offset = urb_size; - fout->length = fsize; - urb_size += fsize; - } - - if (urb_size == 0) { - /* can't determine URB size */ - spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); - dev_err(line6pcm->line6->ifcdev, "driver bug: urb_size = 0\n"); - return -EINVAL; - } - - urb_frames = urb_size / bytes_per_frame; - urb_out->transfer_buffer = - line6pcm->buffer_out + - index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; - urb_out->transfer_buffer_length = urb_size; - urb_out->context = line6pcm; - - if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, &line6pcm->flags) && - !test_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags)) { - struct snd_pcm_runtime *runtime = - get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK)->runtime; - - if (line6pcm->pos_out + urb_frames > runtime->buffer_size) { - /* - The transferred area goes over buffer boundary, - copy the data to the temp buffer. - */ - int len; - - len = runtime->buffer_size - line6pcm->pos_out; - - if (len > 0) { - memcpy(urb_out->transfer_buffer, - runtime->dma_area + - line6pcm->pos_out * bytes_per_frame, - len * bytes_per_frame); - memcpy(urb_out->transfer_buffer + - len * bytes_per_frame, runtime->dma_area, - (urb_frames - len) * bytes_per_frame); - } else - dev_err(line6pcm->line6->ifcdev, "driver bug: len = %d\n", - len); - } else { - memcpy(urb_out->transfer_buffer, - runtime->dma_area + - line6pcm->pos_out * bytes_per_frame, - urb_out->transfer_buffer_length); - } - - line6pcm->pos_out += urb_frames; - if (line6pcm->pos_out >= runtime->buffer_size) - line6pcm->pos_out -= runtime->buffer_size; - } else { - memset(urb_out->transfer_buffer, 0, - urb_out->transfer_buffer_length); - } - - change_volume(urb_out, line6pcm->volume_playback, bytes_per_frame); - - if (line6pcm->prev_fbuf != NULL) { -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - if (line6pcm->flags & LINE6_BITS_PCM_IMPULSE) { - create_impulse_test_signal(line6pcm, urb_out, - bytes_per_frame); - if (line6pcm->flags & - LINE6_BIT_PCM_ALSA_CAPTURE_STREAM) { - line6_capture_copy(line6pcm, - urb_out->transfer_buffer, - urb_out-> - transfer_buffer_length); - line6_capture_check_period(line6pcm, - urb_out->transfer_buffer_length); - } - } else { -#endif - if (! - (line6pcm->line6-> - properties->capabilities & LINE6_BIT_HWMON) - && (line6pcm->flags & LINE6_BITS_PLAYBACK_STREAM) - && (line6pcm->flags & LINE6_BITS_CAPTURE_STREAM)) - add_monitor_signal(urb_out, line6pcm->prev_fbuf, - line6pcm->volume_monitor, - bytes_per_frame); -#ifdef CONFIG_LINE6_USB_IMPULSE_RESPONSE - } -#endif - } - - ret = usb_submit_urb(urb_out, GFP_ATOMIC); - - if (ret == 0) - set_bit(index, &line6pcm->active_urb_out); - else - dev_err(line6pcm->line6->ifcdev, - "URB out #%d submission failed (%d)\n", index, ret); - - spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); - return 0; -} - -/* - Submit all currently available playback URBs. -*/ -int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm) -{ - int ret, i; - - for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { - ret = submit_audio_out_urb(line6pcm); - if (ret < 0) - return ret; - } - - return 0; -} - -/* - Unlink all currently active playback URBs. -*/ -void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm) -{ - unsigned int i; - - for (i = LINE6_ISO_BUFFERS; i--;) { - if (test_bit(i, &line6pcm->active_urb_out)) { - if (!test_and_set_bit(i, &line6pcm->unlink_urb_out)) { - struct urb *u = line6pcm->urb_audio_out[i]; - - usb_unlink_urb(u); - } - } - } -} - -/* - Wait until unlinking of all currently active playback URBs has been - finished. -*/ -void line6_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) -{ - int timeout = HZ; - unsigned int i; - int alive; - - do { - alive = 0; - for (i = LINE6_ISO_BUFFERS; i--;) { - if (test_bit(i, &line6pcm->active_urb_out)) - alive++; - } - if (!alive) - break; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } while (--timeout > 0); - if (alive) - snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); -} - -/* - Unlink all currently active playback URBs, and wait for finishing. -*/ -void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) -{ - line6_unlink_audio_out_urbs(line6pcm); - line6_wait_clear_audio_out_urbs(line6pcm); -} - -void line6_free_playback_buffer(struct snd_line6_pcm *line6pcm) -{ - kfree(line6pcm->buffer_out); - line6pcm->buffer_out = NULL; -} - -/* - Callback for completed playback URB. -*/ -static void audio_out_callback(struct urb *urb) -{ - int i, index, length = 0, shutdown = 0; - unsigned long flags; - struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context; - struct snd_pcm_substream *substream = - get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK); - -#if USE_CLEAR_BUFFER_WORKAROUND - memset(urb->transfer_buffer, 0, urb->transfer_buffer_length); -#endif - - line6pcm->last_frame_out = urb->start_frame; - - /* find index of URB */ - for (index = LINE6_ISO_BUFFERS; index--;) - if (urb == line6pcm->urb_audio_out[index]) - break; - - if (index < 0) - return; /* URB has been unlinked asynchronously */ - - for (i = LINE6_ISO_PACKETS; i--;) - length += urb->iso_frame_desc[i].length; - - spin_lock_irqsave(&line6pcm->lock_audio_out, flags); - - if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, &line6pcm->flags)) { - struct snd_pcm_runtime *runtime = substream->runtime; - - line6pcm->pos_out_done += - length / line6pcm->properties->bytes_per_frame; - - if (line6pcm->pos_out_done >= runtime->buffer_size) - line6pcm->pos_out_done -= runtime->buffer_size; - } - - clear_bit(index, &line6pcm->active_urb_out); - - for (i = LINE6_ISO_PACKETS; i--;) - if (urb->iso_frame_desc[i].status == -EXDEV) { - shutdown = 1; - break; - } - - if (test_and_clear_bit(index, &line6pcm->unlink_urb_out)) - shutdown = 1; - - spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); - - if (!shutdown) { - submit_audio_out_urb(line6pcm); - - if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, - &line6pcm->flags)) { - line6pcm->bytes_out += length; - if (line6pcm->bytes_out >= line6pcm->period_out) { - line6pcm->bytes_out %= line6pcm->period_out; - snd_pcm_period_elapsed(substream); - } - } - } -} - -/* open playback callback */ -static int snd_line6_playback_open(struct snd_pcm_substream *substream) -{ - int err; - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - err = snd_pcm_hw_constraint_ratdens(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - (&line6pcm-> - properties->snd_line6_rates)); - if (err < 0) - return err; - - runtime->hw = line6pcm->properties->snd_line6_playback_hw; - return 0; -} - -/* close playback callback */ -static int snd_line6_playback_close(struct snd_pcm_substream *substream) -{ - return 0; -} - -/* hw_params playback callback */ -static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - int ret; - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - /* -- Florian Demski [FD] */ - /* don't ask me why, but this fixes the bug on my machine */ - if (line6pcm == NULL) { - if (substream->pcm == NULL) - return -ENOMEM; - if (substream->pcm->private_data == NULL) - return -ENOMEM; - substream->private_data = substream->pcm->private_data; - line6pcm = snd_pcm_substream_chip(substream); - } - /* -- [FD] end */ - - ret = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER); - - if (ret < 0) - return ret; - - ret = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - if (ret < 0) { - line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER); - return ret; - } - - line6pcm->period_out = params_period_bytes(hw_params); - return 0; -} - -/* hw_free playback callback */ -static int snd_line6_playback_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER); - return snd_pcm_lib_free_pages(substream); -} - -/* trigger playback callback */ -int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd) -{ - int err; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: -#ifdef CONFIG_PM - case SNDRV_PCM_TRIGGER_RESUME: -#endif - err = line6_pcm_acquire(line6pcm, - LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM); - - if (err < 0) - return err; - - break; - - case SNDRV_PCM_TRIGGER_STOP: -#ifdef CONFIG_PM - case SNDRV_PCM_TRIGGER_SUSPEND: -#endif - err = line6_pcm_release(line6pcm, - LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM); - - if (err < 0) - return err; - - break; - - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - set_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags); - break; - - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - clear_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags); - break; - - default: - return -EINVAL; - } - - return 0; -} - -/* playback pointer callback */ -static snd_pcm_uframes_t -snd_line6_playback_pointer(struct snd_pcm_substream *substream) -{ - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - return line6pcm->pos_out_done; -} - -/* playback operators */ -struct snd_pcm_ops snd_line6_playback_ops = { - .open = snd_line6_playback_open, - .close = snd_line6_playback_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_line6_playback_hw_params, - .hw_free = snd_line6_playback_hw_free, - .prepare = snd_line6_prepare, - .trigger = snd_line6_trigger, - .pointer = snd_line6_playback_pointer, -}; - -int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm) -{ - int i; - - /* create audio URBs and fill in constant values: */ - for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { - struct urb *urb; - - /* URB for audio out: */ - urb = line6pcm->urb_audio_out[i] = - usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL); - - if (urb == NULL) { - dev_err(line6pcm->line6->ifcdev, "Out of memory\n"); - return -ENOMEM; - } - - urb->dev = line6pcm->line6->usbdev; - urb->pipe = - usb_sndisocpipe(line6pcm->line6->usbdev, - line6pcm->ep_audio_write & - USB_ENDPOINT_NUMBER_MASK); - urb->transfer_flags = URB_ISO_ASAP; - urb->start_frame = -1; - urb->number_of_packets = LINE6_ISO_PACKETS; - urb->interval = LINE6_ISO_INTERVAL; - urb->error_count = 0; - urb->complete = audio_out_callback; - } - - return 0; -} diff --git a/drivers/staging/line6/playback.h b/drivers/staging/line6/playback.h deleted file mode 100644 index 743bd6f74c57..000000000000 --- a/drivers/staging/line6/playback.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * 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. - * - */ - -#ifndef PLAYBACK_H -#define PLAYBACK_H - -#include <sound/pcm.h> - -#include "driver.h" - -/* - * When the TonePort is used with jack in full duplex mode and the outputs are - * not connected, the software monitor produces an ugly noise since everything - * written to the output buffer (i.e., the input signal) will be repeated in - * the next period (sounds like a delay effect). As a workaround, the output - * buffer is cleared after the data have been read, but there must be a better - * solution. Until one is found, this workaround can be used to fix the - * problem. - */ -#define USE_CLEAR_BUFFER_WORKAROUND 1 - -extern struct snd_pcm_ops snd_line6_playback_ops; - -extern int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm); -extern void line6_free_playback_buffer(struct snd_line6_pcm *line6pcm); -extern int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm); -extern void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm); -extern void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm - *line6pcm); -extern void line6_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm); -extern int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd); - -#endif diff --git a/drivers/staging/line6/pod.c b/drivers/staging/line6/pod.c deleted file mode 100644 index 44f4b2f98570..000000000000 --- a/drivers/staging/line6/pod.c +++ /dev/null @@ -1,458 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * 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. - * - */ - -#include <linux/slab.h> -#include <linux/wait.h> -#include <sound/control.h> - -#include "audio.h" -#include "capture.h" -#include "driver.h" -#include "playback.h" -#include "pod.h" - -#define POD_SYSEX_CODE 3 -#define POD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */ - -/* *INDENT-OFF* */ - -enum { - POD_SYSEX_SAVE = 0x24, - POD_SYSEX_SYSTEM = 0x56, - POD_SYSEX_SYSTEMREQ = 0x57, - /* POD_SYSEX_UPDATE = 0x6c, */ /* software update! */ - POD_SYSEX_STORE = 0x71, - POD_SYSEX_FINISH = 0x72, - POD_SYSEX_DUMPMEM = 0x73, - POD_SYSEX_DUMP = 0x74, - POD_SYSEX_DUMPREQ = 0x75 - - /* dumps entire internal memory of PODxt Pro */ - /* POD_SYSEX_DUMPMEM2 = 0x76 */ -}; - -enum { - POD_MONITOR_LEVEL = 0x04, - POD_SYSTEM_INVALID = 0x10000 -}; - -/* *INDENT-ON* */ - -enum { - POD_DUMP_MEMORY = 2 -}; - -enum { - POD_BUSY_READ, - POD_BUSY_WRITE, - POD_CHANNEL_DIRTY, - POD_SAVE_PRESSED, - POD_BUSY_MIDISEND -}; - -static struct snd_ratden pod_ratden = { - .num_min = 78125, - .num_max = 78125, - .num_step = 1, - .den = 2 -}; - -static struct line6_pcm_properties pod_pcm_properties = { - .snd_line6_playback_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | -#ifdef CONFIG_PM - SNDRV_PCM_INFO_RESUME | -#endif - SNDRV_PCM_INFO_SYNC_START), - .formats = SNDRV_PCM_FMTBIT_S24_3LE, - .rates = SNDRV_PCM_RATE_KNOT, - .rate_min = 39062, - .rate_max = 39063, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 60000, - .period_bytes_min = 64, - .period_bytes_max = 8192, - .periods_min = 1, - .periods_max = 1024}, - .snd_line6_capture_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | -#ifdef CONFIG_PM - SNDRV_PCM_INFO_RESUME | -#endif - SNDRV_PCM_INFO_SYNC_START), - .formats = SNDRV_PCM_FMTBIT_S24_3LE, - .rates = SNDRV_PCM_RATE_KNOT, - .rate_min = 39062, - .rate_max = 39063, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 60000, - .period_bytes_min = 64, - .period_bytes_max = 8192, - .periods_min = 1, - .periods_max = 1024}, - .snd_line6_rates = { - .nrats = 1, - .rats = &pod_ratden}, - .bytes_per_frame = POD_BYTES_PER_FRAME -}; - -static const char pod_version_header[] = { - 0xf2, 0x7e, 0x7f, 0x06, 0x02 -}; - -/* forward declarations: */ -static void pod_startup2(unsigned long data); -static void pod_startup3(struct usb_line6_pod *pod); - -static char *pod_alloc_sysex_buffer(struct usb_line6_pod *pod, int code, - int size) -{ - return line6_alloc_sysex_buffer(&pod->line6, POD_SYSEX_CODE, code, - size); -} - -/* - Process a completely received message. -*/ -void line6_pod_process_message(struct usb_line6_pod *pod) -{ - const unsigned char *buf = pod->line6.buffer_message; - - if (memcmp(buf, pod_version_header, sizeof(pod_version_header)) == 0) { - pod->firmware_version = buf[13] * 100 + buf[14] * 10 + buf[15]; - pod->device_id = ((int)buf[8] << 16) | ((int)buf[9] << 8) | - (int) buf[10]; - pod_startup3(pod); - return; - } - - /* Only look for sysex messages from this device */ - if (buf[0] != (LINE6_SYSEX_BEGIN | LINE6_CHANNEL_DEVICE) && - buf[0] != (LINE6_SYSEX_BEGIN | LINE6_CHANNEL_UNKNOWN)) { - return; - } - if (memcmp(buf + 1, line6_midi_id, sizeof(line6_midi_id)) != 0) - return; - - if (buf[5] == POD_SYSEX_SYSTEM && buf[6] == POD_MONITOR_LEVEL) { - short value = ((int)buf[7] << 12) | ((int)buf[8] << 8) | - ((int)buf[9] << 4) | (int)buf[10]; - pod->monitor_level = value; - } -} - -/* - Transmit PODxt Pro control parameter. -*/ -void line6_pod_transmit_parameter(struct usb_line6_pod *pod, int param, - u8 value) -{ - line6_transmit_parameter(&pod->line6, param, value); -} - -/* - Send system parameter (from integer). -*/ -static int pod_set_system_param_int(struct usb_line6_pod *pod, int value, - int code) -{ - char *sysex; - static const int size = 5; - - sysex = pod_alloc_sysex_buffer(pod, POD_SYSEX_SYSTEM, size); - if (!sysex) - return -ENOMEM; - sysex[SYSEX_DATA_OFS] = code; - sysex[SYSEX_DATA_OFS + 1] = (value >> 12) & 0x0f; - sysex[SYSEX_DATA_OFS + 2] = (value >> 8) & 0x0f; - sysex[SYSEX_DATA_OFS + 3] = (value >> 4) & 0x0f; - sysex[SYSEX_DATA_OFS + 4] = (value) & 0x0f; - line6_send_sysex_message(&pod->line6, sysex, size); - kfree(sysex); - return 0; -} - -/* - "read" request on "serial_number" special file. -*/ -static ssize_t serial_number_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - - return sprintf(buf, "%d\n", pod->serial_number); -} - -/* - "read" request on "firmware_version" special file. -*/ -static ssize_t firmware_version_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - - return sprintf(buf, "%d.%02d\n", pod->firmware_version / 100, - pod->firmware_version % 100); -} - -/* - "read" request on "device_id" special file. -*/ -static ssize_t device_id_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_pod *pod = usb_get_intfdata(interface); - - return sprintf(buf, "%d\n", pod->device_id); -} - -/* - POD startup procedure. - This is a sequence of functions with special requirements (e.g., must - not run immediately after initialization, must not run in interrupt - context). After the last one has finished, the device is ready to use. -*/ - -static void pod_startup1(struct usb_line6_pod *pod) -{ - CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_INIT); - - /* delay startup procedure: */ - line6_start_timer(&pod->startup_timer, POD_STARTUP_DELAY, pod_startup2, - (unsigned long)pod); -} - -static void pod_startup2(unsigned long data) -{ - struct usb_line6_pod *pod = (struct usb_line6_pod *)data; - struct usb_line6 *line6 = &pod->line6; - - CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_VERSIONREQ); - - /* request firmware version: */ - line6_version_request_async(line6); -} - -static void pod_startup3(struct usb_line6_pod *pod) -{ - CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_WORKQUEUE); - - /* schedule work for global work queue: */ - schedule_work(&pod->startup_work); -} - -static void pod_startup4(struct work_struct *work) -{ - struct usb_line6_pod *pod = - container_of(work, struct usb_line6_pod, startup_work); - struct usb_line6 *line6 = &pod->line6; - - CHECK_STARTUP_PROGRESS(pod->startup_progress, POD_STARTUP_SETUP); - - /* serial number: */ - line6_read_serial_number(&pod->line6, &pod->serial_number); - - /* ALSA audio interface: */ - line6_register_audio(line6); -} - -/* POD special files: */ -static DEVICE_ATTR_RO(device_id); -static DEVICE_ATTR_RO(firmware_version); -static DEVICE_ATTR_RO(serial_number); - -/* control info callback */ -static int snd_pod_control_monitor_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 65535; - return 0; -} - -/* control get callback */ -static int snd_pod_control_monitor_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6; - - ucontrol->value.integer.value[0] = pod->monitor_level; - return 0; -} - -/* control put callback */ -static int snd_pod_control_monitor_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - struct usb_line6_pod *pod = (struct usb_line6_pod *)line6pcm->line6; - - if (ucontrol->value.integer.value[0] == pod->monitor_level) - return 0; - - pod->monitor_level = ucontrol->value.integer.value[0]; - pod_set_system_param_int(pod, ucontrol->value.integer.value[0], - POD_MONITOR_LEVEL); - return 1; -} - -/* control definition */ -static struct snd_kcontrol_new pod_control_monitor = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Monitor Playback Volume", - .index = 0, - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = snd_pod_control_monitor_info, - .get = snd_pod_control_monitor_get, - .put = snd_pod_control_monitor_put -}; - -/* - POD destructor. -*/ -static void pod_destruct(struct usb_interface *interface) -{ - struct usb_line6_pod *pod = usb_get_intfdata(interface); - - if (pod == NULL) - return; - line6_cleanup_audio(&pod->line6); - - del_timer(&pod->startup_timer); - cancel_work_sync(&pod->startup_work); -} - -/* - Create sysfs entries. -*/ -static int pod_create_files2(struct device *dev) -{ - int err; - - CHECK_RETURN(device_create_file(dev, &dev_attr_device_id)); - CHECK_RETURN(device_create_file(dev, &dev_attr_firmware_version)); - CHECK_RETURN(device_create_file(dev, &dev_attr_serial_number)); - return 0; -} - -/* - Try to init POD device. -*/ -static int pod_try_init(struct usb_interface *interface, - struct usb_line6_pod *pod) -{ - int err; - struct usb_line6 *line6 = &pod->line6; - - init_timer(&pod->startup_timer); - INIT_WORK(&pod->startup_work, pod_startup4); - - if ((interface == NULL) || (pod == NULL)) - return -ENODEV; - - /* create sysfs entries: */ - err = pod_create_files2(&interface->dev); - if (err < 0) - return err; - - /* initialize audio system: */ - err = line6_init_audio(line6); - if (err < 0) - return err; - - /* initialize MIDI subsystem: */ - err = line6_init_midi(line6); - if (err < 0) - return err; - - /* initialize PCM subsystem: */ - err = line6_init_pcm(line6, &pod_pcm_properties); - if (err < 0) - return err; - - /* register monitor control: */ - err = snd_ctl_add(line6->card, - snd_ctl_new1(&pod_control_monitor, line6->line6pcm)); - if (err < 0) - return err; - - /* - When the sound card is registered at this point, the PODxt Live - displays "Invalid Code Error 07", so we do it later in the event - handler. - */ - - if (pod->line6.properties->capabilities & LINE6_BIT_CONTROL) { - pod->monitor_level = POD_SYSTEM_INVALID; - - /* initiate startup procedure: */ - pod_startup1(pod); - } - - return 0; -} - -/* - Init POD device (and clean up in case of failure). -*/ -int line6_pod_init(struct usb_interface *interface, struct usb_line6_pod *pod) -{ - int err = pod_try_init(interface, pod); - - if (err < 0) - pod_destruct(interface); - - return err; -} - -/* - POD device disconnected. -*/ -void line6_pod_disconnect(struct usb_interface *interface) -{ - struct usb_line6_pod *pod; - - if (interface == NULL) - return; - pod = usb_get_intfdata(interface); - - if (pod != NULL) { - struct snd_line6_pcm *line6pcm = pod->line6.line6pcm; - struct device *dev = &interface->dev; - - if (line6pcm != NULL) - line6_pcm_disconnect(line6pcm); - - if (dev != NULL) { - /* remove sysfs entries: */ - device_remove_file(dev, &dev_attr_device_id); - device_remove_file(dev, &dev_attr_firmware_version); - device_remove_file(dev, &dev_attr_serial_number); - } - } - - pod_destruct(interface); -} diff --git a/drivers/staging/line6/pod.h b/drivers/staging/line6/pod.h deleted file mode 100644 index 3e3f1671337a..000000000000 --- a/drivers/staging/line6/pod.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * 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. - * - */ - -#ifndef POD_H -#define POD_H - -#include <linux/interrupt.h> -#include <linux/spinlock.h> -#include <linux/usb.h> - -#include <sound/core.h> - -#include "driver.h" - -/* - PODxt Live interfaces -*/ -#define PODXTLIVE_INTERFACE_POD 0 -#define PODXTLIVE_INTERFACE_VARIAX 1 - -/* - Locate name in binary program dump -*/ -#define POD_NAME_OFFSET 0 -#define POD_NAME_LENGTH 16 - -/* - Other constants -*/ -#define POD_CONTROL_SIZE 0x80 -#define POD_BUFSIZE_DUMPREQ 7 -#define POD_STARTUP_DELAY 1000 - -/* - Stages of POD startup procedure -*/ -enum { - POD_STARTUP_INIT = 1, - POD_STARTUP_VERSIONREQ, - POD_STARTUP_WORKQUEUE, - POD_STARTUP_SETUP, - POD_STARTUP_LAST = POD_STARTUP_SETUP - 1 -}; - -struct usb_line6_pod { - /** - Generic Line6 USB data. - */ - struct usb_line6 line6; - - /** - Instrument monitor level. - */ - int monitor_level; - - /** - Timer for device initializaton. - */ - struct timer_list startup_timer; - - /** - Work handler for device initializaton. - */ - struct work_struct startup_work; - - /** - Current progress in startup procedure. - */ - int startup_progress; - - /** - Serial number of device. - */ - int serial_number; - - /** - Firmware version (x 100). - */ - int firmware_version; - - /** - Device ID. - */ - int device_id; -}; - -extern void line6_pod_disconnect(struct usb_interface *interface); -extern int line6_pod_init(struct usb_interface *interface, - struct usb_line6_pod *pod); -extern void line6_pod_process_message(struct usb_line6_pod *pod); -extern void line6_pod_transmit_parameter(struct usb_line6_pod *pod, int param, - u8 value); - -#endif diff --git a/drivers/staging/line6/podhd.c b/drivers/staging/line6/podhd.c deleted file mode 100644 index 7ef45437b4f2..000000000000 --- a/drivers/staging/line6/podhd.c +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Line6 Pod HD - * - * Copyright (C) 2011 Stefan Hajnoczi <stefanha@gmail.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, version 2. - * - */ - -#include <sound/core.h> -#include <sound/pcm.h> - -#include "audio.h" -#include "driver.h" -#include "pcm.h" -#include "podhd.h" - -#define PODHD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */ - -static struct snd_ratden podhd_ratden = { - .num_min = 48000, - .num_max = 48000, - .num_step = 1, - .den = 1, -}; - -static struct line6_pcm_properties podhd_pcm_properties = { - .snd_line6_playback_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | -#ifdef CONFIG_PM - SNDRV_PCM_INFO_RESUME | -#endif - SNDRV_PCM_INFO_SYNC_START), - .formats = SNDRV_PCM_FMTBIT_S24_3LE, - .rates = SNDRV_PCM_RATE_48000, - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 60000, - .period_bytes_min = 64, - .period_bytes_max = 8192, - .periods_min = 1, - .periods_max = 1024}, - .snd_line6_capture_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | -#ifdef CONFIG_PM - SNDRV_PCM_INFO_RESUME | -#endif - SNDRV_PCM_INFO_SYNC_START), - .formats = SNDRV_PCM_FMTBIT_S24_3LE, - .rates = SNDRV_PCM_RATE_48000, - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 60000, - .period_bytes_min = 64, - .period_bytes_max = 8192, - .periods_min = 1, - .periods_max = 1024}, - .snd_line6_rates = { - .nrats = 1, - .rats = &podhd_ratden}, - .bytes_per_frame = PODHD_BYTES_PER_FRAME -}; - -/* - POD HD destructor. -*/ -static void podhd_destruct(struct usb_interface *interface) -{ - struct usb_line6_podhd *podhd = usb_get_intfdata(interface); - - if (podhd == NULL) - return; - line6_cleanup_audio(&podhd->line6); -} - -/* - Try to init POD HD device. -*/ -static int podhd_try_init(struct usb_interface *interface, - struct usb_line6_podhd *podhd) -{ - int err; - struct usb_line6 *line6 = &podhd->line6; - - if ((interface == NULL) || (podhd == NULL)) - return -ENODEV; - - /* initialize audio system: */ - err = line6_init_audio(line6); - if (err < 0) - return err; - - /* initialize MIDI subsystem: */ - err = line6_init_midi(line6); - if (err < 0) - return err; - - /* initialize PCM subsystem: */ - err = line6_init_pcm(line6, &podhd_pcm_properties); - if (err < 0) - return err; - - /* register USB audio system: */ - err = line6_register_audio(line6); - return err; -} - -/* - Init POD HD device (and clean up in case of failure). -*/ -int line6_podhd_init(struct usb_interface *interface, - struct usb_line6_podhd *podhd) -{ - int err = podhd_try_init(interface, podhd); - - if (err < 0) - podhd_destruct(interface); - - return err; -} - -/* - POD HD device disconnected. -*/ -void line6_podhd_disconnect(struct usb_interface *interface) -{ - struct usb_line6_podhd *podhd; - - if (interface == NULL) - return; - podhd = usb_get_intfdata(interface); - - if (podhd != NULL) { - struct snd_line6_pcm *line6pcm = podhd->line6.line6pcm; - - if (line6pcm != NULL) - line6_pcm_disconnect(line6pcm); - } - - podhd_destruct(interface); -} diff --git a/drivers/staging/line6/podhd.h b/drivers/staging/line6/podhd.h deleted file mode 100644 index 652f74056bb9..000000000000 --- a/drivers/staging/line6/podhd.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Line6 Pod HD - * - * Copyright (C) 2011 Stefan Hajnoczi <stefanha@gmail.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, version 2. - * - */ - -#ifndef PODHD_H -#define PODHD_H - -#include <linux/usb.h> - -#include "driver.h" - -struct usb_line6_podhd { - /** - Generic Line6 USB data. - */ - struct usb_line6 line6; -}; - -extern void line6_podhd_disconnect(struct usb_interface *interface); -extern int line6_podhd_init(struct usb_interface *interface, - struct usb_line6_podhd *podhd); - -#endif /* PODHD_H */ diff --git a/drivers/staging/line6/revision.h b/drivers/staging/line6/revision.h deleted file mode 100644 index b4eee2b73831..000000000000 --- a/drivers/staging/line6/revision.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef DRIVER_REVISION -/* current subversion revision */ -#define DRIVER_REVISION " (904)" -#endif diff --git a/drivers/staging/line6/toneport.c b/drivers/staging/line6/toneport.c deleted file mode 100644 index 69437158d383..000000000000 --- a/drivers/staging/line6/toneport.c +++ /dev/null @@ -1,460 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * Emil Myhrman (emil.myhrman@gmail.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, version 2. - * - */ - -#include <linux/wait.h> -#include <sound/control.h> - -#include "audio.h" -#include "capture.h" -#include "driver.h" -#include "playback.h" -#include "toneport.h" - -static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2); - -#define TONEPORT_PCM_DELAY 1 - -static struct snd_ratden toneport_ratden = { - .num_min = 44100, - .num_max = 44100, - .num_step = 1, - .den = 1 -}; - -static struct line6_pcm_properties toneport_pcm_properties = { - .snd_line6_playback_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | -#ifdef CONFIG_PM - SNDRV_PCM_INFO_RESUME | -#endif - SNDRV_PCM_INFO_SYNC_START), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_KNOT, - .rate_min = 44100, - .rate_max = 44100, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 60000, - .period_bytes_min = 64, - .period_bytes_max = 8192, - .periods_min = 1, - .periods_max = 1024}, - .snd_line6_capture_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID | -#ifdef CONFIG_PM - SNDRV_PCM_INFO_RESUME | -#endif - SNDRV_PCM_INFO_SYNC_START), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_KNOT, - .rate_min = 44100, - .rate_max = 44100, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 60000, - .period_bytes_min = 64, - .period_bytes_max = 8192, - .periods_min = 1, - .periods_max = 1024}, - .snd_line6_rates = { - .nrats = 1, - .rats = &toneport_ratden}, - .bytes_per_frame = 4 -}; - -/* - For the led on Guitarport. - Brightness goes from 0x00 to 0x26. Set a value above this to have led - blink. - (void cmd_0x02(byte red, byte green) -*/ -static int led_red = 0x00; -static int led_green = 0x26; - -static const struct { - const char *name; - int code; -} toneport_source_info[] = { - {"Microphone", 0x0a01}, - {"Line", 0x0801}, - {"Instrument", 0x0b01}, - {"Inst & Mic", 0x0901} -}; - -static bool toneport_has_led(short product) -{ - return - (product == LINE6_DEVID_GUITARPORT) || - (product == LINE6_DEVID_TONEPORT_GX); - /* add your device here if you are missing support for the LEDs */ -} - -static void toneport_update_led(struct device *dev) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_toneport *tp = usb_get_intfdata(interface); - struct usb_line6 *line6; - - if (!tp) - return; - - line6 = &tp->line6; - if (line6) - toneport_send_cmd(line6->usbdev, (led_red << 8) | 0x0002, - led_green); -} - -static ssize_t toneport_set_led_red(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - int retval; - - retval = kstrtoint(buf, 10, &led_red); - if (retval) - return retval; - - toneport_update_led(dev); - return count; -} - -static ssize_t toneport_set_led_green(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - int retval; - - retval = kstrtoint(buf, 10, &led_green); - if (retval) - return retval; - - toneport_update_led(dev); - return count; -} - -static DEVICE_ATTR(led_red, S_IWUSR | S_IRUGO, line6_nop_read, - toneport_set_led_red); -static DEVICE_ATTR(led_green, S_IWUSR | S_IRUGO, line6_nop_read, - toneport_set_led_green); - -static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2) -{ - int ret; - - ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0), 0x67, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT, - cmd1, cmd2, NULL, 0, LINE6_TIMEOUT * HZ); - - if (ret < 0) { - dev_err(&usbdev->dev, "send failed (error %d)\n", ret); - return ret; - } - - return 0; -} - -/* monitor info callback */ -static int snd_toneport_monitor_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 256; - return 0; -} - -/* monitor get callback */ -static int snd_toneport_monitor_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - - ucontrol->value.integer.value[0] = line6pcm->volume_monitor; - return 0; -} - -/* monitor put callback */ -static int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - - if (ucontrol->value.integer.value[0] == line6pcm->volume_monitor) - return 0; - - line6pcm->volume_monitor = ucontrol->value.integer.value[0]; - - if (line6pcm->volume_monitor > 0) - line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_MONITOR); - else - line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR); - - return 1; -} - -/* source info callback */ -static int snd_toneport_source_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - const int size = ARRAY_SIZE(toneport_source_info); - - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = size; - - if (uinfo->value.enumerated.item >= size) - uinfo->value.enumerated.item = size - 1; - - strcpy(uinfo->value.enumerated.name, - toneport_source_info[uinfo->value.enumerated.item].name); - - return 0; -} - -/* source get callback */ -static int snd_toneport_source_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - struct usb_line6_toneport *toneport = - (struct usb_line6_toneport *)line6pcm->line6; - ucontrol->value.enumerated.item[0] = toneport->source; - return 0; -} - -/* source put callback */ -static int snd_toneport_source_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - struct usb_line6_toneport *toneport = - (struct usb_line6_toneport *)line6pcm->line6; - unsigned int source; - - source = ucontrol->value.enumerated.item[0]; - if (source >= ARRAY_SIZE(toneport_source_info)) - return -EINVAL; - if (source == toneport->source) - return 0; - - toneport->source = source; - toneport_send_cmd(toneport->line6.usbdev, - toneport_source_info[source].code, 0x0000); - return 1; -} - -static void toneport_start_pcm(unsigned long arg) -{ - struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)arg; - struct usb_line6 *line6 = &toneport->line6; - - line6_pcm_acquire(line6->line6pcm, LINE6_BITS_PCM_MONITOR); -} - -/* control definition */ -static struct snd_kcontrol_new toneport_control_monitor = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Monitor Playback Volume", - .index = 0, - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = snd_toneport_monitor_info, - .get = snd_toneport_monitor_get, - .put = snd_toneport_monitor_put -}; - -/* source selector definition */ -static struct snd_kcontrol_new toneport_control_source = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "PCM Capture Source", - .index = 0, - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, - .info = snd_toneport_source_info, - .get = snd_toneport_source_get, - .put = snd_toneport_source_put -}; - -/* - Toneport destructor. -*/ -static void toneport_destruct(struct usb_interface *interface) -{ - struct usb_line6_toneport *toneport = usb_get_intfdata(interface); - - if (toneport == NULL) - return; - line6_cleanup_audio(&toneport->line6); -} - -/* - Setup Toneport device. -*/ -static void toneport_setup(struct usb_line6_toneport *toneport) -{ - int ticks; - struct usb_line6 *line6 = &toneport->line6; - struct usb_device *usbdev = line6->usbdev; - u16 idProduct = le16_to_cpu(usbdev->descriptor.idProduct); - - /* sync time on device with host: */ - ticks = (int)get_seconds(); - line6_write_data(line6, 0x80c6, &ticks, 4); - - /* enable device: */ - toneport_send_cmd(usbdev, 0x0301, 0x0000); - - /* initialize source select: */ - switch (le16_to_cpu(usbdev->descriptor.idProduct)) { - case LINE6_DEVID_TONEPORT_UX1: - case LINE6_DEVID_TONEPORT_UX2: - case LINE6_DEVID_PODSTUDIO_UX1: - case LINE6_DEVID_PODSTUDIO_UX2: - toneport_send_cmd(usbdev, - toneport_source_info[toneport->source].code, - 0x0000); - } - - if (toneport_has_led(idProduct)) - toneport_update_led(&usbdev->dev); -} - -/* - Try to init Toneport device. -*/ -static int toneport_try_init(struct usb_interface *interface, - struct usb_line6_toneport *toneport) -{ - int err; - struct usb_line6 *line6 = &toneport->line6; - struct usb_device *usbdev = line6->usbdev; - u16 idProduct = le16_to_cpu(usbdev->descriptor.idProduct); - - if ((interface == NULL) || (toneport == NULL)) - return -ENODEV; - - /* initialize audio system: */ - err = line6_init_audio(line6); - if (err < 0) - return err; - - /* initialize PCM subsystem: */ - err = line6_init_pcm(line6, &toneport_pcm_properties); - if (err < 0) - return err; - - /* register monitor control: */ - err = snd_ctl_add(line6->card, - snd_ctl_new1(&toneport_control_monitor, - line6->line6pcm)); - if (err < 0) - return err; - - /* register source select control: */ - switch (le16_to_cpu(usbdev->descriptor.idProduct)) { - case LINE6_DEVID_TONEPORT_UX1: - case LINE6_DEVID_TONEPORT_UX2: - case LINE6_DEVID_PODSTUDIO_UX1: - case LINE6_DEVID_PODSTUDIO_UX2: - err = - snd_ctl_add(line6->card, - snd_ctl_new1(&toneport_control_source, - line6->line6pcm)); - if (err < 0) - return err; - } - - /* register audio system: */ - err = line6_register_audio(line6); - if (err < 0) - return err; - - line6_read_serial_number(line6, &toneport->serial_number); - line6_read_data(line6, 0x80c2, &toneport->firmware_version, 1); - - if (toneport_has_led(idProduct)) { - CHECK_RETURN(device_create_file - (&interface->dev, &dev_attr_led_red)); - CHECK_RETURN(device_create_file - (&interface->dev, &dev_attr_led_green)); - } - - toneport_setup(toneport); - - init_timer(&toneport->timer); - toneport->timer.expires = jiffies + TONEPORT_PCM_DELAY * HZ; - toneport->timer.function = toneport_start_pcm; - toneport->timer.data = (unsigned long)toneport; - add_timer(&toneport->timer); - - return 0; -} - -/* - Init Toneport device (and clean up in case of failure). -*/ -int line6_toneport_init(struct usb_interface *interface, - struct usb_line6_toneport *toneport) -{ - int err = toneport_try_init(interface, toneport); - - if (err < 0) - toneport_destruct(interface); - - return err; -} - -/* - Resume Toneport device after reset. -*/ -void line6_toneport_reset_resume(struct usb_line6_toneport *toneport) -{ - toneport_setup(toneport); -} - -/* - Toneport device disconnected. -*/ -void line6_toneport_disconnect(struct usb_interface *interface) -{ - struct usb_line6_toneport *toneport; - u16 idProduct; - - if (interface == NULL) - return; - - toneport = usb_get_intfdata(interface); - del_timer_sync(&toneport->timer); - idProduct = le16_to_cpu(toneport->line6.usbdev->descriptor.idProduct); - - if (toneport_has_led(idProduct)) { - device_remove_file(&interface->dev, &dev_attr_led_red); - device_remove_file(&interface->dev, &dev_attr_led_green); - } - - if (toneport != NULL) { - struct snd_line6_pcm *line6pcm = toneport->line6.line6pcm; - - if (line6pcm != NULL) { - line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR); - line6_pcm_disconnect(line6pcm); - } - } - - toneport_destruct(interface); -} diff --git a/drivers/staging/line6/toneport.h b/drivers/staging/line6/toneport.h deleted file mode 100644 index 8576b7263648..000000000000 --- a/drivers/staging/line6/toneport.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * 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. - * - */ - -#ifndef TONEPORT_H -#define TONEPORT_H - -#include <linux/usb.h> -#include <sound/core.h> - -#include "driver.h" - -struct usb_line6_toneport { - /** - Generic Line6 USB data. - */ - struct usb_line6 line6; - - /** - Source selector. - */ - int source; - - /** - Serial number of device. - */ - int serial_number; - - /** - Firmware version (x 100). - */ - int firmware_version; - - /** - Timer for delayed PCM startup. - */ - struct timer_list timer; -}; - -extern void line6_toneport_disconnect(struct usb_interface *interface); -extern int line6_toneport_init(struct usb_interface *interface, - struct usb_line6_toneport *toneport); -extern void line6_toneport_reset_resume(struct usb_line6_toneport *toneport); - -#endif diff --git a/drivers/staging/line6/usbdefs.h b/drivers/staging/line6/usbdefs.h deleted file mode 100644 index 2d1cc472bead..000000000000 --- a/drivers/staging/line6/usbdefs.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2005-2008 Markus Grabner (grabner@icg.tugraz.at) - * - * 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. - * - */ - -#ifndef USBDEFS_H -#define USBDEFS_H - -#define LINE6_VENDOR_ID 0x0e41 - -#define USB_INTERVALS_PER_SECOND 1000 - -/* - Device ids. -*/ -#define LINE6_DEVID_BASSPODXT 0x4250 -#define LINE6_DEVID_BASSPODXTLIVE 0x4642 -#define LINE6_DEVID_BASSPODXTPRO 0x4252 -#define LINE6_DEVID_GUITARPORT 0x4750 -#define LINE6_DEVID_POCKETPOD 0x5051 -#define LINE6_DEVID_PODHD300 0x5057 -#define LINE6_DEVID_PODHD400 0x5058 -#define LINE6_DEVID_PODHD500 0x414D -#define LINE6_DEVID_PODSTUDIO_GX 0x4153 -#define LINE6_DEVID_PODSTUDIO_UX1 0x4150 -#define LINE6_DEVID_PODSTUDIO_UX2 0x4151 -#define LINE6_DEVID_PODX3 0x414a -#define LINE6_DEVID_PODX3LIVE 0x414b -#define LINE6_DEVID_PODXT 0x5044 -#define LINE6_DEVID_PODXTLIVE 0x4650 -#define LINE6_DEVID_PODXTPRO 0x5050 -#define LINE6_DEVID_TONEPORT_GX 0x4147 -#define LINE6_DEVID_TONEPORT_UX1 0x4141 -#define LINE6_DEVID_TONEPORT_UX2 0x4142 -#define LINE6_DEVID_VARIAX 0x534d - -#define LINE6_BIT(x) LINE6_BIT_ ## x = 1 << LINE6_INDEX_ ## x - -enum { - LINE6_INDEX_BASSPODXT, - LINE6_INDEX_BASSPODXTLIVE, - LINE6_INDEX_BASSPODXTPRO, - LINE6_INDEX_GUITARPORT, - LINE6_INDEX_POCKETPOD, - LINE6_INDEX_PODHD300, - LINE6_INDEX_PODHD400, - LINE6_INDEX_PODHD500, - LINE6_INDEX_PODSTUDIO_GX, - LINE6_INDEX_PODSTUDIO_UX1, - LINE6_INDEX_PODSTUDIO_UX2, - LINE6_INDEX_PODX3, - LINE6_INDEX_PODX3LIVE, - LINE6_INDEX_PODXT, - LINE6_INDEX_PODXTLIVE, - LINE6_INDEX_PODXTPRO, - LINE6_INDEX_TONEPORT_GX, - LINE6_INDEX_TONEPORT_UX1, - LINE6_INDEX_TONEPORT_UX2, - LINE6_INDEX_VARIAX, - - LINE6_BIT(BASSPODXT), - LINE6_BIT(BASSPODXTLIVE), - LINE6_BIT(BASSPODXTPRO), - LINE6_BIT(GUITARPORT), - LINE6_BIT(POCKETPOD), - LINE6_BIT(PODHD300), - LINE6_BIT(PODHD400), - LINE6_BIT(PODHD500), - LINE6_BIT(PODSTUDIO_GX), - LINE6_BIT(PODSTUDIO_UX1), - LINE6_BIT(PODSTUDIO_UX2), - LINE6_BIT(PODX3), - LINE6_BIT(PODX3LIVE), - LINE6_BIT(PODXT), - LINE6_BIT(PODXTLIVE), - LINE6_BIT(PODXTPRO), - LINE6_BIT(TONEPORT_GX), - LINE6_BIT(TONEPORT_UX1), - LINE6_BIT(TONEPORT_UX2), - LINE6_BIT(VARIAX), - - LINE6_BITS_PRO = LINE6_BIT_BASSPODXTPRO | LINE6_BIT_PODXTPRO, - LINE6_BITS_LIVE = LINE6_BIT_BASSPODXTLIVE | LINE6_BIT_PODXTLIVE | - LINE6_BIT_PODX3LIVE, - LINE6_BITS_PODXTALL = LINE6_BIT_PODXT | LINE6_BIT_PODXTLIVE | - LINE6_BIT_PODXTPRO, - LINE6_BITS_PODX3ALL = LINE6_BIT_PODX3 | LINE6_BIT_PODX3LIVE, - LINE6_BITS_PODHDALL = LINE6_BIT_PODHD300 | - LINE6_BIT_PODHD400 | - LINE6_BIT_PODHD500, - LINE6_BITS_BASSPODXTALL = LINE6_BIT_BASSPODXT | - LINE6_BIT_BASSPODXTLIVE | - LINE6_BIT_BASSPODXTPRO -}; - -/* device supports settings parameter via USB */ -#define LINE6_BIT_CONTROL (1 << 0) -/* device supports PCM input/output via USB */ -#define LINE6_BIT_PCM (1 << 1) -/* device support hardware monitoring */ -#define LINE6_BIT_HWMON (1 << 2) - -#define LINE6_BIT_CTRL_PCM_HW (LINE6_BIT_CONTROL | \ - LINE6_BIT_PCM | \ - LINE6_BIT_HWMON) - -#define LINE6_FALLBACK_INTERVAL 10 -#define LINE6_FALLBACK_MAXPACKETSIZE 16 - -#endif diff --git a/drivers/staging/line6/variax.c b/drivers/staging/line6/variax.c deleted file mode 100644 index ae2be99f9a92..000000000000 --- a/drivers/staging/line6/variax.c +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * 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. - * - */ - -#include <linux/slab.h> - -#include "audio.h" -#include "driver.h" -#include "variax.h" - -#define VARIAX_OFFSET_ACTIVATE 7 - -/* - This message is sent by the device during initialization and identifies - the connected guitar version. -*/ -static const char variax_init_version[] = { - 0xf0, 0x7e, 0x7f, 0x06, 0x02, 0x00, 0x01, 0x0c, - 0x07, 0x00, 0x00, 0x00 -}; - -/* - This message is the last one sent by the device during initialization. -*/ -static const char variax_init_done[] = { - 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x6b -}; - -static const char variax_activate[] = { - 0xf0, 0x00, 0x01, 0x0c, 0x07, 0x00, 0x2a, 0x01, - 0xf7 -}; - -/* forward declarations: */ -static void variax_startup2(unsigned long data); -static void variax_startup4(unsigned long data); -static void variax_startup5(unsigned long data); - -static void variax_activate_async(struct usb_line6_variax *variax, int a) -{ - variax->buffer_activate[VARIAX_OFFSET_ACTIVATE] = a; - line6_send_raw_message_async(&variax->line6, variax->buffer_activate, - sizeof(variax_activate)); -} - -/* - Variax startup procedure. - This is a sequence of functions with special requirements (e.g., must - not run immediately after initialization, must not run in interrupt - context). After the last one has finished, the device is ready to use. -*/ - -static void variax_startup1(struct usb_line6_variax *variax) -{ - CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_INIT); - - /* delay startup procedure: */ - line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, - variax_startup2, (unsigned long)variax); -} - -static void variax_startup2(unsigned long data) -{ - struct usb_line6_variax *variax = (struct usb_line6_variax *)data; - struct usb_line6 *line6 = &variax->line6; - - /* schedule another startup procedure until startup is complete: */ - if (variax->startup_progress >= VARIAX_STARTUP_LAST) - return; - - variax->startup_progress = VARIAX_STARTUP_VERSIONREQ; - line6_start_timer(&variax->startup_timer1, VARIAX_STARTUP_DELAY1, - variax_startup2, (unsigned long)variax); - - /* request firmware version: */ - line6_version_request_async(line6); -} - -static void variax_startup3(struct usb_line6_variax *variax) -{ - CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_WAIT); - - /* delay startup procedure: */ - line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY3, - variax_startup4, (unsigned long)variax); -} - -static void variax_startup4(unsigned long data) -{ - struct usb_line6_variax *variax = (struct usb_line6_variax *)data; - - CHECK_STARTUP_PROGRESS(variax->startup_progress, - VARIAX_STARTUP_ACTIVATE); - - /* activate device: */ - variax_activate_async(variax, 1); - line6_start_timer(&variax->startup_timer2, VARIAX_STARTUP_DELAY4, - variax_startup5, (unsigned long)variax); -} - -static void variax_startup5(unsigned long data) -{ - struct usb_line6_variax *variax = (struct usb_line6_variax *)data; - - CHECK_STARTUP_PROGRESS(variax->startup_progress, - VARIAX_STARTUP_WORKQUEUE); - - /* schedule work for global work queue: */ - schedule_work(&variax->startup_work); -} - -static void variax_startup6(struct work_struct *work) -{ - struct usb_line6_variax *variax = - container_of(work, struct usb_line6_variax, startup_work); - - CHECK_STARTUP_PROGRESS(variax->startup_progress, VARIAX_STARTUP_SETUP); - - /* ALSA audio interface: */ - line6_register_audio(&variax->line6); -} - -/* - Process a completely received message. -*/ -void line6_variax_process_message(struct usb_line6_variax *variax) -{ - const unsigned char *buf = variax->line6.buffer_message; - - switch (buf[0]) { - case LINE6_RESET: - dev_info(variax->line6.ifcdev, "VARIAX reset\n"); - break; - - case LINE6_SYSEX_BEGIN: - if (memcmp(buf + 1, variax_init_version + 1, - sizeof(variax_init_version) - 1) == 0) { - variax_startup3(variax); - } else if (memcmp(buf + 1, variax_init_done + 1, - sizeof(variax_init_done) - 1) == 0) { - /* notify of complete initialization: */ - variax_startup4((unsigned long)variax); - } - break; - } -} - -/* - Variax destructor. -*/ -static void variax_destruct(struct usb_interface *interface) -{ - struct usb_line6_variax *variax = usb_get_intfdata(interface); - - if (variax == NULL) - return; - line6_cleanup_audio(&variax->line6); - - del_timer(&variax->startup_timer1); - del_timer(&variax->startup_timer2); - cancel_work_sync(&variax->startup_work); - - kfree(variax->buffer_activate); -} - -/* - Try to init workbench device. -*/ -static int variax_try_init(struct usb_interface *interface, - struct usb_line6_variax *variax) -{ - int err; - - init_timer(&variax->startup_timer1); - init_timer(&variax->startup_timer2); - INIT_WORK(&variax->startup_work, variax_startup6); - - if ((interface == NULL) || (variax == NULL)) - return -ENODEV; - - /* initialize USB buffers: */ - variax->buffer_activate = kmemdup(variax_activate, - sizeof(variax_activate), GFP_KERNEL); - - if (variax->buffer_activate == NULL) { - dev_err(&interface->dev, "Out of memory\n"); - return -ENOMEM; - } - - /* initialize audio system: */ - err = line6_init_audio(&variax->line6); - if (err < 0) - return err; - - /* initialize MIDI subsystem: */ - err = line6_init_midi(&variax->line6); - if (err < 0) - return err; - - /* initiate startup procedure: */ - variax_startup1(variax); - return 0; -} - -/* - Init workbench device (and clean up in case of failure). -*/ -int line6_variax_init(struct usb_interface *interface, - struct usb_line6_variax *variax) -{ - int err = variax_try_init(interface, variax); - - if (err < 0) - variax_destruct(interface); - - return err; -} - -/* - Workbench device disconnected. -*/ -void line6_variax_disconnect(struct usb_interface *interface) -{ - if (interface == NULL) - return; - - variax_destruct(interface); -} diff --git a/drivers/staging/line6/variax.h b/drivers/staging/line6/variax.h deleted file mode 100644 index 24de79620d89..000000000000 --- a/drivers/staging/line6/variax.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Line6 Linux USB driver - 0.9.1beta - * - * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) - * - * 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. - * - */ - -#ifndef VARIAX_H -#define VARIAX_H - -#include <linux/spinlock.h> -#include <linux/usb.h> -#include <linux/wait.h> -#include <sound/core.h> - -#include "driver.h" - -#define VARIAX_STARTUP_DELAY1 1000 -#define VARIAX_STARTUP_DELAY3 100 -#define VARIAX_STARTUP_DELAY4 100 - -/* - Stages of Variax startup procedure -*/ -enum { - VARIAX_STARTUP_INIT = 1, - VARIAX_STARTUP_VERSIONREQ, - VARIAX_STARTUP_WAIT, - VARIAX_STARTUP_ACTIVATE, - VARIAX_STARTUP_WORKQUEUE, - VARIAX_STARTUP_SETUP, - VARIAX_STARTUP_LAST = VARIAX_STARTUP_SETUP - 1 -}; - -struct usb_line6_variax { - /** - Generic Line6 USB data. - */ - struct usb_line6 line6; - - /** - Buffer for activation code. - */ - unsigned char *buffer_activate; - - /** - Handler for device initializaton. - */ - struct work_struct startup_work; - - /** - Timers for device initializaton. - */ - struct timer_list startup_timer1; - struct timer_list startup_timer2; - - /** - Current progress in startup procedure. - */ - int startup_progress; -}; - -extern void line6_variax_disconnect(struct usb_interface *interface); -extern int line6_variax_init(struct usb_interface *interface, - struct usb_line6_variax *variax); -extern void line6_variax_process_message(struct usb_line6_variax *variax); - -#endif diff --git a/drivers/staging/lustre/lustre/llite/vvp_io.c b/drivers/staging/lustre/lustre/llite/vvp_io.c index 930f6010203e..65d610abe06e 100644 --- a/drivers/staging/lustre/lustre/llite/vvp_io.c +++ b/drivers/staging/lustre/lustre/llite/vvp_io.c @@ -632,7 +632,7 @@ static int vvp_io_kernel_fault(struct vvp_fault_io *cfio) return 0; } - if (cfio->fault.ft_flags & VM_FAULT_SIGBUS) { + if (cfio->fault.ft_flags & (VM_FAULT_SIGBUS | VM_FAULT_SIGSEGV)) { CDEBUG(D_PAGE, "got addr %p - SIGBUS\n", vmf->virtual_address); return -EFAULT; } diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig index 2a054a99d433..96498b7fc20e 100644 --- a/drivers/staging/media/Kconfig +++ b/drivers/staging/media/Kconfig @@ -27,18 +27,12 @@ source "drivers/staging/media/davinci_vpfe/Kconfig" source "drivers/staging/media/dt3155v4l/Kconfig" -source "drivers/staging/media/tlg2300/Kconfig" - source "drivers/staging/media/mn88472/Kconfig" source "drivers/staging/media/mn88473/Kconfig" source "drivers/staging/media/omap4iss/Kconfig" -source "drivers/staging/media/parport/Kconfig" - -source "drivers/staging/media/vino/Kconfig" - # Keep LIRC at the end, as it has sub-menus source "drivers/staging/media/lirc/Kconfig" diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile index 412b28408398..a9006bcb4472 100644 --- a/drivers/staging/media/Makefile +++ b/drivers/staging/media/Makefile @@ -6,7 +6,3 @@ obj-$(CONFIG_VIDEO_DM365_VPFE) += davinci_vpfe/ obj-$(CONFIG_VIDEO_OMAP4) += omap4iss/ obj-$(CONFIG_DVB_MN88472) += mn88472/ obj-$(CONFIG_DVB_MN88473) += mn88473/ -obj-y += parport/ -obj-$(CONFIG_VIDEO_TLG2300) += tlg2300/ -obj-y += vino/ - diff --git a/drivers/staging/media/bcm2048/radio-bcm2048.c b/drivers/staging/media/bcm2048/radio-bcm2048.c index 60a57b2a8fb2..538250667918 100644 --- a/drivers/staging/media/bcm2048/radio-bcm2048.c +++ b/drivers/staging/media/bcm2048/radio-bcm2048.c @@ -2684,9 +2684,7 @@ static int __exit bcm2048_i2c_driver_remove(struct i2c_client *client) vd = bdev->videodev; bcm2048_sysfs_unregister_properties(bdev, ARRAY_SIZE(attrs)); - - if (vd) - video_unregister_device(vd); + video_unregister_device(vd); if (bdev->power_state) bcm2048_set_power_state(bdev, BCM2048_POWER_OFF); @@ -2699,8 +2697,6 @@ static int __exit bcm2048_i2c_driver_remove(struct i2c_client *client) kfree(bdev); } - i2c_set_clientdata(client, NULL); - return 0; } diff --git a/drivers/staging/media/davinci_vpfe/dm365_isif.c b/drivers/staging/media/davinci_vpfe/dm365_isif.c index 0ba0bf2c1cff..bcf762bc233d 100644 --- a/drivers/staging/media/davinci_vpfe/dm365_isif.c +++ b/drivers/staging/media/davinci_vpfe/dm365_isif.c @@ -1535,7 +1535,7 @@ isif_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, } /* - * isif_pad_set_crop() - set crop rectangle on pad + * isif_pad_set_selection() - set crop rectangle on pad * @sd: VPFE isif V4L2 subdevice * @fh: V4L2 subdev file handle * @code: pointer to v4l2_subdev_mbus_code_enum structure @@ -1543,35 +1543,36 @@ isif_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, * Return 0 on success, -EINVAL if pad is invalid */ static int -isif_pad_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +isif_pad_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - /* check wether its a valid pad */ - if (crop->pad != ISIF_PAD_SINK) + /* check whether it's a valid pad and target */ + if (sel->pad != ISIF_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - format = __isif_get_format(vpfe_isif, fh, crop->pad, crop->which); + format = __isif_get_format(vpfe_isif, fh, sel->pad, sel->which); if (format == NULL) return -EINVAL; /* check wether crop rect is within limits */ - if (crop->rect.top < 0 || crop->rect.left < 0 || - (crop->rect.left + crop->rect.width > + if (sel->r.top < 0 || sel->r.left < 0 || + (sel->r.left + sel->r.width > vpfe_isif->formats[ISIF_PAD_SINK].width) || - (crop->rect.top + crop->rect.height > + (sel->r.top + sel->r.height > vpfe_isif->formats[ISIF_PAD_SINK].height)) { - crop->rect.left = 0; - crop->rect.top = 0; - crop->rect.width = format->width; - crop->rect.height = format->height; + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = format->width; + sel->r.height = format->height; } /* adjust the width to 16 pixel boundary */ - crop->rect.width = ((crop->rect.width + 15) & ~0xf); - vpfe_isif->crop = crop->rect; - if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sel->r.width = ((sel->r.width + 15) & ~0xf); + vpfe_isif->crop = sel->r; + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { isif_set_image_window(vpfe_isif); } else { struct v4l2_rect *rect; @@ -1583,7 +1584,7 @@ isif_pad_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, } /* - * isif_pad_get_crop() - get crop rectangle on pad + * isif_pad_get_selection() - get crop rectangle on pad * @sd: VPFE isif V4L2 subdevice * @fh: V4L2 subdev file handle * @code: pointer to v4l2_subdev_mbus_code_enum structure @@ -1591,22 +1592,23 @@ isif_pad_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, * Return 0 on success, -EINVAL if pad is invalid */ static int -isif_pad_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +isif_pad_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct vpfe_isif_device *vpfe_isif = v4l2_get_subdevdata(sd); - /* check wether its a valid pad */ - if (crop->pad != ISIF_PAD_SINK) + /* check whether it's a valid pad and target */ + if (sel->pad != ISIF_PAD_SINK || sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - if (crop->which == V4L2_SUBDEV_FORMAT_TRY) { + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_rect *rect; rect = v4l2_subdev_get_try_crop(fh, ISIF_PAD_SINK); - memcpy(&crop->rect, rect, sizeof(*rect)); + memcpy(&sel->r, rect, sizeof(*rect)); } else { - crop->rect = vpfe_isif->crop; + sel->r = vpfe_isif->crop; } return 0; @@ -1626,7 +1628,7 @@ isif_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct v4l2_subdev_format format; - struct v4l2_subdev_crop crop; + struct v4l2_subdev_selection sel; memset(&format, 0, sizeof(format)); format.pad = ISIF_PAD_SINK; @@ -1644,12 +1646,13 @@ isif_init_formats(struct v4l2_subdev *sd, format.format.height = MAX_HEIGHT; isif_set_format(sd, fh, &format); - memset(&crop, 0, sizeof(crop)); - crop.pad = ISIF_PAD_SINK; - crop.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; - crop.rect.width = MAX_WIDTH; - crop.rect.height = MAX_HEIGHT; - isif_pad_set_crop(sd, fh, &crop); + memset(&sel, 0, sizeof(sel)); + sel.pad = ISIF_PAD_SINK; + sel.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + sel.target = V4L2_SEL_TGT_CROP; + sel.r.width = MAX_WIDTH; + sel.r.height = MAX_HEIGHT; + isif_pad_set_selection(sd, fh, &sel); return 0; } @@ -1675,8 +1678,8 @@ static const struct v4l2_subdev_pad_ops isif_v4l2_pad_ops = { .enum_frame_size = isif_enum_frame_size, .get_fmt = isif_get_format, .set_fmt = isif_set_format, - .set_crop = isif_pad_set_crop, - .get_crop = isif_pad_get_crop, + .set_selection = isif_pad_set_selection, + .get_selection = isif_pad_get_selection, }; /* subdev operations */ diff --git a/drivers/staging/media/lirc/lirc_serial.c b/drivers/staging/media/lirc/lirc_serial.c index eb4ccb8d2a93..19628d0104ab 100644 --- a/drivers/staging/media/lirc/lirc_serial.c +++ b/drivers/staging/media/lirc/lirc_serial.c @@ -107,7 +107,7 @@ static int io; static int irq; static bool iommap; static int ioshift; -static bool softcarrier = 1; +static bool softcarrier = true; static bool share_irq; static bool debug; static int sense = -1; /* -1 = auto, 0 = active high, 1 = active low */ @@ -266,7 +266,7 @@ static unsigned long space_width; /* fetch serial input packet (1 byte) from register offset */ static u8 sinp(int offset) { - if (iommap != 0) + if (iommap) /* the register is memory-mapped */ offset <<= ioshift; @@ -276,7 +276,7 @@ static u8 sinp(int offset) /* write serial output packet (1 byte) of value to register offset */ static void soutp(int offset, u8 value) { - if (iommap != 0) + if (iommap) /* the register is memory-mapped */ offset <<= ioshift; @@ -799,10 +799,10 @@ static int lirc_serial_probe(struct platform_device *dev) * For memory mapped I/O you *might* need to use ioremap() first, * for the NSLU2 it's done in boot code. */ - if (((iommap != 0) + if (((iommap) && (devm_request_mem_region(&dev->dev, iommap, 8 << ioshift, LIRC_DRIVER_NAME) == NULL)) - || ((iommap == 0) + || ((!iommap) && (devm_request_region(&dev->dev, io, 8, LIRC_DRIVER_NAME) == NULL))) { dev_err(&dev->dev, "port %04x already in use\n", io); diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c index cc872fb4ca68..e16627ca488e 100644 --- a/drivers/staging/media/lirc/lirc_zilog.c +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -369,17 +369,17 @@ static int add_to_buf(struct IR *ir) ret = i2c_master_send(rx->c, sendbuf, 1); if (ret != 1) { dev_err(ir->l.dev, "i2c_master_send failed with %d\n", - ret); + ret); if (failures >= 3) { mutex_unlock(&ir->ir_lock); - dev_err(ir->l.dev, "unable to read from the IR chip " - "after 3 resets, giving up\n"); + dev_err(ir->l.dev, + "unable to read from the IR chip after 3 resets, giving up\n"); break; } /* Looks like the chip crashed, reset it */ - dev_err(ir->l.dev, "polling the IR receiver chip failed, " - "trying reset\n"); + dev_err(ir->l.dev, + "polling the IR receiver chip failed, trying reset\n"); set_current_state(TASK_UNINTERRUPTIBLE); if (kthread_should_stop()) { @@ -405,14 +405,16 @@ static int add_to_buf(struct IR *ir) ret = i2c_master_recv(rx->c, keybuf, sizeof(keybuf)); mutex_unlock(&ir->ir_lock); if (ret != sizeof(keybuf)) { - dev_err(ir->l.dev, "i2c_master_recv failed with %d -- " - "keeping last read buffer\n", ret); + dev_err(ir->l.dev, + "i2c_master_recv failed with %d -- keeping last read buffer\n", + ret); } else { rx->b[0] = keybuf[3]; rx->b[1] = keybuf[4]; rx->b[2] = keybuf[5]; - dev_dbg(ir->l.dev, "key (0x%02x/0x%02x)\n", - rx->b[0], rx->b[1]); + dev_dbg(ir->l.dev, + "key (0x%02x/0x%02x)\n", + rx->b[0], rx->b[1]); } /* key pressed ? */ @@ -656,8 +658,8 @@ static int send_data_block(struct IR_tx *tx, unsigned char *data_block) dev_dbg(tx->ir->l.dev, "%*ph", 5, buf); ret = i2c_master_send(tx->c, buf, tosend + 1); if (ret != tosend + 1) { - dev_err(tx->ir->l.dev, "i2c_master_send failed with %d\n", - ret); + dev_err(tx->ir->l.dev, + "i2c_master_send failed with %d\n", ret); return ret < 0 ? ret : -EFAULT; } i += tosend; @@ -710,11 +712,12 @@ static int send_boot_data(struct IR_tx *tx) } if ((buf[0] != 0x80) && (buf[0] != 0xa0)) { dev_err(tx->ir->l.dev, "unexpected IR TX init response: %02x\n", - buf[0]); + buf[0]); return 0; } - dev_notice(tx->ir->l.dev, "Zilog/Hauppauge IR blaster firmware version " - "%d.%d.%d loaded\n", buf[1], buf[2], buf[3]); + dev_notice(tx->ir->l.dev, + "Zilog/Hauppauge IR blaster firmware version %d.%d.%d loaded\n", + buf[1], buf[2], buf[3]); return 0; } @@ -759,8 +762,9 @@ static int fw_load(struct IR_tx *tx) /* Request codeset data file */ ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", tx->ir->l.dev); if (ret != 0) { - dev_err(tx->ir->l.dev, "firmware haup-ir-blaster.bin not available (%d)\n", - ret); + dev_err(tx->ir->l.dev, + "firmware haup-ir-blaster.bin not available (%d)\n", + ret); ret = ret < 0 ? ret : -EFAULT; goto out; } @@ -792,9 +796,9 @@ static int fw_load(struct IR_tx *tx) if (!read_uint8(&data, tx_data->endp, &version)) goto corrupt; if (version != 1) { - dev_err(tx->ir->l.dev, "unsupported code set file version (%u, expected" - "1) -- please upgrade to a newer driver", - version); + dev_err(tx->ir->l.dev, + "unsupported code set file version (%u, expected 1) -- please upgrade to a newer driver\n", + version); fw_unload_locked(); ret = -EFAULT; goto out; @@ -810,7 +814,7 @@ static int fw_load(struct IR_tx *tx) goto corrupt; dev_dbg(tx->ir->l.dev, "%u IR blaster codesets loaded\n", - tx_data->num_code_sets); + tx_data->num_code_sets); tx_data->code_sets = vmalloc( tx_data->num_code_sets * sizeof(char *)); @@ -940,8 +944,9 @@ static ssize_t read(struct file *filep, char __user *outbuf, size_t n, unsigned char buf[MAX_XFER_SIZE]; if (rbuf->chunk_size > sizeof(buf)) { - dev_err(ir->l.dev, "chunk_size is too big (%d)!\n", - rbuf->chunk_size); + dev_err(ir->l.dev, + "chunk_size is too big (%d)!\n", + rbuf->chunk_size); ret = -EINVAL; break; } @@ -964,8 +969,8 @@ static ssize_t read(struct file *filep, char __user *outbuf, size_t n, put_ir_rx(rx, false); set_current_state(TASK_RUNNING); - dev_dbg(ir->l.dev, "read result = %d (%s)\n", - ret, ret ? "Error" : "OK"); + dev_dbg(ir->l.dev, "read result = %d (%s)\n", ret, + ret ? "Error" : "OK"); return ret ? ret : written; } @@ -981,8 +986,9 @@ static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key) ret = get_key_data(data_block, code, key); if (ret == -EPROTO) { - dev_err(tx->ir->l.dev, "failed to get data for code %u, key %u -- check " - "lircd.conf entries\n", code, key); + dev_err(tx->ir->l.dev, + "failed to get data for code %u, key %u -- check lircd.conf entries\n", + code, key); return ret; } else if (ret != 0) return ret; @@ -1057,12 +1063,14 @@ static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key) ret = i2c_master_send(tx->c, buf, 1); if (ret == 1) break; - dev_dbg(tx->ir->l.dev, "NAK expected: i2c_master_send " - "failed with %d (try %d)\n", ret, i+1); + dev_dbg(tx->ir->l.dev, + "NAK expected: i2c_master_send failed with %d (try %d)\n", + ret, i+1); } if (ret != 1) { - dev_err(tx->ir->l.dev, "IR TX chip never got ready: last i2c_master_send " - "failed with %d\n", ret); + dev_err(tx->ir->l.dev, + "IR TX chip never got ready: last i2c_master_send failed with %d\n", + ret); return ret < 0 ? ret : -EFAULT; } @@ -1074,7 +1082,7 @@ static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key) } if (buf[0] != 0x80) { dev_err(tx->ir->l.dev, "unexpected IR TX response #2: %02x\n", - buf[0]); + buf[0]); return -EFAULT; } @@ -1165,12 +1173,12 @@ static ssize_t write(struct file *filep, const char __user *buf, size_t n, */ if (ret != 0) { /* Looks like the chip crashed, reset it */ - dev_err(tx->ir->l.dev, "sending to the IR transmitter chip " - "failed, trying reset\n"); + dev_err(tx->ir->l.dev, + "sending to the IR transmitter chip failed, trying reset\n"); if (failures >= 3) { - dev_err(tx->ir->l.dev, "unable to send to the IR chip " - "after 3 resets, giving up\n"); + dev_err(tx->ir->l.dev, + "unable to send to the IR chip after 3 resets, giving up\n"); mutex_unlock(&ir->ir_lock); mutex_unlock(&tx->client_lock); put_ir_tx(tx, false); @@ -1226,7 +1234,7 @@ static unsigned int poll(struct file *filep, poll_table *wait) ret = lirc_buffer_empty(rbuf) ? 0 : (POLLIN|POLLRDNORM); dev_dbg(ir->l.dev, "poll result = %s\n", - ret ? "POLLIN|POLLRDNORM" : "none"); + ret ? "POLLIN|POLLRDNORM" : "none"); return ret; } @@ -1333,7 +1341,8 @@ static int close(struct inode *node, struct file *filep) struct IR *ir = filep->private_data; if (ir == NULL) { - dev_err(ir->l.dev, "close: no private_data attached to the file!\n"); + dev_err(ir->l.dev, + "close: no private_data attached to the file!\n"); return -ENODEV; } @@ -1540,8 +1549,9 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) /* Proceed only if the Rx client is also ready or not needed */ if (rx == NULL && !tx_only) { - dev_info(tx->ir->l.dev, "probe of IR Tx on %s (i2c-%d) done. Waiting" - " on IR Rx.\n", adap->name, adap->nr); + dev_info(tx->ir->l.dev, + "probe of IR Tx on %s (i2c-%d) done. Waiting on IR Rx.\n", + adap->name, adap->nr); goto out_ok; } } else { @@ -1579,8 +1589,9 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) "zilog-rx-i2c-%d", adap->nr); if (IS_ERR(rx->task)) { ret = PTR_ERR(rx->task); - dev_err(tx->ir->l.dev, "%s: could not start IR Rx polling thread" - "\n", __func__); + dev_err(tx->ir->l.dev, + "%s: could not start IR Rx polling thread\n", + __func__); /* Failed kthread, so put back the ir ref */ put_ir_device(ir, true); /* Failure exit, so put back rx ref from i2c_client */ @@ -1592,8 +1603,8 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) /* Proceed only if the Tx client is also ready */ if (tx == NULL) { - pr_info("probe of IR Rx on %s (i2c-%d) done. Waiting" - " on IR Tx.\n", adap->name, adap->nr); + pr_info("probe of IR Rx on %s (i2c-%d) done. Waiting on IR Tx.\n", + adap->name, adap->nr); goto out_ok; } } @@ -1602,13 +1613,15 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir->l.minor = minor; /* module option: user requested minor number */ ir->l.minor = lirc_register_driver(&ir->l); if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) { - dev_err(tx->ir->l.dev, "%s: \"minor\" must be between 0 and %d (%d)!\n", - __func__, MAX_IRCTL_DEVICES-1, ir->l.minor); + dev_err(tx->ir->l.dev, + "%s: \"minor\" must be between 0 and %d (%d)!\n", + __func__, MAX_IRCTL_DEVICES-1, ir->l.minor); ret = -EBADRQC; goto out_put_xx; } - dev_info(ir->l.dev, "IR unit on %s (i2c-%d) registered as lirc%d and ready\n", - adap->name, adap->nr, ir->l.minor); + dev_info(ir->l.dev, + "IR unit on %s (i2c-%d) registered as lirc%d and ready\n", + adap->name, adap->nr, ir->l.minor); out_ok: if (rx != NULL) @@ -1616,8 +1629,9 @@ out_ok: if (tx != NULL) put_ir_tx(tx, true); put_ir_device(ir, true); - dev_info(ir->l.dev, "probe of IR %s on %s (i2c-%d) done\n", - tx_probe ? "Tx" : "Rx", adap->name, adap->nr); + dev_info(ir->l.dev, + "probe of IR %s on %s (i2c-%d) done\n", + tx_probe ? "Tx" : "Rx", adap->name, adap->nr); mutex_unlock(&ir_devices_lock); return 0; @@ -1629,9 +1643,9 @@ out_put_xx: out_put_ir: put_ir_device(ir, true); out_no_ir: - dev_err(&client->dev, "%s: probing IR %s on %s (i2c-%d) failed with %d\n", - __func__, tx_probe ? "Tx" : "Rx", adap->name, adap->nr, - ret); + dev_err(&client->dev, + "%s: probing IR %s on %s (i2c-%d) failed with %d\n", + __func__, tx_probe ? "Tx" : "Rx", adap->name, adap->nr, ret); mutex_unlock(&ir_devices_lock); return ret; } diff --git a/drivers/staging/media/mn88472/mn88472.c b/drivers/staging/media/mn88472/mn88472.c index 52de8f85d36c..6eebe564e557 100644 --- a/drivers/staging/media/mn88472/mn88472.c +++ b/drivers/staging/media/mn88472/mn88472.c @@ -30,6 +30,7 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) struct dtv_frontend_properties *c = &fe->dtv_property_cache; int ret, i; u32 if_frequency = 0; + u64 tmp; u8 delivery_system_val, if_val[3], bw_val[7], bw_val2; dev_dbg(&client->dev, @@ -57,36 +58,22 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) goto err; } - switch (c->delivery_system) { - case SYS_DVBT: - case SYS_DVBT2: - if (c->bandwidth_hz <= 6000000) { - /* IF 3570000 Hz, BW 6000000 Hz */ - memcpy(if_val, "\x2c\x94\xdb", 3); - memcpy(bw_val, "\xbf\x55\x55\x15\x6b\x15\x6b", 7); - bw_val2 = 0x02; - } else if (c->bandwidth_hz <= 7000000) { - /* IF 4570000 Hz, BW 7000000 Hz */ - memcpy(if_val, "\x39\x11\xbc", 3); - memcpy(bw_val, "\xa4\x00\x00\x0f\x2c\x0f\x2c", 7); - bw_val2 = 0x01; - } else if (c->bandwidth_hz <= 8000000) { - /* IF 4570000 Hz, BW 8000000 Hz */ - memcpy(if_val, "\x39\x11\xbc", 3); - memcpy(bw_val, "\x8f\x80\x00\x08\xee\x08\xee", 7); - bw_val2 = 0x00; - } else { - ret = -EINVAL; - goto err; - } - break; - case SYS_DVBC_ANNEX_A: - /* IF 5070000 Hz, BW 8000000 Hz */ - memcpy(if_val, "\x3f\x50\x2c", 3); + if (c->bandwidth_hz <= 5000000) { + memcpy(bw_val, "\xe5\x99\x9a\x1b\xa9\x1b\xa9", 7); + bw_val2 = 0x03; + } else if (c->bandwidth_hz <= 6000000) { + /* IF 3570000 Hz, BW 6000000 Hz */ + memcpy(bw_val, "\xbf\x55\x55\x15\x6b\x15\x6b", 7); + bw_val2 = 0x02; + } else if (c->bandwidth_hz <= 7000000) { + /* IF 4570000 Hz, BW 7000000 Hz */ + memcpy(bw_val, "\xa4\x00\x00\x0f\x2c\x0f\x2c", 7); + bw_val2 = 0x01; + } else if (c->bandwidth_hz <= 8000000) { + /* IF 4570000 Hz, BW 8000000 Hz */ memcpy(bw_val, "\x8f\x80\x00\x08\xee\x08\xee", 7); bw_val2 = 0x00; - break; - default: + } else { ret = -EINVAL; goto err; } @@ -106,17 +93,12 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) dev_dbg(&client->dev, "get_if_frequency=%d\n", if_frequency); } - switch (if_frequency) { - case 3570000: - case 4570000: - case 5070000: - break; - default: - dev_err(&client->dev, "IF frequency %d not supported\n", - if_frequency); - ret = -EINVAL; - goto err; - } + /* Calculate IF registers ( (1<<24)*IF / Xtal ) */ + tmp = div_u64(if_frequency * (u64)(1<<24) + (dev->xtal / 2), + dev->xtal); + if_val[0] = ((tmp >> 16) & 0xff); + if_val[1] = ((tmp >> 8) & 0xff); + if_val[2] = ((tmp >> 0) & 0xff); ret = regmap_write(dev->regmap[2], 0xfb, 0x13); ret = regmap_write(dev->regmap[2], 0xef, 0x13); @@ -198,6 +180,8 @@ static int mn88472_set_frontend(struct dvb_frontend *fe) ret = regmap_write(dev->regmap[0], 0xae, 0x00); ret = regmap_write(dev->regmap[2], 0x08, 0x1d); ret = regmap_write(dev->regmap[0], 0xd9, 0xe3); + + /* Reset demod */ ret = regmap_write(dev->regmap[2], 0xf8, 0x9f); if (ret) goto err; @@ -411,6 +395,7 @@ static int mn88472_probe(struct i2c_client *client, } dev->i2c_wr_max = config->i2c_wr_max; + dev->xtal = config->xtal; dev->client[0] = client; dev->regmap[0] = regmap_init_i2c(dev->client[0], ®map_config); if (IS_ERR(dev->regmap[0])) { diff --git a/drivers/staging/media/mn88472/mn88472_priv.h b/drivers/staging/media/mn88472/mn88472_priv.h index 1095949f040d..b12b731e2d4e 100644 --- a/drivers/staging/media/mn88472/mn88472_priv.h +++ b/drivers/staging/media/mn88472/mn88472_priv.h @@ -31,6 +31,7 @@ struct mn88472_dev { u16 i2c_wr_max; fe_delivery_system_t delivery_system; bool warm; /* FW running */ + u32 xtal; }; #endif diff --git a/drivers/staging/media/omap4iss/iss.c b/drivers/staging/media/omap4iss/iss.c index cc1dfadd91eb..44b81a2c8b6f 100644 --- a/drivers/staging/media/omap4iss/iss.c +++ b/drivers/staging/media/omap4iss/iss.c @@ -560,41 +560,28 @@ static int iss_pipeline_link_notify(struct media_link *link, u32 flags, */ /* - * iss_pipeline_enable - Enable streaming on a pipeline + * iss_pipeline_disable - Disable streaming on a pipeline * @pipe: ISS pipeline - * @mode: Stream mode (single shot or continuous) + * @until: entity at which to stop pipeline walk * - * Walk the entities chain starting at the pipeline output video node and start - * all modules in the chain in the given mode. + * Walk the entities chain starting at the pipeline output video node and stop + * all modules in the chain. Wait synchronously for the modules to be stopped if + * necessary. * - * Return 0 if successful, or the return value of the failed video::s_stream - * operation otherwise. + * If the until argument isn't NULL, stop the pipeline walk when reaching the + * until entity. This is used to disable a partially started pipeline due to a + * subdev start error. */ -static int iss_pipeline_enable(struct iss_pipeline *pipe, - enum iss_pipeline_stream_state mode) +static int iss_pipeline_disable(struct iss_pipeline *pipe, + struct media_entity *until) { struct iss_device *iss = pipe->output->iss; struct media_entity *entity; struct media_pad *pad; struct v4l2_subdev *subdev; - unsigned long flags; + int failure = 0; int ret; - /* If one of the entities in the pipeline has crashed it will not work - * properly. Refuse to start streaming in that case. This check must be - * performed before the loop below to avoid starting entities if the - * pipeline won't start anyway (those entities would then likely fail to - * stop, making the problem worse). - */ - if (pipe->entities & iss->crashed) - return -EIO; - - spin_lock_irqsave(&pipe->lock, flags); - pipe->state &= ~(ISS_PIPELINE_IDLE_INPUT | ISS_PIPELINE_IDLE_OUTPUT); - spin_unlock_irqrestore(&pipe->lock, flags); - - pipe->do_propagation = false; - entity = &pipe->output->video.entity; while (1) { pad = &entity->pads[0]; @@ -607,33 +594,62 @@ static int iss_pipeline_enable(struct iss_pipeline *pipe, break; entity = pad->entity; - subdev = media_entity_to_v4l2_subdev(entity); + if (entity == until) + break; - ret = v4l2_subdev_call(subdev, video, s_stream, mode); - if (ret < 0 && ret != -ENOIOCTLCMD) - return ret; + subdev = media_entity_to_v4l2_subdev(entity); + ret = v4l2_subdev_call(subdev, video, s_stream, 0); + if (ret < 0) { + dev_dbg(iss->dev, "%s: module stop timeout.\n", + subdev->name); + /* If the entity failed to stopped, assume it has + * crashed. Mark it as such, the ISS will be reset when + * applications will release it. + */ + iss->crashed |= 1U << subdev->entity.id; + failure = -ETIMEDOUT; + } } - iss_print_status(pipe->output->iss); - return 0; + + return failure; } /* - * iss_pipeline_disable - Disable streaming on a pipeline + * iss_pipeline_enable - Enable streaming on a pipeline * @pipe: ISS pipeline + * @mode: Stream mode (single shot or continuous) * - * Walk the entities chain starting at the pipeline output video node and stop - * all modules in the chain. Wait synchronously for the modules to be stopped if - * necessary. + * Walk the entities chain starting at the pipeline output video node and start + * all modules in the chain in the given mode. + * + * Return 0 if successful, or the return value of the failed video::s_stream + * operation otherwise. */ -static int iss_pipeline_disable(struct iss_pipeline *pipe) +static int iss_pipeline_enable(struct iss_pipeline *pipe, + enum iss_pipeline_stream_state mode) { struct iss_device *iss = pipe->output->iss; struct media_entity *entity; struct media_pad *pad; struct v4l2_subdev *subdev; - int failure = 0; + unsigned long flags; int ret; + /* If one of the entities in the pipeline has crashed it will not work + * properly. Refuse to start streaming in that case. This check must be + * performed before the loop below to avoid starting entities if the + * pipeline won't start anyway (those entities would then likely fail to + * stop, making the problem worse). + */ + if (pipe->entities & iss->crashed) + return -EIO; + + spin_lock_irqsave(&pipe->lock, flags); + pipe->state &= ~(ISS_PIPELINE_IDLE_INPUT | ISS_PIPELINE_IDLE_OUTPUT); + spin_unlock_irqrestore(&pipe->lock, flags); + + pipe->do_propagation = false; + entity = &pipe->output->video.entity; while (1) { pad = &entity->pads[0]; @@ -648,20 +664,19 @@ static int iss_pipeline_disable(struct iss_pipeline *pipe) entity = pad->entity; subdev = media_entity_to_v4l2_subdev(entity); - ret = v4l2_subdev_call(subdev, video, s_stream, 0); - if (ret < 0) { - dev_dbg(iss->dev, "%s: module stop timeout.\n", - subdev->name); - /* If the entity failed to stopped, assume it has - * crashed. Mark it as such, the ISS will be reset when - * applications will release it. - */ - iss->crashed |= 1U << subdev->entity.id; - failure = -ETIMEDOUT; + ret = v4l2_subdev_call(subdev, video, s_stream, mode); + if (ret < 0 && ret != -ENOIOCTLCMD) { + iss_pipeline_disable(pipe, entity); + return ret; } + + if (subdev == &iss->csi2a.subdev || + subdev == &iss->csi2b.subdev) + pipe->do_propagation = true; } - return failure; + iss_print_status(pipe->output->iss); + return 0; } /* @@ -682,7 +697,7 @@ int omap4iss_pipeline_set_stream(struct iss_pipeline *pipe, int ret; if (state == ISS_PIPELINE_STREAM_STOPPED) - ret = iss_pipeline_disable(pipe); + ret = iss_pipeline_disable(pipe, NULL); else ret = iss_pipeline_enable(pipe, state); diff --git a/drivers/staging/media/omap4iss/iss_csi2.c b/drivers/staging/media/omap4iss/iss_csi2.c index 21971c675b8c..2d96fb3eca53 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.c +++ b/drivers/staging/media/omap4iss/iss_csi2.c @@ -319,6 +319,8 @@ static void csi2_ctx_config(struct iss_csi2_device *csi2, { u32 reg = 0; + ctx->frame = 0; + /* Set up CSI2_CTx_CTRL1 */ if (ctx->eof_enabled) reg = CSI2_CTX_CTRL1_EOF_EN; @@ -396,21 +398,18 @@ static void csi2_timing_config(struct iss_csi2_device *csi2, */ static void csi2_irq_ctx_set(struct iss_csi2_device *csi2, int enable) { - u32 reg = CSI2_CTX_IRQ_FE; + const u32 mask = CSI2_CTX_IRQ_FE | CSI2_CTX_IRQ_FS; int i; - if (csi2->use_fs_irq) - reg |= CSI2_CTX_IRQ_FS; - for (i = 0; i < 8; i++) { iss_reg_write(csi2->iss, csi2->regs1, CSI2_CTX_IRQSTATUS(i), - reg); + mask); if (enable) iss_reg_set(csi2->iss, csi2->regs1, - CSI2_CTX_IRQENABLE(i), reg); + CSI2_CTX_IRQENABLE(i), mask); else iss_reg_clr(csi2->iss, csi2->regs1, - CSI2_CTX_IRQENABLE(i), reg); + CSI2_CTX_IRQENABLE(i), mask); } } @@ -679,8 +678,34 @@ static void csi2_isr_ctx(struct iss_csi2_device *csi2, if (status & CSI2_CTX_IRQ_FS) { struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity); - if (pipe->do_propagation) + u16 frame; + u16 delta; + + frame = iss_reg_read(csi2->iss, csi2->regs1, + CSI2_CTX_CTRL2(ctx->ctxnum)) + >> CSI2_CTX_CTRL2_FRAME_SHIFT; + + if (frame == 0) { + /* A zero value means that the counter isn't implemented + * by the source. Increment the frame number in software + * in that case. + */ atomic_inc(&pipe->frame_number); + } else { + /* Extend the 16 bit frame number to 32 bits by + * computing the delta between two consecutive CSI2 + * frame numbers and adding it to the software frame + * number. The hardware counter starts at 1 and wraps + * from 0xffff to 1 without going through 0, so subtract + * 1 when the counter wraps. + */ + delta = frame - ctx->frame; + if (frame < ctx->frame) + delta--; + ctx->frame = frame; + + atomic_add(delta, &pipe->frame_number); + } } if (!(status & CSI2_CTX_IRQ_FE)) @@ -1039,7 +1064,6 @@ static int csi2_set_stream(struct v4l2_subdev *sd, int enable) { struct iss_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct iss_device *iss = csi2->iss; - struct iss_pipeline *pipe = to_iss_pipeline(&csi2->subdev.entity); struct iss_video *video_out = &csi2->video_out; int ret = 0; @@ -1058,7 +1082,6 @@ static int csi2_set_stream(struct v4l2_subdev *sd, int enable) if (omap4iss_csiphy_acquire(csi2->phy) < 0) return -ENODEV; - csi2->use_fs_irq = pipe->do_propagation; csi2_configure(csi2); csi2_print_status(csi2); diff --git a/drivers/staging/media/omap4iss/iss_csi2.h b/drivers/staging/media/omap4iss/iss_csi2.h index 971aa7b08013..3b37978a3bdf 100644 --- a/drivers/staging/media/omap4iss/iss_csi2.h +++ b/drivers/staging/media/omap4iss/iss_csi2.h @@ -82,6 +82,7 @@ struct iss_csi2_ctx_cfg { u8 virtual_id; u16 format_id; /* as in CSI2_CTx_CTRL2[9:0] */ u8 dpcm_predictor; /* 1: simple, 0: advanced */ + u16 frame; /* Fields in CSI2_CTx_CTRL1/3 - Shadowed */ u16 alpha; @@ -137,7 +138,6 @@ struct iss_csi2_device { u32 output; /* output to IPIPEIF, memory or both? */ bool dpcm_decompress; unsigned int frame_skip; - bool use_fs_irq; struct iss_csiphy *phy; struct iss_csi2_ctx_cfg contexts[ISS_CSI2_MAX_CTX_NUM + 1]; diff --git a/drivers/staging/media/omap4iss/iss_ipipeif.c b/drivers/staging/media/omap4iss/iss_ipipeif.c index 32a748398ced..3943fae699ee 100644 --- a/drivers/staging/media/omap4iss/iss_ipipeif.c +++ b/drivers/staging/media/omap4iss/iss_ipipeif.c @@ -242,23 +242,6 @@ static void ipipeif_isr_buffer(struct iss_ipipeif_device *ipipeif) } /* - * ipipeif_isif0_isr - Handle ISIF0 event - * @ipipeif: Pointer to ISP IPIPEIF device. - * - * Executes LSC deferred enablement before next frame starts. - */ -static void ipipeif_isif0_isr(struct iss_ipipeif_device *ipipeif) -{ - struct iss_pipeline *pipe = - to_iss_pipeline(&ipipeif->subdev.entity); - if (pipe->do_propagation) - atomic_inc(&pipe->frame_number); - - if (ipipeif->output & IPIPEIF_OUTPUT_MEMORY) - ipipeif_isr_buffer(ipipeif); -} - -/* * omap4iss_ipipeif_isr - Configure ipipeif during interframe time. * @ipipeif: Pointer to ISP IPIPEIF device. * @events: IPIPEIF events @@ -269,8 +252,9 @@ void omap4iss_ipipeif_isr(struct iss_ipipeif_device *ipipeif, u32 events) &ipipeif->stopping)) return; - if (events & ISP5_IRQ_ISIF_INT(0)) - ipipeif_isif0_isr(ipipeif); + if ((events & ISP5_IRQ_ISIF_INT(0)) && + (ipipeif->output & IPIPEIF_OUTPUT_MEMORY)) + ipipeif_isr_buffer(ipipeif); } /* ----------------------------------------------------------------------------- diff --git a/drivers/staging/media/omap4iss/iss_regs.h b/drivers/staging/media/omap4iss/iss_regs.h index efd0291a86f7..d2b6b6ae9174 100644 --- a/drivers/staging/media/omap4iss/iss_regs.h +++ b/drivers/staging/media/omap4iss/iss_regs.h @@ -215,6 +215,8 @@ #define CSI2_CTX_CTRL1_CTX_EN (1 << 0) #define CSI2_CTX_CTRL2(i) (0x74 + (0x20 * i)) +#define CSI2_CTX_CTRL2_FRAME_MASK (0xffff << 16) +#define CSI2_CTX_CTRL2_FRAME_SHIFT 16 #define CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT 13 #define CSI2_CTX_CTRL2_USER_DEF_MAP_MASK \ (0x3 << CSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT) diff --git a/drivers/staging/media/omap4iss/iss_resizer.c b/drivers/staging/media/omap4iss/iss_resizer.c index 88522a8cdf56..3ab972818f1b 100644 --- a/drivers/staging/media/omap4iss/iss_resizer.c +++ b/drivers/staging/media/omap4iss/iss_resizer.c @@ -283,22 +283,6 @@ static void resizer_isr_buffer(struct iss_resizer_device *resizer) } /* - * resizer_isif0_isr - Handle ISIF0 event - * @resizer: Pointer to ISP RESIZER device. - * - * Executes LSC deferred enablement before next frame starts. - */ -static void resizer_int_dma_isr(struct iss_resizer_device *resizer) -{ - struct iss_pipeline *pipe = - to_iss_pipeline(&resizer->subdev.entity); - if (pipe->do_propagation) - atomic_inc(&pipe->frame_number); - - resizer_isr_buffer(resizer); -} - -/* * omap4iss_resizer_isr - Configure resizer during interframe time. * @resizer: Pointer to ISP RESIZER device. * @events: RESIZER events @@ -322,7 +306,7 @@ void omap4iss_resizer_isr(struct iss_resizer_device *resizer, u32 events) return; if (events & ISP5_IRQ_RSZ_INT_DMA) - resizer_int_dma_isr(resizer); + resizer_isr_buffer(resizer); } /* ----------------------------------------------------------------------------- diff --git a/drivers/staging/media/omap4iss/iss_video.c b/drivers/staging/media/omap4iss/iss_video.c index cdee5966cbca..69550445a341 100644 --- a/drivers/staging/media/omap4iss/iss_video.c +++ b/drivers/staging/media/omap4iss/iss_video.c @@ -25,9 +25,6 @@ #include "iss_video.h" #include "iss.h" -static unsigned debug; -module_param(debug, uint, 0644); -MODULE_PARM_DESC(debug, "activates debug info"); /* ----------------------------------------------------------------------------- * Helper functions @@ -773,6 +770,14 @@ iss_video_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) } static int +iss_video_expbuf(struct file *file, void *fh, struct v4l2_exportbuffer *e) +{ + struct iss_video_fh *vfh = to_iss_video_fh(fh); + + return vb2_expbuf(&vfh->queue, e); +} + +static int iss_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) { struct iss_video_fh *vfh = to_iss_video_fh(fh); @@ -1021,6 +1026,7 @@ static const struct v4l2_ioctl_ops iss_video_ioctl_ops = { .vidioc_reqbufs = iss_video_reqbufs, .vidioc_querybuf = iss_video_querybuf, .vidioc_qbuf = iss_video_qbuf, + .vidioc_expbuf = iss_video_expbuf, .vidioc_dqbuf = iss_video_dqbuf, .vidioc_streamon = iss_video_streamon, .vidioc_streamoff = iss_video_streamoff, @@ -1044,8 +1050,6 @@ static int iss_video_open(struct file *file) if (handle == NULL) return -ENOMEM; - video->video.debug = debug; - v4l2_fh_init(&handle->vfh, &video->video); v4l2_fh_add(&handle->vfh); @@ -1071,7 +1075,7 @@ static int iss_video_open(struct file *file) q = &handle->queue; q->type = video->type; - q->io_modes = VB2_MMAP; + q->io_modes = VB2_MMAP | VB2_DMABUF; q->drv_priv = handle; q->ops = &iss_video_vb2ops; q->mem_ops = &vb2_dma_contig_memops; diff --git a/drivers/staging/media/parport/Kconfig b/drivers/staging/media/parport/Kconfig deleted file mode 100644 index 15974efdba1d..000000000000 --- a/drivers/staging/media/parport/Kconfig +++ /dev/null @@ -1,69 +0,0 @@ -menuconfig MEDIA_PARPORT_SUPPORT - bool "ISA and parallel port devices" - depends on (ISA || PARPORT) && MEDIA_CAMERA_SUPPORT - help - Enables drivers for ISA and parallel port bus. If you - need media drivers using those legacy buses, say Y. - -if MEDIA_PARPORT_SUPPORT -config VIDEO_BWQCAM - tristate "Quickcam BW Video For Linux (Deprecated)" - depends on PARPORT && VIDEO_V4L2 - select VIDEOBUF2_VMALLOC - help - Say Y have if you the black and white version of the QuickCam - camera. See the next option for the color version. - - This driver is deprecated and will be removed soon. If you have - hardware for this and you want to work on this driver, then contact - the linux-media mailinglist. - - To compile this driver as a module, choose M here: the - module will be called bw-qcam. - -config VIDEO_CQCAM - tristate "QuickCam Colour Video For Linux (Deprecated)" - depends on PARPORT && VIDEO_V4L2 - help - This is the video4linux driver for the colour version of the - Connectix QuickCam. If you have one of these cameras, say Y here, - otherwise say N. This driver does not work with the original - monochrome QuickCam, QuickCam VC or QuickClip. It is also available - as a module (c-qcam). - Read <file:Documentation/video4linux/CQcam.txt> for more information. - - This driver is deprecated and will be removed soon. If you have - hardware for this and you want to work on this driver, then contact - the linux-media mailinglist. - -config VIDEO_PMS - tristate "Mediavision Pro Movie Studio Video For Linux (Deprecated)" - depends on ISA && VIDEO_V4L2 - help - Say Y if you have the ISA Mediavision Pro Movie Studio - capture card. - - This driver is deprecated and will be removed soon. If you have - hardware for this and you want to work on this driver, then contact - the linux-media mailinglist. - - To compile this driver as a module, choose M here: the - module will be called pms. - -config VIDEO_W9966 - tristate "W9966CF Webcam (FlyCam Supra and others) Video For Linux (Deprecated)" - depends on PARPORT_1284 && PARPORT && VIDEO_V4L2 - help - Video4linux driver for Winbond's w9966 based Webcams. - Currently tested with the LifeView FlyCam Supra. - If you have one of these cameras, say Y here - otherwise say N. - This driver is also available as a module (w9966). - - Check out <file:Documentation/video4linux/w9966.txt> for more - information. - - This driver is deprecated and will be removed soon. If you have - hardware for this and you want to work on this driver, then contact - the linux-media mailinglist. -endif diff --git a/drivers/staging/media/parport/Makefile b/drivers/staging/media/parport/Makefile deleted file mode 100644 index 4eea06d7af5b..000000000000 --- a/drivers/staging/media/parport/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -obj-$(CONFIG_VIDEO_CQCAM) += c-qcam.o -obj-$(CONFIG_VIDEO_BWQCAM) += bw-qcam.o -obj-$(CONFIG_VIDEO_W9966) += w9966.o -obj-$(CONFIG_VIDEO_PMS) += pms.o diff --git a/drivers/staging/media/parport/bw-qcam.c b/drivers/staging/media/parport/bw-qcam.c deleted file mode 100644 index 67b9da1dc43f..000000000000 --- a/drivers/staging/media/parport/bw-qcam.c +++ /dev/null @@ -1,1177 +0,0 @@ -/* - * QuickCam Driver For Video4Linux. - * - * Video4Linux conversion work by Alan Cox. - * Parport compatibility by Phil Blundell. - * Busy loop avoidance by Mark Cooke. - * - * Module parameters: - * - * maxpoll=<1 - 5000> - * - * When polling the QuickCam for a response, busy-wait for a - * maximum of this many loops. The default of 250 gives little - * impact on interactive response. - * - * NOTE: If this parameter is set too high, the processor - * will busy wait until this loop times out, and then - * slowly poll for a further 5 seconds before failing - * the transaction. You have been warned. - * - * yieldlines=<1 - 250> - * - * When acquiring a frame from the camera, the data gathering - * loop will yield back to the scheduler after completing - * this many lines. The default of 4 provides a trade-off - * between increased frame acquisition time and impact on - * interactive response. - */ - -/* qcam-lib.c -- Library for programming with the Connectix QuickCam. - * See the included documentation for usage instructions and details - * of the protocol involved. */ - - -/* Version 0.5, August 4, 1996 */ -/* Version 0.7, August 27, 1996 */ -/* Version 0.9, November 17, 1996 */ - - -/****************************************************************** - -Copyright (C) 1996 by Scott Laird - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL SCOTT LAIRD BE LIABLE FOR ANY CLAIM, DAMAGES OR -OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. - -******************************************************************/ - -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/parport.h> -#include <linux/sched.h> -#include <linux/videodev2.h> -#include <linux/mutex.h> -#include <asm/uaccess.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-device.h> -#include <media/v4l2-fh.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-event.h> -#include <media/videobuf2-vmalloc.h> - -/* One from column A... */ -#define QC_NOTSET 0 -#define QC_UNIDIR 1 -#define QC_BIDIR 2 -#define QC_SERIAL 3 - -/* ... and one from column B */ -#define QC_ANY 0x00 -#define QC_FORCE_UNIDIR 0x10 -#define QC_FORCE_BIDIR 0x20 -#define QC_FORCE_SERIAL 0x30 -/* in the port_mode member */ - -#define QC_MODE_MASK 0x07 -#define QC_FORCE_MASK 0x70 - -#define MAX_HEIGHT 243 -#define MAX_WIDTH 336 - -/* Bit fields for status flags */ -#define QC_PARAM_CHANGE 0x01 /* Camera status change has occurred */ - -struct qcam { - struct v4l2_device v4l2_dev; - struct video_device vdev; - struct v4l2_ctrl_handler hdl; - struct vb2_queue vb_vidq; - struct pardevice *pdev; - struct parport *pport; - struct mutex lock; - struct mutex queue_lock; - int width, height; - int bpp; - int mode; - int contrast, brightness, whitebal; - int port_mode; - int transfer_scale; - int top, left; - int status; - unsigned int saved_bits; - unsigned long in_use; -}; - -static unsigned int maxpoll = 250; /* Maximum busy-loop count for qcam I/O */ -static unsigned int yieldlines = 4; /* Yield after this many during capture */ -static int video_nr = -1; -static unsigned int force_init; /* Whether to probe aggressively */ - -module_param(maxpoll, int, 0); -module_param(yieldlines, int, 0); -module_param(video_nr, int, 0); - -/* Set force_init=1 to avoid detection by polling status register and - * immediately attempt to initialize qcam */ -module_param(force_init, int, 0); - -#define MAX_CAMS 4 -static struct qcam *qcams[MAX_CAMS]; -static unsigned int num_cams; - -static inline int read_lpstatus(struct qcam *q) -{ - return parport_read_status(q->pport); -} - -static inline int read_lpdata(struct qcam *q) -{ - return parport_read_data(q->pport); -} - -static inline void write_lpdata(struct qcam *q, int d) -{ - parport_write_data(q->pport, d); -} - -static void write_lpcontrol(struct qcam *q, int d) -{ - if (d & 0x20) { - /* Set bidirectional mode to reverse (data in) */ - parport_data_reverse(q->pport); - } else { - /* Set bidirectional mode to forward (data out) */ - parport_data_forward(q->pport); - } - - /* Now issue the regular port command, but strip out the - * direction flag */ - d &= ~0x20; - parport_write_control(q->pport, d); -} - - -/* qc_waithand busy-waits for a handshake signal from the QuickCam. - * Almost all communication with the camera requires handshaking. */ - -static int qc_waithand(struct qcam *q, int val) -{ - int status; - int runs = 0; - - if (val) { - while (!((status = read_lpstatus(q)) & 8)) { - /* 1000 is enough spins on the I/O for all normal - cases, at that point we start to poll slowly - until the camera wakes up. However, we are - busy blocked until the camera responds, so - setting it lower is much better for interactive - response. */ - - if (runs++ > maxpoll) - msleep_interruptible(5); - if (runs > (maxpoll + 1000)) /* 5 seconds */ - return -1; - } - } else { - while (((status = read_lpstatus(q)) & 8)) { - /* 1000 is enough spins on the I/O for all normal - cases, at that point we start to poll slowly - until the camera wakes up. However, we are - busy blocked until the camera responds, so - setting it lower is much better for interactive - response. */ - - if (runs++ > maxpoll) - msleep_interruptible(5); - if (runs++ > (maxpoll + 1000)) /* 5 seconds */ - return -1; - } - } - - return status; -} - -/* Waithand2 is used when the qcam is in bidirectional mode, and the - * handshaking signal is CamRdy2 (bit 0 of data reg) instead of CamRdy1 - * (bit 3 of status register). It also returns the last value read, - * since this data is useful. */ - -static unsigned int qc_waithand2(struct qcam *q, int val) -{ - unsigned int status; - int runs = 0; - - do { - status = read_lpdata(q); - /* 1000 is enough spins on the I/O for all normal - cases, at that point we start to poll slowly - until the camera wakes up. However, we are - busy blocked until the camera responds, so - setting it lower is much better for interactive - response. */ - - if (runs++ > maxpoll) - msleep_interruptible(5); - if (runs++ > (maxpoll + 1000)) /* 5 seconds */ - return 0; - } while ((status & 1) != val); - - return status; -} - -/* qc_command is probably a bit of a misnomer -- it's used to send - * bytes *to* the camera. Generally, these bytes are either commands - * or arguments to commands, so the name fits, but it still bugs me a - * bit. See the documentation for a list of commands. */ - -static int qc_command(struct qcam *q, int command) -{ - int n1, n2; - int cmd; - - write_lpdata(q, command); - write_lpcontrol(q, 6); - - n1 = qc_waithand(q, 1); - - write_lpcontrol(q, 0xe); - n2 = qc_waithand(q, 0); - - cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4); - return cmd; -} - -static int qc_readparam(struct qcam *q) -{ - int n1, n2; - int cmd; - - write_lpcontrol(q, 6); - n1 = qc_waithand(q, 1); - - write_lpcontrol(q, 0xe); - n2 = qc_waithand(q, 0); - - cmd = (n1 & 0xf0) | ((n2 & 0xf0) >> 4); - return cmd; -} - - -/* Try to detect a QuickCam. It appears to flash the upper 4 bits of - the status register at 5-10 Hz. This is only used in the autoprobe - code. Be aware that this isn't the way Connectix detects the - camera (they send a reset and try to handshake), but this should be - almost completely safe, while their method screws up my printer if - I plug it in before the camera. */ - -static int qc_detect(struct qcam *q) -{ - int reg, lastreg; - int count = 0; - int i; - - if (force_init) - return 1; - - lastreg = reg = read_lpstatus(q) & 0xf0; - - for (i = 0; i < 500; i++) { - reg = read_lpstatus(q) & 0xf0; - if (reg != lastreg) - count++; - lastreg = reg; - mdelay(2); - } - - -#if 0 - /* Force camera detection during testing. Sometimes the camera - won't be flashing these bits. Possibly unloading the module - in the middle of a grab? Or some timeout condition? - I've seen this parameter as low as 19 on my 450Mhz box - mpc */ - printk(KERN_DEBUG "Debugging: QCam detection counter <30-200 counts as detected>: %d\n", count); - return 1; -#endif - - /* Be (even more) liberal in what you accept... */ - - if (count > 20 && count < 400) { - return 1; /* found */ - } else { - printk(KERN_ERR "No Quickcam found on port %s\n", - q->pport->name); - printk(KERN_DEBUG "Quickcam detection counter: %u\n", count); - return 0; /* not found */ - } -} - -/* Decide which scan mode to use. There's no real requirement that - * the scanmode match the resolution in q->height and q-> width -- the - * camera takes the picture at the resolution specified in the - * "scanmode" and then returns the image at the resolution specified - * with the resolution commands. If the scan is bigger than the - * requested resolution, the upper-left hand corner of the scan is - * returned. If the scan is smaller, then the rest of the image - * returned contains garbage. */ - -static int qc_setscanmode(struct qcam *q) -{ - int old_mode = q->mode; - - switch (q->transfer_scale) { - case 1: - q->mode = 0; - break; - case 2: - q->mode = 4; - break; - case 4: - q->mode = 8; - break; - } - - switch (q->bpp) { - case 4: - break; - case 6: - q->mode += 2; - break; - } - - switch (q->port_mode & QC_MODE_MASK) { - case QC_BIDIR: - q->mode += 1; - break; - case QC_NOTSET: - case QC_UNIDIR: - break; - } - - if (q->mode != old_mode) - q->status |= QC_PARAM_CHANGE; - - return 0; -} - - -/* Reset the QuickCam. This uses the same sequence the Windows - * QuickPic program uses. Someone with a bi-directional port should - * check that bi-directional mode is detected right, and then - * implement bi-directional mode in qc_readbyte(). */ - -static void qc_reset(struct qcam *q) -{ - switch (q->port_mode & QC_FORCE_MASK) { - case QC_FORCE_UNIDIR: - q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR; - break; - - case QC_FORCE_BIDIR: - q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR; - break; - - case QC_ANY: - write_lpcontrol(q, 0x20); - write_lpdata(q, 0x75); - - if (read_lpdata(q) != 0x75) - q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_BIDIR; - else - q->port_mode = (q->port_mode & ~QC_MODE_MASK) | QC_UNIDIR; - break; - } - - write_lpcontrol(q, 0xb); - udelay(250); - write_lpcontrol(q, 0xe); - qc_setscanmode(q); /* in case port_mode changed */ -} - - - -/* Reset the QuickCam and program for brightness, contrast, - * white-balance, and resolution. */ - -static void qc_set(struct qcam *q) -{ - int val; - int val2; - - /* Set the brightness. Yes, this is repetitive, but it works. - * Shorter versions seem to fail subtly. Feel free to try :-). */ - /* I think the problem was in qc_command, not here -- bls */ - - qc_command(q, 0xb); - qc_command(q, q->brightness); - - val = q->height / q->transfer_scale; - qc_command(q, 0x11); - qc_command(q, val); - if ((q->port_mode & QC_MODE_MASK) == QC_UNIDIR && q->bpp == 6) { - /* The normal "transfers per line" calculation doesn't seem to work - as expected here (and yet it works fine in qc_scan). No idea - why this case is the odd man out. Fortunately, Laird's original - working version gives me a good way to guess at working values. - -- bls */ - val = q->width; - val2 = q->transfer_scale * 4; - } else { - val = q->width * q->bpp; - val2 = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) * - q->transfer_scale; - } - val = DIV_ROUND_UP(val, val2); - qc_command(q, 0x13); - qc_command(q, val); - - /* Setting top and left -- bls */ - qc_command(q, 0xd); - qc_command(q, q->top); - qc_command(q, 0xf); - qc_command(q, q->left / 2); - - qc_command(q, 0x19); - qc_command(q, q->contrast); - qc_command(q, 0x1f); - qc_command(q, q->whitebal); - - /* Clear flag that we must update the grabbing parameters on the camera - before we grab the next frame */ - q->status &= (~QC_PARAM_CHANGE); -} - -/* Qc_readbytes reads some bytes from the QC and puts them in - the supplied buffer. It returns the number of bytes read, - or -1 on error. */ - -static inline int qc_readbytes(struct qcam *q, char buffer[]) -{ - int ret = 1; - unsigned int hi, lo; - unsigned int hi2, lo2; - static int state; - - if (buffer == NULL) { - state = 0; - return 0; - } - - switch (q->port_mode & QC_MODE_MASK) { - case QC_BIDIR: /* Bi-directional Port */ - write_lpcontrol(q, 0x26); - lo = (qc_waithand2(q, 1) >> 1); - hi = (read_lpstatus(q) >> 3) & 0x1f; - write_lpcontrol(q, 0x2e); - lo2 = (qc_waithand2(q, 0) >> 1); - hi2 = (read_lpstatus(q) >> 3) & 0x1f; - switch (q->bpp) { - case 4: - buffer[0] = lo & 0xf; - buffer[1] = ((lo & 0x70) >> 4) | ((hi & 1) << 3); - buffer[2] = (hi & 0x1e) >> 1; - buffer[3] = lo2 & 0xf; - buffer[4] = ((lo2 & 0x70) >> 4) | ((hi2 & 1) << 3); - buffer[5] = (hi2 & 0x1e) >> 1; - ret = 6; - break; - case 6: - buffer[0] = lo & 0x3f; - buffer[1] = ((lo & 0x40) >> 6) | (hi << 1); - buffer[2] = lo2 & 0x3f; - buffer[3] = ((lo2 & 0x40) >> 6) | (hi2 << 1); - ret = 4; - break; - } - break; - - case QC_UNIDIR: /* Unidirectional Port */ - write_lpcontrol(q, 6); - lo = (qc_waithand(q, 1) & 0xf0) >> 4; - write_lpcontrol(q, 0xe); - hi = (qc_waithand(q, 0) & 0xf0) >> 4; - - switch (q->bpp) { - case 4: - buffer[0] = lo; - buffer[1] = hi; - ret = 2; - break; - case 6: - switch (state) { - case 0: - buffer[0] = (lo << 2) | ((hi & 0xc) >> 2); - q->saved_bits = (hi & 3) << 4; - state = 1; - ret = 1; - break; - case 1: - buffer[0] = lo | q->saved_bits; - q->saved_bits = hi << 2; - state = 2; - ret = 1; - break; - case 2: - buffer[0] = ((lo & 0xc) >> 2) | q->saved_bits; - buffer[1] = ((lo & 3) << 4) | hi; - state = 0; - ret = 2; - break; - } - break; - } - break; - } - return ret; -} - -/* requests a scan from the camera. It sends the correct instructions - * to the camera and then reads back the correct number of bytes. In - * previous versions of this routine the return structure contained - * the raw output from the camera, and there was a 'qc_convertscan' - * function that converted that to a useful format. In version 0.3 I - * rolled qc_convertscan into qc_scan and now I only return the - * converted scan. The format is just an one-dimensional array of - * characters, one for each pixel, with 0=black up to n=white, where - * n=2^(bit depth)-1. Ask me for more details if you don't understand - * this. */ - -static long qc_capture(struct qcam *q, u8 *buf, unsigned long len) -{ - int i, j, k, yield; - int bytes; - int linestotrans, transperline; - int divisor; - int pixels_per_line; - int pixels_read = 0; - int got = 0; - char buffer[6]; - int shift = 8 - q->bpp; - char invert; - - if (q->mode == -1) - return -ENXIO; - - qc_command(q, 0x7); - qc_command(q, q->mode); - - if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) { - write_lpcontrol(q, 0x2e); /* turn port around */ - write_lpcontrol(q, 0x26); - qc_waithand(q, 1); - write_lpcontrol(q, 0x2e); - qc_waithand(q, 0); - } - - /* strange -- should be 15:63 below, but 4bpp is odd */ - invert = (q->bpp == 4) ? 16 : 63; - - linestotrans = q->height / q->transfer_scale; - pixels_per_line = q->width / q->transfer_scale; - transperline = q->width * q->bpp; - divisor = (((q->port_mode & QC_MODE_MASK) == QC_BIDIR) ? 24 : 8) * - q->transfer_scale; - transperline = DIV_ROUND_UP(transperline, divisor); - - for (i = 0, yield = yieldlines; i < linestotrans; i++) { - for (pixels_read = j = 0; j < transperline; j++) { - bytes = qc_readbytes(q, buffer); - for (k = 0; k < bytes && (pixels_read + k) < pixels_per_line; k++) { - int o; - if (buffer[k] == 0 && invert == 16) { - /* 4bpp is odd (again) -- inverter is 16, not 15, but output - must be 0-15 -- bls */ - buffer[k] = 16; - } - o = i * pixels_per_line + pixels_read + k; - if (o < len) { - u8 ch = invert - buffer[k]; - got++; - buf[o] = ch << shift; - } - } - pixels_read += bytes; - } - qc_readbytes(q, NULL); /* reset state machine */ - - /* Grabbing an entire frame from the quickcam is a lengthy - process. We don't (usually) want to busy-block the - processor for the entire frame. yieldlines is a module - parameter. If we yield every line, the minimum frame - time will be 240 / 200 = 1.2 seconds. The compile-time - default is to yield every 4 lines. */ - if (i >= yield) { - msleep_interruptible(5); - yield = i + yieldlines; - } - } - - if ((q->port_mode & QC_MODE_MASK) == QC_BIDIR) { - write_lpcontrol(q, 2); - write_lpcontrol(q, 6); - udelay(3); - write_lpcontrol(q, 0xe); - } - if (got < len) - return got; - return len; -} - -/* ------------------------------------------------------------------ - Videobuf operations - ------------------------------------------------------------------*/ -static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], void *alloc_ctxs[]) -{ - struct qcam *dev = vb2_get_drv_priv(vq); - - if (0 == *nbuffers) - *nbuffers = 3; - *nplanes = 1; - mutex_lock(&dev->lock); - if (fmt) - sizes[0] = fmt->fmt.pix.width * fmt->fmt.pix.height; - else - sizes[0] = (dev->width / dev->transfer_scale) * - (dev->height / dev->transfer_scale); - mutex_unlock(&dev->lock); - return 0; -} - -static void buffer_queue(struct vb2_buffer *vb) -{ - vb2_buffer_done(vb, VB2_BUF_STATE_DONE); -} - -static void buffer_finish(struct vb2_buffer *vb) -{ - struct qcam *qcam = vb2_get_drv_priv(vb->vb2_queue); - void *vbuf = vb2_plane_vaddr(vb, 0); - int size = vb->vb2_queue->plane_sizes[0]; - int len; - - if (!vb2_is_streaming(vb->vb2_queue)) - return; - - mutex_lock(&qcam->lock); - parport_claim_or_block(qcam->pdev); - - qc_reset(qcam); - - /* Update the camera parameters if we need to */ - if (qcam->status & QC_PARAM_CHANGE) - qc_set(qcam); - - len = qc_capture(qcam, vbuf, size); - - parport_release(qcam->pdev); - mutex_unlock(&qcam->lock); - v4l2_get_timestamp(&vb->v4l2_buf.timestamp); - if (len != size) - vb->state = VB2_BUF_STATE_ERROR; - vb2_set_plane_payload(vb, 0, len); -} - -static struct vb2_ops qcam_video_qops = { - .queue_setup = queue_setup, - .buf_queue = buffer_queue, - .buf_finish = buffer_finish, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -/* - * Video4linux interfacing - */ - -static int qcam_querycap(struct file *file, void *priv, - struct v4l2_capability *vcap) -{ - struct qcam *qcam = video_drvdata(file); - - strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver)); - strlcpy(vcap->card, "Connectix B&W Quickcam", sizeof(vcap->card)); - strlcpy(vcap->bus_info, qcam->pport->name, sizeof(vcap->bus_info)); - vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -static int qcam_enum_input(struct file *file, void *fh, struct v4l2_input *vin) -{ - if (vin->index > 0) - return -EINVAL; - strlcpy(vin->name, "Camera", sizeof(vin->name)); - vin->type = V4L2_INPUT_TYPE_CAMERA; - vin->audioset = 0; - vin->tuner = 0; - vin->std = 0; - vin->status = 0; - return 0; -} - -static int qcam_g_input(struct file *file, void *fh, unsigned int *inp) -{ - *inp = 0; - return 0; -} - -static int qcam_s_input(struct file *file, void *fh, unsigned int inp) -{ - return (inp > 0) ? -EINVAL : 0; -} - -static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) -{ - struct qcam *qcam = video_drvdata(file); - struct v4l2_pix_format *pix = &fmt->fmt.pix; - - pix->width = qcam->width / qcam->transfer_scale; - pix->height = qcam->height / qcam->transfer_scale; - pix->pixelformat = (qcam->bpp == 4) ? V4L2_PIX_FMT_Y4 : V4L2_PIX_FMT_Y6; - pix->field = V4L2_FIELD_NONE; - pix->bytesperline = pix->width; - pix->sizeimage = pix->width * pix->height; - /* Just a guess */ - pix->colorspace = V4L2_COLORSPACE_SRGB; - return 0; -} - -static int qcam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) -{ - struct v4l2_pix_format *pix = &fmt->fmt.pix; - - if (pix->height <= 60 || pix->width <= 80) { - pix->height = 60; - pix->width = 80; - } else if (pix->height <= 120 || pix->width <= 160) { - pix->height = 120; - pix->width = 160; - } else { - pix->height = 240; - pix->width = 320; - } - if (pix->pixelformat != V4L2_PIX_FMT_Y4 && - pix->pixelformat != V4L2_PIX_FMT_Y6) - pix->pixelformat = V4L2_PIX_FMT_Y4; - pix->field = V4L2_FIELD_NONE; - pix->bytesperline = pix->width; - pix->sizeimage = pix->width * pix->height; - /* Just a guess */ - pix->colorspace = V4L2_COLORSPACE_SRGB; - return 0; -} - -static int qcam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) -{ - struct qcam *qcam = video_drvdata(file); - struct v4l2_pix_format *pix = &fmt->fmt.pix; - int ret = qcam_try_fmt_vid_cap(file, fh, fmt); - - if (ret) - return ret; - if (vb2_is_busy(&qcam->vb_vidq)) - return -EBUSY; - qcam->width = 320; - qcam->height = 240; - if (pix->height == 60) - qcam->transfer_scale = 4; - else if (pix->height == 120) - qcam->transfer_scale = 2; - else - qcam->transfer_scale = 1; - if (pix->pixelformat == V4L2_PIX_FMT_Y6) - qcam->bpp = 6; - else - qcam->bpp = 4; - - qc_setscanmode(qcam); - /* We must update the camera before we grab. We could - just have changed the grab size */ - qcam->status |= QC_PARAM_CHANGE; - return 0; -} - -static int qcam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) -{ - static struct v4l2_fmtdesc formats[] = { - { 0, 0, 0, - "4-Bit Monochrome", V4L2_PIX_FMT_Y4, - { 0, 0, 0, 0 } - }, - { 1, 0, 0, - "6-Bit Monochrome", V4L2_PIX_FMT_Y6, - { 0, 0, 0, 0 } - }, - }; - enum v4l2_buf_type type = fmt->type; - - if (fmt->index > 1) - return -EINVAL; - - *fmt = formats[fmt->index]; - fmt->type = type; - return 0; -} - -static int qcam_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) -{ - static const struct v4l2_frmsize_discrete sizes[] = { - { 80, 60 }, - { 160, 120 }, - { 320, 240 }, - }; - - if (fsize->index > 2) - return -EINVAL; - if (fsize->pixel_format != V4L2_PIX_FMT_Y4 && - fsize->pixel_format != V4L2_PIX_FMT_Y6) - return -EINVAL; - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete = sizes[fsize->index]; - return 0; -} - -static int qcam_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct qcam *qcam = - container_of(ctrl->handler, struct qcam, hdl); - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - qcam->brightness = ctrl->val; - break; - case V4L2_CID_CONTRAST: - qcam->contrast = ctrl->val; - break; - case V4L2_CID_GAMMA: - qcam->whitebal = ctrl->val; - break; - default: - ret = -EINVAL; - break; - } - if (ret == 0) - qcam->status |= QC_PARAM_CHANGE; - return ret; -} - -static const struct v4l2_file_operations qcam_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .poll = vb2_fop_poll, - .unlocked_ioctl = video_ioctl2, - .read = vb2_fop_read, - .mmap = vb2_fop_mmap, -}; - -static const struct v4l2_ioctl_ops qcam_ioctl_ops = { - .vidioc_querycap = qcam_querycap, - .vidioc_g_input = qcam_g_input, - .vidioc_s_input = qcam_s_input, - .vidioc_enum_input = qcam_enum_input, - .vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap, - .vidioc_enum_framesizes = qcam_enum_framesizes, - .vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static const struct v4l2_ctrl_ops qcam_ctrl_ops = { - .s_ctrl = qcam_s_ctrl, -}; - -/* Initialize the QuickCam driver control structure. This is where - * defaults are set for people who don't have a config file.*/ - -static struct qcam *qcam_init(struct parport *port) -{ - struct qcam *qcam; - struct v4l2_device *v4l2_dev; - struct vb2_queue *q; - int err; - - qcam = kzalloc(sizeof(struct qcam), GFP_KERNEL); - if (qcam == NULL) - return NULL; - - v4l2_dev = &qcam->v4l2_dev; - snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "bw-qcam%u", num_cams); - - if (v4l2_device_register(port->dev, v4l2_dev) < 0) { - v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); - kfree(qcam); - return NULL; - } - - v4l2_ctrl_handler_init(&qcam->hdl, 3); - v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 180); - v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 192); - v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, - V4L2_CID_GAMMA, 0, 255, 1, 105); - if (qcam->hdl.error) { - v4l2_err(v4l2_dev, "couldn't register controls\n"); - goto exit; - } - - mutex_init(&qcam->lock); - mutex_init(&qcam->queue_lock); - - /* initialize queue */ - q = &qcam->vb_vidq; - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; - q->drv_priv = qcam; - q->ops = &qcam_video_qops; - q->mem_ops = &vb2_vmalloc_memops; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - err = vb2_queue_init(q); - if (err < 0) { - v4l2_err(v4l2_dev, "couldn't init vb2_queue for %s.\n", port->name); - goto exit; - } - qcam->vdev.queue = q; - qcam->vdev.queue->lock = &qcam->queue_lock; - - qcam->pport = port; - qcam->pdev = parport_register_device(port, v4l2_dev->name, NULL, NULL, - NULL, 0, NULL); - if (qcam->pdev == NULL) { - v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name); - goto exit; - } - - strlcpy(qcam->vdev.name, "Connectix QuickCam", sizeof(qcam->vdev.name)); - qcam->vdev.v4l2_dev = v4l2_dev; - qcam->vdev.ctrl_handler = &qcam->hdl; - qcam->vdev.fops = &qcam_fops; - qcam->vdev.lock = &qcam->lock; - qcam->vdev.ioctl_ops = &qcam_ioctl_ops; - qcam->vdev.release = video_device_release_empty; - video_set_drvdata(&qcam->vdev, qcam); - - qcam->port_mode = (QC_ANY | QC_NOTSET); - qcam->width = 320; - qcam->height = 240; - qcam->bpp = 4; - qcam->transfer_scale = 2; - qcam->contrast = 192; - qcam->brightness = 180; - qcam->whitebal = 105; - qcam->top = 1; - qcam->left = 14; - qcam->mode = -1; - qcam->status = QC_PARAM_CHANGE; - return qcam; - -exit: - v4l2_ctrl_handler_free(&qcam->hdl); - kfree(qcam); - return NULL; -} - -static int qc_calibrate(struct qcam *q) -{ - /* - * Bugfix by Hanno Mueller hmueller@kabel.de, Mai 21 96 - * The white balance is an individual value for each - * quickcam. - */ - - int value; - int count = 0; - - qc_command(q, 27); /* AutoAdjustOffset */ - qc_command(q, 0); /* Dummy Parameter, ignored by the camera */ - - /* GetOffset (33) will read 255 until autocalibration */ - /* is finished. After that, a value of 1-254 will be */ - /* returned. */ - - do { - qc_command(q, 33); - value = qc_readparam(q); - mdelay(1); - schedule(); - count++; - } while (value == 0xff && count < 2048); - - q->whitebal = value; - return value; -} - -static int init_bwqcam(struct parport *port) -{ - struct qcam *qcam; - - if (num_cams == MAX_CAMS) { - printk(KERN_ERR "Too many Quickcams (max %d)\n", MAX_CAMS); - return -ENOSPC; - } - - qcam = qcam_init(port); - if (qcam == NULL) - return -ENODEV; - - parport_claim_or_block(qcam->pdev); - - qc_reset(qcam); - - if (qc_detect(qcam) == 0) { - parport_release(qcam->pdev); - parport_unregister_device(qcam->pdev); - kfree(qcam); - return -ENODEV; - } - qc_calibrate(qcam); - v4l2_ctrl_handler_setup(&qcam->hdl); - - parport_release(qcam->pdev); - - v4l2_info(&qcam->v4l2_dev, "Connectix Quickcam on %s\n", qcam->pport->name); - - if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { - parport_unregister_device(qcam->pdev); - kfree(qcam); - return -ENODEV; - } - - qcams[num_cams++] = qcam; - - return 0; -} - -static void close_bwqcam(struct qcam *qcam) -{ - video_unregister_device(&qcam->vdev); - v4l2_ctrl_handler_free(&qcam->hdl); - parport_unregister_device(qcam->pdev); - kfree(qcam); -} - -/* The parport parameter controls which parports will be scanned. - * Scanning all parports causes some printers to print a garbage page. - * -- March 14, 1999 Billy Donahue <billy@escape.com> */ -#ifdef MODULE -static char *parport[MAX_CAMS] = { NULL, }; -module_param_array(parport, charp, NULL, 0); -#endif - -static int accept_bwqcam(struct parport *port) -{ -#ifdef MODULE - int n; - - if (parport[0] && strncmp(parport[0], "auto", 4) != 0) { - /* user gave parport parameters */ - for (n = 0; n < MAX_CAMS && parport[n]; n++) { - char *ep; - unsigned long r; - r = simple_strtoul(parport[n], &ep, 0); - if (ep == parport[n]) { - printk(KERN_ERR - "bw-qcam: bad port specifier \"%s\"\n", - parport[n]); - continue; - } - if (r == port->number) - return 1; - } - return 0; - } -#endif - return 1; -} - -static void bwqcam_attach(struct parport *port) -{ - if (accept_bwqcam(port)) - init_bwqcam(port); -} - -static void bwqcam_detach(struct parport *port) -{ - int i; - for (i = 0; i < num_cams; i++) { - struct qcam *qcam = qcams[i]; - if (qcam && qcam->pdev->port == port) { - qcams[i] = NULL; - close_bwqcam(qcam); - } - } -} - -static struct parport_driver bwqcam_driver = { - .name = "bw-qcam", - .attach = bwqcam_attach, - .detach = bwqcam_detach, -}; - -static void __exit exit_bw_qcams(void) -{ - parport_unregister_driver(&bwqcam_driver); -} - -static int __init init_bw_qcams(void) -{ -#ifdef MODULE - /* Do some sanity checks on the module parameters. */ - if (maxpoll > 5000) { - printk(KERN_INFO "Connectix Quickcam max-poll was above 5000. Using 5000.\n"); - maxpoll = 5000; - } - - if (yieldlines < 1) { - printk(KERN_INFO "Connectix Quickcam yieldlines was less than 1. Using 1.\n"); - yieldlines = 1; - } -#endif - return parport_register_driver(&bwqcam_driver); -} - -module_init(init_bw_qcams); -module_exit(exit_bw_qcams); - -MODULE_LICENSE("GPL"); -MODULE_VERSION("0.0.3"); diff --git a/drivers/staging/media/parport/c-qcam.c b/drivers/staging/media/parport/c-qcam.c deleted file mode 100644 index b9010bd3ed3e..000000000000 --- a/drivers/staging/media/parport/c-qcam.c +++ /dev/null @@ -1,882 +0,0 @@ -/* - * Video4Linux Colour QuickCam driver - * Copyright 1997-2000 Philip Blundell <philb@gnu.org> - * - * Module parameters: - * - * parport=auto -- probe all parports (default) - * parport=0 -- parport0 becomes qcam1 - * parport=2,0,1 -- parports 2,0,1 are tried in that order - * - * probe=0 -- do no probing, assume camera is present - * probe=1 -- use IEEE-1284 autoprobe data only (default) - * probe=2 -- probe aggressively for cameras - * - * force_rgb=1 -- force data format to RGB (default is BGR) - * - * The parport parameter controls which parports will be scanned. - * Scanning all parports causes some printers to print a garbage page. - * -- March 14, 1999 Billy Donahue <billy@escape.com> - * - * Fixed data format to BGR, added force_rgb parameter. Added missing - * parport_unregister_driver() on module removal. - * -- May 28, 2000 Claudio Matsuoka <claudio@conectiva.com> - */ - -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/parport.h> -#include <linux/sched.h> -#include <linux/mutex.h> -#include <linux/jiffies.h> -#include <linux/videodev2.h> -#include <asm/uaccess.h> -#include <media/v4l2-device.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-fh.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-event.h> - -struct qcam { - struct v4l2_device v4l2_dev; - struct video_device vdev; - struct v4l2_ctrl_handler hdl; - struct pardevice *pdev; - struct parport *pport; - int width, height; - int ccd_width, ccd_height; - int mode; - int contrast, brightness, whitebal; - int top, left; - unsigned int bidirectional; - struct mutex lock; -}; - -/* cameras maximum */ -#define MAX_CAMS 4 - -/* The three possible QuickCam modes */ -#define QC_MILLIONS 0x18 -#define QC_BILLIONS 0x10 -#define QC_THOUSANDS 0x08 /* with VIDEC compression (not supported) */ - -/* The three possible decimations */ -#define QC_DECIMATION_1 0 -#define QC_DECIMATION_2 2 -#define QC_DECIMATION_4 4 - -#define BANNER "Colour QuickCam for Video4Linux v0.06" - -static int parport[MAX_CAMS] = { [1 ... MAX_CAMS-1] = -1 }; -static int probe = 2; -static bool force_rgb; -static int video_nr = -1; - -/* FIXME: parport=auto would never have worked, surely? --RR */ -MODULE_PARM_DESC(parport, "parport=<auto|n[,n]...> for port detection method\n" - "probe=<0|1|2> for camera detection method\n" - "force_rgb=<0|1> for RGB data format (default BGR)"); -module_param_array(parport, int, NULL, 0); -module_param(probe, int, 0); -module_param(force_rgb, bool, 0); -module_param(video_nr, int, 0); - -static struct qcam *qcams[MAX_CAMS]; -static unsigned int num_cams; - -static inline void qcam_set_ack(struct qcam *qcam, unsigned int i) -{ - /* note: the QC specs refer to the PCAck pin by voltage, not - software level. PC ports have builtin inverters. */ - parport_frob_control(qcam->pport, 8, i ? 8 : 0); -} - -static inline unsigned int qcam_ready1(struct qcam *qcam) -{ - return (parport_read_status(qcam->pport) & 0x8) ? 1 : 0; -} - -static inline unsigned int qcam_ready2(struct qcam *qcam) -{ - return (parport_read_data(qcam->pport) & 0x1) ? 1 : 0; -} - -static unsigned int qcam_await_ready1(struct qcam *qcam, int value) -{ - struct v4l2_device *v4l2_dev = &qcam->v4l2_dev; - unsigned long oldjiffies = jiffies; - unsigned int i; - - for (oldjiffies = jiffies; - time_before(jiffies, oldjiffies + msecs_to_jiffies(40));) - if (qcam_ready1(qcam) == value) - return 0; - - /* If the camera didn't respond within 1/25 second, poll slowly - for a while. */ - for (i = 0; i < 50; i++) { - if (qcam_ready1(qcam) == value) - return 0; - msleep_interruptible(100); - } - - /* Probably somebody pulled the plug out. Not much we can do. */ - v4l2_err(v4l2_dev, "ready1 timeout (%d) %x %x\n", value, - parport_read_status(qcam->pport), - parport_read_control(qcam->pport)); - return 1; -} - -static unsigned int qcam_await_ready2(struct qcam *qcam, int value) -{ - struct v4l2_device *v4l2_dev = &qcam->v4l2_dev; - unsigned long oldjiffies = jiffies; - unsigned int i; - - for (oldjiffies = jiffies; - time_before(jiffies, oldjiffies + msecs_to_jiffies(40));) - if (qcam_ready2(qcam) == value) - return 0; - - /* If the camera didn't respond within 1/25 second, poll slowly - for a while. */ - for (i = 0; i < 50; i++) { - if (qcam_ready2(qcam) == value) - return 0; - msleep_interruptible(100); - } - - /* Probably somebody pulled the plug out. Not much we can do. */ - v4l2_err(v4l2_dev, "ready2 timeout (%d) %x %x %x\n", value, - parport_read_status(qcam->pport), - parport_read_control(qcam->pport), - parport_read_data(qcam->pport)); - return 1; -} - -static int qcam_read_data(struct qcam *qcam) -{ - unsigned int idata; - - qcam_set_ack(qcam, 0); - if (qcam_await_ready1(qcam, 1)) - return -1; - idata = parport_read_status(qcam->pport) & 0xf0; - qcam_set_ack(qcam, 1); - if (qcam_await_ready1(qcam, 0)) - return -1; - idata |= parport_read_status(qcam->pport) >> 4; - return idata; -} - -static int qcam_write_data(struct qcam *qcam, unsigned int data) -{ - struct v4l2_device *v4l2_dev = &qcam->v4l2_dev; - unsigned int idata; - - parport_write_data(qcam->pport, data); - idata = qcam_read_data(qcam); - if (data != idata) { - v4l2_warn(v4l2_dev, "sent %x but received %x\n", data, - idata); - return 1; - } - return 0; -} - -static inline int qcam_set(struct qcam *qcam, unsigned int cmd, unsigned int data) -{ - if (qcam_write_data(qcam, cmd)) - return -1; - if (qcam_write_data(qcam, data)) - return -1; - return 0; -} - -static inline int qcam_get(struct qcam *qcam, unsigned int cmd) -{ - if (qcam_write_data(qcam, cmd)) - return -1; - return qcam_read_data(qcam); -} - -static int qc_detect(struct qcam *qcam) -{ - unsigned int stat, ostat, i, count = 0; - - /* The probe routine below is not very reliable. The IEEE-1284 - probe takes precedence. */ - /* XXX Currently parport provides no way to distinguish between - "the IEEE probe was not done" and "the probe was done, but - no device was found". Fix this one day. */ - if (qcam->pport->probe_info[0].class == PARPORT_CLASS_MEDIA - && qcam->pport->probe_info[0].model - && !strcmp(qcam->pdev->port->probe_info[0].model, - "Color QuickCam 2.0")) { - printk(KERN_DEBUG "QuickCam: Found by IEEE1284 probe.\n"); - return 1; - } - - if (probe < 2) - return 0; - - parport_write_control(qcam->pport, 0xc); - - /* look for a heartbeat */ - ostat = stat = parport_read_status(qcam->pport); - for (i = 0; i < 250; i++) { - mdelay(1); - stat = parport_read_status(qcam->pport); - if (ostat != stat) { - if (++count >= 3) - return 1; - ostat = stat; - } - } - - /* Reset the camera and try again */ - parport_write_control(qcam->pport, 0xc); - parport_write_control(qcam->pport, 0x8); - mdelay(1); - parport_write_control(qcam->pport, 0xc); - mdelay(1); - count = 0; - - ostat = stat = parport_read_status(qcam->pport); - for (i = 0; i < 250; i++) { - mdelay(1); - stat = parport_read_status(qcam->pport); - if (ostat != stat) { - if (++count >= 3) - return 1; - ostat = stat; - } - } - - /* no (or flatline) camera, give up */ - return 0; -} - -static void qc_reset(struct qcam *qcam) -{ - parport_write_control(qcam->pport, 0xc); - parport_write_control(qcam->pport, 0x8); - mdelay(1); - parport_write_control(qcam->pport, 0xc); - mdelay(1); -} - -/* Reset the QuickCam and program for brightness, contrast, - * white-balance, and resolution. */ - -static void qc_setup(struct qcam *qcam) -{ - qc_reset(qcam); - - /* Set the brightness. */ - qcam_set(qcam, 11, qcam->brightness); - - /* Set the height and width. These refer to the actual - CCD area *before* applying the selected decimation. */ - qcam_set(qcam, 17, qcam->ccd_height); - qcam_set(qcam, 19, qcam->ccd_width / 2); - - /* Set top and left. */ - qcam_set(qcam, 0xd, qcam->top); - qcam_set(qcam, 0xf, qcam->left); - - /* Set contrast and white balance. */ - qcam_set(qcam, 0x19, qcam->contrast); - qcam_set(qcam, 0x1f, qcam->whitebal); - - /* Set the speed. */ - qcam_set(qcam, 45, 2); -} - -/* Read some bytes from the camera and put them in the buffer. - nbytes should be a multiple of 3, because bidirectional mode gives - us three bytes at a time. */ - -static unsigned int qcam_read_bytes(struct qcam *qcam, unsigned char *buf, unsigned int nbytes) -{ - unsigned int bytes = 0; - - qcam_set_ack(qcam, 0); - if (qcam->bidirectional) { - /* It's a bidirectional port */ - while (bytes < nbytes) { - unsigned int lo1, hi1, lo2, hi2; - unsigned char r, g, b; - - if (qcam_await_ready2(qcam, 1)) - return bytes; - lo1 = parport_read_data(qcam->pport) >> 1; - hi1 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10; - qcam_set_ack(qcam, 1); - if (qcam_await_ready2(qcam, 0)) - return bytes; - lo2 = parport_read_data(qcam->pport) >> 1; - hi2 = ((parport_read_status(qcam->pport) >> 3) & 0x1f) ^ 0x10; - qcam_set_ack(qcam, 0); - r = lo1 | ((hi1 & 1) << 7); - g = ((hi1 & 0x1e) << 3) | ((hi2 & 0x1e) >> 1); - b = lo2 | ((hi2 & 1) << 7); - if (force_rgb) { - buf[bytes++] = r; - buf[bytes++] = g; - buf[bytes++] = b; - } else { - buf[bytes++] = b; - buf[bytes++] = g; - buf[bytes++] = r; - } - } - } else { - /* It's a unidirectional port */ - int i = 0, n = bytes; - unsigned char rgb[3]; - - while (bytes < nbytes) { - unsigned int hi, lo; - - if (qcam_await_ready1(qcam, 1)) - return bytes; - hi = (parport_read_status(qcam->pport) & 0xf0); - qcam_set_ack(qcam, 1); - if (qcam_await_ready1(qcam, 0)) - return bytes; - lo = (parport_read_status(qcam->pport) & 0xf0); - qcam_set_ack(qcam, 0); - /* flip some bits */ - rgb[(i = bytes++ % 3)] = (hi | (lo >> 4)) ^ 0x88; - if (i >= 2) { -get_fragment: - if (force_rgb) { - buf[n++] = rgb[0]; - buf[n++] = rgb[1]; - buf[n++] = rgb[2]; - } else { - buf[n++] = rgb[2]; - buf[n++] = rgb[1]; - buf[n++] = rgb[0]; - } - } - } - if (i) { - i = 0; - goto get_fragment; - } - } - return bytes; -} - -#define BUFSZ 150 - -static long qc_capture(struct qcam *qcam, char __user *buf, unsigned long len) -{ - struct v4l2_device *v4l2_dev = &qcam->v4l2_dev; - unsigned lines, pixelsperline; - unsigned int is_bi_dir = qcam->bidirectional; - size_t wantlen, outptr = 0; - char tmpbuf[BUFSZ]; - - if (!access_ok(VERIFY_WRITE, buf, len)) - return -EFAULT; - - /* Wait for camera to become ready */ - for (;;) { - int i = qcam_get(qcam, 41); - - if (i == -1) { - qc_setup(qcam); - return -EIO; - } - if ((i & 0x80) == 0) - break; - schedule(); - } - - if (qcam_set(qcam, 7, (qcam->mode | (is_bi_dir ? 1 : 0)) + 1)) - return -EIO; - - lines = qcam->height; - pixelsperline = qcam->width; - - if (is_bi_dir) { - /* Turn the port around */ - parport_data_reverse(qcam->pport); - mdelay(3); - qcam_set_ack(qcam, 0); - if (qcam_await_ready1(qcam, 1)) { - qc_setup(qcam); - return -EIO; - } - qcam_set_ack(qcam, 1); - if (qcam_await_ready1(qcam, 0)) { - qc_setup(qcam); - return -EIO; - } - } - - wantlen = lines * pixelsperline * 24 / 8; - - while (wantlen) { - size_t t, s; - - s = (wantlen > BUFSZ) ? BUFSZ : wantlen; - t = qcam_read_bytes(qcam, tmpbuf, s); - if (outptr < len) { - size_t sz = len - outptr; - - if (sz > t) - sz = t; - if (__copy_to_user(buf + outptr, tmpbuf, sz)) - break; - outptr += sz; - } - wantlen -= t; - if (t < s) - break; - cond_resched(); - } - - len = outptr; - - if (wantlen) { - v4l2_err(v4l2_dev, "short read.\n"); - if (is_bi_dir) - parport_data_forward(qcam->pport); - qc_setup(qcam); - return len; - } - - if (is_bi_dir) { - int l; - - do { - l = qcam_read_bytes(qcam, tmpbuf, 3); - cond_resched(); - } while (l && (tmpbuf[0] == 0x7e || tmpbuf[1] == 0x7e || tmpbuf[2] == 0x7e)); - if (force_rgb) { - if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf) - v4l2_err(v4l2_dev, "bad EOF\n"); - } else { - if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe) - v4l2_err(v4l2_dev, "bad EOF\n"); - } - qcam_set_ack(qcam, 0); - if (qcam_await_ready1(qcam, 1)) { - v4l2_err(v4l2_dev, "no ack after EOF\n"); - parport_data_forward(qcam->pport); - qc_setup(qcam); - return len; - } - parport_data_forward(qcam->pport); - mdelay(3); - qcam_set_ack(qcam, 1); - if (qcam_await_ready1(qcam, 0)) { - v4l2_err(v4l2_dev, "no ack to port turnaround\n"); - qc_setup(qcam); - return len; - } - } else { - int l; - - do { - l = qcam_read_bytes(qcam, tmpbuf, 1); - cond_resched(); - } while (l && tmpbuf[0] == 0x7e); - l = qcam_read_bytes(qcam, tmpbuf + 1, 2); - if (force_rgb) { - if (tmpbuf[0] != 0xe || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xf) - v4l2_err(v4l2_dev, "bad EOF\n"); - } else { - if (tmpbuf[0] != 0xf || tmpbuf[1] != 0x0 || tmpbuf[2] != 0xe) - v4l2_err(v4l2_dev, "bad EOF\n"); - } - } - - qcam_write_data(qcam, 0); - return len; -} - -/* - * Video4linux interfacing - */ - -static int qcam_querycap(struct file *file, void *priv, - struct v4l2_capability *vcap) -{ - struct qcam *qcam = video_drvdata(file); - - strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver)); - strlcpy(vcap->card, "Color Quickcam", sizeof(vcap->card)); - strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info)); - vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; - vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -static int qcam_enum_input(struct file *file, void *fh, struct v4l2_input *vin) -{ - if (vin->index > 0) - return -EINVAL; - strlcpy(vin->name, "Camera", sizeof(vin->name)); - vin->type = V4L2_INPUT_TYPE_CAMERA; - vin->audioset = 0; - vin->tuner = 0; - vin->std = 0; - vin->status = 0; - return 0; -} - -static int qcam_g_input(struct file *file, void *fh, unsigned int *inp) -{ - *inp = 0; - return 0; -} - -static int qcam_s_input(struct file *file, void *fh, unsigned int inp) -{ - return (inp > 0) ? -EINVAL : 0; -} - -static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) -{ - struct qcam *qcam = video_drvdata(file); - struct v4l2_pix_format *pix = &fmt->fmt.pix; - - pix->width = qcam->width; - pix->height = qcam->height; - pix->pixelformat = V4L2_PIX_FMT_RGB24; - pix->field = V4L2_FIELD_NONE; - pix->bytesperline = 3 * qcam->width; - pix->sizeimage = 3 * qcam->width * qcam->height; - /* Just a guess */ - pix->colorspace = V4L2_COLORSPACE_SRGB; - return 0; -} - -static int qcam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) -{ - struct v4l2_pix_format *pix = &fmt->fmt.pix; - - if (pix->height < 60 || pix->width < 80) { - pix->height = 60; - pix->width = 80; - } else if (pix->height < 120 || pix->width < 160) { - pix->height = 120; - pix->width = 160; - } else { - pix->height = 240; - pix->width = 320; - } - pix->pixelformat = V4L2_PIX_FMT_RGB24; - pix->field = V4L2_FIELD_NONE; - pix->bytesperline = 3 * pix->width; - pix->sizeimage = 3 * pix->width * pix->height; - /* Just a guess */ - pix->colorspace = V4L2_COLORSPACE_SRGB; - return 0; -} - -static int qcam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) -{ - struct qcam *qcam = video_drvdata(file); - struct v4l2_pix_format *pix = &fmt->fmt.pix; - int ret = qcam_try_fmt_vid_cap(file, fh, fmt); - - if (ret) - return ret; - switch (pix->height) { - case 60: - qcam->mode = QC_DECIMATION_4; - break; - case 120: - qcam->mode = QC_DECIMATION_2; - break; - default: - qcam->mode = QC_DECIMATION_1; - break; - } - - mutex_lock(&qcam->lock); - qcam->mode |= QC_MILLIONS; - qcam->height = pix->height; - qcam->width = pix->width; - parport_claim_or_block(qcam->pdev); - qc_setup(qcam); - parport_release(qcam->pdev); - mutex_unlock(&qcam->lock); - return 0; -} - -static int qcam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) -{ - static struct v4l2_fmtdesc formats[] = { - { 0, 0, 0, - "RGB 8:8:8", V4L2_PIX_FMT_RGB24, - { 0, 0, 0, 0 } - }, - }; - enum v4l2_buf_type type = fmt->type; - - if (fmt->index > 0) - return -EINVAL; - - *fmt = formats[fmt->index]; - fmt->type = type; - return 0; -} - -static ssize_t qcam_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct qcam *qcam = video_drvdata(file); - int len; - - mutex_lock(&qcam->lock); - parport_claim_or_block(qcam->pdev); - /* Probably should have a semaphore against multiple users */ - len = qc_capture(qcam, buf, count); - parport_release(qcam->pdev); - mutex_unlock(&qcam->lock); - return len; -} - -static int qcam_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct qcam *qcam = - container_of(ctrl->handler, struct qcam, hdl); - int ret = 0; - - mutex_lock(&qcam->lock); - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - qcam->brightness = ctrl->val; - break; - case V4L2_CID_CONTRAST: - qcam->contrast = ctrl->val; - break; - case V4L2_CID_GAMMA: - qcam->whitebal = ctrl->val; - break; - default: - ret = -EINVAL; - break; - } - if (ret == 0) { - parport_claim_or_block(qcam->pdev); - qc_setup(qcam); - parport_release(qcam->pdev); - } - mutex_unlock(&qcam->lock); - return ret; -} - -static const struct v4l2_file_operations qcam_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = v4l2_fh_release, - .poll = v4l2_ctrl_poll, - .unlocked_ioctl = video_ioctl2, - .read = qcam_read, -}; - -static const struct v4l2_ioctl_ops qcam_ioctl_ops = { - .vidioc_querycap = qcam_querycap, - .vidioc_g_input = qcam_g_input, - .vidioc_s_input = qcam_s_input, - .vidioc_enum_input = qcam_enum_input, - .vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap, - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static const struct v4l2_ctrl_ops qcam_ctrl_ops = { - .s_ctrl = qcam_s_ctrl, -}; - -/* Initialize the QuickCam driver control structure. */ - -static struct qcam *qcam_init(struct parport *port) -{ - struct qcam *qcam; - struct v4l2_device *v4l2_dev; - - qcam = kzalloc(sizeof(*qcam), GFP_KERNEL); - if (qcam == NULL) - return NULL; - - v4l2_dev = &qcam->v4l2_dev; - strlcpy(v4l2_dev->name, "c-qcam", sizeof(v4l2_dev->name)); - - if (v4l2_device_register(NULL, v4l2_dev) < 0) { - v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); - kfree(qcam); - return NULL; - } - - v4l2_ctrl_handler_init(&qcam->hdl, 3); - v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 240); - v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 192); - v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, - V4L2_CID_GAMMA, 0, 255, 1, 128); - if (qcam->hdl.error) { - v4l2_err(v4l2_dev, "couldn't register controls\n"); - v4l2_ctrl_handler_free(&qcam->hdl); - kfree(qcam); - return NULL; - } - - qcam->pport = port; - qcam->pdev = parport_register_device(port, "c-qcam", NULL, NULL, - NULL, 0, NULL); - - qcam->bidirectional = (qcam->pport->modes & PARPORT_MODE_TRISTATE) ? 1 : 0; - - if (qcam->pdev == NULL) { - v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name); - v4l2_ctrl_handler_free(&qcam->hdl); - kfree(qcam); - return NULL; - } - - strlcpy(qcam->vdev.name, "Colour QuickCam", sizeof(qcam->vdev.name)); - qcam->vdev.v4l2_dev = v4l2_dev; - qcam->vdev.fops = &qcam_fops; - qcam->vdev.ioctl_ops = &qcam_ioctl_ops; - qcam->vdev.release = video_device_release_empty; - qcam->vdev.ctrl_handler = &qcam->hdl; - video_set_drvdata(&qcam->vdev, qcam); - - mutex_init(&qcam->lock); - qcam->width = qcam->ccd_width = 320; - qcam->height = qcam->ccd_height = 240; - qcam->mode = QC_MILLIONS | QC_DECIMATION_1; - qcam->contrast = 192; - qcam->brightness = 240; - qcam->whitebal = 128; - qcam->top = 1; - qcam->left = 14; - return qcam; -} - -static int init_cqcam(struct parport *port) -{ - struct qcam *qcam; - struct v4l2_device *v4l2_dev; - - if (parport[0] != -1) { - /* The user gave specific instructions */ - int i, found = 0; - - for (i = 0; i < MAX_CAMS && parport[i] != -1; i++) { - if (parport[0] == port->number) - found = 1; - } - if (!found) - return -ENODEV; - } - - if (num_cams == MAX_CAMS) - return -ENOSPC; - - qcam = qcam_init(port); - if (qcam == NULL) - return -ENODEV; - - v4l2_dev = &qcam->v4l2_dev; - - parport_claim_or_block(qcam->pdev); - - qc_reset(qcam); - - if (probe && qc_detect(qcam) == 0) { - parport_release(qcam->pdev); - parport_unregister_device(qcam->pdev); - kfree(qcam); - return -ENODEV; - } - - qc_setup(qcam); - - parport_release(qcam->pdev); - - if (video_register_device(&qcam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { - v4l2_err(v4l2_dev, "Unable to register Colour QuickCam on %s\n", - qcam->pport->name); - parport_unregister_device(qcam->pdev); - kfree(qcam); - return -ENODEV; - } - - v4l2_info(v4l2_dev, "%s: Colour QuickCam found on %s\n", - video_device_node_name(&qcam->vdev), qcam->pport->name); - - qcams[num_cams++] = qcam; - - return 0; -} - -static void close_cqcam(struct qcam *qcam) -{ - video_unregister_device(&qcam->vdev); - v4l2_ctrl_handler_free(&qcam->hdl); - parport_unregister_device(qcam->pdev); - kfree(qcam); -} - -static void cq_attach(struct parport *port) -{ - init_cqcam(port); -} - -static void cq_detach(struct parport *port) -{ - /* Write this some day. */ -} - -static struct parport_driver cqcam_driver = { - .name = "cqcam", - .attach = cq_attach, - .detach = cq_detach, -}; - -static int __init cqcam_init(void) -{ - printk(KERN_INFO BANNER "\n"); - - return parport_register_driver(&cqcam_driver); -} - -static void __exit cqcam_cleanup(void) -{ - unsigned int i; - - for (i = 0; i < num_cams; i++) - close_cqcam(qcams[i]); - - parport_unregister_driver(&cqcam_driver); -} - -MODULE_AUTHOR("Philip Blundell <philb@gnu.org>"); -MODULE_DESCRIPTION(BANNER); -MODULE_LICENSE("GPL"); -MODULE_VERSION("0.0.4"); - -module_init(cqcam_init); -module_exit(cqcam_cleanup); diff --git a/drivers/staging/media/parport/pms.c b/drivers/staging/media/parport/pms.c deleted file mode 100644 index e6b497528cea..000000000000 --- a/drivers/staging/media/parport/pms.c +++ /dev/null @@ -1,1156 +0,0 @@ -/* - * Media Vision Pro Movie Studio - * or - * "all you need is an I2C bus some RAM and a prayer" - * - * This draws heavily on code - * - * (c) Wolfgang Koehler, wolf@first.gmd.de, Dec. 1994 - * Kiefernring 15 - * 14478 Potsdam, Germany - * - * Most of this code is directly derived from his userspace driver. - * His driver works so send any reports to alan@lxorguk.ukuu.org.uk - * unless the userspace driver also doesn't work for you... - * - * Changes: - * 25-11-2009 Hans Verkuil <hverkuil@xs4all.nl> - * - converted to version 2 of the V4L API. - * 08/07/2003 Daniele Bellucci <bellucda@tiscali.it> - * - pms_capture: report back -EFAULT - */ - -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/kernel.h> -#include <linux/mm.h> -#include <linux/slab.h> -#include <linux/ioport.h> -#include <linux/init.h> -#include <linux/mutex.h> -#include <linux/uaccess.h> -#include <linux/isa.h> -#include <asm/io.h> - -#include <linux/videodev2.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-fh.h> -#include <media/v4l2-event.h> -#include <media/v4l2-device.h> - -MODULE_LICENSE("GPL"); -MODULE_VERSION("0.0.5"); - -#define MOTOROLA 1 -#define PHILIPS2 2 /* SAA7191 */ -#define PHILIPS1 3 -#define MVVMEMORYWIDTH 0x40 /* 512 bytes */ - -struct i2c_info { - u8 slave; - u8 sub; - u8 data; - u8 hits; -}; - -struct pms { - struct v4l2_device v4l2_dev; - struct video_device vdev; - struct v4l2_ctrl_handler hdl; - int height; - int width; - int depth; - int input; - struct mutex lock; - int i2c_count; - struct i2c_info i2cinfo[64]; - - int decoder; - int standard; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */ - v4l2_std_id std; - int io; - int data; - void __iomem *mem; -}; - -/* - * I/O ports and Shared Memory - */ - -static int io_port = 0x250; -module_param(io_port, int, 0); - -static int mem_base = 0xc8000; -module_param(mem_base, int, 0); - -static int video_nr = -1; -module_param(video_nr, int, 0); - - -static inline void mvv_write(struct pms *dev, u8 index, u8 value) -{ - outw(index | (value << 8), dev->io); -} - -static inline u8 mvv_read(struct pms *dev, u8 index) -{ - outb(index, dev->io); - return inb(dev->data); -} - -static int pms_i2c_stat(struct pms *dev, u8 slave) -{ - int counter = 0; - int i; - - outb(0x28, dev->io); - - while ((inb(dev->data) & 0x01) == 0) - if (counter++ == 256) - break; - - while ((inb(dev->data) & 0x01) != 0) - if (counter++ == 256) - break; - - outb(slave, dev->io); - - counter = 0; - while ((inb(dev->data) & 0x01) == 0) - if (counter++ == 256) - break; - - while ((inb(dev->data) & 0x01) != 0) - if (counter++ == 256) - break; - - for (i = 0; i < 12; i++) { - char st = inb(dev->data); - - if ((st & 2) != 0) - return -1; - if ((st & 1) == 0) - break; - } - outb(0x29, dev->io); - return inb(dev->data); -} - -static int pms_i2c_write(struct pms *dev, u16 slave, u16 sub, u16 data) -{ - int skip = 0; - int count; - int i; - - for (i = 0; i < dev->i2c_count; i++) { - if ((dev->i2cinfo[i].slave == slave) && - (dev->i2cinfo[i].sub == sub)) { - if (dev->i2cinfo[i].data == data) - skip = 1; - dev->i2cinfo[i].data = data; - i = dev->i2c_count + 1; - } - } - - if (i == dev->i2c_count && dev->i2c_count < 64) { - dev->i2cinfo[dev->i2c_count].slave = slave; - dev->i2cinfo[dev->i2c_count].sub = sub; - dev->i2cinfo[dev->i2c_count].data = data; - dev->i2c_count++; - } - - if (skip) - return 0; - - mvv_write(dev, 0x29, sub); - mvv_write(dev, 0x2A, data); - mvv_write(dev, 0x28, slave); - - outb(0x28, dev->io); - - count = 0; - while ((inb(dev->data) & 1) == 0) - if (count > 255) - break; - while ((inb(dev->data) & 1) != 0) - if (count > 255) - break; - - count = inb(dev->data); - - if (count & 2) - return -1; - return count; -} - -static int pms_i2c_read(struct pms *dev, int slave, int sub) -{ - int i; - - for (i = 0; i < dev->i2c_count; i++) { - if (dev->i2cinfo[i].slave == slave && dev->i2cinfo[i].sub == sub) - return dev->i2cinfo[i].data; - } - return 0; -} - - -static void pms_i2c_andor(struct pms *dev, int slave, int sub, int and, int or) -{ - u8 tmp; - - tmp = pms_i2c_read(dev, slave, sub); - tmp = (tmp & and) | or; - pms_i2c_write(dev, slave, sub, tmp); -} - -/* - * Control functions - */ - - -static void pms_videosource(struct pms *dev, short source) -{ - switch (dev->decoder) { - case MOTOROLA: - break; - case PHILIPS2: - pms_i2c_andor(dev, 0x8a, 0x06, 0x7f, source ? 0x80 : 0); - break; - case PHILIPS1: - break; - } - mvv_write(dev, 0x2E, 0x31); - /* Was: mvv_write(dev, 0x2E, source ? 0x31 : 0x30); - But could not make this work correctly. Only Composite input - worked for me. */ -} - -static void pms_hue(struct pms *dev, short hue) -{ - switch (dev->decoder) { - case MOTOROLA: - pms_i2c_write(dev, 0x8a, 0x00, hue); - break; - case PHILIPS2: - pms_i2c_write(dev, 0x8a, 0x07, hue); - break; - case PHILIPS1: - pms_i2c_write(dev, 0x42, 0x07, hue); - break; - } -} - -static void pms_saturation(struct pms *dev, short sat) -{ - switch (dev->decoder) { - case MOTOROLA: - pms_i2c_write(dev, 0x8a, 0x00, sat); - break; - case PHILIPS1: - pms_i2c_write(dev, 0x42, 0x12, sat); - break; - } -} - - -static void pms_contrast(struct pms *dev, short contrast) -{ - switch (dev->decoder) { - case MOTOROLA: - pms_i2c_write(dev, 0x8a, 0x00, contrast); - break; - case PHILIPS1: - pms_i2c_write(dev, 0x42, 0x13, contrast); - break; - } -} - -static void pms_brightness(struct pms *dev, short brightness) -{ - switch (dev->decoder) { - case MOTOROLA: - pms_i2c_write(dev, 0x8a, 0x00, brightness); - pms_i2c_write(dev, 0x8a, 0x00, brightness); - pms_i2c_write(dev, 0x8a, 0x00, brightness); - break; - case PHILIPS1: - pms_i2c_write(dev, 0x42, 0x19, brightness); - break; - } -} - - -static void pms_format(struct pms *dev, short format) -{ - int target; - - dev->standard = format; - - if (dev->decoder == PHILIPS1) - target = 0x42; - else if (dev->decoder == PHILIPS2) - target = 0x8a; - else - return; - - switch (format) { - case 0: /* Auto */ - pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00); - pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x80); - break; - case 1: /* NTSC */ - pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00); - pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x40); - break; - case 2: /* PAL */ - pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00); - pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00); - break; - case 3: /* SECAM */ - pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x01); - pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00); - break; - } -} - -#ifdef FOR_FUTURE_EXPANSION - -/* - * These features of the PMS card are not currently exposes. They - * could become a private v4l ioctl for PMSCONFIG or somesuch if - * people need it. We also don't yet use the PMS interrupt. - */ - -static void pms_hstart(struct pms *dev, short start) -{ - switch (dev->decoder) { - case PHILIPS1: - pms_i2c_write(dev, 0x8a, 0x05, start); - pms_i2c_write(dev, 0x8a, 0x18, start); - break; - case PHILIPS2: - pms_i2c_write(dev, 0x42, 0x05, start); - pms_i2c_write(dev, 0x42, 0x18, start); - break; - } -} - -/* - * Bandpass filters - */ - -static void pms_bandpass(struct pms *dev, short pass) -{ - if (dev->decoder == PHILIPS2) - pms_i2c_andor(dev, 0x8a, 0x06, 0xcf, (pass & 0x03) << 4); - else if (dev->decoder == PHILIPS1) - pms_i2c_andor(dev, 0x42, 0x06, 0xcf, (pass & 0x03) << 4); -} - -static void pms_antisnow(struct pms *dev, short snow) -{ - if (dev->decoder == PHILIPS2) - pms_i2c_andor(dev, 0x8a, 0x06, 0xf3, (snow & 0x03) << 2); - else if (dev->decoder == PHILIPS1) - pms_i2c_andor(dev, 0x42, 0x06, 0xf3, (snow & 0x03) << 2); -} - -static void pms_sharpness(struct pms *dev, short sharp) -{ - if (dev->decoder == PHILIPS2) - pms_i2c_andor(dev, 0x8a, 0x06, 0xfc, sharp & 0x03); - else if (dev->decoder == PHILIPS1) - pms_i2c_andor(dev, 0x42, 0x06, 0xfc, sharp & 0x03); -} - -static void pms_chromaagc(struct pms *dev, short agc) -{ - if (dev->decoder == PHILIPS2) - pms_i2c_andor(dev, 0x8a, 0x0c, 0x9f, (agc & 0x03) << 5); - else if (dev->decoder == PHILIPS1) - pms_i2c_andor(dev, 0x42, 0x0c, 0x9f, (agc & 0x03) << 5); -} - -static void pms_vertnoise(struct pms *dev, short noise) -{ - if (dev->decoder == PHILIPS2) - pms_i2c_andor(dev, 0x8a, 0x10, 0xfc, noise & 3); - else if (dev->decoder == PHILIPS1) - pms_i2c_andor(dev, 0x42, 0x10, 0xfc, noise & 3); -} - -static void pms_forcecolour(struct pms *dev, short colour) -{ - if (dev->decoder == PHILIPS2) - pms_i2c_andor(dev, 0x8a, 0x0c, 0x7f, (colour & 1) << 7); - else if (dev->decoder == PHILIPS1) - pms_i2c_andor(dev, 0x42, 0x0c, 0x7, (colour & 1) << 7); -} - -static void pms_antigamma(struct pms *dev, short gamma) -{ - if (dev->decoder == PHILIPS2) - pms_i2c_andor(dev, 0xb8, 0x00, 0x7f, (gamma & 1) << 7); - else if (dev->decoder == PHILIPS1) - pms_i2c_andor(dev, 0x42, 0x20, 0x7, (gamma & 1) << 7); -} - -static void pms_prefilter(struct pms *dev, short filter) -{ - if (dev->decoder == PHILIPS2) - pms_i2c_andor(dev, 0x8a, 0x06, 0xbf, (filter & 1) << 6); - else if (dev->decoder == PHILIPS1) - pms_i2c_andor(dev, 0x42, 0x06, 0xbf, (filter & 1) << 6); -} - -static void pms_hfilter(struct pms *dev, short filter) -{ - if (dev->decoder == PHILIPS2) - pms_i2c_andor(dev, 0xb8, 0x04, 0x1f, (filter & 7) << 5); - else if (dev->decoder == PHILIPS1) - pms_i2c_andor(dev, 0x42, 0x24, 0x1f, (filter & 7) << 5); -} - -static void pms_vfilter(struct pms *dev, short filter) -{ - if (dev->decoder == PHILIPS2) - pms_i2c_andor(dev, 0xb8, 0x08, 0x9f, (filter & 3) << 5); - else if (dev->decoder == PHILIPS1) - pms_i2c_andor(dev, 0x42, 0x28, 0x9f, (filter & 3) << 5); -} - -static void pms_killcolour(struct pms *dev, short colour) -{ - if (dev->decoder == PHILIPS2) { - pms_i2c_andor(dev, 0x8a, 0x08, 0x07, (colour & 0x1f) << 3); - pms_i2c_andor(dev, 0x8a, 0x09, 0x07, (colour & 0x1f) << 3); - } else if (dev->decoder == PHILIPS1) { - pms_i2c_andor(dev, 0x42, 0x08, 0x07, (colour & 0x1f) << 3); - pms_i2c_andor(dev, 0x42, 0x09, 0x07, (colour & 0x1f) << 3); - } -} - -static void pms_chromagain(struct pms *dev, short chroma) -{ - if (dev->decoder == PHILIPS2) - pms_i2c_write(dev, 0x8a, 0x11, chroma); - else if (dev->decoder == PHILIPS1) - pms_i2c_write(dev, 0x42, 0x11, chroma); -} - - -static void pms_spacialcompl(struct pms *dev, short data) -{ - mvv_write(dev, 0x3b, data); -} - -static void pms_spacialcomph(struct pms *dev, short data) -{ - mvv_write(dev, 0x3a, data); -} - -static void pms_vstart(struct pms *dev, short start) -{ - mvv_write(dev, 0x16, start); - mvv_write(dev, 0x17, (start >> 8) & 0x01); -} - -#endif - -static void pms_secamcross(struct pms *dev, short cross) -{ - if (dev->decoder == PHILIPS2) - pms_i2c_andor(dev, 0x8a, 0x0f, 0xdf, (cross & 1) << 5); - else if (dev->decoder == PHILIPS1) - pms_i2c_andor(dev, 0x42, 0x0f, 0xdf, (cross & 1) << 5); -} - - -static void pms_swsense(struct pms *dev, short sense) -{ - if (dev->decoder == PHILIPS2) { - pms_i2c_write(dev, 0x8a, 0x0a, sense); - pms_i2c_write(dev, 0x8a, 0x0b, sense); - } else if (dev->decoder == PHILIPS1) { - pms_i2c_write(dev, 0x42, 0x0a, sense); - pms_i2c_write(dev, 0x42, 0x0b, sense); - } -} - - -static void pms_framerate(struct pms *dev, short frr) -{ - int fps = (dev->std & V4L2_STD_525_60) ? 30 : 25; - - if (frr == 0) - return; - fps = fps/frr; - mvv_write(dev, 0x14, 0x80 | fps); - mvv_write(dev, 0x15, 1); -} - -static void pms_vert(struct pms *dev, u8 deciden, u8 decinum) -{ - mvv_write(dev, 0x1c, deciden); /* Denominator */ - mvv_write(dev, 0x1d, decinum); /* Numerator */ -} - -/* - * Turn 16bit ratios into best small ratio the chipset can grok - */ - -static void pms_vertdeci(struct pms *dev, unsigned short decinum, unsigned short deciden) -{ - /* Knock it down by / 5 once */ - if (decinum % 5 == 0) { - deciden /= 5; - decinum /= 5; - } - /* - * 3's - */ - while (decinum % 3 == 0 && deciden % 3 == 0) { - deciden /= 3; - decinum /= 3; - } - /* - * 2's - */ - while (decinum % 2 == 0 && deciden % 2 == 0) { - decinum /= 2; - deciden /= 2; - } - /* - * Fudgyify - */ - while (deciden > 32) { - deciden /= 2; - decinum = (decinum + 1) / 2; - } - if (deciden == 32) - deciden--; - pms_vert(dev, deciden, decinum); -} - -static void pms_horzdeci(struct pms *dev, short decinum, short deciden) -{ - if (decinum <= 512) { - if (decinum % 5 == 0) { - decinum /= 5; - deciden /= 5; - } - } else { - decinum = 512; - deciden = 640; /* 768 would be ideal */ - } - - while (((decinum | deciden) & 1) == 0) { - decinum >>= 1; - deciden >>= 1; - } - while (deciden > 32) { - deciden >>= 1; - decinum = (decinum + 1) >> 1; - } - if (deciden == 32) - deciden--; - - mvv_write(dev, 0x24, 0x80 | deciden); - mvv_write(dev, 0x25, decinum); -} - -static void pms_resolution(struct pms *dev, short width, short height) -{ - int fg_height; - - fg_height = height; - if (fg_height > 280) - fg_height = 280; - - mvv_write(dev, 0x18, fg_height); - mvv_write(dev, 0x19, fg_height >> 8); - - if (dev->std & V4L2_STD_525_60) { - mvv_write(dev, 0x1a, 0xfc); - mvv_write(dev, 0x1b, 0x00); - if (height > fg_height) - pms_vertdeci(dev, 240, 240); - else - pms_vertdeci(dev, fg_height, 240); - } else { - mvv_write(dev, 0x1a, 0x1a); - mvv_write(dev, 0x1b, 0x01); - if (fg_height > 256) - pms_vertdeci(dev, 270, 270); - else - pms_vertdeci(dev, fg_height, 270); - } - mvv_write(dev, 0x12, 0); - mvv_write(dev, 0x13, MVVMEMORYWIDTH); - mvv_write(dev, 0x42, 0x00); - mvv_write(dev, 0x43, 0x00); - mvv_write(dev, 0x44, MVVMEMORYWIDTH); - - mvv_write(dev, 0x22, width + 8); - mvv_write(dev, 0x23, (width + 8) >> 8); - - if (dev->std & V4L2_STD_525_60) - pms_horzdeci(dev, width, 640); - else - pms_horzdeci(dev, width + 8, 768); - - mvv_write(dev, 0x30, mvv_read(dev, 0x30) & 0xfe); - mvv_write(dev, 0x08, mvv_read(dev, 0x08) | 0x01); - mvv_write(dev, 0x01, mvv_read(dev, 0x01) & 0xfd); - mvv_write(dev, 0x32, 0x00); - mvv_write(dev, 0x33, MVVMEMORYWIDTH); -} - - -/* - * Set Input - */ - -static void pms_vcrinput(struct pms *dev, short input) -{ - if (dev->decoder == PHILIPS2) - pms_i2c_andor(dev, 0x8a, 0x0d, 0x7f, (input & 1) << 7); - else if (dev->decoder == PHILIPS1) - pms_i2c_andor(dev, 0x42, 0x0d, 0x7f, (input & 1) << 7); -} - - -static int pms_capture(struct pms *dev, char __user *buf, int rgb555, int count) -{ - int y; - int dw = 2 * dev->width; - char *tmp; /* using a temp buffer is faster than direct */ - int cnt = 0; - int len = 0; - unsigned char r8 = 0x5; /* value for reg8 */ - - tmp = kmalloc(dw + 32, GFP_KERNEL); - if (!tmp) - return 0; - - if (rgb555) - r8 |= 0x20; /* else use untranslated rgb = 565 */ - mvv_write(dev, 0x08, r8); /* capture rgb555/565, init DRAM, PC enable */ - -/* printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */ - - for (y = 0; y < dev->height; y++) { - writeb(0, dev->mem); /* synchronisiert neue Zeile */ - - /* - * This is in truth a fifo, be very careful as if you - * forgot this odd things will occur 8) - */ - - memcpy_fromio(tmp, dev->mem, dw + 32); /* discard 16 word */ - cnt -= dev->height; - while (cnt <= 0) { - /* - * Don't copy too far - */ - int dt = dw; - if (dt + len > count) - dt = count - len; - cnt += dev->height; - if (copy_to_user(buf, tmp + 32, dt)) - return len ? len : -EFAULT; - buf += dt; - len += dt; - } - } - kfree(tmp); - return len; -} - - -/* - * Video4linux interfacing - */ - -static int pms_querycap(struct file *file, void *priv, - struct v4l2_capability *vcap) -{ - struct pms *dev = video_drvdata(file); - - strlcpy(vcap->driver, dev->v4l2_dev.name, sizeof(vcap->driver)); - strlcpy(vcap->card, "Mediavision PMS", sizeof(vcap->card)); - snprintf(vcap->bus_info, sizeof(vcap->bus_info), - "ISA:%s", dev->v4l2_dev.name); - vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; - vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -static int pms_enum_input(struct file *file, void *fh, struct v4l2_input *vin) -{ - static const char *inputs[4] = { - "Composite", - "S-Video", - "Composite (VCR)", - "S-Video (VCR)" - }; - - if (vin->index > 3) - return -EINVAL; - strlcpy(vin->name, inputs[vin->index], sizeof(vin->name)); - vin->type = V4L2_INPUT_TYPE_CAMERA; - vin->audioset = 0; - vin->tuner = 0; - vin->std = V4L2_STD_ALL; - vin->status = 0; - return 0; -} - -static int pms_g_input(struct file *file, void *fh, unsigned int *inp) -{ - struct pms *dev = video_drvdata(file); - - *inp = dev->input; - return 0; -} - -static int pms_s_input(struct file *file, void *fh, unsigned int inp) -{ - struct pms *dev = video_drvdata(file); - - if (inp > 3) - return -EINVAL; - - dev->input = inp; - pms_videosource(dev, inp & 1); - pms_vcrinput(dev, inp >> 1); - return 0; -} - -static int pms_g_std(struct file *file, void *fh, v4l2_std_id *std) -{ - struct pms *dev = video_drvdata(file); - - *std = dev->std; - return 0; -} - -static int pms_s_std(struct file *file, void *fh, v4l2_std_id std) -{ - struct pms *dev = video_drvdata(file); - int ret = 0; - - dev->std = std; - if (dev->std & V4L2_STD_NTSC) { - pms_framerate(dev, 30); - pms_secamcross(dev, 0); - pms_format(dev, 1); - } else if (dev->std & V4L2_STD_PAL) { - pms_framerate(dev, 25); - pms_secamcross(dev, 0); - pms_format(dev, 2); - } else if (dev->std & V4L2_STD_SECAM) { - pms_framerate(dev, 25); - pms_secamcross(dev, 1); - pms_format(dev, 2); - } else { - ret = -EINVAL; - } - /* - switch (v->mode) { - case VIDEO_MODE_AUTO: - pms_framerate(dev, 25); - pms_secamcross(dev, 0); - pms_format(dev, 0); - break; - }*/ - return ret; -} - -static int pms_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct pms *dev = container_of(ctrl->handler, struct pms, hdl); - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - pms_brightness(dev, ctrl->val); - break; - case V4L2_CID_CONTRAST: - pms_contrast(dev, ctrl->val); - break; - case V4L2_CID_SATURATION: - pms_saturation(dev, ctrl->val); - break; - case V4L2_CID_HUE: - pms_hue(dev, ctrl->val); - break; - default: - ret = -EINVAL; - break; - } - return ret; -} - -static int pms_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) -{ - struct pms *dev = video_drvdata(file); - struct v4l2_pix_format *pix = &fmt->fmt.pix; - - pix->width = dev->width; - pix->height = dev->height; - pix->pixelformat = dev->width == 15 ? - V4L2_PIX_FMT_RGB555 : V4L2_PIX_FMT_RGB565; - pix->field = V4L2_FIELD_NONE; - pix->bytesperline = 2 * dev->width; - pix->sizeimage = 2 * dev->width * dev->height; - /* Just a guess */ - pix->colorspace = V4L2_COLORSPACE_SRGB; - return 0; -} - -static int pms_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) -{ - struct v4l2_pix_format *pix = &fmt->fmt.pix; - - if (pix->height < 16 || pix->height > 480) - return -EINVAL; - if (pix->width < 16 || pix->width > 640) - return -EINVAL; - if (pix->pixelformat != V4L2_PIX_FMT_RGB555 && - pix->pixelformat != V4L2_PIX_FMT_RGB565) - return -EINVAL; - pix->field = V4L2_FIELD_NONE; - pix->bytesperline = 2 * pix->width; - pix->sizeimage = 2 * pix->width * pix->height; - /* Just a guess */ - pix->colorspace = V4L2_COLORSPACE_SRGB; - return 0; -} - -static int pms_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) -{ - struct pms *dev = video_drvdata(file); - struct v4l2_pix_format *pix = &fmt->fmt.pix; - int ret = pms_try_fmt_vid_cap(file, fh, fmt); - - if (ret) - return ret; - dev->width = pix->width; - dev->height = pix->height; - dev->depth = (pix->pixelformat == V4L2_PIX_FMT_RGB555) ? 15 : 16; - pms_resolution(dev, dev->width, dev->height); - /* Ok we figured out what to use from our wide choice */ - return 0; -} - -static int pms_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) -{ - static struct v4l2_fmtdesc formats[] = { - { 0, 0, 0, - "RGB 5:5:5", V4L2_PIX_FMT_RGB555, - { 0, 0, 0, 0 } - }, - { 1, 0, 0, - "RGB 5:6:5", V4L2_PIX_FMT_RGB565, - { 0, 0, 0, 0 } - }, - }; - enum v4l2_buf_type type = fmt->type; - - if (fmt->index > 1) - return -EINVAL; - - *fmt = formats[fmt->index]; - fmt->type = type; - return 0; -} - -static ssize_t pms_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct pms *dev = video_drvdata(file); - int len; - - len = pms_capture(dev, buf, (dev->depth == 15), count); - return len; -} - -static unsigned int pms_poll(struct file *file, struct poll_table_struct *wait) -{ - struct v4l2_fh *fh = file->private_data; - unsigned int res = POLLIN | POLLRDNORM; - - if (v4l2_event_pending(fh)) - res |= POLLPRI; - poll_wait(file, &fh->wait, wait); - return res; -} - -static const struct v4l2_file_operations pms_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = v4l2_fh_release, - .poll = pms_poll, - .unlocked_ioctl = video_ioctl2, - .read = pms_read, -}; - -static const struct v4l2_ioctl_ops pms_ioctl_ops = { - .vidioc_querycap = pms_querycap, - .vidioc_g_input = pms_g_input, - .vidioc_s_input = pms_s_input, - .vidioc_enum_input = pms_enum_input, - .vidioc_g_std = pms_g_std, - .vidioc_s_std = pms_s_std, - .vidioc_enum_fmt_vid_cap = pms_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = pms_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = pms_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = pms_try_fmt_vid_cap, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -/* - * Probe for and initialise the Mediavision PMS - */ - -static int init_mediavision(struct pms *dev) -{ - int idec, decst; - int i; - static const unsigned char i2c_defs[] = { - 0x4c, 0x30, 0x00, 0xe8, - 0xb6, 0xe2, 0x00, 0x00, - 0xff, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x78, 0x98, - 0x00, 0x00, 0x00, 0x00, - 0x34, 0x0a, 0xf4, 0xce, - 0xe4 - }; - - dev->mem = ioremap(mem_base, 0x800); - if (!dev->mem) - return -ENOMEM; - - if (!request_region(0x9a01, 1, "Mediavision PMS config")) { - printk(KERN_WARNING "mediavision: unable to detect: 0x9a01 in use.\n"); - iounmap(dev->mem); - return -EBUSY; - } - if (!request_region(dev->io, 3, "Mediavision PMS")) { - printk(KERN_WARNING "mediavision: I/O port %d in use.\n", dev->io); - release_region(0x9a01, 1); - iounmap(dev->mem); - return -EBUSY; - } - outb(0xb8, 0x9a01); /* Unlock */ - outb(dev->io >> 4, 0x9a01); /* Set IO port */ - - - decst = pms_i2c_stat(dev, 0x43); - - if (decst != -1) - idec = 2; - else if (pms_i2c_stat(dev, 0xb9) != -1) - idec = 3; - else if (pms_i2c_stat(dev, 0x8b) != -1) - idec = 1; - else - idec = 0; - - printk(KERN_INFO "PMS type is %d\n", idec); - if (idec == 0) { - release_region(dev->io, 3); - release_region(0x9a01, 1); - iounmap(dev->mem); - return -ENODEV; - } - - /* - * Ok we have a PMS of some sort - */ - - mvv_write(dev, 0x04, mem_base >> 12); /* Set the memory area */ - - /* Ok now load the defaults */ - - for (i = 0; i < 0x19; i++) { - if (i2c_defs[i] == 0xff) - pms_i2c_andor(dev, 0x8a, i, 0x07, 0x00); - else - pms_i2c_write(dev, 0x8a, i, i2c_defs[i]); - } - - pms_i2c_write(dev, 0xb8, 0x00, 0x12); - pms_i2c_write(dev, 0xb8, 0x04, 0x00); - pms_i2c_write(dev, 0xb8, 0x07, 0x00); - pms_i2c_write(dev, 0xb8, 0x08, 0x00); - pms_i2c_write(dev, 0xb8, 0x09, 0xff); - pms_i2c_write(dev, 0xb8, 0x0a, 0x00); - pms_i2c_write(dev, 0xb8, 0x0b, 0x10); - pms_i2c_write(dev, 0xb8, 0x10, 0x03); - - mvv_write(dev, 0x01, 0x00); - mvv_write(dev, 0x05, 0xa0); - mvv_write(dev, 0x08, 0x25); - mvv_write(dev, 0x09, 0x00); - mvv_write(dev, 0x0a, 0x20 | MVVMEMORYWIDTH); - - mvv_write(dev, 0x10, 0x02); - mvv_write(dev, 0x1e, 0x0c); - mvv_write(dev, 0x1f, 0x03); - mvv_write(dev, 0x26, 0x06); - - mvv_write(dev, 0x2b, 0x00); - mvv_write(dev, 0x2c, 0x20); - mvv_write(dev, 0x2d, 0x00); - mvv_write(dev, 0x2f, 0x70); - mvv_write(dev, 0x32, 0x00); - mvv_write(dev, 0x33, MVVMEMORYWIDTH); - mvv_write(dev, 0x34, 0x00); - mvv_write(dev, 0x35, 0x00); - mvv_write(dev, 0x3a, 0x80); - mvv_write(dev, 0x3b, 0x10); - mvv_write(dev, 0x20, 0x00); - mvv_write(dev, 0x21, 0x00); - mvv_write(dev, 0x30, 0x22); - return 0; -} - -/* - * Initialization and module stuff - */ - -#ifndef MODULE -static int enable; -module_param(enable, int, 0); -#endif - -static const struct v4l2_ctrl_ops pms_ctrl_ops = { - .s_ctrl = pms_s_ctrl, -}; - -static int pms_probe(struct device *pdev, unsigned int card) -{ - struct pms *dev; - struct v4l2_device *v4l2_dev; - struct v4l2_ctrl_handler *hdl; - int res; - -#ifndef MODULE - if (!enable) { - pr_err("PMS: not enabled, use pms.enable=1 to probe\n"); - return -ENODEV; - } -#endif - - dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (dev == NULL) - return -ENOMEM; - - dev->decoder = PHILIPS2; - dev->io = io_port; - dev->data = io_port + 1; - v4l2_dev = &dev->v4l2_dev; - hdl = &dev->hdl; - - res = v4l2_device_register(pdev, v4l2_dev); - if (res < 0) { - v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); - goto free_dev; - } - v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.05\n"); - - res = init_mediavision(dev); - if (res) { - v4l2_err(v4l2_dev, "Board not found.\n"); - goto free_io; - } - - v4l2_ctrl_handler_init(hdl, 4); - v4l2_ctrl_new_std(hdl, &pms_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 139); - v4l2_ctrl_new_std(hdl, &pms_ctrl_ops, - V4L2_CID_CONTRAST, 0, 255, 1, 70); - v4l2_ctrl_new_std(hdl, &pms_ctrl_ops, - V4L2_CID_SATURATION, 0, 255, 1, 64); - v4l2_ctrl_new_std(hdl, &pms_ctrl_ops, - V4L2_CID_HUE, 0, 255, 1, 0); - if (hdl->error) { - res = hdl->error; - goto free_hdl; - } - - mutex_init(&dev->lock); - strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); - dev->vdev.v4l2_dev = v4l2_dev; - dev->vdev.ctrl_handler = hdl; - dev->vdev.fops = &pms_fops; - dev->vdev.ioctl_ops = &pms_ioctl_ops; - dev->vdev.release = video_device_release_empty; - dev->vdev.lock = &dev->lock; - dev->vdev.tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; - video_set_drvdata(&dev->vdev, dev); - dev->std = V4L2_STD_NTSC_M; - dev->height = 240; - dev->width = 320; - dev->depth = 16; - pms_swsense(dev, 75); - pms_resolution(dev, 320, 240); - pms_videosource(dev, 0); - pms_vcrinput(dev, 0); - v4l2_ctrl_handler_setup(hdl); - res = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr); - if (res >= 0) - return 0; - -free_hdl: - v4l2_ctrl_handler_free(hdl); - v4l2_device_unregister(&dev->v4l2_dev); -free_io: - release_region(dev->io, 3); - release_region(0x9a01, 1); - iounmap(dev->mem); -free_dev: - kfree(dev); - return res; -} - -static int pms_remove(struct device *pdev, unsigned int card) -{ - struct pms *dev = dev_get_drvdata(pdev); - - video_unregister_device(&dev->vdev); - v4l2_ctrl_handler_free(&dev->hdl); - release_region(dev->io, 3); - release_region(0x9a01, 1); - iounmap(dev->mem); - return 0; -} - -static struct isa_driver pms_driver = { - .probe = pms_probe, - .remove = pms_remove, - .driver = { - .name = "pms", - }, -}; - -static int __init pms_init(void) -{ - return isa_register_driver(&pms_driver, 1); -} - -static void __exit pms_exit(void) -{ - isa_unregister_driver(&pms_driver); -} - -module_init(pms_init); -module_exit(pms_exit); diff --git a/drivers/staging/media/parport/w9966.c b/drivers/staging/media/parport/w9966.c deleted file mode 100644 index f7502f3a6a3c..000000000000 --- a/drivers/staging/media/parport/w9966.c +++ /dev/null @@ -1,980 +0,0 @@ -/* - Winbond w9966cf Webcam parport driver. - - Version 0.33 - - Copyright (C) 2001 Jakob Kemi <jakob.kemi@post.utfors.se> - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ -/* - Supported devices: - *Lifeview FlyCam Supra (using the Philips saa7111a chip) - - Does any other model using the w9966 interface chip exist ? - - Todo: - - *Add a working EPP mode, since DMA ECP read isn't implemented - in the parport drivers. (That's why it's so sloow) - - *Add support for other ccd-control chips than the saa7111 - please send me feedback on what kind of chips you have. - - *Add proper probing. I don't know what's wrong with the IEEE1284 - parport drivers but (IEEE1284_MODE_NIBBLE|IEEE1284_DEVICE_ID) - and nibble read seems to be broken for some peripherals. - - *Add probing for onboard SRAM, port directions etc. (if possible) - - *Add support for the hardware compressed modes (maybe using v4l2) - - *Fix better support for the capture window (no skewed images, v4l - interface to capt. window) - - *Probably some bugs that I don't know of - - Please support me by sending feedback! - - Changes: - - Alan Cox: Removed RGB mode for kernel merge, added THIS_MODULE - and owner support for newer module locks -*/ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/videodev2.h> -#include <linux/slab.h> -#include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-device.h> -#include <media/v4l2-fh.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-event.h> -#include <linux/parport.h> - -/*#define DEBUG*/ /* Undef me for production */ - -#ifdef DEBUG -#define DPRINTF(x, a...) printk(KERN_DEBUG "W9966: %s(): "x, __func__ , ##a) -#else -#define DPRINTF(x...) -#endif - -/* - * Defines, simple typedefs etc. - */ - -#define W9966_DRIVERNAME "W9966CF Webcam" -#define W9966_MAXCAMS 4 /* Maximum number of cameras */ -#define W9966_RBUFFER 2048 /* Read buffer (must be an even number) */ -#define W9966_SRAMSIZE 131072 /* 128kb */ -#define W9966_SRAMID 0x02 /* check w9966cf.pdf */ - -/* Empirically determined window limits */ -#define W9966_WND_MIN_X 16 -#define W9966_WND_MIN_Y 14 -#define W9966_WND_MAX_X 705 -#define W9966_WND_MAX_Y 253 -#define W9966_WND_MAX_W (W9966_WND_MAX_X - W9966_WND_MIN_X) -#define W9966_WND_MAX_H (W9966_WND_MAX_Y - W9966_WND_MIN_Y) - -/* Keep track of our current state */ -#define W9966_STATE_PDEV 0x01 -#define W9966_STATE_CLAIMED 0x02 -#define W9966_STATE_VDEV 0x04 - -#define W9966_I2C_W_ID 0x48 -#define W9966_I2C_R_ID 0x49 -#define W9966_I2C_R_DATA 0x08 -#define W9966_I2C_R_CLOCK 0x04 -#define W9966_I2C_W_DATA 0x02 -#define W9966_I2C_W_CLOCK 0x01 - -struct w9966 { - struct v4l2_device v4l2_dev; - struct v4l2_ctrl_handler hdl; - unsigned char dev_state; - unsigned char i2c_state; - unsigned short ppmode; - struct parport *pport; - struct pardevice *pdev; - struct video_device vdev; - unsigned short width; - unsigned short height; - unsigned char brightness; - signed char contrast; - signed char color; - signed char hue; - struct mutex lock; -}; - -/* - * Module specific properties - */ - -MODULE_AUTHOR("Jakob Kemi <jakob.kemi@post.utfors.se>"); -MODULE_DESCRIPTION("Winbond w9966cf WebCam driver (0.32)"); -MODULE_LICENSE("GPL"); -MODULE_VERSION("0.33.1"); - -#ifdef MODULE -static char *pardev[] = {[0 ... W9966_MAXCAMS] = ""}; -#else -static char *pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"}; -#endif -module_param_array(pardev, charp, NULL, 0); -MODULE_PARM_DESC(pardev, "pardev: where to search for\n" - "\teach camera. 'aggressive' means brute-force search.\n" - "\tEg: >pardev=parport3,aggressive,parport2,parport1< would assign\n" - "\tcam 1 to parport3 and search every parport for cam 2 etc..."); - -static int parmode; -module_param(parmode, int, 0); -MODULE_PARM_DESC(parmode, "parmode: transfer mode (0=auto, 1=ecp, 2=epp"); - -static int video_nr = -1; -module_param(video_nr, int, 0); - -static struct w9966 w9966_cams[W9966_MAXCAMS]; - -/* - * Private function defines - */ - - -/* Set camera phase flags, so we know what to uninit when terminating */ -static inline void w9966_set_state(struct w9966 *cam, int mask, int val) -{ - cam->dev_state = (cam->dev_state & ~mask) ^ val; -} - -/* Get camera phase flags */ -static inline int w9966_get_state(struct w9966 *cam, int mask, int val) -{ - return ((cam->dev_state & mask) == val); -} - -/* Claim parport for ourself */ -static void w9966_pdev_claim(struct w9966 *cam) -{ - if (w9966_get_state(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED)) - return; - parport_claim_or_block(cam->pdev); - w9966_set_state(cam, W9966_STATE_CLAIMED, W9966_STATE_CLAIMED); -} - -/* Release parport for others to use */ -static void w9966_pdev_release(struct w9966 *cam) -{ - if (w9966_get_state(cam, W9966_STATE_CLAIMED, 0)) - return; - parport_release(cam->pdev); - w9966_set_state(cam, W9966_STATE_CLAIMED, 0); -} - -/* Read register from W9966 interface-chip - Expects a claimed pdev - -1 on error, else register data (byte) */ -static int w9966_read_reg(struct w9966 *cam, int reg) -{ - /* ECP, read, regtransfer, REG, REG, REG, REG, REG */ - const unsigned char addr = 0x80 | (reg & 0x1f); - unsigned char val; - - if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0) - return -1; - if (parport_write(cam->pport, &addr, 1) != 1) - return -1; - if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0) - return -1; - if (parport_read(cam->pport, &val, 1) != 1) - return -1; - - return val; -} - -/* Write register to W9966 interface-chip - Expects a claimed pdev - -1 on error */ -static int w9966_write_reg(struct w9966 *cam, int reg, int data) -{ - /* ECP, write, regtransfer, REG, REG, REG, REG, REG */ - const unsigned char addr = 0xc0 | (reg & 0x1f); - const unsigned char val = data; - - if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_ADDR) != 0) - return -1; - if (parport_write(cam->pport, &addr, 1) != 1) - return -1; - if (parport_negotiate(cam->pport, cam->ppmode | IEEE1284_DATA) != 0) - return -1; - if (parport_write(cam->pport, &val, 1) != 1) - return -1; - - return 0; -} - -/* - * Ugly and primitive i2c protocol functions - */ - -/* Sets the data line on the i2c bus. - Expects a claimed pdev. */ -static void w9966_i2c_setsda(struct w9966 *cam, int state) -{ - if (state) - cam->i2c_state |= W9966_I2C_W_DATA; - else - cam->i2c_state &= ~W9966_I2C_W_DATA; - - w9966_write_reg(cam, 0x18, cam->i2c_state); - udelay(5); -} - -/* Get peripheral clock line - Expects a claimed pdev. */ -static int w9966_i2c_getscl(struct w9966 *cam) -{ - const unsigned char state = w9966_read_reg(cam, 0x18); - return ((state & W9966_I2C_R_CLOCK) > 0); -} - -/* Sets the clock line on the i2c bus. - Expects a claimed pdev. -1 on error */ -static int w9966_i2c_setscl(struct w9966 *cam, int state) -{ - unsigned long timeout; - - if (state) - cam->i2c_state |= W9966_I2C_W_CLOCK; - else - cam->i2c_state &= ~W9966_I2C_W_CLOCK; - - w9966_write_reg(cam, 0x18, cam->i2c_state); - udelay(5); - - /* we go to high, we also expect the peripheral to ack. */ - if (state) { - timeout = jiffies + 100; - while (!w9966_i2c_getscl(cam)) { - if (time_after(jiffies, timeout)) - return -1; - } - } - return 0; -} - -#if 0 -/* Get peripheral data line - Expects a claimed pdev. */ -static int w9966_i2c_getsda(struct w9966 *cam) -{ - const unsigned char state = w9966_read_reg(cam, 0x18); - return ((state & W9966_I2C_R_DATA) > 0); -} -#endif - -/* Write a byte with ack to the i2c bus. - Expects a claimed pdev. -1 on error */ -static int w9966_i2c_wbyte(struct w9966 *cam, int data) -{ - int i; - - for (i = 7; i >= 0; i--) { - w9966_i2c_setsda(cam, (data >> i) & 0x01); - - if (w9966_i2c_setscl(cam, 1) == -1) - return -1; - w9966_i2c_setscl(cam, 0); - } - - w9966_i2c_setsda(cam, 1); - - if (w9966_i2c_setscl(cam, 1) == -1) - return -1; - w9966_i2c_setscl(cam, 0); - - return 0; -} - -/* Read a data byte with ack from the i2c-bus - Expects a claimed pdev. -1 on error */ -#if 0 -static int w9966_i2c_rbyte(struct w9966 *cam) -{ - unsigned char data = 0x00; - int i; - - w9966_i2c_setsda(cam, 1); - - for (i = 0; i < 8; i++) { - if (w9966_i2c_setscl(cam, 1) == -1) - return -1; - data = data << 1; - if (w9966_i2c_getsda(cam)) - data |= 0x01; - - w9966_i2c_setscl(cam, 0); - } - return data; -} -#endif - -/* Read a register from the i2c device. - Expects claimed pdev. -1 on error */ -#if 0 -static int w9966_read_reg_i2c(struct w9966 *cam, int reg) -{ - int data; - - w9966_i2c_setsda(cam, 0); - w9966_i2c_setscl(cam, 0); - - if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 || - w9966_i2c_wbyte(cam, reg) == -1) - return -1; - - w9966_i2c_setsda(cam, 1); - if (w9966_i2c_setscl(cam, 1) == -1) - return -1; - w9966_i2c_setsda(cam, 0); - w9966_i2c_setscl(cam, 0); - - if (w9966_i2c_wbyte(cam, W9966_I2C_R_ID) == -1) - return -1; - data = w9966_i2c_rbyte(cam); - if (data == -1) - return -1; - - w9966_i2c_setsda(cam, 0); - - if (w9966_i2c_setscl(cam, 1) == -1) - return -1; - w9966_i2c_setsda(cam, 1); - - return data; -} -#endif - -/* Write a register to the i2c device. - Expects claimed pdev. -1 on error */ -static int w9966_write_reg_i2c(struct w9966 *cam, int reg, int data) -{ - w9966_i2c_setsda(cam, 0); - w9966_i2c_setscl(cam, 0); - - if (w9966_i2c_wbyte(cam, W9966_I2C_W_ID) == -1 || - w9966_i2c_wbyte(cam, reg) == -1 || - w9966_i2c_wbyte(cam, data) == -1) - return -1; - - w9966_i2c_setsda(cam, 0); - if (w9966_i2c_setscl(cam, 1) == -1) - return -1; - - w9966_i2c_setsda(cam, 1); - - return 0; -} - -/* Find a good length for capture window (used both for W and H) - A bit ugly but pretty functional. The capture length - have to match the downscale */ -static int w9966_findlen(int near, int size, int maxlen) -{ - int bestlen = size; - int besterr = abs(near - bestlen); - int len; - - for (len = size + 1; len < maxlen; len++) { - int err; - if (((64 * size) % len) != 0) - continue; - - err = abs(near - len); - - /* Only continue as long as we keep getting better values */ - if (err > besterr) - break; - - besterr = err; - bestlen = len; - } - - return bestlen; -} - -/* Modify capture window (if necessary) - and calculate downscaling - Return -1 on error */ -static int w9966_calcscale(int size, int min, int max, int *beg, int *end, unsigned char *factor) -{ - int maxlen = max - min; - int len = *end - *beg + 1; - int newlen = w9966_findlen(len, size, maxlen); - int err = newlen - len; - - /* Check for bad format */ - if (newlen > maxlen || newlen < size) - return -1; - - /* Set factor (6 bit fixed) */ - *factor = (64 * size) / newlen; - if (*factor == 64) - *factor = 0x00; /* downscale is disabled */ - else - *factor |= 0x80; /* set downscale-enable bit */ - - /* Modify old beginning and end */ - *beg -= err / 2; - *end += err - (err / 2); - - /* Move window if outside borders */ - if (*beg < min) { - *end += min - *beg; - *beg += min - *beg; - } - if (*end > max) { - *beg -= *end - max; - *end -= *end - max; - } - - return 0; -} - -/* Setup the cameras capture window etc. - Expects a claimed pdev - return -1 on error */ -static int w9966_setup(struct w9966 *cam, int x1, int y1, int x2, int y2, int w, int h) -{ - unsigned int i; - unsigned int enh_s, enh_e; - unsigned char scale_x, scale_y; - unsigned char regs[0x1c]; - unsigned char saa7111_regs[] = { - 0x21, 0x00, 0xd8, 0x23, 0x00, 0x80, 0x80, 0x00, - 0x88, 0x10, 0x80, 0x40, 0x40, 0x00, 0x01, 0x00, - 0x48, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x71, 0xe7, 0x00, 0x00, 0xc0 - }; - - - if (w * h * 2 > W9966_SRAMSIZE) { - DPRINTF("capture window exceeds SRAM size!.\n"); - w = 200; h = 160; /* Pick default values */ - } - - w &= ~0x1; - if (w < 2) - w = 2; - if (h < 1) - h = 1; - if (w > W9966_WND_MAX_W) - w = W9966_WND_MAX_W; - if (h > W9966_WND_MAX_H) - h = W9966_WND_MAX_H; - - cam->width = w; - cam->height = h; - - enh_s = 0; - enh_e = w * h * 2; - - /* Modify capture window if necessary and calculate downscaling */ - if (w9966_calcscale(w, W9966_WND_MIN_X, W9966_WND_MAX_X, &x1, &x2, &scale_x) != 0 || - w9966_calcscale(h, W9966_WND_MIN_Y, W9966_WND_MAX_Y, &y1, &y2, &scale_y) != 0) - return -1; - - DPRINTF("%dx%d, x: %d<->%d, y: %d<->%d, sx: %d/64, sy: %d/64.\n", - w, h, x1, x2, y1, y2, scale_x & ~0x80, scale_y & ~0x80); - - /* Setup registers */ - regs[0x00] = 0x00; /* Set normal operation */ - regs[0x01] = 0x18; /* Capture mode */ - regs[0x02] = scale_y; /* V-scaling */ - regs[0x03] = scale_x; /* H-scaling */ - - /* Capture window */ - regs[0x04] = (x1 & 0x0ff); /* X-start (8 low bits) */ - regs[0x05] = (x1 & 0x300)>>8; /* X-start (2 high bits) */ - regs[0x06] = (y1 & 0x0ff); /* Y-start (8 low bits) */ - regs[0x07] = (y1 & 0x300)>>8; /* Y-start (2 high bits) */ - regs[0x08] = (x2 & 0x0ff); /* X-end (8 low bits) */ - regs[0x09] = (x2 & 0x300)>>8; /* X-end (2 high bits) */ - regs[0x0a] = (y2 & 0x0ff); /* Y-end (8 low bits) */ - - regs[0x0c] = W9966_SRAMID; /* SRAM-banks (1x 128kb) */ - - /* Enhancement layer */ - regs[0x0d] = (enh_s & 0x000ff); /* Enh. start (0-7) */ - regs[0x0e] = (enh_s & 0x0ff00) >> 8; /* Enh. start (8-15) */ - regs[0x0f] = (enh_s & 0x70000) >> 16; /* Enh. start (16-17/18??) */ - regs[0x10] = (enh_e & 0x000ff); /* Enh. end (0-7) */ - regs[0x11] = (enh_e & 0x0ff00) >> 8; /* Enh. end (8-15) */ - regs[0x12] = (enh_e & 0x70000) >> 16; /* Enh. end (16-17/18??) */ - - /* Misc */ - regs[0x13] = 0x40; /* VEE control (raw 4:2:2) */ - regs[0x17] = 0x00; /* ??? */ - regs[0x18] = cam->i2c_state = 0x00; /* Serial bus */ - regs[0x19] = 0xff; /* I/O port direction control */ - regs[0x1a] = 0xff; /* I/O port data register */ - regs[0x1b] = 0x10; /* ??? */ - - /* SAA7111 chip settings */ - saa7111_regs[0x0a] = cam->brightness; - saa7111_regs[0x0b] = cam->contrast; - saa7111_regs[0x0c] = cam->color; - saa7111_regs[0x0d] = cam->hue; - - /* Reset (ECP-fifo & serial-bus) */ - if (w9966_write_reg(cam, 0x00, 0x03) == -1) - return -1; - - /* Write regs to w9966cf chip */ - for (i = 0; i < 0x1c; i++) - if (w9966_write_reg(cam, i, regs[i]) == -1) - return -1; - - /* Write regs to saa7111 chip */ - for (i = 0; i < 0x20; i++) - if (w9966_write_reg_i2c(cam, i, saa7111_regs[i]) == -1) - return -1; - - return 0; -} - -/* - * Video4linux interfacing - */ - -static int cam_querycap(struct file *file, void *priv, - struct v4l2_capability *vcap) -{ - struct w9966 *cam = video_drvdata(file); - - strlcpy(vcap->driver, cam->v4l2_dev.name, sizeof(vcap->driver)); - strlcpy(vcap->card, W9966_DRIVERNAME, sizeof(vcap->card)); - strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info)); - vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; - vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -static int cam_enum_input(struct file *file, void *fh, struct v4l2_input *vin) -{ - if (vin->index > 0) - return -EINVAL; - strlcpy(vin->name, "Camera", sizeof(vin->name)); - vin->type = V4L2_INPUT_TYPE_CAMERA; - vin->audioset = 0; - vin->tuner = 0; - vin->std = 0; - vin->status = 0; - return 0; -} - -static int cam_g_input(struct file *file, void *fh, unsigned int *inp) -{ - *inp = 0; - return 0; -} - -static int cam_s_input(struct file *file, void *fh, unsigned int inp) -{ - return (inp > 0) ? -EINVAL : 0; -} - -static int cam_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct w9966 *cam = - container_of(ctrl->handler, struct w9966, hdl); - int ret = 0; - - mutex_lock(&cam->lock); - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - cam->brightness = ctrl->val; - break; - case V4L2_CID_CONTRAST: - cam->contrast = ctrl->val; - break; - case V4L2_CID_SATURATION: - cam->color = ctrl->val; - break; - case V4L2_CID_HUE: - cam->hue = ctrl->val; - break; - default: - ret = -EINVAL; - break; - } - - if (ret == 0) { - w9966_pdev_claim(cam); - - if (w9966_write_reg_i2c(cam, 0x0a, cam->brightness) == -1 || - w9966_write_reg_i2c(cam, 0x0b, cam->contrast) == -1 || - w9966_write_reg_i2c(cam, 0x0c, cam->color) == -1 || - w9966_write_reg_i2c(cam, 0x0d, cam->hue) == -1) { - ret = -EIO; - } - - w9966_pdev_release(cam); - } - mutex_unlock(&cam->lock); - return ret; -} - -static int cam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) -{ - struct w9966 *cam = video_drvdata(file); - struct v4l2_pix_format *pix = &fmt->fmt.pix; - - pix->width = cam->width; - pix->height = cam->height; - pix->pixelformat = V4L2_PIX_FMT_YUYV; - pix->field = V4L2_FIELD_NONE; - pix->bytesperline = 2 * cam->width; - pix->sizeimage = 2 * cam->width * cam->height; - /* Just a guess */ - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - return 0; -} - -static int cam_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) -{ - struct v4l2_pix_format *pix = &fmt->fmt.pix; - - if (pix->width < 2) - pix->width = 2; - if (pix->height < 1) - pix->height = 1; - if (pix->width > W9966_WND_MAX_W) - pix->width = W9966_WND_MAX_W; - if (pix->height > W9966_WND_MAX_H) - pix->height = W9966_WND_MAX_H; - pix->pixelformat = V4L2_PIX_FMT_YUYV; - pix->field = V4L2_FIELD_NONE; - pix->bytesperline = 2 * pix->width; - pix->sizeimage = 2 * pix->width * pix->height; - /* Just a guess */ - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - return 0; -} - -static int cam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) -{ - struct w9966 *cam = video_drvdata(file); - struct v4l2_pix_format *pix = &fmt->fmt.pix; - int ret = cam_try_fmt_vid_cap(file, fh, fmt); - - if (ret) - return ret; - - mutex_lock(&cam->lock); - /* Update camera regs */ - w9966_pdev_claim(cam); - ret = w9966_setup(cam, 0, 0, 1023, 1023, pix->width, pix->height); - w9966_pdev_release(cam); - mutex_unlock(&cam->lock); - return ret; -} - -static int cam_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) -{ - static struct v4l2_fmtdesc formats[] = { - { 0, 0, 0, - "YUV 4:2:2", V4L2_PIX_FMT_YUYV, - { 0, 0, 0, 0 } - }, - }; - enum v4l2_buf_type type = fmt->type; - - if (fmt->index > 0) - return -EINVAL; - - *fmt = formats[fmt->index]; - fmt->type = type; - return 0; -} - -/* Capture data */ -static ssize_t w9966_v4l_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct w9966 *cam = video_drvdata(file); - unsigned char addr = 0xa0; /* ECP, read, CCD-transfer, 00000 */ - unsigned char __user *dest = (unsigned char __user *)buf; - unsigned long dleft = count; - unsigned char *tbuf; - - /* Why would anyone want more than this?? */ - if (count > cam->width * cam->height * 2) - return -EINVAL; - - mutex_lock(&cam->lock); - w9966_pdev_claim(cam); - w9966_write_reg(cam, 0x00, 0x02); /* Reset ECP-FIFO buffer */ - w9966_write_reg(cam, 0x00, 0x00); /* Return to normal operation */ - w9966_write_reg(cam, 0x01, 0x98); /* Enable capture */ - - /* write special capture-addr and negotiate into data transfer */ - if ((parport_negotiate(cam->pport, cam->ppmode|IEEE1284_ADDR) != 0) || - (parport_write(cam->pport, &addr, 1) != 1) || - (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_DATA) != 0)) { - w9966_pdev_release(cam); - mutex_unlock(&cam->lock); - return -EFAULT; - } - - tbuf = kmalloc(W9966_RBUFFER, GFP_KERNEL); - if (tbuf == NULL) { - count = -ENOMEM; - goto out; - } - - while (dleft > 0) { - unsigned long tsize = (dleft > W9966_RBUFFER) ? W9966_RBUFFER : dleft; - - if (parport_read(cam->pport, tbuf, tsize) < tsize) { - count = -EFAULT; - goto out; - } - if (copy_to_user(dest, tbuf, tsize) != 0) { - count = -EFAULT; - goto out; - } - dest += tsize; - dleft -= tsize; - } - - w9966_write_reg(cam, 0x01, 0x18); /* Disable capture */ - -out: - kfree(tbuf); - w9966_pdev_release(cam); - mutex_unlock(&cam->lock); - - return count; -} - -static const struct v4l2_file_operations w9966_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = v4l2_fh_release, - .poll = v4l2_ctrl_poll, - .unlocked_ioctl = video_ioctl2, - .read = w9966_v4l_read, -}; - -static const struct v4l2_ioctl_ops w9966_ioctl_ops = { - .vidioc_querycap = cam_querycap, - .vidioc_g_input = cam_g_input, - .vidioc_s_input = cam_s_input, - .vidioc_enum_input = cam_enum_input, - .vidioc_enum_fmt_vid_cap = cam_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cam_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = cam_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cam_try_fmt_vid_cap, - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static const struct v4l2_ctrl_ops cam_ctrl_ops = { - .s_ctrl = cam_s_ctrl, -}; - - -/* Initialize camera device. Setup all internal flags, set a - default video mode, setup ccd-chip, register v4l device etc.. - Also used for 'probing' of hardware. - -1 on error */ -static int w9966_init(struct w9966 *cam, struct parport *port) -{ - struct v4l2_device *v4l2_dev = &cam->v4l2_dev; - - if (cam->dev_state != 0) - return -1; - - strlcpy(v4l2_dev->name, "w9966", sizeof(v4l2_dev->name)); - - if (v4l2_device_register(NULL, v4l2_dev) < 0) { - v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); - return -1; - } - - v4l2_ctrl_handler_init(&cam->hdl, 4); - v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); - v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops, - V4L2_CID_CONTRAST, -64, 64, 1, 64); - v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops, - V4L2_CID_SATURATION, -64, 64, 1, 64); - v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops, - V4L2_CID_HUE, -128, 127, 1, 0); - if (cam->hdl.error) { - v4l2_err(v4l2_dev, "couldn't register controls\n"); - return -1; - } - cam->pport = port; - cam->brightness = 128; - cam->contrast = 64; - cam->color = 64; - cam->hue = 0; - - /* Select requested transfer mode */ - switch (parmode) { - default: /* Auto-detect (priority: hw-ecp, hw-epp, sw-ecp) */ - case 0: - if (port->modes & PARPORT_MODE_ECP) - cam->ppmode = IEEE1284_MODE_ECP; - else if (port->modes & PARPORT_MODE_EPP) - cam->ppmode = IEEE1284_MODE_EPP; - else - cam->ppmode = IEEE1284_MODE_ECP; - break; - case 1: /* hw- or sw-ecp */ - cam->ppmode = IEEE1284_MODE_ECP; - break; - case 2: /* hw- or sw-epp */ - cam->ppmode = IEEE1284_MODE_EPP; - break; - } - - /* Tell the parport driver that we exists */ - cam->pdev = parport_register_device(port, "w9966", NULL, NULL, NULL, 0, NULL); - if (cam->pdev == NULL) { - DPRINTF("parport_register_device() failed\n"); - return -1; - } - w9966_set_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV); - - w9966_pdev_claim(cam); - - /* Setup a default capture mode */ - if (w9966_setup(cam, 0, 0, 1023, 1023, 200, 160) != 0) { - DPRINTF("w9966_setup() failed.\n"); - return -1; - } - - w9966_pdev_release(cam); - - /* Fill in the video_device struct and register us to v4l */ - strlcpy(cam->vdev.name, W9966_DRIVERNAME, sizeof(cam->vdev.name)); - cam->vdev.v4l2_dev = v4l2_dev; - cam->vdev.fops = &w9966_fops; - cam->vdev.ioctl_ops = &w9966_ioctl_ops; - cam->vdev.release = video_device_release_empty; - cam->vdev.ctrl_handler = &cam->hdl; - video_set_drvdata(&cam->vdev, cam); - - mutex_init(&cam->lock); - - if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) - return -1; - - w9966_set_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV); - - /* All ok */ - v4l2_info(v4l2_dev, "Found and initialized a webcam on %s.\n", - cam->pport->name); - return 0; -} - - -/* Terminate everything gracefully */ -static void w9966_term(struct w9966 *cam) -{ - /* Unregister from v4l */ - if (w9966_get_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV)) { - video_unregister_device(&cam->vdev); - w9966_set_state(cam, W9966_STATE_VDEV, 0); - } - - v4l2_ctrl_handler_free(&cam->hdl); - - /* Terminate from IEEE1284 mode and release pdev block */ - if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) { - w9966_pdev_claim(cam); - parport_negotiate(cam->pport, IEEE1284_MODE_COMPAT); - w9966_pdev_release(cam); - } - - /* Unregister from parport */ - if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) { - parport_unregister_device(cam->pdev); - w9966_set_state(cam, W9966_STATE_PDEV, 0); - } - memset(cam, 0, sizeof(*cam)); -} - - -/* Called once for every parport on init */ -static void w9966_attach(struct parport *port) -{ - int i; - - for (i = 0; i < W9966_MAXCAMS; i++) { - if (w9966_cams[i].dev_state != 0) /* Cam is already assigned */ - continue; - if (strcmp(pardev[i], "aggressive") == 0 || strcmp(pardev[i], port->name) == 0) { - if (w9966_init(&w9966_cams[i], port) != 0) - w9966_term(&w9966_cams[i]); - break; /* return */ - } - } -} - -/* Called once for every parport on termination */ -static void w9966_detach(struct parport *port) -{ - int i; - - for (i = 0; i < W9966_MAXCAMS; i++) - if (w9966_cams[i].dev_state != 0 && w9966_cams[i].pport == port) - w9966_term(&w9966_cams[i]); -} - - -static struct parport_driver w9966_ppd = { - .name = W9966_DRIVERNAME, - .attach = w9966_attach, - .detach = w9966_detach, -}; - -/* Module entry point */ -static int __init w9966_mod_init(void) -{ - int i; - - for (i = 0; i < W9966_MAXCAMS; i++) - w9966_cams[i].dev_state = 0; - - return parport_register_driver(&w9966_ppd); -} - -/* Module cleanup */ -static void __exit w9966_mod_term(void) -{ - parport_unregister_driver(&w9966_ppd); -} - -module_init(w9966_mod_init); -module_exit(w9966_mod_term); diff --git a/drivers/staging/media/tlg2300/Kconfig b/drivers/staging/media/tlg2300/Kconfig deleted file mode 100644 index 81784c6f7b88..000000000000 --- a/drivers/staging/media/tlg2300/Kconfig +++ /dev/null @@ -1,20 +0,0 @@ -config VIDEO_TLG2300 - tristate "Telegent TLG2300 USB video capture support (Deprecated)" - depends on VIDEO_DEV && I2C && SND && DVB_CORE - select VIDEO_TUNER - select VIDEO_TVEEPROM - depends on RC_CORE - select VIDEOBUF_VMALLOC - select SND_PCM - select VIDEOBUF_DVB - - ---help--- - This is a video4linux driver for Telegent tlg2300 based TV cards. - The driver supports V4L2, DVB-T and radio. - - This driver is deprecated and will be removed soon. If you have - hardware for this and you want to work on this driver, then contact - the linux-media mailinglist. - - To compile this driver as a module, choose M here: the - module will be called poseidon diff --git a/drivers/staging/media/tlg2300/Makefile b/drivers/staging/media/tlg2300/Makefile deleted file mode 100644 index 137f8e38cdec..000000000000 --- a/drivers/staging/media/tlg2300/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -poseidon-objs := pd-video.o pd-alsa.o pd-dvb.o pd-radio.o pd-main.o - -obj-$(CONFIG_VIDEO_TLG2300) += poseidon.o - -ccflags-y += -Idrivers/media/i2c -ccflags-y += -Idrivers/media/tuners -ccflags-y += -Idrivers/media/dvb-core -ccflags-y += -Idrivers/media/dvb-frontends - diff --git a/drivers/staging/media/tlg2300/pd-alsa.c b/drivers/staging/media/tlg2300/pd-alsa.c deleted file mode 100644 index dd8fe100590f..000000000000 --- a/drivers/staging/media/tlg2300/pd-alsa.c +++ /dev/null @@ -1,337 +0,0 @@ -#include <linux/kernel.h> -#include <linux/usb.h> -#include <linux/init.h> -#include <linux/sound.h> -#include <linux/spinlock.h> -#include <linux/soundcard.h> -#include <linux/vmalloc.h> -#include <linux/proc_fs.h> -#include <linux/module.h> -#include <linux/gfp.h> -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/info.h> -#include <sound/initval.h> -#include <sound/control.h> -#include <media/v4l2-common.h> -#include "pd-common.h" -#include "vendorcmds.h" - -static void complete_handler_audio(struct urb *urb); -#define AUDIO_EP (0x83) -#define AUDIO_BUF_SIZE (512) -#define PERIOD_SIZE (1024 * 8) -#define PERIOD_MIN (4) -#define PERIOD_MAX PERIOD_MIN - -static struct snd_pcm_hardware snd_pd_hw_capture = { - .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_MMAP_VALID, - - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_48000, - - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = PERIOD_SIZE * PERIOD_MIN, - .period_bytes_min = PERIOD_SIZE, - .period_bytes_max = PERIOD_SIZE, - .periods_min = PERIOD_MIN, - .periods_max = PERIOD_MAX, - /* - .buffer_bytes_max = 62720 * 8, - .period_bytes_min = 64, - .period_bytes_max = 12544, - .periods_min = 2, - .periods_max = 98 - */ -}; - -static int snd_pd_capture_open(struct snd_pcm_substream *substream) -{ - struct poseidon *p = snd_pcm_substream_chip(substream); - struct poseidon_audio *pa = &p->audio; - struct snd_pcm_runtime *runtime = substream->runtime; - - if (!p) - return -ENODEV; - pa->users++; - pa->card_close = 0; - pa->capture_pcm_substream = substream; - runtime->private_data = p; - - runtime->hw = snd_pd_hw_capture; - snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); - usb_autopm_get_interface(p->interface); - kref_get(&p->kref); - return 0; -} - -static int snd_pd_pcm_close(struct snd_pcm_substream *substream) -{ - struct poseidon *p = snd_pcm_substream_chip(substream); - struct poseidon_audio *pa = &p->audio; - - pa->users--; - pa->card_close = 1; - usb_autopm_put_interface(p->interface); - kref_put(&p->kref, poseidon_delete); - return 0; -} - -static int snd_pd_hw_capture_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int size; - - size = params_buffer_bytes(hw_params); - if (runtime->dma_area) { - if (runtime->dma_bytes > size) - return 0; - vfree(runtime->dma_area); - } - runtime->dma_area = vmalloc(size); - if (!runtime->dma_area) - return -ENOMEM; - else - runtime->dma_bytes = size; - return 0; -} - -static int audio_buf_free(struct poseidon *p) -{ - struct poseidon_audio *pa = &p->audio; - int i; - - for (i = 0; i < AUDIO_BUFS; i++) - if (pa->urb_array[i]) - usb_kill_urb(pa->urb_array[i]); - free_all_urb_generic(pa->urb_array, AUDIO_BUFS); - logpm(); - return 0; -} - -static int snd_pd_hw_capture_free(struct snd_pcm_substream *substream) -{ - struct poseidon *p = snd_pcm_substream_chip(substream); - - logpm(); - audio_buf_free(p); - return 0; -} - -static int snd_pd_prepare(struct snd_pcm_substream *substream) -{ - return 0; -} - -#define AUDIO_TRAILER_SIZE (16) -static inline void handle_audio_data(struct urb *urb, int *period_elapsed) -{ - struct poseidon_audio *pa = urb->context; - struct snd_pcm_runtime *runtime = pa->capture_pcm_substream->runtime; - - int stride = runtime->frame_bits >> 3; - int len = urb->actual_length / stride; - unsigned char *cp = urb->transfer_buffer; - unsigned int oldptr = pa->rcv_position; - - if (urb->actual_length == AUDIO_BUF_SIZE - 4) - len -= (AUDIO_TRAILER_SIZE / stride); - - /* do the copy */ - if (oldptr + len >= runtime->buffer_size) { - unsigned int cnt = runtime->buffer_size - oldptr; - - memcpy(runtime->dma_area + oldptr * stride, cp, cnt * stride); - memcpy(runtime->dma_area, (cp + cnt * stride), - (len * stride - cnt * stride)); - } else - memcpy(runtime->dma_area + oldptr * stride, cp, len * stride); - - /* update the statas */ - snd_pcm_stream_lock(pa->capture_pcm_substream); - pa->rcv_position += len; - if (pa->rcv_position >= runtime->buffer_size) - pa->rcv_position -= runtime->buffer_size; - - pa->copied_position += (len); - if (pa->copied_position >= runtime->period_size) { - pa->copied_position -= runtime->period_size; - *period_elapsed = 1; - } - snd_pcm_stream_unlock(pa->capture_pcm_substream); -} - -static void complete_handler_audio(struct urb *urb) -{ - struct poseidon_audio *pa = urb->context; - struct snd_pcm_substream *substream = pa->capture_pcm_substream; - int period_elapsed = 0; - int ret; - - if (1 == pa->card_close || pa->capture_stream != STREAM_ON) - return; - - if (urb->status != 0) { - /*if (urb->status == -ESHUTDOWN)*/ - return; - } - - if (substream) { - if (urb->actual_length) { - handle_audio_data(urb, &period_elapsed); - if (period_elapsed) - snd_pcm_period_elapsed(substream); - } - } - - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret < 0) - log("audio urb failed (errcod = %i)", ret); - return; -} - -static int fire_audio_urb(struct poseidon *p) -{ - int i, ret = 0; - struct poseidon_audio *pa = &p->audio; - - alloc_bulk_urbs_generic(pa->urb_array, AUDIO_BUFS, - p->udev, AUDIO_EP, - AUDIO_BUF_SIZE, GFP_ATOMIC, - complete_handler_audio, pa); - - for (i = 0; i < AUDIO_BUFS; i++) { - ret = usb_submit_urb(pa->urb_array[i], GFP_KERNEL); - if (ret) - log("urb err : %d", ret); - } - log(); - return ret; -} - -static int snd_pd_capture_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct poseidon *p = snd_pcm_substream_chip(substream); - struct poseidon_audio *pa = &p->audio; - - if (debug_mode) - log("cmd %d, audio stat : %d\n", cmd, pa->capture_stream); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_START: - if (pa->capture_stream == STREAM_ON) - return 0; - - pa->rcv_position = pa->copied_position = 0; - pa->capture_stream = STREAM_ON; - - if (in_hibernation(p)) - return 0; - fire_audio_urb(p); - return 0; - - case SNDRV_PCM_TRIGGER_SUSPEND: - pa->capture_stream = STREAM_SUSPEND; - return 0; - case SNDRV_PCM_TRIGGER_STOP: - pa->capture_stream = STREAM_OFF; - return 0; - default: - return -EINVAL; - } -} - -static snd_pcm_uframes_t -snd_pd_capture_pointer(struct snd_pcm_substream *substream) -{ - struct poseidon *p = snd_pcm_substream_chip(substream); - struct poseidon_audio *pa = &p->audio; - return pa->rcv_position; -} - -static struct page *snd_pcm_pd_get_page(struct snd_pcm_substream *subs, - unsigned long offset) -{ - void *pageptr = subs->runtime->dma_area + offset; - return vmalloc_to_page(pageptr); -} - -static struct snd_pcm_ops pcm_capture_ops = { - .open = snd_pd_capture_open, - .close = snd_pd_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_pd_hw_capture_params, - .hw_free = snd_pd_hw_capture_free, - .prepare = snd_pd_prepare, - .trigger = snd_pd_capture_trigger, - .pointer = snd_pd_capture_pointer, - .page = snd_pcm_pd_get_page, -}; - -#ifdef CONFIG_PM -int pm_alsa_suspend(struct poseidon *p) -{ - logpm(p); - audio_buf_free(p); - return 0; -} - -int pm_alsa_resume(struct poseidon *p) -{ - logpm(p); - fire_audio_urb(p); - return 0; -} -#endif - -int poseidon_audio_init(struct poseidon *p) -{ - struct poseidon_audio *pa = &p->audio; - struct snd_card *card; - struct snd_pcm *pcm; - int ret; - - ret = snd_card_new(&p->interface->dev, -1, "Telegent", - THIS_MODULE, 0, &card); - if (ret != 0) - return ret; - - ret = snd_pcm_new(card, "poseidon audio", 0, 0, 1, &pcm); - if (ret < 0) { - snd_card_free(card); - return ret; - } - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); - pcm->info_flags = 0; - pcm->private_data = p; - strcpy(pcm->name, "poseidon audio capture"); - - strcpy(card->driver, "ALSA driver"); - strcpy(card->shortname, "poseidon Audio"); - strcpy(card->longname, "poseidon ALSA Audio"); - - if (snd_card_register(card)) { - snd_card_free(card); - return -ENOMEM; - } - pa->card = card; - return 0; -} - -int poseidon_audio_free(struct poseidon *p) -{ - struct poseidon_audio *pa = &p->audio; - - if (pa->card) - snd_card_free(pa->card); - return 0; -} diff --git a/drivers/staging/media/tlg2300/pd-common.h b/drivers/staging/media/tlg2300/pd-common.h deleted file mode 100644 index 9e23ad32d2fe..000000000000 --- a/drivers/staging/media/tlg2300/pd-common.h +++ /dev/null @@ -1,271 +0,0 @@ -#ifndef PD_COMMON_H -#define PD_COMMON_H - -#include <linux/fs.h> -#include <linux/wait.h> -#include <linux/list.h> -#include <linux/videodev2.h> -#include <linux/semaphore.h> -#include <linux/usb.h> -#include <linux/poll.h> -#include <media/videobuf-vmalloc.h> -#include <media/v4l2-device.h> -#include <media/v4l2-ctrls.h> - -#include "dvb_frontend.h" -#include "dvbdev.h" -#include "dvb_demux.h" -#include "dmxdev.h" - -#define SBUF_NUM 8 -#define MAX_BUFFER_NUM 6 -#define PK_PER_URB 32 -#define ISO_PKT_SIZE 3072 - -#define POSEIDON_STATE_NONE (0x0000) -#define POSEIDON_STATE_ANALOG (0x0001) -#define POSEIDON_STATE_FM (0x0002) -#define POSEIDON_STATE_DVBT (0x0004) -#define POSEIDON_STATE_DISCONNECT (0x0080) - -#define PM_SUSPEND_DELAY 3 - -#define V4L_PAL_VBI_LINES 18 -#define V4L_NTSC_VBI_LINES 12 -#define V4L_PAL_VBI_FRAMESIZE (V4L_PAL_VBI_LINES * 1440 * 2) -#define V4L_NTSC_VBI_FRAMESIZE (V4L_NTSC_VBI_LINES * 1440 * 2) - -#define TUNER_FREQ_MIN (45000000U) -#define TUNER_FREQ_MAX (862000000U) - -struct vbi_data { - struct video_device v_dev; - struct video_data *video; - struct front_face *front; - - unsigned int copied; - unsigned int vbi_size; /* the whole size of two fields */ - int users; -}; - -/* - * This is the running context of the video, it is useful for - * resume() - */ -struct running_context { - u32 freq; /* VIDIOC_S_FREQUENCY */ - int audio_idx; /* VIDIOC_S_TUNER */ - v4l2_std_id tvnormid; /* VIDIOC_S_STD */ - int sig_index; /* VIDIOC_S_INPUT */ - struct v4l2_pix_format pix; /* VIDIOC_S_FMT */ -}; - -struct video_data { - /* v4l2 video device */ - struct video_device v_dev; - struct v4l2_ctrl_handler ctrl_handler; - - /* the working context */ - struct running_context context; - - /* for data copy */ - int field_count; - - char *dst; - int lines_copied; - int prev_left; - - int lines_per_field; - int lines_size; - - /* for communication */ - u8 endpoint_addr; - struct urb *urb_array[SBUF_NUM]; - struct vbi_data *vbi; - struct poseidon *pd; - struct front_face *front; - - int is_streaming; - int users; - - /* for bubble handler */ - struct work_struct bubble_work; -}; - -enum pcm_stream_state { - STREAM_OFF, - STREAM_ON, - STREAM_SUSPEND, -}; - -#define AUDIO_BUFS (3) -#define CAPTURE_STREAM_EN 1 -struct poseidon_audio { - struct urb *urb_array[AUDIO_BUFS]; - unsigned int copied_position; - struct snd_pcm_substream *capture_pcm_substream; - - unsigned int rcv_position; - struct snd_card *card; - int card_close; - - int users; - int pm_state; - enum pcm_stream_state capture_stream; -}; - -struct radio_data { - __u32 fm_freq; - unsigned int is_radio_streaming; - int pre_emphasis; - struct video_device fm_dev; - struct v4l2_ctrl_handler ctrl_handler; -}; - -#define DVB_SBUF_NUM 4 -#define DVB_URB_BUF_SIZE 0x2000 -struct pd_dvb_adapter { - struct dvb_adapter dvb_adap; - struct dvb_frontend dvb_fe; - struct dmxdev dmxdev; - struct dvb_demux demux; - - atomic_t users; - atomic_t active_feed; - - /* data transfer */ - s32 is_streaming; - struct urb *urb_array[DVB_SBUF_NUM]; - struct poseidon *pd_device; - u8 ep_addr; - u8 reserved[3]; - - /* data for power resume*/ - struct dtv_frontend_properties fe_param; - - /* for channel scanning */ - int prev_freq; - int bandwidth; - unsigned long last_jiffies; -}; - -struct front_face { - /* use this field to distinguish VIDEO and VBI */ - enum v4l2_buf_type type; - - /* for host */ - struct videobuf_queue q; - - /* the bridge for host and device */ - struct videobuf_buffer *curr_frame; - - /* for device */ - spinlock_t queue_lock; - struct list_head active; - struct poseidon *pd; -}; - -struct poseidon { - struct list_head device_list; - - struct mutex lock; - struct kref kref; - - /* for V4L2 */ - struct v4l2_device v4l2_dev; - - /* hardware info */ - struct usb_device *udev; - struct usb_interface *interface; - int cur_transfer_mode; - - struct video_data video_data; /* video */ - struct vbi_data vbi_data; /* vbi */ - struct poseidon_audio audio; /* audio (alsa) */ - struct radio_data radio_data; /* FM */ - struct pd_dvb_adapter dvb_data; /* DVB */ - - u32 state; - struct file *file_for_stream; /* the active stream*/ - -#ifdef CONFIG_PM - int (*pm_suspend)(struct poseidon *); - int (*pm_resume)(struct poseidon *); - pm_message_t msg; - - struct work_struct pm_work; - u8 portnum; -#endif -}; - -struct poseidon_format { - char *name; - int fourcc; /* video4linux 2 */ - int depth; /* bit/pixel */ - int flags; -}; - -struct poseidon_tvnorm { - v4l2_std_id v4l2_id; - char name[12]; - u32 tlg_tvnorm; -}; - -/* video */ -int pd_video_init(struct poseidon *); -void pd_video_exit(struct poseidon *); -int stop_all_video_stream(struct poseidon *); - -/* alsa audio */ -int poseidon_audio_init(struct poseidon *); -int poseidon_audio_free(struct poseidon *); -#ifdef CONFIG_PM -int pm_alsa_suspend(struct poseidon *); -int pm_alsa_resume(struct poseidon *); -#endif - -/* dvb */ -int pd_dvb_usb_device_init(struct poseidon *); -void pd_dvb_usb_device_exit(struct poseidon *); -void pd_dvb_usb_device_cleanup(struct poseidon *); -int pd_dvb_get_adapter_num(struct pd_dvb_adapter *); -void dvb_stop_streaming(struct pd_dvb_adapter *); - -/* FM */ -int poseidon_fm_init(struct poseidon *); -int poseidon_fm_exit(struct poseidon *); - -/* vendor command ops */ -int send_set_req(struct poseidon*, u8, s32, s32*); -int send_get_req(struct poseidon*, u8, s32, void*, s32*, s32); -s32 set_tuner_mode(struct poseidon*, unsigned char); - -/* bulk urb alloc/free */ -int alloc_bulk_urbs_generic(struct urb **urb_array, int num, - struct usb_device *udev, u8 ep_addr, - int buf_size, gfp_t gfp_flags, - usb_complete_t complete_fn, void *context); -void free_all_urb_generic(struct urb **urb_array, int num); - -/* misc */ -void poseidon_delete(struct kref *kref); -extern int debug_mode; -void set_debug_mode(struct video_device *vfd, int debug_mode); - -#ifdef CONFIG_PM -#define in_hibernation(pd) (pd->msg.event == PM_EVENT_FREEZE) -#else -#define in_hibernation(pd) (0) -#endif -#define get_pm_count(p) (atomic_read(&(p)->interface->pm_usage_cnt)) - -#define log(a, ...) printk(KERN_DEBUG "\t[ %s : %.3d ] "a"\n", \ - __func__, __LINE__, ## __VA_ARGS__) - -/* for power management */ -#define logpm(pd) do {\ - if (debug_mode & 0x10)\ - log();\ - } while (0) - -#endif diff --git a/drivers/staging/media/tlg2300/pd-dvb.c b/drivers/staging/media/tlg2300/pd-dvb.c deleted file mode 100644 index ca4994a5190c..000000000000 --- a/drivers/staging/media/tlg2300/pd-dvb.c +++ /dev/null @@ -1,597 +0,0 @@ -#include "pd-common.h" -#include <linux/kernel.h> -#include <linux/usb.h> -#include <linux/time.h> -#include <linux/dvb/dmx.h> -#include <linux/delay.h> -#include <linux/gfp.h> - -#include "vendorcmds.h" -#include <linux/sched.h> -#include <linux/atomic.h> - -static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb); - -static int dvb_bandwidth[][2] = { - { TLG_BW_8, 8000000 }, - { TLG_BW_7, 7000000 }, - { TLG_BW_6, 6000000 } -}; -static int dvb_bandwidth_length = ARRAY_SIZE(dvb_bandwidth); - -static s32 dvb_start_streaming(struct pd_dvb_adapter *pd_dvb); -static int poseidon_check_mode_dvbt(struct poseidon *pd) -{ - s32 ret = 0, cmd_status = 0; - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/4); - - ret = usb_set_interface(pd->udev, 0, BULK_ALTERNATE_IFACE); - if (ret != 0) - return ret; - - ret = set_tuner_mode(pd, TLG_MODE_CAPS_DVB_T); - if (ret) - return ret; - - /* signal source */ - ret = send_set_req(pd, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &cmd_status); - if (ret|cmd_status) - return ret; - - return 0; -} - -/* acquire : - * 1 == open - * 0 == release - */ -static int poseidon_ts_bus_ctrl(struct dvb_frontend *fe, int acquire) -{ - struct poseidon *pd = fe->demodulator_priv; - struct pd_dvb_adapter *pd_dvb; - int ret = 0; - - if (!pd) - return -ENODEV; - - pd_dvb = container_of(fe, struct pd_dvb_adapter, dvb_fe); - if (acquire) { - mutex_lock(&pd->lock); - if (pd->state & POSEIDON_STATE_DISCONNECT) { - ret = -ENODEV; - goto open_out; - } - - if (pd->state && !(pd->state & POSEIDON_STATE_DVBT)) { - ret = -EBUSY; - goto open_out; - } - - usb_autopm_get_interface(pd->interface); - if (0 == pd->state) { - ret = poseidon_check_mode_dvbt(pd); - if (ret < 0) { - usb_autopm_put_interface(pd->interface); - goto open_out; - } - pd->state |= POSEIDON_STATE_DVBT; - pd_dvb->bandwidth = 0; - pd_dvb->prev_freq = 0; - } - atomic_inc(&pd_dvb->users); - kref_get(&pd->kref); -open_out: - mutex_unlock(&pd->lock); - } else { - dvb_stop_streaming(pd_dvb); - - if (atomic_dec_and_test(&pd_dvb->users)) { - mutex_lock(&pd->lock); - pd->state &= ~POSEIDON_STATE_DVBT; - mutex_unlock(&pd->lock); - } - kref_put(&pd->kref, poseidon_delete); - usb_autopm_put_interface(pd->interface); - } - return ret; -} - -#ifdef CONFIG_PM -static void poseidon_fe_release(struct dvb_frontend *fe) -{ - struct poseidon *pd = fe->demodulator_priv; - - pd->pm_suspend = NULL; - pd->pm_resume = NULL; -} -#else -#define poseidon_fe_release NULL -#endif - -static s32 poseidon_fe_sleep(struct dvb_frontend *fe) -{ - return 0; -} - -/* - * return true if we can satisfy the conditions, else return false. - */ -static bool check_scan_ok(__u32 freq, int bandwidth, - struct pd_dvb_adapter *adapter) -{ - if (bandwidth < 0) - return false; - - if (adapter->prev_freq == freq - && adapter->bandwidth == bandwidth) { - long nl = jiffies - adapter->last_jiffies; - unsigned int msec ; - - msec = jiffies_to_msecs(abs(nl)); - return msec > 15000 ? true : false; - } - return true; -} - -/* - * Check if the firmware delays too long for an invalid frequency. - */ -static int fw_delay_overflow(struct pd_dvb_adapter *adapter) -{ - long nl = jiffies - adapter->last_jiffies; - unsigned int msec ; - - msec = jiffies_to_msecs(abs(nl)); - return msec > 800 ? true : false; -} - -static int poseidon_set_fe(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *fep = &fe->dtv_property_cache; - s32 ret = 0, cmd_status = 0; - s32 i, bandwidth = -1; - struct poseidon *pd = fe->demodulator_priv; - struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; - - if (in_hibernation(pd)) - return -EBUSY; - - mutex_lock(&pd->lock); - for (i = 0; i < dvb_bandwidth_length; i++) - if (fep->bandwidth_hz == dvb_bandwidth[i][1]) - bandwidth = dvb_bandwidth[i][0]; - - if (check_scan_ok(fep->frequency, bandwidth, pd_dvb)) { - ret = send_set_req(pd, TUNE_FREQ_SELECT, - fep->frequency / 1000, &cmd_status); - if (ret | cmd_status) { - log("error line"); - goto front_out; - } - - ret = send_set_req(pd, DVBT_BANDW_SEL, - bandwidth, &cmd_status); - if (ret | cmd_status) { - log("error line"); - goto front_out; - } - - ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); - if (ret | cmd_status) { - log("error line"); - goto front_out; - } - - /* save the context for future */ - memcpy(&pd_dvb->fe_param, fep, sizeof(*fep)); - pd_dvb->bandwidth = bandwidth; - pd_dvb->prev_freq = fep->frequency; - pd_dvb->last_jiffies = jiffies; - } -front_out: - mutex_unlock(&pd->lock); - return ret; -} - -#ifdef CONFIG_PM -static int pm_dvb_suspend(struct poseidon *pd) -{ - struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; - dvb_stop_streaming(pd_dvb); - dvb_urb_cleanup(pd_dvb); - msleep(500); - return 0; -} - -static int pm_dvb_resume(struct poseidon *pd) -{ - struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; - - poseidon_check_mode_dvbt(pd); - msleep(300); - poseidon_set_fe(&pd_dvb->dvb_fe); - - dvb_start_streaming(pd_dvb); - return 0; -} -#endif - -static s32 poseidon_fe_init(struct dvb_frontend *fe) -{ - struct poseidon *pd = fe->demodulator_priv; - struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; - -#ifdef CONFIG_PM - pd->pm_suspend = pm_dvb_suspend; - pd->pm_resume = pm_dvb_resume; -#endif - memset(&pd_dvb->fe_param, 0, - sizeof(struct dtv_frontend_properties)); - return 0; -} - -static int poseidon_get_fe(struct dvb_frontend *fe) -{ - struct dtv_frontend_properties *fep = &fe->dtv_property_cache; - struct poseidon *pd = fe->demodulator_priv; - struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; - - memcpy(fep, &pd_dvb->fe_param, sizeof(*fep)); - return 0; -} - -static int poseidon_fe_get_tune_settings(struct dvb_frontend *fe, - struct dvb_frontend_tune_settings *tune) -{ - tune->min_delay_ms = 1000; - return 0; -} - -static int poseidon_read_status(struct dvb_frontend *fe, fe_status_t *stat) -{ - struct poseidon *pd = fe->demodulator_priv; - s32 ret = -1, cmd_status; - struct tuner_dtv_sig_stat_s status = {}; - - if (in_hibernation(pd)) - return -EBUSY; - mutex_lock(&pd->lock); - - ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_DVB_T, - &status, &cmd_status, sizeof(status)); - if (ret | cmd_status) { - log("get tuner status error"); - goto out; - } - - if (debug_mode) - log("P : %d, L %d, LB :%d", status.sig_present, - status.sig_locked, status.sig_lock_busy); - - if (status.sig_lock_busy) { - goto out; - } else if (status.sig_present || status.sig_locked) { - *stat |= FE_HAS_LOCK | FE_HAS_SIGNAL | FE_HAS_CARRIER - | FE_HAS_SYNC | FE_HAS_VITERBI; - } else { - if (fw_delay_overflow(&pd->dvb_data)) - *stat |= FE_TIMEDOUT; - } -out: - mutex_unlock(&pd->lock); - return ret; -} - -static int poseidon_read_ber(struct dvb_frontend *fe, u32 *ber) -{ - struct poseidon *pd = fe->demodulator_priv; - struct tuner_ber_rate_s tlg_ber = {}; - s32 ret = -1, cmd_status; - - mutex_lock(&pd->lock); - ret = send_get_req(pd, TUNER_BER_RATE, 0, - &tlg_ber, &cmd_status, sizeof(tlg_ber)); - if (ret | cmd_status) - goto out; - *ber = tlg_ber.ber_rate; -out: - mutex_unlock(&pd->lock); - return ret; -} - -static s32 poseidon_read_signal_strength(struct dvb_frontend *fe, u16 *strength) -{ - struct poseidon *pd = fe->demodulator_priv; - struct tuner_dtv_sig_stat_s status = {}; - s32 ret = 0, cmd_status; - - mutex_lock(&pd->lock); - ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_DVB_T, - &status, &cmd_status, sizeof(status)); - if (ret | cmd_status) - goto out; - if ((status.sig_present || status.sig_locked) && !status.sig_strength) - *strength = 0xFFFF; - else - *strength = status.sig_strength; -out: - mutex_unlock(&pd->lock); - return ret; -} - -static int poseidon_read_snr(struct dvb_frontend *fe, u16 *snr) -{ - return 0; -} - -static int poseidon_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) -{ - *unc = 0; - return 0; -} - -static struct dvb_frontend_ops poseidon_frontend_ops = { - .delsys = { SYS_DVBT }, - .info = { - .name = "Poseidon DVB-T", - .frequency_min = 174000000, - .frequency_max = 862000000, - .frequency_stepsize = 62500,/* FIXME */ - .caps = FE_CAN_INVERSION_AUTO | - FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | - FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | - FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | - FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | - FE_CAN_GUARD_INTERVAL_AUTO | - FE_CAN_RECOVER | - FE_CAN_HIERARCHY_AUTO, - }, - - .release = poseidon_fe_release, - - .init = poseidon_fe_init, - .sleep = poseidon_fe_sleep, - - .set_frontend = poseidon_set_fe, - .get_frontend = poseidon_get_fe, - .get_tune_settings = poseidon_fe_get_tune_settings, - - .read_status = poseidon_read_status, - .read_ber = poseidon_read_ber, - .read_signal_strength = poseidon_read_signal_strength, - .read_snr = poseidon_read_snr, - .read_ucblocks = poseidon_read_unc_blocks, - - .ts_bus_ctrl = poseidon_ts_bus_ctrl, -}; - -static void dvb_urb_irq(struct urb *urb) -{ - struct pd_dvb_adapter *pd_dvb = urb->context; - int len = urb->transfer_buffer_length; - struct dvb_demux *demux = &pd_dvb->demux; - s32 ret; - - if (!pd_dvb->is_streaming || urb->status) { - if (urb->status == -EPROTO) - goto resend; - return; - } - - if (urb->actual_length == len) - dvb_dmx_swfilter(demux, urb->transfer_buffer, len); - else if (urb->actual_length == len - 4) { - int offset; - u8 *buf = urb->transfer_buffer; - - /* - * The packet size is 512, - * last packet contains 456 bytes tsp data - */ - for (offset = 456; offset < len; offset += 512) { - if (!strncmp(buf + offset, "DVHS", 4)) { - dvb_dmx_swfilter(demux, buf, offset); - if (len > offset + 52 + 4) { - /*16 bytes trailer + 36 bytes padding */ - buf += offset + 52; - len -= offset + 52 + 4; - dvb_dmx_swfilter(demux, buf, len); - } - break; - } - } - } - -resend: - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret) - log(" usb_submit_urb failed: error %d", ret); -} - -static int dvb_urb_init(struct pd_dvb_adapter *pd_dvb) -{ - if (pd_dvb->urb_array[0]) - return 0; - - alloc_bulk_urbs_generic(pd_dvb->urb_array, DVB_SBUF_NUM, - pd_dvb->pd_device->udev, pd_dvb->ep_addr, - DVB_URB_BUF_SIZE, GFP_KERNEL, - dvb_urb_irq, pd_dvb); - return 0; -} - -static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb) -{ - free_all_urb_generic(pd_dvb->urb_array, DVB_SBUF_NUM); -} - -static s32 dvb_start_streaming(struct pd_dvb_adapter *pd_dvb) -{ - struct poseidon *pd = pd_dvb->pd_device; - int ret = 0; - - if (pd->state & POSEIDON_STATE_DISCONNECT) - return -ENODEV; - - mutex_lock(&pd->lock); - if (!pd_dvb->is_streaming) { - s32 i, cmd_status = 0; - /* - * Once upon a time, there was a difficult bug lying here. - * ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); - */ - - ret = send_set_req(pd, PLAY_SERVICE, 1, &cmd_status); - if (ret | cmd_status) - goto out; - - ret = dvb_urb_init(pd_dvb); - if (ret < 0) - goto out; - - pd_dvb->is_streaming = 1; - for (i = 0; i < DVB_SBUF_NUM; i++) { - ret = usb_submit_urb(pd_dvb->urb_array[i], - GFP_KERNEL); - if (ret) { - log(" submit urb error %d", ret); - goto out; - } - } - } -out: - mutex_unlock(&pd->lock); - return ret; -} - -void dvb_stop_streaming(struct pd_dvb_adapter *pd_dvb) -{ - struct poseidon *pd = pd_dvb->pd_device; - - mutex_lock(&pd->lock); - if (pd_dvb->is_streaming) { - s32 i, ret, cmd_status = 0; - - pd_dvb->is_streaming = 0; - - for (i = 0; i < DVB_SBUF_NUM; i++) - if (pd_dvb->urb_array[i]) - usb_kill_urb(pd_dvb->urb_array[i]); - - ret = send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, - &cmd_status); - if (ret | cmd_status) - log("error"); - } - mutex_unlock(&pd->lock); -} - -static int pd_start_feed(struct dvb_demux_feed *feed) -{ - struct pd_dvb_adapter *pd_dvb = feed->demux->priv; - int ret = 0; - - if (!pd_dvb) - return -1; - if (atomic_inc_return(&pd_dvb->active_feed) == 1) - ret = dvb_start_streaming(pd_dvb); - return ret; -} - -static int pd_stop_feed(struct dvb_demux_feed *feed) -{ - struct pd_dvb_adapter *pd_dvb = feed->demux->priv; - - if (!pd_dvb) - return -1; - if (atomic_dec_and_test(&pd_dvb->active_feed)) - dvb_stop_streaming(pd_dvb); - return 0; -} - -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); -int pd_dvb_usb_device_init(struct poseidon *pd) -{ - struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; - struct dvb_demux *dvbdemux; - int ret = 0; - - pd_dvb->ep_addr = 0x82; - atomic_set(&pd_dvb->users, 0); - atomic_set(&pd_dvb->active_feed, 0); - pd_dvb->pd_device = pd; - - ret = dvb_register_adapter(&pd_dvb->dvb_adap, - "Poseidon dvbt adapter", - THIS_MODULE, - NULL /* for hibernation correctly*/, - adapter_nr); - if (ret < 0) - goto error1; - - /* register frontend */ - pd_dvb->dvb_fe.demodulator_priv = pd; - memcpy(&pd_dvb->dvb_fe.ops, &poseidon_frontend_ops, - sizeof(struct dvb_frontend_ops)); - ret = dvb_register_frontend(&pd_dvb->dvb_adap, &pd_dvb->dvb_fe); - if (ret < 0) - goto error2; - - /* register demux device */ - dvbdemux = &pd_dvb->demux; - dvbdemux->dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; - dvbdemux->priv = pd_dvb; - dvbdemux->feednum = dvbdemux->filternum = 64; - dvbdemux->start_feed = pd_start_feed; - dvbdemux->stop_feed = pd_stop_feed; - dvbdemux->write_to_decoder = NULL; - - ret = dvb_dmx_init(dvbdemux); - if (ret < 0) - goto error3; - - pd_dvb->dmxdev.filternum = pd_dvb->demux.filternum; - pd_dvb->dmxdev.demux = &pd_dvb->demux.dmx; - pd_dvb->dmxdev.capabilities = 0; - - ret = dvb_dmxdev_init(&pd_dvb->dmxdev, &pd_dvb->dvb_adap); - if (ret < 0) - goto error3; - return 0; - -error3: - dvb_unregister_frontend(&pd_dvb->dvb_fe); -error2: - dvb_unregister_adapter(&pd_dvb->dvb_adap); -error1: - return ret; -} - -void pd_dvb_usb_device_exit(struct poseidon *pd) -{ - struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; - - while (atomic_read(&pd_dvb->users) != 0 - || atomic_read(&pd_dvb->active_feed) != 0) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ); - } - dvb_dmxdev_release(&pd_dvb->dmxdev); - dvb_unregister_frontend(&pd_dvb->dvb_fe); - dvb_unregister_adapter(&pd_dvb->dvb_adap); - pd_dvb_usb_device_cleanup(pd); -} - -void pd_dvb_usb_device_cleanup(struct poseidon *pd) -{ - struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; - - dvb_urb_cleanup(pd_dvb); -} - -int pd_dvb_get_adapter_num(struct pd_dvb_adapter *pd_dvb) -{ - return pd_dvb->dvb_adap.num; -} diff --git a/drivers/staging/media/tlg2300/pd-main.c b/drivers/staging/media/tlg2300/pd-main.c deleted file mode 100644 index b31f4791b8ff..000000000000 --- a/drivers/staging/media/tlg2300/pd-main.c +++ /dev/null @@ -1,553 +0,0 @@ -/* - * device driver for Telegent tlg2300 based TV cards - * - * Author : - * Kang Yong <kangyong@telegent.com> - * Zhang Xiaobing <xbzhang@telegent.com> - * Huang Shijie <zyziii@telegent.com> or <shijie8@gmail.com> - * - * (c) 2009 Telegent Systems - * (c) 2010 Telegent Systems - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/kref.h> -#include <linux/suspend.h> -#include <linux/usb/quirks.h> -#include <linux/ctype.h> -#include <linux/string.h> -#include <linux/types.h> -#include <linux/firmware.h> - -#include "vendorcmds.h" -#include "pd-common.h" - -#define VENDOR_ID 0x1B24 -#define PRODUCT_ID 0x4001 -static struct usb_device_id id_table[] = { - { USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 0) }, - { USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 1) }, - { }, -}; -MODULE_DEVICE_TABLE(usb, id_table); - -int debug_mode; -module_param(debug_mode, int, 0644); -MODULE_PARM_DESC(debug_mode, "0 = disable, 1 = enable, 2 = verbose"); - -#define TLG2300_FIRMWARE "tlg2300_firmware.bin" -static const char *firmware_name = TLG2300_FIRMWARE; -static LIST_HEAD(pd_device_list); - -/* - * send set request to USB firmware. - */ -s32 send_set_req(struct poseidon *pd, u8 cmdid, s32 param, s32 *cmd_status) -{ - s32 ret; - s8 data[32] = {}; - u16 lower_16, upper_16; - - if (pd->state & POSEIDON_STATE_DISCONNECT) - return -ENODEV; - - mdelay(30); - - if (param == 0) { - upper_16 = lower_16 = 0; - } else { - /* send 32 bit param as two 16 bit param,little endian */ - lower_16 = (unsigned short)(param & 0xffff); - upper_16 = (unsigned short)((param >> 16) & 0xffff); - } - ret = usb_control_msg(pd->udev, - usb_rcvctrlpipe(pd->udev, 0), - REQ_SET_CMD | cmdid, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - lower_16, - upper_16, - &data, - sizeof(*cmd_status), - USB_CTRL_GET_TIMEOUT); - - if (!ret) { - return -ENXIO; - } else { - /* 1st 4 bytes into cmd_status */ - memcpy((char *)cmd_status, &(data[0]), sizeof(*cmd_status)); - } - return 0; -} - -/* - * send get request to Poseidon firmware. - */ -s32 send_get_req(struct poseidon *pd, u8 cmdid, s32 param, - void *buf, s32 *cmd_status, s32 datalen) -{ - s32 ret; - s8 data[128] = {}; - u16 lower_16, upper_16; - - if (pd->state & POSEIDON_STATE_DISCONNECT) - return -ENODEV; - - mdelay(30); - if (param == 0) { - upper_16 = lower_16 = 0; - } else { - /*send 32 bit param as two 16 bit param, little endian */ - lower_16 = (unsigned short)(param & 0xffff); - upper_16 = (unsigned short)((param >> 16) & 0xffff); - } - ret = usb_control_msg(pd->udev, - usb_rcvctrlpipe(pd->udev, 0), - REQ_GET_CMD | cmdid, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - lower_16, - upper_16, - &data, - (datalen + sizeof(*cmd_status)), - USB_CTRL_GET_TIMEOUT); - - if (ret < 0) { - return -ENXIO; - } else { - /* 1st 4 bytes into cmd_status, remaining data into cmd_data */ - memcpy((char *)cmd_status, &data[0], sizeof(*cmd_status)); - memcpy((char *)buf, &data[sizeof(*cmd_status)], datalen); - } - return 0; -} - -static int pm_notifier_block(struct notifier_block *nb, - unsigned long event, void *dummy) -{ - struct poseidon *pd = NULL; - struct list_head *node, *next; - - switch (event) { - case PM_POST_HIBERNATION: - list_for_each_safe(node, next, &pd_device_list) { - struct usb_device *udev; - struct usb_interface *iface; - int rc = 0; - - pd = container_of(node, struct poseidon, device_list); - udev = pd->udev; - iface = pd->interface; - - /* It will cause the system to reload the firmware */ - rc = usb_lock_device_for_reset(udev, iface); - if (rc >= 0) { - usb_reset_device(udev); - usb_unlock_device(udev); - } - } - break; - default: - break; - } - log("event :%ld\n", event); - return 0; -} - -static struct notifier_block pm_notifer = { - .notifier_call = pm_notifier_block, -}; - -int set_tuner_mode(struct poseidon *pd, unsigned char mode) -{ - s32 ret, cmd_status; - - if (pd->state & POSEIDON_STATE_DISCONNECT) - return -ENODEV; - - ret = send_set_req(pd, TUNE_MODE_SELECT, mode, &cmd_status); - if (ret || cmd_status) - return -ENXIO; - return 0; -} - -void poseidon_delete(struct kref *kref) -{ - struct poseidon *pd = container_of(kref, struct poseidon, kref); - - if (!pd) - return; - list_del_init(&pd->device_list); - - pd_dvb_usb_device_cleanup(pd); - /* clean_audio_data(&pd->audio_data);*/ - - if (pd->udev) { - usb_put_dev(pd->udev); - pd->udev = NULL; - } - if (pd->interface) { - usb_put_intf(pd->interface); - pd->interface = NULL; - } - kfree(pd); - log(); -} - -static int firmware_download(struct usb_device *udev) -{ - int ret = 0, actual_length; - const struct firmware *fw = NULL; - void *fwbuf = NULL; - size_t fwlength = 0, offset; - size_t max_packet_size; - - ret = request_firmware(&fw, firmware_name, &udev->dev); - if (ret) { - log("download err : %d", ret); - return ret; - } - - fwlength = fw->size; - - fwbuf = kmemdup(fw->data, fwlength, GFP_KERNEL); - if (!fwbuf) { - ret = -ENOMEM; - goto out; - } - - max_packet_size = le16_to_cpu(udev->ep_out[0x1]->desc.wMaxPacketSize); - log("\t\t download size : %d", (int)max_packet_size); - - for (offset = 0; offset < fwlength; offset += max_packet_size) { - actual_length = 0; - ret = usb_bulk_msg(udev, - usb_sndbulkpipe(udev, 0x01), /* ep 1 */ - fwbuf + offset, - min(max_packet_size, fwlength - offset), - &actual_length, - HZ * 10); - if (ret) - break; - } - kfree(fwbuf); -out: - release_firmware(fw); - return ret; -} - -static inline struct poseidon *get_pd(struct usb_interface *intf) -{ - return usb_get_intfdata(intf); -} - -#ifdef CONFIG_PM -/* one-to-one map : poseidon{} <----> usb_device{}'s port */ -static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev) -{ - pd->portnum = udev->portnum; -} - -static inline int get_autopm_ref(struct poseidon *pd) -{ - return pd->video_data.users + pd->vbi_data.users + pd->audio.users - + atomic_read(&pd->dvb_data.users) + - !list_empty(&pd->radio_data.fm_dev.fh_list); -} - -/* fixup something for poseidon */ -static inline struct poseidon *fixup(struct poseidon *pd) -{ - int count; - - /* old udev and interface have gone, so put back reference . */ - count = get_autopm_ref(pd); - log("count : %d, ref count : %d", count, get_pm_count(pd)); - while (count--) - usb_autopm_put_interface(pd->interface); - /*usb_autopm_set_interface(pd->interface); */ - - usb_put_dev(pd->udev); - usb_put_intf(pd->interface); - log("event : %d\n", pd->msg.event); - return pd; -} - -static struct poseidon *find_old_poseidon(struct usb_device *udev) -{ - struct poseidon *pd; - - list_for_each_entry(pd, &pd_device_list, device_list) { - if (pd->portnum == udev->portnum && in_hibernation(pd)) - return fixup(pd); - } - return NULL; -} - -/* Is the card working now ? */ -static inline int is_working(struct poseidon *pd) -{ - return get_pm_count(pd) > 0; -} - -static int poseidon_suspend(struct usb_interface *intf, pm_message_t msg) -{ - struct poseidon *pd = get_pd(intf); - - if (!pd) - return 0; - if (!is_working(pd)) { - if (get_pm_count(pd) <= 0 && !in_hibernation(pd)) { - pd->msg.event = PM_EVENT_AUTO_SUSPEND; - pd->pm_resume = NULL; /* a good guard */ - printk(KERN_DEBUG "TLG2300 auto suspend\n"); - } - return 0; - } - pd->msg = msg; /* save it here */ - logpm(pd); - return pd->pm_suspend ? pd->pm_suspend(pd) : 0; -} - -static int poseidon_resume(struct usb_interface *intf) -{ - struct poseidon *pd = get_pd(intf); - - if (!pd) - return 0; - printk(KERN_DEBUG "TLG2300 resume\n"); - - if (!is_working(pd)) { - if (PM_EVENT_AUTO_SUSPEND == pd->msg.event) - pd->msg = PMSG_ON; - return 0; - } - if (in_hibernation(pd)) { - logpm(pd); - return 0; - } - logpm(pd); - return pd->pm_resume ? pd->pm_resume(pd) : 0; -} - -static void hibernation_resume(struct work_struct *w) -{ - struct poseidon *pd = container_of(w, struct poseidon, pm_work); - int count; - - pd->msg.event = 0; /* clear it here */ - pd->state &= ~POSEIDON_STATE_DISCONNECT; - - /* set the new interface's reference */ - count = get_autopm_ref(pd); - while (count--) - usb_autopm_get_interface(pd->interface); - - /* resume the context */ - logpm(pd); - if (pd->pm_resume) - pd->pm_resume(pd); -} -#else /* CONFIG_PM is not enabled: */ -static inline struct poseidon *find_old_poseidon(struct usb_device *udev) -{ - return NULL; -} - -static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev) -{ -} -#endif - -static int check_firmware(struct usb_device *udev) -{ - void *buf; - int ret; - struct cmd_firmware_vers_s *cmd_firm; - - buf = kzalloc(sizeof(*cmd_firm) + sizeof(u32), GFP_KERNEL); - if (!buf) - return -ENOMEM; - ret = usb_control_msg(udev, - usb_rcvctrlpipe(udev, 0), - REQ_GET_CMD | GET_FW_ID, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, - 0, - buf, - sizeof(*cmd_firm) + sizeof(u32), - USB_CTRL_GET_TIMEOUT); - kfree(buf); - - if (ret < 0) - return firmware_download(udev); - return 0; -} - -static int poseidon_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(interface); - struct poseidon *pd = NULL; - int ret = 0; - int new_one = 0; - - /* download firmware */ - ret = check_firmware(udev); - if (ret) - return ret; - - /* Do I recovery from the hibernate ? */ - pd = find_old_poseidon(udev); - if (!pd) { - pd = kzalloc(sizeof(*pd), GFP_KERNEL); - if (!pd) - return -ENOMEM; - kref_init(&pd->kref); - set_map_flags(pd, udev); - new_one = 1; - } - - pd->udev = usb_get_dev(udev); - pd->interface = usb_get_intf(interface); - usb_set_intfdata(interface, pd); - - if (new_one) { - logpm(pd); - mutex_init(&pd->lock); - - /* register v4l2 device */ - ret = v4l2_device_register(&interface->dev, &pd->v4l2_dev); - if (ret) - goto err_v4l2; - - /* register devices in directory /dev */ - ret = pd_video_init(pd); - if (ret) - goto err_video; - ret = poseidon_audio_init(pd); - if (ret) - goto err_audio; - ret = poseidon_fm_init(pd); - if (ret) - goto err_fm; - ret = pd_dvb_usb_device_init(pd); - if (ret) - goto err_dvb; - - INIT_LIST_HEAD(&pd->device_list); - list_add_tail(&pd->device_list, &pd_device_list); - } - - device_init_wakeup(&udev->dev, 1); -#ifdef CONFIG_PM - pm_runtime_set_autosuspend_delay(&pd->udev->dev, - 1000 * PM_SUSPEND_DELAY); - usb_enable_autosuspend(pd->udev); - - if (in_hibernation(pd)) { - INIT_WORK(&pd->pm_work, hibernation_resume); - schedule_work(&pd->pm_work); - } -#endif - return 0; -err_dvb: - poseidon_fm_exit(pd); -err_fm: - poseidon_audio_free(pd); -err_audio: - pd_video_exit(pd); -err_video: - v4l2_device_unregister(&pd->v4l2_dev); -err_v4l2: - usb_put_intf(pd->interface); - usb_put_dev(pd->udev); - kfree(pd); - return ret; -} - -static void poseidon_disconnect(struct usb_interface *interface) -{ - struct poseidon *pd = get_pd(interface); - - if (!pd) - return; - logpm(pd); - if (in_hibernation(pd)) - return; - - mutex_lock(&pd->lock); - pd->state |= POSEIDON_STATE_DISCONNECT; - mutex_unlock(&pd->lock); - - /* stop urb transferring */ - stop_all_video_stream(pd); - dvb_stop_streaming(&pd->dvb_data); - - /*unregister v4l2 device */ - v4l2_device_unregister(&pd->v4l2_dev); - - pd_dvb_usb_device_exit(pd); - poseidon_fm_exit(pd); - - poseidon_audio_free(pd); - pd_video_exit(pd); - - usb_set_intfdata(interface, NULL); - kref_put(&pd->kref, poseidon_delete); -} - -static struct usb_driver poseidon_driver = { - .name = "poseidon", - .probe = poseidon_probe, - .disconnect = poseidon_disconnect, - .id_table = id_table, -#ifdef CONFIG_PM - .suspend = poseidon_suspend, - .resume = poseidon_resume, -#endif - .supports_autosuspend = 1, -}; - -static int __init poseidon_init(void) -{ - int ret; - - ret = usb_register(&poseidon_driver); - if (ret) - return ret; - register_pm_notifier(&pm_notifer); - return ret; -} - -static void __exit poseidon_exit(void) -{ - log(); - unregister_pm_notifier(&pm_notifer); - usb_deregister(&poseidon_driver); -} - -module_init(poseidon_init); -module_exit(poseidon_exit); - -MODULE_AUTHOR("Telegent Systems"); -MODULE_DESCRIPTION("For tlg2300-based USB device"); -MODULE_LICENSE("GPL"); -MODULE_VERSION("0.0.2"); -MODULE_FIRMWARE(TLG2300_FIRMWARE); diff --git a/drivers/staging/media/tlg2300/pd-radio.c b/drivers/staging/media/tlg2300/pd-radio.c deleted file mode 100644 index b391194a840c..000000000000 --- a/drivers/staging/media/tlg2300/pd-radio.c +++ /dev/null @@ -1,339 +0,0 @@ -#include <linux/init.h> -#include <linux/list.h> -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/bitmap.h> -#include <linux/usb.h> -#include <linux/i2c.h> -#include <media/v4l2-dev.h> -#include <linux/mm.h> -#include <linux/mutex.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-event.h> -#include <media/v4l2-fh.h> -#include <linux/sched.h> - -#include "pd-common.h" -#include "vendorcmds.h" - -static int set_frequency(struct poseidon *p, __u32 frequency); -static int poseidon_fm_close(struct file *filp); -static int poseidon_fm_open(struct file *filp); - -#define TUNER_FREQ_MIN_FM 76000000U -#define TUNER_FREQ_MAX_FM 108000000U - -#define MAX_PREEMPHASIS (V4L2_PREEMPHASIS_75_uS + 1) -static int preemphasis[MAX_PREEMPHASIS] = { - TLG_TUNE_ASTD_NONE, /* V4L2_PREEMPHASIS_DISABLED */ - TLG_TUNE_ASTD_FM_EUR, /* V4L2_PREEMPHASIS_50_uS */ - TLG_TUNE_ASTD_FM_US, /* V4L2_PREEMPHASIS_75_uS */ -}; - -static int poseidon_check_mode_radio(struct poseidon *p) -{ - int ret; - u32 status; - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/2); - ret = usb_set_interface(p->udev, 0, BULK_ALTERNATE_IFACE); - if (ret < 0) - goto out; - - ret = set_tuner_mode(p, TLG_MODE_FM_RADIO); - if (ret != 0) - goto out; - - ret = send_set_req(p, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &status); - ret = send_set_req(p, TUNER_AUD_ANA_STD, - p->radio_data.pre_emphasis, &status); - ret |= send_set_req(p, TUNER_AUD_MODE, - TLG_TUNE_TVAUDIO_MODE_STEREO, &status); - ret |= send_set_req(p, AUDIO_SAMPLE_RATE_SEL, - ATV_AUDIO_RATE_48K, &status); - ret |= send_set_req(p, TUNE_FREQ_SELECT, TUNER_FREQ_MIN_FM, &status); -out: - return ret; -} - -#ifdef CONFIG_PM -static int pm_fm_suspend(struct poseidon *p) -{ - logpm(p); - pm_alsa_suspend(p); - usb_set_interface(p->udev, 0, 0); - msleep(300); - return 0; -} - -static int pm_fm_resume(struct poseidon *p) -{ - logpm(p); - poseidon_check_mode_radio(p); - set_frequency(p, p->radio_data.fm_freq); - pm_alsa_resume(p); - return 0; -} -#endif - -static int poseidon_fm_open(struct file *filp) -{ - struct poseidon *p = video_drvdata(filp); - int ret = 0; - - mutex_lock(&p->lock); - if (p->state & POSEIDON_STATE_DISCONNECT) { - ret = -ENODEV; - goto out; - } - - if (p->state && !(p->state & POSEIDON_STATE_FM)) { - ret = -EBUSY; - goto out; - } - ret = v4l2_fh_open(filp); - if (ret) - goto out; - - usb_autopm_get_interface(p->interface); - if (0 == p->state) { - struct video_device *vfd = &p->radio_data.fm_dev; - - /* default pre-emphasis */ - if (p->radio_data.pre_emphasis == 0) - p->radio_data.pre_emphasis = TLG_TUNE_ASTD_FM_EUR; - set_debug_mode(vfd, debug_mode); - - ret = poseidon_check_mode_radio(p); - if (ret < 0) { - usb_autopm_put_interface(p->interface); - goto out; - } - p->state |= POSEIDON_STATE_FM; - } - kref_get(&p->kref); -out: - mutex_unlock(&p->lock); - return ret; -} - -static int poseidon_fm_close(struct file *filp) -{ - struct poseidon *p = video_drvdata(filp); - struct radio_data *fm = &p->radio_data; - uint32_t status; - - mutex_lock(&p->lock); - if (v4l2_fh_is_singular_file(filp)) - p->state &= ~POSEIDON_STATE_FM; - - if (fm->is_radio_streaming && filp == p->file_for_stream) { - fm->is_radio_streaming = 0; - send_set_req(p, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, &status); - } - usb_autopm_put_interface(p->interface); - mutex_unlock(&p->lock); - - kref_put(&p->kref, poseidon_delete); - return v4l2_fh_release(filp); -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *v) -{ - struct poseidon *p = video_drvdata(file); - - strlcpy(v->driver, "tele-radio", sizeof(v->driver)); - strlcpy(v->card, "Telegent Poseidon", sizeof(v->card)); - usb_make_path(p->udev, v->bus_info, sizeof(v->bus_info)); - v->device_caps = V4L2_CAP_TUNER | V4L2_CAP_RADIO; - /* Report all capabilities of the USB device */ - v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS | - V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE | - V4L2_CAP_AUDIO | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE; - return 0; -} - -static const struct v4l2_file_operations poseidon_fm_fops = { - .owner = THIS_MODULE, - .open = poseidon_fm_open, - .release = poseidon_fm_close, - .poll = v4l2_ctrl_poll, - .unlocked_ioctl = video_ioctl2, -}; - -static int tlg_fm_vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *vt) -{ - struct poseidon *p = video_drvdata(file); - struct tuner_fm_sig_stat_s fm_stat = {}; - int ret, status, count = 5; - - if (vt->index != 0) - return -EINVAL; - - vt->type = V4L2_TUNER_RADIO; - vt->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW; - vt->rangelow = TUNER_FREQ_MIN_FM * 2 / 125; - vt->rangehigh = TUNER_FREQ_MAX_FM * 2 / 125; - vt->rxsubchans = V4L2_TUNER_SUB_STEREO; - vt->audmode = V4L2_TUNER_MODE_STEREO; - vt->signal = 0; - vt->afc = 0; - strlcpy(vt->name, "Radio", sizeof(vt->name)); - - mutex_lock(&p->lock); - ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO, - &fm_stat, &status, sizeof(fm_stat)); - - while (fm_stat.sig_lock_busy && count-- && !ret) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ); - - ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO, - &fm_stat, &status, sizeof(fm_stat)); - } - mutex_unlock(&p->lock); - - if (ret || status) { - vt->signal = 0; - } else if ((fm_stat.sig_present || fm_stat.sig_locked) - && fm_stat.sig_strength == 0) { - vt->signal = 0xffff; - } else - vt->signal = (fm_stat.sig_strength * 255 / 10) << 8; - - return 0; -} - -static int fm_get_freq(struct file *file, void *priv, - struct v4l2_frequency *argp) -{ - struct poseidon *p = video_drvdata(file); - - if (argp->tuner) - return -EINVAL; - argp->frequency = p->radio_data.fm_freq; - return 0; -} - -static int set_frequency(struct poseidon *p, __u32 frequency) -{ - __u32 freq ; - int ret, status; - - mutex_lock(&p->lock); - - ret = send_set_req(p, TUNER_AUD_ANA_STD, - p->radio_data.pre_emphasis, &status); - - freq = (frequency * 125) / 2; /* Hz */ - freq = clamp(freq, TUNER_FREQ_MIN_FM, TUNER_FREQ_MAX_FM); - - ret = send_set_req(p, TUNE_FREQ_SELECT, freq, &status); - if (ret < 0) - goto error ; - ret = send_set_req(p, TAKE_REQUEST, 0, &status); - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/4); - if (!p->radio_data.is_radio_streaming) { - ret = send_set_req(p, TAKE_REQUEST, 0, &status); - ret = send_set_req(p, PLAY_SERVICE, - TLG_TUNE_PLAY_SVC_START, &status); - p->radio_data.is_radio_streaming = 1; - } - p->radio_data.fm_freq = freq * 2 / 125; -error: - mutex_unlock(&p->lock); - return ret; -} - -static int fm_set_freq(struct file *file, void *priv, - const struct v4l2_frequency *argp) -{ - struct poseidon *p = video_drvdata(file); - - if (argp->tuner) - return -EINVAL; - p->file_for_stream = file; -#ifdef CONFIG_PM - p->pm_suspend = pm_fm_suspend; - p->pm_resume = pm_fm_resume; -#endif - return set_frequency(p, argp->frequency); -} - -static int tlg_fm_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct poseidon *p = container_of(ctrl->handler, struct poseidon, - radio_data.ctrl_handler); - int pre_emphasis; - u32 status; - - switch (ctrl->id) { - case V4L2_CID_TUNE_PREEMPHASIS: - pre_emphasis = preemphasis[ctrl->val]; - send_set_req(p, TUNER_AUD_ANA_STD, pre_emphasis, &status); - p->radio_data.pre_emphasis = pre_emphasis; - return 0; - } - return -EINVAL; -} - -static int vidioc_s_tuner(struct file *file, void *priv, const struct v4l2_tuner *vt) -{ - return vt->index > 0 ? -EINVAL : 0; -} - -static const struct v4l2_ctrl_ops tlg_fm_ctrl_ops = { - .s_ctrl = tlg_fm_s_ctrl, -}; - -static const struct v4l2_ioctl_ops poseidon_fm_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_tuner = tlg_fm_vidioc_g_tuner, - .vidioc_g_frequency = fm_get_freq, - .vidioc_s_frequency = fm_set_freq, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static struct video_device poseidon_fm_template = { - .name = "Telegent-Radio", - .fops = &poseidon_fm_fops, - .minor = -1, - .release = video_device_release_empty, - .ioctl_ops = &poseidon_fm_ioctl_ops, -}; - -int poseidon_fm_init(struct poseidon *p) -{ - struct video_device *vfd = &p->radio_data.fm_dev; - struct v4l2_ctrl_handler *hdl = &p->radio_data.ctrl_handler; - - *vfd = poseidon_fm_template; - - set_frequency(p, TUNER_FREQ_MIN_FM); - v4l2_ctrl_handler_init(hdl, 1); - v4l2_ctrl_new_std_menu(hdl, &tlg_fm_ctrl_ops, V4L2_CID_TUNE_PREEMPHASIS, - V4L2_PREEMPHASIS_75_uS, 0, V4L2_PREEMPHASIS_50_uS); - if (hdl->error) { - v4l2_ctrl_handler_free(hdl); - return hdl->error; - } - vfd->v4l2_dev = &p->v4l2_dev; - vfd->ctrl_handler = hdl; - video_set_drvdata(vfd, p); - return video_register_device(vfd, VFL_TYPE_RADIO, -1); -} - -int poseidon_fm_exit(struct poseidon *p) -{ - video_unregister_device(&p->radio_data.fm_dev); - v4l2_ctrl_handler_free(&p->radio_data.ctrl_handler); - return 0; -} diff --git a/drivers/staging/media/tlg2300/pd-video.c b/drivers/staging/media/tlg2300/pd-video.c deleted file mode 100644 index 8cd7f02fcf9f..000000000000 --- a/drivers/staging/media/tlg2300/pd-video.c +++ /dev/null @@ -1,1570 +0,0 @@ -#include <linux/fs.h> -#include <linux/vmalloc.h> -#include <linux/videodev2.h> -#include <linux/usb.h> -#include <linux/mm.h> -#include <linux/sched.h> -#include <linux/slab.h> - -#include <media/v4l2-ioctl.h> -#include <media/v4l2-dev.h> -#include <media/v4l2-ctrls.h> - -#include "pd-common.h" -#include "vendorcmds.h" - -#ifdef CONFIG_PM -static int pm_video_suspend(struct poseidon *pd); -static int pm_video_resume(struct poseidon *pd); -#endif -static void iso_bubble_handler(struct work_struct *w); - -static int usb_transfer_mode; -module_param(usb_transfer_mode, int, 0644); -MODULE_PARM_DESC(usb_transfer_mode, "0 = Bulk, 1 = Isochronous"); - -static const struct poseidon_format poseidon_formats[] = { - { "YUV 422", V4L2_PIX_FMT_YUYV, 16, 0}, - { "RGB565", V4L2_PIX_FMT_RGB565, 16, 0}, -}; - -static const struct poseidon_tvnorm poseidon_tvnorms[] = { - { V4L2_STD_PAL_D, "PAL-D", TLG_TUNE_VSTD_PAL_D }, - { V4L2_STD_PAL_B, "PAL-B", TLG_TUNE_VSTD_PAL_B }, - { V4L2_STD_PAL_G, "PAL-G", TLG_TUNE_VSTD_PAL_G }, - { V4L2_STD_PAL_H, "PAL-H", TLG_TUNE_VSTD_PAL_H }, - { V4L2_STD_PAL_I, "PAL-I", TLG_TUNE_VSTD_PAL_I }, - { V4L2_STD_PAL_M, "PAL-M", TLG_TUNE_VSTD_PAL_M }, - { V4L2_STD_PAL_N, "PAL-N", TLG_TUNE_VSTD_PAL_N_COMBO }, - { V4L2_STD_PAL_Nc, "PAL-Nc", TLG_TUNE_VSTD_PAL_N_COMBO }, - { V4L2_STD_NTSC_M, "NTSC-M", TLG_TUNE_VSTD_NTSC_M }, - { V4L2_STD_NTSC_M_JP, "NTSC-JP", TLG_TUNE_VSTD_NTSC_M_J }, - { V4L2_STD_SECAM_B, "SECAM-B", TLG_TUNE_VSTD_SECAM_B }, - { V4L2_STD_SECAM_D, "SECAM-D", TLG_TUNE_VSTD_SECAM_D }, - { V4L2_STD_SECAM_G, "SECAM-G", TLG_TUNE_VSTD_SECAM_G }, - { V4L2_STD_SECAM_H, "SECAM-H", TLG_TUNE_VSTD_SECAM_H }, - { V4L2_STD_SECAM_K, "SECAM-K", TLG_TUNE_VSTD_SECAM_K }, - { V4L2_STD_SECAM_K1, "SECAM-K1", TLG_TUNE_VSTD_SECAM_K1 }, - { V4L2_STD_SECAM_L, "SECAM-L", TLG_TUNE_VSTD_SECAM_L }, - { V4L2_STD_SECAM_LC, "SECAM-LC", TLG_TUNE_VSTD_SECAM_L1 }, -}; -static const unsigned int POSEIDON_TVNORMS = ARRAY_SIZE(poseidon_tvnorms); - -struct pd_audio_mode { - u32 tlg_audio_mode; - u32 v4l2_audio_sub; - u32 v4l2_audio_mode; -}; - -static const struct pd_audio_mode pd_audio_modes[] = { - { TLG_TUNE_TVAUDIO_MODE_MONO, V4L2_TUNER_SUB_MONO, - V4L2_TUNER_MODE_MONO }, - { TLG_TUNE_TVAUDIO_MODE_STEREO, V4L2_TUNER_SUB_STEREO, - V4L2_TUNER_MODE_STEREO }, - { TLG_TUNE_TVAUDIO_MODE_LANG_A, V4L2_TUNER_SUB_LANG1, - V4L2_TUNER_MODE_LANG1 }, - { TLG_TUNE_TVAUDIO_MODE_LANG_B, V4L2_TUNER_SUB_LANG2, - V4L2_TUNER_MODE_LANG2 }, - { TLG_TUNE_TVAUDIO_MODE_LANG_C, V4L2_TUNER_SUB_LANG1, - V4L2_TUNER_MODE_LANG1_LANG2 } -}; -static const unsigned int POSEIDON_AUDIOMODS = ARRAY_SIZE(pd_audio_modes); - -struct pd_input { - char *name; - uint32_t tlg_src; -}; - -static const struct pd_input pd_inputs[] = { - { "TV Antenna", TLG_SIG_SRC_ANTENNA }, - { "TV Cable", TLG_SIG_SRC_CABLE }, - { "TV SVideo", TLG_SIG_SRC_SVIDEO }, - { "TV Composite", TLG_SIG_SRC_COMPOSITE } -}; -static const unsigned int POSEIDON_INPUTS = ARRAY_SIZE(pd_inputs); - -struct video_std_to_audio_std { - v4l2_std_id video_std; - int audio_std; -}; - -static const struct video_std_to_audio_std video_to_audio_map[] = { - /* country : { 27, 32, 33, 34, 36, 44, 45, 46, 47, 48, 64, - 65, 86, 351, 352, 353, 354, 358, 372, 852, 972 } */ - { (V4L2_STD_PAL_I | V4L2_STD_PAL_B | V4L2_STD_PAL_D | - V4L2_STD_SECAM_L | V4L2_STD_SECAM_D), TLG_TUNE_ASTD_NICAM }, - - /* country : { 1, 52, 54, 55, 886 } */ - {V4L2_STD_NTSC_M | V4L2_STD_PAL_N | V4L2_STD_PAL_M, TLG_TUNE_ASTD_BTSC}, - - /* country : { 81 } */ - { V4L2_STD_NTSC_M_JP, TLG_TUNE_ASTD_EIAJ }, - - /* other country : TLG_TUNE_ASTD_A2 */ -}; -static const unsigned int map_size = ARRAY_SIZE(video_to_audio_map); - -static int get_audio_std(v4l2_std_id v4l2_std) -{ - int i = 0; - - for (; i < map_size; i++) { - if (v4l2_std & video_to_audio_map[i].video_std) - return video_to_audio_map[i].audio_std; - } - return TLG_TUNE_ASTD_A2; -} - -static int vidioc_querycap(struct file *file, void *fh, - struct v4l2_capability *cap) -{ - struct video_device *vdev = video_devdata(file); - struct poseidon *p = video_get_drvdata(vdev); - - strcpy(cap->driver, "tele-video"); - strcpy(cap->card, "Telegent Poseidon"); - usb_make_path(p->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_TUNER | V4L2_CAP_AUDIO | - V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; - if (vdev->vfl_type == VFL_TYPE_VBI) - cap->device_caps |= V4L2_CAP_VBI_CAPTURE; - else - cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS | - V4L2_CAP_RADIO | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE; - return 0; -} - -/*====================================================================*/ -static void init_copy(struct video_data *video, bool index) -{ - struct front_face *front = video->front; - - video->field_count = index; - video->lines_copied = 0; - video->prev_left = 0 ; - video->dst = (char *)videobuf_to_vmalloc(front->curr_frame) - + index * video->lines_size; - video->vbi->copied = 0; /* set it here */ -} - -static bool get_frame(struct front_face *front, int *need_init) -{ - struct videobuf_buffer *vb = front->curr_frame; - - if (vb) - return true; - - spin_lock(&front->queue_lock); - if (!list_empty(&front->active)) { - vb = list_entry(front->active.next, - struct videobuf_buffer, queue); - if (need_init) - *need_init = 1; - front->curr_frame = vb; - list_del_init(&vb->queue); - } - spin_unlock(&front->queue_lock); - - return !!vb; -} - -/* check if the video's buffer is ready */ -static bool get_video_frame(struct front_face *front, struct video_data *video) -{ - int need_init = 0; - bool ret = true; - - ret = get_frame(front, &need_init); - if (ret && need_init) - init_copy(video, 0); - return ret; -} - -static void submit_frame(struct front_face *front) -{ - struct videobuf_buffer *vb = front->curr_frame; - - if (vb == NULL) - return; - - front->curr_frame = NULL; - vb->state = VIDEOBUF_DONE; - vb->field_count++; - v4l2_get_timestamp(&vb->ts); - - wake_up(&vb->done); -} - -/* - * A frame is composed of two fields. If we receive all the two fields, - * call the submit_frame() to submit the whole frame to applications. - */ -static void end_field(struct video_data *video) -{ - if (1 == video->field_count) - submit_frame(video->front); - else - init_copy(video, 1); -} - -static void copy_video_data(struct video_data *video, char *src, - unsigned int count) -{ -#define copy_data(len) \ - do { \ - if (++video->lines_copied > video->lines_per_field) \ - goto overflow; \ - memcpy(video->dst, src, len);\ - video->dst += len + video->lines_size; \ - src += len; \ - count -= len; \ - } while (0) - - while (count && count >= video->lines_size) { - if (video->prev_left) { - copy_data(video->prev_left); - video->prev_left = 0; - continue; - } - copy_data(video->lines_size); - } - if (count && count < video->lines_size) { - memcpy(video->dst, src, count); - - video->prev_left = video->lines_size - count; - video->dst += count; - } - return; - -overflow: - end_field(video); -} - -static void check_trailer(struct video_data *video, char *src, int count) -{ - struct vbi_data *vbi = video->vbi; - int offset; /* trailer's offset */ - char *buf; - - offset = (video->context.pix.sizeimage / 2 + vbi->vbi_size / 2) - - (vbi->copied + video->lines_size * video->lines_copied); - if (video->prev_left) - offset -= (video->lines_size - video->prev_left); - - if (offset > count || offset <= 0) - goto short_package; - - buf = src + offset; - - /* trailer : (VFHS) + U32 + U32 + field_num */ - if (!strncmp(buf, "VFHS", 4)) { - int field_num = *((u32 *)(buf + 12)); - - if ((field_num & 1) ^ video->field_count) { - init_copy(video, video->field_count); - return; - } - copy_video_data(video, src, offset); - } -short_package: - end_field(video); -} - -/* ========== Check this more carefully! =========== */ -static inline void copy_vbi_data(struct vbi_data *vbi, - char *src, unsigned int count) -{ - struct front_face *front = vbi->front; - - if (front && get_frame(front, NULL)) { - char *buf = videobuf_to_vmalloc(front->curr_frame); - - if (vbi->video->field_count) - buf += (vbi->vbi_size / 2); - memcpy(buf + vbi->copied, src, count); - } - vbi->copied += count; -} - -/* - * Copy the normal data (VBI or VIDEO) without the trailer. - * VBI is not interlaced, while VIDEO is interlaced. - */ -static inline void copy_vbi_video_data(struct video_data *video, - char *src, unsigned int count) -{ - struct vbi_data *vbi = video->vbi; - unsigned int vbi_delta = (vbi->vbi_size / 2) - vbi->copied; - - if (vbi_delta >= count) { - copy_vbi_data(vbi, src, count); - } else { - if (vbi_delta) { - copy_vbi_data(vbi, src, vbi_delta); - - /* we receive the two fields of the VBI*/ - if (vbi->front && video->field_count) - submit_frame(vbi->front); - } - copy_video_data(video, src + vbi_delta, count - vbi_delta); - } -} - -static void urb_complete_bulk(struct urb *urb) -{ - struct front_face *front = urb->context; - struct video_data *video = &front->pd->video_data; - char *src = (char *)urb->transfer_buffer; - int count = urb->actual_length; - int ret = 0; - - if (!video->is_streaming || urb->status) { - if (urb->status == -EPROTO) - goto resend_it; - return; - } - if (!get_video_frame(front, video)) - goto resend_it; - - if (count == urb->transfer_buffer_length) - copy_vbi_video_data(video, src, count); - else - check_trailer(video, src, count); - -resend_it: - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret) - log(" submit failed: error %d", ret); -} - -/************************* for ISO *********************/ -#define GET_SUCCESS (0) -#define GET_TRAILER (1) -#define GET_TOO_MUCH_BUBBLE (2) -#define GET_NONE (3) -static int get_chunk(int start, struct urb *urb, - int *head, int *tail, int *bubble_err) -{ - struct usb_iso_packet_descriptor *pkt = NULL; - int ret = GET_SUCCESS; - - for (*head = *tail = -1; start < urb->number_of_packets; start++) { - pkt = &urb->iso_frame_desc[start]; - - /* handle the bubble of the Hub */ - if (-EOVERFLOW == pkt->status) { - if (++*bubble_err > urb->number_of_packets / 3) - return GET_TOO_MUCH_BUBBLE; - continue; - } - - /* This is the gap */ - if (pkt->status || pkt->actual_length <= 0 - || pkt->actual_length > ISO_PKT_SIZE) { - if (*head != -1) - break; - continue; - } - - /* a good isochronous packet */ - if (pkt->actual_length == ISO_PKT_SIZE) { - if (*head == -1) - *head = start; - *tail = start; - continue; - } - - /* trailer is here */ - if (pkt->actual_length < ISO_PKT_SIZE) { - if (*head == -1) { - *head = start; - *tail = start; - return GET_TRAILER; - } - break; - } - } - - if (*head == -1 && *tail == -1) - ret = GET_NONE; - return ret; -} - -/* - * |__|------|___|-----|_______| - * ^ ^ - * | | - * gap gap - */ -static void urb_complete_iso(struct urb *urb) -{ - struct front_face *front = urb->context; - struct video_data *video = &front->pd->video_data; - int bubble_err = 0, head = 0, tail = 0; - char *src = (char *)urb->transfer_buffer; - int ret = 0; - - if (!video->is_streaming) - return; - - do { - if (!get_video_frame(front, video)) - goto out; - - switch (get_chunk(head, urb, &head, &tail, &bubble_err)) { - case GET_SUCCESS: - copy_vbi_video_data(video, src + (head * ISO_PKT_SIZE), - (tail - head + 1) * ISO_PKT_SIZE); - break; - case GET_TRAILER: - check_trailer(video, src + (head * ISO_PKT_SIZE), - ISO_PKT_SIZE); - break; - case GET_NONE: - goto out; - case GET_TOO_MUCH_BUBBLE: - log("\t We got too much bubble"); - schedule_work(&video->bubble_work); - return; - } - } while (head = tail + 1, head < urb->number_of_packets); - -out: - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret) - log("usb_submit_urb err : %d", ret); -} -/*============================= [ end ] =====================*/ - -static int prepare_iso_urb(struct video_data *video) -{ - struct usb_device *udev = video->pd->udev; - int i; - - if (video->urb_array[0]) - return 0; - - for (i = 0; i < SBUF_NUM; i++) { - struct urb *urb; - void *mem; - int j; - - urb = usb_alloc_urb(PK_PER_URB, GFP_KERNEL); - if (urb == NULL) - goto out; - - video->urb_array[i] = urb; - mem = usb_alloc_coherent(udev, - ISO_PKT_SIZE * PK_PER_URB, - GFP_KERNEL, - &urb->transfer_dma); - - urb->complete = urb_complete_iso; /* handler */ - urb->dev = udev; - urb->context = video->front; - urb->pipe = usb_rcvisocpipe(udev, - video->endpoint_addr); - urb->interval = 1; - urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; - urb->number_of_packets = PK_PER_URB; - urb->transfer_buffer = mem; - urb->transfer_buffer_length = PK_PER_URB * ISO_PKT_SIZE; - - for (j = 0; j < PK_PER_URB; j++) { - urb->iso_frame_desc[j].offset = ISO_PKT_SIZE * j; - urb->iso_frame_desc[j].length = ISO_PKT_SIZE; - } - } - return 0; -out: - for (; i > 0; i--) - ; - return -ENOMEM; -} - -/* return the succeeded number of the allocation */ -int alloc_bulk_urbs_generic(struct urb **urb_array, int num, - struct usb_device *udev, u8 ep_addr, - int buf_size, gfp_t gfp_flags, - usb_complete_t complete_fn, void *context) -{ - int i = 0; - - for (; i < num; i++) { - void *mem; - struct urb *urb = usb_alloc_urb(0, gfp_flags); - if (urb == NULL) - return i; - - mem = usb_alloc_coherent(udev, buf_size, gfp_flags, - &urb->transfer_dma); - if (mem == NULL) { - usb_free_urb(urb); - return i; - } - - usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, ep_addr), - mem, buf_size, complete_fn, context); - urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - urb_array[i] = urb; - } - return i; -} - -void free_all_urb_generic(struct urb **urb_array, int num) -{ - int i; - struct urb *urb; - - for (i = 0; i < num; i++) { - urb = urb_array[i]; - if (urb) { - usb_free_coherent(urb->dev, - urb->transfer_buffer_length, - urb->transfer_buffer, - urb->transfer_dma); - usb_free_urb(urb); - urb_array[i] = NULL; - } - } -} - -static int prepare_bulk_urb(struct video_data *video) -{ - if (video->urb_array[0]) - return 0; - - alloc_bulk_urbs_generic(video->urb_array, SBUF_NUM, - video->pd->udev, video->endpoint_addr, - 0x2000, GFP_KERNEL, - urb_complete_bulk, video->front); - return 0; -} - -/* free the URBs */ -static void free_all_urb(struct video_data *video) -{ - free_all_urb_generic(video->urb_array, SBUF_NUM); -} - -static void pd_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb) -{ - videobuf_vmalloc_free(vb); - vb->state = VIDEOBUF_NEEDS_INIT; -} - -static void pd_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) -{ - struct front_face *front = q->priv_data; - vb->state = VIDEOBUF_QUEUED; - list_add_tail(&vb->queue, &front->active); -} - -static int pd_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct front_face *front = q->priv_data; - int rc; - - switch (front->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (VIDEOBUF_NEEDS_INIT == vb->state) { - struct v4l2_pix_format *pix; - - pix = &front->pd->video_data.context.pix; - vb->size = pix->sizeimage; /* real frame size */ - vb->width = pix->width; - vb->height = pix->height; - rc = videobuf_iolock(q, vb, NULL); - if (rc < 0) - return rc; - } - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - if (VIDEOBUF_NEEDS_INIT == vb->state) { - vb->size = front->pd->vbi_data.vbi_size; - rc = videobuf_iolock(q, vb, NULL); - if (rc < 0) - return rc; - } - break; - default: - return -EINVAL; - } - vb->field = field; - vb->state = VIDEOBUF_PREPARED; - return 0; -} - -static int fire_all_urb(struct video_data *video) -{ - int i, ret; - - video->is_streaming = 1; - - for (i = 0; i < SBUF_NUM; i++) { - ret = usb_submit_urb(video->urb_array[i], GFP_KERNEL); - if (ret) - log("(%d) failed: error %d", i, ret); - } - return ret; -} - -static int start_video_stream(struct poseidon *pd) -{ - struct video_data *video = &pd->video_data; - s32 cmd_status; - - send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); - send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_START, &cmd_status); - - if (pd->cur_transfer_mode) { - prepare_iso_urb(video); - INIT_WORK(&video->bubble_work, iso_bubble_handler); - } else { - /* The bulk mode does not need a bubble handler */ - prepare_bulk_urb(video); - } - fire_all_urb(video); - return 0; -} - -static int pd_buf_setup(struct videobuf_queue *q, unsigned int *count, - unsigned int *size) -{ - struct front_face *front = q->priv_data; - struct poseidon *pd = front->pd; - - switch (front->type) { - default: - return -EINVAL; - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - struct video_data *video = &pd->video_data; - struct v4l2_pix_format *pix = &video->context.pix; - - *size = PAGE_ALIGN(pix->sizeimage);/* page aligned frame size */ - if (*count < 4) - *count = 4; - if (1) { - /* same in different altersetting */ - video->endpoint_addr = 0x82; - video->vbi = &pd->vbi_data; - video->vbi->video = video; - video->pd = pd; - video->lines_per_field = pix->height / 2; - video->lines_size = pix->width * 2; - video->front = front; - } - return start_video_stream(pd); - } - - case V4L2_BUF_TYPE_VBI_CAPTURE: { - struct vbi_data *vbi = &pd->vbi_data; - - *size = PAGE_ALIGN(vbi->vbi_size); - log("size : %d", *size); - if (*count == 0) - *count = 4; - } - break; - } - return 0; -} - -static struct videobuf_queue_ops pd_video_qops = { - .buf_setup = pd_buf_setup, - .buf_prepare = pd_buf_prepare, - .buf_queue = pd_buf_queue, - .buf_release = pd_buf_release, -}; - -static int vidioc_enum_fmt(struct file *file, void *fh, - struct v4l2_fmtdesc *f) -{ - if (ARRAY_SIZE(poseidon_formats) <= f->index) - return -EINVAL; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->flags = 0; - f->pixelformat = poseidon_formats[f->index].fourcc; - strcpy(f->description, poseidon_formats[f->index].name); - return 0; -} - -static int vidioc_g_fmt(struct file *file, void *fh, struct v4l2_format *f) -{ - struct front_face *front = fh; - struct poseidon *pd = front->pd; - - f->fmt.pix = pd->video_data.context.pix; - return 0; -} - -/* - * VLC calls VIDIOC_S_STD before VIDIOC_S_FMT, while - * Mplayer calls them in the reverse order. - */ -static int pd_vidioc_s_fmt(struct poseidon *pd, struct v4l2_pix_format *pix) -{ - struct video_data *video = &pd->video_data; - struct running_context *context = &video->context; - struct v4l2_pix_format *pix_def = &context->pix; - s32 ret = 0, cmd_status = 0, vid_resol; - - /* set the pixel format to firmware */ - if (pix->pixelformat == V4L2_PIX_FMT_RGB565) { - vid_resol = TLG_TUNER_VID_FORMAT_RGB_565; - } else { - pix->pixelformat = V4L2_PIX_FMT_YUYV; - vid_resol = TLG_TUNER_VID_FORMAT_YUV; - } - ret = send_set_req(pd, VIDEO_STREAM_FMT_SEL, - vid_resol, &cmd_status); - - /* set the resolution to firmware */ - vid_resol = TLG_TUNE_VID_RES_720; - switch (pix->width) { - case 704: - vid_resol = TLG_TUNE_VID_RES_704; - break; - default: - pix->width = 720; - case 720: - break; - } - ret |= send_set_req(pd, VIDEO_ROSOLU_SEL, - vid_resol, &cmd_status); - if (ret || cmd_status) - return -EBUSY; - - pix_def->pixelformat = pix->pixelformat; /* save it */ - pix->height = (context->tvnormid & V4L2_STD_525_60) ? 480 : 576; - - /* Compare with the default setting */ - if ((pix_def->width != pix->width) - || (pix_def->height != pix->height)) { - pix_def->width = pix->width; - pix_def->height = pix->height; - pix_def->bytesperline = pix->width * 2; - pix_def->sizeimage = pix->width * pix->height * 2; - } - *pix = *pix_def; - - return 0; -} - -static int vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f) -{ - struct front_face *front = fh; - struct poseidon *pd = front->pd; - - /* stop VBI here */ - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type) - return -EINVAL; - - mutex_lock(&pd->lock); - if (pd->file_for_stream == NULL) - pd->file_for_stream = file; - else if (file != pd->file_for_stream) { - mutex_unlock(&pd->lock); - return -EINVAL; - } - - pd_vidioc_s_fmt(pd, &f->fmt.pix); - mutex_unlock(&pd->lock); - return 0; -} - -static int vidioc_g_fmt_vbi(struct file *file, void *fh, - struct v4l2_format *v4l2_f) -{ - struct front_face *front = fh; - struct poseidon *pd = front->pd; - struct v4l2_vbi_format *vbi_fmt = &v4l2_f->fmt.vbi; - - vbi_fmt->samples_per_line = 720 * 2; - vbi_fmt->sampling_rate = 6750000 * 4; - vbi_fmt->sample_format = V4L2_PIX_FMT_GREY; - vbi_fmt->offset = 64 * 4; /*FIXME: why offset */ - if (pd->video_data.context.tvnormid & V4L2_STD_525_60) { - vbi_fmt->start[0] = 10; - vbi_fmt->start[1] = 264; - vbi_fmt->count[0] = V4L_NTSC_VBI_LINES; - vbi_fmt->count[1] = V4L_NTSC_VBI_LINES; - } else { - vbi_fmt->start[0] = 6; - vbi_fmt->start[1] = 314; - vbi_fmt->count[0] = V4L_PAL_VBI_LINES; - vbi_fmt->count[1] = V4L_PAL_VBI_LINES; - } - vbi_fmt->flags = V4L2_VBI_UNSYNC; - return 0; -} - -static int set_std(struct poseidon *pd, v4l2_std_id norm) -{ - struct video_data *video = &pd->video_data; - struct vbi_data *vbi = &pd->vbi_data; - struct running_context *context; - struct v4l2_pix_format *pix; - s32 i, ret = 0, cmd_status, param; - int height; - - for (i = 0; i < POSEIDON_TVNORMS; i++) { - if (norm & poseidon_tvnorms[i].v4l2_id) { - param = poseidon_tvnorms[i].tlg_tvnorm; - log("name : %s", poseidon_tvnorms[i].name); - goto found; - } - } - return -EINVAL; -found: - mutex_lock(&pd->lock); - ret = send_set_req(pd, VIDEO_STD_SEL, param, &cmd_status); - if (ret || cmd_status) - goto out; - - /* Set vbi size and check the height of the frame */ - context = &video->context; - context->tvnormid = poseidon_tvnorms[i].v4l2_id; - if (context->tvnormid & V4L2_STD_525_60) { - vbi->vbi_size = V4L_NTSC_VBI_FRAMESIZE; - height = 480; - } else { - vbi->vbi_size = V4L_PAL_VBI_FRAMESIZE; - height = 576; - } - - pix = &context->pix; - if (pix->height != height) { - pix->height = height; - pix->sizeimage = pix->width * pix->height * 2; - } - -out: - mutex_unlock(&pd->lock); - return ret; -} - -static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id norm) -{ - struct front_face *front = fh; - - return set_std(front->pd, norm); -} - -static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm) -{ - struct front_face *front = fh; - - *norm = front->pd->video_data.context.tvnormid; - return 0; -} - -static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *in) -{ - if (in->index >= POSEIDON_INPUTS) - return -EINVAL; - strcpy(in->name, pd_inputs[in->index].name); - in->type = V4L2_INPUT_TYPE_TUNER; - - /* - * the audio input index mixed with this video input, - * Poseidon only have one audio/video, set to "0" - */ - in->audioset = 1; - in->tuner = 0; - in->std = V4L2_STD_ALL; - in->status = 0; - return 0; -} - -static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) -{ - struct front_face *front = fh; - struct poseidon *pd = front->pd; - struct running_context *context = &pd->video_data.context; - - *i = context->sig_index; - return 0; -} - -/* We can support several inputs */ -static int vidioc_s_input(struct file *file, void *fh, unsigned int i) -{ - struct front_face *front = fh; - struct poseidon *pd = front->pd; - s32 ret, cmd_status; - - if (i >= POSEIDON_INPUTS) - return -EINVAL; - ret = send_set_req(pd, SGNL_SRC_SEL, - pd_inputs[i].tlg_src, &cmd_status); - if (ret) - return ret; - - pd->video_data.context.sig_index = i; - return 0; -} - -static int tlg_s_ctrl(struct v4l2_ctrl *c) -{ - struct poseidon *pd = container_of(c->handler, struct poseidon, - video_data.ctrl_handler); - struct tuner_custom_parameter_s param = {0}; - s32 ret = 0, cmd_status, params; - - switch (c->id) { - case V4L2_CID_BRIGHTNESS: - param.param_id = CUST_PARM_ID_BRIGHTNESS_CTRL; - break; - case V4L2_CID_CONTRAST: - param.param_id = CUST_PARM_ID_CONTRAST_CTRL; - break; - case V4L2_CID_HUE: - param.param_id = CUST_PARM_ID_HUE_CTRL; - break; - case V4L2_CID_SATURATION: - param.param_id = CUST_PARM_ID_SATURATION_CTRL; - break; - } - param.param_value = c->val; - params = *(s32 *)¶m; /* temp code */ - - mutex_lock(&pd->lock); - ret = send_set_req(pd, TUNER_CUSTOM_PARAMETER, params, &cmd_status); - ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); - mutex_unlock(&pd->lock); - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/4); - return ret; -} - -/* Audio ioctls */ -static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) -{ - if (0 != a->index) - return -EINVAL; - a->capability = V4L2_AUDCAP_STEREO; - strcpy(a->name, "USB audio in"); - /*Poseidon have no AVL function.*/ - a->mode = 0; - return 0; -} - -static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) -{ - a->index = 0; - a->capability = V4L2_AUDCAP_STEREO; - strcpy(a->name, "USB audio in"); - a->mode = 0; - return 0; -} - -static int vidioc_s_audio(struct file *file, void *fh, const struct v4l2_audio *a) -{ - return (0 == a->index) ? 0 : -EINVAL; -} - -/* Tuner ioctls */ -static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *tuner) -{ - struct front_face *front = fh; - struct poseidon *pd = front->pd; - struct tuner_atv_sig_stat_s atv_stat; - s32 count = 5, ret, cmd_status; - int index; - - if (0 != tuner->index) - return -EINVAL; - - mutex_lock(&pd->lock); - ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV, - &atv_stat, &cmd_status, sizeof(atv_stat)); - - while (atv_stat.sig_lock_busy && count-- && !ret) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ); - - ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV, - &atv_stat, &cmd_status, sizeof(atv_stat)); - } - mutex_unlock(&pd->lock); - - if (debug_mode) - log("P:%d,S:%d", atv_stat.sig_present, atv_stat.sig_strength); - - if (ret || cmd_status) - tuner->signal = 0; - else if (atv_stat.sig_present && !atv_stat.sig_strength) - tuner->signal = 0xFFFF; - else - tuner->signal = (atv_stat.sig_strength * 255 / 10) << 8; - - strcpy(tuner->name, "Telegent Systems"); - tuner->type = V4L2_TUNER_ANALOG_TV; - tuner->rangelow = TUNER_FREQ_MIN / 62500; - tuner->rangehigh = TUNER_FREQ_MAX / 62500; - tuner->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; - index = pd->video_data.context.audio_idx; - tuner->rxsubchans = pd_audio_modes[index].v4l2_audio_sub; - tuner->audmode = pd_audio_modes[index].v4l2_audio_mode; - tuner->afc = 0; - return 0; -} - -static int pd_vidioc_s_tuner(struct poseidon *pd, int index) -{ - s32 ret = 0, cmd_status, param, audiomode; - - mutex_lock(&pd->lock); - param = pd_audio_modes[index].tlg_audio_mode; - ret = send_set_req(pd, TUNER_AUD_MODE, param, &cmd_status); - audiomode = get_audio_std(pd->video_data.context.tvnormid); - ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode, - &cmd_status); - if (!ret) - pd->video_data.context.audio_idx = index; - mutex_unlock(&pd->lock); - return ret; -} - -static int vidioc_s_tuner(struct file *file, void *fh, const struct v4l2_tuner *a) -{ - struct front_face *front = fh; - struct poseidon *pd = front->pd; - int index; - - if (0 != a->index) - return -EINVAL; - for (index = 0; index < POSEIDON_AUDIOMODS; index++) - if (a->audmode == pd_audio_modes[index].v4l2_audio_mode) - return pd_vidioc_s_tuner(pd, index); - return -EINVAL; -} - -static int vidioc_g_frequency(struct file *file, void *fh, - struct v4l2_frequency *freq) -{ - struct front_face *front = fh; - struct poseidon *pd = front->pd; - struct running_context *context = &pd->video_data.context; - - if (0 != freq->tuner) - return -EINVAL; - freq->frequency = context->freq; - freq->type = V4L2_TUNER_ANALOG_TV; - return 0; -} - -static int set_frequency(struct poseidon *pd, u32 *frequency) -{ - s32 ret = 0, param, cmd_status; - struct running_context *context = &pd->video_data.context; - - *frequency = clamp(*frequency, - TUNER_FREQ_MIN / 62500, TUNER_FREQ_MAX / 62500); - param = (*frequency) * 62500 / 1000; - - mutex_lock(&pd->lock); - ret = send_set_req(pd, TUNE_FREQ_SELECT, param, &cmd_status); - ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); - - msleep(250); /* wait for a while until the hardware is ready. */ - context->freq = *frequency; - mutex_unlock(&pd->lock); - return ret; -} - -static int vidioc_s_frequency(struct file *file, void *fh, - const struct v4l2_frequency *freq) -{ - struct front_face *front = fh; - struct poseidon *pd = front->pd; - u32 frequency = freq->frequency; - - if (freq->tuner) - return -EINVAL; -#ifdef CONFIG_PM - pd->pm_suspend = pm_video_suspend; - pd->pm_resume = pm_video_resume; -#endif - return set_frequency(pd, &frequency); -} - -static int vidioc_reqbufs(struct file *file, void *fh, - struct v4l2_requestbuffers *b) -{ - struct front_face *front = file->private_data; - return videobuf_reqbufs(&front->q, b); -} - -static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) -{ - struct front_face *front = file->private_data; - return videobuf_querybuf(&front->q, b); -} - -static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) -{ - struct front_face *front = file->private_data; - return videobuf_qbuf(&front->q, b); -} - -static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) -{ - struct front_face *front = file->private_data; - return videobuf_dqbuf(&front->q, b, file->f_flags & O_NONBLOCK); -} - -/* Just stop the URBs, do not free the URBs */ -static int usb_transfer_stop(struct video_data *video) -{ - if (video->is_streaming) { - int i; - s32 cmd_status; - struct poseidon *pd = video->pd; - - video->is_streaming = 0; - for (i = 0; i < SBUF_NUM; ++i) { - if (video->urb_array[i]) - usb_kill_urb(video->urb_array[i]); - } - - send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, - &cmd_status); - } - return 0; -} - -int stop_all_video_stream(struct poseidon *pd) -{ - struct video_data *video = &pd->video_data; - struct vbi_data *vbi = &pd->vbi_data; - - mutex_lock(&pd->lock); - if (video->is_streaming) { - struct front_face *front = video->front; - - /* stop the URBs */ - usb_transfer_stop(video); - free_all_urb(video); - - /* stop the host side of VIDEO */ - videobuf_stop(&front->q); - videobuf_mmap_free(&front->q); - - /* stop the host side of VBI */ - front = vbi->front; - if (front) { - videobuf_stop(&front->q); - videobuf_mmap_free(&front->q); - } - } - mutex_unlock(&pd->lock); - return 0; -} - -/* - * The bubbles can seriously damage the video's quality, - * though it occurs in very rare situation. - */ -static void iso_bubble_handler(struct work_struct *w) -{ - struct video_data *video; - struct poseidon *pd; - - video = container_of(w, struct video_data, bubble_work); - pd = video->pd; - - mutex_lock(&pd->lock); - usb_transfer_stop(video); - msleep(500); - start_video_stream(pd); - mutex_unlock(&pd->lock); -} - - -static int vidioc_streamon(struct file *file, void *fh, - enum v4l2_buf_type type) -{ - struct front_face *front = fh; - - if (unlikely(type != front->type)) - return -EINVAL; - return videobuf_streamon(&front->q); -} - -static int vidioc_streamoff(struct file *file, void *fh, - enum v4l2_buf_type type) -{ - struct front_face *front = file->private_data; - - if (unlikely(type != front->type)) - return -EINVAL; - return videobuf_streamoff(&front->q); -} - -/* Set the firmware's default values : need altersetting */ -static int pd_video_checkmode(struct poseidon *pd) -{ - s32 ret = 0, cmd_status, audiomode; - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/2); - - /* choose the altersetting */ - ret = usb_set_interface(pd->udev, 0, - (pd->cur_transfer_mode ? - ISO_3K_BULK_ALTERNATE_IFACE : - BULK_ALTERNATE_IFACE)); - if (ret < 0) - goto error; - - /* set default parameters for PAL-D , with the VBI enabled*/ - ret = set_tuner_mode(pd, TLG_MODE_ANALOG_TV); - ret |= send_set_req(pd, SGNL_SRC_SEL, - TLG_SIG_SRC_ANTENNA, &cmd_status); - ret |= send_set_req(pd, VIDEO_STD_SEL, - TLG_TUNE_VSTD_PAL_D, &cmd_status); - ret |= send_set_req(pd, VIDEO_STREAM_FMT_SEL, - TLG_TUNER_VID_FORMAT_YUV, &cmd_status); - ret |= send_set_req(pd, VIDEO_ROSOLU_SEL, - TLG_TUNE_VID_RES_720, &cmd_status); - ret |= send_set_req(pd, TUNE_FREQ_SELECT, TUNER_FREQ_MIN, &cmd_status); - ret |= send_set_req(pd, VBI_DATA_SEL, 1, &cmd_status);/* enable vbi */ - - /* set the audio */ - audiomode = get_audio_std(pd->video_data.context.tvnormid); - ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode, &cmd_status); - ret |= send_set_req(pd, TUNER_AUD_MODE, - TLG_TUNE_TVAUDIO_MODE_STEREO, &cmd_status); - ret |= send_set_req(pd, AUDIO_SAMPLE_RATE_SEL, - ATV_AUDIO_RATE_48K, &cmd_status); -error: - return ret; -} - -#ifdef CONFIG_PM -static int pm_video_suspend(struct poseidon *pd) -{ - /* stop audio */ - pm_alsa_suspend(pd); - - /* stop and free all the URBs */ - usb_transfer_stop(&pd->video_data); - free_all_urb(&pd->video_data); - - /* reset the interface */ - usb_set_interface(pd->udev, 0, 0); - msleep(300); - return 0; -} - -static int restore_v4l2_context(struct poseidon *pd, - struct running_context *context) -{ - struct front_face *front = pd->video_data.front; - - pd_video_checkmode(pd); - - set_std(pd, context->tvnormid); - vidioc_s_input(NULL, front, context->sig_index); - pd_vidioc_s_tuner(pd, context->audio_idx); - pd_vidioc_s_fmt(pd, &context->pix); - set_frequency(pd, &context->freq); - return 0; -} - -static int pm_video_resume(struct poseidon *pd) -{ - struct video_data *video = &pd->video_data; - - /* resume the video */ - /* [1] restore the origin V4L2 parameters */ - restore_v4l2_context(pd, &video->context); - - /* [2] initiate video copy variables */ - if (video->front->curr_frame) - init_copy(video, 0); - - /* [3] fire urbs */ - start_video_stream(pd); - - /* resume the audio */ - pm_alsa_resume(pd); - return 0; -} -#endif - -void set_debug_mode(struct video_device *vfd, int debug_mode) -{ - vfd->debug = 0; - if (debug_mode & 0x1) - vfd->debug = V4L2_DEBUG_IOCTL; - if (debug_mode & 0x2) - vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; -} - -static void init_video_context(struct running_context *context) -{ - context->sig_index = 0; - context->audio_idx = 1; /* stereo */ - context->tvnormid = V4L2_STD_PAL_D; - context->pix = (struct v4l2_pix_format) { - .width = 720, - .height = 576, - .pixelformat = V4L2_PIX_FMT_YUYV, - .field = V4L2_FIELD_INTERLACED, - .bytesperline = 720 * 2, - .sizeimage = 720 * 576 * 2, - .colorspace = V4L2_COLORSPACE_SMPTE170M, - }; -} - -static int pd_video_open(struct file *file) -{ - struct video_device *vfd = video_devdata(file); - struct poseidon *pd = video_get_drvdata(vfd); - struct front_face *front = NULL; - int ret = -ENOMEM; - - mutex_lock(&pd->lock); - usb_autopm_get_interface(pd->interface); - - if (pd->state && !(pd->state & POSEIDON_STATE_ANALOG)) { - ret = -EBUSY; - goto out; - } - front = kzalloc(sizeof(struct front_face), GFP_KERNEL); - if (!front) - goto out; - if (vfd->vfl_type == VFL_TYPE_GRABBER) { - pd->cur_transfer_mode = usb_transfer_mode;/* bulk or iso */ - init_video_context(&pd->video_data.context); - - ret = pd_video_checkmode(pd); - if (ret < 0) { - kfree(front); - ret = -1; - goto out; - } - - front->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - pd->video_data.users++; - set_debug_mode(vfd, debug_mode); - - videobuf_queue_vmalloc_init(&front->q, &pd_video_qops, - NULL, &front->queue_lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED,/* video is interlacd */ - sizeof(struct videobuf_buffer),/*it's enough*/ - front, NULL); - } else { - front->type = V4L2_BUF_TYPE_VBI_CAPTURE; - pd->vbi_data.front = front; - pd->vbi_data.users++; - - videobuf_queue_vmalloc_init(&front->q, &pd_video_qops, - NULL, &front->queue_lock, - V4L2_BUF_TYPE_VBI_CAPTURE, - V4L2_FIELD_NONE, /* vbi is NONE mode */ - sizeof(struct videobuf_buffer), - front, NULL); - } - - pd->state |= POSEIDON_STATE_ANALOG; - front->pd = pd; - front->curr_frame = NULL; - INIT_LIST_HEAD(&front->active); - spin_lock_init(&front->queue_lock); - - file->private_data = front; - kref_get(&pd->kref); - - mutex_unlock(&pd->lock); - return 0; -out: - usb_autopm_put_interface(pd->interface); - mutex_unlock(&pd->lock); - return ret; -} - -static int pd_video_release(struct file *file) -{ - struct front_face *front = file->private_data; - struct poseidon *pd = front->pd; - s32 cmd_status = 0; - - mutex_lock(&pd->lock); - - if (front->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - /* stop the device, and free the URBs */ - usb_transfer_stop(&pd->video_data); - free_all_urb(&pd->video_data); - - /* stop the firmware */ - send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, - &cmd_status); - - pd->file_for_stream = NULL; - pd->video_data.users--; - } else if (front->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - pd->vbi_data.front = NULL; - pd->vbi_data.users--; - } - if (!pd->vbi_data.users && !pd->video_data.users) - pd->state &= ~POSEIDON_STATE_ANALOG; - videobuf_stop(&front->q); - videobuf_mmap_free(&front->q); - - usb_autopm_put_interface(pd->interface); - mutex_unlock(&pd->lock); - - kfree(front); - file->private_data = NULL; - kref_put(&pd->kref, poseidon_delete); - return 0; -} - -static int pd_video_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct front_face *front = file->private_data; - return videobuf_mmap_mapper(&front->q, vma); -} - -static unsigned int pd_video_poll(struct file *file, poll_table *table) -{ - struct front_face *front = file->private_data; - return videobuf_poll_stream(file, &front->q, table); -} - -static ssize_t pd_video_read(struct file *file, char __user *buffer, - size_t count, loff_t *ppos) -{ - struct front_face *front = file->private_data; - return videobuf_read_stream(&front->q, buffer, count, ppos, - 0, file->f_flags & O_NONBLOCK); -} - -/* This struct works for both VIDEO and VBI */ -static const struct v4l2_file_operations pd_video_fops = { - .owner = THIS_MODULE, - .open = pd_video_open, - .release = pd_video_release, - .read = pd_video_read, - .poll = pd_video_poll, - .mmap = pd_video_mmap, - .ioctl = video_ioctl2, /* maybe changed in future */ -}; - -static const struct v4l2_ioctl_ops pd_video_ioctl_ops = { - .vidioc_querycap = vidioc_querycap, - - /* Video format */ - .vidioc_g_fmt_vid_cap = vidioc_g_fmt, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt, - .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi, /* VBI */ - - /* Input */ - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_enum_input = vidioc_enum_input, - - /* Audio ioctls */ - .vidioc_enumaudio = vidioc_enumaudio, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - - /* Tuner ioctls */ - .vidioc_g_tuner = vidioc_g_tuner, - .vidioc_s_tuner = vidioc_s_tuner, - .vidioc_g_std = vidioc_g_std, - .vidioc_s_std = vidioc_s_std, - .vidioc_g_frequency = vidioc_g_frequency, - .vidioc_s_frequency = vidioc_s_frequency, - - /* Buffer handlers */ - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - - /* Stream on/off */ - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, -}; - -static struct video_device pd_video_template = { - .name = "Telegent-Video", - .fops = &pd_video_fops, - .minor = -1, - .release = video_device_release_empty, - .tvnorms = V4L2_STD_ALL, - .ioctl_ops = &pd_video_ioctl_ops, -}; - -static const struct v4l2_ctrl_ops tlg_ctrl_ops = { - .s_ctrl = tlg_s_ctrl, -}; - -void pd_video_exit(struct poseidon *pd) -{ - struct video_data *video = &pd->video_data; - struct vbi_data *vbi = &pd->vbi_data; - - video_unregister_device(&video->v_dev); - video_unregister_device(&vbi->v_dev); - v4l2_ctrl_handler_free(&video->ctrl_handler); - log(); -} - -int pd_video_init(struct poseidon *pd) -{ - struct video_data *video = &pd->video_data; - struct vbi_data *vbi = &pd->vbi_data; - struct v4l2_ctrl_handler *hdl = &video->ctrl_handler; - u32 freq = TUNER_FREQ_MIN / 62500; - int ret = -ENOMEM; - - v4l2_ctrl_handler_init(hdl, 4); - v4l2_ctrl_new_std(hdl, &tlg_ctrl_ops, V4L2_CID_BRIGHTNESS, - 0, 10000, 1, 100); - v4l2_ctrl_new_std(hdl, &tlg_ctrl_ops, V4L2_CID_CONTRAST, - 0, 10000, 1, 100); - v4l2_ctrl_new_std(hdl, &tlg_ctrl_ops, V4L2_CID_HUE, - 0, 10000, 1, 100); - v4l2_ctrl_new_std(hdl, &tlg_ctrl_ops, V4L2_CID_SATURATION, - 0, 10000, 1, 100); - if (hdl->error) { - v4l2_ctrl_handler_free(hdl); - return hdl->error; - } - set_frequency(pd, &freq); - video->v_dev = pd_video_template; - video->v_dev.v4l2_dev = &pd->v4l2_dev; - video->v_dev.ctrl_handler = hdl; - video_set_drvdata(&video->v_dev, pd); - - ret = video_register_device(&video->v_dev, VFL_TYPE_GRABBER, -1); - if (ret != 0) - goto out; - - /* VBI uses the same template as video */ - vbi->v_dev = pd_video_template; - vbi->v_dev.v4l2_dev = &pd->v4l2_dev; - vbi->v_dev.ctrl_handler = hdl; - video_set_drvdata(&vbi->v_dev, pd); - ret = video_register_device(&vbi->v_dev, VFL_TYPE_VBI, -1); - if (ret != 0) - goto out; - log("register VIDEO/VBI devices"); - return 0; -out: - log("VIDEO/VBI devices register failed, : %d", ret); - pd_video_exit(pd); - return ret; -} diff --git a/drivers/staging/media/tlg2300/vendorcmds.h b/drivers/staging/media/tlg2300/vendorcmds.h deleted file mode 100644 index ba6f4ae3b2c2..000000000000 --- a/drivers/staging/media/tlg2300/vendorcmds.h +++ /dev/null @@ -1,243 +0,0 @@ -#ifndef VENDOR_CMD_H_ -#define VENDOR_CMD_H_ - -#define BULK_ALTERNATE_IFACE (2) -#define ISO_3K_BULK_ALTERNATE_IFACE (1) -#define REQ_SET_CMD (0X00) -#define REQ_GET_CMD (0X80) - -enum tlg__analog_audio_standard { - TLG_TUNE_ASTD_NONE = 0x00000000, - TLG_TUNE_ASTD_A2 = 0x00000001, - TLG_TUNE_ASTD_NICAM = 0x00000002, - TLG_TUNE_ASTD_EIAJ = 0x00000004, - TLG_TUNE_ASTD_BTSC = 0x00000008, - TLG_TUNE_ASTD_FM_US = 0x00000010, - TLG_TUNE_ASTD_FM_EUR = 0x00000020, - TLG_TUNE_ASTD_ALL = 0x0000003f -}; - -/* - * identifiers for Custom Parameter messages. - * @typedef cmd_custom_param_id_t - */ -enum cmd_custom_param_id { - CUST_PARM_ID_NONE = 0x00, - CUST_PARM_ID_BRIGHTNESS_CTRL = 0x01, - CUST_PARM_ID_CONTRAST_CTRL = 0x02, - CUST_PARM_ID_HUE_CTRL = 0x03, - CUST_PARM_ID_SATURATION_CTRL = 0x04, - CUST_PARM_ID_AUDIO_SNR_THRESHOLD = 0x10, - CUST_PARM_ID_AUDIO_AGC_THRESHOLD = 0x11, - CUST_PARM_ID_MAX -}; - -struct tuner_custom_parameter_s { - uint16_t param_id; /* Parameter identifier */ - uint16_t param_value; /* Parameter value */ -}; - -struct tuner_ber_rate_s { - uint32_t ber_rate; /* BER sample rate in seconds */ -}; - -struct tuner_atv_sig_stat_s { - uint32_t sig_present; - uint32_t sig_locked; - uint32_t sig_lock_busy; - uint32_t sig_strength; /* milliDb */ - uint32_t tv_audio_chan; /* mono/stereo/sap*/ - uint32_t mvision_stat; /* macrovision status */ -}; - -struct tuner_dtv_sig_stat_s { - uint32_t sig_present; /* Boolean*/ - uint32_t sig_locked; /* Boolean */ - uint32_t sig_lock_busy; /* Boolean (Can this time-out?) */ - uint32_t sig_strength; /* milliDb*/ -}; - -struct tuner_fm_sig_stat_s { - uint32_t sig_present; /* Boolean*/ - uint32_t sig_locked; /* Boolean */ - uint32_t sig_lock_busy; /* Boolean */ - uint32_t sig_stereo_mono;/* TBD*/ - uint32_t sig_strength; /* milliDb*/ -}; - -enum _tag_tlg_tune_srv_cmd { - TLG_TUNE_PLAY_SVC_START = 1, - TLG_TUNE_PLAY_SVC_STOP -}; - -enum _tag_tune_atv_audio_mode_caps { - TLG_TUNE_TVAUDIO_MODE_MONO = 0x00000001, - TLG_TUNE_TVAUDIO_MODE_STEREO = 0x00000002, - TLG_TUNE_TVAUDIO_MODE_LANG_A = 0x00000010,/* Primary language*/ - TLG_TUNE_TVAUDIO_MODE_LANG_B = 0x00000020,/* 2nd avail language*/ - TLG_TUNE_TVAUDIO_MODE_LANG_C = 0x00000040 -}; - - -enum _tag_tuner_atv_audio_rates { - ATV_AUDIO_RATE_NONE = 0x00,/* Audio not supported*/ - ATV_AUDIO_RATE_32K = 0x01,/* Audio rate = 32 KHz*/ - ATV_AUDIO_RATE_48K = 0x02, /* Audio rate = 48 KHz*/ - ATV_AUDIO_RATE_31_25K = 0x04 /* Audio rate = 31.25KHz */ -}; - -enum _tag_tune_atv_vid_res_caps { - TLG_TUNE_VID_RES_NONE = 0x00000000, - TLG_TUNE_VID_RES_720 = 0x00000001, - TLG_TUNE_VID_RES_704 = 0x00000002, - TLG_TUNE_VID_RES_360 = 0x00000004 -}; - -enum _tag_tuner_analog_video_format { - TLG_TUNER_VID_FORMAT_YUV = 0x00000001, - TLG_TUNER_VID_FORMAT_YCRCB = 0x00000002, - TLG_TUNER_VID_FORMAT_RGB_565 = 0x00000004, -}; - -enum tlg_ext_audio_support { - TLG_EXT_AUDIO_NONE = 0x00,/* No external audio input supported */ - TLG_EXT_AUDIO_LR = 0x01/* LR external audio inputs supported*/ -}; - -enum { - TLG_MODE_NONE = 0x00, /* No Mode specified*/ - TLG_MODE_ANALOG_TV = 0x01, /* Analog Television mode*/ - TLG_MODE_ANALOG_TV_UNCOMP = 0x01, /* Analog Television mode*/ - TLG_MODE_ANALOG_TV_COMP = 0x02, /* Analog TV mode (compressed)*/ - TLG_MODE_FM_RADIO = 0x04, /* FM Radio mode*/ - TLG_MODE_DVB_T = 0x08, /* Digital TV (DVB-T)*/ -}; - -enum tlg_signal_sources_t { - TLG_SIG_SRC_NONE = 0x00,/* Signal source not specified */ - TLG_SIG_SRC_ANTENNA = 0x01,/* Signal src is: Antenna */ - TLG_SIG_SRC_CABLE = 0x02,/* Signal src is: Coax Cable*/ - TLG_SIG_SRC_SVIDEO = 0x04,/* Signal src is: S_VIDEO */ - TLG_SIG_SRC_COMPOSITE = 0x08 /* Signal src is: Composite Video */ -}; - -enum tuner_analog_video_standard { - TLG_TUNE_VSTD_NONE = 0x00000000, - TLG_TUNE_VSTD_NTSC_M = 0x00000001, - TLG_TUNE_VSTD_NTSC_M_J = 0x00000002,/* Japan */ - TLG_TUNE_VSTD_PAL_B = 0x00000010, - TLG_TUNE_VSTD_PAL_D = 0x00000020, - TLG_TUNE_VSTD_PAL_G = 0x00000040, - TLG_TUNE_VSTD_PAL_H = 0x00000080, - TLG_TUNE_VSTD_PAL_I = 0x00000100, - TLG_TUNE_VSTD_PAL_M = 0x00000200, - TLG_TUNE_VSTD_PAL_N = 0x00000400, - TLG_TUNE_VSTD_SECAM_B = 0x00001000, - TLG_TUNE_VSTD_SECAM_D = 0x00002000, - TLG_TUNE_VSTD_SECAM_G = 0x00004000, - TLG_TUNE_VSTD_SECAM_H = 0x00008000, - TLG_TUNE_VSTD_SECAM_K = 0x00010000, - TLG_TUNE_VSTD_SECAM_K1 = 0x00020000, - TLG_TUNE_VSTD_SECAM_L = 0x00040000, - TLG_TUNE_VSTD_SECAM_L1 = 0x00080000, - TLG_TUNE_VSTD_PAL_N_COMBO = 0x00100000 -}; - -enum tlg_mode_caps { - TLG_MODE_CAPS_NONE = 0x00, /* No Mode specified */ - TLG_MODE_CAPS_ANALOG_TV_UNCOMP = 0x01, /* Analog TV mode */ - TLG_MODE_CAPS_ANALOG_TV_COMP = 0x02, /* Analog TV (compressed)*/ - TLG_MODE_CAPS_FM_RADIO = 0x04, /* FM Radio mode */ - TLG_MODE_CAPS_DVB_T = 0x08, /* Digital TV (DVB-T) */ -}; - -enum poseidon_vendor_cmds { - LAST_CMD_STAT = 0x00, - GET_CHIP_ID = 0x01, - GET_FW_ID = 0x02, - PRODUCT_CAPS = 0x03, - - TUNE_MODE_CAP_ATV = 0x10, - TUNE_MODE_CAP_ATVCOMP = 0X10, - TUNE_MODE_CAP_DVBT = 0x10, - TUNE_MODE_CAP_FM = 0x10, - TUNE_MODE_SELECT = 0x11, - TUNE_FREQ_SELECT = 0x12, - SGNL_SRC_SEL = 0x13, - - VIDEO_STD_SEL = 0x14, - VIDEO_STREAM_FMT_SEL = 0x15, - VIDEO_ROSOLU_AVAIL = 0x16, - VIDEO_ROSOLU_SEL = 0x17, - VIDEO_CONT_PROTECT = 0x20, - - VCR_TIMING_MODSEL = 0x21, - EXT_AUDIO_CAP = 0x22, - EXT_AUDIO_SEL = 0x23, - TEST_PATTERN_SEL = 0x24, - VBI_DATA_SEL = 0x25, - AUDIO_SAMPLE_RATE_CAP = 0x28, - AUDIO_SAMPLE_RATE_SEL = 0x29, - TUNER_AUD_MODE = 0x2a, - TUNER_AUD_MODE_AVAIL = 0x2b, - TUNER_AUD_ANA_STD = 0x2c, - TUNER_CUSTOM_PARAMETER = 0x2f, - - DVBT_TUNE_MODE_SEL = 0x30, - DVBT_BANDW_CAP = 0x31, - DVBT_BANDW_SEL = 0x32, - DVBT_GUARD_INTERV_CAP = 0x33, - DVBT_GUARD_INTERV_SEL = 0x34, - DVBT_MODULATION_CAP = 0x35, - DVBT_MODULATION_SEL = 0x36, - DVBT_INNER_FEC_RATE_CAP = 0x37, - DVBT_INNER_FEC_RATE_SEL = 0x38, - DVBT_TRANS_MODE_CAP = 0x39, - DVBT_TRANS_MODE_SEL = 0x3a, - DVBT_SEARCH_RANG = 0x3c, - - TUNER_SETUP_ANALOG = 0x40, - TUNER_SETUP_DIGITAL = 0x41, - TUNER_SETUP_FM_RADIO = 0x42, - TAKE_REQUEST = 0x43, /* Take effect of the command */ - PLAY_SERVICE = 0x44, /* Play start or Play stop */ - TUNER_STATUS = 0x45, - TUNE_PROP_DVBT = 0x46, - ERR_RATE_STATS = 0x47, - TUNER_BER_RATE = 0x48, - - SCAN_CAPS = 0x50, - SCAN_SETUP = 0x51, - SCAN_SERVICE = 0x52, - SCAN_STATS = 0x53, - - PID_SET = 0x58, - PID_UNSET = 0x59, - PID_LIST = 0x5a, - - IRD_CAP = 0x60, - IRD_MODE_SEL = 0x61, - IRD_SETUP = 0x62, - - PTM_MODE_CAP = 0x70, - PTM_MODE_SEL = 0x71, - PTM_SERVICE = 0x72, - TUNER_REG_SCRIPT = 0x73, - CMD_CHIP_RST = 0x74, -}; - -enum tlg_bw { - TLG_BW_5 = 5, - TLG_BW_6 = 6, - TLG_BW_7 = 7, - TLG_BW_8 = 8, - TLG_BW_12 = 12, - TLG_BW_15 = 15 -}; - -struct cmd_firmware_vers_s { - uint8_t fw_rev_major; - uint8_t fw_rev_minor; - uint16_t fw_patch; -}; -#endif /* VENDOR_CMD_H_ */ diff --git a/drivers/staging/media/vino/Kconfig b/drivers/staging/media/vino/Kconfig deleted file mode 100644 index 03700dadafd8..000000000000 --- a/drivers/staging/media/vino/Kconfig +++ /dev/null @@ -1,24 +0,0 @@ -config VIDEO_VINO - tristate "SGI Vino Video For Linux (Deprecated)" - depends on I2C && SGI_IP22 && VIDEO_V4L2 - select VIDEO_SAA7191 if MEDIA_SUBDRV_AUTOSELECT - help - Say Y here to build in support for the Vino video input system found - on SGI Indy machines. - - This driver is deprecated and will be removed soon. If you have - hardware for this and you want to work on this driver, then contact - the linux-media mailinglist. - -config VIDEO_SAA7191 - tristate "Philips SAA7191 video decoder (Deprecated)" - depends on VIDEO_V4L2 && I2C - ---help--- - Support for the Philips SAA7191 video decoder. - - This driver is deprecated and will be removed soon. If you have - hardware for this and you want to work on this driver, then contact - the linux-media mailinglist. - - To compile this driver as a module, choose M here: the - module will be called saa7191. diff --git a/drivers/staging/media/vino/Makefile b/drivers/staging/media/vino/Makefile deleted file mode 100644 index 914c2513687c..000000000000 --- a/drivers/staging/media/vino/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_VIDEO_VINO) += indycam.o -obj-$(CONFIG_VIDEO_VINO) += vino.o -obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o diff --git a/drivers/staging/media/vino/indycam.c b/drivers/staging/media/vino/indycam.c deleted file mode 100644 index f1d192bbcb4c..000000000000 --- a/drivers/staging/media/vino/indycam.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - * indycam.c - Silicon Graphics IndyCam digital camera driver - * - * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org> - * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi> - * - * 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/fs.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/major.h> -#include <linux/module.h> -#include <linux/mm.h> -#include <linux/slab.h> - -/* IndyCam decodes stream of photons into digital image representation ;-) */ -#include <linux/videodev2.h> -#include <linux/i2c.h> -#include <media/v4l2-device.h> - -#include "indycam.h" - -#define INDYCAM_MODULE_VERSION "0.0.5" - -MODULE_DESCRIPTION("SGI IndyCam driver"); -MODULE_VERSION(INDYCAM_MODULE_VERSION); -MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>"); -MODULE_LICENSE("GPL"); - - -// #define INDYCAM_DEBUG - -#ifdef INDYCAM_DEBUG -#define dprintk(x...) printk("IndyCam: " x); -#define indycam_regdump(client) indycam_regdump_debug(client) -#else -#define dprintk(x...) -#define indycam_regdump(client) -#endif - -struct indycam { - struct v4l2_subdev sd; - u8 version; -}; - -static inline struct indycam *to_indycam(struct v4l2_subdev *sd) -{ - return container_of(sd, struct indycam, sd); -} - -static const u8 initseq[] = { - INDYCAM_CONTROL_AGCENA, /* INDYCAM_CONTROL */ - INDYCAM_SHUTTER_60, /* INDYCAM_SHUTTER */ - INDYCAM_GAIN_DEFAULT, /* INDYCAM_GAIN */ - 0x00, /* INDYCAM_BRIGHTNESS (read-only) */ - INDYCAM_RED_BALANCE_DEFAULT, /* INDYCAM_RED_BALANCE */ - INDYCAM_BLUE_BALANCE_DEFAULT, /* INDYCAM_BLUE_BALANCE */ - INDYCAM_RED_SATURATION_DEFAULT, /* INDYCAM_RED_SATURATION */ - INDYCAM_BLUE_SATURATION_DEFAULT,/* INDYCAM_BLUE_SATURATION */ -}; - -/* IndyCam register handling */ - -static int indycam_read_reg(struct v4l2_subdev *sd, u8 reg, u8 *value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - if (reg == INDYCAM_REG_RESET) { - dprintk("indycam_read_reg(): " - "skipping write-only register %d\n", reg); - *value = 0; - return 0; - } - - ret = i2c_smbus_read_byte_data(client, reg); - - if (ret < 0) { - printk(KERN_ERR "IndyCam: indycam_read_reg(): read failed, " - "register = 0x%02x\n", reg); - return ret; - } - - *value = (u8)ret; - - return 0; -} - -static int indycam_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int err; - - if (reg == INDYCAM_REG_BRIGHTNESS || reg == INDYCAM_REG_VERSION) { - dprintk("indycam_write_reg(): " - "skipping read-only register %d\n", reg); - return 0; - } - - dprintk("Writing Reg %d = 0x%02x\n", reg, value); - err = i2c_smbus_write_byte_data(client, reg, value); - - if (err) { - printk(KERN_ERR "IndyCam: indycam_write_reg(): write failed, " - "register = 0x%02x, value = 0x%02x\n", reg, value); - } - return err; -} - -static int indycam_write_block(struct v4l2_subdev *sd, u8 reg, - u8 length, u8 *data) -{ - int i, err; - - for (i = 0; i < length; i++) { - err = indycam_write_reg(sd, reg + i, data[i]); - if (err) - return err; - } - - return 0; -} - -/* Helper functions */ - -#ifdef INDYCAM_DEBUG -static void indycam_regdump_debug(struct v4l2_subdev *sd) -{ - int i; - u8 val; - - for (i = 0; i < 9; i++) { - indycam_read_reg(sd, i, &val); - dprintk("Reg %d = 0x%02x\n", i, val); - } -} -#endif - -static int indycam_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct indycam *camera = to_indycam(sd); - u8 reg; - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_AUTOGAIN: - case V4L2_CID_AUTO_WHITE_BALANCE: - ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, ®); - if (ret) - return -EIO; - if (ctrl->id == V4L2_CID_AUTOGAIN) - ctrl->value = (reg & INDYCAM_CONTROL_AGCENA) - ? 1 : 0; - else - ctrl->value = (reg & INDYCAM_CONTROL_AWBCTL) - ? 1 : 0; - break; - case V4L2_CID_EXPOSURE: - ret = indycam_read_reg(sd, INDYCAM_REG_SHUTTER, ®); - if (ret) - return -EIO; - ctrl->value = ((s32)reg == 0x00) ? 0xff : ((s32)reg - 1); - break; - case V4L2_CID_GAIN: - ret = indycam_read_reg(sd, INDYCAM_REG_GAIN, ®); - if (ret) - return -EIO; - ctrl->value = (s32)reg; - break; - case V4L2_CID_RED_BALANCE: - ret = indycam_read_reg(sd, INDYCAM_REG_RED_BALANCE, ®); - if (ret) - return -EIO; - ctrl->value = (s32)reg; - break; - case V4L2_CID_BLUE_BALANCE: - ret = indycam_read_reg(sd, INDYCAM_REG_BLUE_BALANCE, ®); - if (ret) - return -EIO; - ctrl->value = (s32)reg; - break; - case INDYCAM_CONTROL_RED_SATURATION: - ret = indycam_read_reg(sd, - INDYCAM_REG_RED_SATURATION, ®); - if (ret) - return -EIO; - ctrl->value = (s32)reg; - break; - case INDYCAM_CONTROL_BLUE_SATURATION: - ret = indycam_read_reg(sd, - INDYCAM_REG_BLUE_SATURATION, ®); - if (ret) - return -EIO; - ctrl->value = (s32)reg; - break; - case V4L2_CID_GAMMA: - if (camera->version == CAMERA_VERSION_MOOSE) { - ret = indycam_read_reg(sd, - INDYCAM_REG_GAMMA, ®); - if (ret) - return -EIO; - ctrl->value = (s32)reg; - } else { - ctrl->value = INDYCAM_GAMMA_DEFAULT; - } - break; - default: - ret = -EINVAL; - } - - return ret; -} - -static int indycam_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct indycam *camera = to_indycam(sd); - u8 reg; - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_AUTOGAIN: - case V4L2_CID_AUTO_WHITE_BALANCE: - ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, ®); - if (ret) - break; - - if (ctrl->id == V4L2_CID_AUTOGAIN) { - if (ctrl->value) - reg |= INDYCAM_CONTROL_AGCENA; - else - reg &= ~INDYCAM_CONTROL_AGCENA; - } else { - if (ctrl->value) - reg |= INDYCAM_CONTROL_AWBCTL; - else - reg &= ~INDYCAM_CONTROL_AWBCTL; - } - - ret = indycam_write_reg(sd, INDYCAM_REG_CONTROL, reg); - break; - case V4L2_CID_EXPOSURE: - reg = (ctrl->value == 0xff) ? 0x00 : (ctrl->value + 1); - ret = indycam_write_reg(sd, INDYCAM_REG_SHUTTER, reg); - break; - case V4L2_CID_GAIN: - ret = indycam_write_reg(sd, INDYCAM_REG_GAIN, ctrl->value); - break; - case V4L2_CID_RED_BALANCE: - ret = indycam_write_reg(sd, INDYCAM_REG_RED_BALANCE, - ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_BALANCE, - ctrl->value); - break; - case INDYCAM_CONTROL_RED_SATURATION: - ret = indycam_write_reg(sd, INDYCAM_REG_RED_SATURATION, - ctrl->value); - break; - case INDYCAM_CONTROL_BLUE_SATURATION: - ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_SATURATION, - ctrl->value); - break; - case V4L2_CID_GAMMA: - if (camera->version == CAMERA_VERSION_MOOSE) { - ret = indycam_write_reg(sd, INDYCAM_REG_GAMMA, - ctrl->value); - } - break; - default: - ret = -EINVAL; - } - - return ret; -} - -/* I2C-interface */ - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops indycam_core_ops = { - .g_ctrl = indycam_g_ctrl, - .s_ctrl = indycam_s_ctrl, -}; - -static const struct v4l2_subdev_ops indycam_ops = { - .core = &indycam_core_ops, -}; - -static int indycam_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - int err = 0; - struct indycam *camera; - struct v4l2_subdev *sd; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - camera = kzalloc(sizeof(struct indycam), GFP_KERNEL); - if (!camera) - return -ENOMEM; - - sd = &camera->sd; - v4l2_i2c_subdev_init(sd, client, &indycam_ops); - - camera->version = i2c_smbus_read_byte_data(client, - INDYCAM_REG_VERSION); - if (camera->version != CAMERA_VERSION_INDY && - camera->version != CAMERA_VERSION_MOOSE) { - kfree(camera); - return -ENODEV; - } - - printk(KERN_INFO "IndyCam v%d.%d detected\n", - INDYCAM_VERSION_MAJOR(camera->version), - INDYCAM_VERSION_MINOR(camera->version)); - - indycam_regdump(sd); - - // initialize - err = indycam_write_block(sd, 0, sizeof(initseq), (u8 *)&initseq); - if (err) { - printk(KERN_ERR "IndyCam initialization failed\n"); - kfree(camera); - return -EIO; - } - - indycam_regdump(sd); - - // white balance - err = indycam_write_reg(sd, INDYCAM_REG_CONTROL, - INDYCAM_CONTROL_AGCENA | INDYCAM_CONTROL_AWBCTL); - if (err) { - printk(KERN_ERR "IndyCam: White balancing camera failed\n"); - kfree(camera); - return -EIO; - } - - indycam_regdump(sd); - - printk(KERN_INFO "IndyCam initialized\n"); - - return 0; -} - -static int indycam_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - kfree(to_indycam(sd)); - return 0; -} - -static const struct i2c_device_id indycam_id[] = { - { "indycam", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, indycam_id); - -static struct i2c_driver indycam_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "indycam", - }, - .probe = indycam_probe, - .remove = indycam_remove, - .id_table = indycam_id, -}; - -module_i2c_driver(indycam_driver); diff --git a/drivers/staging/media/vino/indycam.h b/drivers/staging/media/vino/indycam.h deleted file mode 100644 index 881f21c474c4..000000000000 --- a/drivers/staging/media/vino/indycam.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * indycam.h - Silicon Graphics IndyCam digital camera driver - * - * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org> - * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi> - * - * 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. - */ - -#ifndef _INDYCAM_H_ -#define _INDYCAM_H_ - -/* I2C address for the Guinness Camera */ -#define INDYCAM_ADDR 0x56 - -/* Camera version */ -#define CAMERA_VERSION_INDY 0x10 /* v1.0 */ -#define CAMERA_VERSION_MOOSE 0x12 /* v1.2 */ -#define INDYCAM_VERSION_MAJOR(x) (((x) & 0xf0) >> 4) -#define INDYCAM_VERSION_MINOR(x) ((x) & 0x0f) - -/* Register bus addresses */ -#define INDYCAM_REG_CONTROL 0x00 -#define INDYCAM_REG_SHUTTER 0x01 -#define INDYCAM_REG_GAIN 0x02 -#define INDYCAM_REG_BRIGHTNESS 0x03 /* read-only */ -#define INDYCAM_REG_RED_BALANCE 0x04 -#define INDYCAM_REG_BLUE_BALANCE 0x05 -#define INDYCAM_REG_RED_SATURATION 0x06 -#define INDYCAM_REG_BLUE_SATURATION 0x07 -#define INDYCAM_REG_GAMMA 0x08 -#define INDYCAM_REG_VERSION 0x0e /* read-only */ -#define INDYCAM_REG_RESET 0x0f /* write-only */ - -#define INDYCAM_REG_LED 0x46 -#define INDYCAM_REG_ORIENTATION 0x47 -#define INDYCAM_REG_BUTTON 0x48 - -/* Field definitions of registers */ -#define INDYCAM_CONTROL_AGCENA (1<<0) /* automatic gain control */ -#define INDYCAM_CONTROL_AWBCTL (1<<1) /* automatic white balance */ - /* 2-3 are reserved */ -#define INDYCAM_CONTROL_EVNFLD (1<<4) /* read-only */ - -#define INDYCAM_SHUTTER_10000 0x02 /* 1/10000 second */ -#define INDYCAM_SHUTTER_4000 0x04 /* 1/4000 second */ -#define INDYCAM_SHUTTER_2000 0x08 /* 1/2000 second */ -#define INDYCAM_SHUTTER_1000 0x10 /* 1/1000 second */ -#define INDYCAM_SHUTTER_500 0x20 /* 1/500 second */ -#define INDYCAM_SHUTTER_250 0x3f /* 1/250 second */ -#define INDYCAM_SHUTTER_125 0x7e /* 1/125 second */ -#define INDYCAM_SHUTTER_100 0x9e /* 1/100 second */ -#define INDYCAM_SHUTTER_60 0x00 /* 1/60 second */ - -#define INDYCAM_LED_ACTIVE 0x10 -#define INDYCAM_LED_INACTIVE 0x30 -#define INDYCAM_ORIENTATION_BOTTOM_TO_TOP 0x40 -#define INDYCAM_BUTTON_RELEASED 0x10 - -/* Values for controls */ -#define INDYCAM_SHUTTER_MIN 0x00 -#define INDYCAM_SHUTTER_MAX 0xff -#define INDYCAM_GAIN_MIN 0x00 -#define INDYCAM_GAIN_MAX 0xff -#define INDYCAM_RED_BALANCE_MIN 0x00 -#define INDYCAM_RED_BALANCE_MAX 0xff -#define INDYCAM_BLUE_BALANCE_MIN 0x00 -#define INDYCAM_BLUE_BALANCE_MAX 0xff -#define INDYCAM_RED_SATURATION_MIN 0x00 -#define INDYCAM_RED_SATURATION_MAX 0xff -#define INDYCAM_BLUE_SATURATION_MIN 0x00 -#define INDYCAM_BLUE_SATURATION_MAX 0xff -#define INDYCAM_GAMMA_MIN 0x00 -#define INDYCAM_GAMMA_MAX 0xff - -#define INDYCAM_AGC_DEFAULT 1 -#define INDYCAM_AWB_DEFAULT 0 -#define INDYCAM_SHUTTER_DEFAULT 0xff -#define INDYCAM_GAIN_DEFAULT 0x80 -#define INDYCAM_RED_BALANCE_DEFAULT 0x18 -#define INDYCAM_BLUE_BALANCE_DEFAULT 0xa4 -#define INDYCAM_RED_SATURATION_DEFAULT 0x80 -#define INDYCAM_BLUE_SATURATION_DEFAULT 0xc0 -#define INDYCAM_GAMMA_DEFAULT 0x80 - -/* Driver interface definitions */ - -#define INDYCAM_CONTROL_RED_SATURATION (V4L2_CID_PRIVATE_BASE + 0) -#define INDYCAM_CONTROL_BLUE_SATURATION (V4L2_CID_PRIVATE_BASE + 1) - -#endif diff --git a/drivers/staging/media/vino/saa7191.c b/drivers/staging/media/vino/saa7191.c deleted file mode 100644 index 8e9699268a63..000000000000 --- a/drivers/staging/media/vino/saa7191.c +++ /dev/null @@ -1,649 +0,0 @@ -/* - * saa7191.c - Philips SAA7191 video decoder driver - * - * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org> - * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi> - * - * 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/fs.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/major.h> -#include <linux/module.h> -#include <linux/mm.h> -#include <linux/slab.h> - -#include <linux/videodev2.h> -#include <linux/i2c.h> -#include <media/v4l2-device.h> - -#include "saa7191.h" - -#define SAA7191_MODULE_VERSION "0.0.5" - -MODULE_DESCRIPTION("Philips SAA7191 video decoder driver"); -MODULE_VERSION(SAA7191_MODULE_VERSION); -MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>"); -MODULE_LICENSE("GPL"); - - -// #define SAA7191_DEBUG - -#ifdef SAA7191_DEBUG -#define dprintk(x...) printk("SAA7191: " x); -#else -#define dprintk(x...) -#endif - -#define SAA7191_SYNC_COUNT 30 -#define SAA7191_SYNC_DELAY 100 /* milliseconds */ - -struct saa7191 { - struct v4l2_subdev sd; - - /* the register values are stored here as the actual - * I2C-registers are write-only */ - u8 reg[25]; - - int input; - v4l2_std_id norm; -}; - -static inline struct saa7191 *to_saa7191(struct v4l2_subdev *sd) -{ - return container_of(sd, struct saa7191, sd); -} - -static const u8 initseq[] = { - 0, /* Subaddress */ - - 0x50, /* (0x50) SAA7191_REG_IDEL */ - - /* 50 Hz signal timing */ - 0x30, /* (0x30) SAA7191_REG_HSYB */ - 0x00, /* (0x00) SAA7191_REG_HSYS */ - 0xe8, /* (0xe8) SAA7191_REG_HCLB */ - 0xb6, /* (0xb6) SAA7191_REG_HCLS */ - 0xf4, /* (0xf4) SAA7191_REG_HPHI */ - - /* control */ - SAA7191_LUMA_APER_1, /* (0x01) SAA7191_REG_LUMA - CVBS mode */ - 0x00, /* (0x00) SAA7191_REG_HUEC */ - 0xf8, /* (0xf8) SAA7191_REG_CKTQ */ - 0xf8, /* (0xf8) SAA7191_REG_CKTS */ - 0x90, /* (0x90) SAA7191_REG_PLSE */ - 0x90, /* (0x90) SAA7191_REG_SESE */ - 0x00, /* (0x00) SAA7191_REG_GAIN */ - SAA7191_STDC_NFEN | SAA7191_STDC_HRMV, /* (0x0c) SAA7191_REG_STDC - * - not SECAM, - * slow time constant */ - SAA7191_IOCK_OEDC | SAA7191_IOCK_OEHS | SAA7191_IOCK_OEVS - | SAA7191_IOCK_OEDY, /* (0x78) SAA7191_REG_IOCK - * - chroma from CVBS, GPSW1 & 2 off */ - SAA7191_CTL3_AUFD | SAA7191_CTL3_SCEN | SAA7191_CTL3_OFTS - | SAA7191_CTL3_YDEL0, /* (0x99) SAA7191_REG_CTL3 - * - automatic field detection */ - 0x00, /* (0x00) SAA7191_REG_CTL4 */ - 0x2c, /* (0x2c) SAA7191_REG_CHCV - PAL nominal value */ - 0x00, /* unused */ - 0x00, /* unused */ - - /* 60 Hz signal timing */ - 0x34, /* (0x34) SAA7191_REG_HS6B */ - 0x0a, /* (0x0a) SAA7191_REG_HS6S */ - 0xf4, /* (0xf4) SAA7191_REG_HC6B */ - 0xce, /* (0xce) SAA7191_REG_HC6S */ - 0xf4, /* (0xf4) SAA7191_REG_HP6I */ -}; - -/* SAA7191 register handling */ - -static u8 saa7191_read_reg(struct v4l2_subdev *sd, u8 reg) -{ - return to_saa7191(sd)->reg[reg]; -} - -static int saa7191_read_status(struct v4l2_subdev *sd, u8 *value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - ret = i2c_master_recv(client, value, 1); - if (ret < 0) { - printk(KERN_ERR "SAA7191: saa7191_read_status(): read failed\n"); - return ret; - } - - return 0; -} - - -static int saa7191_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - to_saa7191(sd)->reg[reg] = value; - return i2c_smbus_write_byte_data(client, reg, value); -} - -/* the first byte of data must be the first subaddress number (register) */ -static int saa7191_write_block(struct v4l2_subdev *sd, - u8 length, const u8 *data) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct saa7191 *decoder = to_saa7191(sd); - int i; - int ret; - - for (i = 0; i < (length - 1); i++) { - decoder->reg[data[0] + i] = data[i + 1]; - } - - ret = i2c_master_send(client, data, length); - if (ret < 0) { - printk(KERN_ERR "SAA7191: saa7191_write_block(): " - "write failed\n"); - return ret; - } - - return 0; -} - -/* Helper functions */ - -static int saa7191_s_routing(struct v4l2_subdev *sd, - u32 input, u32 output, u32 config) -{ - struct saa7191 *decoder = to_saa7191(sd); - u8 luma = saa7191_read_reg(sd, SAA7191_REG_LUMA); - u8 iock = saa7191_read_reg(sd, SAA7191_REG_IOCK); - int err; - - switch (input) { - case SAA7191_INPUT_COMPOSITE: /* Set Composite input */ - iock &= ~(SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW1 - | SAA7191_IOCK_GPSW2); - /* Chrominance trap active */ - luma &= ~SAA7191_LUMA_BYPS; - break; - case SAA7191_INPUT_SVIDEO: /* Set S-Video input */ - iock |= SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW2; - /* Chrominance trap bypassed */ - luma |= SAA7191_LUMA_BYPS; - break; - default: - return -EINVAL; - } - - err = saa7191_write_reg(sd, SAA7191_REG_LUMA, luma); - if (err) - return -EIO; - err = saa7191_write_reg(sd, SAA7191_REG_IOCK, iock); - if (err) - return -EIO; - - decoder->input = input; - - return 0; -} - -static int saa7191_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) -{ - struct saa7191 *decoder = to_saa7191(sd); - u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC); - u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3); - u8 chcv = saa7191_read_reg(sd, SAA7191_REG_CHCV); - int err; - - if (norm & V4L2_STD_PAL) { - stdc &= ~SAA7191_STDC_SECS; - ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL); - chcv = SAA7191_CHCV_PAL; - } else if (norm & V4L2_STD_NTSC) { - stdc &= ~SAA7191_STDC_SECS; - ctl3 &= ~SAA7191_CTL3_AUFD; - ctl3 |= SAA7191_CTL3_FSEL; - chcv = SAA7191_CHCV_NTSC; - } else if (norm & V4L2_STD_SECAM) { - stdc |= SAA7191_STDC_SECS; - ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL); - chcv = SAA7191_CHCV_PAL; - } else { - return -EINVAL; - } - - err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); - if (err) - return -EIO; - err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc); - if (err) - return -EIO; - err = saa7191_write_reg(sd, SAA7191_REG_CHCV, chcv); - if (err) - return -EIO; - - decoder->norm = norm; - - dprintk("ctl3: %02x stdc: %02x chcv: %02x\n", ctl3, - stdc, chcv); - dprintk("norm: %llx\n", norm); - - return 0; -} - -static int saa7191_wait_for_signal(struct v4l2_subdev *sd, u8 *status) -{ - int i = 0; - - dprintk("Checking for signal...\n"); - - for (i = 0; i < SAA7191_SYNC_COUNT; i++) { - if (saa7191_read_status(sd, status)) - return -EIO; - - if (((*status) & SAA7191_STATUS_HLCK) == 0) { - dprintk("Signal found\n"); - return 0; - } - - msleep(SAA7191_SYNC_DELAY); - } - - dprintk("No signal\n"); - - return -EBUSY; -} - -static int saa7191_querystd(struct v4l2_subdev *sd, v4l2_std_id *norm) -{ - struct saa7191 *decoder = to_saa7191(sd); - u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC); - u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3); - u8 status; - v4l2_std_id old_norm = decoder->norm; - int err = 0; - - dprintk("SAA7191 extended signal auto-detection...\n"); - - *norm &= V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; - stdc &= ~SAA7191_STDC_SECS; - ctl3 &= ~(SAA7191_CTL3_FSEL); - - err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc); - if (err) { - err = -EIO; - goto out; - } - err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); - if (err) { - err = -EIO; - goto out; - } - - ctl3 |= SAA7191_CTL3_AUFD; - err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3); - if (err) { - err = -EIO; - goto out; - } - - msleep(SAA7191_SYNC_DELAY); - - err = saa7191_wait_for_signal(sd, &status); - if (err) - goto out; - - if (status & SAA7191_STATUS_FIDT) { - /* 60Hz signal -> NTSC */ - dprintk("60Hz signal: NTSC\n"); - *norm &= V4L2_STD_NTSC; - return 0; - } - - /* 50Hz signal */ - dprintk("50Hz signal: Trying PAL...\n"); - - /* try PAL first */ - err = saa7191_s_std(sd, V4L2_STD_PAL); - if (err) - goto out; - - msleep(SAA7191_SYNC_DELAY); - - err = saa7191_wait_for_signal(sd, &status); - if (err) - goto out; - - /* not 50Hz ? */ - if (status & SAA7191_STATUS_FIDT) { - dprintk("No 50Hz signal\n"); - saa7191_s_std(sd, old_norm); - *norm = V4L2_STD_UNKNOWN; - return 0; - } - - if (status & SAA7191_STATUS_CODE) { - dprintk("PAL\n"); - *norm &= V4L2_STD_PAL; - return saa7191_s_std(sd, old_norm); - } - - dprintk("No color detected with PAL - Trying SECAM...\n"); - - /* no color detected ? -> try SECAM */ - err = saa7191_s_std(sd, V4L2_STD_SECAM); - if (err) - goto out; - - msleep(SAA7191_SYNC_DELAY); - - err = saa7191_wait_for_signal(sd, &status); - if (err) - goto out; - - /* not 50Hz ? */ - if (status & SAA7191_STATUS_FIDT) { - dprintk("No 50Hz signal\n"); - *norm = V4L2_STD_UNKNOWN; - goto out; - } - - if (status & SAA7191_STATUS_CODE) { - /* Color detected -> SECAM */ - dprintk("SECAM\n"); - *norm &= V4L2_STD_SECAM; - return saa7191_s_std(sd, old_norm); - } - - dprintk("No color detected with SECAM - Going back to PAL.\n"); - *norm = V4L2_STD_UNKNOWN; - -out: - return saa7191_s_std(sd, old_norm); -} - -static int saa7191_autodetect_norm(struct v4l2_subdev *sd) -{ - u8 status; - - dprintk("SAA7191 signal auto-detection...\n"); - - dprintk("Reading status...\n"); - - if (saa7191_read_status(sd, &status)) - return -EIO; - - dprintk("Checking for signal...\n"); - - /* no signal ? */ - if (status & SAA7191_STATUS_HLCK) { - dprintk("No signal\n"); - return -EBUSY; - } - - dprintk("Signal found\n"); - - if (status & SAA7191_STATUS_FIDT) { - /* 60hz signal -> NTSC */ - dprintk("NTSC\n"); - return saa7191_s_std(sd, V4L2_STD_NTSC); - } else { - /* 50hz signal -> PAL */ - dprintk("PAL\n"); - return saa7191_s_std(sd, V4L2_STD_PAL); - } -} - -static int saa7191_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - u8 reg; - int ret = 0; - - switch (ctrl->id) { - case SAA7191_CONTROL_BANDPASS: - case SAA7191_CONTROL_BANDPASS_WEIGHT: - case SAA7191_CONTROL_CORING: - reg = saa7191_read_reg(sd, SAA7191_REG_LUMA); - switch (ctrl->id) { - case SAA7191_CONTROL_BANDPASS: - ctrl->value = ((s32)reg & SAA7191_LUMA_BPSS_MASK) - >> SAA7191_LUMA_BPSS_SHIFT; - break; - case SAA7191_CONTROL_BANDPASS_WEIGHT: - ctrl->value = ((s32)reg & SAA7191_LUMA_APER_MASK) - >> SAA7191_LUMA_APER_SHIFT; - break; - case SAA7191_CONTROL_CORING: - ctrl->value = ((s32)reg & SAA7191_LUMA_CORI_MASK) - >> SAA7191_LUMA_CORI_SHIFT; - break; - } - break; - case SAA7191_CONTROL_FORCE_COLOUR: - case SAA7191_CONTROL_CHROMA_GAIN: - reg = saa7191_read_reg(sd, SAA7191_REG_GAIN); - if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) - ctrl->value = ((s32)reg & SAA7191_GAIN_COLO) ? 1 : 0; - else - ctrl->value = ((s32)reg & SAA7191_GAIN_LFIS_MASK) - >> SAA7191_GAIN_LFIS_SHIFT; - break; - case V4L2_CID_HUE: - reg = saa7191_read_reg(sd, SAA7191_REG_HUEC); - if (reg < 0x80) - reg += 0x80; - else - reg -= 0x80; - ctrl->value = (s32)reg; - break; - case SAA7191_CONTROL_VTRC: - reg = saa7191_read_reg(sd, SAA7191_REG_STDC); - ctrl->value = ((s32)reg & SAA7191_STDC_VTRC) ? 1 : 0; - break; - case SAA7191_CONTROL_LUMA_DELAY: - reg = saa7191_read_reg(sd, SAA7191_REG_CTL3); - ctrl->value = ((s32)reg & SAA7191_CTL3_YDEL_MASK) - >> SAA7191_CTL3_YDEL_SHIFT; - if (ctrl->value >= 4) - ctrl->value -= 8; - break; - case SAA7191_CONTROL_VNR: - reg = saa7191_read_reg(sd, SAA7191_REG_CTL4); - ctrl->value = ((s32)reg & SAA7191_CTL4_VNOI_MASK) - >> SAA7191_CTL4_VNOI_SHIFT; - break; - default: - ret = -EINVAL; - } - - return ret; -} - -static int saa7191_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - u8 reg; - int ret = 0; - - switch (ctrl->id) { - case SAA7191_CONTROL_BANDPASS: - case SAA7191_CONTROL_BANDPASS_WEIGHT: - case SAA7191_CONTROL_CORING: - reg = saa7191_read_reg(sd, SAA7191_REG_LUMA); - switch (ctrl->id) { - case SAA7191_CONTROL_BANDPASS: - reg &= ~SAA7191_LUMA_BPSS_MASK; - reg |= (ctrl->value << SAA7191_LUMA_BPSS_SHIFT) - & SAA7191_LUMA_BPSS_MASK; - break; - case SAA7191_CONTROL_BANDPASS_WEIGHT: - reg &= ~SAA7191_LUMA_APER_MASK; - reg |= (ctrl->value << SAA7191_LUMA_APER_SHIFT) - & SAA7191_LUMA_APER_MASK; - break; - case SAA7191_CONTROL_CORING: - reg &= ~SAA7191_LUMA_CORI_MASK; - reg |= (ctrl->value << SAA7191_LUMA_CORI_SHIFT) - & SAA7191_LUMA_CORI_MASK; - break; - } - ret = saa7191_write_reg(sd, SAA7191_REG_LUMA, reg); - break; - case SAA7191_CONTROL_FORCE_COLOUR: - case SAA7191_CONTROL_CHROMA_GAIN: - reg = saa7191_read_reg(sd, SAA7191_REG_GAIN); - if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) { - if (ctrl->value) - reg |= SAA7191_GAIN_COLO; - else - reg &= ~SAA7191_GAIN_COLO; - } else { - reg &= ~SAA7191_GAIN_LFIS_MASK; - reg |= (ctrl->value << SAA7191_GAIN_LFIS_SHIFT) - & SAA7191_GAIN_LFIS_MASK; - } - ret = saa7191_write_reg(sd, SAA7191_REG_GAIN, reg); - break; - case V4L2_CID_HUE: - reg = ctrl->value & 0xff; - if (reg < 0x80) - reg += 0x80; - else - reg -= 0x80; - ret = saa7191_write_reg(sd, SAA7191_REG_HUEC, reg); - break; - case SAA7191_CONTROL_VTRC: - reg = saa7191_read_reg(sd, SAA7191_REG_STDC); - if (ctrl->value) - reg |= SAA7191_STDC_VTRC; - else - reg &= ~SAA7191_STDC_VTRC; - ret = saa7191_write_reg(sd, SAA7191_REG_STDC, reg); - break; - case SAA7191_CONTROL_LUMA_DELAY: { - s32 value = ctrl->value; - if (value < 0) - value += 8; - reg = saa7191_read_reg(sd, SAA7191_REG_CTL3); - reg &= ~SAA7191_CTL3_YDEL_MASK; - reg |= (value << SAA7191_CTL3_YDEL_SHIFT) - & SAA7191_CTL3_YDEL_MASK; - ret = saa7191_write_reg(sd, SAA7191_REG_CTL3, reg); - break; - } - case SAA7191_CONTROL_VNR: - reg = saa7191_read_reg(sd, SAA7191_REG_CTL4); - reg &= ~SAA7191_CTL4_VNOI_MASK; - reg |= (ctrl->value << SAA7191_CTL4_VNOI_SHIFT) - & SAA7191_CTL4_VNOI_MASK; - ret = saa7191_write_reg(sd, SAA7191_REG_CTL4, reg); - break; - default: - ret = -EINVAL; - } - - return ret; -} - -/* I2C-interface */ - -static int saa7191_g_input_status(struct v4l2_subdev *sd, u32 *status) -{ - u8 status_reg; - int res = V4L2_IN_ST_NO_SIGNAL; - - if (saa7191_read_status(sd, &status_reg)) - return -EIO; - if ((status_reg & SAA7191_STATUS_HLCK) == 0) - res = 0; - if (!(status_reg & SAA7191_STATUS_CODE)) - res |= V4L2_IN_ST_NO_COLOR; - *status = res; - return 0; -} - - -/* ----------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops saa7191_core_ops = { - .g_ctrl = saa7191_g_ctrl, - .s_ctrl = saa7191_s_ctrl, -}; - -static const struct v4l2_subdev_video_ops saa7191_video_ops = { - .s_std = saa7191_s_std, - .s_routing = saa7191_s_routing, - .querystd = saa7191_querystd, - .g_input_status = saa7191_g_input_status, -}; - -static const struct v4l2_subdev_ops saa7191_ops = { - .core = &saa7191_core_ops, - .video = &saa7191_video_ops, -}; - -static int saa7191_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - int err = 0; - struct saa7191 *decoder; - struct v4l2_subdev *sd; - - v4l_info(client, "chip found @ 0x%x (%s)\n", - client->addr << 1, client->adapter->name); - - decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL); - if (!decoder) - return -ENOMEM; - - sd = &decoder->sd; - v4l2_i2c_subdev_init(sd, client, &saa7191_ops); - - err = saa7191_write_block(sd, sizeof(initseq), initseq); - if (err) { - printk(KERN_ERR "SAA7191 initialization failed\n"); - return err; - } - - printk(KERN_INFO "SAA7191 initialized\n"); - - decoder->input = SAA7191_INPUT_COMPOSITE; - decoder->norm = V4L2_STD_PAL; - - err = saa7191_autodetect_norm(sd); - if (err && (err != -EBUSY)) - printk(KERN_ERR "SAA7191: Signal auto-detection failed\n"); - - return 0; -} - -static int saa7191_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - return 0; -} - -static const struct i2c_device_id saa7191_id[] = { - { "saa7191", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, saa7191_id); - -static struct i2c_driver saa7191_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "saa7191", - }, - .probe = saa7191_probe, - .remove = saa7191_remove, - .id_table = saa7191_id, -}; - -module_i2c_driver(saa7191_driver); diff --git a/drivers/staging/media/vino/saa7191.h b/drivers/staging/media/vino/saa7191.h deleted file mode 100644 index 803c74d6066f..000000000000 --- a/drivers/staging/media/vino/saa7191.h +++ /dev/null @@ -1,245 +0,0 @@ -/* - * saa7191.h - Philips SAA7191 video decoder driver - * - * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org> - * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi> - * - * 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. - */ - -#ifndef _SAA7191_H_ -#define _SAA7191_H_ - -/* Philips SAA7191 DMSD I2C bus address */ -#define SAA7191_ADDR 0x8a - -/* Register subaddresses. */ -#define SAA7191_REG_IDEL 0x00 -#define SAA7191_REG_HSYB 0x01 -#define SAA7191_REG_HSYS 0x02 -#define SAA7191_REG_HCLB 0x03 -#define SAA7191_REG_HCLS 0x04 -#define SAA7191_REG_HPHI 0x05 -#define SAA7191_REG_LUMA 0x06 -#define SAA7191_REG_HUEC 0x07 -#define SAA7191_REG_CKTQ 0x08 /* bits 3-7 */ -#define SAA7191_REG_CKTS 0x09 /* bits 3-7 */ -#define SAA7191_REG_PLSE 0x0a -#define SAA7191_REG_SESE 0x0b -#define SAA7191_REG_GAIN 0x0c -#define SAA7191_REG_STDC 0x0d -#define SAA7191_REG_IOCK 0x0e -#define SAA7191_REG_CTL3 0x0f -#define SAA7191_REG_CTL4 0x10 -#define SAA7191_REG_CHCV 0x11 -#define SAA7191_REG_HS6B 0x14 -#define SAA7191_REG_HS6S 0x15 -#define SAA7191_REG_HC6B 0x16 -#define SAA7191_REG_HC6S 0x17 -#define SAA7191_REG_HP6I 0x18 -#define SAA7191_REG_STATUS 0xff /* not really a subaddress */ - -/* Status Register definitions */ -#define SAA7191_STATUS_CODE 0x01 /* color detected flag */ -#define SAA7191_STATUS_FIDT 0x20 /* signal type 50/60 Hz */ -#define SAA7191_STATUS_HLCK 0x40 /* PLL unlocked(1)/locked(0) */ -#define SAA7191_STATUS_STTC 0x80 /* tv/vtr time constant */ - -/* Luminance Control Register definitions */ -/* input mode select bit: - * 0=CVBS (chrominance trap active), 1=S-Video (trap bypassed) */ -#define SAA7191_LUMA_BYPS 0x80 -/* pre-filter (only when chrominance trap is active) */ -#define SAA7191_LUMA_PREF 0x40 -/* aperture bandpass to select different characteristics with maximums - * (bits 4-5) */ -#define SAA7191_LUMA_BPSS_MASK 0x30 -#define SAA7191_LUMA_BPSS_SHIFT 4 -#define SAA7191_LUMA_BPSS_3 0x30 -#define SAA7191_LUMA_BPSS_2 0x20 -#define SAA7191_LUMA_BPSS_1 0x10 -#define SAA7191_LUMA_BPSS_0 0x00 -/* coring range for high frequency components according to 8-bit luminance - * (bits 2-3) - * 0=coring off, n= (+-)n LSB */ -#define SAA7191_LUMA_CORI_MASK 0x0c -#define SAA7191_LUMA_CORI_SHIFT 2 -#define SAA7191_LUMA_CORI_3 0x0c -#define SAA7191_LUMA_CORI_2 0x08 -#define SAA7191_LUMA_CORI_1 0x04 -#define SAA7191_LUMA_CORI_0 0x00 -/* aperture bandpass filter weights high frequency components of luminance - * signal (bits 0-1) - * 0=factor 0, 1=0.25, 2=0.5, 3=1 */ -#define SAA7191_LUMA_APER_MASK 0x03 -#define SAA7191_LUMA_APER_SHIFT 0 -#define SAA7191_LUMA_APER_3 0x03 -#define SAA7191_LUMA_APER_2 0x02 -#define SAA7191_LUMA_APER_1 0x01 -#define SAA7191_LUMA_APER_0 0x00 - -/* Chrominance Gain Control Settings Register definitions */ -/* colour on: 0=automatic colour-killer enabled, 1=forced colour on */ -#define SAA7191_GAIN_COLO 0x80 -/* chrominance gain control (AGC filter) - * 0=loop filter time constant slow, 1=medium, 2=fast, 3=actual gain */ -#define SAA7191_GAIN_LFIS_MASK 0x60 -#define SAA7191_GAIN_LFIS_SHIFT 5 -#define SAA7191_GAIN_LFIS_3 0x60 -#define SAA7191_GAIN_LFIS_2 0x40 -#define SAA7191_GAIN_LFIS_1 0x20 -#define SAA7191_GAIN_LFIS_0 0x00 - -/* Standard/Mode Control Register definitions */ -/* tv/vtr mode bit: 0=TV mode (slow time constant), - * 1=VTR mode (fast time constant) */ -#define SAA7191_STDC_VTRC 0x80 -/* SAA7191B-specific functions enable (RTCO, ODD and GPSW0 outputs) - * 0=outputs set to high-impedance (circuit equals SAA7191), 1=enabled */ -#define SAA7191_STDC_NFEN 0x08 -/* HREF generation: 0=like SAA7191, 1=HREF is 8xLLC2 clocks earlier */ -#define SAA7191_STDC_HRMV 0x04 -/* general purpose switch 0 - * (not used with VINO afaik) */ -#define SAA7191_STDC_GPSW0 0x02 -/* SECAM mode bit: 0=other standards, 1=SECAM */ -#define SAA7191_STDC_SECS 0x01 - -/* I/O and Clock Control Register definitions */ -/* horizontal clock PLL: 0=PLL closed, - * 1=PLL circuit open and horizontal freq fixed */ -#define SAA7191_IOCK_HPLL 0x80 -/* colour-difference output enable (outputs UV0-UV7) */ -#define SAA7191_IOCK_OEDC 0x40 -/* H-sync output enable */ -#define SAA7191_IOCK_OEHS 0x20 -/* V-sync output enable */ -#define SAA7191_IOCK_OEVS 0x10 -/* luminance output enable (outputs Y0-Y7) */ -#define SAA7191_IOCK_OEDY 0x08 -/* S-VHS bit (chrominance from CVBS or from chrominance input): - * 0=controlled by BYPS-bit, 1=from chrominance input */ -#define SAA7191_IOCK_CHRS 0x04 -/* general purpose switch 2 - * VINO-specific: 0=used with CVBS, 1=used with S-Video */ -#define SAA7191_IOCK_GPSW2 0x02 -/* general purpose switch 1 */ -/* VINO-specific: 0=always, 1=not used!*/ -#define SAA7191_IOCK_GPSW1 0x01 - -/* Miscellaneous Control #1 Register definitions */ -/* automatic field detection (50/60Hz standard) */ -#define SAA7191_CTL3_AUFD 0x80 -/* field select: (if AUFD=0) - * 0=50Hz (625 lines), 1=60Hz (525 lines) */ -#define SAA7191_CTL3_FSEL 0x40 -/* SECAM cross-colour reduction enable */ -#define SAA7191_CTL3_SXCR 0x20 -/* sync and clamping pulse enable (HCL and HSY outputs) */ -#define SAA7191_CTL3_SCEN 0x10 -/* output format: 0=4:1:1, 1=4:2:2 (4:2:2 for VINO) */ -#define SAA7191_CTL3_OFTS 0x08 -/* luminance delay compensation - * 0=0*2/LLC, 1=+1*2/LLC, 2=+2*2/LLC, 3=+3*2/LLC, - * 4=-4*2/LLC, 5=-3*2/LLC, 6=-2*2/LLC, 7=-1*2/LLC - * step size = 2/LLC = 67.8ns for 50Hz, 81.5ns for 60Hz */ -#define SAA7191_CTL3_YDEL_MASK 0x07 -#define SAA7191_CTL3_YDEL_SHIFT 0 -#define SAA7191_CTL3_YDEL2 0x04 -#define SAA7191_CTL3_YDEL1 0x02 -#define SAA7191_CTL3_YDEL0 0x01 - -/* Miscellaneous Control #2 Register definitions */ -/* select HREF position - * 0=normal, HREF is matched to YUV output port, - * 1=HREF is matched to CVBS input port */ -#define SAA7191_CTL4_HRFS 0x04 -/* vertical noise reduction - * 0=normal, 1=searching window, 2=auto-deflection, 3=reduction bypassed */ -#define SAA7191_CTL4_VNOI_MASK 0x03 -#define SAA7191_CTL4_VNOI_SHIFT 0 -#define SAA7191_CTL4_VNOI_3 0x03 -#define SAA7191_CTL4_VNOI_2 0x02 -#define SAA7191_CTL4_VNOI_1 0x01 -#define SAA7191_CTL4_VNOI_0 0x00 - -/* Chrominance Gain Control Register definitions - * - for QAM-modulated input signals, effects output amplitude - * (SECAM gain fixed) - * (nominal values for UV CCIR level) */ -#define SAA7191_CHCV_NTSC 0x2c -#define SAA7191_CHCV_PAL 0x59 - -/* Driver interface definitions */ -#define SAA7191_INPUT_COMPOSITE 0 -#define SAA7191_INPUT_SVIDEO 1 - -#define SAA7191_NORM_PAL 1 -#define SAA7191_NORM_NTSC 2 -#define SAA7191_NORM_SECAM 3 - -struct saa7191_status { - /* 0=no signal, 1=signal detected */ - int signal; - /* 0=50hz (pal) signal, 1=60hz (ntsc) signal */ - int signal_60hz; - /* 0=no color detected, 1=color detected */ - int color; - - /* current SAA7191_INPUT_ */ - int input; - /* current SAA7191_NORM_ */ - int norm; -}; - -#define SAA7191_BANDPASS_MIN 0x00 -#define SAA7191_BANDPASS_MAX 0x03 -#define SAA7191_BANDPASS_DEFAULT 0x00 - -#define SAA7191_BANDPASS_WEIGHT_MIN 0x00 -#define SAA7191_BANDPASS_WEIGHT_MAX 0x03 -#define SAA7191_BANDPASS_WEIGHT_DEFAULT 0x01 - -#define SAA7191_CORING_MIN 0x00 -#define SAA7191_CORING_MAX 0x03 -#define SAA7191_CORING_DEFAULT 0x00 - -#define SAA7191_HUE_MIN 0x00 -#define SAA7191_HUE_MAX 0xff -#define SAA7191_HUE_DEFAULT 0x80 - -#define SAA7191_VTRC_MIN 0x00 -#define SAA7191_VTRC_MAX 0x01 -#define SAA7191_VTRC_DEFAULT 0x00 - -#define SAA7191_FORCE_COLOUR_MIN 0x00 -#define SAA7191_FORCE_COLOUR_MAX 0x01 -#define SAA7191_FORCE_COLOUR_DEFAULT 0x00 - -#define SAA7191_CHROMA_GAIN_MIN 0x00 -#define SAA7191_CHROMA_GAIN_MAX 0x03 -#define SAA7191_CHROMA_GAIN_DEFAULT 0x00 - -#define SAA7191_LUMA_DELAY_MIN -0x04 -#define SAA7191_LUMA_DELAY_MAX 0x03 -#define SAA7191_LUMA_DELAY_DEFAULT 0x01 - -#define SAA7191_VNR_MIN 0x00 -#define SAA7191_VNR_MAX 0x03 -#define SAA7191_VNR_DEFAULT 0x00 - -#define SAA7191_CONTROL_BANDPASS (V4L2_CID_PRIVATE_BASE + 0) -#define SAA7191_CONTROL_BANDPASS_WEIGHT (V4L2_CID_PRIVATE_BASE + 1) -#define SAA7191_CONTROL_CORING (V4L2_CID_PRIVATE_BASE + 2) -#define SAA7191_CONTROL_FORCE_COLOUR (V4L2_CID_PRIVATE_BASE + 3) -#define SAA7191_CONTROL_CHROMA_GAIN (V4L2_CID_PRIVATE_BASE + 4) -#define SAA7191_CONTROL_VTRC (V4L2_CID_PRIVATE_BASE + 5) -#define SAA7191_CONTROL_LUMA_DELAY (V4L2_CID_PRIVATE_BASE + 6) -#define SAA7191_CONTROL_VNR (V4L2_CID_PRIVATE_BASE + 7) - -#define DECODER_SAA7191_GET_STATUS _IOR('d', 195, struct saa7191_status) -#define DECODER_SAA7191_SET_NORM _IOW('d', 196, int) - -#endif diff --git a/drivers/staging/media/vino/vino.c b/drivers/staging/media/vino/vino.c deleted file mode 100644 index 2c85357f774d..000000000000 --- a/drivers/staging/media/vino/vino.c +++ /dev/null @@ -1,4345 +0,0 @@ -/* - * Driver for the VINO (Video In No Out) system found in SGI Indys. - * - * This file is subject to the terms and conditions of the GNU General Public - * License version 2 as published by the Free Software Foundation. - * - * Copyright (C) 2004,2005 Mikael Nousiainen <tmnousia@cc.hut.fi> - * - * Based on the previous version of the driver for 2.4 kernels by: - * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org> - * - * v4l2_device/v4l2_subdev conversion by: - * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl> - * - * Note: this conversion is untested! Please contact the linux-media - * mailinglist if you can test this, together with the test results. - */ - -/* - * TODO: - * - remove "mark pages reserved-hacks" from memory allocation code - * and implement fault() - * - check decimation, calculating and reporting image size when - * using decimation - * - implement read(), user mode buffers and overlay (?) - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/dma-mapping.h> -#include <linux/errno.h> -#include <linux/fs.h> -#include <linux/interrupt.h> -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/mm.h> -#include <linux/time.h> -#include <linux/kmod.h> - -#include <linux/i2c.h> - -#include <linux/videodev2.h> -#include <media/v4l2-device.h> -#include <media/v4l2-ioctl.h> -#include <linux/mutex.h> - -#include <asm/paccess.h> -#include <asm/io.h> -#include <asm/sgi/ip22.h> -#include <asm/sgi/mc.h> - -#include "vino.h" -#include "saa7191.h" -#include "indycam.h" - -/* Uncomment the following line to get lots and lots of (mostly useless) - * debug info. - * Note that the debug output also slows down the driver significantly */ -// #define VINO_DEBUG -// #define VINO_DEBUG_INT - -#define VINO_MODULE_VERSION "0.0.7" - -MODULE_DESCRIPTION("SGI VINO Video4Linux2 driver"); -MODULE_VERSION(VINO_MODULE_VERSION); -MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>"); -MODULE_LICENSE("GPL"); - -#ifdef VINO_DEBUG -#define dprintk(x...) printk("VINO: " x); -#else -#define dprintk(x...) -#endif - -#define VINO_NO_CHANNEL 0 -#define VINO_CHANNEL_A 1 -#define VINO_CHANNEL_B 2 - -#define VINO_PAL_WIDTH 768 -#define VINO_PAL_HEIGHT 576 -#define VINO_NTSC_WIDTH 640 -#define VINO_NTSC_HEIGHT 480 - -#define VINO_MIN_WIDTH 32 -#define VINO_MIN_HEIGHT 32 - -#define VINO_CLIPPING_START_ODD_D1 1 -#define VINO_CLIPPING_START_ODD_PAL 15 -#define VINO_CLIPPING_START_ODD_NTSC 12 - -#define VINO_CLIPPING_START_EVEN_D1 2 -#define VINO_CLIPPING_START_EVEN_PAL 15 -#define VINO_CLIPPING_START_EVEN_NTSC 12 - -#define VINO_INPUT_CHANNEL_COUNT 3 - -/* the number is the index for vino_inputs */ -#define VINO_INPUT_NONE -1 -#define VINO_INPUT_COMPOSITE 0 -#define VINO_INPUT_SVIDEO 1 -#define VINO_INPUT_D1 2 - -#define VINO_PAGE_RATIO (PAGE_SIZE / VINO_PAGE_SIZE) - -#define VINO_FIFO_THRESHOLD_DEFAULT 16 - -#define VINO_FRAMEBUFFER_SIZE ((VINO_PAL_WIDTH \ - * VINO_PAL_HEIGHT * 4 \ - + 3 * PAGE_SIZE) & ~(PAGE_SIZE - 1)) - -#define VINO_FRAMEBUFFER_COUNT_MAX 8 - -#define VINO_FRAMEBUFFER_UNUSED 0 -#define VINO_FRAMEBUFFER_IN_USE 1 -#define VINO_FRAMEBUFFER_READY 2 - -#define VINO_QUEUE_ERROR -1 -#define VINO_QUEUE_MAGIC 0x20050125 - -#define VINO_MEMORY_NONE 0 -#define VINO_MEMORY_MMAP 1 -#define VINO_MEMORY_USERPTR 2 - -#define VINO_DUMMY_DESC_COUNT 4 -#define VINO_DESC_FETCH_DELAY 5 /* microseconds */ - -#define VINO_MAX_FRAME_SKIP_COUNT 128 - -/* the number is the index for vino_data_formats */ -#define VINO_DATA_FMT_NONE -1 -#define VINO_DATA_FMT_GREY 0 -#define VINO_DATA_FMT_RGB332 1 -#define VINO_DATA_FMT_RGB32 2 -#define VINO_DATA_FMT_YUV 3 - -#define VINO_DATA_FMT_COUNT 4 - -/* the number is the index for vino_data_norms */ -#define VINO_DATA_NORM_NONE -1 -#define VINO_DATA_NORM_NTSC 0 -#define VINO_DATA_NORM_PAL 1 -#define VINO_DATA_NORM_SECAM 2 -#define VINO_DATA_NORM_D1 3 - -#define VINO_DATA_NORM_COUNT 4 - -/* I2C controller flags */ -#define SGI_I2C_FORCE_IDLE (0 << 0) -#define SGI_I2C_NOT_IDLE (1 << 0) -#define SGI_I2C_WRITE (0 << 1) -#define SGI_I2C_READ (1 << 1) -#define SGI_I2C_RELEASE_BUS (0 << 2) -#define SGI_I2C_HOLD_BUS (1 << 2) -#define SGI_I2C_XFER_DONE (0 << 4) -#define SGI_I2C_XFER_BUSY (1 << 4) -#define SGI_I2C_ACK (0 << 5) -#define SGI_I2C_NACK (1 << 5) -#define SGI_I2C_BUS_OK (0 << 7) -#define SGI_I2C_BUS_ERR (1 << 7) - -/* Internal data structure definitions */ - -struct vino_input { - char *name; - v4l2_std_id std; -}; - -struct vino_clipping { - unsigned int left, right, top, bottom; -}; - -struct vino_data_format { - /* the description */ - char *description; - /* bytes per pixel */ - unsigned int bpp; - /* V4L2 fourcc code */ - __u32 pixelformat; - /* V4L2 colorspace (duh!) */ - enum v4l2_colorspace colorspace; -}; - -struct vino_data_norm { - char *description; - unsigned int width, height; - struct vino_clipping odd; - struct vino_clipping even; - - v4l2_std_id std; - unsigned int fps_min, fps_max; - __u32 framelines; -}; - -struct vino_descriptor_table { - /* the number of PAGE_SIZE sized pages in the buffer */ - unsigned int page_count; - /* virtual (kmalloc'd) pointers to the actual data - * (in PAGE_SIZE chunks, used with mmap streaming) */ - unsigned long *virtual; - - /* cpu address for the VINO descriptor table - * (contains DMA addresses, VINO_PAGE_SIZE chunks) */ - unsigned long *dma_cpu; - /* dma address for the VINO descriptor table - * (contains DMA addresses, VINO_PAGE_SIZE chunks) */ - dma_addr_t dma; -}; - -struct vino_framebuffer { - /* identifier nubmer */ - unsigned int id; - /* the length of the whole buffer */ - unsigned int size; - /* the length of actual data in buffer */ - unsigned int data_size; - /* the data format */ - unsigned int data_format; - /* the state of buffer data */ - unsigned int state; - /* is the buffer mapped in user space? */ - unsigned int map_count; - /* memory offset for mmap() */ - unsigned int offset; - /* frame counter */ - unsigned int frame_counter; - /* timestamp (written when image capture finishes) */ - struct timeval timestamp; - - struct vino_descriptor_table desc_table; - - spinlock_t state_lock; -}; - -struct vino_framebuffer_fifo { - unsigned int length; - - unsigned int used; - unsigned int head; - unsigned int tail; - - unsigned int data[VINO_FRAMEBUFFER_COUNT_MAX]; -}; - -struct vino_framebuffer_queue { - unsigned int magic; - - /* VINO_MEMORY_NONE, VINO_MEMORY_MMAP or VINO_MEMORY_USERPTR */ - unsigned int type; - unsigned int length; - - /* data field of in and out contain index numbers for buffer */ - struct vino_framebuffer_fifo in; - struct vino_framebuffer_fifo out; - - struct vino_framebuffer *buffer[VINO_FRAMEBUFFER_COUNT_MAX]; - - spinlock_t queue_lock; - struct mutex queue_mutex; - wait_queue_head_t frame_wait_queue; -}; - -struct vino_interrupt_data { - struct timeval timestamp; - unsigned int frame_counter; - unsigned int skip_count; - unsigned int skip; -}; - -struct vino_channel_settings { - unsigned int channel; - - int input; - unsigned int data_format; - unsigned int data_norm; - struct vino_clipping clipping; - unsigned int decimation; - unsigned int line_size; - unsigned int alpha; - unsigned int fps; - unsigned int framert_reg; - - unsigned int fifo_threshold; - - struct vino_framebuffer_queue fb_queue; - - /* number of the current field */ - unsigned int field; - - /* read in progress */ - int reading; - /* streaming is active */ - int streaming; - /* the driver is currently processing the queue */ - int capturing; - - struct mutex mutex; - spinlock_t capture_lock; - - unsigned int users; - - struct vino_interrupt_data int_data; - - /* V4L support */ - struct video_device *vdev; -}; - -struct vino_settings { - struct v4l2_device v4l2_dev; - struct vino_channel_settings a; - struct vino_channel_settings b; - - /* the channel which owns this client: - * VINO_NO_CHANNEL, VINO_CHANNEL_A or VINO_CHANNEL_B */ - unsigned int decoder_owner; - struct v4l2_subdev *decoder; - unsigned int camera_owner; - struct v4l2_subdev *camera; - - /* a lock for vino register access */ - spinlock_t vino_lock; - /* a lock for channel input changes */ - spinlock_t input_lock; - - unsigned long dummy_page; - struct vino_descriptor_table dummy_desc_table; -}; - -/* Module parameters */ - -/* - * Using vino_pixel_conversion the ABGR32-format pixels supplied - * by the VINO chip can be converted to more common formats - * like RGBA32 (or probably RGB24 in the future). This way we - * can give out data that can be specified correctly with - * the V4L2-definitions. - * - * The pixel format is specified as RGBA32 when no conversion - * is used. - * - * Note that this only affects the 32-bit bit depth. - * - * Use non-zero value to enable conversion. - */ -static int vino_pixel_conversion; - -module_param_named(pixelconv, vino_pixel_conversion, int, 0); - -MODULE_PARM_DESC(pixelconv, - "enable pixel conversion (non-zero value enables)"); - -/* Internal data structures */ - -static struct sgi_vino *vino; - -static struct vino_settings *vino_drvdata; - -#define camera_call(o, f, args...) \ - v4l2_subdev_call(vino_drvdata->camera, o, f, ##args) -#define decoder_call(o, f, args...) \ - v4l2_subdev_call(vino_drvdata->decoder, o, f, ##args) - -static const char *vino_driver_name = "vino"; -static const char *vino_driver_description = "SGI VINO"; -static const char *vino_bus_name = "GIO64 bus"; -static const char *vino_vdev_name_a = "SGI VINO Channel A"; -static const char *vino_vdev_name_b = "SGI VINO Channel B"; - -static void vino_capture_tasklet(unsigned long channel); - -DECLARE_TASKLET(vino_tasklet_a, vino_capture_tasklet, VINO_CHANNEL_A); -DECLARE_TASKLET(vino_tasklet_b, vino_capture_tasklet, VINO_CHANNEL_B); - -static const struct vino_input vino_inputs[] = { - { - .name = "Composite", - .std = V4L2_STD_NTSC | V4L2_STD_PAL - | V4L2_STD_SECAM, - }, { - .name = "S-Video", - .std = V4L2_STD_NTSC | V4L2_STD_PAL - | V4L2_STD_SECAM, - }, { - .name = "D1/IndyCam", - .std = V4L2_STD_NTSC, - } -}; - -static const struct vino_data_format vino_data_formats[] = { - { - .description = "8-bit greyscale", - .bpp = 1, - .pixelformat = V4L2_PIX_FMT_GREY, - .colorspace = V4L2_COLORSPACE_SMPTE170M, - }, { - .description = "8-bit dithered RGB 3-3-2", - .bpp = 1, - .pixelformat = V4L2_PIX_FMT_RGB332, - .colorspace = V4L2_COLORSPACE_SRGB, - }, { - .description = "32-bit RGB", - .bpp = 4, - .pixelformat = V4L2_PIX_FMT_RGB32, - .colorspace = V4L2_COLORSPACE_SRGB, - }, { - .description = "YUV 4:2:2", - .bpp = 2, - .pixelformat = V4L2_PIX_FMT_YUYV, // XXX: swapped? - .colorspace = V4L2_COLORSPACE_SMPTE170M, - } -}; - -static const struct vino_data_norm vino_data_norms[] = { - { - .description = "NTSC", - .std = V4L2_STD_NTSC, - .fps_min = 6, - .fps_max = 30, - .framelines = 525, - .width = VINO_NTSC_WIDTH, - .height = VINO_NTSC_HEIGHT, - .odd = { - .top = VINO_CLIPPING_START_ODD_NTSC, - .left = 0, - .bottom = VINO_CLIPPING_START_ODD_NTSC - + VINO_NTSC_HEIGHT / 2 - 1, - .right = VINO_NTSC_WIDTH, - }, - .even = { - .top = VINO_CLIPPING_START_EVEN_NTSC, - .left = 0, - .bottom = VINO_CLIPPING_START_EVEN_NTSC - + VINO_NTSC_HEIGHT / 2 - 1, - .right = VINO_NTSC_WIDTH, - }, - }, { - .description = "PAL", - .std = V4L2_STD_PAL, - .fps_min = 5, - .fps_max = 25, - .framelines = 625, - .width = VINO_PAL_WIDTH, - .height = VINO_PAL_HEIGHT, - .odd = { - .top = VINO_CLIPPING_START_ODD_PAL, - .left = 0, - .bottom = VINO_CLIPPING_START_ODD_PAL - + VINO_PAL_HEIGHT / 2 - 1, - .right = VINO_PAL_WIDTH, - }, - .even = { - .top = VINO_CLIPPING_START_EVEN_PAL, - .left = 0, - .bottom = VINO_CLIPPING_START_EVEN_PAL - + VINO_PAL_HEIGHT / 2 - 1, - .right = VINO_PAL_WIDTH, - }, - }, { - .description = "SECAM", - .std = V4L2_STD_SECAM, - .fps_min = 5, - .fps_max = 25, - .framelines = 625, - .width = VINO_PAL_WIDTH, - .height = VINO_PAL_HEIGHT, - .odd = { - .top = VINO_CLIPPING_START_ODD_PAL, - .left = 0, - .bottom = VINO_CLIPPING_START_ODD_PAL - + VINO_PAL_HEIGHT / 2 - 1, - .right = VINO_PAL_WIDTH, - }, - .even = { - .top = VINO_CLIPPING_START_EVEN_PAL, - .left = 0, - .bottom = VINO_CLIPPING_START_EVEN_PAL - + VINO_PAL_HEIGHT / 2 - 1, - .right = VINO_PAL_WIDTH, - }, - }, { - .description = "NTSC/D1", - .std = V4L2_STD_NTSC, - .fps_min = 6, - .fps_max = 30, - .framelines = 525, - .width = VINO_NTSC_WIDTH, - .height = VINO_NTSC_HEIGHT, - .odd = { - .top = VINO_CLIPPING_START_ODD_D1, - .left = 0, - .bottom = VINO_CLIPPING_START_ODD_D1 - + VINO_NTSC_HEIGHT / 2 - 1, - .right = VINO_NTSC_WIDTH, - }, - .even = { - .top = VINO_CLIPPING_START_EVEN_D1, - .left = 0, - .bottom = VINO_CLIPPING_START_EVEN_D1 - + VINO_NTSC_HEIGHT / 2 - 1, - .right = VINO_NTSC_WIDTH, - }, - } -}; - -#define VINO_INDYCAM_V4L2_CONTROL_COUNT 9 - -struct v4l2_queryctrl vino_indycam_v4l2_controls[] = { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Automatic Gain Control", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = INDYCAM_AGC_DEFAULT, - }, { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Automatic White Balance", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = INDYCAM_AWB_DEFAULT, - }, { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = INDYCAM_GAIN_MIN, - .maximum = INDYCAM_GAIN_MAX, - .step = 1, - .default_value = INDYCAM_GAIN_DEFAULT, - }, { - .id = INDYCAM_CONTROL_RED_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Saturation", - .minimum = INDYCAM_RED_SATURATION_MIN, - .maximum = INDYCAM_RED_SATURATION_MAX, - .step = 1, - .default_value = INDYCAM_RED_SATURATION_DEFAULT, - }, { - .id = INDYCAM_CONTROL_BLUE_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Saturation", - .minimum = INDYCAM_BLUE_SATURATION_MIN, - .maximum = INDYCAM_BLUE_SATURATION_MAX, - .step = 1, - .default_value = INDYCAM_BLUE_SATURATION_DEFAULT, - }, { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = INDYCAM_RED_BALANCE_MIN, - .maximum = INDYCAM_RED_BALANCE_MAX, - .step = 1, - .default_value = INDYCAM_RED_BALANCE_DEFAULT, - }, { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = INDYCAM_BLUE_BALANCE_MIN, - .maximum = INDYCAM_BLUE_BALANCE_MAX, - .step = 1, - .default_value = INDYCAM_BLUE_BALANCE_DEFAULT, - }, { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Shutter Control", - .minimum = INDYCAM_SHUTTER_MIN, - .maximum = INDYCAM_SHUTTER_MAX, - .step = 1, - .default_value = INDYCAM_SHUTTER_DEFAULT, - }, { - .id = V4L2_CID_GAMMA, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gamma", - .minimum = INDYCAM_GAMMA_MIN, - .maximum = INDYCAM_GAMMA_MAX, - .step = 1, - .default_value = INDYCAM_GAMMA_DEFAULT, - } -}; - -#define VINO_SAA7191_V4L2_CONTROL_COUNT 9 - -struct v4l2_queryctrl vino_saa7191_v4l2_controls[] = { - { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = SAA7191_HUE_MIN, - .maximum = SAA7191_HUE_MAX, - .step = 1, - .default_value = SAA7191_HUE_DEFAULT, - }, { - .id = SAA7191_CONTROL_BANDPASS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Luminance Bandpass", - .minimum = SAA7191_BANDPASS_MIN, - .maximum = SAA7191_BANDPASS_MAX, - .step = 1, - .default_value = SAA7191_BANDPASS_DEFAULT, - }, { - .id = SAA7191_CONTROL_BANDPASS_WEIGHT, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Luminance Bandpass Weight", - .minimum = SAA7191_BANDPASS_WEIGHT_MIN, - .maximum = SAA7191_BANDPASS_WEIGHT_MAX, - .step = 1, - .default_value = SAA7191_BANDPASS_WEIGHT_DEFAULT, - }, { - .id = SAA7191_CONTROL_CORING, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "HF Luminance Coring", - .minimum = SAA7191_CORING_MIN, - .maximum = SAA7191_CORING_MAX, - .step = 1, - .default_value = SAA7191_CORING_DEFAULT, - }, { - .id = SAA7191_CONTROL_FORCE_COLOUR, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Force Colour", - .minimum = SAA7191_FORCE_COLOUR_MIN, - .maximum = SAA7191_FORCE_COLOUR_MAX, - .step = 1, - .default_value = SAA7191_FORCE_COLOUR_DEFAULT, - }, { - .id = SAA7191_CONTROL_CHROMA_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Chrominance Gain Control", - .minimum = SAA7191_CHROMA_GAIN_MIN, - .maximum = SAA7191_CHROMA_GAIN_MAX, - .step = 1, - .default_value = SAA7191_CHROMA_GAIN_DEFAULT, - }, { - .id = SAA7191_CONTROL_VTRC, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "VTR Time Constant", - .minimum = SAA7191_VTRC_MIN, - .maximum = SAA7191_VTRC_MAX, - .step = 1, - .default_value = SAA7191_VTRC_DEFAULT, - }, { - .id = SAA7191_CONTROL_LUMA_DELAY, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Luminance Delay Compensation", - .minimum = SAA7191_LUMA_DELAY_MIN, - .maximum = SAA7191_LUMA_DELAY_MAX, - .step = 1, - .default_value = SAA7191_LUMA_DELAY_DEFAULT, - }, { - .id = SAA7191_CONTROL_VNR, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Vertical Noise Reduction", - .minimum = SAA7191_VNR_MIN, - .maximum = SAA7191_VNR_MAX, - .step = 1, - .default_value = SAA7191_VNR_DEFAULT, - } -}; - -/* VINO framebuffer/DMA descriptor management */ - -static void vino_free_buffer_with_count(struct vino_framebuffer *fb, - unsigned int count) -{ - unsigned int i; - - dprintk("vino_free_buffer_with_count(): count = %d\n", count); - - for (i = 0; i < count; i++) { - ClearPageReserved(virt_to_page((void *)fb->desc_table.virtual[i])); - dma_unmap_single(NULL, - fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i], - PAGE_SIZE, DMA_FROM_DEVICE); - free_page(fb->desc_table.virtual[i]); - } - - dma_free_coherent(NULL, - VINO_PAGE_RATIO * (fb->desc_table.page_count + 4) * - sizeof(dma_addr_t), (void *)fb->desc_table.dma_cpu, - fb->desc_table.dma); - kfree(fb->desc_table.virtual); - - memset(fb, 0, sizeof(struct vino_framebuffer)); -} - -static void vino_free_buffer(struct vino_framebuffer *fb) -{ - vino_free_buffer_with_count(fb, fb->desc_table.page_count); -} - -static int vino_allocate_buffer(struct vino_framebuffer *fb, - unsigned int size) -{ - unsigned int count, i, j; - int ret = 0; - - dprintk("vino_allocate_buffer():\n"); - - if (size < 1) - return -EINVAL; - - memset(fb, 0, sizeof(struct vino_framebuffer)); - - count = ((size / PAGE_SIZE) + 4) & ~3; - - dprintk("vino_allocate_buffer(): size = %d, count = %d\n", - size, count); - - /* allocate memory for table with virtual (page) addresses */ - fb->desc_table.virtual = - kmalloc(count * sizeof(unsigned long), GFP_KERNEL); - if (!fb->desc_table.virtual) - return -ENOMEM; - - /* allocate memory for table with dma addresses - * (has space for four extra descriptors) */ - fb->desc_table.dma_cpu = - dma_alloc_coherent(NULL, VINO_PAGE_RATIO * (count + 4) * - sizeof(dma_addr_t), &fb->desc_table.dma, - GFP_KERNEL | GFP_DMA); - if (!fb->desc_table.dma_cpu) { - ret = -ENOMEM; - goto out_free_virtual; - } - - /* allocate pages for the buffer and acquire the according - * dma addresses */ - for (i = 0; i < count; i++) { - dma_addr_t dma_data_addr; - - fb->desc_table.virtual[i] = - get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!fb->desc_table.virtual[i]) { - ret = -ENOBUFS; - break; - } - - dma_data_addr = - dma_map_single(NULL, - (void *)fb->desc_table.virtual[i], - PAGE_SIZE, DMA_FROM_DEVICE); - - for (j = 0; j < VINO_PAGE_RATIO; j++) { - fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i + j] = - dma_data_addr + VINO_PAGE_SIZE * j; - } - - SetPageReserved(virt_to_page((void *)fb->desc_table.virtual[i])); - } - - /* page_count needs to be set anyway, because the descriptor table has - * been allocated according to this number */ - fb->desc_table.page_count = count; - - if (ret) { - /* the descriptor with index i doesn't contain - * a valid address yet */ - vino_free_buffer_with_count(fb, i); - return ret; - } - - //fb->size = size; - fb->size = count * PAGE_SIZE; - fb->data_format = VINO_DATA_FMT_NONE; - - /* set the dma stop-bit for the last (count+1)th descriptor */ - fb->desc_table.dma_cpu[VINO_PAGE_RATIO * count] = VINO_DESC_STOP; - return 0; - - out_free_virtual: - kfree(fb->desc_table.virtual); - return ret; -} - -#if 0 -/* user buffers not fully implemented yet */ -static int vino_prepare_user_buffer(struct vino_framebuffer *fb, - void *user, - unsigned int size) -{ - unsigned int count, i, j; - int ret = 0; - - dprintk("vino_prepare_user_buffer():\n"); - - if (size < 1) - return -EINVAL; - - memset(fb, 0, sizeof(struct vino_framebuffer)); - - count = ((size / PAGE_SIZE)) & ~3; - - dprintk("vino_prepare_user_buffer(): size = %d, count = %d\n", - size, count); - - /* allocate memory for table with virtual (page) addresses */ - fb->desc_table.virtual = (unsigned long *) - kmalloc(count * sizeof(unsigned long), GFP_KERNEL); - if (!fb->desc_table.virtual) - return -ENOMEM; - - /* allocate memory for table with dma addresses - * (has space for four extra descriptors) */ - fb->desc_table.dma_cpu = - dma_alloc_coherent(NULL, VINO_PAGE_RATIO * (count + 4) * - sizeof(dma_addr_t), &fb->desc_table.dma, - GFP_KERNEL | GFP_DMA); - if (!fb->desc_table.dma_cpu) { - ret = -ENOMEM; - goto out_free_virtual; - } - - /* allocate pages for the buffer and acquire the according - * dma addresses */ - for (i = 0; i < count; i++) { - dma_addr_t dma_data_addr; - - fb->desc_table.virtual[i] = - get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!fb->desc_table.virtual[i]) { - ret = -ENOBUFS; - break; - } - - dma_data_addr = - dma_map_single(NULL, - (void *)fb->desc_table.virtual[i], - PAGE_SIZE, DMA_FROM_DEVICE); - - for (j = 0; j < VINO_PAGE_RATIO; j++) { - fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i + j] = - dma_data_addr + VINO_PAGE_SIZE * j; - } - - SetPageReserved(virt_to_page((void *)fb->desc_table.virtual[i])); - } - - /* page_count needs to be set anyway, because the descriptor table has - * been allocated according to this number */ - fb->desc_table.page_count = count; - - if (ret) { - /* the descriptor with index i doesn't contain - * a valid address yet */ - vino_free_buffer_with_count(fb, i); - return ret; - } - - //fb->size = size; - fb->size = count * PAGE_SIZE; - - /* set the dma stop-bit for the last (count+1)th descriptor */ - fb->desc_table.dma_cpu[VINO_PAGE_RATIO * count] = VINO_DESC_STOP; - return 0; - - out_free_virtual: - kfree(fb->desc_table.virtual); - return ret; -} -#endif - -static void vino_sync_buffer(struct vino_framebuffer *fb) -{ - int i; - - dprintk("vino_sync_buffer():\n"); - - for (i = 0; i < fb->desc_table.page_count; i++) - dma_sync_single_for_cpu(NULL, - fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i], - PAGE_SIZE, DMA_FROM_DEVICE); -} - -/* Framebuffer fifo functions (need to be locked externally) */ - -static inline void vino_fifo_init(struct vino_framebuffer_fifo *f, - unsigned int length) -{ - f->length = 0; - f->used = 0; - f->head = 0; - f->tail = 0; - - if (length > VINO_FRAMEBUFFER_COUNT_MAX) - length = VINO_FRAMEBUFFER_COUNT_MAX; - - f->length = length; -} - -/* returns true/false */ -static inline int vino_fifo_has_id(struct vino_framebuffer_fifo *f, - unsigned int id) -{ - unsigned int i; - - for (i = f->head; i == (f->tail - 1); i = (i + 1) % f->length) { - if (f->data[i] == id) - return 1; - } - - return 0; -} - -#if 0 -/* returns true/false */ -static inline int vino_fifo_full(struct vino_framebuffer_fifo *f) -{ - return (f->used == f->length); -} -#endif - -static inline unsigned int vino_fifo_get_used(struct vino_framebuffer_fifo *f) -{ - return f->used; -} - -static int vino_fifo_enqueue(struct vino_framebuffer_fifo *f, unsigned int id) -{ - if (id >= f->length) { - return VINO_QUEUE_ERROR; - } - - if (vino_fifo_has_id(f, id)) { - return VINO_QUEUE_ERROR; - } - - if (f->used < f->length) { - f->data[f->tail] = id; - f->tail = (f->tail + 1) % f->length; - f->used++; - } else { - return VINO_QUEUE_ERROR; - } - - return 0; -} - -static int vino_fifo_peek(struct vino_framebuffer_fifo *f, unsigned int *id) -{ - if (f->used > 0) { - *id = f->data[f->head]; - } else { - return VINO_QUEUE_ERROR; - } - - return 0; -} - -static int vino_fifo_dequeue(struct vino_framebuffer_fifo *f, unsigned int *id) -{ - if (f->used > 0) { - *id = f->data[f->head]; - f->head = (f->head + 1) % f->length; - f->used--; - } else { - return VINO_QUEUE_ERROR; - } - - return 0; -} - -/* Framebuffer queue functions */ - -/* execute with queue_lock locked */ -static void vino_queue_free_with_count(struct vino_framebuffer_queue *q, - unsigned int length) -{ - unsigned int i; - - q->length = 0; - memset(&q->in, 0, sizeof(struct vino_framebuffer_fifo)); - memset(&q->out, 0, sizeof(struct vino_framebuffer_fifo)); - for (i = 0; i < length; i++) { - dprintk("vino_queue_free_with_count(): freeing buffer %d\n", - i); - vino_free_buffer(q->buffer[i]); - kfree(q->buffer[i]); - } - - q->type = VINO_MEMORY_NONE; - q->magic = 0; -} - -static void vino_queue_free(struct vino_framebuffer_queue *q) -{ - dprintk("vino_queue_free():\n"); - - if (q->magic != VINO_QUEUE_MAGIC) - return; - if (q->type != VINO_MEMORY_MMAP) - return; - - mutex_lock(&q->queue_mutex); - - vino_queue_free_with_count(q, q->length); - - mutex_unlock(&q->queue_mutex); -} - -static int vino_queue_init(struct vino_framebuffer_queue *q, - unsigned int *length) -{ - unsigned int i; - int ret = 0; - - dprintk("vino_queue_init(): length = %d\n", *length); - - if (q->magic == VINO_QUEUE_MAGIC) { - dprintk("vino_queue_init(): queue already initialized!\n"); - return -EINVAL; - } - - if (q->type != VINO_MEMORY_NONE) { - dprintk("vino_queue_init(): queue already initialized!\n"); - return -EINVAL; - } - - if (*length < 1) - return -EINVAL; - - mutex_lock(&q->queue_mutex); - - if (*length > VINO_FRAMEBUFFER_COUNT_MAX) - *length = VINO_FRAMEBUFFER_COUNT_MAX; - - q->length = 0; - - for (i = 0; i < *length; i++) { - dprintk("vino_queue_init(): allocating buffer %d\n", i); - q->buffer[i] = kmalloc(sizeof(struct vino_framebuffer), - GFP_KERNEL); - if (!q->buffer[i]) { - dprintk("vino_queue_init(): kmalloc() failed\n"); - ret = -ENOMEM; - break; - } - - ret = vino_allocate_buffer(q->buffer[i], - VINO_FRAMEBUFFER_SIZE); - if (ret) { - kfree(q->buffer[i]); - dprintk("vino_queue_init(): " - "vino_allocate_buffer() failed\n"); - break; - } - - q->buffer[i]->id = i; - if (i > 0) { - q->buffer[i]->offset = q->buffer[i - 1]->offset + - q->buffer[i - 1]->size; - } else { - q->buffer[i]->offset = 0; - } - - spin_lock_init(&q->buffer[i]->state_lock); - - dprintk("vino_queue_init(): buffer = %d, offset = %d, " - "size = %d\n", i, q->buffer[i]->offset, - q->buffer[i]->size); - } - - if (ret) { - vino_queue_free_with_count(q, i); - *length = 0; - } else { - q->length = *length; - vino_fifo_init(&q->in, q->length); - vino_fifo_init(&q->out, q->length); - q->type = VINO_MEMORY_MMAP; - q->magic = VINO_QUEUE_MAGIC; - } - - mutex_unlock(&q->queue_mutex); - - return ret; -} - -static struct vino_framebuffer *vino_queue_add(struct - vino_framebuffer_queue *q, - unsigned int id) -{ - struct vino_framebuffer *ret = NULL; - unsigned int total; - unsigned long flags; - - dprintk("vino_queue_add(): id = %d\n", id); - - if (q->magic != VINO_QUEUE_MAGIC) { - return ret; - } - - spin_lock_irqsave(&q->queue_lock, flags); - - if (q->length == 0) - goto out; - - if (id >= q->length) - goto out; - - /* not needed?: if (vino_fifo_full(&q->out)) { - goto out; - }*/ - /* check that outgoing queue isn't already full - * (or that it won't become full) */ - total = vino_fifo_get_used(&q->in) + - vino_fifo_get_used(&q->out); - if (total >= q->length) - goto out; - - if (vino_fifo_enqueue(&q->in, id)) - goto out; - - ret = q->buffer[id]; - -out: - spin_unlock_irqrestore(&q->queue_lock, flags); - - return ret; -} - -static struct vino_framebuffer *vino_queue_transfer(struct - vino_framebuffer_queue *q) -{ - struct vino_framebuffer *ret = NULL; - struct vino_framebuffer *fb; - int id; - unsigned long flags; - - dprintk("vino_queue_transfer():\n"); - - if (q->magic != VINO_QUEUE_MAGIC) { - return ret; - } - - spin_lock_irqsave(&q->queue_lock, flags); - - if (q->length == 0) - goto out; - - // now this actually removes an entry from the incoming queue - if (vino_fifo_dequeue(&q->in, &id)) { - goto out; - } - - dprintk("vino_queue_transfer(): id = %d\n", id); - fb = q->buffer[id]; - - // we have already checked that the outgoing queue is not full, but... - if (vino_fifo_enqueue(&q->out, id)) { - printk(KERN_ERR "vino_queue_transfer(): " - "outgoing queue is full, this shouldn't happen!\n"); - goto out; - } - - ret = fb; -out: - spin_unlock_irqrestore(&q->queue_lock, flags); - - return ret; -} - -/* returns true/false */ -static int vino_queue_incoming_contains(struct vino_framebuffer_queue *q, - unsigned int id) -{ - int ret = 0; - unsigned long flags; - - if (q->magic != VINO_QUEUE_MAGIC) { - return ret; - } - - spin_lock_irqsave(&q->queue_lock, flags); - - if (q->length == 0) - goto out; - - ret = vino_fifo_has_id(&q->in, id); - -out: - spin_unlock_irqrestore(&q->queue_lock, flags); - - return ret; -} - -/* returns true/false */ -static int vino_queue_outgoing_contains(struct vino_framebuffer_queue *q, - unsigned int id) -{ - int ret = 0; - unsigned long flags; - - if (q->magic != VINO_QUEUE_MAGIC) { - return ret; - } - - spin_lock_irqsave(&q->queue_lock, flags); - - if (q->length == 0) - goto out; - - ret = vino_fifo_has_id(&q->out, id); - -out: - spin_unlock_irqrestore(&q->queue_lock, flags); - - return ret; -} - -static int vino_queue_get_incoming(struct vino_framebuffer_queue *q, - unsigned int *used) -{ - int ret = 0; - unsigned long flags; - - if (q->magic != VINO_QUEUE_MAGIC) { - return VINO_QUEUE_ERROR; - } - - spin_lock_irqsave(&q->queue_lock, flags); - - if (q->length == 0) { - ret = VINO_QUEUE_ERROR; - goto out; - } - - *used = vino_fifo_get_used(&q->in); - -out: - spin_unlock_irqrestore(&q->queue_lock, flags); - - return ret; -} - -static int vino_queue_get_outgoing(struct vino_framebuffer_queue *q, - unsigned int *used) -{ - int ret = 0; - unsigned long flags; - - if (q->magic != VINO_QUEUE_MAGIC) { - return VINO_QUEUE_ERROR; - } - - spin_lock_irqsave(&q->queue_lock, flags); - - if (q->length == 0) { - ret = VINO_QUEUE_ERROR; - goto out; - } - - *used = vino_fifo_get_used(&q->out); - -out: - spin_unlock_irqrestore(&q->queue_lock, flags); - - return ret; -} - -#if 0 -static int vino_queue_get_total(struct vino_framebuffer_queue *q, - unsigned int *total) -{ - int ret = 0; - unsigned long flags; - - if (q->magic != VINO_QUEUE_MAGIC) { - return VINO_QUEUE_ERROR; - } - - spin_lock_irqsave(&q->queue_lock, flags); - - if (q->length == 0) { - ret = VINO_QUEUE_ERROR; - goto out; - } - - *total = vino_fifo_get_used(&q->in) + - vino_fifo_get_used(&q->out); - -out: - spin_unlock_irqrestore(&q->queue_lock, flags); - - return ret; -} -#endif - -static struct vino_framebuffer *vino_queue_peek(struct - vino_framebuffer_queue *q, - unsigned int *id) -{ - struct vino_framebuffer *ret = NULL; - unsigned long flags; - - if (q->magic != VINO_QUEUE_MAGIC) { - return ret; - } - - spin_lock_irqsave(&q->queue_lock, flags); - - if (q->length == 0) - goto out; - - if (vino_fifo_peek(&q->in, id)) { - goto out; - } - - ret = q->buffer[*id]; -out: - spin_unlock_irqrestore(&q->queue_lock, flags); - - return ret; -} - -static struct vino_framebuffer *vino_queue_remove(struct - vino_framebuffer_queue *q, - unsigned int *id) -{ - struct vino_framebuffer *ret = NULL; - unsigned long flags; - dprintk("vino_queue_remove():\n"); - - if (q->magic != VINO_QUEUE_MAGIC) { - return ret; - } - - spin_lock_irqsave(&q->queue_lock, flags); - - if (q->length == 0) - goto out; - - if (vino_fifo_dequeue(&q->out, id)) { - goto out; - } - - dprintk("vino_queue_remove(): id = %d\n", *id); - ret = q->buffer[*id]; -out: - spin_unlock_irqrestore(&q->queue_lock, flags); - - return ret; -} - -static struct -vino_framebuffer *vino_queue_get_buffer(struct vino_framebuffer_queue *q, - unsigned int id) -{ - struct vino_framebuffer *ret = NULL; - unsigned long flags; - - if (q->magic != VINO_QUEUE_MAGIC) { - return ret; - } - - spin_lock_irqsave(&q->queue_lock, flags); - - if (q->length == 0) - goto out; - - if (id >= q->length) - goto out; - - ret = q->buffer[id]; - out: - spin_unlock_irqrestore(&q->queue_lock, flags); - - return ret; -} - -static unsigned int vino_queue_get_length(struct vino_framebuffer_queue *q) -{ - unsigned int length = 0; - unsigned long flags; - - if (q->magic != VINO_QUEUE_MAGIC) { - return length; - } - - spin_lock_irqsave(&q->queue_lock, flags); - length = q->length; - spin_unlock_irqrestore(&q->queue_lock, flags); - - return length; -} - -static int vino_queue_has_mapped_buffers(struct vino_framebuffer_queue *q) -{ - unsigned int i; - int ret = 0; - unsigned long flags; - - if (q->magic != VINO_QUEUE_MAGIC) { - return ret; - } - - spin_lock_irqsave(&q->queue_lock, flags); - for (i = 0; i < q->length; i++) { - if (q->buffer[i]->map_count > 0) { - ret = 1; - break; - } - } - spin_unlock_irqrestore(&q->queue_lock, flags); - - return ret; -} - -/* VINO functions */ - -/* execute with input_lock locked */ -static void vino_update_line_size(struct vino_channel_settings *vcs) -{ - unsigned int w = vcs->clipping.right - vcs->clipping.left; - unsigned int d = vcs->decimation; - unsigned int bpp = vino_data_formats[vcs->data_format].bpp; - unsigned int lsize; - - dprintk("update_line_size(): before: w = %d, d = %d, " - "line_size = %d\n", w, d, vcs->line_size); - - /* line size must be multiple of 8 bytes */ - lsize = (bpp * (w / d)) & ~7; - w = (lsize / bpp) * d; - - vcs->clipping.right = vcs->clipping.left + w; - vcs->line_size = lsize; - - dprintk("update_line_size(): after: w = %d, d = %d, " - "line_size = %d\n", w, d, vcs->line_size); -} - -/* execute with input_lock locked */ -static void vino_set_clipping(struct vino_channel_settings *vcs, - unsigned int x, unsigned int y, - unsigned int w, unsigned int h) -{ - unsigned int maxwidth, maxheight; - unsigned int d; - - maxwidth = vino_data_norms[vcs->data_norm].width; - maxheight = vino_data_norms[vcs->data_norm].height; - d = vcs->decimation; - - y &= ~1; /* odd/even fields */ - - if (x > maxwidth) { - x = 0; - } - if (y > maxheight) { - y = 0; - } - - if (((w / d) < VINO_MIN_WIDTH) - || ((h / d) < VINO_MIN_HEIGHT)) { - w = VINO_MIN_WIDTH * d; - h = VINO_MIN_HEIGHT * d; - } - - if ((x + w) > maxwidth) { - w = maxwidth - x; - if ((w / d) < VINO_MIN_WIDTH) - x = maxwidth - VINO_MIN_WIDTH * d; - } - if ((y + h) > maxheight) { - h = maxheight - y; - if ((h / d) < VINO_MIN_HEIGHT) - y = maxheight - VINO_MIN_HEIGHT * d; - } - - vcs->clipping.left = x; - vcs->clipping.top = y; - vcs->clipping.right = x + w; - vcs->clipping.bottom = y + h; - - vino_update_line_size(vcs); - - dprintk("clipping %d, %d, %d, %d / %d - %d\n", - vcs->clipping.left, vcs->clipping.top, vcs->clipping.right, - vcs->clipping.bottom, vcs->decimation, vcs->line_size); -} - -/* execute with input_lock locked */ -static inline void vino_set_default_clipping(struct vino_channel_settings *vcs) -{ - vino_set_clipping(vcs, 0, 0, vino_data_norms[vcs->data_norm].width, - vino_data_norms[vcs->data_norm].height); -} - -/* execute with input_lock locked */ -static void vino_set_scaling(struct vino_channel_settings *vcs, - unsigned int w, unsigned int h) -{ - unsigned int x, y, curw, curh, d; - - x = vcs->clipping.left; - y = vcs->clipping.top; - curw = vcs->clipping.right - vcs->clipping.left; - curh = vcs->clipping.bottom - vcs->clipping.top; - - d = max(curw / w, curh / h); - - dprintk("scaling w: %d, h: %d, curw: %d, curh: %d, d: %d\n", - w, h, curw, curh, d); - - if (d < 1) { - d = 1; - } else if (d > 8) { - d = 8; - } - - vcs->decimation = d; - vino_set_clipping(vcs, x, y, w * d, h * d); - - dprintk("scaling %d, %d, %d, %d / %d - %d\n", vcs->clipping.left, - vcs->clipping.top, vcs->clipping.right, vcs->clipping.bottom, - vcs->decimation, vcs->line_size); -} - -/* execute with input_lock locked */ -static inline void vino_set_default_scaling(struct vino_channel_settings *vcs) -{ - vino_set_scaling(vcs, vcs->clipping.right - vcs->clipping.left, - vcs->clipping.bottom - vcs->clipping.top); -} - -/* execute with input_lock locked */ -static void vino_set_framerate(struct vino_channel_settings *vcs, - unsigned int fps) -{ - unsigned int mask; - - switch (vcs->data_norm) { - case VINO_DATA_NORM_NTSC: - case VINO_DATA_NORM_D1: - fps = (unsigned int)(fps / 6) * 6; // FIXME: round! - - if (fps < vino_data_norms[vcs->data_norm].fps_min) - fps = vino_data_norms[vcs->data_norm].fps_min; - if (fps > vino_data_norms[vcs->data_norm].fps_max) - fps = vino_data_norms[vcs->data_norm].fps_max; - - switch (fps) { - case 6: - mask = 0x003; - break; - case 12: - mask = 0x0c3; - break; - case 18: - mask = 0x333; - break; - case 24: - mask = 0x3ff; - break; - case 30: - mask = 0xfff; - break; - default: - mask = VINO_FRAMERT_FULL; - } - vcs->framert_reg = VINO_FRAMERT_RT(mask); - break; - case VINO_DATA_NORM_PAL: - case VINO_DATA_NORM_SECAM: - fps = (unsigned int)(fps / 5) * 5; // FIXME: round! - - if (fps < vino_data_norms[vcs->data_norm].fps_min) - fps = vino_data_norms[vcs->data_norm].fps_min; - if (fps > vino_data_norms[vcs->data_norm].fps_max) - fps = vino_data_norms[vcs->data_norm].fps_max; - - switch (fps) { - case 5: - mask = 0x003; - break; - case 10: - mask = 0x0c3; - break; - case 15: - mask = 0x333; - break; - case 20: - mask = 0x0ff; - break; - case 25: - mask = 0x3ff; - break; - default: - mask = VINO_FRAMERT_FULL; - } - vcs->framert_reg = VINO_FRAMERT_RT(mask) | VINO_FRAMERT_PAL; - break; - } - - vcs->fps = fps; -} - -/* execute with input_lock locked */ -static inline void vino_set_default_framerate(struct - vino_channel_settings *vcs) -{ - vino_set_framerate(vcs, vino_data_norms[vcs->data_norm].fps_max); -} - -/* VINO I2C bus functions */ - -struct i2c_algo_sgi_data { - void *data; /* private data for lowlevel routines */ - unsigned (*getctrl)(void *data); - void (*setctrl)(void *data, unsigned val); - unsigned (*rdata)(void *data); - void (*wdata)(void *data, unsigned val); - - int xfer_timeout; - int ack_timeout; -}; - -static int wait_xfer_done(struct i2c_algo_sgi_data *adap) -{ - int i; - - for (i = 0; i < adap->xfer_timeout; i++) { - if ((adap->getctrl(adap->data) & SGI_I2C_XFER_BUSY) == 0) - return 0; - udelay(1); - } - - return -ETIMEDOUT; -} - -static int wait_ack(struct i2c_algo_sgi_data *adap) -{ - int i; - - if (wait_xfer_done(adap)) - return -ETIMEDOUT; - for (i = 0; i < adap->ack_timeout; i++) { - if ((adap->getctrl(adap->data) & SGI_I2C_NACK) == 0) - return 0; - udelay(1); - } - - return -ETIMEDOUT; -} - -static int force_idle(struct i2c_algo_sgi_data *adap) -{ - int i; - - adap->setctrl(adap->data, SGI_I2C_FORCE_IDLE); - for (i = 0; i < adap->xfer_timeout; i++) { - if ((adap->getctrl(adap->data) & SGI_I2C_NOT_IDLE) == 0) - goto out; - udelay(1); - } - return -ETIMEDOUT; -out: - if (adap->getctrl(adap->data) & SGI_I2C_BUS_ERR) - return -EIO; - return 0; -} - -static int do_address(struct i2c_algo_sgi_data *adap, unsigned int addr, - int rd) -{ - if (rd) - adap->setctrl(adap->data, SGI_I2C_NOT_IDLE); - /* Check if bus is idle, eventually force it to do so */ - if (adap->getctrl(adap->data) & SGI_I2C_NOT_IDLE) - if (force_idle(adap)) - return -EIO; - /* Write out the i2c chip address and specify operation */ - adap->setctrl(adap->data, - SGI_I2C_HOLD_BUS | SGI_I2C_WRITE | SGI_I2C_NOT_IDLE); - if (rd) - addr |= 1; - adap->wdata(adap->data, addr); - if (wait_ack(adap)) - return -EIO; - return 0; -} - -static int i2c_read(struct i2c_algo_sgi_data *adap, unsigned char *buf, - unsigned int len) -{ - int i; - - adap->setctrl(adap->data, - SGI_I2C_HOLD_BUS | SGI_I2C_READ | SGI_I2C_NOT_IDLE); - for (i = 0; i < len; i++) { - if (wait_xfer_done(adap)) - return -EIO; - buf[i] = adap->rdata(adap->data); - } - adap->setctrl(adap->data, SGI_I2C_RELEASE_BUS | SGI_I2C_FORCE_IDLE); - - return 0; - -} - -static int i2c_write(struct i2c_algo_sgi_data *adap, unsigned char *buf, - unsigned int len) -{ - int i; - - /* We are already in write state */ - for (i = 0; i < len; i++) { - adap->wdata(adap->data, buf[i]); - if (wait_ack(adap)) - return -EIO; - } - return 0; -} - -static int sgi_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, - int num) -{ - struct i2c_algo_sgi_data *adap = i2c_adap->algo_data; - struct i2c_msg *p; - int i, err = 0; - - for (i = 0; !err && i < num; i++) { - p = &msgs[i]; - err = do_address(adap, p->addr, p->flags & I2C_M_RD); - if (err || !p->len) - continue; - if (p->flags & I2C_M_RD) - err = i2c_read(adap, p->buf, p->len); - else - err = i2c_write(adap, p->buf, p->len); - } - - return (err < 0) ? err : i; -} - -static u32 sgi_func(struct i2c_adapter *adap) -{ - return I2C_FUNC_SMBUS_EMUL; -} - -static const struct i2c_algorithm sgi_algo = { - .master_xfer = sgi_xfer, - .functionality = sgi_func, -}; - -static unsigned i2c_vino_getctrl(void *data) -{ - return vino->i2c_control; -} - -static void i2c_vino_setctrl(void *data, unsigned val) -{ - vino->i2c_control = val; -} - -static unsigned i2c_vino_rdata(void *data) -{ - return vino->i2c_data; -} - -static void i2c_vino_wdata(void *data, unsigned val) -{ - vino->i2c_data = val; -} - -static struct i2c_algo_sgi_data i2c_sgi_vino_data = { - .getctrl = &i2c_vino_getctrl, - .setctrl = &i2c_vino_setctrl, - .rdata = &i2c_vino_rdata, - .wdata = &i2c_vino_wdata, - .xfer_timeout = 200, - .ack_timeout = 1000, -}; - -static struct i2c_adapter vino_i2c_adapter = { - .name = "VINO I2C bus", - .algo = &sgi_algo, - .algo_data = &i2c_sgi_vino_data, - .owner = THIS_MODULE, -}; - -/* - * Prepare VINO for DMA transfer... - * (execute only with vino_lock and input_lock locked) - */ -static int vino_dma_setup(struct vino_channel_settings *vcs, - struct vino_framebuffer *fb) -{ - u32 ctrl, intr; - struct sgi_vino_channel *ch; - const struct vino_data_norm *norm; - - dprintk("vino_dma_setup():\n"); - - vcs->field = 0; - fb->frame_counter = 0; - - ch = (vcs->channel == VINO_CHANNEL_A) ? &vino->a : &vino->b; - norm = &vino_data_norms[vcs->data_norm]; - - ch->page_index = 0; - ch->line_count = 0; - - /* VINO line size register is set 8 bytes less than actual */ - ch->line_size = vcs->line_size - 8; - - /* let VINO know where to transfer data */ - ch->start_desc_tbl = fb->desc_table.dma; - ch->next_4_desc = fb->desc_table.dma; - - /* give vino time to fetch the first four descriptors, 5 usec - * should be more than enough time */ - udelay(VINO_DESC_FETCH_DELAY); - - dprintk("vino_dma_setup(): start desc = %08x, next 4 desc = %08x\n", - ch->start_desc_tbl, ch->next_4_desc); - - /* set the alpha register */ - ch->alpha = vcs->alpha; - - /* set clipping registers */ - ch->clip_start = VINO_CLIP_ODD(norm->odd.top + vcs->clipping.top / 2) | - VINO_CLIP_EVEN(norm->even.top + - vcs->clipping.top / 2) | - VINO_CLIP_X(vcs->clipping.left); - ch->clip_end = VINO_CLIP_ODD(norm->odd.top + - vcs->clipping.bottom / 2 - 1) | - VINO_CLIP_EVEN(norm->even.top + - vcs->clipping.bottom / 2 - 1) | - VINO_CLIP_X(vcs->clipping.right); - - /* set the size of actual content in the buffer (DECIMATION !) */ - fb->data_size = ((vcs->clipping.right - vcs->clipping.left) / - vcs->decimation) * - ((vcs->clipping.bottom - vcs->clipping.top) / - vcs->decimation) * - vino_data_formats[vcs->data_format].bpp; - - ch->frame_rate = vcs->framert_reg; - - ctrl = vino->control; - intr = vino->intr_status; - - if (vcs->channel == VINO_CHANNEL_A) { - /* All interrupt conditions for this channel was cleared - * so clear the interrupt status register and enable - * interrupts */ - intr &= ~VINO_INTSTAT_A; - ctrl |= VINO_CTRL_A_INT; - - /* enable synchronization */ - ctrl |= VINO_CTRL_A_SYNC_ENBL; - - /* enable frame assembly */ - ctrl |= VINO_CTRL_A_INTERLEAVE_ENBL; - - /* set decimation used */ - if (vcs->decimation < 2) - ctrl &= ~VINO_CTRL_A_DEC_ENBL; - else { - ctrl |= VINO_CTRL_A_DEC_ENBL; - ctrl &= ~VINO_CTRL_A_DEC_SCALE_MASK; - ctrl |= (vcs->decimation - 1) << - VINO_CTRL_A_DEC_SCALE_SHIFT; - } - - /* select input interface */ - if (vcs->input == VINO_INPUT_D1) - ctrl |= VINO_CTRL_A_SELECT; - else - ctrl &= ~VINO_CTRL_A_SELECT; - - /* palette */ - ctrl &= ~(VINO_CTRL_A_LUMA_ONLY | VINO_CTRL_A_RGB | - VINO_CTRL_A_DITHER); - } else { - intr &= ~VINO_INTSTAT_B; - ctrl |= VINO_CTRL_B_INT; - - ctrl |= VINO_CTRL_B_SYNC_ENBL; - ctrl |= VINO_CTRL_B_INTERLEAVE_ENBL; - - if (vcs->decimation < 2) - ctrl &= ~VINO_CTRL_B_DEC_ENBL; - else { - ctrl |= VINO_CTRL_B_DEC_ENBL; - ctrl &= ~VINO_CTRL_B_DEC_SCALE_MASK; - ctrl |= (vcs->decimation - 1) << - VINO_CTRL_B_DEC_SCALE_SHIFT; - - } - if (vcs->input == VINO_INPUT_D1) - ctrl |= VINO_CTRL_B_SELECT; - else - ctrl &= ~VINO_CTRL_B_SELECT; - - ctrl &= ~(VINO_CTRL_B_LUMA_ONLY | VINO_CTRL_B_RGB | - VINO_CTRL_B_DITHER); - } - - /* set palette */ - fb->data_format = vcs->data_format; - - switch (vcs->data_format) { - case VINO_DATA_FMT_GREY: - ctrl |= (vcs->channel == VINO_CHANNEL_A) ? - VINO_CTRL_A_LUMA_ONLY : VINO_CTRL_B_LUMA_ONLY; - break; - case VINO_DATA_FMT_RGB32: - ctrl |= (vcs->channel == VINO_CHANNEL_A) ? - VINO_CTRL_A_RGB : VINO_CTRL_B_RGB; - break; - case VINO_DATA_FMT_YUV: - /* nothing needs to be done */ - break; - case VINO_DATA_FMT_RGB332: - ctrl |= (vcs->channel == VINO_CHANNEL_A) ? - VINO_CTRL_A_RGB | VINO_CTRL_A_DITHER : - VINO_CTRL_B_RGB | VINO_CTRL_B_DITHER; - break; - } - - vino->intr_status = intr; - vino->control = ctrl; - - return 0; -} - -/* (execute only with vino_lock locked) */ -static inline void vino_dma_start(struct vino_channel_settings *vcs) -{ - u32 ctrl = vino->control; - - dprintk("vino_dma_start():\n"); - ctrl |= (vcs->channel == VINO_CHANNEL_A) ? - VINO_CTRL_A_DMA_ENBL : VINO_CTRL_B_DMA_ENBL; - vino->control = ctrl; -} - -/* (execute only with vino_lock locked) */ -static inline void vino_dma_stop(struct vino_channel_settings *vcs) -{ - u32 ctrl = vino->control; - - ctrl &= (vcs->channel == VINO_CHANNEL_A) ? - ~VINO_CTRL_A_DMA_ENBL : ~VINO_CTRL_B_DMA_ENBL; - ctrl &= (vcs->channel == VINO_CHANNEL_A) ? - ~VINO_CTRL_A_INT : ~VINO_CTRL_B_INT; - vino->control = ctrl; - dprintk("vino_dma_stop():\n"); -} - -/* - * Load dummy page to descriptor registers. This prevents generating of - * spurious interrupts. (execute only with vino_lock locked) - */ -static void vino_clear_interrupt(struct vino_channel_settings *vcs) -{ - struct sgi_vino_channel *ch; - - ch = (vcs->channel == VINO_CHANNEL_A) ? &vino->a : &vino->b; - - ch->page_index = 0; - ch->line_count = 0; - - ch->start_desc_tbl = vino_drvdata->dummy_desc_table.dma; - ch->next_4_desc = vino_drvdata->dummy_desc_table.dma; - - udelay(VINO_DESC_FETCH_DELAY); - dprintk("channel %c clear interrupt condition\n", - (vcs->channel == VINO_CHANNEL_A) ? 'A':'B'); -} - -static int vino_capture(struct vino_channel_settings *vcs, - struct vino_framebuffer *fb) -{ - int err = 0; - unsigned long flags, flags2; - - spin_lock_irqsave(&fb->state_lock, flags); - - if (fb->state == VINO_FRAMEBUFFER_IN_USE) - err = -EBUSY; - fb->state = VINO_FRAMEBUFFER_IN_USE; - - spin_unlock_irqrestore(&fb->state_lock, flags); - - if (err) - return err; - - spin_lock_irqsave(&vino_drvdata->vino_lock, flags); - spin_lock_irqsave(&vino_drvdata->input_lock, flags2); - - vino_dma_setup(vcs, fb); - vino_dma_start(vcs); - - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags2); - spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags); - - return err; -} - -static -struct vino_framebuffer *vino_capture_enqueue(struct - vino_channel_settings *vcs, - unsigned int index) -{ - struct vino_framebuffer *fb; - unsigned long flags; - - dprintk("vino_capture_enqueue():\n"); - - spin_lock_irqsave(&vcs->capture_lock, flags); - - fb = vino_queue_add(&vcs->fb_queue, index); - if (fb == NULL) { - dprintk("vino_capture_enqueue(): vino_queue_add() failed, " - "queue full?\n"); - goto out; - } -out: - spin_unlock_irqrestore(&vcs->capture_lock, flags); - - return fb; -} - -static int vino_capture_next(struct vino_channel_settings *vcs, int start) -{ - struct vino_framebuffer *fb; - unsigned int incoming, id; - int err = 0; - unsigned long flags; - - dprintk("vino_capture_next():\n"); - - spin_lock_irqsave(&vcs->capture_lock, flags); - - if (start) { - /* start capture only if capture isn't in progress already */ - if (vcs->capturing) { - spin_unlock_irqrestore(&vcs->capture_lock, flags); - return 0; - } - - } else { - /* capture next frame: - * stop capture if capturing is not set */ - if (!vcs->capturing) { - spin_unlock_irqrestore(&vcs->capture_lock, flags); - return 0; - } - } - - err = vino_queue_get_incoming(&vcs->fb_queue, &incoming); - if (err) { - dprintk("vino_capture_next(): vino_queue_get_incoming() " - "failed\n"); - err = -EINVAL; - goto out; - } - if (incoming == 0) { - dprintk("vino_capture_next(): no buffers available\n"); - goto out; - } - - fb = vino_queue_peek(&vcs->fb_queue, &id); - if (fb == NULL) { - dprintk("vino_capture_next(): vino_queue_peek() failed\n"); - err = -EINVAL; - goto out; - } - - if (start) { - vcs->capturing = 1; - } - - spin_unlock_irqrestore(&vcs->capture_lock, flags); - - err = vino_capture(vcs, fb); - - return err; - -out: - vcs->capturing = 0; - spin_unlock_irqrestore(&vcs->capture_lock, flags); - - return err; -} - -static inline int vino_is_capturing(struct vino_channel_settings *vcs) -{ - int ret; - unsigned long flags; - - spin_lock_irqsave(&vcs->capture_lock, flags); - - ret = vcs->capturing; - - spin_unlock_irqrestore(&vcs->capture_lock, flags); - - return ret; -} - -/* waits until a frame is captured */ -static int vino_wait_for_frame(struct vino_channel_settings *vcs) -{ - wait_queue_t wait; - int err = 0; - - dprintk("vino_wait_for_frame():\n"); - - init_waitqueue_entry(&wait, current); - /* add ourselves into wait queue */ - add_wait_queue(&vcs->fb_queue.frame_wait_queue, &wait); - - /* to ensure that schedule_timeout will return immediately - * if VINO interrupt was triggered meanwhile */ - schedule_timeout_interruptible(msecs_to_jiffies(100)); - - if (signal_pending(current)) - err = -EINTR; - - remove_wait_queue(&vcs->fb_queue.frame_wait_queue, &wait); - - dprintk("vino_wait_for_frame(): waiting for frame %s\n", - err ? "failed" : "ok"); - - return err; -} - -/* the function assumes that PAGE_SIZE % 4 == 0 */ -static void vino_convert_to_rgba(struct vino_framebuffer *fb) { - unsigned char *pageptr; - unsigned int page, i; - unsigned char a; - - for (page = 0; page < fb->desc_table.page_count; page++) { - pageptr = (unsigned char *)fb->desc_table.virtual[page]; - - for (i = 0; i < PAGE_SIZE; i += 4) { - a = pageptr[0]; - pageptr[0] = pageptr[3]; - pageptr[1] = pageptr[2]; - pageptr[2] = pageptr[1]; - pageptr[3] = a; - pageptr += 4; - } - } -} - -/* checks if the buffer is in correct state and syncs data */ -static int vino_check_buffer(struct vino_channel_settings *vcs, - struct vino_framebuffer *fb) -{ - int err = 0; - unsigned long flags; - - dprintk("vino_check_buffer():\n"); - - spin_lock_irqsave(&fb->state_lock, flags); - switch (fb->state) { - case VINO_FRAMEBUFFER_IN_USE: - err = -EIO; - break; - case VINO_FRAMEBUFFER_READY: - vino_sync_buffer(fb); - fb->state = VINO_FRAMEBUFFER_UNUSED; - break; - default: - err = -EINVAL; - } - spin_unlock_irqrestore(&fb->state_lock, flags); - - if (!err) { - if (vino_pixel_conversion - && (fb->data_format == VINO_DATA_FMT_RGB32)) { - vino_convert_to_rgba(fb); - } - } else if (err && (err != -EINVAL)) { - dprintk("vino_check_buffer(): buffer not ready\n"); - - spin_lock_irqsave(&vino_drvdata->vino_lock, flags); - vino_dma_stop(vcs); - vino_clear_interrupt(vcs); - spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags); - } - - return err; -} - -/* forcefully terminates capture */ -static void vino_capture_stop(struct vino_channel_settings *vcs) -{ - unsigned int incoming = 0, outgoing = 0, id; - unsigned long flags, flags2; - - dprintk("vino_capture_stop():\n"); - - spin_lock_irqsave(&vcs->capture_lock, flags); - - /* unset capturing to stop queue processing */ - vcs->capturing = 0; - - spin_lock_irqsave(&vino_drvdata->vino_lock, flags2); - - vino_dma_stop(vcs); - vino_clear_interrupt(vcs); - - spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags2); - - /* remove all items from the queue */ - if (vino_queue_get_incoming(&vcs->fb_queue, &incoming)) { - dprintk("vino_capture_stop(): " - "vino_queue_get_incoming() failed\n"); - goto out; - } - while (incoming > 0) { - vino_queue_transfer(&vcs->fb_queue); - - if (vino_queue_get_incoming(&vcs->fb_queue, &incoming)) { - dprintk("vino_capture_stop(): " - "vino_queue_get_incoming() failed\n"); - goto out; - } - } - - if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) { - dprintk("vino_capture_stop(): " - "vino_queue_get_outgoing() failed\n"); - goto out; - } - while (outgoing > 0) { - vino_queue_remove(&vcs->fb_queue, &id); - - if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) { - dprintk("vino_capture_stop(): " - "vino_queue_get_outgoing() failed\n"); - goto out; - } - } - -out: - spin_unlock_irqrestore(&vcs->capture_lock, flags); -} - -#if 0 -static int vino_capture_failed(struct vino_channel_settings *vcs) -{ - struct vino_framebuffer *fb; - unsigned long flags; - unsigned int i; - int ret; - - dprintk("vino_capture_failed():\n"); - - spin_lock_irqsave(&vino_drvdata->vino_lock, flags); - - vino_dma_stop(vcs); - vino_clear_interrupt(vcs); - - spin_unlock_irqrestore(&vino_drvdata->vino_lock, flags); - - ret = vino_queue_get_incoming(&vcs->fb_queue, &i); - if (ret == VINO_QUEUE_ERROR) { - dprintk("vino_queue_get_incoming() failed\n"); - return -EINVAL; - } - if (i == 0) { - /* no buffers to process */ - return 0; - } - - fb = vino_queue_peek(&vcs->fb_queue, &i); - if (fb == NULL) { - dprintk("vino_queue_peek() failed\n"); - return -EINVAL; - } - - spin_lock_irqsave(&fb->state_lock, flags); - if (fb->state == VINO_FRAMEBUFFER_IN_USE) { - fb->state = VINO_FRAMEBUFFER_UNUSED; - vino_queue_transfer(&vcs->fb_queue); - vino_queue_remove(&vcs->fb_queue, &i); - /* we should actually discard the newest frame, - * but who cares ... */ - } - spin_unlock_irqrestore(&fb->state_lock, flags); - - return 0; -} -#endif - -static void vino_skip_frame(struct vino_channel_settings *vcs) -{ - struct vino_framebuffer *fb; - unsigned long flags; - unsigned int id; - - spin_lock_irqsave(&vcs->capture_lock, flags); - fb = vino_queue_peek(&vcs->fb_queue, &id); - if (!fb) { - spin_unlock_irqrestore(&vcs->capture_lock, flags); - dprintk("vino_skip_frame(): vino_queue_peek() failed!\n"); - return; - } - spin_unlock_irqrestore(&vcs->capture_lock, flags); - - spin_lock_irqsave(&fb->state_lock, flags); - fb->state = VINO_FRAMEBUFFER_UNUSED; - spin_unlock_irqrestore(&fb->state_lock, flags); - - vino_capture_next(vcs, 0); -} - -static void vino_frame_done(struct vino_channel_settings *vcs) -{ - struct vino_framebuffer *fb; - unsigned long flags; - - spin_lock_irqsave(&vcs->capture_lock, flags); - fb = vino_queue_transfer(&vcs->fb_queue); - if (!fb) { - spin_unlock_irqrestore(&vcs->capture_lock, flags); - dprintk("vino_frame_done(): vino_queue_transfer() failed!\n"); - return; - } - spin_unlock_irqrestore(&vcs->capture_lock, flags); - - fb->frame_counter = vcs->int_data.frame_counter; - memcpy(&fb->timestamp, &vcs->int_data.timestamp, - sizeof(struct timeval)); - - spin_lock_irqsave(&fb->state_lock, flags); - if (fb->state == VINO_FRAMEBUFFER_IN_USE) - fb->state = VINO_FRAMEBUFFER_READY; - spin_unlock_irqrestore(&fb->state_lock, flags); - - wake_up(&vcs->fb_queue.frame_wait_queue); - - vino_capture_next(vcs, 0); -} - -static void vino_capture_tasklet(unsigned long channel) { - struct vino_channel_settings *vcs; - - vcs = (channel == VINO_CHANNEL_A) - ? &vino_drvdata->a : &vino_drvdata->b; - - if (vcs->int_data.skip) - vcs->int_data.skip_count++; - - if (vcs->int_data.skip && (vcs->int_data.skip_count - <= VINO_MAX_FRAME_SKIP_COUNT)) { - vino_skip_frame(vcs); - } else { - vcs->int_data.skip_count = 0; - vino_frame_done(vcs); - } -} - -static irqreturn_t vino_interrupt(int irq, void *dev_id) -{ - u32 ctrl, intr; - unsigned int fc_a, fc_b; - int handled_a = 0, skip_a = 0, done_a = 0; - int handled_b = 0, skip_b = 0, done_b = 0; - -#ifdef VINO_DEBUG_INT - int loop = 0; - unsigned int line_count = vino->a.line_count, - page_index = vino->a.page_index, - field_counter = vino->a.field_counter, - start_desc_tbl = vino->a.start_desc_tbl, - next_4_desc = vino->a.next_4_desc; - unsigned int line_count_2, - page_index_2, - field_counter_2, - start_desc_tbl_2, - next_4_desc_2; -#endif - - spin_lock(&vino_drvdata->vino_lock); - - while ((intr = vino->intr_status)) { - fc_a = vino->a.field_counter >> 1; - fc_b = vino->b.field_counter >> 1; - - /* handle error-interrupts in some special way ? - * --> skips frames */ - if (intr & VINO_INTSTAT_A) { - if (intr & VINO_INTSTAT_A_EOF) { - vino_drvdata->a.field++; - if (vino_drvdata->a.field > 1) { - vino_dma_stop(&vino_drvdata->a); - vino_clear_interrupt(&vino_drvdata->a); - vino_drvdata->a.field = 0; - done_a = 1; - } else { - if (vino->a.page_index - != vino_drvdata->a.line_size) { - vino->a.line_count = 0; - vino->a.page_index = - vino_drvdata-> - a.line_size; - vino->a.next_4_desc = - vino->a.start_desc_tbl; - } - } - dprintk("channel A end-of-field " - "interrupt: %04x\n", intr); - } else { - vino_dma_stop(&vino_drvdata->a); - vino_clear_interrupt(&vino_drvdata->a); - vino_drvdata->a.field = 0; - skip_a = 1; - dprintk("channel A error interrupt: %04x\n", - intr); - } - -#ifdef VINO_DEBUG_INT - line_count_2 = vino->a.line_count; - page_index_2 = vino->a.page_index; - field_counter_2 = vino->a.field_counter; - start_desc_tbl_2 = vino->a.start_desc_tbl; - next_4_desc_2 = vino->a.next_4_desc; - - printk("intr = %04x, loop = %d, field = %d\n", - intr, loop, vino_drvdata->a.field); - printk("1- line count = %04d, page index = %04d, " - "start = %08x, next = %08x\n" - " fieldc = %d, framec = %d\n", - line_count, page_index, start_desc_tbl, - next_4_desc, field_counter, fc_a); - printk("12-line count = %04d, page index = %04d, " - " start = %08x, next = %08x\n", - line_count_2, page_index_2, start_desc_tbl_2, - next_4_desc_2); - - if (done_a) - printk("\n"); -#endif - } - - if (intr & VINO_INTSTAT_B) { - if (intr & VINO_INTSTAT_B_EOF) { - vino_drvdata->b.field++; - if (vino_drvdata->b.field > 1) { - vino_dma_stop(&vino_drvdata->b); - vino_clear_interrupt(&vino_drvdata->b); - vino_drvdata->b.field = 0; - done_b = 1; - } - dprintk("channel B end-of-field " - "interrupt: %04x\n", intr); - } else { - vino_dma_stop(&vino_drvdata->b); - vino_clear_interrupt(&vino_drvdata->b); - vino_drvdata->b.field = 0; - skip_b = 1; - dprintk("channel B error interrupt: %04x\n", - intr); - } - } - - /* Always remember to clear interrupt status. - * Disable VINO interrupts while we do this. */ - ctrl = vino->control; - vino->control = ctrl & ~(VINO_CTRL_A_INT | VINO_CTRL_B_INT); - vino->intr_status = ~intr; - vino->control = ctrl; - - spin_unlock(&vino_drvdata->vino_lock); - - if ((!handled_a) && (done_a || skip_a)) { - if (!skip_a) { - v4l2_get_timestamp( - &vino_drvdata->a.int_data.timestamp); - vino_drvdata->a.int_data.frame_counter = fc_a; - } - vino_drvdata->a.int_data.skip = skip_a; - - dprintk("channel A %s, interrupt: %d\n", - skip_a ? "skipping frame" : "frame done", - intr); - tasklet_hi_schedule(&vino_tasklet_a); - handled_a = 1; - } - - if ((!handled_b) && (done_b || skip_b)) { - if (!skip_b) { - v4l2_get_timestamp( - &vino_drvdata->b.int_data.timestamp); - vino_drvdata->b.int_data.frame_counter = fc_b; - } - vino_drvdata->b.int_data.skip = skip_b; - - dprintk("channel B %s, interrupt: %d\n", - skip_b ? "skipping frame" : "frame done", - intr); - tasklet_hi_schedule(&vino_tasklet_b); - handled_b = 1; - } - -#ifdef VINO_DEBUG_INT - loop++; -#endif - spin_lock(&vino_drvdata->vino_lock); - } - - spin_unlock(&vino_drvdata->vino_lock); - - return IRQ_HANDLED; -} - -/* VINO video input management */ - -static int vino_get_saa7191_input(int input) -{ - switch (input) { - case VINO_INPUT_COMPOSITE: - return SAA7191_INPUT_COMPOSITE; - case VINO_INPUT_SVIDEO: - return SAA7191_INPUT_SVIDEO; - default: - printk(KERN_ERR "VINO: vino_get_saa7191_input(): " - "invalid input!\n"); - return -1; - } -} - -/* execute with input_lock locked */ -static int vino_is_input_owner(struct vino_channel_settings *vcs) -{ - switch(vcs->input) { - case VINO_INPUT_COMPOSITE: - case VINO_INPUT_SVIDEO: - return vino_drvdata->decoder_owner == vcs->channel; - case VINO_INPUT_D1: - return vino_drvdata->camera_owner == vcs->channel; - default: - return 0; - } -} - -static int vino_acquire_input(struct vino_channel_settings *vcs) -{ - unsigned long flags; - int ret = 0; - - dprintk("vino_acquire_input():\n"); - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - /* First try D1 and then SAA7191 */ - if (vino_drvdata->camera - && (vino_drvdata->camera_owner == VINO_NO_CHANNEL)) { - vino_drvdata->camera_owner = vcs->channel; - vcs->input = VINO_INPUT_D1; - vcs->data_norm = VINO_DATA_NORM_D1; - } else if (vino_drvdata->decoder - && (vino_drvdata->decoder_owner == VINO_NO_CHANNEL)) { - int input; - int data_norm = 0; - v4l2_std_id norm; - - input = VINO_INPUT_COMPOSITE; - - ret = decoder_call(video, s_routing, - vino_get_saa7191_input(input), 0, 0); - if (ret) { - ret = -EINVAL; - goto out; - } - - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - /* Don't hold spinlocks while auto-detecting norm - * as it may take a while... */ - - ret = decoder_call(video, querystd, &norm); - if (!ret) { - for (data_norm = 0; data_norm < 3; data_norm++) { - if (vino_data_norms[data_norm].std & norm) - break; - } - if (data_norm == 3) - data_norm = VINO_DATA_NORM_PAL; - ret = decoder_call(video, s_std, norm); - } - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - if (ret) { - ret = -EINVAL; - goto out; - } - - vino_drvdata->decoder_owner = vcs->channel; - - vcs->input = input; - vcs->data_norm = data_norm; - } else { - vcs->input = (vcs->channel == VINO_CHANNEL_A) ? - vino_drvdata->b.input : vino_drvdata->a.input; - vcs->data_norm = (vcs->channel == VINO_CHANNEL_A) ? - vino_drvdata->b.data_norm : vino_drvdata->a.data_norm; - } - - if (vcs->input == VINO_INPUT_NONE) { - ret = -ENODEV; - goto out; - } - - vino_set_default_clipping(vcs); - vino_set_default_scaling(vcs); - vino_set_default_framerate(vcs); - - dprintk("vino_acquire_input(): %s\n", vino_inputs[vcs->input].name); - -out: - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - return ret; -} - -static int vino_set_input(struct vino_channel_settings *vcs, int input) -{ - struct vino_channel_settings *vcs2 = (vcs->channel == VINO_CHANNEL_A) ? - &vino_drvdata->b : &vino_drvdata->a; - unsigned long flags; - int ret = 0; - - dprintk("vino_set_input():\n"); - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - if (vcs->input == input) - goto out; - - switch (input) { - case VINO_INPUT_COMPOSITE: - case VINO_INPUT_SVIDEO: - if (!vino_drvdata->decoder) { - ret = -EINVAL; - goto out; - } - - if (vino_drvdata->decoder_owner == VINO_NO_CHANNEL) { - vino_drvdata->decoder_owner = vcs->channel; - } - - if (vino_drvdata->decoder_owner == vcs->channel) { - int data_norm = 0; - v4l2_std_id norm; - - ret = decoder_call(video, s_routing, - vino_get_saa7191_input(input), 0, 0); - if (ret) { - vino_drvdata->decoder_owner = VINO_NO_CHANNEL; - ret = -EINVAL; - goto out; - } - - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - /* Don't hold spinlocks while auto-detecting norm - * as it may take a while... */ - - ret = decoder_call(video, querystd, &norm); - if (!ret) { - for (data_norm = 0; data_norm < 3; data_norm++) { - if (vino_data_norms[data_norm].std & norm) - break; - } - if (data_norm == 3) - data_norm = VINO_DATA_NORM_PAL; - ret = decoder_call(video, s_std, norm); - } - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - if (ret) { - vino_drvdata->decoder_owner = VINO_NO_CHANNEL; - ret = -EINVAL; - goto out; - } - - vcs->input = input; - vcs->data_norm = data_norm; - } else { - if (input != vcs2->input) { - ret = -EBUSY; - goto out; - } - - vcs->input = input; - vcs->data_norm = vcs2->data_norm; - } - - if (vino_drvdata->camera_owner == vcs->channel) { - /* Transfer the ownership or release the input */ - if (vcs2->input == VINO_INPUT_D1) { - vino_drvdata->camera_owner = vcs2->channel; - } else { - vino_drvdata->camera_owner = VINO_NO_CHANNEL; - } - } - break; - case VINO_INPUT_D1: - if (!vino_drvdata->camera) { - ret = -EINVAL; - goto out; - } - - if (vino_drvdata->camera_owner == VINO_NO_CHANNEL) - vino_drvdata->camera_owner = vcs->channel; - - if (vino_drvdata->decoder_owner == vcs->channel) { - /* Transfer the ownership or release the input */ - if ((vcs2->input == VINO_INPUT_COMPOSITE) || - (vcs2->input == VINO_INPUT_SVIDEO)) { - vino_drvdata->decoder_owner = vcs2->channel; - } else { - vino_drvdata->decoder_owner = VINO_NO_CHANNEL; - } - } - - vcs->input = input; - vcs->data_norm = VINO_DATA_NORM_D1; - break; - default: - ret = -EINVAL; - goto out; - } - - vino_set_default_clipping(vcs); - vino_set_default_scaling(vcs); - vino_set_default_framerate(vcs); - - dprintk("vino_set_input(): %s\n", vino_inputs[vcs->input].name); - -out: - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - return ret; -} - -static void vino_release_input(struct vino_channel_settings *vcs) -{ - struct vino_channel_settings *vcs2 = (vcs->channel == VINO_CHANNEL_A) ? - &vino_drvdata->b : &vino_drvdata->a; - unsigned long flags; - - dprintk("vino_release_input():\n"); - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - /* Release ownership of the channel - * and if the other channel takes input from - * the same source, transfer the ownership */ - if (vino_drvdata->camera_owner == vcs->channel) { - if (vcs2->input == VINO_INPUT_D1) { - vino_drvdata->camera_owner = vcs2->channel; - } else { - vino_drvdata->camera_owner = VINO_NO_CHANNEL; - } - } else if (vino_drvdata->decoder_owner == vcs->channel) { - if ((vcs2->input == VINO_INPUT_COMPOSITE) || - (vcs2->input == VINO_INPUT_SVIDEO)) { - vino_drvdata->decoder_owner = vcs2->channel; - } else { - vino_drvdata->decoder_owner = VINO_NO_CHANNEL; - } - } - vcs->input = VINO_INPUT_NONE; - - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); -} - -/* execute with input_lock locked */ -static int vino_set_data_norm(struct vino_channel_settings *vcs, - unsigned int data_norm, - unsigned long *flags) -{ - int err = 0; - - if (data_norm == vcs->data_norm) - return 0; - - switch (vcs->input) { - case VINO_INPUT_D1: - /* only one "norm" supported */ - if (data_norm != VINO_DATA_NORM_D1) - return -EINVAL; - break; - case VINO_INPUT_COMPOSITE: - case VINO_INPUT_SVIDEO: { - v4l2_std_id norm; - - if ((data_norm != VINO_DATA_NORM_PAL) - && (data_norm != VINO_DATA_NORM_NTSC) - && (data_norm != VINO_DATA_NORM_SECAM)) - return -EINVAL; - - spin_unlock_irqrestore(&vino_drvdata->input_lock, *flags); - - /* Don't hold spinlocks while setting norm - * as it may take a while... */ - - norm = vino_data_norms[data_norm].std; - err = decoder_call(video, s_std, norm); - - spin_lock_irqsave(&vino_drvdata->input_lock, *flags); - - if (err) - goto out; - - vcs->data_norm = data_norm; - - vino_set_default_clipping(vcs); - vino_set_default_scaling(vcs); - vino_set_default_framerate(vcs); - break; - } - default: - return -EINVAL; - } - -out: - return err; -} - -/* V4L2 helper functions */ - -static int vino_find_data_format(__u32 pixelformat) -{ - int i; - - for (i = 0; i < VINO_DATA_FMT_COUNT; i++) { - if (vino_data_formats[i].pixelformat == pixelformat) - return i; - } - - return VINO_DATA_FMT_NONE; -} - -static int vino_int_enum_input(struct vino_channel_settings *vcs, __u32 index) -{ - int input = VINO_INPUT_NONE; - unsigned long flags; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - if (vino_drvdata->decoder && vino_drvdata->camera) { - switch (index) { - case 0: - input = VINO_INPUT_COMPOSITE; - break; - case 1: - input = VINO_INPUT_SVIDEO; - break; - case 2: - input = VINO_INPUT_D1; - break; - } - } else if (vino_drvdata->decoder) { - switch (index) { - case 0: - input = VINO_INPUT_COMPOSITE; - break; - case 1: - input = VINO_INPUT_SVIDEO; - break; - } - } else if (vino_drvdata->camera) { - switch (index) { - case 0: - input = VINO_INPUT_D1; - break; - } - } - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - return input; -} - -/* execute with input_lock locked */ -static __u32 vino_find_input_index(struct vino_channel_settings *vcs) -{ - __u32 index = 0; - // FIXME: detect when no inputs available - - if (vino_drvdata->decoder && vino_drvdata->camera) { - switch (vcs->input) { - case VINO_INPUT_COMPOSITE: - index = 0; - break; - case VINO_INPUT_SVIDEO: - index = 1; - break; - case VINO_INPUT_D1: - index = 2; - break; - } - } else if (vino_drvdata->decoder) { - switch (vcs->input) { - case VINO_INPUT_COMPOSITE: - index = 0; - break; - case VINO_INPUT_SVIDEO: - index = 1; - break; - } - } else if (vino_drvdata->camera) { - switch (vcs->input) { - case VINO_INPUT_D1: - index = 0; - break; - } - } - - return index; -} - -/* V4L2 ioctls */ - -static int vino_querycap(struct file *file, void *__fh, - struct v4l2_capability *cap) -{ - memset(cap, 0, sizeof(struct v4l2_capability)); - - strcpy(cap->driver, vino_driver_name); - strcpy(cap->card, vino_driver_description); - strcpy(cap->bus_info, vino_bus_name); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - return 0; -} - -static int vino_enum_input(struct file *file, void *__fh, - struct v4l2_input *i) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - __u32 index = i->index; - int input; - dprintk("requested index = %d\n", index); - - input = vino_int_enum_input(vcs, index); - if (input == VINO_INPUT_NONE) - return -EINVAL; - - i->type = V4L2_INPUT_TYPE_CAMERA; - i->std = vino_inputs[input].std; - strcpy(i->name, vino_inputs[input].name); - - if (input == VINO_INPUT_COMPOSITE || input == VINO_INPUT_SVIDEO) - decoder_call(video, g_input_status, &i->status); - return 0; -} - -static int vino_g_input(struct file *file, void *__fh, - unsigned int *i) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - __u32 index; - int input; - unsigned long flags; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - input = vcs->input; - index = vino_find_input_index(vcs); - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - dprintk("input = %d\n", input); - - if (input == VINO_INPUT_NONE) { - return -EINVAL; - } - - *i = index; - - return 0; -} - -static int vino_s_input(struct file *file, void *__fh, - unsigned int i) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - int input; - dprintk("requested input = %d\n", i); - - input = vino_int_enum_input(vcs, i); - if (input == VINO_INPUT_NONE) - return -EINVAL; - - return vino_set_input(vcs, input); -} - -static int vino_querystd(struct file *file, void *__fh, - v4l2_std_id *std) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - unsigned long flags; - int err = 0; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - switch (vcs->input) { - case VINO_INPUT_D1: - *std = vino_inputs[vcs->input].std; - break; - case VINO_INPUT_COMPOSITE: - case VINO_INPUT_SVIDEO: { - decoder_call(video, querystd, std); - break; - } - default: - err = -EINVAL; - } - - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - return err; -} - -static int vino_g_std(struct file *file, void *__fh, - v4l2_std_id *std) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - unsigned long flags; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - *std = vino_data_norms[vcs->data_norm].std; - dprintk("current standard = %d\n", vcs->data_norm); - - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - return 0; -} - -static int vino_s_std(struct file *file, void *__fh, - v4l2_std_id std) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - unsigned long flags; - int ret = 0; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - if (!vino_is_input_owner(vcs)) { - ret = -EBUSY; - goto out; - } - - /* check if the standard is valid for the current input */ - if (std & vino_inputs[vcs->input].std) { - dprintk("standard accepted\n"); - - /* change the video norm for SAA7191 - * and accept NTSC for D1 (do nothing) */ - - if (vcs->input == VINO_INPUT_D1) - goto out; - - if (std & V4L2_STD_PAL) { - ret = vino_set_data_norm(vcs, VINO_DATA_NORM_PAL, - &flags); - } else if (std & V4L2_STD_NTSC) { - ret = vino_set_data_norm(vcs, VINO_DATA_NORM_NTSC, - &flags); - } else if (std & V4L2_STD_SECAM) { - ret = vino_set_data_norm(vcs, VINO_DATA_NORM_SECAM, - &flags); - } else { - ret = -EINVAL; - } - - if (ret) { - ret = -EINVAL; - } - } else { - ret = -EINVAL; - } - -out: - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - return ret; -} - -static int vino_enum_fmt_vid_cap(struct file *file, void *__fh, - struct v4l2_fmtdesc *fd) -{ - dprintk("format index = %d\n", fd->index); - - if (fd->index >= VINO_DATA_FMT_COUNT) - return -EINVAL; - dprintk("format name = %s\n", vino_data_formats[fd->index].description); - - fd->pixelformat = vino_data_formats[fd->index].pixelformat; - strcpy(fd->description, vino_data_formats[fd->index].description); - return 0; -} - -static int vino_try_fmt_vid_cap(struct file *file, void *__fh, - struct v4l2_format *f) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - struct vino_channel_settings tempvcs; - unsigned long flags; - struct v4l2_pix_format *pf = &f->fmt.pix; - - dprintk("requested: w = %d, h = %d\n", - pf->width, pf->height); - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - memcpy(&tempvcs, vcs, sizeof(struct vino_channel_settings)); - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - tempvcs.data_format = vino_find_data_format(pf->pixelformat); - if (tempvcs.data_format == VINO_DATA_FMT_NONE) { - tempvcs.data_format = VINO_DATA_FMT_GREY; - pf->pixelformat = - vino_data_formats[tempvcs.data_format]. - pixelformat; - } - - /* data format must be set before clipping/scaling */ - vino_set_scaling(&tempvcs, pf->width, pf->height); - - dprintk("data format = %s\n", - vino_data_formats[tempvcs.data_format].description); - - pf->width = (tempvcs.clipping.right - tempvcs.clipping.left) / - tempvcs.decimation; - pf->height = (tempvcs.clipping.bottom - tempvcs.clipping.top) / - tempvcs.decimation; - - pf->field = V4L2_FIELD_INTERLACED; - pf->bytesperline = tempvcs.line_size; - pf->sizeimage = tempvcs.line_size * - (tempvcs.clipping.bottom - tempvcs.clipping.top) / - tempvcs.decimation; - pf->colorspace = - vino_data_formats[tempvcs.data_format].colorspace; - - return 0; -} - -static int vino_g_fmt_vid_cap(struct file *file, void *__fh, - struct v4l2_format *f) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - unsigned long flags; - struct v4l2_pix_format *pf = &f->fmt.pix; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - pf->width = (vcs->clipping.right - vcs->clipping.left) / - vcs->decimation; - pf->height = (vcs->clipping.bottom - vcs->clipping.top) / - vcs->decimation; - pf->pixelformat = - vino_data_formats[vcs->data_format].pixelformat; - - pf->field = V4L2_FIELD_INTERLACED; - pf->bytesperline = vcs->line_size; - pf->sizeimage = vcs->line_size * - (vcs->clipping.bottom - vcs->clipping.top) / - vcs->decimation; - pf->colorspace = - vino_data_formats[vcs->data_format].colorspace; - - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - return 0; -} - -static int vino_s_fmt_vid_cap(struct file *file, void *__fh, - struct v4l2_format *f) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - int data_format; - unsigned long flags; - struct v4l2_pix_format *pf = &f->fmt.pix; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - data_format = vino_find_data_format(pf->pixelformat); - - if (data_format == VINO_DATA_FMT_NONE) { - vcs->data_format = VINO_DATA_FMT_GREY; - pf->pixelformat = - vino_data_formats[vcs->data_format]. - pixelformat; - } else { - vcs->data_format = data_format; - } - - /* data format must be set before clipping/scaling */ - vino_set_scaling(vcs, pf->width, pf->height); - - dprintk("data format = %s\n", - vino_data_formats[vcs->data_format].description); - - pf->width = vcs->clipping.right - vcs->clipping.left; - pf->height = vcs->clipping.bottom - vcs->clipping.top; - - pf->field = V4L2_FIELD_INTERLACED; - pf->bytesperline = vcs->line_size; - pf->sizeimage = vcs->line_size * - (vcs->clipping.bottom - vcs->clipping.top) / - vcs->decimation; - pf->colorspace = - vino_data_formats[vcs->data_format].colorspace; - - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - return 0; -} - -static int vino_cropcap(struct file *file, void *__fh, - struct v4l2_cropcap *ccap) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - const struct vino_data_norm *norm; - unsigned long flags; - - switch (ccap->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - norm = &vino_data_norms[vcs->data_norm]; - - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - ccap->bounds.left = 0; - ccap->bounds.top = 0; - ccap->bounds.width = norm->width; - ccap->bounds.height = norm->height; - memcpy(&ccap->defrect, &ccap->bounds, - sizeof(struct v4l2_rect)); - - ccap->pixelaspect.numerator = 1; - ccap->pixelaspect.denominator = 1; - break; - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: - return -EINVAL; - } - - return 0; -} - -static int vino_g_crop(struct file *file, void *__fh, - struct v4l2_crop *c) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - unsigned long flags; - - switch (c->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - c->c.left = vcs->clipping.left; - c->c.top = vcs->clipping.top; - c->c.width = vcs->clipping.right - vcs->clipping.left; - c->c.height = vcs->clipping.bottom - vcs->clipping.top; - - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - break; - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: - return -EINVAL; - } - - return 0; -} - -static int vino_s_crop(struct file *file, void *__fh, - const struct v4l2_crop *c) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - unsigned long flags; - - switch (c->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - vino_set_clipping(vcs, c->c.left, c->c.top, - c->c.width, c->c.height); - - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - break; - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: - return -EINVAL; - } - - return 0; -} - -static int vino_g_parm(struct file *file, void *__fh, - struct v4l2_streamparm *sp) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - unsigned long flags; - struct v4l2_captureparm *cp = &sp->parm.capture; - - cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->timeperframe.numerator = 1; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - cp->timeperframe.denominator = vcs->fps; - - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - /* TODO: cp->readbuffers = xxx; */ - - return 0; -} - -static int vino_s_parm(struct file *file, void *__fh, - struct v4l2_streamparm *sp) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - unsigned long flags; - struct v4l2_captureparm *cp = &sp->parm.capture; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - if ((cp->timeperframe.numerator == 0) || - (cp->timeperframe.denominator == 0)) { - /* reset framerate */ - vino_set_default_framerate(vcs); - } else { - vino_set_framerate(vcs, cp->timeperframe.denominator / - cp->timeperframe.numerator); - } - - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - return 0; -} - -static int vino_reqbufs(struct file *file, void *__fh, - struct v4l2_requestbuffers *rb) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - - if (vcs->reading) - return -EBUSY; - - /* TODO: check queue type */ - if (rb->memory != V4L2_MEMORY_MMAP) { - dprintk("type not mmap\n"); - return -EINVAL; - } - - dprintk("count = %d\n", rb->count); - if (rb->count > 0) { - if (vino_is_capturing(vcs)) { - dprintk("busy, capturing\n"); - return -EBUSY; - } - - if (vino_queue_has_mapped_buffers(&vcs->fb_queue)) { - dprintk("busy, buffers still mapped\n"); - return -EBUSY; - } else { - vcs->streaming = 0; - vino_queue_free(&vcs->fb_queue); - vino_queue_init(&vcs->fb_queue, &rb->count); - } - } else { - vcs->streaming = 0; - vino_capture_stop(vcs); - vino_queue_free(&vcs->fb_queue); - } - - return 0; -} - -static void vino_v4l2_get_buffer_status(struct vino_channel_settings *vcs, - struct vino_framebuffer *fb, - struct v4l2_buffer *b) -{ - if (vino_queue_outgoing_contains(&vcs->fb_queue, - fb->id)) { - b->flags &= ~V4L2_BUF_FLAG_QUEUED; - b->flags |= V4L2_BUF_FLAG_DONE; - } else if (vino_queue_incoming_contains(&vcs->fb_queue, - fb->id)) { - b->flags &= ~V4L2_BUF_FLAG_DONE; - b->flags |= V4L2_BUF_FLAG_QUEUED; - } else { - b->flags &= ~(V4L2_BUF_FLAG_DONE | - V4L2_BUF_FLAG_QUEUED); - } - - b->flags &= ~(V4L2_BUF_FLAG_TIMECODE); - - if (fb->map_count > 0) - b->flags |= V4L2_BUF_FLAG_MAPPED; - - b->flags &= ~V4L2_BUF_FLAG_TIMESTAMP_MASK; - b->flags |= V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - - b->index = fb->id; - b->memory = (vcs->fb_queue.type == VINO_MEMORY_MMAP) ? - V4L2_MEMORY_MMAP : V4L2_MEMORY_USERPTR; - b->m.offset = fb->offset; - b->bytesused = fb->data_size; - b->length = fb->size; - b->field = V4L2_FIELD_INTERLACED; - b->sequence = fb->frame_counter; - memcpy(&b->timestamp, &fb->timestamp, - sizeof(struct timeval)); - // b->input ? - - dprintk("buffer %d: length = %d, bytesused = %d, offset = %d\n", - fb->id, fb->size, fb->data_size, fb->offset); -} - -static int vino_querybuf(struct file *file, void *__fh, - struct v4l2_buffer *b) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - struct vino_framebuffer *fb; - - if (vcs->reading) - return -EBUSY; - - /* TODO: check queue type */ - if (b->index >= vino_queue_get_length(&vcs->fb_queue)) { - dprintk("invalid index = %d\n", - b->index); - return -EINVAL; - } - - fb = vino_queue_get_buffer(&vcs->fb_queue, - b->index); - if (fb == NULL) { - dprintk("vino_queue_get_buffer() failed"); - return -EINVAL; - } - - vino_v4l2_get_buffer_status(vcs, fb, b); - - return 0; -} - -static int vino_qbuf(struct file *file, void *__fh, - struct v4l2_buffer *b) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - struct vino_framebuffer *fb; - int ret; - - if (vcs->reading) - return -EBUSY; - - /* TODO: check queue type */ - if (b->memory != V4L2_MEMORY_MMAP) { - dprintk("type not mmap\n"); - return -EINVAL; - } - - fb = vino_capture_enqueue(vcs, b->index); - if (fb == NULL) - return -EINVAL; - - vino_v4l2_get_buffer_status(vcs, fb, b); - - if (vcs->streaming) { - ret = vino_capture_next(vcs, 1); - if (ret) - return ret; - } - - return 0; -} - -static int vino_dqbuf(struct file *file, void *__fh, - struct v4l2_buffer *b) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - unsigned int nonblocking = file->f_flags & O_NONBLOCK; - struct vino_framebuffer *fb; - unsigned int incoming, outgoing; - int err; - - if (vcs->reading) - return -EBUSY; - - /* TODO: check queue type */ - - err = vino_queue_get_incoming(&vcs->fb_queue, &incoming); - if (err) { - dprintk("vino_queue_get_incoming() failed\n"); - return -EINVAL; - } - err = vino_queue_get_outgoing(&vcs->fb_queue, &outgoing); - if (err) { - dprintk("vino_queue_get_outgoing() failed\n"); - return -EINVAL; - } - - dprintk("incoming = %d, outgoing = %d\n", incoming, outgoing); - - if (outgoing == 0) { - if (incoming == 0) { - dprintk("no incoming or outgoing buffers\n"); - return -EINVAL; - } - if (nonblocking) { - dprintk("non-blocking I/O was selected and " - "there are no buffers to dequeue\n"); - return -EAGAIN; - } - - err = vino_wait_for_frame(vcs); - if (err) { - err = vino_wait_for_frame(vcs); - if (err) { - /* interrupted or no frames captured because of - * frame skipping */ - /* vino_capture_failed(vcs); */ - return -EIO; - } - } - } - - fb = vino_queue_remove(&vcs->fb_queue, &b->index); - if (fb == NULL) { - dprintk("vino_queue_remove() failed\n"); - return -EINVAL; - } - - err = vino_check_buffer(vcs, fb); - - vino_v4l2_get_buffer_status(vcs, fb, b); - - if (err) - return -EIO; - - return 0; -} - -static int vino_streamon(struct file *file, void *__fh, - enum v4l2_buf_type i) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - unsigned int incoming; - int ret; - if (vcs->reading) - return -EBUSY; - - if (vcs->streaming) - return 0; - - // TODO: check queue type - - if (vino_queue_get_length(&vcs->fb_queue) < 1) { - dprintk("no buffers allocated\n"); - return -EINVAL; - } - - ret = vino_queue_get_incoming(&vcs->fb_queue, &incoming); - if (ret) { - dprintk("vino_queue_get_incoming() failed\n"); - return -EINVAL; - } - - vcs->streaming = 1; - - if (incoming > 0) { - ret = vino_capture_next(vcs, 1); - if (ret) { - vcs->streaming = 0; - - dprintk("couldn't start capture\n"); - return -EINVAL; - } - } - - return 0; -} - -static int vino_streamoff(struct file *file, void *__fh, - enum v4l2_buf_type i) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - if (vcs->reading) - return -EBUSY; - - if (!vcs->streaming) - return 0; - - vcs->streaming = 0; - vino_capture_stop(vcs); - - return 0; -} - -static int vino_queryctrl(struct file *file, void *__fh, - struct v4l2_queryctrl *queryctrl) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - unsigned long flags; - int i; - int err = 0; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - switch (vcs->input) { - case VINO_INPUT_D1: - for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) { - if (vino_indycam_v4l2_controls[i].id == - queryctrl->id) { - memcpy(queryctrl, - &vino_indycam_v4l2_controls[i], - sizeof(struct v4l2_queryctrl)); - queryctrl->reserved[0] = 0; - goto found; - } - } - - err = -EINVAL; - break; - case VINO_INPUT_COMPOSITE: - case VINO_INPUT_SVIDEO: - for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) { - if (vino_saa7191_v4l2_controls[i].id == - queryctrl->id) { - memcpy(queryctrl, - &vino_saa7191_v4l2_controls[i], - sizeof(struct v4l2_queryctrl)); - queryctrl->reserved[0] = 0; - goto found; - } - } - - err = -EINVAL; - break; - default: - err = -EINVAL; - } - - found: - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - return err; -} - -static int vino_g_ctrl(struct file *file, void *__fh, - struct v4l2_control *control) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - unsigned long flags; - int i; - int err = 0; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - switch (vcs->input) { - case VINO_INPUT_D1: { - err = -EINVAL; - for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) { - if (vino_indycam_v4l2_controls[i].id == control->id) { - err = 0; - break; - } - } - - if (err) - goto out; - - err = camera_call(core, g_ctrl, control); - if (err) - err = -EINVAL; - break; - } - case VINO_INPUT_COMPOSITE: - case VINO_INPUT_SVIDEO: { - err = -EINVAL; - for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) { - if (vino_saa7191_v4l2_controls[i].id == control->id) { - err = 0; - break; - } - } - - if (err) - goto out; - - err = decoder_call(core, g_ctrl, control); - if (err) - err = -EINVAL; - break; - } - default: - err = -EINVAL; - } - -out: - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - return err; -} - -static int vino_s_ctrl(struct file *file, void *__fh, - struct v4l2_control *control) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - unsigned long flags; - int i; - int err = 0; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - if (!vino_is_input_owner(vcs)) { - err = -EBUSY; - goto out; - } - - switch (vcs->input) { - case VINO_INPUT_D1: { - err = -EINVAL; - for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) { - if (vino_indycam_v4l2_controls[i].id == control->id) { - err = 0; - break; - } - } - if (err) - goto out; - if (control->value < vino_indycam_v4l2_controls[i].minimum || - control->value > vino_indycam_v4l2_controls[i].maximum) { - err = -ERANGE; - goto out; - } - err = camera_call(core, s_ctrl, control); - if (err) - err = -EINVAL; - break; - } - case VINO_INPUT_COMPOSITE: - case VINO_INPUT_SVIDEO: { - err = -EINVAL; - for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) { - if (vino_saa7191_v4l2_controls[i].id == control->id) { - err = 0; - break; - } - } - if (err) - goto out; - if (control->value < vino_saa7191_v4l2_controls[i].minimum || - control->value > vino_saa7191_v4l2_controls[i].maximum) { - err = -ERANGE; - goto out; - } - - err = decoder_call(core, s_ctrl, control); - if (err) - err = -EINVAL; - break; - } - default: - err = -EINVAL; - } - -out: - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - - return err; -} - -/* File operations */ - -static int vino_open(struct file *file) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - int ret = 0; - dprintk("open(): channel = %c\n", - (vcs->channel == VINO_CHANNEL_A) ? 'A' : 'B'); - - mutex_lock(&vcs->mutex); - - if (vcs->users) { - dprintk("open(): driver busy\n"); - ret = -EBUSY; - goto out; - } - - ret = vino_acquire_input(vcs); - if (ret) { - dprintk("open(): vino_acquire_input() failed\n"); - goto out; - } - - vcs->users++; - - out: - mutex_unlock(&vcs->mutex); - - dprintk("open(): %s!\n", ret ? "failed" : "complete"); - - return ret; -} - -static int vino_close(struct file *file) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - dprintk("close():\n"); - - mutex_lock(&vcs->mutex); - - vcs->users--; - - if (!vcs->users) { - vino_release_input(vcs); - - /* stop DMA and free buffers */ - vino_capture_stop(vcs); - vino_queue_free(&vcs->fb_queue); - } - - mutex_unlock(&vcs->mutex); - - return 0; -} - -static void vino_vm_open(struct vm_area_struct *vma) -{ - struct vino_framebuffer *fb = vma->vm_private_data; - - fb->map_count++; - dprintk("vino_vm_open(): count = %d\n", fb->map_count); -} - -static void vino_vm_close(struct vm_area_struct *vma) -{ - struct vino_framebuffer *fb = vma->vm_private_data; - - fb->map_count--; - dprintk("vino_vm_close(): count = %d\n", fb->map_count); -} - -static const struct vm_operations_struct vino_vm_ops = { - .open = vino_vm_open, - .close = vino_vm_close, -}; - -static int vino_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end - vma->vm_start; - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - - struct vino_framebuffer *fb = NULL; - unsigned int i, length; - int ret = 0; - - dprintk("mmap():\n"); - - // TODO: reject mmap if already mapped - - if (mutex_lock_interruptible(&vcs->mutex)) - return -EINTR; - - if (vcs->reading) { - ret = -EBUSY; - goto out; - } - - // TODO: check queue type - - if (!(vma->vm_flags & VM_WRITE)) { - dprintk("mmap(): app bug: PROT_WRITE please\n"); - ret = -EINVAL; - goto out; - } - if (!(vma->vm_flags & VM_SHARED)) { - dprintk("mmap(): app bug: MAP_SHARED please\n"); - ret = -EINVAL; - goto out; - } - - /* find the correct buffer using offset */ - length = vino_queue_get_length(&vcs->fb_queue); - if (length == 0) { - dprintk("mmap(): queue not initialized\n"); - ret = -EINVAL; - goto out; - } - - for (i = 0; i < length; i++) { - fb = vino_queue_get_buffer(&vcs->fb_queue, i); - if (fb == NULL) { - dprintk("mmap(): vino_queue_get_buffer() failed\n"); - ret = -EINVAL; - goto out; - } - - if (fb->offset == offset) - goto found; - } - - dprintk("mmap(): invalid offset = %lu\n", offset); - ret = -EINVAL; - goto out; - -found: - dprintk("mmap(): buffer = %d\n", i); - - if (size > (fb->desc_table.page_count * PAGE_SIZE)) { - dprintk("mmap(): failed: size = %lu > %lu\n", - size, fb->desc_table.page_count * PAGE_SIZE); - ret = -EINVAL; - goto out; - } - - for (i = 0; i < fb->desc_table.page_count; i++) { - unsigned long pfn = - virt_to_phys((void *)fb->desc_table.virtual[i]) >> - PAGE_SHIFT; - - if (size < PAGE_SIZE) - break; - - // protection was: PAGE_READONLY - if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, - vma->vm_page_prot)) { - dprintk("mmap(): remap_pfn_range() failed\n"); - ret = -EAGAIN; - goto out; - } - - start += PAGE_SIZE; - size -= PAGE_SIZE; - } - - fb->map_count = 1; - - vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; - vma->vm_flags &= ~VM_IO; - vma->vm_private_data = fb; - vma->vm_file = file; - vma->vm_ops = &vino_vm_ops; - -out: - mutex_unlock(&vcs->mutex); - - return ret; -} - -static unsigned int vino_poll(struct file *file, poll_table *pt) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - unsigned int outgoing; - unsigned int ret = 0; - - // lock mutex (?) - // TODO: this has to be corrected for different read modes - - dprintk("poll():\n"); - - if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) { - dprintk("poll(): vino_queue_get_outgoing() failed\n"); - ret = POLLERR; - goto error; - } - if (outgoing > 0) - goto over; - - poll_wait(file, &vcs->fb_queue.frame_wait_queue, pt); - - if (vino_queue_get_outgoing(&vcs->fb_queue, &outgoing)) { - dprintk("poll(): vino_queue_get_outgoing() failed\n"); - ret = POLLERR; - goto error; - } - -over: - dprintk("poll(): data %savailable\n", - (outgoing > 0) ? "" : "not "); - - if (outgoing > 0) - ret = POLLIN | POLLRDNORM; - -error: - return ret; -} - -static long vino_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct vino_channel_settings *vcs = video_drvdata(file); - long ret; - - if (mutex_lock_interruptible(&vcs->mutex)) - return -EINTR; - - ret = video_ioctl2(file, cmd, arg); - - mutex_unlock(&vcs->mutex); - - return ret; -} - -/* Initialization and cleanup */ - -/* __initdata */ -static int vino_init_stage; - -const struct v4l2_ioctl_ops vino_ioctl_ops = { - .vidioc_enum_fmt_vid_cap = vino_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vino_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vino_s_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vino_try_fmt_vid_cap, - .vidioc_querycap = vino_querycap, - .vidioc_enum_input = vino_enum_input, - .vidioc_g_input = vino_g_input, - .vidioc_s_input = vino_s_input, - .vidioc_g_std = vino_g_std, - .vidioc_s_std = vino_s_std, - .vidioc_querystd = vino_querystd, - .vidioc_cropcap = vino_cropcap, - .vidioc_s_crop = vino_s_crop, - .vidioc_g_crop = vino_g_crop, - .vidioc_s_parm = vino_s_parm, - .vidioc_g_parm = vino_g_parm, - .vidioc_reqbufs = vino_reqbufs, - .vidioc_querybuf = vino_querybuf, - .vidioc_qbuf = vino_qbuf, - .vidioc_dqbuf = vino_dqbuf, - .vidioc_streamon = vino_streamon, - .vidioc_streamoff = vino_streamoff, - .vidioc_queryctrl = vino_queryctrl, - .vidioc_g_ctrl = vino_g_ctrl, - .vidioc_s_ctrl = vino_s_ctrl, -}; - -static const struct v4l2_file_operations vino_fops = { - .owner = THIS_MODULE, - .open = vino_open, - .release = vino_close, - .unlocked_ioctl = vino_ioctl, - .mmap = vino_mmap, - .poll = vino_poll, -}; - -static struct video_device vdev_template = { - .name = "NOT SET", - .fops = &vino_fops, - .ioctl_ops = &vino_ioctl_ops, - .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM, -}; - -static void vino_module_cleanup(int stage) -{ - switch(stage) { - case 11: - video_unregister_device(vino_drvdata->b.vdev); - vino_drvdata->b.vdev = NULL; - case 10: - video_unregister_device(vino_drvdata->a.vdev); - vino_drvdata->a.vdev = NULL; - case 9: - i2c_del_adapter(&vino_i2c_adapter); - case 8: - free_irq(SGI_VINO_IRQ, NULL); - case 7: - if (vino_drvdata->b.vdev) { - video_device_release(vino_drvdata->b.vdev); - vino_drvdata->b.vdev = NULL; - } - case 6: - if (vino_drvdata->a.vdev) { - video_device_release(vino_drvdata->a.vdev); - vino_drvdata->a.vdev = NULL; - } - case 5: - /* all entries in dma_cpu dummy table have the same address */ - dma_unmap_single(NULL, - vino_drvdata->dummy_desc_table.dma_cpu[0], - PAGE_SIZE, DMA_FROM_DEVICE); - dma_free_coherent(NULL, VINO_DUMMY_DESC_COUNT - * sizeof(dma_addr_t), - (void *)vino_drvdata-> - dummy_desc_table.dma_cpu, - vino_drvdata->dummy_desc_table.dma); - case 4: - free_page(vino_drvdata->dummy_page); - case 3: - v4l2_device_unregister(&vino_drvdata->v4l2_dev); - case 2: - kfree(vino_drvdata); - case 1: - iounmap(vino); - case 0: - break; - default: - dprintk("vino_module_cleanup(): invalid cleanup stage = %d\n", - stage); - } -} - -static int vino_probe(void) -{ - unsigned long rev_id; - - if (ip22_is_fullhouse()) { - printk(KERN_ERR "VINO doesn't exist in IP22 Fullhouse\n"); - return -ENODEV; - } - - if (!(sgimc->systemid & SGIMC_SYSID_EPRESENT)) { - printk(KERN_ERR "VINO is not found (EISA BUS not present)\n"); - return -ENODEV; - } - - vino = (struct sgi_vino *)ioremap(VINO_BASE, sizeof(struct sgi_vino)); - if (!vino) { - printk(KERN_ERR "VINO: ioremap() failed\n"); - return -EIO; - } - vino_init_stage++; - - if (get_dbe(rev_id, &(vino->rev_id))) { - printk(KERN_ERR "Failed to read VINO revision register\n"); - vino_module_cleanup(vino_init_stage); - return -ENODEV; - } - - if (VINO_ID_VALUE(rev_id) != VINO_CHIP_ID) { - printk(KERN_ERR "Unknown VINO chip ID (Rev/ID: 0x%02lx)\n", - rev_id); - vino_module_cleanup(vino_init_stage); - return -ENODEV; - } - - printk(KERN_INFO "VINO revision %ld found\n", VINO_REV_NUM(rev_id)); - - return 0; -} - -static int vino_init(void) -{ - dma_addr_t dma_dummy_address; - int err; - int i; - - vino_drvdata = kzalloc(sizeof(struct vino_settings), GFP_KERNEL); - if (!vino_drvdata) { - vino_module_cleanup(vino_init_stage); - return -ENOMEM; - } - vino_init_stage++; - strlcpy(vino_drvdata->v4l2_dev.name, "vino", - sizeof(vino_drvdata->v4l2_dev.name)); - err = v4l2_device_register(NULL, &vino_drvdata->v4l2_dev); - if (err) - return err; - vino_init_stage++; - - /* create a dummy dma descriptor */ - vino_drvdata->dummy_page = get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!vino_drvdata->dummy_page) { - vino_module_cleanup(vino_init_stage); - return -ENOMEM; - } - vino_init_stage++; - - // TODO: use page_count in dummy_desc_table - - vino_drvdata->dummy_desc_table.dma_cpu = - dma_alloc_coherent(NULL, - VINO_DUMMY_DESC_COUNT * sizeof(dma_addr_t), - &vino_drvdata->dummy_desc_table.dma, - GFP_KERNEL | GFP_DMA); - if (!vino_drvdata->dummy_desc_table.dma_cpu) { - vino_module_cleanup(vino_init_stage); - return -ENOMEM; - } - vino_init_stage++; - - dma_dummy_address = dma_map_single(NULL, - (void *)vino_drvdata->dummy_page, - PAGE_SIZE, DMA_FROM_DEVICE); - for (i = 0; i < VINO_DUMMY_DESC_COUNT; i++) { - vino_drvdata->dummy_desc_table.dma_cpu[i] = dma_dummy_address; - } - - /* initialize VINO */ - - vino->control = 0; - vino->a.next_4_desc = vino_drvdata->dummy_desc_table.dma; - vino->b.next_4_desc = vino_drvdata->dummy_desc_table.dma; - udelay(VINO_DESC_FETCH_DELAY); - - vino->intr_status = 0; - - vino->a.fifo_thres = VINO_FIFO_THRESHOLD_DEFAULT; - vino->b.fifo_thres = VINO_FIFO_THRESHOLD_DEFAULT; - - return 0; -} - -static int vino_init_channel_settings(struct vino_channel_settings *vcs, - unsigned int channel, const char *name) -{ - vcs->channel = channel; - vcs->input = VINO_INPUT_NONE; - vcs->alpha = 0; - vcs->users = 0; - vcs->data_format = VINO_DATA_FMT_GREY; - vcs->data_norm = VINO_DATA_NORM_NTSC; - vcs->decimation = 1; - vino_set_default_clipping(vcs); - vino_set_default_framerate(vcs); - - vcs->capturing = 0; - - mutex_init(&vcs->mutex); - spin_lock_init(&vcs->capture_lock); - - mutex_init(&vcs->fb_queue.queue_mutex); - spin_lock_init(&vcs->fb_queue.queue_lock); - init_waitqueue_head(&vcs->fb_queue.frame_wait_queue); - - vcs->vdev = video_device_alloc(); - if (!vcs->vdev) { - vino_module_cleanup(vino_init_stage); - return -ENOMEM; - } - vino_init_stage++; - - memcpy(vcs->vdev, &vdev_template, - sizeof(struct video_device)); - strcpy(vcs->vdev->name, name); - vcs->vdev->release = video_device_release; - vcs->vdev->v4l2_dev = &vino_drvdata->v4l2_dev; - - video_set_drvdata(vcs->vdev, vcs); - - return 0; -} - -static int __init vino_module_init(void) -{ - int ret; - - printk(KERN_INFO "SGI VINO driver version %s\n", - VINO_MODULE_VERSION); - - ret = vino_probe(); - if (ret) - return ret; - - ret = vino_init(); - if (ret) - return ret; - - /* initialize data structures */ - - spin_lock_init(&vino_drvdata->vino_lock); - spin_lock_init(&vino_drvdata->input_lock); - - ret = vino_init_channel_settings(&vino_drvdata->a, VINO_CHANNEL_A, - vino_vdev_name_a); - if (ret) - return ret; - - ret = vino_init_channel_settings(&vino_drvdata->b, VINO_CHANNEL_B, - vino_vdev_name_b); - if (ret) - return ret; - - /* initialize hardware and register V4L devices */ - - ret = request_irq(SGI_VINO_IRQ, vino_interrupt, 0, - vino_driver_description, NULL); - if (ret) { - printk(KERN_ERR "VINO: requesting IRQ %02d failed\n", - SGI_VINO_IRQ); - vino_module_cleanup(vino_init_stage); - return -EAGAIN; - } - vino_init_stage++; - - ret = i2c_add_adapter(&vino_i2c_adapter); - if (ret) { - printk(KERN_ERR "VINO I2C bus registration failed\n"); - vino_module_cleanup(vino_init_stage); - return ret; - } - i2c_set_adapdata(&vino_i2c_adapter, &vino_drvdata->v4l2_dev); - vino_init_stage++; - - ret = video_register_device(vino_drvdata->a.vdev, - VFL_TYPE_GRABBER, -1); - if (ret < 0) { - printk(KERN_ERR "VINO channel A Video4Linux-device " - "registration failed\n"); - vino_module_cleanup(vino_init_stage); - return -EINVAL; - } - vino_init_stage++; - - ret = video_register_device(vino_drvdata->b.vdev, - VFL_TYPE_GRABBER, -1); - if (ret < 0) { - printk(KERN_ERR "VINO channel B Video4Linux-device " - "registration failed\n"); - vino_module_cleanup(vino_init_stage); - return -EINVAL; - } - vino_init_stage++; - - vino_drvdata->decoder = - v4l2_i2c_new_subdev(&vino_drvdata->v4l2_dev, &vino_i2c_adapter, - "saa7191", 0, I2C_ADDRS(0x45)); - vino_drvdata->camera = - v4l2_i2c_new_subdev(&vino_drvdata->v4l2_dev, &vino_i2c_adapter, - "indycam", 0, I2C_ADDRS(0x2b)); - - dprintk("init complete!\n"); - - return 0; -} - -static void __exit vino_module_exit(void) -{ - dprintk("exiting, stage = %d ...\n", vino_init_stage); - vino_module_cleanup(vino_init_stage); - dprintk("cleanup complete, exit!\n"); -} - -module_init(vino_module_init); -module_exit(vino_module_exit); diff --git a/drivers/staging/media/vino/vino.h b/drivers/staging/media/vino/vino.h deleted file mode 100644 index de2d615ae7c9..000000000000 --- a/drivers/staging/media/vino/vino.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Driver for the VINO (Video In No Out) system found in SGI Indys. - * - * This file is subject to the terms and conditions of the GNU General Public - * License version 2 as published by the Free Software Foundation. - * - * Copyright (C) 1999 Ulf Karlsson <ulfc@bun.falkenberg.se> - * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org> - */ - -#ifndef _VINO_H_ -#define _VINO_H_ - -#define VINO_BASE 0x00080000 /* Vino is in the EISA address space, - * but it is not an EISA bus card */ -#define VINO_PAGE_SIZE 4096 - -struct sgi_vino_channel { - u32 _pad_alpha; - volatile u32 alpha; - -#define VINO_CLIP_X(x) ((x) & 0x3ff) /* bits 0:9 */ -#define VINO_CLIP_ODD(x) (((x) & 0x1ff) << 10) /* bits 10:18 */ -#define VINO_CLIP_EVEN(x) (((x) & 0x1ff) << 19) /* bits 19:27 */ - u32 _pad_clip_start; - volatile u32 clip_start; - u32 _pad_clip_end; - volatile u32 clip_end; - -#define VINO_FRAMERT_FULL 0xfff -#define VINO_FRAMERT_PAL (1<<0) /* 0=NTSC 1=PAL */ -#define VINO_FRAMERT_RT(x) (((x) & 0xfff) << 1) /* bits 1:12 */ - u32 _pad_frame_rate; - volatile u32 frame_rate; - - u32 _pad_field_counter; - volatile u32 field_counter; - u32 _pad_line_size; - volatile u32 line_size; - u32 _pad_line_count; - volatile u32 line_count; - u32 _pad_page_index; - volatile u32 page_index; - u32 _pad_next_4_desc; - volatile u32 next_4_desc; - u32 _pad_start_desc_tbl; - volatile u32 start_desc_tbl; - -#define VINO_DESC_JUMP (1<<30) -#define VINO_DESC_STOP (1<<31) -#define VINO_DESC_VALID (1<<32) - u32 _pad_desc_0; - volatile u32 desc_0; - u32 _pad_desc_1; - volatile u32 desc_1; - u32 _pad_desc_2; - volatile u32 desc_2; - u32 _pad_Bdesc_3; - volatile u32 desc_3; - - u32 _pad_fifo_thres; - volatile u32 fifo_thres; - u32 _pad_fifo_read; - volatile u32 fifo_read; - u32 _pad_fifo_write; - volatile u32 fifo_write; -}; - -struct sgi_vino { -#define VINO_CHIP_ID 0xb -#define VINO_REV_NUM(x) ((x) & 0x0f) -#define VINO_ID_VALUE(x) (((x) & 0xf0) >> 4) - u32 _pad_rev_id; - volatile u32 rev_id; - -#define VINO_CTRL_LITTLE_ENDIAN (1<<0) -#define VINO_CTRL_A_EOF_INT (1<<1) /* Field transferred int */ -#define VINO_CTRL_A_FIFO_INT (1<<2) /* FIFO overflow int */ -#define VINO_CTRL_A_EOD_INT (1<<3) /* End of desc table int */ -#define VINO_CTRL_A_INT (VINO_CTRL_A_EOF_INT | \ - VINO_CTRL_A_FIFO_INT | \ - VINO_CTRL_A_EOD_INT) -#define VINO_CTRL_B_EOF_INT (1<<4) /* Field transferred int */ -#define VINO_CTRL_B_FIFO_INT (1<<5) /* FIFO overflow int */ -#define VINO_CTRL_B_EOD_INT (1<<6) /* End of desc table int */ -#define VINO_CTRL_B_INT (VINO_CTRL_B_EOF_INT | \ - VINO_CTRL_B_FIFO_INT | \ - VINO_CTRL_B_EOD_INT) -#define VINO_CTRL_A_DMA_ENBL (1<<7) -#define VINO_CTRL_A_INTERLEAVE_ENBL (1<<8) -#define VINO_CTRL_A_SYNC_ENBL (1<<9) -#define VINO_CTRL_A_SELECT (1<<10) /* 1=D1 0=Philips */ -#define VINO_CTRL_A_RGB (1<<11) /* 1=RGB 0=YUV */ -#define VINO_CTRL_A_LUMA_ONLY (1<<12) -#define VINO_CTRL_A_DEC_ENBL (1<<13) /* Decimation */ -#define VINO_CTRL_A_DEC_SCALE_MASK 0x1c000 /* bits 14:17 */ -#define VINO_CTRL_A_DEC_SCALE_SHIFT (14) -#define VINO_CTRL_A_DEC_HOR_ONLY (1<<17) /* Horizontal only */ -#define VINO_CTRL_A_DITHER (1<<18) /* 24 -> 8 bit dither */ -#define VINO_CTRL_B_DMA_ENBL (1<<19) -#define VINO_CTRL_B_INTERLEAVE_ENBL (1<<20) -#define VINO_CTRL_B_SYNC_ENBL (1<<21) -#define VINO_CTRL_B_SELECT (1<<22) /* 1=D1 0=Philips */ -#define VINO_CTRL_B_RGB (1<<23) /* 1=RGB 0=YUV */ -#define VINO_CTRL_B_LUMA_ONLY (1<<24) -#define VINO_CTRL_B_DEC_ENBL (1<<25) /* Decimation */ -#define VINO_CTRL_B_DEC_SCALE_MASK 0x1c000000 /* bits 26:28 */ -#define VINO_CTRL_B_DEC_SCALE_SHIFT (26) -#define VINO_CTRL_B_DEC_HOR_ONLY (1<<29) /* Decimation horizontal only */ -#define VINO_CTRL_B_DITHER (1<<30) /* ChanB 24 -> 8 bit dither */ - u32 _pad_control; - volatile u32 control; - -#define VINO_INTSTAT_A_EOF (1<<0) /* Field transferred int */ -#define VINO_INTSTAT_A_FIFO (1<<1) /* FIFO overflow int */ -#define VINO_INTSTAT_A_EOD (1<<2) /* End of desc table int */ -#define VINO_INTSTAT_A (VINO_INTSTAT_A_EOF | \ - VINO_INTSTAT_A_FIFO | \ - VINO_INTSTAT_A_EOD) -#define VINO_INTSTAT_B_EOF (1<<3) /* Field transferred int */ -#define VINO_INTSTAT_B_FIFO (1<<4) /* FIFO overflow int */ -#define VINO_INTSTAT_B_EOD (1<<5) /* End of desc table int */ -#define VINO_INTSTAT_B (VINO_INTSTAT_B_EOF | \ - VINO_INTSTAT_B_FIFO | \ - VINO_INTSTAT_B_EOD) - u32 _pad_intr_status; - volatile u32 intr_status; - - u32 _pad_i2c_control; - volatile u32 i2c_control; - u32 _pad_i2c_data; - volatile u32 i2c_data; - - struct sgi_vino_channel a; - struct sgi_vino_channel b; -}; - -#endif diff --git a/drivers/staging/nvec/nvec.c b/drivers/staging/nvec/nvec.c index 093535c6217b..120b70d72d79 100644 --- a/drivers/staging/nvec/nvec.c +++ b/drivers/staging/nvec/nvec.c @@ -85,23 +85,20 @@ static struct nvec_chip *nvec_power_handle; static const struct mfd_cell nvec_devices[] = { { .name = "nvec-kbd", - .id = 1, }, { .name = "nvec-mouse", - .id = 1, }, { .name = "nvec-power", - .id = 1, + .id = 0, }, { .name = "nvec-power", - .id = 2, + .id = 1, }, { .name = "nvec-paz00", - .id = 1, }, }; @@ -891,7 +888,7 @@ static int tegra_nvec_probe(struct platform_device *pdev) nvec_msg_free(nvec, msg); } - ret = mfd_add_devices(nvec->dev, -1, nvec_devices, + ret = mfd_add_devices(nvec->dev, 0, nvec_devices, ARRAY_SIZE(nvec_devices), NULL, 0, NULL); if (ret) dev_err(nvec->dev, "error adding subdevices\n"); diff --git a/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c index 82a8c06ab347..c5800ae71fcf 100644 --- a/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723au/os_dep/ioctl_cfg80211.c @@ -1091,17 +1091,17 @@ static int cfg80211_rtw_get_station(struct wiphy *wiphy, goto exit; } - sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); sinfo->signal = translate_percentage_to_dbm(padapter->recvpriv. signal_strength); - sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); sinfo->txrate.legacy = rtw_get_cur_max_rate(padapter); - sinfo->filled |= STATION_INFO_RX_PACKETS; + sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS); sinfo->rx_packets = sta_rx_data_pkts(psta); - sinfo->filled |= STATION_INFO_TX_PACKETS; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS); sinfo->tx_packets = psta->sta_stats.tx_pkts; } @@ -2363,7 +2363,7 @@ void rtw_cfg80211_indicate_sta_assoc(struct rtw_adapter *padapter, ie_offset = offsetof(struct ieee80211_mgmt, u.reassoc_req.variable); - sinfo.filled = STATION_INFO_ASSOC_REQ_IES; + sinfo.filled = 0; sinfo.assoc_req_ies = pmgmt_frame + ie_offset; sinfo.assoc_req_ies_len = frame_len - ie_offset; cfg80211_new_sta(ndev, hdr->addr2, &sinfo, GFP_ATOMIC); diff --git a/drivers/staging/speakup/speakup_dtlk.c b/drivers/staging/speakup/speakup_dtlk.c index 4e059ea78d4c..89592c0b9151 100644 --- a/drivers/staging/speakup/speakup_dtlk.c +++ b/drivers/staging/speakup/speakup_dtlk.c @@ -325,7 +325,7 @@ static struct synth_settings *synth_interrogate(struct spk_synth *synth) static int synth_probe(struct spk_synth *synth) { - unsigned int port_val = 0; + unsigned int port_val = 0; int i = 0; struct synth_settings *sp; @@ -361,7 +361,8 @@ static int synth_probe(struct spk_synth *synth) port_val &= 0xfbff; if (port_val != 0x107f) { pr_info("DoubleTalk PC: not found\n"); - synth_release_region(synth_lpc, SYNTH_IO_EXTENT); + if (synth_lpc) + synth_release_region(synth_lpc, SYNTH_IO_EXTENT); return -ENODEV; } while (inw_p(synth_lpc) != 0x147f) @@ -369,7 +370,7 @@ static int synth_probe(struct spk_synth *synth) sp = synth_interrogate(synth); pr_info("%s: %03x-%03x, ROM ver %s, s/n %u, driver: %s\n", synth->long_name, synth_lpc, synth_lpc+SYNTH_IO_EXTENT - 1, - sp->rom_version, sp->serial_number, synth->version); + sp->rom_version, sp->serial_number, synth->version); synth->alive = 1; return 0; } diff --git a/drivers/staging/vt6655/baseband.c b/drivers/staging/vt6655/baseband.c index 86c72ba0a0cd..f8c5fc371c4c 100644 --- a/drivers/staging/vt6655/baseband.c +++ b/drivers/staging/vt6655/baseband.c @@ -2177,7 +2177,7 @@ bool BBbVT3253Init(struct vnt_private *priv) /* Init ANT B select,RX Config CR10 = 0x28->0x2A, 0x2A->0x28(VC1/VC2 define, make the ANT_A, ANT_B inverted) */ /*bResult &= BBbWriteEmbedded(dwIoBase,0x0a,0x28);*/ /* Select VC1/VC2, CR215 = 0x02->0x06 */ - bResult &= BBbWriteEmbedded(dwIoBase, 0xd7, 0x06); + bResult &= BBbWriteEmbedded(priv, 0xd7, 0x06); /* }} */ for (ii = 0; ii < CB_VT3253B0_AGC; ii++) diff --git a/drivers/staging/vt6655/channel.c b/drivers/staging/vt6655/channel.c index c8f739dd346e..70f870541f92 100644 --- a/drivers/staging/vt6655/channel.c +++ b/drivers/staging/vt6655/channel.c @@ -182,6 +182,14 @@ bool set_channel(void *pDeviceHandler, unsigned int uConnectionChannel) if (pDevice->byCurrentCh == uConnectionChannel) return bResult; + /* Set VGA to max sensitivity */ + if (pDevice->bUpdateBBVGA && + pDevice->byBBVGACurrent != pDevice->abyBBVGA[0]) { + pDevice->byBBVGACurrent = pDevice->abyBBVGA[0]; + + BBvSetVGAGainOffset(pDevice, pDevice->byBBVGACurrent); + } + /* clear NAV */ MACvRegBitsOn(pDevice->PortOffset, MAC_REG_MACCR, MACCR_CLRNAV); diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c index 83e4162c0094..cd1a277d853b 100644 --- a/drivers/staging/vt6655/device_main.c +++ b/drivers/staging/vt6655/device_main.c @@ -1232,7 +1232,7 @@ static int vnt_tx_packet(struct vnt_private *priv, struct sk_buff *skb) head_td = priv->apCurrTD[dma_idx]; - head_td->m_td1TD1.byTCR = (TCR_EDP|TCR_STP); + head_td->m_td1TD1.byTCR = 0; head_td->pTDInfo->skb = skb; @@ -1257,6 +1257,11 @@ static int vnt_tx_packet(struct vnt_private *priv, struct sk_buff *skb) priv->bPWBitOn = false; + /* Set TSR1 & ReqCount in TxDescHead */ + head_td->m_td1TD1.byTCR |= (TCR_STP | TCR_EDP | EDMSDU); + head_td->m_td1TD1.wReqCount = + cpu_to_le16((u16)head_td->pTDInfo->dwReqCount); + head_td->pTDInfo->byFlags = TD_FLAGS_NETIF_SKB; if (dma_idx == TYPE_AC0DMA) @@ -1500,9 +1505,11 @@ static void vnt_bss_info_changed(struct ieee80211_hw *hw, if (conf->enable_beacon) { vnt_beacon_enable(priv, vif, conf); - MACvRegBitsOn(priv, MAC_REG_TCR, TCR_AUTOBCNTX); + MACvRegBitsOn(priv->PortOffset, MAC_REG_TCR, + TCR_AUTOBCNTX); } else { - MACvRegBitsOff(priv, MAC_REG_TCR, TCR_AUTOBCNTX); + MACvRegBitsOff(priv->PortOffset, MAC_REG_TCR, + TCR_AUTOBCNTX); } } diff --git a/drivers/staging/vt6655/rxtx.c b/drivers/staging/vt6655/rxtx.c index 61c39dd7ad01..b5b0155961f2 100644 --- a/drivers/staging/vt6655/rxtx.c +++ b/drivers/staging/vt6655/rxtx.c @@ -1204,13 +1204,10 @@ s_cbFillTxBufHead(struct vnt_private *pDevice, unsigned char byPktType, ptdCurr = (PSTxDesc)pHeadTD; - ptdCurr->pTDInfo->dwReqCount = cbReqCount - uPadding; + ptdCurr->pTDInfo->dwReqCount = cbReqCount; ptdCurr->pTDInfo->dwHeaderLength = cbHeaderLength; ptdCurr->pTDInfo->skb_dma = ptdCurr->pTDInfo->buf_dma; ptdCurr->buff_addr = cpu_to_le32(ptdCurr->pTDInfo->skb_dma); - /* Set TSR1 & ReqCount in TxDescHead */ - ptdCurr->m_td1TD1.byTCR |= (TCR_STP | TCR_EDP | EDMSDU); - ptdCurr->m_td1TD1.wReqCount = cpu_to_le16((unsigned short)(cbReqCount)); return cbHeaderLength; } diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c index 8942dcb44180..7c87aecf4744 100644 --- a/drivers/staging/wlan-ng/cfg80211.c +++ b/drivers/staging/wlan-ng/cfg80211.c @@ -325,9 +325,9 @@ static int prism2_get_station(struct wiphy *wiphy, struct net_device *dev, if (result == 0) { sinfo->txrate.legacy = quality.txrate.data; - sinfo->filled |= STATION_INFO_TX_BITRATE; + sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE); sinfo->signal = quality.level.data; - sinfo->filled |= STATION_INFO_SIGNAL; + sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL); } return result; diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index 55f6774f706f..aebde3289c50 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -2027,10 +2027,10 @@ iscsit_process_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, goto reject; } if (!strncmp("=All", text_ptr, 4)) { - cmd->cmd_flags |= IFC_SENDTARGETS_ALL; + cmd->cmd_flags |= ICF_SENDTARGETS_ALL; } else if (!strncmp("=iqn.", text_ptr, 5) || !strncmp("=eui.", text_ptr, 5)) { - cmd->cmd_flags |= IFC_SENDTARGETS_SINGLE; + cmd->cmd_flags |= ICF_SENDTARGETS_SINGLE; } else { pr_err("Unable to locate valid SendTargets=%s value\n", text_ptr); goto reject; @@ -3415,10 +3415,10 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd, return -ENOMEM; } /* - * Locate pointer to iqn./eui. string for IFC_SENDTARGETS_SINGLE + * Locate pointer to iqn./eui. string for ICF_SENDTARGETS_SINGLE * explicit case.. */ - if (cmd->cmd_flags & IFC_SENDTARGETS_SINGLE) { + if (cmd->cmd_flags & ICF_SENDTARGETS_SINGLE) { text_ptr = strchr(text_in, '='); if (!text_ptr) { pr_err("Unable to locate '=' string in text_in:" @@ -3434,7 +3434,7 @@ iscsit_build_sendtargets_response(struct iscsi_cmd *cmd, spin_lock(&tiqn_lock); list_for_each_entry(tiqn, &g_tiqn_list, tiqn_list) { - if ((cmd->cmd_flags & IFC_SENDTARGETS_SINGLE) && + if ((cmd->cmd_flags & ICF_SENDTARGETS_SINGLE) && strcmp(tiqn->tiqn, text_ptr)) { continue; } @@ -3512,7 +3512,7 @@ eob: if (end_of_buf) break; - if (cmd->cmd_flags & IFC_SENDTARGETS_SINGLE) + if (cmd->cmd_flags & ICF_SENDTARGETS_SINGLE) break; } spin_unlock(&tiqn_lock); diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h index 09a522bae222..cbcff38ac9b7 100644 --- a/drivers/target/iscsi/iscsi_target_core.h +++ b/drivers/target/iscsi/iscsi_target_core.h @@ -135,8 +135,8 @@ enum cmd_flags_table { ICF_CONTIG_MEMORY = 0x00000020, ICF_ATTACHED_TO_RQUEUE = 0x00000040, ICF_OOO_CMDSN = 0x00000080, - IFC_SENDTARGETS_ALL = 0x00000100, - IFC_SENDTARGETS_SINGLE = 0x00000200, + ICF_SENDTARGETS_ALL = 0x00000100, + ICF_SENDTARGETS_SINGLE = 0x00000200, }; /* struct iscsi_cmd->i_state */ diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 7653cfb027a2..58f49ff69b14 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -1103,51 +1103,6 @@ int se_dev_set_queue_depth(struct se_device *dev, u32 queue_depth) } EXPORT_SYMBOL(se_dev_set_queue_depth); -int se_dev_set_fabric_max_sectors(struct se_device *dev, u32 fabric_max_sectors) -{ - int block_size = dev->dev_attrib.block_size; - - if (dev->export_count) { - pr_err("dev[%p]: Unable to change SE Device" - " fabric_max_sectors while export_count is %d\n", - dev, dev->export_count); - return -EINVAL; - } - if (!fabric_max_sectors) { - pr_err("dev[%p]: Illegal ZERO value for" - " fabric_max_sectors\n", dev); - return -EINVAL; - } - if (fabric_max_sectors < DA_STATUS_MAX_SECTORS_MIN) { - pr_err("dev[%p]: Passed fabric_max_sectors: %u less than" - " DA_STATUS_MAX_SECTORS_MIN: %u\n", dev, fabric_max_sectors, - DA_STATUS_MAX_SECTORS_MIN); - return -EINVAL; - } - if (fabric_max_sectors > DA_STATUS_MAX_SECTORS_MAX) { - pr_err("dev[%p]: Passed fabric_max_sectors: %u" - " greater than DA_STATUS_MAX_SECTORS_MAX:" - " %u\n", dev, fabric_max_sectors, - DA_STATUS_MAX_SECTORS_MAX); - return -EINVAL; - } - /* - * Align max_sectors down to PAGE_SIZE to follow transport_allocate_data_tasks() - */ - if (!block_size) { - block_size = 512; - pr_warn("Defaulting to 512 for zero block_size\n"); - } - fabric_max_sectors = se_dev_align_max_sectors(fabric_max_sectors, - block_size); - - dev->dev_attrib.fabric_max_sectors = fabric_max_sectors; - pr_debug("dev[%p]: SE Device max_sectors changed to %u\n", - dev, fabric_max_sectors); - return 0; -} -EXPORT_SYMBOL(se_dev_set_fabric_max_sectors); - int se_dev_set_optimal_sectors(struct se_device *dev, u32 optimal_sectors) { if (dev->export_count) { @@ -1156,10 +1111,10 @@ int se_dev_set_optimal_sectors(struct se_device *dev, u32 optimal_sectors) dev, dev->export_count); return -EINVAL; } - if (optimal_sectors > dev->dev_attrib.fabric_max_sectors) { + if (optimal_sectors > dev->dev_attrib.hw_max_sectors) { pr_err("dev[%p]: Passed optimal_sectors %u cannot be" - " greater than fabric_max_sectors: %u\n", dev, - optimal_sectors, dev->dev_attrib.fabric_max_sectors); + " greater than hw_max_sectors: %u\n", dev, + optimal_sectors, dev->dev_attrib.hw_max_sectors); return -EINVAL; } @@ -1553,8 +1508,6 @@ struct se_device *target_alloc_device(struct se_hba *hba, const char *name) dev->dev_attrib.unmap_granularity_alignment = DA_UNMAP_GRANULARITY_ALIGNMENT_DEFAULT; dev->dev_attrib.max_write_same_len = DA_MAX_WRITE_SAME_LEN; - dev->dev_attrib.fabric_max_sectors = DA_FABRIC_MAX_SECTORS; - dev->dev_attrib.optimal_sectors = DA_FABRIC_MAX_SECTORS; xcopy_lun = &dev->xcopy_lun; xcopy_lun->lun_se_dev = dev; @@ -1595,6 +1548,7 @@ int target_configure_device(struct se_device *dev) dev->dev_attrib.hw_max_sectors = se_dev_align_max_sectors(dev->dev_attrib.hw_max_sectors, dev->dev_attrib.hw_block_size); + dev->dev_attrib.optimal_sectors = dev->dev_attrib.hw_max_sectors; dev->dev_index = scsi_get_new_index(SCSI_DEVICE_INDEX); dev->creation_time = get_jiffies_64(); diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index c2aea099ea4a..d836de200a03 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -621,7 +621,16 @@ fd_execute_rw(struct se_cmd *cmd, struct scatterlist *sgl, u32 sgl_nents, struct fd_prot fd_prot; sense_reason_t rc; int ret = 0; - + /* + * We are currently limited by the number of iovecs (2048) per + * single vfs_[writev,readv] call. + */ + if (cmd->data_length > FD_MAX_BYTES) { + pr_err("FILEIO: Not able to process I/O of %u bytes due to" + "FD_MAX_BYTES: %u iovec count limitiation\n", + cmd->data_length, FD_MAX_BYTES); + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + } /* * Call vectorized fileio functions to map struct scatterlist * physical memory addresses to struct iovec virtual memory. @@ -959,7 +968,6 @@ static struct configfs_attribute *fileio_backend_dev_attrs[] = { &fileio_dev_attrib_hw_block_size.attr, &fileio_dev_attrib_block_size.attr, &fileio_dev_attrib_hw_max_sectors.attr, - &fileio_dev_attrib_fabric_max_sectors.attr, &fileio_dev_attrib_optimal_sectors.attr, &fileio_dev_attrib_hw_queue_depth.attr, &fileio_dev_attrib_queue_depth.attr, diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 3efff94fbd97..78346b850968 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -124,7 +124,7 @@ static int iblock_configure_device(struct se_device *dev) q = bdev_get_queue(bd); dev->dev_attrib.hw_block_size = bdev_logical_block_size(bd); - dev->dev_attrib.hw_max_sectors = UINT_MAX; + dev->dev_attrib.hw_max_sectors = queue_max_hw_sectors(q); dev->dev_attrib.hw_queue_depth = q->nr_requests; /* @@ -883,7 +883,6 @@ static struct configfs_attribute *iblock_backend_dev_attrs[] = { &iblock_dev_attrib_hw_block_size.attr, &iblock_dev_attrib_block_size.attr, &iblock_dev_attrib_hw_max_sectors.attr, - &iblock_dev_attrib_fabric_max_sectors.attr, &iblock_dev_attrib_optimal_sectors.attr, &iblock_dev_attrib_hw_queue_depth.attr, &iblock_dev_attrib_queue_depth.attr, diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index d56f2aaba9af..283cf786ef98 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -528,6 +528,18 @@ static int core_scsi3_pr_seq_non_holder( return 0; } + } else if (we && registered_nexus) { + /* + * Reads are allowed for Write Exclusive locks + * from all registrants. + */ + if (cmd->data_direction == DMA_FROM_DEVICE) { + pr_debug("Allowing READ CDB: 0x%02x for %s" + " reservation\n", cdb[0], + core_scsi3_pr_dump_type(pr_reg_type)); + + return 0; + } } pr_debug("%s Conflict for %sregistered nexus %s CDB: 0x%2x" " for %s reservation\n", transport_dump_cmd_direction(cmd), diff --git a/drivers/target/target_core_rd.c b/drivers/target/target_core_rd.c index 60ebd170a561..98e83ac5661b 100644 --- a/drivers/target/target_core_rd.c +++ b/drivers/target/target_core_rd.c @@ -657,7 +657,6 @@ static struct configfs_attribute *rd_mcp_backend_dev_attrs[] = { &rd_mcp_dev_attrib_hw_block_size.attr, &rd_mcp_dev_attrib_block_size.attr, &rd_mcp_dev_attrib_hw_max_sectors.attr, - &rd_mcp_dev_attrib_fabric_max_sectors.attr, &rd_mcp_dev_attrib_optimal_sectors.attr, &rd_mcp_dev_attrib_hw_queue_depth.attr, &rd_mcp_dev_attrib_queue_depth.attr, diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index 11bea1952435..cd4bed7b2757 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -953,21 +953,6 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops) if (cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) { unsigned long long end_lba; - - if (sectors > dev->dev_attrib.fabric_max_sectors) { - printk_ratelimited(KERN_ERR "SCSI OP %02xh with too" - " big sectors %u exceeds fabric_max_sectors:" - " %u\n", cdb[0], sectors, - dev->dev_attrib.fabric_max_sectors); - return TCM_INVALID_CDB_FIELD; - } - if (sectors > dev->dev_attrib.hw_max_sectors) { - printk_ratelimited(KERN_ERR "SCSI OP %02xh with too" - " big sectors %u exceeds backend hw_max_sectors:" - " %u\n", cdb[0], sectors, - dev->dev_attrib.hw_max_sectors); - return TCM_INVALID_CDB_FIELD; - } check_lba: end_lba = dev->transport->get_blocks(dev) + 1; if (cmd->t_task_lba + sectors > end_lba) { diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index 1307600fe726..4c71657da56a 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -505,7 +505,6 @@ static sense_reason_t spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf) { struct se_device *dev = cmd->se_dev; - u32 max_sectors; int have_tp = 0; int opt, min; @@ -539,9 +538,7 @@ spc_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf) /* * Set MAXIMUM TRANSFER LENGTH */ - max_sectors = min(dev->dev_attrib.fabric_max_sectors, - dev->dev_attrib.hw_max_sectors); - put_unaligned_be32(max_sectors, &buf[8]); + put_unaligned_be32(dev->dev_attrib.hw_max_sectors, &buf[8]); /* * Set OPTIMAL TRANSFER LENGTH diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 8bfa61c9693d..1a1bcf71ec9d 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -784,9 +784,7 @@ static int tcmu_netlink_event(enum tcmu_genl_cmd cmd, const char *name, int mino if (ret < 0) goto free_skb; - ret = genlmsg_end(skb, msg_header); - if (ret < 0) - goto free_skb; + genlmsg_end(skb, msg_header); ret = genlmsg_multicast(&tcmu_genl_family, skb, 0, TCMU_MCGRP_CONFIG, GFP_KERNEL); @@ -1118,7 +1116,6 @@ static struct configfs_attribute *tcmu_backend_dev_attrs[] = { &tcmu_dev_attrib_hw_block_size.attr, &tcmu_dev_attrib_block_size.attr, &tcmu_dev_attrib_hw_max_sectors.attr, - &tcmu_dev_attrib_fabric_max_sectors.attr, &tcmu_dev_attrib_optimal_sectors.attr, &tcmu_dev_attrib_hw_queue_depth.attr, &tcmu_dev_attrib_queue_depth.attr, diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index c1188ac053c9..2ccbc0788353 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -608,6 +608,7 @@ static int imx_thermal_suspend(struct device *dev) regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP); regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN); data->mode = THERMAL_DEVICE_DISABLED; + clk_disable_unprepare(data->thermal_clk); return 0; } @@ -617,6 +618,7 @@ static int imx_thermal_resume(struct device *dev) struct imx_thermal_data *data = dev_get_drvdata(dev); struct regmap *map = data->tempmon; + clk_prepare_enable(data->thermal_clk); /* Enabled thermal sensor after resume */ regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN); regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP); diff --git a/drivers/thermal/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/int340x_thermal/acpi_thermal_rel.c index 231cabc16e16..2c2ec7666eb1 100644 --- a/drivers/thermal/int340x_thermal/acpi_thermal_rel.c +++ b/drivers/thermal/int340x_thermal/acpi_thermal_rel.c @@ -119,15 +119,11 @@ int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp, continue; result = acpi_bus_get_device(trt->source, &adev); - if (!result) - acpi_create_platform_device(adev); - else + if (result) pr_warn("Failed to get source ACPI device\n"); result = acpi_bus_get_device(trt->target, &adev); - if (!result) - acpi_create_platform_device(adev); - else + if (result) pr_warn("Failed to get target ACPI device\n"); } @@ -206,16 +202,12 @@ int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp, if (art->source) { result = acpi_bus_get_device(art->source, &adev); - if (!result) - acpi_create_platform_device(adev); - else + if (result) pr_warn("Failed to get source ACPI device\n"); } if (art->target) { result = acpi_bus_get_device(art->target, &adev); - if (!result) - acpi_create_platform_device(adev); - else + if (result) pr_warn("Failed to get source ACPI device\n"); } } diff --git a/drivers/thermal/int340x_thermal/processor_thermal_device.c b/drivers/thermal/int340x_thermal/processor_thermal_device.c index 31bb553aac26..0fe5dbbea968 100644 --- a/drivers/thermal/int340x_thermal/processor_thermal_device.c +++ b/drivers/thermal/int340x_thermal/processor_thermal_device.c @@ -130,6 +130,8 @@ static int proc_thermal_add(struct device *dev, int ret; adev = ACPI_COMPANION(dev); + if (!adev) + return -ENODEV; status = acpi_evaluate_object(adev->handle, "PPCC", NULL, &buf); if (ACPI_FAILURE(status)) diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index e145b66df444..d717f3dab6f1 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -149,7 +149,7 @@ EXPORT_SYMBOL_GPL(of_thermal_is_trip_valid); * * Return: pointer to trip points table, NULL otherwise */ -const struct thermal_trip * const +const struct thermal_trip * of_thermal_get_trip_points(struct thermal_zone_device *tz) { struct __thermal_zone *data = tz->devdata; diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 8803e693fe68..2580a4872f90 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -63,7 +63,7 @@ struct rcar_thermal_priv { struct mutex lock; struct list_head list; int id; - int ctemp; + u32 ctemp; }; #define rcar_thermal_for_each_priv(pos, common) \ @@ -145,7 +145,7 @@ static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv) { struct device *dev = rcar_priv_to_dev(priv); int i; - int ctemp, old, new; + u32 ctemp, old, new; int ret = -EINVAL; mutex_lock(&priv->lock); @@ -372,6 +372,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) int i; int ret = -ENODEV; int idle = IDLE_INTERVAL; + u32 enr_bits = 0; common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL); if (!common) @@ -390,7 +391,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) /* * platform has IRQ support. - * Then, drier use common register + * Then, driver uses common registers */ ret = devm_request_irq(dev, irq->start, rcar_thermal_irq, 0, @@ -408,9 +409,6 @@ static int rcar_thermal_probe(struct platform_device *pdev) if (IS_ERR(common->base)) return PTR_ERR(common->base); - /* enable temperature comparation */ - rcar_thermal_common_write(common, ENR, 0x00030303); - idle = 0; /* polling delay is not needed */ } @@ -452,8 +450,15 @@ static int rcar_thermal_probe(struct platform_device *pdev) rcar_thermal_irq_enable(priv); list_move_tail(&priv->list, &common->head); + + /* update ENR bits */ + enr_bits |= 3 << (i * 8); } + /* enable temperature comparation */ + if (irq) + rcar_thermal_common_write(common, ENR, enr_bits); + platform_set_drvdata(pdev, common); dev_info(dev, "%d sensor probed\n", i); diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 87e0b0782023..48491d1a81d6 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -1759,11 +1759,7 @@ int thermal_generate_netlink_event(struct thermal_zone_device *tz, thermal_event->event = event; /* send multicast genetlink message */ - result = genlmsg_end(skb, msg_header); - if (result < 0) { - nlmsg_free(skb); - return result; - } + genlmsg_end(skb, msg_header); result = genlmsg_multicast(&thermal_event_genl_family, skb, 0, 0, GFP_ATOMIC); diff --git a/drivers/thermal/thermal_core.h b/drivers/thermal/thermal_core.h index 9083e7520623..0531c752fbbb 100644 --- a/drivers/thermal/thermal_core.h +++ b/drivers/thermal/thermal_core.h @@ -91,7 +91,7 @@ int of_parse_thermal_zones(void); void of_thermal_destroy_zones(void); int of_thermal_get_ntrips(struct thermal_zone_device *); bool of_thermal_is_trip_valid(struct thermal_zone_device *, int); -const struct thermal_trip * const +const struct thermal_trip * of_thermal_get_trip_points(struct thermal_zone_device *); #else static inline int of_parse_thermal_zones(void) { return 0; } @@ -105,7 +105,7 @@ static inline bool of_thermal_is_trip_valid(struct thermal_zone_device *tz, { return 0; } -static inline const struct thermal_trip * const +static inline const struct thermal_trip * of_thermal_get_trip_points(struct thermal_zone_device *tz) { return NULL; diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index d2b496750d59..4ddfa60c9222 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -2399,17 +2399,12 @@ static unsigned int n_tty_poll(struct tty_struct *tty, struct file *file, poll_wait(file, &tty->read_wait, wait); poll_wait(file, &tty->write_wait, wait); - if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) - mask |= POLLHUP; if (input_available_p(tty, 1)) mask |= POLLIN | POLLRDNORM; - else if (mask & POLLHUP) { - tty_flush_to_ldisc(tty); - if (input_available_p(tty, 1)) - mask |= POLLIN | POLLRDNORM; - } if (tty->packet && tty->link->ctrl_status) mask |= POLLPRI | POLLIN | POLLRDNORM; + if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) + mask |= POLLHUP; if (tty_hung_up_p(file)) mask |= POLLHUP; if (!(mask & (POLLHUP | POLLIN | POLLRDNORM))) { diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c index 31feeb2d0a66..d1f8dc6aabcb 100644 --- a/drivers/tty/serial/8250/8250_pci.c +++ b/drivers/tty/serial/8250/8250_pci.c @@ -1815,7 +1815,7 @@ pci_wch_ch353_setup(struct serial_private *priv, } static int -pci_wch_ch382_setup(struct serial_private *priv, +pci_wch_ch38x_setup(struct serial_private *priv, const struct pciserial_board *board, struct uart_8250_port *port, int idx) { @@ -1880,6 +1880,7 @@ pci_wch_ch382_setup(struct serial_private *priv, #define PCIE_VENDOR_ID_WCH 0x1c00 #define PCIE_DEVICE_ID_WCH_CH382_2S1P 0x3250 +#define PCIE_DEVICE_ID_WCH_CH384_4S 0x3470 /* Unknown vendors/cards - this should not be in linux/pci_ids.h */ #define PCI_SUBDEVICE_ID_UNKNOWN_0x1584 0x1584 @@ -2571,13 +2572,21 @@ static struct pci_serial_quirk pci_serial_quirks[] __refdata = { .subdevice = PCI_ANY_ID, .setup = pci_wch_ch353_setup, }, - /* WCH CH382 2S1P card (16750 clone) */ + /* WCH CH382 2S1P card (16850 clone) */ { .vendor = PCIE_VENDOR_ID_WCH, .device = PCIE_DEVICE_ID_WCH_CH382_2S1P, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .setup = pci_wch_ch382_setup, + .setup = pci_wch_ch38x_setup, + }, + /* WCH CH384 4S card (16850 clone) */ + { + .vendor = PCIE_VENDOR_ID_WCH, + .device = PCIE_DEVICE_ID_WCH_CH384_4S, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .setup = pci_wch_ch38x_setup, }, /* * ASIX devices with FIFO bug @@ -2876,6 +2885,7 @@ enum pci_board_num_t { pbn_fintek_4, pbn_fintek_8, pbn_fintek_12, + pbn_wch384_4, }; /* @@ -3675,6 +3685,14 @@ static struct pciserial_board pci_boards[] = { .base_baud = 115200, .first_offset = 0x40, }, + + [pbn_wch384_4] = { + .flags = FL_BASE0, + .num_ports = 4, + .base_baud = 115200, + .uart_offset = 8, + .first_offset = 0xC0, + }, }; static const struct pci_device_id blacklist[] = { @@ -3687,6 +3705,7 @@ static const struct pci_device_id blacklist[] = { { PCI_DEVICE(0x4348, 0x7053), }, /* WCH CH353 2S1P */ { PCI_DEVICE(0x4348, 0x5053), }, /* WCH CH353 1S1P */ { PCI_DEVICE(0x1c00, 0x3250), }, /* WCH CH382 2S1P */ + { PCI_DEVICE(0x1c00, 0x3470), }, /* WCH CH384 4S */ }; /* @@ -5400,6 +5419,10 @@ static struct pci_device_id serial_pci_tbl[] = { PCI_ANY_ID, PCI_ANY_ID, 0, 0, pbn_b0_bt_2_115200 }, + { PCIE_VENDOR_ID_WCH, PCIE_DEVICE_ID_WCH_CH384_4S, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, pbn_wch384_4 }, + /* * Commtech, Inc. Fastcom adapters */ diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c index 19273e31d224..107e80722575 100644 --- a/drivers/tty/serial/samsung.c +++ b/drivers/tty/serial/samsung.c @@ -1757,32 +1757,43 @@ static struct s3c24xx_serial_drv_data s5pv210_serial_drv_data = { #endif #if defined(CONFIG_ARCH_EXYNOS) +#define EXYNOS_COMMON_SERIAL_DRV_DATA \ + .info = &(struct s3c24xx_uart_info) { \ + .name = "Samsung Exynos UART", \ + .type = PORT_S3C6400, \ + .has_divslot = 1, \ + .rx_fifomask = S5PV210_UFSTAT_RXMASK, \ + .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT, \ + .rx_fifofull = S5PV210_UFSTAT_RXFULL, \ + .tx_fifofull = S5PV210_UFSTAT_TXFULL, \ + .tx_fifomask = S5PV210_UFSTAT_TXMASK, \ + .tx_fifoshift = S5PV210_UFSTAT_TXSHIFT, \ + .def_clk_sel = S3C2410_UCON_CLKSEL0, \ + .num_clks = 1, \ + .clksel_mask = 0, \ + .clksel_shift = 0, \ + }, \ + .def_cfg = &(struct s3c2410_uartcfg) { \ + .ucon = S5PV210_UCON_DEFAULT, \ + .ufcon = S5PV210_UFCON_DEFAULT, \ + .has_fracval = 1, \ + } \ + static struct s3c24xx_serial_drv_data exynos4210_serial_drv_data = { - .info = &(struct s3c24xx_uart_info) { - .name = "Samsung Exynos4 UART", - .type = PORT_S3C6400, - .has_divslot = 1, - .rx_fifomask = S5PV210_UFSTAT_RXMASK, - .rx_fifoshift = S5PV210_UFSTAT_RXSHIFT, - .rx_fifofull = S5PV210_UFSTAT_RXFULL, - .tx_fifofull = S5PV210_UFSTAT_TXFULL, - .tx_fifomask = S5PV210_UFSTAT_TXMASK, - .tx_fifoshift = S5PV210_UFSTAT_TXSHIFT, - .def_clk_sel = S3C2410_UCON_CLKSEL0, - .num_clks = 1, - .clksel_mask = 0, - .clksel_shift = 0, - }, - .def_cfg = &(struct s3c2410_uartcfg) { - .ucon = S5PV210_UCON_DEFAULT, - .ufcon = S5PV210_UFCON_DEFAULT, - .has_fracval = 1, - }, + EXYNOS_COMMON_SERIAL_DRV_DATA, .fifosize = { 256, 64, 16, 16 }, }; + +static struct s3c24xx_serial_drv_data exynos5433_serial_drv_data = { + EXYNOS_COMMON_SERIAL_DRV_DATA, + .fifosize = { 64, 256, 16, 256 }, +}; + #define EXYNOS4210_SERIAL_DRV_DATA ((kernel_ulong_t)&exynos4210_serial_drv_data) +#define EXYNOS5433_SERIAL_DRV_DATA ((kernel_ulong_t)&exynos5433_serial_drv_data) #else #define EXYNOS4210_SERIAL_DRV_DATA (kernel_ulong_t)NULL +#define EXYNOS5433_SERIAL_DRV_DATA (kernel_ulong_t)NULL #endif static struct platform_device_id s3c24xx_serial_driver_ids[] = { @@ -1804,6 +1815,9 @@ static struct platform_device_id s3c24xx_serial_driver_ids[] = { }, { .name = "exynos4210-uart", .driver_data = EXYNOS4210_SERIAL_DRV_DATA, + }, { + .name = "exynos5433-uart", + .driver_data = EXYNOS5433_SERIAL_DRV_DATA, }, { }, }; @@ -1823,6 +1837,8 @@ static const struct of_device_id s3c24xx_uart_dt_match[] = { .data = (void *)S5PV210_SERIAL_DRV_DATA }, { .compatible = "samsung,exynos4210-uart", .data = (void *)EXYNOS4210_SERIAL_DRV_DATA }, + { .compatible = "samsung,exynos5433-uart", + .data = (void *)EXYNOS5433_SERIAL_DRV_DATA }, {}, }; MODULE_DEVICE_TABLE(of, s3c24xx_uart_dt_match); diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 57ca61b14670..984605bb5bf1 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -2164,7 +2164,9 @@ uart_report_port(struct uart_driver *drv, struct uart_port *port) break; } - dev_info(port->dev, "%s%d at %s (irq = %d, base_baud = %d) is a %s\n", + printk(KERN_INFO "%s%s%s%d at %s (irq = %d, base_baud = %d) is a %s\n", + port->dev ? dev_name(port->dev) : "", + port->dev ? ": " : "", drv->dev_name, drv->tty_driver->name_base + port->line, address, port->irq, port->uartclk / 16, uart_type(port)); diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 4f35b43e2475..51f066aa375e 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -1464,6 +1464,9 @@ static int tty_reopen(struct tty_struct *tty) driver->subtype == PTY_TYPE_MASTER) return -EIO; + if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN)) + return -EBUSY; + tty->count++; WARN_ON(!tty->ldisc); @@ -2106,10 +2109,6 @@ retry_open: retval = -ENODEV; filp->f_flags = saved_flags; - if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && - !capable(CAP_SYS_ADMIN)) - retval = -EBUSY; - if (retval) { #ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "%s: error %d in opening %s...\n", __func__, diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 5b9825a4538a..a57dc8866fc5 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -669,7 +669,6 @@ static int ci_hdrc_probe(struct platform_device *pdev) if (!ci) return -ENOMEM; - platform_set_drvdata(pdev, ci); ci->dev = dev; ci->platdata = dev_get_platdata(dev); ci->imx28_write_fix = !!(ci->platdata->flags & @@ -783,6 +782,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) } } + platform_set_drvdata(pdev, ci); ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED, ci->platdata->name, ci); if (ret) diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index c1694cff1eaf..48731d0bab35 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -91,6 +91,7 @@ static int host_start(struct ci_hdrc *ci) if (!hcd) return -ENOMEM; + dev_set_drvdata(ci->dev, ci); hcd->rsrc_start = ci->hw_bank.phys; hcd->rsrc_len = ci->hw_bank.size; hcd->regs = ci->hw_bank.abs; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index aeb50bb6ba9c..eaffb0248de1 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -3452,8 +3452,6 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) return status; } -#ifdef CONFIG_PM - int usb_remote_wakeup(struct usb_device *udev) { int status = 0; @@ -3512,16 +3510,6 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, return connect_change; } -#else - -static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, - u16 portstatus, u16 portchange) -{ - return 0; -} - -#endif - static int check_ports_changed(struct usb_hub *hub) { int port1; diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_whitelist.h index de0c9c9d7091..a6315abe7b7c 100644 --- a/drivers/usb/core/otg_whitelist.h +++ b/drivers/usb/core/otg_whitelist.h @@ -55,6 +55,11 @@ static int is_targeted(struct usb_device *dev) le16_to_cpu(dev->descriptor.idProduct) == 0xbadd)) return 0; + /* OTG PET device is always targeted (see OTG 2.0 ECN 6.4.2) */ + if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1a0a && + le16_to_cpu(dev->descriptor.idProduct) == 0x0200)) + return 1; + /* NOTE: can't use usb_match_id() since interface caches * aren't set up yet. this is cut/paste from that code. */ diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 0ffb4ed0a945..41e510ae8c83 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -179,6 +179,10 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x0b05, 0x17e0), .driver_info = USB_QUIRK_IGNORE_REMOTE_WAKEUP }, + /* Protocol and OTG Electrical Test Device */ + { USB_DEVICE(0x1a0a, 0x0200), .driver_info = + USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL }, + { } /* terminating entry must be last */ }; diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index ad43c5bc1ef1..02e3e2d4ea56 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -476,13 +476,13 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev) u32 gintsts; irqreturn_t retval = IRQ_NONE; + spin_lock(&hsotg->lock); + if (!dwc2_is_controller_alive(hsotg)) { dev_warn(hsotg->dev, "Controller is dead\n"); goto out; } - spin_lock(&hsotg->lock); - gintsts = dwc2_read_common_intr(hsotg); if (gintsts & ~GINTSTS_PRTINT) retval = IRQ_HANDLED; @@ -515,8 +515,8 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev) } } - spin_unlock(&hsotg->lock); out: + spin_unlock(&hsotg->lock); return retval; } EXPORT_SYMBOL_GPL(dwc2_handle_common_intr); diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 200168ec2d75..79242008085b 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2567,7 +2567,7 @@ error: * s3c_hsotg_ep_disable - disable given endpoint * @ep: The endpoint to disable. */ -static int s3c_hsotg_ep_disable(struct usb_ep *ep) +static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force) { struct s3c_hsotg_ep *hs_ep = our_ep(ep); struct dwc2_hsotg *hsotg = hs_ep->parent; @@ -2588,7 +2588,7 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep) spin_lock_irqsave(&hsotg->lock, flags); /* terminate all requests with shutdown */ - kill_all_requests(hsotg, hs_ep, -ESHUTDOWN, false); + kill_all_requests(hsotg, hs_ep, -ESHUTDOWN, force); hsotg->fifo_map &= ~(1<<hs_ep->fifo_index); hs_ep->fifo_index = 0; @@ -2609,6 +2609,10 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep) return 0; } +static int s3c_hsotg_ep_disable(struct usb_ep *ep) +{ + return s3c_hsotg_ep_disable_force(ep, false); +} /** * on_list - check request is on the given endpoint * @ep: The endpoint to check. @@ -2924,7 +2928,7 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget) /* all endpoints should be shutdown */ for (ep = 1; ep < hsotg->num_of_eps; ep++) - s3c_hsotg_ep_disable(&hsotg->eps[ep].ep); + s3c_hsotg_ep_disable_force(&hsotg->eps[ep].ep, true); spin_lock_irqsave(&hsotg->lock, flags); diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 7c4faf738747..b642a2f998f9 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -33,6 +33,8 @@ #define PCI_DEVICE_ID_INTEL_BYT 0x0f37 #define PCI_DEVICE_ID_INTEL_MRFLD 0x119e #define PCI_DEVICE_ID_INTEL_BSW 0x22B7 +#define PCI_DEVICE_ID_INTEL_SPTLP 0x9d30 +#define PCI_DEVICE_ID_INTEL_SPTH 0xa130 struct dwc3_pci { struct device *dev; @@ -219,6 +221,8 @@ static const struct pci_device_id dwc3_pci_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BSW), }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT), }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MRFLD), }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SPTLP), }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SPTH), }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB), }, { } /* Terminating Entry */ }; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f03b136ecfce..8f65ab3a3b92 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -882,8 +882,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting) if (i == (request->num_mapped_sgs - 1) || sg_is_last(s)) { - if (list_is_last(&req->list, - &dep->request_list)) + if (list_empty(&dep->request_list)) last_one = true; chain = false; } @@ -901,6 +900,9 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting) if (last_one) break; } + + if (last_one) + break; } else { dma = req->request.dma; length = req->request.length; diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 6e04e302dc3a..a1bc3e3a0b09 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -399,8 +399,9 @@ static int hidg_setup(struct usb_function *f, value = __le16_to_cpu(ctrl->wValue); length = __le16_to_cpu(ctrl->wLength); - VDBG(cdev, "hid_setup crtl_request : bRequestType:0x%x bRequest:0x%x " - "Value:0x%x\n", ctrl->bRequestType, ctrl->bRequest, value); + VDBG(cdev, + "%s crtl_request : bRequestType:0x%x bRequest:0x%x Value:0x%x\n", + __func__, ctrl->bRequestType, ctrl->bRequest, value); switch ((ctrl->bRequestType << 8) | ctrl->bRequest) { case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8 diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index a90440300735..259b656c0b3e 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -520,7 +520,7 @@ static void f_midi_transmit(struct f_midi *midi, struct usb_request *req) req = midi_alloc_ep_req(ep, midi->buflen); if (!req) { - ERROR(midi, "gmidi_transmit: alloc_ep_request failed\n"); + ERROR(midi, "%s: alloc_ep_request failed\n", __func__); return; } req->length = 0; diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index f7b203293205..e9715845f82e 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -897,7 +897,6 @@ static void f_audio_free_inst(struct usb_function_instance *f) struct f_uac1_opts *opts; opts = container_of(f, struct f_uac1_opts, func_inst); - gaudio_cleanup(opts->card); if (opts->fn_play_alloc) kfree(opts->fn_play); if (opts->fn_cap_alloc) @@ -935,6 +934,7 @@ static void f_audio_free(struct usb_function *f) struct f_audio *audio = func_to_audio(f); struct f_uac1_opts *opts; + gaudio_cleanup(&audio->card); opts = container_of(f->fi, struct f_uac1_opts, func_inst); kfree(audio); mutex_lock(&opts->lock); diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index c744e4975d74..db49ec4c748e 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -441,6 +441,7 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) kbuf = memdup_user(buf, len); if (IS_ERR(kbuf)) { value = PTR_ERR(kbuf); + kbuf = NULL; goto free1; } @@ -449,6 +450,7 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr) data->name, len, (int) value); free1: mutex_unlock(&data->lock); + kfree (kbuf); return value; } diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index ce882371786b..9f93bed42052 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -716,10 +716,10 @@ static int queue_dma(struct usba_udc *udc, struct usba_ep *ep, req->using_dma = 1; req->ctrl = USBA_BF(DMA_BUF_LEN, req->req.length) | USBA_DMA_CH_EN | USBA_DMA_END_BUF_IE - | USBA_DMA_END_TR_EN | USBA_DMA_END_TR_IE; + | USBA_DMA_END_BUF_EN; - if (ep->is_in) - req->ctrl |= USBA_DMA_END_BUF_EN; + if (!ep->is_in) + req->ctrl |= USBA_DMA_END_TR_EN | USBA_DMA_END_TR_IE; /* * Add this request to the queue and submit for DMA if @@ -828,7 +828,7 @@ static int usba_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) { struct usba_ep *ep = to_usba_ep(_ep); struct usba_udc *udc = ep->udc; - struct usba_request *req = to_usba_req(_req); + struct usba_request *req; unsigned long flags; u32 status; @@ -837,6 +837,16 @@ static int usba_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) spin_lock_irqsave(&udc->lock, flags); + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + + if (&req->req != _req) { + spin_unlock_irqrestore(&udc->lock, flags); + return -EINVAL; + } + if (req->using_dma) { /* * If this request is currently being transferred, @@ -1563,7 +1573,6 @@ static void usba_ep_irq(struct usba_udc *udc, struct usba_ep *ep) if ((epstatus & epctrl) & USBA_RX_BK_RDY) { DBG(DBG_BUS, "%s: RX data ready\n", ep->ep.name); receive_data(ep); - usba_ep_writel(ep, CLR_STA, USBA_RX_BK_RDY); } } diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c index ff67ceac77c4..d4fe8d769bd6 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_ep.c +++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c @@ -718,10 +718,11 @@ static int ep_queue(struct bdc_ep *ep, struct bdc_req *req) struct bdc *bdc; int ret = 0; - bdc = ep->bdc; if (!req || !ep || !ep->usb_ep.desc) return -EINVAL; + bdc = ep->bdc; + req->usb_req.actual = 0; req->usb_req.status = -EINPROGRESS; req->epnum = ep->ep_num; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index e113fd73aeae..f9a332775c47 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1581,6 +1581,10 @@ iso_stream_schedule ( else next = (now + 2 + 7) & ~0x07; /* full frame cache */ + /* If needed, initialize last_iso_frame so that this URB will be seen */ + if (ehci->isoc_count == 0) + ehci->last_iso_frame = now >> 3; + /* * Use ehci->last_iso_frame as the base. There can't be any * TDs scheduled for earlier than that. @@ -1600,11 +1604,11 @@ iso_stream_schedule ( */ now2 = (now - base) & (mod - 1); - /* Is the schedule already full? */ + /* Is the schedule about to wrap around? */ if (unlikely(!empty && start < period)) { - ehci_dbg(ehci, "iso sched full %p (%u-%u < %u mod %u)\n", + ehci_dbg(ehci, "request %p would overflow (%u-%u < %u mod %u)\n", urb, stream->next_uframe, base, period, mod); - status = -ENOSPC; + status = -EFBIG; goto fail; } @@ -1671,10 +1675,6 @@ iso_stream_schedule ( urb->start_frame = start & (mod - 1); if (!stream->highspeed) urb->start_frame >>= 3; - - /* Make sure scan_isoc() sees these */ - if (ehci->isoc_count == 0) - ehci->last_iso_frame = now >> 3; return status; fail: diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 19a9af1b4d74..ff9af29b4e9f 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -451,7 +451,7 @@ static int tegra_ehci_probe(struct platform_device *pdev) u_phy = devm_usb_get_phy_by_phandle(&pdev->dev, "nvidia,phy", 0); if (IS_ERR(u_phy)) { - err = PTR_ERR(u_phy); + err = -EPROBE_DEFER; goto cleanup_clk_en; } hcd->usb_phy = u_phy; diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index dd483c13565b..ce636466edb7 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -567,7 +567,8 @@ static void quirk_usb_handoff_ohci(struct pci_dev *pdev) { void __iomem *base; u32 control; - u32 fminterval; + u32 fminterval = 0; + bool no_fminterval = false; int cnt; if (!mmio_resource_enabled(pdev, 0)) @@ -577,6 +578,13 @@ static void quirk_usb_handoff_ohci(struct pci_dev *pdev) if (base == NULL) return; + /* + * ULi M5237 OHCI controller locks the whole system when accessing + * the OHCI_FMINTERVAL offset. + */ + if (pdev->vendor == PCI_VENDOR_ID_AL && pdev->device == 0x5237) + no_fminterval = true; + control = readl(base + OHCI_CONTROL); /* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */ @@ -615,7 +623,9 @@ static void quirk_usb_handoff_ohci(struct pci_dev *pdev) } /* software reset of the controller, preserving HcFmInterval */ - fminterval = readl(base + OHCI_FMINTERVAL); + if (!no_fminterval) + fminterval = readl(base + OHCI_FMINTERVAL); + writel(OHCI_HCR, base + OHCI_CMDSTATUS); /* reset requires max 10 us delay */ @@ -624,7 +634,9 @@ static void quirk_usb_handoff_ohci(struct pci_dev *pdev) break; udelay(1); } - writel(fminterval, base + OHCI_FMINTERVAL); + + if (!no_fminterval) + writel(fminterval, base + OHCI_FMINTERVAL); /* Now the controller is safely in SUSPEND and nothing can wake it up */ iounmap(base); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 142b601f9563..7f76c8a12f89 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -82,6 +82,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) "must be suspended extra slowly", pdev->revision); } + if (pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_PDK) + xhci->quirks |= XHCI_BROKEN_STREAMS; /* Fresco Logic confirms: all revisions of this chip do not * support MSI, even though some of them claim to in their PCI * capabilities. diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 01fcbb5eb06e..c50d8d202618 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3803,6 +3803,15 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, return -EINVAL; } + if (setup == SETUP_CONTEXT_ONLY) { + slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx); + if (GET_SLOT_STATE(le32_to_cpu(slot_ctx->dev_state)) == + SLOT_STATE_DEFAULT) { + xhci_dbg(xhci, "Slot already in default state\n"); + return 0; + } + } + command = xhci_alloc_command(xhci, false, false, GFP_KERNEL); if (!command) return -ENOMEM; diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 9d68372dd9aa..b005010240e5 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -72,6 +72,8 @@ config USB_MUSB_DA8XX config USB_MUSB_TUSB6010 tristate "TUSB6010" + depends on ARCH_OMAP2PLUS || COMPILE_TEST + depends on NOP_USB_XCEIV = USB_MUSB_HDRC # both built-in or both modules config USB_MUSB_OMAP2PLUS tristate "OMAP2430 and onwards" @@ -85,6 +87,7 @@ config USB_MUSB_AM35X config USB_MUSB_DSPS tristate "TI DSPS platforms" select USB_MUSB_AM335X_CHILD + depends on ARCH_OMAP2PLUS || COMPILE_TEST depends on OF_IRQ config USB_MUSB_BLACKFIN @@ -93,6 +96,7 @@ config USB_MUSB_BLACKFIN config USB_MUSB_UX500 tristate "Ux500 platforms" + depends on ARCH_U8500 || COMPILE_TEST config USB_MUSB_JZ4740 tristate "JZ4740" diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index a441a2de8619..178250145613 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -63,7 +63,7 @@ static void bfin_writew(void __iomem *addr, unsigned offset, u16 data) bfin_write16(addr + offset, data); } -static void binf_writel(void __iomem *addr, unsigned offset, u32 data) +static void bfin_writel(void __iomem *addr, unsigned offset, u32 data) { bfin_write16(addr + offset, (u16)data); } diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index f64fd964dc6d..c39a16ad7832 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -628,9 +628,9 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller) ret = of_property_read_string_index(np, "dma-names", i, &str); if (ret) goto err; - if (!strncmp(str, "tx", 2)) + if (strstarts(str, "tx")) is_tx = 1; - else if (!strncmp(str, "rx", 2)) + else if (strstarts(str, "rx")) is_tx = 0; else { dev_err(dev, "Wrong dmatype %s\n", str); diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c index ad3701a97389..48131aa8472c 100644 --- a/drivers/usb/musb/musb_debugfs.c +++ b/drivers/usb/musb/musb_debugfs.c @@ -59,20 +59,12 @@ static const struct musb_register_map musb_regmap[] = { { "RxMaxPp", MUSB_RXMAXP, 16 }, { "RxCSR", MUSB_RXCSR, 16 }, { "RxCount", MUSB_RXCOUNT, 16 }, - { "ConfigData", MUSB_CONFIGDATA,8 }, { "IntrRxE", MUSB_INTRRXE, 16 }, { "IntrTxE", MUSB_INTRTXE, 16 }, { "IntrUsbE", MUSB_INTRUSBE, 8 }, { "DevCtl", MUSB_DEVCTL, 8 }, - { "BabbleCtl", MUSB_BABBLE_CTL,8 }, - { "TxFIFOsz", MUSB_TXFIFOSZ, 8 }, - { "RxFIFOsz", MUSB_RXFIFOSZ, 8 }, - { "TxFIFOadd", MUSB_TXFIFOADD, 16 }, - { "RxFIFOadd", MUSB_RXFIFOADD, 16 }, { "VControl", 0x68, 32 }, { "HWVers", 0x69, 16 }, - { "EPInfo", MUSB_EPINFO, 8 }, - { "RAMInfo", MUSB_RAMINFO, 8 }, { "LinkInfo", MUSB_LINKINFO, 8 }, { "VPLen", MUSB_VPLEN, 8 }, { "HS_EOF1", MUSB_HS_EOF1, 8 }, @@ -103,6 +95,16 @@ static const struct musb_register_map musb_regmap[] = { { "DMA_CNTLch7", 0x274, 16 }, { "DMA_ADDRch7", 0x278, 32 }, { "DMA_COUNTch7", 0x27C, 32 }, +#ifndef CONFIG_BLACKFIN + { "ConfigData", MUSB_CONFIGDATA,8 }, + { "BabbleCtl", MUSB_BABBLE_CTL,8 }, + { "TxFIFOsz", MUSB_TXFIFOSZ, 8 }, + { "RxFIFOsz", MUSB_RXFIFOSZ, 8 }, + { "TxFIFOadd", MUSB_TXFIFOADD, 16 }, + { "RxFIFOadd", MUSB_RXFIFOADD, 16 }, + { "EPInfo", MUSB_EPINFO, 8 }, + { "RAMInfo", MUSB_RAMINFO, 8 }, +#endif { } /* Terminating Entry */ }; @@ -197,30 +199,30 @@ static ssize_t musb_test_mode_write(struct file *file, if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) return -EFAULT; - if (!strncmp(buf, "force host", 9)) + if (strstarts(buf, "force host")) test = MUSB_TEST_FORCE_HOST; - if (!strncmp(buf, "fifo access", 11)) + if (strstarts(buf, "fifo access")) test = MUSB_TEST_FIFO_ACCESS; - if (!strncmp(buf, "force full-speed", 15)) + if (strstarts(buf, "force full-speed")) test = MUSB_TEST_FORCE_FS; - if (!strncmp(buf, "force high-speed", 15)) + if (strstarts(buf, "force high-speed")) test = MUSB_TEST_FORCE_HS; - if (!strncmp(buf, "test packet", 10)) { + if (strstarts(buf, "test packet")) { test = MUSB_TEST_PACKET; musb_load_testpacket(musb); } - if (!strncmp(buf, "test K", 6)) + if (strstarts(buf, "test K")) test = MUSB_TEST_K; - if (!strncmp(buf, "test J", 6)) + if (strstarts(buf, "test J")) test = MUSB_TEST_J; - if (!strncmp(buf, "test SE0 NAK", 12)) + if (strstarts(buf, "test SE0 NAK")) test = MUSB_TEST_SE0_NAK; musb_writeb(musb->mregs, MUSB_TESTMODE, test); diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 23d474d3d7f4..883a9adfdfff 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -2663,7 +2663,6 @@ void musb_host_cleanup(struct musb *musb) if (musb->port_mode == MUSB_PORT_MODE_GADGET) return; usb_remove_hcd(musb->hcd); - musb->hcd = NULL; } void musb_host_free(struct musb *musb) diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c index 699e38c73d82..697a741a0cb1 100644 --- a/drivers/usb/phy/phy-mv-usb.c +++ b/drivers/usb/phy/phy-mv-usb.c @@ -338,7 +338,6 @@ static void mv_otg_update_inputs(struct mv_otg *mvotg) static void mv_otg_update_state(struct mv_otg *mvotg) { struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl; - struct usb_phy *phy = &mvotg->phy; int old_state = mvotg->phy.otg->state; switch (old_state) { @@ -858,10 +857,10 @@ static int mv_otg_suspend(struct platform_device *pdev, pm_message_t state) { struct mv_otg *mvotg = platform_get_drvdata(pdev); - if (mvotg->phy.state != OTG_STATE_B_IDLE) { + if (mvotg->phy.otg->state != OTG_STATE_B_IDLE) { dev_info(&pdev->dev, "OTG state is not B_IDLE, it is %d!\n", - mvotg->phy.state); + mvotg->phy.otg->state); return -EAGAIN; } diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index b4066a001ba0..2f9735b35338 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -59,6 +59,9 @@ static struct usb_phy *__of_usb_find_phy(struct device_node *node) { struct usb_phy *phy; + if (!of_device_is_available(node)) + return ERR_PTR(-ENODEV); + list_for_each_entry(phy, &phy_list, head) { if (node != phy->dev->of_node) continue; @@ -66,7 +69,7 @@ static struct usb_phy *__of_usb_find_phy(struct device_node *node) return phy; } - return ERR_PTR(-ENODEV); + return ERR_PTR(-EPROBE_DEFER); } static void devm_usb_phy_release(struct device *dev, void *res) @@ -190,10 +193,13 @@ struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, spin_lock_irqsave(&phy_lock, flags); phy = __of_usb_find_phy(node); - if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) { - if (!IS_ERR(phy)) - phy = ERR_PTR(-EPROBE_DEFER); + if (IS_ERR(phy)) { + devres_free(ptr); + goto err1; + } + if (!try_module_get(phy->dev->driver->owner)) { + phy = ERR_PTR(-ENODEV); devres_free(ptr); goto err1; } diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c index 8d7fc48b1f30..29fa1c3d0089 100644 --- a/drivers/usb/serial/console.c +++ b/drivers/usb/serial/console.c @@ -46,6 +46,8 @@ static struct console usbcons; * ------------------------------------------------------------ */ +static const struct tty_operations usb_console_fake_tty_ops = { +}; /* * The parsing of the command line works exactly like the @@ -137,13 +139,17 @@ static int usb_console_setup(struct console *co, char *options) goto reset_open_count; } kref_init(&tty->kref); - tty_port_tty_set(&port->port, tty); tty->driver = usb_serial_tty_driver; tty->index = co->index; + init_ldsem(&tty->ldisc_sem); + INIT_LIST_HEAD(&tty->tty_files); + kref_get(&tty->driver->kref); + tty->ops = &usb_console_fake_tty_ops; if (tty_init_termios(tty)) { retval = -ENOMEM; - goto free_tty; + goto put_tty; } + tty_port_tty_set(&port->port, tty); } /* only call the device specific open if this @@ -161,7 +167,7 @@ static int usb_console_setup(struct console *co, char *options) serial->type->set_termios(tty, port, &dummy); tty_port_tty_set(&port->port, NULL); - kfree(tty); + tty_kref_put(tty); } set_bit(ASYNCB_INITIALIZED, &port->port.flags); } @@ -177,8 +183,8 @@ static int usb_console_setup(struct console *co, char *options) fail: tty_port_tty_set(&port->port, NULL); - free_tty: - kfree(tty); + put_tty: + tty_kref_put(tty); reset_open_count: port->port.count = 0; usb_autopm_put_interface(serial->interface); diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 6c4eb3cf5efd..f4c56fc1a9f6 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -120,10 +120,12 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */ { USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */ { USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */ - { USB_DEVICE(0x10C4, 0x8875) }, /* CEL MeshConnect USB Stick */ + { USB_DEVICE(0x10C4, 0x8856) }, /* CEL EM357 ZigBee USB Stick - LR */ + { USB_DEVICE(0x10C4, 0x8857) }, /* CEL EM357 ZigBee USB Stick */ { USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */ { USB_DEVICE(0x10C4, 0x88A5) }, /* Planet Innovation Ingeni ZigBee USB Device */ { USB_DEVICE(0x10C4, 0x8946) }, /* Ketra N1 Wireless Interface */ + { USB_DEVICE(0x10C4, 0x8977) }, /* CEL MeshWorks DevKit Device */ { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA70) }, /* Silicon Labs factory default */ diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 1bd192290b08..ccf1df7c4b80 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -286,7 +286,7 @@ static int usb_serial_generic_submit_read_urb(struct usb_serial_port *port, res = usb_submit_urb(port->read_urbs[index], mem_flags); if (res) { - if (res != -EPERM) { + if (res != -EPERM && res != -ENODEV) { dev_err(&port->dev, "%s - usb_submit_urb failed: %d\n", __func__, res); @@ -373,7 +373,7 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb) __func__, urb->status); return; default: - dev_err(&port->dev, "%s - nonzero urb status: %d\n", + dev_dbg(&port->dev, "%s - nonzero urb status: %d\n", __func__, urb->status); goto resubmit; } diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 077c714f1285..e07b15ed5814 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -410,6 +410,8 @@ static void usa26_instat_callback(struct urb *urb) } port = serial->port[msg->port]; p_priv = usb_get_serial_port_data(port); + if (!p_priv) + goto resubmit; /* Update handshaking pin state information */ old_dcd_state = p_priv->dcd_state; @@ -420,7 +422,7 @@ static void usa26_instat_callback(struct urb *urb) if (old_dcd_state != p_priv->dcd_state) tty_port_tty_hangup(&port->port, true); - +resubmit: /* Resubmit urb so we continue receiving */ err = usb_submit_urb(urb, GFP_ATOMIC); if (err != 0) @@ -527,6 +529,8 @@ static void usa28_instat_callback(struct urb *urb) } port = serial->port[msg->port]; p_priv = usb_get_serial_port_data(port); + if (!p_priv) + goto resubmit; /* Update handshaking pin state information */ old_dcd_state = p_priv->dcd_state; @@ -537,7 +541,7 @@ static void usa28_instat_callback(struct urb *urb) if (old_dcd_state != p_priv->dcd_state && old_dcd_state) tty_port_tty_hangup(&port->port, true); - +resubmit: /* Resubmit urb so we continue receiving */ err = usb_submit_urb(urb, GFP_ATOMIC); if (err != 0) @@ -607,6 +611,8 @@ static void usa49_instat_callback(struct urb *urb) } port = serial->port[msg->portNumber]; p_priv = usb_get_serial_port_data(port); + if (!p_priv) + goto resubmit; /* Update handshaking pin state information */ old_dcd_state = p_priv->dcd_state; @@ -617,7 +623,7 @@ static void usa49_instat_callback(struct urb *urb) if (old_dcd_state != p_priv->dcd_state && old_dcd_state) tty_port_tty_hangup(&port->port, true); - +resubmit: /* Resubmit urb so we continue receiving */ err = usb_submit_urb(urb, GFP_ATOMIC); if (err != 0) @@ -855,6 +861,8 @@ static void usa90_instat_callback(struct urb *urb) port = serial->port[0]; p_priv = usb_get_serial_port_data(port); + if (!p_priv) + goto resubmit; /* Update handshaking pin state information */ old_dcd_state = p_priv->dcd_state; @@ -865,7 +873,7 @@ static void usa90_instat_callback(struct urb *urb) if (old_dcd_state != p_priv->dcd_state && old_dcd_state) tty_port_tty_hangup(&port->port, true); - +resubmit: /* Resubmit urb so we continue receiving */ err = usb_submit_urb(urb, GFP_ATOMIC); if (err != 0) @@ -926,6 +934,8 @@ static void usa67_instat_callback(struct urb *urb) port = serial->port[msg->port]; p_priv = usb_get_serial_port_data(port); + if (!p_priv) + goto resubmit; /* Update handshaking pin state information */ old_dcd_state = p_priv->dcd_state; @@ -934,7 +944,7 @@ static void usa67_instat_callback(struct urb *urb) if (old_dcd_state != p_priv->dcd_state && old_dcd_state) tty_port_tty_hangup(&port->port, true); - +resubmit: /* Resubmit urb so we continue receiving */ err = usb_submit_urb(urb, GFP_ATOMIC); if (err != 0) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 7a4c21b4f676..efdcee15b520 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -234,6 +234,8 @@ static void option_instat_callback(struct urb *urb); #define QUALCOMM_VENDOR_ID 0x05C6 +#define SIERRA_VENDOR_ID 0x1199 + #define CMOTECH_VENDOR_ID 0x16d8 #define CMOTECH_PRODUCT_6001 0x6001 #define CMOTECH_PRODUCT_CMU_300 0x6002 @@ -512,7 +514,7 @@ enum option_blacklist_reason { OPTION_BLACKLIST_RESERVED_IF = 2 }; -#define MAX_BL_NUM 8 +#define MAX_BL_NUM 11 struct option_blacklist_info { /* bitfield of interface numbers for OPTION_BLACKLIST_SENDSETUP */ const unsigned long sendsetup; @@ -601,6 +603,11 @@ static const struct option_blacklist_info telit_le920_blacklist = { .reserved = BIT(1) | BIT(5), }; +static const struct option_blacklist_info sierra_mc73xx_blacklist = { + .sendsetup = BIT(0) | BIT(2), + .reserved = BIT(8) | BIT(10) | BIT(11), +}; + static const struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, @@ -1098,6 +1105,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x0023)}, /* ONYX 3G device */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9000)}, /* SIMCom SIM5218 */ + { USB_DEVICE_INTERFACE_CLASS(SIERRA_VENDOR_ID, 0x68c0, 0xff), + .driver_info = (kernel_ulong_t)&sierra_mc73xx_blacklist }, /* MC73xx */ { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_300) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6003), diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index cb3e14780a7e..9c63897b3a56 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -142,7 +142,6 @@ static const struct usb_device_id id_table[] = { {DEVICE_SWI(0x0f3d, 0x68a2)}, /* Sierra Wireless MC7700 */ {DEVICE_SWI(0x114f, 0x68a2)}, /* Sierra Wireless MC7750 */ {DEVICE_SWI(0x1199, 0x68a2)}, /* Sierra Wireless MC7710 */ - {DEVICE_SWI(0x1199, 0x68c0)}, /* Sierra Wireless MC73xx */ {DEVICE_SWI(0x1199, 0x901c)}, /* Sierra Wireless EM7700 */ {DEVICE_SWI(0x1199, 0x901f)}, /* Sierra Wireless EM7355 */ {DEVICE_SWI(0x1199, 0x9040)}, /* Sierra Wireless Modem */ diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h index 8a6f371ed6e7..9893d696fc97 100644 --- a/drivers/usb/storage/uas-detect.h +++ b/drivers/usb/storage/uas-detect.h @@ -69,16 +69,39 @@ static int uas_use_uas_driver(struct usb_interface *intf, return 0; /* - * ASM1051 and older ASM1053 devices have the same usb-id, and UAS is - * broken on the ASM1051, use the number of streams to differentiate. - * New ASM1053-s also support 32 streams, but have a different prod-id. + * ASMedia has a number of usb3 to sata bridge chips, at the time of + * this writing the following versions exist: + * ASM1051 - no uas support version + * ASM1051 - with broken (*) uas support + * ASM1053 - with working uas support + * ASM1153 - with working uas support + * + * Devices with these chips re-use a number of device-ids over the + * entire line, so the device-id is useless to determine if we're + * dealing with an ASM1051 (which we want to avoid). + * + * The ASM1153 can be identified by config.MaxPower == 0, + * where as the ASM105x models have config.MaxPower == 36. + * + * Differentiating between the ASM1053 and ASM1051 is trickier, when + * connected over USB-3 we can look at the number of streams supported, + * ASM1051 supports 32 streams, where as early ASM1053 versions support + * 16 streams, newer ASM1053-s also support 32 streams, but have a + * different prod-id. + * + * (*) ASM1051 chips do work with UAS with some disks (with the + * US_FL_NO_REPORT_OPCODES quirk), but are broken with other disks */ if (le16_to_cpu(udev->descriptor.idVendor) == 0x174c && - le16_to_cpu(udev->descriptor.idProduct) == 0x55aa) { - if (udev->speed < USB_SPEED_SUPER) { + (le16_to_cpu(udev->descriptor.idProduct) == 0x5106 || + le16_to_cpu(udev->descriptor.idProduct) == 0x55aa)) { + if (udev->actconfig->desc.bMaxPower == 0) { + /* ASM1153, do nothing */ + } else if (udev->speed < USB_SPEED_SUPER) { /* No streams info, assume ASM1051 */ flags |= US_FL_IGNORE_UAS; } else if (usb_ss_max_streams(&eps[1]->ss_ep_comp) == 32) { + /* Possibly an ASM1051, disable uas */ flags |= US_FL_IGNORE_UAS; } } diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index 11c7a9676441..d684b4b8108f 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -507,7 +507,7 @@ UNUSUAL_DEV( 0x04e6, 0x000c, 0x0100, 0x0100, UNUSUAL_DEV( 0x04e6, 0x000f, 0x0000, 0x9999, "SCM Microsystems", "eUSB SCSI Adapter (Bus Powered)", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_euscsi_init, + USB_SC_SCSI, USB_PR_BULK, usb_stor_euscsi_init, US_FL_SCM_MULT_TARG ), UNUSUAL_DEV( 0x04e6, 0x0101, 0x0200, 0x0200, @@ -1995,6 +1995,13 @@ UNUSUAL_DEV( 0x152d, 0x2329, 0x0100, 0x0100, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE | US_FL_SANE_SENSE ), +/* Reported by Dmitry Nezhevenko <dion@dion.org.ua> */ +UNUSUAL_DEV( 0x152d, 0x2566, 0x0114, 0x0114, + "JMicron", + "USB to ATA/ATAPI Bridge", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_BROKEN_FUA ), + /* Entrega Technologies U1-SC25 (later Xircom PortGear PGSCSI) * and Mac USB Dock USB-SCSI */ UNUSUAL_DEV( 0x1645, 0x0007, 0x0100, 0x0133, diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index 18a283d6de1c..dbc00e56c7f5 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -40,6 +40,16 @@ * and don't forget to CC: the USB development list <linux-usb@vger.kernel.org> */ +/* + * Apricorn USB3 dongle sometimes returns "USBSUSBSUSBS" in response to SCSI + * commands in UAS mode. Observed with the 1.28 firmware; are there others? + */ +UNUSUAL_DEV(0x0984, 0x0301, 0x0128, 0x0128, + "Apricorn", + "", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_IGNORE_UAS), + /* https://bugzilla.kernel.org/show_bug.cgi?id=79511 */ UNUSUAL_DEV(0x0bc2, 0x2312, 0x0000, 0x9999, "Seagate", @@ -68,6 +78,20 @@ UNUSUAL_DEV(0x0bc2, 0xa003, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_ATA_1X), +/* Reported-by: Marcin ZajÄ…czkowski <mszpak@wp.pl> */ +UNUSUAL_DEV(0x0bc2, 0xa013, 0x0000, 0x9999, + "Seagate", + "Backup Plus", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_ATA_1X), + +/* Reported-by: Hans de Goede <hdegoede@redhat.com> */ +UNUSUAL_DEV(0x0bc2, 0xa0a4, 0x0000, 0x9999, + "Seagate", + "Backup Plus Desk", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_ATA_1X), + /* https://bbs.archlinux.org/viewtopic.php?id=183190 */ UNUSUAL_DEV(0x0bc2, 0xab20, 0x0000, 0x9999, "Seagate", @@ -82,6 +106,13 @@ UNUSUAL_DEV(0x0bc2, 0xab21, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_ATA_1X), +/* Reported-by: G. Richard Bellamy <rbellamy@pteradigm.com> */ +UNUSUAL_DEV(0x0bc2, 0xab2a, 0x0000, 0x9999, + "Seagate", + "BUP Fast HDD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_ATA_1X), + /* Reported-by: Claudio Bizzarri <claudio.bizzarri@gmail.com> */ UNUSUAL_DEV(0x152d, 0x0567, 0x0000, 0x9999, "JMicron", @@ -89,14 +120,6 @@ UNUSUAL_DEV(0x152d, 0x0567, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_REPORT_OPCODES), -/* Most ASM1051 based devices have issues with uas, blacklist them all */ -/* Reported-by: Hans de Goede <hdegoede@redhat.com> */ -UNUSUAL_DEV(0x174c, 0x5106, 0x0000, 0x9999, - "ASMedia", - "ASM1051", - USB_SC_DEVICE, USB_PR_DEVICE, NULL, - US_FL_IGNORE_UAS), - /* Reported-by: Hans de Goede <hdegoede@redhat.com> */ UNUSUAL_DEV(0x2109, 0x0711, 0x0000, 0x9999, "VIA", @@ -104,9 +127,23 @@ UNUSUAL_DEV(0x2109, 0x0711, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_ATA_1X), +/* Reported-by: Takeo Nakayama <javhera@gmx.com> */ +UNUSUAL_DEV(0x357d, 0x7788, 0x0000, 0x9999, + "JMicron", + "JMS566", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_REPORT_OPCODES), + /* Reported-by: Hans de Goede <hdegoede@redhat.com> */ UNUSUAL_DEV(0x4971, 0x1012, 0x0000, 0x9999, "Hitachi", "External HDD", USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_IGNORE_UAS), + +/* Reported-by: Richard Henderson <rth@redhat.com> */ +UNUSUAL_DEV(0x4971, 0x8017, 0x0000, 0x9999, + "SimpleTech", + "External HDD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_REPORT_OPCODES), diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c index 255201f22126..7cc0122a18ce 100644 --- a/drivers/vfio/pci/vfio_pci.c +++ b/drivers/vfio/pci/vfio_pci.c @@ -840,13 +840,11 @@ static const struct vfio_device_ops vfio_pci_ops = { static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - u8 type; struct vfio_pci_device *vdev; struct iommu_group *group; int ret; - pci_read_config_byte(pdev, PCI_HEADER_TYPE, &type); - if ((type & PCI_HEADER_TYPE) != PCI_HEADER_TYPE_NORMAL) + if (pdev->hdr_type != PCI_HEADER_TYPE_NORMAL) return -EINVAL; group = iommu_group_get(&pdev->dev); diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 14419a8ccbb6..8dccca9013ed 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -84,10 +84,6 @@ struct vhost_net_ubuf_ref { struct vhost_net_virtqueue { struct vhost_virtqueue vq; - /* hdr is used to store the virtio header. - * Since each iovec has >= 1 byte length, we never need more than - * header length entries to store the header. */ - struct iovec hdr[sizeof(struct virtio_net_hdr_mrg_rxbuf)]; size_t vhost_hlen; size_t sock_hlen; /* vhost zerocopy support fields below: */ @@ -235,44 +231,6 @@ static bool vhost_sock_zcopy(struct socket *sock) sock_flag(sock->sk, SOCK_ZEROCOPY); } -/* Pop first len bytes from iovec. Return number of segments used. */ -static int move_iovec_hdr(struct iovec *from, struct iovec *to, - size_t len, int iov_count) -{ - int seg = 0; - size_t size; - - while (len && seg < iov_count) { - size = min(from->iov_len, len); - to->iov_base = from->iov_base; - to->iov_len = size; - from->iov_len -= size; - from->iov_base += size; - len -= size; - ++from; - ++to; - ++seg; - } - return seg; -} -/* Copy iovec entries for len bytes from iovec. */ -static void copy_iovec_hdr(const struct iovec *from, struct iovec *to, - size_t len, int iovcount) -{ - int seg = 0; - size_t size; - - while (len && seg < iovcount) { - size = min(from->iov_len, len); - to->iov_base = from->iov_base; - to->iov_len = size; - len -= size; - ++from; - ++to; - ++seg; - } -} - /* In case of DMA done not in order in lower device driver for some reason. * upend_idx is used to track end of used idx, done_idx is used to track head * of used idx. Once lower device DMA done contiguously, we will signal KVM @@ -336,7 +294,7 @@ static void handle_tx(struct vhost_net *net) { struct vhost_net_virtqueue *nvq = &net->vqs[VHOST_NET_VQ_TX]; struct vhost_virtqueue *vq = &nvq->vq; - unsigned out, in, s; + unsigned out, in; int head; struct msghdr msg = { .msg_name = NULL, @@ -395,16 +353,17 @@ static void handle_tx(struct vhost_net *net) break; } /* Skip header. TODO: support TSO. */ - s = move_iovec_hdr(vq->iov, nvq->hdr, hdr_size, out); len = iov_length(vq->iov, out); iov_iter_init(&msg.msg_iter, WRITE, vq->iov, out, len); + iov_iter_advance(&msg.msg_iter, hdr_size); /* Sanity check */ - if (!len) { + if (!iov_iter_count(&msg.msg_iter)) { vq_err(vq, "Unexpected header len for TX: " "%zd expected %zd\n", - iov_length(nvq->hdr, s), hdr_size); + len, hdr_size); break; } + len = iov_iter_count(&msg.msg_iter); zcopy_used = zcopy && len >= VHOST_GOODCOPY_LEN && (nvq->upend_idx + 1) % UIO_MAXIOV != @@ -469,7 +428,7 @@ static int peek_head_len(struct sock *sk) head = skb_peek(&sk->sk_receive_queue); if (likely(head)) { len = head->len; - if (vlan_tx_tag_present(head)) + if (skb_vlan_tag_present(head)) len += VLAN_HLEN; } @@ -538,7 +497,7 @@ static int get_rx_bufs(struct vhost_virtqueue *vq, ++headcount; seg += in; } - heads[headcount - 1].len = cpu_to_vhost32(vq, len - datalen); + heads[headcount - 1].len = cpu_to_vhost32(vq, len + datalen); *iovcount = seg; if (unlikely(log)) *log_num = nlogs; @@ -579,6 +538,7 @@ static void handle_rx(struct vhost_net *net) size_t vhost_hlen, sock_hlen; size_t vhost_len, sock_len; struct socket *sock; + struct iov_iter fixup; mutex_lock(&vq->mutex); sock = vq->private_data; @@ -623,14 +583,19 @@ static void handle_rx(struct vhost_net *net) break; } /* We don't need to be notified again. */ - if (unlikely((vhost_hlen))) - /* Skip header. TODO: support TSO. */ - move_iovec_hdr(vq->iov, nvq->hdr, vhost_hlen, in); - else - /* Copy the header for use in VIRTIO_NET_F_MRG_RXBUF: - * needed because recvmsg can modify msg_iov. */ - copy_iovec_hdr(vq->iov, nvq->hdr, sock_hlen, in); - iov_iter_init(&msg.msg_iter, READ, vq->iov, in, sock_len); + iov_iter_init(&msg.msg_iter, READ, vq->iov, in, vhost_len); + fixup = msg.msg_iter; + if (unlikely((vhost_hlen))) { + /* We will supply the header ourselves + * TODO: support TSO. + */ + iov_iter_advance(&msg.msg_iter, vhost_hlen); + } else { + /* It'll come from socket; we'll need to patch + * ->num_buffers over if VIRTIO_NET_F_MRG_RXBUF + */ + iov_iter_advance(&fixup, sizeof(hdr)); + } err = sock->ops->recvmsg(NULL, sock, &msg, sock_len, MSG_DONTWAIT | MSG_TRUNC); /* Userspace might have consumed the packet meanwhile: @@ -642,18 +607,18 @@ static void handle_rx(struct vhost_net *net) vhost_discard_vq_desc(vq, headcount); continue; } + /* Supply virtio_net_hdr if VHOST_NET_F_VIRTIO_NET_HDR */ if (unlikely(vhost_hlen) && - memcpy_toiovecend(nvq->hdr, (unsigned char *)&hdr, 0, - vhost_hlen)) { + copy_to_iter(&hdr, sizeof(hdr), &fixup) != sizeof(hdr)) { vq_err(vq, "Unable to write vnet_hdr at addr %p\n", vq->iov->iov_base); break; } /* TODO: Should check and handle checksum. */ + + hdr.num_buffers = cpu_to_vhost16(vq, headcount); if (likely(mergeable) && - memcpy_toiovecend(nvq->hdr, (unsigned char *)&headcount, - offsetof(typeof(hdr), num_buffers), - sizeof hdr.num_buffers)) { + copy_to_iter(&hdr.num_buffers, 2, &fixup) != 2) { vq_err(vq, "Failed num_buffers write"); vhost_discard_vq_desc(vq, headcount); break; diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 01c01cb3933f..dc78d87e0fc2 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -911,6 +911,23 @@ vhost_scsi_map_iov_to_prot(struct tcm_vhost_cmd *cmd, return 0; } +static int vhost_scsi_to_tcm_attr(int attr) +{ + switch (attr) { + case VIRTIO_SCSI_S_SIMPLE: + return TCM_SIMPLE_TAG; + case VIRTIO_SCSI_S_ORDERED: + return TCM_ORDERED_TAG; + case VIRTIO_SCSI_S_HEAD: + return TCM_HEAD_TAG; + case VIRTIO_SCSI_S_ACA: + return TCM_ACA_TAG; + default: + break; + } + return TCM_SIMPLE_TAG; +} + static void tcm_vhost_submission_work(struct work_struct *work) { struct tcm_vhost_cmd *cmd = @@ -936,9 +953,10 @@ static void tcm_vhost_submission_work(struct work_struct *work) rc = target_submit_cmd_map_sgls(se_cmd, tv_nexus->tvn_se_sess, cmd->tvc_cdb, &cmd->tvc_sense_buf[0], cmd->tvc_lun, cmd->tvc_exp_data_len, - cmd->tvc_task_attr, cmd->tvc_data_direction, - TARGET_SCF_ACK_KREF, sg_ptr, cmd->tvc_sgl_count, - NULL, 0, sg_prot_ptr, cmd->tvc_prot_sgl_count); + vhost_scsi_to_tcm_attr(cmd->tvc_task_attr), + cmd->tvc_data_direction, TARGET_SCF_ACK_KREF, + sg_ptr, cmd->tvc_sgl_count, NULL, 0, sg_prot_ptr, + cmd->tvc_prot_sgl_count); if (rc < 0) { transport_send_check_condition_and_sense(se_cmd, TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0); @@ -1061,7 +1079,7 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq) req_size, vq->iov[0].iov_len); break; } - ret = memcpy_fromiovecend(req, &vq->iov[0], 0, req_size); + ret = copy_from_user(req, vq->iov[0].iov_base, req_size); if (unlikely(ret)) { vq_err(vq, "Faulted on virtio_scsi_cmd_req\n"); break; diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index cb807d0ea498..2ee28266fd07 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -1125,6 +1125,7 @@ static int get_indirect(struct vhost_virtqueue *vq, struct vring_desc desc; unsigned int i = 0, count, found = 0; u32 len = vhost32_to_cpu(vq, indirect->len); + struct iov_iter from; int ret; /* Sanity check */ @@ -1142,6 +1143,7 @@ static int get_indirect(struct vhost_virtqueue *vq, vq_err(vq, "Translation failure %d in indirect.\n", ret); return ret; } + iov_iter_init(&from, READ, vq->indirect, ret, len); /* We will use the result as an address to read from, so most * architectures only need a compiler barrier here. */ @@ -1164,8 +1166,8 @@ static int get_indirect(struct vhost_virtqueue *vq, i, count); return -EINVAL; } - if (unlikely(memcpy_fromiovec((unsigned char *)&desc, - vq->indirect, sizeof desc))) { + if (unlikely(copy_from_iter(&desc, sizeof(desc), &from) != + sizeof(desc))) { vq_err(vq, "Failed indirect descriptor: idx %d, %zx\n", i, (size_t)vhost64_to_cpu(vq, indirect->addr) + i * sizeof desc); return -EINVAL; diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index fe1cd0148e13..ba97efc3bf70 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -77,18 +77,22 @@ config DUMMY_CONSOLE config DUMMY_CONSOLE_COLUMNS int "Initial number of console screen columns" - depends on PARISC && DUMMY_CONSOLE - default "160" + depends on DUMMY_CONSOLE && !ARM + default 160 if PARISC + default 80 help - The default value is 160, which should fit a 1280x1024 monitor. + On PA-RISC, the default value is 160, which should fit a 1280x1024 + monitor. Select 80 if you use a 640x480 resolution by default. config DUMMY_CONSOLE_ROWS int "Initial number of console screen rows" - depends on PARISC && DUMMY_CONSOLE - default "64" + depends on DUMMY_CONSOLE && !ARM + default 64 if PARISC + default 25 help - The default value is 64, which should fit a 1280x1024 monitor. + On PA-RISC, the default value is 64, which should fit a 1280x1024 + monitor. Select 25 if you use a 640x480 resolution by default. config FRAMEBUFFER_CONSOLE diff --git a/drivers/video/console/dummycon.c b/drivers/video/console/dummycon.c index 40bec8d64b0a..0efc52f11ad0 100644 --- a/drivers/video/console/dummycon.c +++ b/drivers/video/console/dummycon.c @@ -20,13 +20,10 @@ #if defined(__arm__) #define DUMMY_COLUMNS screen_info.orig_video_cols #define DUMMY_ROWS screen_info.orig_video_lines -#elif defined(__hppa__) +#else /* set by Kconfig. Use 80x25 for 640x480 and 160x64 for 1280x1024 */ #define DUMMY_COLUMNS CONFIG_DUMMY_CONSOLE_COLUMNS #define DUMMY_ROWS CONFIG_DUMMY_CONSOLE_ROWS -#else -#define DUMMY_COLUMNS 80 -#define DUMMY_ROWS 25 #endif static const char *dummycon_startup(void) diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index ea437245562e..b97210671a81 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -146,9 +146,6 @@ static const struct consw fb_con; static int fbcon_set_origin(struct vc_data *); -#define CURSOR_DRAW_DELAY (1) - -static int vbl_cursor_cnt; static int fbcon_cursor_noblink; #define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1) @@ -1329,7 +1326,6 @@ static void fbcon_cursor(struct vc_data *vc, int mode) ops->cursor(vc, info, mode, y, get_color(vc, info, c, 1), get_color(vc, info, c, 0)); - vbl_cursor_cnt = CURSOR_DRAW_DELAY; } static int scrollback_phys_max = 0; diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 4916c97216f8..b3dd417b4719 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -1530,13 +1530,11 @@ config FB_SIS_315 config FB_VIA tristate "VIA UniChrome (Pro) and Chrome9 display support" - depends on FB && PCI && X86 + depends on FB && PCI && X86 && GPIOLIB && I2C select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT select I2C_ALGOBIT - select I2C - select GPIOLIB help This is the frame buffer device driver for Graphics chips of VIA UniChrome (Pro) Family (CLE266,PM800/CN400,P4M800CE/P4M800Pro/ @@ -2151,7 +2149,6 @@ config FB_PS3 select FB_SYS_COPYAREA select FB_SYS_IMAGEBLIT select FB_SYS_FOPS - select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE ---help--- Include support for the virtual frame buffer in the PS3 platform. diff --git a/drivers/video/fbdev/atafb.c b/drivers/video/fbdev/atafb.c index 4953b657635e..cb9ee2556850 100644 --- a/drivers/video/fbdev/atafb.c +++ b/drivers/video/fbdev/atafb.c @@ -3118,8 +3118,7 @@ int __init atafb_init(void) printk("atafb_init: initializing Falcon hw\n"); fbhw = &falcon_switch; atafb_ops.fb_setcolreg = &falcon_setcolreg; - error = request_irq(IRQ_AUTO_4, falcon_vbl_switcher, - IRQ_TYPE_PRIO, + error = request_irq(IRQ_AUTO_4, falcon_vbl_switcher, 0, "framebuffer:modeswitch", falcon_vbl_switcher); if (error) diff --git a/drivers/video/fbdev/aty/atyfb_base.c b/drivers/video/fbdev/aty/atyfb_base.c index 37ec09b3fffd..8789e487b96e 100644 --- a/drivers/video/fbdev/aty/atyfb_base.c +++ b/drivers/video/fbdev/aty/atyfb_base.c @@ -3948,7 +3948,7 @@ static struct notifier_block atyfb_reboot_notifier = { .notifier_call = atyfb_reboot_notify, }; -static const struct dmi_system_id atyfb_reboot_ids[] = { +static const struct dmi_system_id atyfb_reboot_ids[] __initconst = { { .ident = "HP OmniBook 500", .matches = { @@ -3960,6 +3960,7 @@ static const struct dmi_system_id atyfb_reboot_ids[] = { { } }; +static bool registered_notifier = false; static int __init atyfb_init(void) { @@ -3982,15 +3983,17 @@ static int __init atyfb_init(void) if (err1 && err2) return -ENODEV; - if (dmi_check_system(atyfb_reboot_ids)) + if (dmi_check_system(atyfb_reboot_ids)) { register_reboot_notifier(&atyfb_reboot_notifier); + registered_notifier = true; + } return 0; } static void __exit atyfb_exit(void) { - if (dmi_check_system(atyfb_reboot_ids)) + if (registered_notifier) unregister_reboot_notifier(&atyfb_reboot_notifier); #ifdef CONFIG_PCI diff --git a/drivers/video/fbdev/broadsheetfb.c b/drivers/video/fbdev/broadsheetfb.c index 1c29bd19e3d5..0e5fde1d3ffb 100644 --- a/drivers/video/fbdev/broadsheetfb.c +++ b/drivers/video/fbdev/broadsheetfb.c @@ -636,7 +636,7 @@ static int broadsheet_spiflash_rewrite_sector(struct broadsheetfb_par *par, err = broadsheet_spiflash_read_range(par, start_sector_addr, data_start_addr, sector_buffer); if (err) - return err; + goto out; } /* now we copy our data into the right place in the sector buffer */ @@ -657,7 +657,7 @@ static int broadsheet_spiflash_rewrite_sector(struct broadsheetfb_par *par, err = broadsheet_spiflash_read_range(par, tail_start_addr, tail_len, sector_buffer + tail_start_addr); if (err) - return err; + goto out; } /* if we got here we have the full sector that we want to rewrite. */ @@ -665,11 +665,13 @@ static int broadsheet_spiflash_rewrite_sector(struct broadsheetfb_par *par, /* first erase the sector */ err = broadsheet_spiflash_erase_sector(par, start_sector_addr); if (err) - return err; + goto out; /* now write it */ err = broadsheet_spiflash_write_sector(par, start_sector_addr, sector_buffer, sector_size); +out: + kfree(sector_buffer); return err; } diff --git a/drivers/video/fbdev/core/fbcvt.c b/drivers/video/fbdev/core/fbcvt.c index 7cb715dfc0e1..55d2bd0ce5c0 100644 --- a/drivers/video/fbdev/core/fbcvt.c +++ b/drivers/video/fbdev/core/fbcvt.c @@ -369,9 +369,9 @@ int fb_find_mode_cvt(struct fb_videomode *mode, int margins, int rb) cvt.h_back_porch = cvt.hblank/2 + cvt.h_margin; cvt.h_front_porch = cvt.hblank - cvt.hsync - cvt.h_back_porch + 2 * cvt.h_margin; - cvt.v_back_porch = 3 + cvt.v_margin; - cvt.v_front_porch = cvt.vtotal - cvt.yres/cvt.interlace - - cvt.v_back_porch - cvt.vsync; + cvt.v_front_porch = 3 + cvt.v_margin; + cvt.v_back_porch = cvt.vtotal - cvt.yres/cvt.interlace - + cvt.v_front_porch - cvt.vsync; fb_cvt_print_name(&cvt); fb_cvt_convert_to_mode(&cvt, mode); diff --git a/drivers/video/fbdev/core/fbmon.c b/drivers/video/fbdev/core/fbmon.c index 5b0e313849bd..95338593ebf4 100644 --- a/drivers/video/fbdev/core/fbmon.c +++ b/drivers/video/fbdev/core/fbmon.c @@ -496,56 +496,71 @@ static int get_est_timing(unsigned char *block, struct fb_videomode *mode) } static int get_std_timing(unsigned char *block, struct fb_videomode *mode, - int ver, int rev) + int ver, int rev, const struct fb_monspecs *specs) { - int xres, yres = 0, refresh, ratio, i; - - xres = (block[0] + 31) * 8; - if (xres <= 256) - return 0; + int i; - ratio = (block[1] & 0xc0) >> 6; - switch (ratio) { - case 0: - /* in EDID 1.3 the meaning of 0 changed to 16:10 (prior 1:1) */ - if (ver < 1 || (ver == 1 && rev < 3)) - yres = xres; - else - yres = (xres * 10)/16; - break; - case 1: - yres = (xres * 3)/4; - break; - case 2: - yres = (xres * 4)/5; - break; - case 3: - yres = (xres * 9)/16; - break; + for (i = 0; i < DMT_SIZE; i++) { + u32 std_2byte_code = block[0] << 8 | block[1]; + if (std_2byte_code == dmt_modes[i].std_2byte_code) + break; } - refresh = (block[1] & 0x3f) + 60; - - DPRINTK(" %dx%d@%dHz\n", xres, yres, refresh); - for (i = 0; i < VESA_MODEDB_SIZE; i++) { - if (vesa_modes[i].xres == xres && - vesa_modes[i].yres == yres && - vesa_modes[i].refresh == refresh) { - *mode = vesa_modes[i]; - mode->flag |= FB_MODE_IS_STANDARD; - return 1; + + if (i < DMT_SIZE && dmt_modes[i].mode) { + /* DMT mode found */ + *mode = *dmt_modes[i].mode; + mode->flag |= FB_MODE_IS_STANDARD; + DPRINTK(" DMT id=%d\n", dmt_modes[i].dmt_id); + + } else { + int xres, yres = 0, refresh, ratio; + + xres = (block[0] + 31) * 8; + if (xres <= 256) + return 0; + + ratio = (block[1] & 0xc0) >> 6; + switch (ratio) { + case 0: + /* in EDID 1.3 the meaning of 0 changed to 16:10 (prior 1:1) */ + if (ver < 1 || (ver == 1 && rev < 3)) + yres = xres; + else + yres = (xres * 10)/16; + break; + case 1: + yres = (xres * 3)/4; + break; + case 2: + yres = (xres * 4)/5; + break; + case 3: + yres = (xres * 9)/16; + break; } + refresh = (block[1] & 0x3f) + 60; + DPRINTK(" %dx%d@%dHz\n", xres, yres, refresh); + + calc_mode_timings(xres, yres, refresh, mode); } - calc_mode_timings(xres, yres, refresh, mode); + + /* Check the mode we got is within valid spec of the monitor */ + if (specs && specs->dclkmax + && PICOS2KHZ(mode->pixclock) * 1000 > specs->dclkmax) { + DPRINTK(" mode exceed max DCLK\n"); + return 0; + } + return 1; } -static int get_dst_timing(unsigned char *block, - struct fb_videomode *mode, int ver, int rev) +static int get_dst_timing(unsigned char *block, struct fb_videomode *mode, + int ver, int rev, const struct fb_monspecs *specs) { int j, num = 0; for (j = 0; j < 6; j++, block += STD_TIMING_DESCRIPTION_SIZE) - num += get_std_timing(block, &mode[num], ver, rev); + num += get_std_timing(block, &mode[num], ver, rev, specs); return num; } @@ -601,7 +616,8 @@ static void get_detailed_timing(unsigned char *block, * This function builds a mode database using the contents of the EDID * data */ -static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize) +static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize, + const struct fb_monspecs *specs) { struct fb_videomode *mode, *m; unsigned char *block; @@ -643,12 +659,13 @@ static struct fb_videomode *fb_create_modedb(unsigned char *edid, int *dbsize) DPRINTK(" Standard Timings\n"); block = edid + STD_TIMING_DESCRIPTIONS_START; for (i = 0; i < STD_TIMING; i++, block += STD_TIMING_DESCRIPTION_SIZE) - num += get_std_timing(block, &mode[num], ver, rev); + num += get_std_timing(block, &mode[num], ver, rev, specs); block = edid + DETAILED_TIMING_DESCRIPTIONS_START; for (i = 0; i < 4; i++, block+= DETAILED_TIMING_DESCRIPTION_SIZE) { if (block[0] == 0x00 && block[1] == 0x00 && block[3] == 0xfa) - num += get_dst_timing(block + 5, &mode[num], ver, rev); + num += get_dst_timing(block + 5, &mode[num], + ver, rev, specs); } /* Yikes, EDID data is totally useless */ @@ -707,7 +724,7 @@ static int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs) int num_modes, hz, hscan, pixclock; int vtotal, htotal; - modes = fb_create_modedb(edid, &num_modes); + modes = fb_create_modedb(edid, &num_modes, specs); if (!modes) { DPRINTK("None Available\n"); return 1; @@ -964,7 +981,7 @@ void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs) DPRINTK(" Display Characteristics:\n"); get_monspecs(edid, specs); - specs->modedb = fb_create_modedb(edid, &specs->modedb_len); + specs->modedb = fb_create_modedb(edid, &specs->modedb_len, specs); /* * Workaround for buggy EDIDs that sets that the first diff --git a/drivers/video/fbdev/core/modedb.c b/drivers/video/fbdev/core/modedb.c index 388f7971494b..7d07cf824b64 100644 --- a/drivers/video/fbdev/core/modedb.c +++ b/drivers/video/fbdev/core/modedb.c @@ -468,8 +468,119 @@ const struct fb_videomode vesa_modes[] = { /* 33 1920x1440-75 VESA */ { NULL, 75, 1920, 1440, 3367, 352, 144, 56, 1, 224, 3, FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 34 1920x1200-60 RB VESA */ + { NULL, 60, 1920, 1200, 6493, 80, 48, 26, 3, 32, 6, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 35 1920x1200-60 VESA */ + { NULL, 60, 1920, 1200, 5174, 336, 136, 36, 3, 200, 6, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 36 1920x1200-75 VESA */ + { NULL, 75, 1920, 1200, 4077, 344, 136, 46, 3, 208, 6, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 37 1920x1200-85 VESA */ + { NULL, 85, 1920, 1200, 3555, 352, 144, 53, 3, 208, 6, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 38 2560x1600-60 RB VESA */ + { NULL, 60, 2560, 1600, 3724, 80, 48, 37, 3, 32, 6, + FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 39 2560x1600-60 VESA */ + { NULL, 60, 2560, 1600, 2869, 472, 192, 49, 3, 280, 6, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 40 2560x1600-75 VESA */ + { NULL, 75, 2560, 1600, 2256, 488, 208, 63, 3, 280, 6, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 41 2560x1600-85 VESA */ + { NULL, 85, 2560, 1600, 1979, 488, 208, 73, 3, 280, 6, + FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, + /* 42 2560x1600-120 RB VESA */ + { NULL, 120, 2560, 1600, 1809, 80, 48, 85, 3, 32, 6, + FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED, FB_MODE_IS_VESA }, }; EXPORT_SYMBOL(vesa_modes); + +const struct dmt_videomode dmt_modes[DMT_SIZE] = { + { 0x01, 0x0000, 0x000000, &vesa_modes[0] }, + { 0x02, 0x3119, 0x000000, &vesa_modes[1] }, + { 0x03, 0x0000, 0x000000, &vesa_modes[2] }, + { 0x04, 0x3140, 0x000000, &vesa_modes[3] }, + { 0x05, 0x314c, 0x000000, &vesa_modes[4] }, + { 0x06, 0x314f, 0x000000, &vesa_modes[5] }, + { 0x07, 0x3159, 0x000000, &vesa_modes[6] }, + { 0x08, 0x0000, 0x000000, &vesa_modes[7] }, + { 0x09, 0x4540, 0x000000, &vesa_modes[8] }, + { 0x0a, 0x454c, 0x000000, &vesa_modes[9] }, + { 0x0b, 0x454f, 0x000000, &vesa_modes[10] }, + { 0x0c, 0x4559, 0x000000, &vesa_modes[11] }, + { 0x0d, 0x0000, 0x000000, NULL }, + { 0x0e, 0x0000, 0x000000, NULL }, + { 0x0f, 0x0000, 0x000000, &vesa_modes[12] }, + { 0x10, 0x6140, 0x000000, &vesa_modes[13] }, + { 0x11, 0x614a, 0x000000, &vesa_modes[14] }, + { 0x12, 0x614f, 0x000000, &vesa_modes[15] }, + { 0x13, 0x6159, 0x000000, &vesa_modes[16] }, + { 0x14, 0x0000, 0x000000, NULL }, + { 0x15, 0x714f, 0x000000, &vesa_modes[17] }, + { 0x16, 0x0000, 0x7f1c21, NULL }, + { 0x17, 0x0000, 0x7f1c28, NULL }, + { 0x18, 0x0000, 0x7f1c44, NULL }, + { 0x19, 0x0000, 0x7f1c62, NULL }, + { 0x1a, 0x0000, 0x000000, NULL }, + { 0x1b, 0x0000, 0x8f1821, NULL }, + { 0x1c, 0x8100, 0x8f1828, NULL }, + { 0x1d, 0x810f, 0x8f1844, NULL }, + { 0x1e, 0x8119, 0x8f1862, NULL }, + { 0x1f, 0x0000, 0x000000, NULL }, + { 0x20, 0x8140, 0x000000, &vesa_modes[18] }, + { 0x21, 0x8159, 0x000000, &vesa_modes[19] }, + { 0x22, 0x0000, 0x000000, NULL }, + { 0x23, 0x8180, 0x000000, &vesa_modes[20] }, + { 0x24, 0x818f, 0x000000, &vesa_modes[21] }, + { 0x25, 0x8199, 0x000000, &vesa_modes[22] }, + { 0x26, 0x0000, 0x000000, NULL }, + { 0x27, 0x0000, 0x000000, NULL }, + { 0x28, 0x0000, 0x000000, NULL }, + { 0x29, 0x0000, 0x0c2021, NULL }, + { 0x2a, 0x9040, 0x0c2028, NULL }, + { 0x2b, 0x904f, 0x0c2044, NULL }, + { 0x2c, 0x9059, 0x0c2062, NULL }, + { 0x2d, 0x0000, 0x000000, NULL }, + { 0x2e, 0x9500, 0xc11821, NULL }, + { 0x2f, 0x9500, 0xc11828, NULL }, + { 0x30, 0x950f, 0xc11844, NULL }, + { 0x31, 0x9519, 0xc11868, NULL }, + { 0x32, 0x0000, 0x000000, NULL }, + { 0x33, 0xa940, 0x000000, &vesa_modes[23] }, + { 0x34, 0xa945, 0x000000, &vesa_modes[24] }, + { 0x35, 0xa94a, 0x000000, &vesa_modes[25] }, + { 0x36, 0xa94f, 0x000000, &vesa_modes[26] }, + { 0x37, 0xa959, 0x000000, &vesa_modes[27] }, + { 0x38, 0x0000, 0x000000, NULL }, + { 0x39, 0x0000, 0x0c2821, NULL }, + { 0x3a, 0xb300, 0x0c2828, NULL }, + { 0x3b, 0xb30f, 0x0c2844, NULL }, + { 0x3c, 0xb319, 0x0c2868, NULL }, + { 0x3d, 0x0000, 0x000000, NULL }, + { 0x3e, 0xc140, 0x000000, &vesa_modes[28] }, + { 0x3f, 0xc14f, 0x000000, &vesa_modes[29] }, + { 0x40, 0x0000, 0x000000, NULL}, + { 0x41, 0xc940, 0x000000, &vesa_modes[30] }, + { 0x42, 0xc94f, 0x000000, &vesa_modes[31] }, + { 0x43, 0x0000, 0x000000, NULL }, + { 0x44, 0x0000, 0x572821, &vesa_modes[34] }, + { 0x45, 0xd100, 0x572828, &vesa_modes[35] }, + { 0x46, 0xd10f, 0x572844, &vesa_modes[36] }, + { 0x47, 0xd119, 0x572862, &vesa_modes[37] }, + { 0x48, 0x0000, 0x000000, NULL }, + { 0x49, 0xd140, 0x000000, &vesa_modes[32] }, + { 0x4a, 0xd14f, 0x000000, &vesa_modes[33] }, + { 0x4b, 0x0000, 0x000000, NULL }, + { 0x4c, 0x0000, 0x1f3821, &vesa_modes[38] }, + { 0x4d, 0x0000, 0x1f3828, &vesa_modes[39] }, + { 0x4e, 0x0000, 0x1f3844, &vesa_modes[40] }, + { 0x4f, 0x0000, 0x1f3862, &vesa_modes[41] }, + { 0x50, 0x0000, 0x000000, &vesa_modes[42] }, +}; +EXPORT_SYMBOL(dmt_modes); #endif /* CONFIG_FB_MODE_HELPERS */ /** diff --git a/drivers/video/fbdev/core/syscopyarea.c b/drivers/video/fbdev/core/syscopyarea.c index 844a32fd38ed..c1eda3190968 100644 --- a/drivers/video/fbdev/core/syscopyarea.c +++ b/drivers/video/fbdev/core/syscopyarea.c @@ -25,8 +25,8 @@ */ static void -bitcpy(struct fb_info *p, unsigned long *dst, int dst_idx, - const unsigned long *src, int src_idx, int bits, unsigned n) +bitcpy(struct fb_info *p, unsigned long *dst, unsigned dst_idx, + const unsigned long *src, unsigned src_idx, int bits, unsigned n) { unsigned long first, last; int const shift = dst_idx-src_idx; @@ -86,15 +86,15 @@ bitcpy(struct fb_info *p, unsigned long *dst, int dst_idx, first &= last; if (shift > 0) { /* Single source word */ - *dst = comp(*src >> right, *dst, first); + *dst = comp(*src << left, *dst, first); } else if (src_idx+n <= bits) { /* Single source word */ - *dst = comp(*src << left, *dst, first); + *dst = comp(*src >> right, *dst, first); } else { /* 2 source words */ d0 = *src++; d1 = *src; - *dst = comp(d0 << left | d1 >> right, *dst, + *dst = comp(d0 >> right | d1 << left, *dst, first); } } else { @@ -109,13 +109,14 @@ bitcpy(struct fb_info *p, unsigned long *dst, int dst_idx, /* Leading bits */ if (shift > 0) { /* Single source word */ - *dst = comp(d0 >> right, *dst, first); + *dst = comp(d0 << left, *dst, first); dst++; n -= bits - dst_idx; } else { /* 2 source words */ d1 = *src++; - *dst = comp(d0 << left | *dst >> right, *dst, first); + *dst = comp(d0 >> right | d1 << left, *dst, + first); d0 = d1; dst++; n -= bits - dst_idx; @@ -126,36 +127,36 @@ bitcpy(struct fb_info *p, unsigned long *dst, int dst_idx, n /= bits; while (n >= 4) { d1 = *src++; - *dst++ = d0 << left | d1 >> right; + *dst++ = d0 >> right | d1 << left; d0 = d1; d1 = *src++; - *dst++ = d0 << left | d1 >> right; + *dst++ = d0 >> right | d1 << left; d0 = d1; d1 = *src++; - *dst++ = d0 << left | d1 >> right; + *dst++ = d0 >> right | d1 << left; d0 = d1; d1 = *src++; - *dst++ = d0 << left | d1 >> right; + *dst++ = d0 >> right | d1 << left; d0 = d1; n -= 4; } while (n--) { d1 = *src++; - *dst++ = d0 << left | d1 >> right; + *dst++ = d0 >> right | d1 << left; d0 = d1; } /* Trailing bits */ - if (last) { - if (m <= right) { + if (m) { + if (m <= bits - right) { /* Single source word */ - *dst = comp(d0 << left, *dst, last); + d0 >>= right; } else { /* 2 source words */ d1 = *src; - *dst = comp(d0 << left | d1 >> right, - *dst, last); + d0 = d0 >> right | d1 << left; } + *dst = comp(d0, *dst, last); } } } @@ -166,40 +167,35 @@ bitcpy(struct fb_info *p, unsigned long *dst, int dst_idx, */ static void -bitcpy_rev(struct fb_info *p, unsigned long *dst, int dst_idx, - const unsigned long *src, int src_idx, int bits, unsigned n) +bitcpy_rev(struct fb_info *p, unsigned long *dst, unsigned dst_idx, + const unsigned long *src, unsigned src_idx, unsigned bits, + unsigned n) { unsigned long first, last; int shift; - dst += (n-1)/bits; - src += (n-1)/bits; - if ((n-1) % bits) { - dst_idx += (n-1) % bits; - dst += dst_idx >> (ffs(bits) - 1); - dst_idx &= bits - 1; - src_idx += (n-1) % bits; - src += src_idx >> (ffs(bits) - 1); - src_idx &= bits - 1; - } + dst += (dst_idx + n - 1) / bits; + src += (src_idx + n - 1) / bits; + dst_idx = (dst_idx + n - 1) % bits; + src_idx = (src_idx + n - 1) % bits; shift = dst_idx-src_idx; - first = FB_SHIFT_LOW(p, ~0UL, bits - 1 - dst_idx); - last = ~(FB_SHIFT_LOW(p, ~0UL, bits - 1 - ((dst_idx-n) % bits))); + first = ~FB_SHIFT_HIGH(p, ~0UL, (dst_idx + 1) % bits); + last = FB_SHIFT_HIGH(p, ~0UL, (bits + dst_idx + 1 - n) % bits); if (!shift) { /* Same alignment for source and dest */ if ((unsigned long)dst_idx+1 >= n) { /* Single word */ - if (last) - first &= last; - *dst = comp(*src, *dst, first); + if (first) + last &= first; + *dst = comp(*src, *dst, last); } else { /* Multiple destination words */ /* Leading bits */ - if (first != ~0UL) { + if (first) { *dst = comp(*src, *dst, first); dst--; src--; @@ -222,29 +218,29 @@ bitcpy_rev(struct fb_info *p, unsigned long *dst, int dst_idx, while (n--) *dst-- = *src--; /* Trailing bits */ - if (last) + if (last != -1UL) *dst = comp(*src, *dst, last); } } else { /* Different alignment for source and dest */ - int const left = -shift & (bits-1); - int const right = shift & (bits-1); + int const left = shift & (bits-1); + int const right = -shift & (bits-1); if ((unsigned long)dst_idx+1 >= n) { /* Single destination word */ - if (last) - first &= last; + if (first) + last &= first; if (shift < 0) { /* Single source word */ - *dst = comp(*src << left, *dst, first); + *dst = comp(*src >> right, *dst, last); } else if (1+(unsigned long)src_idx >= n) { /* Single source word */ - *dst = comp(*src >> right, *dst, first); + *dst = comp(*src << left, *dst, last); } else { /* 2 source words */ - *dst = comp(*src >> right | *(src-1) << left, - *dst, first); + *dst = comp(*src << left | *(src-1) >> right, + *dst, last); } } else { /* Multiple destination words */ @@ -261,14 +257,18 @@ bitcpy_rev(struct fb_info *p, unsigned long *dst, int dst_idx, /* Leading bits */ if (shift < 0) { /* Single source word */ - *dst = comp(d0 << left, *dst, first); + d1 = d0; + d0 >>= right; } else { /* 2 source words */ d1 = *src--; - *dst = comp(d0 >> right | d1 << left, *dst, - first); - d0 = d1; + d0 = d0 << left | d1 >> right; } + if (!first) + *dst = d0; + else + *dst = comp(d0, *dst, first); + d0 = d1; dst--; n -= dst_idx+1; @@ -277,36 +277,36 @@ bitcpy_rev(struct fb_info *p, unsigned long *dst, int dst_idx, n /= bits; while (n >= 4) { d1 = *src--; - *dst-- = d0 >> right | d1 << left; + *dst-- = d0 << left | d1 >> right; d0 = d1; d1 = *src--; - *dst-- = d0 >> right | d1 << left; + *dst-- = d0 << left | d1 >> right; d0 = d1; d1 = *src--; - *dst-- = d0 >> right | d1 << left; + *dst-- = d0 << left | d1 >> right; d0 = d1; d1 = *src--; - *dst-- = d0 >> right | d1 << left; + *dst-- = d0 << left | d1 >> right; d0 = d1; n -= 4; } while (n--) { d1 = *src--; - *dst-- = d0 >> right | d1 << left; + *dst-- = d0 << left | d1 >> right; d0 = d1; } /* Trailing bits */ - if (last) { - if (m <= left) { + if (m) { + if (m <= bits - left) { /* Single source word */ - *dst = comp(d0 >> right, *dst, last); + d0 <<= left; } else { /* 2 source words */ d1 = *src; - *dst = comp(d0 >> right | d1 << left, - *dst, last); + d0 = d0 << left | d1 >> right; } + *dst = comp(d0, *dst, last); } } } @@ -317,9 +317,9 @@ void sys_copyarea(struct fb_info *p, const struct fb_copyarea *area) u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy; u32 height = area->height, width = area->width; unsigned long const bits_per_line = p->fix.line_length*8u; - unsigned long *dst = NULL, *src = NULL; + unsigned long *base = NULL; int bits = BITS_PER_LONG, bytes = bits >> 3; - int dst_idx = 0, src_idx = 0, rev_copy = 0; + unsigned dst_idx = 0, src_idx = 0, rev_copy = 0; if (p->state != FBINFO_STATE_RUNNING) return; @@ -334,8 +334,7 @@ void sys_copyarea(struct fb_info *p, const struct fb_copyarea *area) /* split the base of the framebuffer into a long-aligned address and the index of the first bit */ - dst = src = (unsigned long *)((unsigned long)p->screen_base & - ~(bytes-1)); + base = (unsigned long *)((unsigned long)p->screen_base & ~(bytes-1)); dst_idx = src_idx = 8*((unsigned long)p->screen_base & (bytes-1)); /* add offset of source and target area */ dst_idx += dy*bits_per_line + dx*p->var.bits_per_pixel; @@ -348,20 +347,14 @@ void sys_copyarea(struct fb_info *p, const struct fb_copyarea *area) while (height--) { dst_idx -= bits_per_line; src_idx -= bits_per_line; - dst += dst_idx >> (ffs(bits) - 1); - dst_idx &= (bytes - 1); - src += src_idx >> (ffs(bits) - 1); - src_idx &= (bytes - 1); - bitcpy_rev(p, dst, dst_idx, src, src_idx, bits, + bitcpy_rev(p, base + (dst_idx / bits), dst_idx % bits, + base + (src_idx / bits), src_idx % bits, bits, width*p->var.bits_per_pixel); } } else { while (height--) { - dst += dst_idx >> (ffs(bits) - 1); - dst_idx &= (bytes - 1); - src += src_idx >> (ffs(bits) - 1); - src_idx &= (bytes - 1); - bitcpy(p, dst, dst_idx, src, src_idx, bits, + bitcpy(p, base + (dst_idx / bits), dst_idx % bits, + base + (src_idx / bits), src_idx % bits, bits, width*p->var.bits_per_pixel); dst_idx += bits_per_line; src_idx += bits_per_line; diff --git a/drivers/video/fbdev/geode/gx1fb_core.c b/drivers/video/fbdev/geode/gx1fb_core.c index 2794ba11f332..9bee8744c438 100644 --- a/drivers/video/fbdev/geode/gx1fb_core.c +++ b/drivers/video/fbdev/geode/gx1fb_core.c @@ -374,10 +374,8 @@ static int gx1fb_probe(struct pci_dev *pdev, const struct pci_device_id *id) release_mem_region(gx1_gx_base() + 0x8300, 0x100); } - if (info) { - fb_dealloc_cmap(&info->cmap); - framebuffer_release(info); - } + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); return ret; } diff --git a/drivers/video/fbdev/geode/gxfb_core.c b/drivers/video/fbdev/geode/gxfb_core.c index 1790f14bab15..124d7c7e2d14 100644 --- a/drivers/video/fbdev/geode/gxfb_core.c +++ b/drivers/video/fbdev/geode/gxfb_core.c @@ -444,10 +444,8 @@ static int gxfb_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_release_region(pdev, 1); } - if (info) { - fb_dealloc_cmap(&info->cmap); - framebuffer_release(info); - } + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); return ret; } diff --git a/drivers/video/fbdev/geode/lxfb_core.c b/drivers/video/fbdev/geode/lxfb_core.c index 9e1d19d673a1..138da6cb6cbc 100644 --- a/drivers/video/fbdev/geode/lxfb_core.c +++ b/drivers/video/fbdev/geode/lxfb_core.c @@ -577,10 +577,8 @@ err: pci_release_region(pdev, 3); } - if (info) { - fb_dealloc_cmap(&info->cmap); - framebuffer_release(info); - } + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); return ret; } diff --git a/drivers/video/fbdev/hgafb.c b/drivers/video/fbdev/hgafb.c index 5ff9fe2116a4..15d3ccff2965 100644 --- a/drivers/video/fbdev/hgafb.c +++ b/drivers/video/fbdev/hgafb.c @@ -417,8 +417,7 @@ static int hgafb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) { if (var->vmode & FB_VMODE_YWRAP) { - if (var->yoffset < 0 || - var->yoffset >= info->var.yres_virtual || + if (var->yoffset >= info->var.yres_virtual || var->xoffset) return -EINVAL; } else { diff --git a/drivers/video/fbdev/mmp/Makefile b/drivers/video/fbdev/mmp/Makefile index a014cb358bf8..924dd0930cc7 100644 --- a/drivers/video/fbdev/mmp/Makefile +++ b/drivers/video/fbdev/mmp/Makefile @@ -1 +1,3 @@ -obj-y += core.o hw/ panel/ fb/ +obj-$(CONFIG_MMP_DISP) += mmp_disp.o hw/ panel/ fb/ + +mmp_disp-y += core.o diff --git a/drivers/video/fbdev/mmp/fb/Kconfig b/drivers/video/fbdev/mmp/fb/Kconfig index 9b0141f105f5..985e1a7cd254 100644 --- a/drivers/video/fbdev/mmp/fb/Kconfig +++ b/drivers/video/fbdev/mmp/fb/Kconfig @@ -1,7 +1,7 @@ if MMP_DISP config MMP_FB - bool "fb driver for Marvell MMP Display Subsystem" + tristate "fb driver for Marvell MMP Display Subsystem" depends on FB select FB_CFB_FILLRECT select FB_CFB_COPYAREA diff --git a/drivers/video/fbdev/ocfb.c b/drivers/video/fbdev/ocfb.c index 7f9dc9bec309..de9819660ca0 100644 --- a/drivers/video/fbdev/ocfb.c +++ b/drivers/video/fbdev/ocfb.c @@ -61,7 +61,7 @@ struct ocfb_dev { /* flag indicating whether the regs are little endian accessed */ int little_endian; /* Physical and virtual addresses of framebuffer */ - phys_addr_t fb_phys; + dma_addr_t fb_phys; void __iomem *fb_virt; u32 pseudo_palette[PALETTE_SIZE]; }; diff --git a/drivers/video/fbdev/omap2/displays-new/Kconfig b/drivers/video/fbdev/omap2/displays-new/Kconfig index e6cfc38160d3..574710141a61 100644 --- a/drivers/video/fbdev/omap2/displays-new/Kconfig +++ b/drivers/video/fbdev/omap2/displays-new/Kconfig @@ -1,6 +1,12 @@ menu "OMAP Display Device Drivers (new device model)" depends on OMAP2_DSS +config DISPLAY_ENCODER_OPA362 + tristate "OPA362 external analog amplifier" + help + Driver for OPA362 external analog TV amplifier controlled + through a GPIO. + config DISPLAY_ENCODER_TFP410 tristate "TFP410 DPI to DVI Encoder" help diff --git a/drivers/video/fbdev/omap2/displays-new/Makefile b/drivers/video/fbdev/omap2/displays-new/Makefile index 0323a8a1c682..9aa176bfbf2e 100644 --- a/drivers/video/fbdev/omap2/displays-new/Makefile +++ b/drivers/video/fbdev/omap2/displays-new/Makefile @@ -1,3 +1,4 @@ +obj-$(CONFIG_DISPLAY_ENCODER_OPA362) += encoder-opa362.o obj-$(CONFIG_DISPLAY_ENCODER_TFP410) += encoder-tfp410.o obj-$(CONFIG_DISPLAY_ENCODER_TPD12S015) += encoder-tpd12s015.o obj-$(CONFIG_DISPLAY_CONNECTOR_DVI) += connector-dvi.o diff --git a/drivers/video/fbdev/omap2/displays-new/connector-analog-tv.c b/drivers/video/fbdev/omap2/displays-new/connector-analog-tv.c index 9a2b5ce58545..8511c648a15c 100644 --- a/drivers/video/fbdev/omap2/displays-new/connector-analog-tv.c +++ b/drivers/video/fbdev/omap2/displays-new/connector-analog-tv.c @@ -208,7 +208,7 @@ static int tvc_probe_pdata(struct platform_device *pdev) ddata->in = in; ddata->connector_type = pdata->connector_type; - ddata->invert_polarity = ddata->invert_polarity; + ddata->invert_polarity = pdata->invert_polarity; dssdev = &ddata->dssdev; dssdev->name = pdata->name; diff --git a/drivers/video/fbdev/omap2/displays-new/encoder-opa362.c b/drivers/video/fbdev/omap2/displays-new/encoder-opa362.c new file mode 100644 index 000000000000..84a6b3367124 --- /dev/null +++ b/drivers/video/fbdev/omap2/displays-new/encoder-opa362.c @@ -0,0 +1,285 @@ +/* + * OPA362 analog video amplifier with output/power control + * + * Copyright (C) 2014 Golden Delicious Computers + * Author: H. Nikolaus Schaller <hns@goldelico.com> + * + * based on encoder-tfp410 + * + * Copyright (C) 2013 Texas Instruments + * Author: Tomi Valkeinen <tomi.valkeinen@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. + */ + +#include <linux/gpio.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/of_gpio.h> + +#include <video/omapdss.h> + +struct panel_drv_data { + struct omap_dss_device dssdev; + struct omap_dss_device *in; + + struct gpio_desc *enable_gpio; + + struct omap_video_timings timings; +}; + +#define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) + +static int opa362_connect(struct omap_dss_device *dssdev, + struct omap_dss_device *dst) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + dev_dbg(dssdev->dev, "connect\n"); + + if (omapdss_device_is_connected(dssdev)) + return -EBUSY; + + r = in->ops.atv->connect(in, dssdev); + if (r) + return r; + + dst->src = dssdev; + dssdev->dst = dst; + + return 0; +} + +static void opa362_disconnect(struct omap_dss_device *dssdev, + struct omap_dss_device *dst) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + dev_dbg(dssdev->dev, "disconnect\n"); + + WARN_ON(!omapdss_device_is_connected(dssdev)); + if (!omapdss_device_is_connected(dssdev)) + return; + + WARN_ON(dst != dssdev->dst); + if (dst != dssdev->dst) + return; + + dst->src = NULL; + dssdev->dst = NULL; + + in->ops.atv->disconnect(in, &ddata->dssdev); +} + +static int opa362_enable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + int r; + + dev_dbg(dssdev->dev, "enable\n"); + + if (!omapdss_device_is_connected(dssdev)) + return -ENODEV; + + if (omapdss_device_is_enabled(dssdev)) + return 0; + + in->ops.atv->set_timings(in, &ddata->timings); + + r = in->ops.atv->enable(in); + if (r) + return r; + + if (ddata->enable_gpio) + gpiod_set_value_cansleep(ddata->enable_gpio, 1); + + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; + + return 0; +} + +static void opa362_disable(struct omap_dss_device *dssdev) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + dev_dbg(dssdev->dev, "disable\n"); + + if (!omapdss_device_is_enabled(dssdev)) + return; + + if (ddata->enable_gpio) + gpiod_set_value_cansleep(ddata->enable_gpio, 0); + + in->ops.atv->disable(in); + + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; +} + +static void opa362_set_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + dev_dbg(dssdev->dev, "set_timings\n"); + + ddata->timings = *timings; + dssdev->panel.timings = *timings; + + in->ops.atv->set_timings(in, timings); +} + +static void opa362_get_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + + dev_dbg(dssdev->dev, "get_timings\n"); + + *timings = ddata->timings; +} + +static int opa362_check_timings(struct omap_dss_device *dssdev, + struct omap_video_timings *timings) +{ + struct panel_drv_data *ddata = to_panel_data(dssdev); + struct omap_dss_device *in = ddata->in; + + dev_dbg(dssdev->dev, "check_timings\n"); + + return in->ops.atv->check_timings(in, timings); +} + +static void opa362_set_type(struct omap_dss_device *dssdev, + enum omap_dss_venc_type type) +{ + /* we can only drive a COMPOSITE output */ + WARN_ON(type != OMAP_DSS_VENC_TYPE_COMPOSITE); + +} + +static const struct omapdss_atv_ops opa362_atv_ops = { + .connect = opa362_connect, + .disconnect = opa362_disconnect, + + .enable = opa362_enable, + .disable = opa362_disable, + + .check_timings = opa362_check_timings, + .set_timings = opa362_set_timings, + .get_timings = opa362_get_timings, + + .set_type = opa362_set_type, +}; + +static int opa362_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct panel_drv_data *ddata; + struct omap_dss_device *dssdev, *in; + struct gpio_desc *gpio; + int r; + + dev_dbg(&pdev->dev, "probe\n"); + + if (node == NULL) { + dev_err(&pdev->dev, "Unable to find device tree\n"); + return -EINVAL; + } + + ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); + if (!ddata) + return -ENOMEM; + + platform_set_drvdata(pdev, ddata); + + gpio = devm_gpiod_get(&pdev->dev, "enable"); + if (IS_ERR(gpio)) { + if (PTR_ERR(gpio) != -ENOENT) + return PTR_ERR(gpio); + + gpio = NULL; + } else { + gpiod_direction_output(gpio, 0); + } + + ddata->enable_gpio = gpio; + + in = omapdss_of_find_source_for_first_ep(node); + if (IS_ERR(in)) { + dev_err(&pdev->dev, "failed to find video source\n"); + return PTR_ERR(in); + } + + ddata->in = in; + + dssdev = &ddata->dssdev; + dssdev->ops.atv = &opa362_atv_ops; + dssdev->dev = &pdev->dev; + dssdev->type = OMAP_DISPLAY_TYPE_VENC; + dssdev->output_type = OMAP_DISPLAY_TYPE_VENC; + dssdev->owner = THIS_MODULE; + + r = omapdss_register_output(dssdev); + if (r) { + dev_err(&pdev->dev, "Failed to register output\n"); + goto err_reg; + } + + return 0; +err_reg: + omap_dss_put_device(ddata->in); + return r; +} + +static int __exit opa362_remove(struct platform_device *pdev) +{ + struct panel_drv_data *ddata = platform_get_drvdata(pdev); + struct omap_dss_device *dssdev = &ddata->dssdev; + struct omap_dss_device *in = ddata->in; + + omapdss_unregister_output(&ddata->dssdev); + + WARN_ON(omapdss_device_is_enabled(dssdev)); + if (omapdss_device_is_enabled(dssdev)) + opa362_disable(dssdev); + + WARN_ON(omapdss_device_is_connected(dssdev)); + if (omapdss_device_is_connected(dssdev)) + opa362_disconnect(dssdev, dssdev->dst); + + omap_dss_put_device(in); + + return 0; +} + +static const struct of_device_id opa362_of_match[] = { + { .compatible = "omapdss,ti,opa362", }, + {}, +}; +MODULE_DEVICE_TABLE(of, opa362_of_match); + +static struct platform_driver opa362_driver = { + .probe = opa362_probe, + .remove = __exit_p(opa362_remove), + .driver = { + .name = "amplifier-opa362", + .owner = THIS_MODULE, + .of_match_table = opa362_of_match, + .suppress_bind_attrs = true, + }, +}; + +module_platform_driver(opa362_driver); + +MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>"); +MODULE_DESCRIPTION("OPA362 analog video amplifier with output/power control"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/fbdev/omap2/displays-new/encoder-tpd12s015.c b/drivers/video/fbdev/omap2/displays-new/encoder-tpd12s015.c index 7f3e11b16c86..990af6baeb0f 100644 --- a/drivers/video/fbdev/omap2/displays-new/encoder-tpd12s015.c +++ b/drivers/video/fbdev/omap2/displays-new/encoder-tpd12s015.c @@ -29,33 +29,10 @@ struct panel_drv_data { int hpd_gpio; struct omap_video_timings timings; - - struct completion hpd_completion; }; #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) -static irqreturn_t tpd_hpd_irq_handler(int irq, void *data) -{ - struct panel_drv_data *ddata = data; - bool hpd; - - hpd = gpio_get_value_cansleep(ddata->hpd_gpio); - - dev_dbg(ddata->dssdev.dev, "hpd %d\n", hpd); - - if (gpio_is_valid(ddata->ls_oe_gpio)) { - if (hpd) - gpio_set_value_cansleep(ddata->ls_oe_gpio, 1); - else - gpio_set_value_cansleep(ddata->ls_oe_gpio, 0); - } - - complete_all(&ddata->hpd_completion); - - return IRQ_HANDLED; -} - static int tpd_connect(struct omap_dss_device *dssdev, struct omap_dss_device *dst) { @@ -70,23 +47,10 @@ static int tpd_connect(struct omap_dss_device *dssdev, dst->src = dssdev; dssdev->dst = dst; - reinit_completion(&ddata->hpd_completion); - gpio_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1); /* DC-DC converter needs at max 300us to get to 90% of 5V */ udelay(300); - /* - * If there's a cable connected, wait for the hpd irq to trigger, - * which turns on the level shifters. - */ - if (gpio_get_value_cansleep(ddata->hpd_gpio)) { - unsigned long to; - to = wait_for_completion_timeout(&ddata->hpd_completion, - msecs_to_jiffies(250)); - WARN_ON_ONCE(to == 0); - } - return 0; } @@ -179,11 +143,20 @@ static int tpd_read_edid(struct omap_dss_device *dssdev, { struct panel_drv_data *ddata = to_panel_data(dssdev); struct omap_dss_device *in = ddata->in; + int r; if (!gpio_get_value_cansleep(ddata->hpd_gpio)) return -ENODEV; - return in->ops.hdmi->read_edid(in, edid, len); + if (gpio_is_valid(ddata->ls_oe_gpio)) + gpio_set_value_cansleep(ddata->ls_oe_gpio, 1); + + r = in->ops.hdmi->read_edid(in, edid, len); + + if (gpio_is_valid(ddata->ls_oe_gpio)) + gpio_set_value_cansleep(ddata->ls_oe_gpio, 0); + + return r; } static bool tpd_detect(struct omap_dss_device *dssdev) @@ -309,8 +282,6 @@ static int tpd_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - init_completion(&ddata->hpd_completion); - if (dev_get_platdata(&pdev->dev)) { r = tpd_probe_pdata(pdev); if (r) @@ -340,13 +311,6 @@ static int tpd_probe(struct platform_device *pdev) if (r) goto err_gpio; - r = devm_request_threaded_irq(&pdev->dev, gpio_to_irq(ddata->hpd_gpio), - NULL, tpd_hpd_irq_handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, "hpd", ddata); - if (r) - goto err_irq; - dssdev = &ddata->dssdev; dssdev->ops.hdmi = &tpd_hdmi_ops; dssdev->dev = &pdev->dev; @@ -365,7 +329,6 @@ static int tpd_probe(struct platform_device *pdev) return 0; err_reg: -err_irq: err_gpio: omap_dss_put_device(ddata->in); return r; diff --git a/drivers/video/fbdev/omap2/dss/Makefile b/drivers/video/fbdev/omap2/dss/Makefile index 2ea9d382354c..b5136d3d4b77 100644 --- a/drivers/video/fbdev/omap2/dss/Makefile +++ b/drivers/video/fbdev/omap2/dss/Makefile @@ -2,7 +2,7 @@ obj-$(CONFIG_OMAP2_DSS_INIT) += omapdss-boot-init.o obj-$(CONFIG_OMAP2_DSS) += omapdss.o # Core DSS files omapdss-y := core.o dss.o dss_features.o dispc.o dispc_coefs.o display.o \ - output.o dss-of.o pll.o + output.o dss-of.o pll.o video-pll.o # DSS compat layer files omapdss-y += manager.o manager-sysfs.o overlay.o overlay-sysfs.o apply.o \ dispc-compat.o display-sysfs.o diff --git a/drivers/video/fbdev/omap2/dss/dispc.c b/drivers/video/fbdev/omap2/dss/dispc.c index 9850d9ef9a9d..31b743c70272 100644 --- a/drivers/video/fbdev/omap2/dss/dispc.c +++ b/drivers/video/fbdev/omap2/dss/dispc.c @@ -36,6 +36,9 @@ #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/sizes.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> +#include <linux/of.h> #include <video/omapdss.h> @@ -117,6 +120,9 @@ static struct { const struct dispc_features *feat; bool is_enabled; + + struct regmap *syscon_pol; + u32 syscon_pol_offset; } dispc; enum omap_color_component { @@ -2958,6 +2964,25 @@ static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw, FLD_VAL(vsync_level, 12, 12); dispc_write_reg(DISPC_POL_FREQ(channel), l); + + if (dispc.syscon_pol) { + const int shifts[] = { + [OMAP_DSS_CHANNEL_LCD] = 0, + [OMAP_DSS_CHANNEL_LCD2] = 1, + [OMAP_DSS_CHANNEL_LCD3] = 2, + }; + + u32 mask, val; + + mask = (1 << 0) | (1 << 3) | (1 << 6); + val = (rf << 0) | (ipc << 3) | (onoff << 6); + + mask <<= 16 + shifts[channel]; + val <<= 16 + shifts[channel]; + + regmap_update_bits(dispc.syscon_pol, dispc.syscon_pol_offset, + mask, val); + } } /* change name to mode? */ @@ -3037,10 +3062,16 @@ unsigned long dispc_fclk_rate(void) break; case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: pll = dss_pll_find("dsi0"); + if (!pll) + pll = dss_pll_find("video0"); + r = pll->cinfo.clkout[0]; break; case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC: pll = dss_pll_find("dsi1"); + if (!pll) + pll = dss_pll_find("video1"); + r = pll->cinfo.clkout[0]; break; default: @@ -3069,10 +3100,16 @@ unsigned long dispc_mgr_lclk_rate(enum omap_channel channel) break; case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC: pll = dss_pll_find("dsi0"); + if (!pll) + pll = dss_pll_find("video0"); + r = pll->cinfo.clkout[0]; break; case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC: pll = dss_pll_find("dsi1"); + if (!pll) + pll = dss_pll_find("video1"); + r = pll->cinfo.clkout[0]; break; default: @@ -3668,6 +3705,7 @@ static int __init dispc_init_features(struct platform_device *pdev) break; case OMAPDSS_VER_OMAP5: + case OMAPDSS_VER_DRA7xx: src = &omap54xx_dispc_feats; break; @@ -3728,6 +3766,7 @@ static int __init omap_dispchw_probe(struct platform_device *pdev) u32 rev; int r = 0; struct resource *dispc_mem; + struct device_node *np = pdev->dev.of_node; dispc.pdev = pdev; @@ -3754,6 +3793,20 @@ static int __init omap_dispchw_probe(struct platform_device *pdev) return -ENODEV; } + if (np && of_property_read_bool(np, "syscon-pol")) { + dispc.syscon_pol = syscon_regmap_lookup_by_phandle(np, "syscon-pol"); + if (IS_ERR(dispc.syscon_pol)) { + dev_err(&pdev->dev, "failed to get syscon-pol regmap\n"); + return PTR_ERR(dispc.syscon_pol); + } + + if (of_property_read_u32_index(np, "syscon-pol", 1, + &dispc.syscon_pol_offset)) { + dev_err(&pdev->dev, "failed to get syscon-pol offset\n"); + return -EINVAL; + } + } + pm_runtime_enable(&pdev->dev); r = dispc_runtime_get(); @@ -3832,6 +3885,7 @@ static const struct of_device_id dispc_of_match[] = { { .compatible = "ti,omap3-dispc", }, { .compatible = "ti,omap4-dispc", }, { .compatible = "ti,omap5-dispc", }, + { .compatible = "ti,dra7-dispc", }, {}, }; diff --git a/drivers/video/fbdev/omap2/dss/dpi.c b/drivers/video/fbdev/omap2/dss/dpi.c index 9a2f8c3b102d..f83e7b030249 100644 --- a/drivers/video/fbdev/omap2/dss/dpi.c +++ b/drivers/video/fbdev/omap2/dss/dpi.c @@ -106,6 +106,17 @@ static struct dss_pll *dpi_get_pll(enum omap_channel channel) return NULL; } + case OMAPDSS_VER_DRA7xx: + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + case OMAP_DSS_CHANNEL_LCD2: + return dss_pll_find("video0"); + case OMAP_DSS_CHANNEL_LCD3: + return dss_pll_find("video1"); + default: + return NULL; + } + default: return NULL; } @@ -590,6 +601,10 @@ static void dpi_init_pll(struct dpi_data *dpi) if (!pll) return; + /* On DRA7 we need to set a mux to use the PLL */ + if (omapdss_get_version() == OMAPDSS_VER_DRA7xx) + dss_ctrl_pll_set_control_mux(pll->id, dpi->output.dispc_channel); + if (dpi_verify_dsi_pll(pll)) { DSSWARN("DSI PLL not operational\n"); return; @@ -615,6 +630,17 @@ static enum omap_channel dpi_get_channel(int port_num) case OMAPDSS_VER_AM43xx: return OMAP_DSS_CHANNEL_LCD; + case OMAPDSS_VER_DRA7xx: + switch (port_num) { + case 2: + return OMAP_DSS_CHANNEL_LCD3; + case 1: + return OMAP_DSS_CHANNEL_LCD2; + case 0: + default: + return OMAP_DSS_CHANNEL_LCD; + } + case OMAPDSS_VER_OMAP4430_ES1: case OMAPDSS_VER_OMAP4430_ES2: case OMAPDSS_VER_OMAP4: diff --git a/drivers/video/fbdev/omap2/dss/dsi.c b/drivers/video/fbdev/omap2/dss/dsi.c index 3e44c580b1f8..5081f6fb1737 100644 --- a/drivers/video/fbdev/omap2/dss/dsi.c +++ b/drivers/video/fbdev/omap2/dss/dsi.c @@ -5238,6 +5238,7 @@ static int dsi_init_pll_data(struct platform_device *dsidev) } pll->name = dsi->module_id == 0 ? "dsi0" : "dsi1"; + pll->id = dsi->module_id == 0 ? DSS_PLL_DSI1 : DSS_PLL_DSI2; pll->clkin = clk; pll->base = dsi->pll_base; diff --git a/drivers/video/fbdev/omap2/dss/dss.c b/drivers/video/fbdev/omap2/dss/dss.c index 9987154d50b4..a6d10d4279f3 100644 --- a/drivers/video/fbdev/omap2/dss/dss.c +++ b/drivers/video/fbdev/omap2/dss/dss.c @@ -34,7 +34,10 @@ #include <linux/pm_runtime.h> #include <linux/gfp.h> #include <linux/sizes.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h> #include <linux/of.h> +#include <linux/regulator/consumer.h> #include <video/omapdss.h> @@ -63,14 +66,11 @@ struct dss_reg { #define REG_FLD_MOD(idx, val, start, end) \ dss_write_reg(idx, FLD_MOD(dss_read_reg(idx), val, start, end)) -static int dss_runtime_get(void); -static void dss_runtime_put(void); - struct dss_features { u8 fck_div_max; u8 dss_fck_multiplier; const char *parent_clk_name; - enum omap_display_type *ports; + const enum omap_display_type *ports; int num_ports; int (*dpi_select_source)(int port, enum omap_channel channel); }; @@ -78,6 +78,8 @@ struct dss_features { static struct { struct platform_device *pdev; void __iomem *base; + struct regmap *syscon_pll_ctrl; + u32 syscon_pll_ctrl_offset; struct clk *parent_clk; struct clk *dss_clk; @@ -95,6 +97,9 @@ static struct { u32 ctx[DSS_SZ_REGS / sizeof(u32)]; const struct dss_features *feat; + + struct dss_pll *video1_pll; + struct dss_pll *video2_pll; } dss; static const char * const dss_generic_clk_source_names[] = { @@ -158,6 +163,99 @@ static void dss_restore_context(void) #undef SR #undef RR +void dss_ctrl_pll_enable(enum dss_pll_id pll_id, bool enable) +{ + unsigned shift; + unsigned val; + + if (!dss.syscon_pll_ctrl) + return; + + val = !enable; + + switch (pll_id) { + case DSS_PLL_VIDEO1: + shift = 0; + break; + case DSS_PLL_VIDEO2: + shift = 1; + break; + case DSS_PLL_HDMI: + shift = 2; + break; + default: + DSSERR("illegal DSS PLL ID %d\n", pll_id); + return; + } + + regmap_update_bits(dss.syscon_pll_ctrl, dss.syscon_pll_ctrl_offset, + 1 << shift, val << shift); +} + +void dss_ctrl_pll_set_control_mux(enum dss_pll_id pll_id, + enum omap_channel channel) +{ + unsigned shift, val; + + if (!dss.syscon_pll_ctrl) + return; + + switch (channel) { + case OMAP_DSS_CHANNEL_LCD: + shift = 3; + + switch (pll_id) { + case DSS_PLL_VIDEO1: + val = 0; break; + case DSS_PLL_HDMI: + val = 1; break; + default: + DSSERR("error in PLL mux config for LCD\n"); + return; + } + + break; + case OMAP_DSS_CHANNEL_LCD2: + shift = 5; + + switch (pll_id) { + case DSS_PLL_VIDEO1: + val = 0; break; + case DSS_PLL_VIDEO2: + val = 1; break; + case DSS_PLL_HDMI: + val = 2; break; + default: + DSSERR("error in PLL mux config for LCD2\n"); + return; + } + + break; + case OMAP_DSS_CHANNEL_LCD3: + shift = 7; + + switch (pll_id) { + case DSS_PLL_VIDEO1: + val = 1; break; + case DSS_PLL_VIDEO2: + val = 0; break; + case DSS_PLL_HDMI: + val = 2; break; + default: + DSSERR("error in PLL mux config for LCD3\n"); + return; + } + + break; + default: + DSSERR("error in PLL mux config\n"); + return; + } + + regmap_update_bits(dss.syscon_pll_ctrl, dss.syscon_pll_ctrl_offset, + 0x3 << shift, val << shift); +} + void dss_sdi_init(int datapairs) { u32 l; @@ -605,6 +703,26 @@ static int dss_dpi_select_source_omap5(int port, enum omap_channel channel) return 0; } +static int dss_dpi_select_source_dra7xx(int port, enum omap_channel channel) +{ + switch (port) { + case 0: + return dss_dpi_select_source_omap5(port, channel); + case 1: + if (channel != OMAP_DSS_CHANNEL_LCD2) + return -EINVAL; + break; + case 2: + if (channel != OMAP_DSS_CHANNEL_LCD3) + return -EINVAL; + break; + default: + return -EINVAL; + } + + return 0; +} + int dss_dpi_select_source(int port, enum omap_channel channel) { return dss.feat->dpi_select_source(port, channel); @@ -643,7 +761,7 @@ static void dss_put_clocks(void) clk_put(dss.parent_clk); } -static int dss_runtime_get(void) +int dss_runtime_get(void) { int r; @@ -654,7 +772,7 @@ static int dss_runtime_get(void) return r < 0 ? r : 0; } -static void dss_runtime_put(void) +void dss_runtime_put(void) { int r; @@ -677,15 +795,21 @@ void dss_debug_dump_clocks(struct seq_file *s) #endif -static enum omap_display_type omap2plus_ports[] = { +static const enum omap_display_type omap2plus_ports[] = { OMAP_DISPLAY_TYPE_DPI, }; -static enum omap_display_type omap34xx_ports[] = { +static const enum omap_display_type omap34xx_ports[] = { OMAP_DISPLAY_TYPE_DPI, OMAP_DISPLAY_TYPE_SDI, }; +static const enum omap_display_type dra7xx_ports[] = { + OMAP_DISPLAY_TYPE_DPI, + OMAP_DISPLAY_TYPE_DPI, + OMAP_DISPLAY_TYPE_DPI, +}; + static const struct dss_features omap24xx_dss_feats __initconst = { /* * fck div max is really 16, but the divider range has gaps. The range @@ -744,6 +868,15 @@ static const struct dss_features am43xx_dss_feats __initconst = { .num_ports = ARRAY_SIZE(omap2plus_ports), }; +static const struct dss_features dra7xx_dss_feats __initconst = { + .fck_div_max = 64, + .dss_fck_multiplier = 1, + .parent_clk_name = "dpll_per_x2_ck", + .dpi_select_source = &dss_dpi_select_source_dra7xx, + .ports = dra7xx_ports, + .num_ports = ARRAY_SIZE(dra7xx_ports), +}; + static int __init dss_init_features(struct platform_device *pdev) { const struct dss_features *src; @@ -784,6 +917,10 @@ static int __init dss_init_features(struct platform_device *pdev) src = &am43xx_dss_feats; break; + case OMAPDSS_VER_DRA7xx: + src = &dra7xx_dss_feats; + break; + default: return -ENODEV; } @@ -884,8 +1021,10 @@ static void __exit dss_uninit_ports(struct platform_device *pdev) static int __init omap_dsshw_probe(struct platform_device *pdev) { struct resource *dss_mem; + struct device_node *np = pdev->dev.of_node; u32 rev; int r; + struct regulator *pll_regulator; dss.pdev = pdev; @@ -940,6 +1079,57 @@ static int __init omap_dsshw_probe(struct platform_device *pdev) dss_init_ports(pdev); + if (np && of_property_read_bool(np, "syscon-pll-ctrl")) { + dss.syscon_pll_ctrl = syscon_regmap_lookup_by_phandle(np, + "syscon-pll-ctrl"); + if (IS_ERR(dss.syscon_pll_ctrl)) { + dev_err(&pdev->dev, + "failed to get syscon-pll-ctrl regmap\n"); + return PTR_ERR(dss.syscon_pll_ctrl); + } + + if (of_property_read_u32_index(np, "syscon-pll-ctrl", 1, + &dss.syscon_pll_ctrl_offset)) { + dev_err(&pdev->dev, + "failed to get syscon-pll-ctrl offset\n"); + return -EINVAL; + } + } + + pll_regulator = devm_regulator_get(&pdev->dev, "vdda_video"); + if (IS_ERR(pll_regulator)) { + r = PTR_ERR(pll_regulator); + + switch (r) { + case -ENOENT: + pll_regulator = NULL; + break; + + case -EPROBE_DEFER: + return -EPROBE_DEFER; + + default: + DSSERR("can't get DPLL VDDA regulator\n"); + return r; + } + } + + if (of_property_match_string(np, "reg-names", "pll1") >= 0) { + dss.video1_pll = dss_video_pll_init(pdev, 0, pll_regulator); + if (IS_ERR(dss.video1_pll)) { + r = PTR_ERR(dss.video1_pll); + goto err_pll_init; + } + } + + if (of_property_match_string(np, "reg-names", "pll2") >= 0) { + dss.video2_pll = dss_video_pll_init(pdev, 1, pll_regulator); + if (IS_ERR(dss.video2_pll)) { + r = PTR_ERR(dss.video2_pll); + goto err_pll_init; + } + } + rev = dss_read_reg(DSS_REVISION); printk(KERN_INFO "OMAP DSS rev %d.%d\n", FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); @@ -950,6 +1140,12 @@ static int __init omap_dsshw_probe(struct platform_device *pdev) return 0; +err_pll_init: + if (dss.video1_pll) + dss_video_pll_uninit(dss.video1_pll); + + if (dss.video2_pll) + dss_video_pll_uninit(dss.video2_pll); err_runtime_get: pm_runtime_disable(&pdev->dev); err_setup_clocks: @@ -959,6 +1155,12 @@ err_setup_clocks: static int __exit omap_dsshw_remove(struct platform_device *pdev) { + if (dss.video1_pll) + dss_video_pll_uninit(dss.video1_pll); + + if (dss.video2_pll) + dss_video_pll_uninit(dss.video2_pll); + dss_uninit_ports(pdev); pm_runtime_disable(&pdev->dev); @@ -1003,6 +1205,7 @@ static const struct of_device_id dss_of_match[] = { { .compatible = "ti,omap3-dss", }, { .compatible = "ti,omap4-dss", }, { .compatible = "ti,omap5-dss", }, + { .compatible = "ti,dra7-dss", }, {}, }; diff --git a/drivers/video/fbdev/omap2/dss/dss.h b/drivers/video/fbdev/omap2/dss/dss.h index 14fb0c23f4a2..4812eee2622a 100644 --- a/drivers/video/fbdev/omap2/dss/dss.h +++ b/drivers/video/fbdev/omap2/dss/dss.h @@ -100,6 +100,14 @@ enum dss_writeback_channel { DSS_WB_LCD3_MGR = 7, }; +enum dss_pll_id { + DSS_PLL_DSI1, + DSS_PLL_DSI2, + DSS_PLL_HDMI, + DSS_PLL_VIDEO1, + DSS_PLL_VIDEO2, +}; + struct dss_pll; #define DSS_PLL_MAX_HSDIVS 4 @@ -150,6 +158,7 @@ struct dss_pll_hw { struct dss_pll { const char *name; + enum dss_pll_id id; struct clk *clkin; struct regulator *regulator; @@ -250,6 +259,9 @@ void dss_overlay_kobj_uninit(struct omap_overlay *ovl); int dss_init_platform_driver(void) __init; void dss_uninit_platform_driver(void); +int dss_runtime_get(void); +void dss_runtime_put(void); + unsigned long dss_get_dispc_clk_rate(void); int dss_dpi_select_source(int port, enum omap_channel channel); void dss_select_hdmi_venc_clk_source(enum dss_hdmi_venc_clk_source_select); @@ -257,6 +269,11 @@ enum dss_hdmi_venc_clk_source_select dss_get_hdmi_venc_clk_source(void); const char *dss_get_generic_clk_source_name(enum omap_dss_clk_source clk_src); void dss_dump_clocks(struct seq_file *s); +/* DSS VIDEO PLL */ +struct dss_pll *dss_video_pll_init(struct platform_device *pdev, int id, + struct regulator *regulator); +void dss_video_pll_uninit(struct dss_pll *pll); + /* dss-of */ struct device_node *dss_of_port_get_parent_device(struct device_node *port); u32 dss_of_port_get_port_number(struct device_node *port); @@ -265,6 +282,10 @@ u32 dss_of_port_get_port_number(struct device_node *port); void dss_debug_dump_clocks(struct seq_file *s); #endif +void dss_ctrl_pll_enable(enum dss_pll_id pll_id, bool enable); +void dss_ctrl_pll_set_control_mux(enum dss_pll_id pll_id, + enum omap_channel channel); + void dss_sdi_init(int datapairs); int dss_sdi_enable(void); void dss_sdi_disable(void); @@ -446,5 +467,6 @@ int dss_pll_write_config_type_a(struct dss_pll *pll, const struct dss_pll_clock_info *cinfo); int dss_pll_write_config_type_b(struct dss_pll *pll, const struct dss_pll_clock_info *cinfo); +int dss_pll_wait_reset_done(struct dss_pll *pll); #endif diff --git a/drivers/video/fbdev/omap2/dss/dss_features.c b/drivers/video/fbdev/omap2/dss/dss_features.c index 0e3da809473c..376270b777f8 100644 --- a/drivers/video/fbdev/omap2/dss/dss_features.c +++ b/drivers/video/fbdev/omap2/dss/dss_features.c @@ -223,7 +223,7 @@ static const enum omap_dss_output_id omap5_dss_supported_outputs[] = { OMAP_DSS_OUTPUT_DSI1 | OMAP_DSS_OUTPUT_DSI2, /* OMAP_DSS_CHANNEL_DIGIT */ - OMAP_DSS_OUTPUT_HDMI | OMAP_DSS_OUTPUT_DPI, + OMAP_DSS_OUTPUT_HDMI, /* OMAP_DSS_CHANNEL_LCD2 */ OMAP_DSS_OUTPUT_DPI | OMAP_DSS_OUTPUT_DBI | @@ -943,6 +943,7 @@ void dss_features_init(enum omapdss_version version) break; case OMAPDSS_VER_OMAP5: + case OMAPDSS_VER_DRA7xx: omap_current_dss_features = &omap5_dss_features; break; diff --git a/drivers/video/fbdev/omap2/dss/hdmi5.c b/drivers/video/fbdev/omap2/dss/hdmi5.c index 39aae3aa7136..3f0b34a7031a 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi5.c +++ b/drivers/video/fbdev/omap2/dss/hdmi5.c @@ -787,6 +787,7 @@ static const struct dev_pm_ops hdmi_pm_ops = { static const struct of_device_id hdmi_of_match[] = { { .compatible = "ti,omap5-hdmi", }, + { .compatible = "ti,dra7-hdmi", }, {}, }; diff --git a/drivers/video/fbdev/omap2/dss/hdmi_phy.c b/drivers/video/fbdev/omap2/dss/hdmi_phy.c index bc9e07d2afbe..1f5d19c119ce 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi_phy.c +++ b/drivers/video/fbdev/omap2/dss/hdmi_phy.c @@ -208,6 +208,7 @@ static int hdmi_phy_init_features(struct platform_device *pdev) break; case OMAPDSS_VER_OMAP5: + case OMAPDSS_VER_DRA7xx: src = &omap54xx_phy_feats; break; diff --git a/drivers/video/fbdev/omap2/dss/hdmi_pll.c b/drivers/video/fbdev/omap2/dss/hdmi_pll.c index ac83ef5cfd7d..06e23a7c432c 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi_pll.c +++ b/drivers/video/fbdev/omap2/dss/hdmi_pll.c @@ -104,6 +104,8 @@ static int hdmi_pll_enable(struct dss_pll *dsspll) struct hdmi_wp_data *wp = pll->wp; u16 r = 0; + dss_ctrl_pll_enable(DSS_PLL_HDMI, true); + r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS); if (r) return r; @@ -117,6 +119,8 @@ static void hdmi_pll_disable(struct dss_pll *dsspll) struct hdmi_wp_data *wp = pll->wp; hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); + + dss_ctrl_pll_enable(DSS_PLL_HDMI, false); } static const struct dss_pll_ops dsi_pll_ops = { @@ -185,6 +189,7 @@ static int dsi_init_pll_data(struct platform_device *pdev, struct hdmi_pll_data } pll->name = "hdmi"; + pll->id = DSS_PLL_HDMI; pll->base = hpll->base; pll->clkin = clk; @@ -196,6 +201,7 @@ static int dsi_init_pll_data(struct platform_device *pdev, struct hdmi_pll_data break; case OMAPDSS_VER_OMAP5: + case OMAPDSS_VER_DRA7xx: pll->hw = &dss_omap5_hdmi_pll_hw; break; diff --git a/drivers/video/fbdev/omap2/dss/omapdss-boot-init.c b/drivers/video/fbdev/omap2/dss/omapdss-boot-init.c index 2f0822ee3ff9..42b87f95267c 100644 --- a/drivers/video/fbdev/omap2/dss/omapdss-boot-init.c +++ b/drivers/video/fbdev/omap2/dss/omapdss-boot-init.c @@ -186,6 +186,7 @@ static const struct of_device_id omapdss_of_match[] __initconst = { { .compatible = "ti,omap3-dss", }, { .compatible = "ti,omap4-dss", }, { .compatible = "ti,omap5-dss", }, + { .compatible = "ti,dra7-dss", }, {}, }; diff --git a/drivers/video/fbdev/omap2/dss/pll.c b/drivers/video/fbdev/omap2/dss/pll.c index 335ffac224b9..f974ddcd3b6e 100644 --- a/drivers/video/fbdev/omap2/dss/pll.c +++ b/drivers/video/fbdev/omap2/dss/pll.c @@ -222,6 +222,16 @@ static int wait_for_bit_change(void __iomem *reg, int bitnum, int value) return !value; } +int dss_pll_wait_reset_done(struct dss_pll *pll) +{ + void __iomem *base = pll->base; + + if (wait_for_bit_change(base + PLL_STATUS, 0, 1) != 1) + return -ETIMEDOUT; + else + return 0; +} + static int dss_wait_hsdiv_ack(struct dss_pll *pll, u32 hsdiv_ack_mask) { int t = 100; diff --git a/drivers/video/fbdev/omap2/dss/video-pll.c b/drivers/video/fbdev/omap2/dss/video-pll.c new file mode 100644 index 000000000000..b1ec59e42940 --- /dev/null +++ b/drivers/video/fbdev/omap2/dss/video-pll.c @@ -0,0 +1,211 @@ +/* +* Copyright (C) 2014 Texas Instruments Ltd +* +* 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. +* +* You should have received a copy of the GNU General Public License along with +* this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/sched.h> + +#include <video/omapdss.h> + +#include "dss.h" +#include "dss_features.h" + +struct dss_video_pll { + struct dss_pll pll; + + struct device *dev; + + void __iomem *clkctrl_base; +}; + +#define REG_MOD(reg, val, start, end) \ + writel_relaxed(FLD_MOD(readl_relaxed(reg), val, start, end), reg) + +static void dss_dpll_enable_scp_clk(struct dss_video_pll *vpll) +{ + REG_MOD(vpll->clkctrl_base, 1, 14, 14); /* CIO_CLK_ICG */ +} + +static void dss_dpll_disable_scp_clk(struct dss_video_pll *vpll) +{ + REG_MOD(vpll->clkctrl_base, 0, 14, 14); /* CIO_CLK_ICG */ +} + +static void dss_dpll_power_enable(struct dss_video_pll *vpll) +{ + REG_MOD(vpll->clkctrl_base, 2, 31, 30); /* PLL_POWER_ON_ALL */ + + /* + * DRA7x PLL CTRL's PLL_PWR_STATUS seems to always return 0, + * so we have to use fixed delay here. + */ + msleep(1); +} + +static void dss_dpll_power_disable(struct dss_video_pll *vpll) +{ + REG_MOD(vpll->clkctrl_base, 0, 31, 30); /* PLL_POWER_OFF */ +} + +static int dss_video_pll_enable(struct dss_pll *pll) +{ + struct dss_video_pll *vpll = container_of(pll, struct dss_video_pll, pll); + int r; + + r = dss_runtime_get(); + if (r) + return r; + + dss_ctrl_pll_enable(pll->id, true); + + dss_dpll_enable_scp_clk(vpll); + + r = dss_pll_wait_reset_done(pll); + if (r) + goto err_reset; + + dss_dpll_power_enable(vpll); + + return 0; + +err_reset: + dss_dpll_disable_scp_clk(vpll); + dss_ctrl_pll_enable(pll->id, false); + dss_runtime_put(); + + return r; +} + +static void dss_video_pll_disable(struct dss_pll *pll) +{ + struct dss_video_pll *vpll = container_of(pll, struct dss_video_pll, pll); + + dss_dpll_power_disable(vpll); + + dss_dpll_disable_scp_clk(vpll); + + dss_ctrl_pll_enable(pll->id, false); + + dss_runtime_put(); +} + +static const struct dss_pll_ops dss_pll_ops = { + .enable = dss_video_pll_enable, + .disable = dss_video_pll_disable, + .set_config = dss_pll_write_config_type_a, +}; + +static const struct dss_pll_hw dss_dra7_video_pll_hw = { + .n_max = (1 << 8) - 1, + .m_max = (1 << 12) - 1, + .mX_max = (1 << 5) - 1, + .fint_min = 500000, + .fint_max = 2500000, + .clkdco_max = 1800000000, + + .n_msb = 8, + .n_lsb = 1, + .m_msb = 20, + .m_lsb = 9, + + .mX_msb[0] = 25, + .mX_lsb[0] = 21, + .mX_msb[1] = 30, + .mX_lsb[1] = 26, + + .has_refsel = true, +}; + +struct dss_pll *dss_video_pll_init(struct platform_device *pdev, int id, + struct regulator *regulator) +{ + const char * const reg_name[] = { "pll1", "pll2" }; + const char * const clkctrl_name[] = { "pll1_clkctrl", "pll2_clkctrl" }; + const char * const clkin_name[] = { "video1_clk", "video2_clk" }; + + struct resource *res; + struct dss_video_pll *vpll; + void __iomem *pll_base, *clkctrl_base; + struct clk *clk; + struct dss_pll *pll; + int r; + + /* PLL CONTROL */ + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, reg_name[id]); + if (!res) { + dev_err(&pdev->dev, + "missing platform resource data for pll%d\n", id); + return ERR_PTR(-ENODEV); + } + + pll_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pll_base)) { + dev_err(&pdev->dev, "failed to ioremap pll%d reg_name\n", id); + return ERR_CAST(pll_base); + } + + /* CLOCK CONTROL */ + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + clkctrl_name[id]); + if (!res) { + dev_err(&pdev->dev, + "missing platform resource data for pll%d\n", id); + return ERR_PTR(-ENODEV); + } + + clkctrl_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(clkctrl_base)) { + dev_err(&pdev->dev, "failed to ioremap pll%d clkctrl\n", id); + return ERR_CAST(clkctrl_base); + } + + /* CLKIN */ + + clk = devm_clk_get(&pdev->dev, clkin_name[id]); + if (IS_ERR(clk)) { + DSSERR("can't get video pll clkin\n"); + return ERR_CAST(clk); + } + + vpll = devm_kzalloc(&pdev->dev, sizeof(*vpll), GFP_KERNEL); + if (!vpll) + return ERR_PTR(-ENOMEM); + + vpll->dev = &pdev->dev; + vpll->clkctrl_base = clkctrl_base; + + pll = &vpll->pll; + + pll->name = id == 0 ? "video0" : "video1"; + pll->id = id == 0 ? DSS_PLL_VIDEO1 : DSS_PLL_VIDEO2; + pll->clkin = clk; + pll->regulator = regulator; + pll->base = pll_base; + pll->hw = &dss_dra7_video_pll_hw; + pll->ops = &dss_pll_ops; + + r = dss_pll_register(pll); + if (r) + return ERR_PTR(r); + + return pll; +} + +void dss_video_pll_uninit(struct dss_pll *pll) +{ + dss_pll_unregister(pll); +} diff --git a/drivers/video/fbdev/omap2/omapfb/omapfb-ioctl.c b/drivers/video/fbdev/omap2/omapfb/omapfb-ioctl.c index 146b6f5428db..9ddfdd63b84c 100644 --- a/drivers/video/fbdev/omap2/omapfb/omapfb-ioctl.c +++ b/drivers/video/fbdev/omap2/omapfb/omapfb-ioctl.c @@ -137,8 +137,11 @@ static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) goto undo; } - if (ovl->manager) - ovl->manager->apply(ovl->manager); + if (ovl->manager) { + r = ovl->manager->apply(ovl->manager); + if (r) + goto undo; + } if (pi->enabled) { r = ovl->enable(ovl); diff --git a/drivers/video/fbdev/savage/savagefb.h b/drivers/video/fbdev/savage/savagefb.h index dcaab9012ca2..8ff4ab1cb69b 100644 --- a/drivers/video/fbdev/savage/savagefb.h +++ b/drivers/video/fbdev/savage/savagefb.h @@ -351,32 +351,26 @@ static inline void VGAwSEQ(u8 index, u8 val, struct savagefb_par *par) static inline void VGAenablePalette(struct savagefb_par *par) { - u8 tmp; - - tmp = vga_in8(0x3da, par); + vga_in8(0x3da, par); vga_out8(0x3c0, 0x00, par); par->paletteEnabled = 1; } static inline void VGAdisablePalette(struct savagefb_par *par) { - u8 tmp; - - tmp = vga_in8(0x3da, par); + vga_in8(0x3da, par); vga_out8(0x3c0, 0x20, par); par->paletteEnabled = 0; } static inline void VGAwATTR(u8 index, u8 value, struct savagefb_par *par) { - u8 tmp; - if (par->paletteEnabled) index &= ~0x20; else index |= 0x20; - tmp = vga_in8(0x3da, par); + vga_in8(0x3da, par); vga_out8(0x3c0, index, par); vga_out8 (0x3c0, value, par); } diff --git a/drivers/video/fbdev/simplefb.c b/drivers/video/fbdev/simplefb.c index 92cac803dee3..1085c0432158 100644 --- a/drivers/video/fbdev/simplefb.c +++ b/drivers/video/fbdev/simplefb.c @@ -402,7 +402,7 @@ static int __init simplefb_init(void) if (ret) return ret; - if (IS_ENABLED(CONFIG_OF) && of_chosen) { + if (IS_ENABLED(CONFIG_OF_ADDRESS) && of_chosen) { for_each_child_of_node(of_chosen, np) { if (of_device_is_compatible(np, "simple-framebuffer")) of_platform_device_create(np, NULL, NULL); diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index f4daa59f0a80..f7ed6d9016f7 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -122,23 +122,6 @@ static inline int ssd1307fb_write_cmd(struct i2c_client *client, u8 cmd) return ret; } -static inline int ssd1307fb_write_data(struct i2c_client *client, u8 data) -{ - struct ssd1307fb_array *array; - int ret; - - array = ssd1307fb_alloc_array(1, SSD1307FB_DATA); - if (!array) - return -ENOMEM; - - array->data[0] = data; - - ret = ssd1307fb_write_array(client, array, 1); - kfree(array); - - return ret; -} - static void ssd1307fb_update_display(struct ssd1307fb_par *par) { struct ssd1307fb_array *array; @@ -320,7 +303,10 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par) /* Set initial contrast */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CONTRAST); - ret = ret & ssd1307fb_write_cmd(par->client, 0x7f); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, 0x7f); if (ret < 0) return ret; @@ -336,63 +322,99 @@ static int ssd1307fb_ssd1306_init(struct ssd1307fb_par *par) /* Set multiplex ratio value */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_MULTIPLEX_RATIO); - ret = ret & ssd1307fb_write_cmd(par->client, par->height - 1); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, par->height - 1); if (ret < 0) return ret; /* set display offset value */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_DISPLAY_OFFSET); + if (ret < 0) + return ret; + ret = ssd1307fb_write_cmd(par->client, 0x20); if (ret < 0) return ret; /* Set clock frequency */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_CLOCK_FREQ); - ret = ret & ssd1307fb_write_cmd(par->client, 0xf0); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, 0xf0); if (ret < 0) return ret; /* Set precharge period in number of ticks from the internal clock */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PRECHARGE_PERIOD); - ret = ret & ssd1307fb_write_cmd(par->client, 0x22); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, 0x22); if (ret < 0) return ret; /* Set COM pins configuration */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COM_PINS_CONFIG); - ret = ret & ssd1307fb_write_cmd(par->client, 0x22); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, 0x22); if (ret < 0) return ret; /* Set VCOMH */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_VCOMH); - ret = ret & ssd1307fb_write_cmd(par->client, 0x49); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, 0x49); if (ret < 0) return ret; /* Turn on the DC-DC Charge Pump */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_CHARGE_PUMP); - ret = ret & ssd1307fb_write_cmd(par->client, 0x14); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, 0x14); if (ret < 0) return ret; /* Switch to horizontal addressing mode */ ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_ADDRESS_MODE); - ret = ret & ssd1307fb_write_cmd(par->client, - SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, + SSD1307FB_SET_ADDRESS_MODE_HORIZONTAL); if (ret < 0) return ret; ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_COL_RANGE); - ret = ret & ssd1307fb_write_cmd(par->client, 0x0); - ret = ret & ssd1307fb_write_cmd(par->client, par->width - 1); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, 0x0); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, par->width - 1); if (ret < 0) return ret; ret = ssd1307fb_write_cmd(par->client, SSD1307FB_SET_PAGE_RANGE); - ret = ret & ssd1307fb_write_cmd(par->client, 0x0); - ret = ret & ssd1307fb_write_cmd(par->client, - par->page_offset + (par->height / 8) - 1); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, 0x0); + if (ret < 0) + return ret; + + ret = ssd1307fb_write_cmd(par->client, + par->page_offset + (par->height / 8) - 1); if (ret < 0) return ret; @@ -460,7 +482,7 @@ static int ssd1307fb_probe(struct i2c_client *client, par->width = 96; if (of_property_read_u32(node, "solomon,height", &par->height)) - par->width = 16; + par->height = 16; if (of_property_read_u32(node, "solomon,page-offset", &par->page_offset)) par->page_offset = 1; diff --git a/drivers/video/fbdev/vt8500lcdfb.c b/drivers/video/fbdev/vt8500lcdfb.c index ffaf29eeaaba..1a1176bf0906 100644 --- a/drivers/video/fbdev/vt8500lcdfb.c +++ b/drivers/video/fbdev/vt8500lcdfb.c @@ -113,10 +113,8 @@ static int vt8500lcd_set_par(struct fb_info *info) } for (i = 0; i < 8; i++) { - if (bpp_values[i] == info->var.bits_per_pixel) { + if (bpp_values[i] == info->var.bits_per_pixel) reg_bpp = i; - continue; - } } control0 = readl(fbi->regbase) & ~0xf; diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c index 9e758a8f890d..162689227a23 100644 --- a/drivers/video/hdmi.c +++ b/drivers/video/hdmi.c @@ -27,10 +27,12 @@ #include <linux/export.h> #include <linux/hdmi.h> #include <linux/string.h> +#include <linux/device.h> -static void hdmi_infoframe_checksum(void *buffer, size_t size) +#define hdmi_log(fmt, ...) dev_printk(level, dev, fmt, ##__VA_ARGS__) + +static u8 hdmi_infoframe_checksum(u8 *ptr, size_t size) { - u8 *ptr = buffer; u8 csum = 0; size_t i; @@ -38,7 +40,14 @@ static void hdmi_infoframe_checksum(void *buffer, size_t size) for (i = 0; i < size; i++) csum += ptr[i]; - ptr[3] = 256 - csum; + return 256 - csum; +} + +static void hdmi_infoframe_set_checksum(void *buffer, size_t size) +{ + u8 *ptr = buffer; + + ptr[3] = hdmi_infoframe_checksum(buffer, size); } /** @@ -136,7 +145,7 @@ ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer, ptr[11] = frame->right_bar & 0xff; ptr[12] = (frame->right_bar >> 8) & 0xff; - hdmi_infoframe_checksum(buffer, length); + hdmi_infoframe_set_checksum(buffer, length); return length; } @@ -206,7 +215,7 @@ ssize_t hdmi_spd_infoframe_pack(struct hdmi_spd_infoframe *frame, void *buffer, ptr[24] = frame->sdi; - hdmi_infoframe_checksum(buffer, length); + hdmi_infoframe_set_checksum(buffer, length); return length; } @@ -281,7 +290,7 @@ ssize_t hdmi_audio_infoframe_pack(struct hdmi_audio_infoframe *frame, if (frame->downmix_inhibit) ptr[4] |= BIT(7); - hdmi_infoframe_checksum(buffer, length); + hdmi_infoframe_set_checksum(buffer, length); return length; } @@ -373,7 +382,7 @@ ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame, ptr[9] = (frame->s3d_ext_data & 0xf) << 4; } - hdmi_infoframe_checksum(buffer, length); + hdmi_infoframe_set_checksum(buffer, length); return length; } @@ -434,3 +443,802 @@ hdmi_infoframe_pack(union hdmi_infoframe *frame, void *buffer, size_t size) return length; } EXPORT_SYMBOL(hdmi_infoframe_pack); + +static const char *hdmi_infoframe_type_get_name(enum hdmi_infoframe_type type) +{ + if (type < 0x80 || type > 0x9f) + return "Invalid"; + switch (type) { + case HDMI_INFOFRAME_TYPE_VENDOR: + return "Vendor"; + case HDMI_INFOFRAME_TYPE_AVI: + return "Auxiliary Video Information (AVI)"; + case HDMI_INFOFRAME_TYPE_SPD: + return "Source Product Description (SPD)"; + case HDMI_INFOFRAME_TYPE_AUDIO: + return "Audio"; + } + return "Reserved"; +} + +static void hdmi_infoframe_log_header(const char *level, + struct device *dev, + struct hdmi_any_infoframe *frame) +{ + hdmi_log("HDMI infoframe: %s, version %u, length %u\n", + hdmi_infoframe_type_get_name(frame->type), + frame->version, frame->length); +} + +static const char *hdmi_colorspace_get_name(enum hdmi_colorspace colorspace) +{ + switch (colorspace) { + case HDMI_COLORSPACE_RGB: + return "RGB"; + case HDMI_COLORSPACE_YUV422: + return "YCbCr 4:2:2"; + case HDMI_COLORSPACE_YUV444: + return "YCbCr 4:4:4"; + case HDMI_COLORSPACE_YUV420: + return "YCbCr 4:2:0"; + case HDMI_COLORSPACE_RESERVED4: + return "Reserved (4)"; + case HDMI_COLORSPACE_RESERVED5: + return "Reserved (5)"; + case HDMI_COLORSPACE_RESERVED6: + return "Reserved (6)"; + case HDMI_COLORSPACE_IDO_DEFINED: + return "IDO Defined"; + } + return "Invalid"; +} + +static const char *hdmi_scan_mode_get_name(enum hdmi_scan_mode scan_mode) +{ + switch (scan_mode) { + case HDMI_SCAN_MODE_NONE: + return "No Data"; + case HDMI_SCAN_MODE_OVERSCAN: + return "Overscan"; + case HDMI_SCAN_MODE_UNDERSCAN: + return "Underscan"; + case HDMI_SCAN_MODE_RESERVED: + return "Reserved"; + } + return "Invalid"; +} + +static const char *hdmi_colorimetry_get_name(enum hdmi_colorimetry colorimetry) +{ + switch (colorimetry) { + case HDMI_COLORIMETRY_NONE: + return "No Data"; + case HDMI_COLORIMETRY_ITU_601: + return "ITU601"; + case HDMI_COLORIMETRY_ITU_709: + return "ITU709"; + case HDMI_COLORIMETRY_EXTENDED: + return "Extended"; + } + return "Invalid"; +} + +static const char * +hdmi_picture_aspect_get_name(enum hdmi_picture_aspect picture_aspect) +{ + switch (picture_aspect) { + case HDMI_PICTURE_ASPECT_NONE: + return "No Data"; + case HDMI_PICTURE_ASPECT_4_3: + return "4:3"; + case HDMI_PICTURE_ASPECT_16_9: + return "16:9"; + case HDMI_PICTURE_ASPECT_RESERVED: + return "Reserved"; + } + return "Invalid"; +} + +static const char * +hdmi_active_aspect_get_name(enum hdmi_active_aspect active_aspect) +{ + if (active_aspect < 0 || active_aspect > 0xf) + return "Invalid"; + + switch (active_aspect) { + case HDMI_ACTIVE_ASPECT_16_9_TOP: + return "16:9 Top"; + case HDMI_ACTIVE_ASPECT_14_9_TOP: + return "14:9 Top"; + case HDMI_ACTIVE_ASPECT_16_9_CENTER: + return "16:9 Center"; + case HDMI_ACTIVE_ASPECT_PICTURE: + return "Same as Picture"; + case HDMI_ACTIVE_ASPECT_4_3: + return "4:3"; + case HDMI_ACTIVE_ASPECT_16_9: + return "16:9"; + case HDMI_ACTIVE_ASPECT_14_9: + return "14:9"; + case HDMI_ACTIVE_ASPECT_4_3_SP_14_9: + return "4:3 SP 14:9"; + case HDMI_ACTIVE_ASPECT_16_9_SP_14_9: + return "16:9 SP 14:9"; + case HDMI_ACTIVE_ASPECT_16_9_SP_4_3: + return "16:9 SP 4:3"; + } + return "Reserved"; +} + +static const char * +hdmi_extended_colorimetry_get_name(enum hdmi_extended_colorimetry ext_col) +{ + switch (ext_col) { + case HDMI_EXTENDED_COLORIMETRY_XV_YCC_601: + return "xvYCC 601"; + case HDMI_EXTENDED_COLORIMETRY_XV_YCC_709: + return "xvYCC 709"; + case HDMI_EXTENDED_COLORIMETRY_S_YCC_601: + return "sYCC 601"; + case HDMI_EXTENDED_COLORIMETRY_ADOBE_YCC_601: + return "Adobe YCC 601"; + case HDMI_EXTENDED_COLORIMETRY_ADOBE_RGB: + return "Adobe RGB"; + case HDMI_EXTENDED_COLORIMETRY_BT2020_CONST_LUM: + return "BT.2020 Constant Luminance"; + case HDMI_EXTENDED_COLORIMETRY_BT2020: + return "BT.2020"; + case HDMI_EXTENDED_COLORIMETRY_RESERVED: + return "Reserved"; + } + return "Invalid"; +} + +static const char * +hdmi_quantization_range_get_name(enum hdmi_quantization_range qrange) +{ + switch (qrange) { + case HDMI_QUANTIZATION_RANGE_DEFAULT: + return "Default"; + case HDMI_QUANTIZATION_RANGE_LIMITED: + return "Limited"; + case HDMI_QUANTIZATION_RANGE_FULL: + return "Full"; + case HDMI_QUANTIZATION_RANGE_RESERVED: + return "Reserved"; + } + return "Invalid"; +} + +static const char *hdmi_nups_get_name(enum hdmi_nups nups) +{ + switch (nups) { + case HDMI_NUPS_UNKNOWN: + return "Unknown Non-uniform Scaling"; + case HDMI_NUPS_HORIZONTAL: + return "Horizontally Scaled"; + case HDMI_NUPS_VERTICAL: + return "Vertically Scaled"; + case HDMI_NUPS_BOTH: + return "Horizontally and Vertically Scaled"; + } + return "Invalid"; +} + +static const char * +hdmi_ycc_quantization_range_get_name(enum hdmi_ycc_quantization_range qrange) +{ + switch (qrange) { + case HDMI_YCC_QUANTIZATION_RANGE_LIMITED: + return "Limited"; + case HDMI_YCC_QUANTIZATION_RANGE_FULL: + return "Full"; + } + return "Invalid"; +} + +static const char * +hdmi_content_type_get_name(enum hdmi_content_type content_type) +{ + switch (content_type) { + case HDMI_CONTENT_TYPE_GRAPHICS: + return "Graphics"; + case HDMI_CONTENT_TYPE_PHOTO: + return "Photo"; + case HDMI_CONTENT_TYPE_CINEMA: + return "Cinema"; + case HDMI_CONTENT_TYPE_GAME: + return "Game"; + } + return "Invalid"; +} + +/** + * hdmi_avi_infoframe_log() - log info of HDMI AVI infoframe + * @level: logging level + * @dev: device + * @frame: HDMI AVI infoframe + */ +static void hdmi_avi_infoframe_log(const char *level, + struct device *dev, + struct hdmi_avi_infoframe *frame) +{ + hdmi_infoframe_log_header(level, dev, + (struct hdmi_any_infoframe *)frame); + + hdmi_log(" colorspace: %s\n", + hdmi_colorspace_get_name(frame->colorspace)); + hdmi_log(" scan mode: %s\n", + hdmi_scan_mode_get_name(frame->scan_mode)); + hdmi_log(" colorimetry: %s\n", + hdmi_colorimetry_get_name(frame->colorimetry)); + hdmi_log(" picture aspect: %s\n", + hdmi_picture_aspect_get_name(frame->picture_aspect)); + hdmi_log(" active aspect: %s\n", + hdmi_active_aspect_get_name(frame->active_aspect)); + hdmi_log(" itc: %s\n", frame->itc ? "IT Content" : "No Data"); + hdmi_log(" extended colorimetry: %s\n", + hdmi_extended_colorimetry_get_name(frame->extended_colorimetry)); + hdmi_log(" quantization range: %s\n", + hdmi_quantization_range_get_name(frame->quantization_range)); + hdmi_log(" nups: %s\n", hdmi_nups_get_name(frame->nups)); + hdmi_log(" video code: %u\n", frame->video_code); + hdmi_log(" ycc quantization range: %s\n", + hdmi_ycc_quantization_range_get_name(frame->ycc_quantization_range)); + hdmi_log(" hdmi content type: %s\n", + hdmi_content_type_get_name(frame->content_type)); + hdmi_log(" pixel repeat: %u\n", frame->pixel_repeat); + hdmi_log(" bar top %u, bottom %u, left %u, right %u\n", + frame->top_bar, frame->bottom_bar, + frame->left_bar, frame->right_bar); +} + +static const char *hdmi_spd_sdi_get_name(enum hdmi_spd_sdi sdi) +{ + if (sdi < 0 || sdi > 0xff) + return "Invalid"; + switch (sdi) { + case HDMI_SPD_SDI_UNKNOWN: + return "Unknown"; + case HDMI_SPD_SDI_DSTB: + return "Digital STB"; + case HDMI_SPD_SDI_DVDP: + return "DVD Player"; + case HDMI_SPD_SDI_DVHS: + return "D-VHS"; + case HDMI_SPD_SDI_HDDVR: + return "HDD Videorecorder"; + case HDMI_SPD_SDI_DVC: + return "DVC"; + case HDMI_SPD_SDI_DSC: + return "DSC"; + case HDMI_SPD_SDI_VCD: + return "Video CD"; + case HDMI_SPD_SDI_GAME: + return "Game"; + case HDMI_SPD_SDI_PC: + return "PC General"; + case HDMI_SPD_SDI_BD: + return "Blu-Ray Disc (BD)"; + case HDMI_SPD_SDI_SACD: + return "Super Audio CD"; + case HDMI_SPD_SDI_HDDVD: + return "HD DVD"; + case HDMI_SPD_SDI_PMP: + return "PMP"; + } + return "Reserved"; +} + +/** + * hdmi_spd_infoframe_log() - log info of HDMI SPD infoframe + * @level: logging level + * @dev: device + * @frame: HDMI SPD infoframe + */ +static void hdmi_spd_infoframe_log(const char *level, + struct device *dev, + struct hdmi_spd_infoframe *frame) +{ + u8 buf[17]; + + hdmi_infoframe_log_header(level, dev, + (struct hdmi_any_infoframe *)frame); + + memset(buf, 0, sizeof(buf)); + + strncpy(buf, frame->vendor, 8); + hdmi_log(" vendor: %s\n", buf); + strncpy(buf, frame->product, 16); + hdmi_log(" product: %s\n", buf); + hdmi_log(" source device information: %s (0x%x)\n", + hdmi_spd_sdi_get_name(frame->sdi), frame->sdi); +} + +static const char * +hdmi_audio_coding_type_get_name(enum hdmi_audio_coding_type coding_type) +{ + switch (coding_type) { + case HDMI_AUDIO_CODING_TYPE_STREAM: + return "Refer to Stream Header"; + case HDMI_AUDIO_CODING_TYPE_PCM: + return "PCM"; + case HDMI_AUDIO_CODING_TYPE_AC3: + return "AC-3"; + case HDMI_AUDIO_CODING_TYPE_MPEG1: + return "MPEG1"; + case HDMI_AUDIO_CODING_TYPE_MP3: + return "MP3"; + case HDMI_AUDIO_CODING_TYPE_MPEG2: + return "MPEG2"; + case HDMI_AUDIO_CODING_TYPE_AAC_LC: + return "AAC"; + case HDMI_AUDIO_CODING_TYPE_DTS: + return "DTS"; + case HDMI_AUDIO_CODING_TYPE_ATRAC: + return "ATRAC"; + case HDMI_AUDIO_CODING_TYPE_DSD: + return "One Bit Audio"; + case HDMI_AUDIO_CODING_TYPE_EAC3: + return "Dolby Digital +"; + case HDMI_AUDIO_CODING_TYPE_DTS_HD: + return "DTS-HD"; + case HDMI_AUDIO_CODING_TYPE_MLP: + return "MAT (MLP)"; + case HDMI_AUDIO_CODING_TYPE_DST: + return "DST"; + case HDMI_AUDIO_CODING_TYPE_WMA_PRO: + return "WMA PRO"; + case HDMI_AUDIO_CODING_TYPE_CXT: + return "Refer to CXT"; + } + return "Invalid"; +} + +static const char * +hdmi_audio_sample_size_get_name(enum hdmi_audio_sample_size sample_size) +{ + switch (sample_size) { + case HDMI_AUDIO_SAMPLE_SIZE_STREAM: + return "Refer to Stream Header"; + case HDMI_AUDIO_SAMPLE_SIZE_16: + return "16 bit"; + case HDMI_AUDIO_SAMPLE_SIZE_20: + return "20 bit"; + case HDMI_AUDIO_SAMPLE_SIZE_24: + return "24 bit"; + } + return "Invalid"; +} + +static const char * +hdmi_audio_sample_frequency_get_name(enum hdmi_audio_sample_frequency freq) +{ + switch (freq) { + case HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM: + return "Refer to Stream Header"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_32000: + return "32 kHz"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_44100: + return "44.1 kHz (CD)"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_48000: + return "48 kHz"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_88200: + return "88.2 kHz"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_96000: + return "96 kHz"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_176400: + return "176.4 kHz"; + case HDMI_AUDIO_SAMPLE_FREQUENCY_192000: + return "192 kHz"; + } + return "Invalid"; +} + +static const char * +hdmi_audio_coding_type_ext_get_name(enum hdmi_audio_coding_type_ext ctx) +{ + if (ctx < 0 || ctx > 0x1f) + return "Invalid"; + + switch (ctx) { + case HDMI_AUDIO_CODING_TYPE_EXT_CT: + return "Refer to CT"; + case HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC: + return "HE AAC"; + case HDMI_AUDIO_CODING_TYPE_EXT_HE_AAC_V2: + return "HE AAC v2"; + case HDMI_AUDIO_CODING_TYPE_EXT_MPEG_SURROUND: + return "MPEG SURROUND"; + case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC: + return "MPEG-4 HE AAC"; + case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC_V2: + return "MPEG-4 HE AAC v2"; + case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_AAC_LC: + return "MPEG-4 AAC LC"; + case HDMI_AUDIO_CODING_TYPE_EXT_DRA: + return "DRA"; + case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_HE_AAC_SURROUND: + return "MPEG-4 HE AAC + MPEG Surround"; + case HDMI_AUDIO_CODING_TYPE_EXT_MPEG4_AAC_LC_SURROUND: + return "MPEG-4 AAC LC + MPEG Surround"; + } + return "Reserved"; +} + +/** + * hdmi_audio_infoframe_log() - log info of HDMI AUDIO infoframe + * @level: logging level + * @dev: device + * @frame: HDMI AUDIO infoframe + */ +static void hdmi_audio_infoframe_log(const char *level, + struct device *dev, + struct hdmi_audio_infoframe *frame) +{ + hdmi_infoframe_log_header(level, dev, + (struct hdmi_any_infoframe *)frame); + + if (frame->channels) + hdmi_log(" channels: %u\n", frame->channels - 1); + else + hdmi_log(" channels: Refer to stream header\n"); + hdmi_log(" coding type: %s\n", + hdmi_audio_coding_type_get_name(frame->coding_type)); + hdmi_log(" sample size: %s\n", + hdmi_audio_sample_size_get_name(frame->sample_size)); + hdmi_log(" sample frequency: %s\n", + hdmi_audio_sample_frequency_get_name(frame->sample_frequency)); + hdmi_log(" coding type ext: %s\n", + hdmi_audio_coding_type_ext_get_name(frame->coding_type_ext)); + hdmi_log(" channel allocation: 0x%x\n", + frame->channel_allocation); + hdmi_log(" level shift value: %u dB\n", + frame->level_shift_value); + hdmi_log(" downmix inhibit: %s\n", + frame->downmix_inhibit ? "Yes" : "No"); +} + +static const char * +hdmi_3d_structure_get_name(enum hdmi_3d_structure s3d_struct) +{ + if (s3d_struct < 0 || s3d_struct > 0xf) + return "Invalid"; + + switch (s3d_struct) { + case HDMI_3D_STRUCTURE_FRAME_PACKING: + return "Frame Packing"; + case HDMI_3D_STRUCTURE_FIELD_ALTERNATIVE: + return "Field Alternative"; + case HDMI_3D_STRUCTURE_LINE_ALTERNATIVE: + return "Line Alternative"; + case HDMI_3D_STRUCTURE_SIDE_BY_SIDE_FULL: + return "Side-by-side (Full)"; + case HDMI_3D_STRUCTURE_L_DEPTH: + return "L + Depth"; + case HDMI_3D_STRUCTURE_L_DEPTH_GFX_GFX_DEPTH: + return "L + Depth + Graphics + Graphics-depth"; + case HDMI_3D_STRUCTURE_TOP_AND_BOTTOM: + return "Top-and-Bottom"; + case HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF: + return "Side-by-side (Half)"; + default: + break; + } + return "Reserved"; +} + +/** + * hdmi_vendor_infoframe_log() - log info of HDMI VENDOR infoframe + * @level: logging level + * @dev: device + * @frame: HDMI VENDOR infoframe + */ +static void +hdmi_vendor_any_infoframe_log(const char *level, + struct device *dev, + union hdmi_vendor_any_infoframe *frame) +{ + struct hdmi_vendor_infoframe *hvf = &frame->hdmi; + + hdmi_infoframe_log_header(level, dev, + (struct hdmi_any_infoframe *)frame); + + if (frame->any.oui != HDMI_IEEE_OUI) { + hdmi_log(" not a HDMI vendor infoframe\n"); + return; + } + if (hvf->vic == 0 && hvf->s3d_struct == HDMI_3D_STRUCTURE_INVALID) { + hdmi_log(" empty frame\n"); + return; + } + + if (hvf->vic) + hdmi_log(" HDMI VIC: %u\n", hvf->vic); + if (hvf->s3d_struct != HDMI_3D_STRUCTURE_INVALID) { + hdmi_log(" 3D structure: %s\n", + hdmi_3d_structure_get_name(hvf->s3d_struct)); + if (hvf->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) + hdmi_log(" 3D extension data: %d\n", + hvf->s3d_ext_data); + } +} + +/** + * hdmi_infoframe_log() - log info of HDMI infoframe + * @level: logging level + * @dev: device + * @frame: HDMI infoframe + */ +void hdmi_infoframe_log(const char *level, + struct device *dev, + union hdmi_infoframe *frame) +{ + switch (frame->any.type) { + case HDMI_INFOFRAME_TYPE_AVI: + hdmi_avi_infoframe_log(level, dev, &frame->avi); + break; + case HDMI_INFOFRAME_TYPE_SPD: + hdmi_spd_infoframe_log(level, dev, &frame->spd); + break; + case HDMI_INFOFRAME_TYPE_AUDIO: + hdmi_audio_infoframe_log(level, dev, &frame->audio); + break; + case HDMI_INFOFRAME_TYPE_VENDOR: + hdmi_vendor_any_infoframe_log(level, dev, &frame->vendor); + break; + } +} +EXPORT_SYMBOL(hdmi_infoframe_log); + +/** + * hdmi_avi_infoframe_unpack() - unpack binary buffer to a HDMI AVI infoframe + * @buffer: source buffer + * @frame: HDMI AVI infoframe + * + * Unpacks the information contained in binary @buffer into a structured + * @frame of the HDMI Auxiliary Video (AVI) information frame. + * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns 0 on success or a negative error code on failure. + */ +static int hdmi_avi_infoframe_unpack(struct hdmi_avi_infoframe *frame, + void *buffer) +{ + u8 *ptr = buffer; + int ret; + + if (ptr[0] != HDMI_INFOFRAME_TYPE_AVI || + ptr[1] != 2 || + ptr[2] != HDMI_AVI_INFOFRAME_SIZE) + return -EINVAL; + + if (hdmi_infoframe_checksum(buffer, HDMI_INFOFRAME_SIZE(AVI)) != 0) + return -EINVAL; + + ret = hdmi_avi_infoframe_init(frame); + if (ret) + return ret; + + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + frame->colorspace = (ptr[0] >> 5) & 0x3; + if (ptr[0] & 0x10) + frame->active_aspect = ptr[1] & 0xf; + if (ptr[0] & 0x8) { + frame->top_bar = (ptr[5] << 8) + ptr[6]; + frame->bottom_bar = (ptr[7] << 8) + ptr[8]; + } + if (ptr[0] & 0x4) { + frame->left_bar = (ptr[9] << 8) + ptr[10]; + frame->right_bar = (ptr[11] << 8) + ptr[12]; + } + frame->scan_mode = ptr[0] & 0x3; + + frame->colorimetry = (ptr[1] >> 6) & 0x3; + frame->picture_aspect = (ptr[1] >> 4) & 0x3; + frame->active_aspect = ptr[1] & 0xf; + + frame->itc = ptr[2] & 0x80 ? true : false; + frame->extended_colorimetry = (ptr[2] >> 4) & 0x7; + frame->quantization_range = (ptr[2] >> 2) & 0x3; + frame->nups = ptr[2] & 0x3; + + frame->video_code = ptr[3] & 0x7f; + frame->ycc_quantization_range = (ptr[4] >> 6) & 0x3; + frame->content_type = (ptr[4] >> 4) & 0x3; + + frame->pixel_repeat = ptr[4] & 0xf; + + return 0; +} + +/** + * hdmi_spd_infoframe_unpack() - unpack binary buffer to a HDMI SPD infoframe + * @buffer: source buffer + * @frame: HDMI SPD infoframe + * + * Unpacks the information contained in binary @buffer into a structured + * @frame of the HDMI Source Product Description (SPD) information frame. + * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns 0 on success or a negative error code on failure. + */ +static int hdmi_spd_infoframe_unpack(struct hdmi_spd_infoframe *frame, + void *buffer) +{ + u8 *ptr = buffer; + int ret; + + if (ptr[0] != HDMI_INFOFRAME_TYPE_SPD || + ptr[1] != 1 || + ptr[2] != HDMI_SPD_INFOFRAME_SIZE) { + return -EINVAL; + } + + if (hdmi_infoframe_checksum(buffer, HDMI_INFOFRAME_SIZE(SPD)) != 0) + return -EINVAL; + + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + ret = hdmi_spd_infoframe_init(frame, ptr, ptr + 8); + if (ret) + return ret; + + frame->sdi = ptr[24]; + + return 0; +} + +/** + * hdmi_audio_infoframe_unpack() - unpack binary buffer to a HDMI AUDIO infoframe + * @buffer: source buffer + * @frame: HDMI Audio infoframe + * + * Unpacks the information contained in binary @buffer into a structured + * @frame of the HDMI Audio information frame. + * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns 0 on success or a negative error code on failure. + */ +static int hdmi_audio_infoframe_unpack(struct hdmi_audio_infoframe *frame, + void *buffer) +{ + u8 *ptr = buffer; + int ret; + + if (ptr[0] != HDMI_INFOFRAME_TYPE_AUDIO || + ptr[1] != 1 || + ptr[2] != HDMI_AUDIO_INFOFRAME_SIZE) { + return -EINVAL; + } + + if (hdmi_infoframe_checksum(buffer, HDMI_INFOFRAME_SIZE(AUDIO)) != 0) + return -EINVAL; + + ret = hdmi_audio_infoframe_init(frame); + if (ret) + return ret; + + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + frame->channels = ptr[0] & 0x7; + frame->coding_type = (ptr[0] >> 4) & 0xf; + frame->sample_size = ptr[1] & 0x3; + frame->sample_frequency = (ptr[1] >> 2) & 0x7; + frame->coding_type_ext = ptr[2] & 0x1f; + frame->channel_allocation = ptr[3]; + frame->level_shift_value = (ptr[4] >> 3) & 0xf; + frame->downmix_inhibit = ptr[4] & 0x80 ? true : false; + + return 0; +} + +/** + * hdmi_vendor_infoframe_unpack() - unpack binary buffer to a HDMI vendor infoframe + * @buffer: source buffer + * @frame: HDMI Vendor infoframe + * + * Unpacks the information contained in binary @buffer into a structured + * @frame of the HDMI Vendor information frame. + * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns 0 on success or a negative error code on failure. + */ +static int +hdmi_vendor_any_infoframe_unpack(union hdmi_vendor_any_infoframe *frame, + void *buffer) +{ + u8 *ptr = buffer; + size_t length; + int ret; + u8 hdmi_video_format; + struct hdmi_vendor_infoframe *hvf = &frame->hdmi; + + if (ptr[0] != HDMI_INFOFRAME_TYPE_VENDOR || + ptr[1] != 1 || + (ptr[2] != 5 && ptr[2] != 6)) + return -EINVAL; + + length = ptr[2]; + + if (hdmi_infoframe_checksum(buffer, + HDMI_INFOFRAME_HEADER_SIZE + length) != 0) + return -EINVAL; + + ptr += HDMI_INFOFRAME_HEADER_SIZE; + + /* HDMI OUI */ + if ((ptr[0] != 0x03) || + (ptr[1] != 0x0c) || + (ptr[2] != 0x00)) + return -EINVAL; + + hdmi_video_format = ptr[3] >> 5; + + if (hdmi_video_format > 0x2) + return -EINVAL; + + ret = hdmi_vendor_infoframe_init(hvf); + if (ret) + return ret; + + hvf->length = length; + + if (hdmi_video_format == 0x1) { + hvf->vic = ptr[4]; + } else if (hdmi_video_format == 0x2) { + hvf->s3d_struct = ptr[4] >> 4; + if (hvf->s3d_struct >= HDMI_3D_STRUCTURE_SIDE_BY_SIDE_HALF) { + if (length == 6) + hvf->s3d_ext_data = ptr[5] >> 4; + else + return -EINVAL; + } + } + + return 0; +} + +/** + * hdmi_infoframe_unpack() - unpack binary buffer to a HDMI infoframe + * @buffer: source buffer + * @frame: HDMI infoframe + * + * Unpacks the information contained in binary buffer @buffer into a structured + * @frame of a HDMI infoframe. + * Also verifies the checksum as required by section 5.3.5 of the HDMI 1.4 + * specification. + * + * Returns 0 on success or a negative error code on failure. + */ +int hdmi_infoframe_unpack(union hdmi_infoframe *frame, void *buffer) +{ + int ret; + u8 *ptr = buffer; + + switch (ptr[0]) { + case HDMI_INFOFRAME_TYPE_AVI: + ret = hdmi_avi_infoframe_unpack(&frame->avi, buffer); + break; + case HDMI_INFOFRAME_TYPE_SPD: + ret = hdmi_spd_infoframe_unpack(&frame->spd, buffer); + break; + case HDMI_INFOFRAME_TYPE_AUDIO: + ret = hdmi_audio_infoframe_unpack(&frame->audio, buffer); + break; + case HDMI_INFOFRAME_TYPE_VENDOR: + ret = hdmi_vendor_any_infoframe_unpack(&frame->vendor, buffer); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} +EXPORT_SYMBOL(hdmi_infoframe_unpack); diff --git a/drivers/video/vgastate.c b/drivers/video/vgastate.c index b91c466225b9..548c751d2415 100644 --- a/drivers/video/vgastate.c +++ b/drivers/video/vgastate.c @@ -2,7 +2,7 @@ * linux/drivers/video/vgastate.c -- VGA state save/restore * * Copyright 2002 James Simmons - * + * * Copyright history from vga16fb.c: * Copyright 1999 Ben Pfaff and Petr Vandrovec * Based on VGA info at http://www.goodnet.com/~tinara/FreeVGA/home.htm @@ -10,7 +10,7 @@ * * This file is subject to the terms and conditions of the GNU General * Public License. See the file COPYING in the main directory of this - * archive for more details. + * archive for more details. * */ #include <linux/module.h> @@ -29,16 +29,16 @@ struct regstate { __u8 *gfx; __u8 *seq; __u8 misc; -}; +}; -static inline unsigned char vga_rcrtcs(void __iomem *regbase, unsigned short iobase, +static inline unsigned char vga_rcrtcs(void __iomem *regbase, unsigned short iobase, unsigned char reg) { vga_w(regbase, iobase + 0x4, reg); return vga_r(regbase, iobase + 0x5); } -static inline void vga_wcrtcs(void __iomem *regbase, unsigned short iobase, +static inline void vga_wcrtcs(void __iomem *regbase, unsigned short iobase, unsigned char reg, unsigned char val) { vga_w(regbase, iobase + 0x4, reg); @@ -71,7 +71,7 @@ static void save_vga_text(struct vgastate *state, void __iomem *fbbase) gr6 = vga_rgfx(state->vgabase, VGA_GFX_MISC); seq2 = vga_rseq(state->vgabase, VGA_SEQ_PLANE_WRITE); seq4 = vga_rseq(state->vgabase, VGA_SEQ_MEMORY_MODE); - + /* blank screen */ seq1 = vga_rseq(state->vgabase, VGA_SEQ_CLOCK_MODE); vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1); @@ -85,7 +85,7 @@ static void save_vga_text(struct vgastate *state, void __iomem *fbbase) vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x2); vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0); vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5); - for (i = 0; i < 4 * 8192; i++) + for (i = 0; i < 4 * 8192; i++) saved->vga_font0[i] = vga_r(fbbase, i); } @@ -96,10 +96,10 @@ static void save_vga_text(struct vgastate *state, void __iomem *fbbase) vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x3); vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0); vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5); - for (i = 0; i < state->memsize; i++) + for (i = 0; i < state->memsize; i++) saved->vga_font1[i] = vga_r(fbbase, i); } - + /* save font at plane 0/1 */ if (state->flags & VGA_SAVE_TEXT) { vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x1); @@ -107,7 +107,7 @@ static void save_vga_text(struct vgastate *state, void __iomem *fbbase) vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x0); vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0); vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5); - for (i = 0; i < 8192; i++) + for (i = 0; i < 8192; i++) saved->vga_text[i] = vga_r(fbbase, i); vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x2); @@ -115,8 +115,8 @@ static void save_vga_text(struct vgastate *state, void __iomem *fbbase) vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x1); vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0); vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5); - for (i = 0; i < 8192; i++) - saved->vga_text[8192+i] = vga_r(fbbase + 2 * 8192, i); + for (i = 0; i < 8192; i++) + saved->vga_text[8192+i] = vga_r(fbbase + 2 * 8192, i); } /* restore regs */ @@ -151,7 +151,7 @@ static void restore_vga_text(struct vgastate *state, void __iomem *fbbase) gr8 = vga_rgfx(state->vgabase, VGA_GFX_BIT_MASK); seq2 = vga_rseq(state->vgabase, VGA_SEQ_PLANE_WRITE); seq4 = vga_rseq(state->vgabase, VGA_SEQ_MEMORY_MODE); - + /* blank screen */ seq1 = vga_rseq(state->vgabase, VGA_SEQ_CLOCK_MODE); vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x1); @@ -163,7 +163,7 @@ static void restore_vga_text(struct vgastate *state, void __iomem *fbbase) vga_wgfx(state->vgabase, VGA_GFX_BIT_MASK, 0xff); vga_wgfx(state->vgabase, VGA_GFX_SR_ENABLE, 0x00); } - + /* restore font at plane 2 */ if (state->flags & VGA_SAVE_FONT0) { vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x4); @@ -171,7 +171,7 @@ static void restore_vga_text(struct vgastate *state, void __iomem *fbbase) vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x2); vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0); vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5); - for (i = 0; i < 4 * 8192; i++) + for (i = 0; i < 4 * 8192; i++) vga_w(fbbase, i, saved->vga_font0[i]); } @@ -182,10 +182,10 @@ static void restore_vga_text(struct vgastate *state, void __iomem *fbbase) vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x3); vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0); vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5); - for (i = 0; i < state->memsize; i++) + for (i = 0; i < state->memsize; i++) vga_w(fbbase, i, saved->vga_font1[i]); } - + /* restore font at plane 0/1 */ if (state->flags & VGA_SAVE_TEXT) { vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x1); @@ -193,16 +193,16 @@ static void restore_vga_text(struct vgastate *state, void __iomem *fbbase) vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x0); vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0); vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5); - for (i = 0; i < 8192; i++) + for (i = 0; i < 8192; i++) vga_w(fbbase, i, saved->vga_text[i]); - + vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, 0x2); vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, 0x6); vga_wgfx(state->vgabase, VGA_GFX_PLANE_READ, 0x1); vga_wgfx(state->vgabase, VGA_GFX_MODE, 0x0); vga_wgfx(state->vgabase, VGA_GFX_MISC, 0x5); - for (i = 0; i < 8192; i++) - vga_w(fbbase, i, saved->vga_text[8192+i]); + for (i = 0; i < 8192; i++) + vga_w(fbbase, i, saved->vga_text[8192+i]); } /* unblank screen */ @@ -222,7 +222,7 @@ static void restore_vga_text(struct vgastate *state, void __iomem *fbbase) vga_wseq(state->vgabase, VGA_SEQ_PLANE_WRITE, seq2); vga_wseq(state->vgabase, VGA_SEQ_MEMORY_MODE, seq4); } - + static void save_vga_mode(struct vgastate *state) { struct regstate *saved = (struct regstate *) state->vidstate; @@ -235,10 +235,10 @@ static void save_vga_mode(struct vgastate *state) else iobase = 0x3b0; - for (i = 0; i < state->num_crtc; i++) + for (i = 0; i < state->num_crtc; i++) saved->crtc[i] = vga_rcrtcs(state->vgabase, iobase, i); - - vga_r(state->vgabase, iobase + 0xa); + + vga_r(state->vgabase, iobase + 0xa); vga_w(state->vgabase, VGA_ATT_W, 0x00); for (i = 0; i < state->num_attr; i++) { vga_r(state->vgabase, iobase + 0xa); @@ -247,10 +247,10 @@ static void save_vga_mode(struct vgastate *state) vga_r(state->vgabase, iobase + 0xa); vga_w(state->vgabase, VGA_ATT_W, 0x20); - for (i = 0; i < state->num_gfx; i++) + for (i = 0; i < state->num_gfx; i++) saved->gfx[i] = vga_rgfx(state->vgabase, i); - for (i = 0; i < state->num_seq; i++) + for (i = 0; i < state->num_seq; i++) saved->seq[i] = vga_rseq(state->vgabase, i); } @@ -268,26 +268,26 @@ static void restore_vga_mode(struct vgastate *state) iobase = 0x3b0; /* turn off display */ - vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, + vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, saved->seq[VGA_SEQ_CLOCK_MODE] | 0x20); /* disable sequencer */ vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x01); - + /* enable palette addressing */ vga_r(state->vgabase, iobase + 0xa); vga_w(state->vgabase, VGA_ATT_W, 0x00); - for (i = 2; i < state->num_seq; i++) + for (i = 2; i < state->num_seq; i++) vga_wseq(state->vgabase, i, saved->seq[i]); /* unprotect vga regs */ vga_wcrtcs(state->vgabase, iobase, 17, saved->crtc[17] & ~0x80); - for (i = 0; i < state->num_crtc; i++) + for (i = 0; i < state->num_crtc; i++) vga_wcrtcs(state->vgabase, iobase, i, saved->crtc[i]); - - for (i = 0; i < state->num_gfx; i++) + + for (i = 0; i < state->num_gfx; i++) vga_wgfx(state->vgabase, i, saved->gfx[i]); for (i = 0; i < state->num_attr; i++) { @@ -298,7 +298,7 @@ static void restore_vga_mode(struct vgastate *state) /* reenable sequencer */ vga_wseq(state->vgabase, VGA_SEQ_RESET, 0x03); /* turn display on */ - vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, + vga_wseq(state->vgabase, VGA_SEQ_CLOCK_MODE, saved->seq[VGA_SEQ_CLOCK_MODE] & ~(1 << 5)); /* disable video/palette source */ @@ -312,7 +312,7 @@ static void save_vga_cmap(struct vgastate *state) int i; vga_w(state->vgabase, VGA_PEL_MSK, 0xff); - + /* assumes DAC is readable and writable */ vga_w(state->vgabase, VGA_PEL_IR, 0x00); for (i = 0; i < 768; i++) @@ -346,7 +346,7 @@ static void vga_cleanup(struct vgastate *state) state->vidstate = NULL; } } - + int save_vga(struct vgastate *state) { struct regstate *saved; @@ -357,7 +357,7 @@ int save_vga(struct vgastate *state) return 1; state->vidstate = (void *)saved; - + if (state->flags & VGA_SAVE_CMAP) { saved->vga_cmap = vmalloc(768); if (!saved->vga_cmap) { @@ -403,7 +403,7 @@ int save_vga(struct vgastate *state) } if (!state->memsize) state->memsize = 8 * 8192; - + if (!state->membase) state->membase = 0xA0000; @@ -414,7 +414,7 @@ int save_vga(struct vgastate *state) return 1; } - /* + /* * save only first 32K used by vgacon */ if (state->flags & VGA_SAVE_FONT0) { @@ -425,7 +425,7 @@ int save_vga(struct vgastate *state) return 1; } } - /* + /* * largely unused, but if required by the caller * we'll just save everything. */ @@ -448,7 +448,7 @@ int save_vga(struct vgastate *state) return 1; } } - + save_vga_text(state, fbbase); iounmap(fbbase); } diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c index 2ef9529809d8..9756f21b809e 100644 --- a/drivers/virtio/virtio_pci_common.c +++ b/drivers/virtio/virtio_pci_common.c @@ -282,6 +282,7 @@ void vp_del_vqs(struct virtio_device *vdev) vp_free_vectors(vdev); kfree(vp_dev->vqs); + vp_dev->vqs = NULL; } static int vp_try_to_find_vqs(struct virtio_device *vdev, unsigned nvqs, @@ -421,15 +422,6 @@ int vp_set_vq_affinity(struct virtqueue *vq, int cpu) return 0; } -void virtio_pci_release_dev(struct device *_d) -{ - /* - * No need for a release method as we allocate/free - * all devices together with the pci devices. - * Provide an empty one to avoid getting a warning from core. - */ -} - #ifdef CONFIG_PM_SLEEP static int virtio_pci_freeze(struct device *dev) { diff --git a/drivers/virtio/virtio_pci_common.h b/drivers/virtio/virtio_pci_common.h index adddb647b21d..5a497289b7e9 100644 --- a/drivers/virtio/virtio_pci_common.h +++ b/drivers/virtio/virtio_pci_common.h @@ -126,7 +126,6 @@ const char *vp_bus_name(struct virtio_device *vdev); * - ignore the affinity request if we're using INTX */ int vp_set_vq_affinity(struct virtqueue *vq, int cpu); -void virtio_pci_release_dev(struct device *); int virtio_pci_legacy_probe(struct pci_dev *pci_dev, const struct pci_device_id *id); diff --git a/drivers/virtio/virtio_pci_legacy.c b/drivers/virtio/virtio_pci_legacy.c index 6c76f0f5658c..a5486e65e04b 100644 --- a/drivers/virtio/virtio_pci_legacy.c +++ b/drivers/virtio/virtio_pci_legacy.c @@ -211,6 +211,17 @@ static const struct virtio_config_ops virtio_pci_config_ops = { .set_vq_affinity = vp_set_vq_affinity, }; +static void virtio_pci_release_dev(struct device *_d) +{ + struct virtio_device *vdev = dev_to_virtio(_d); + struct virtio_pci_device *vp_dev = to_vp_device(vdev); + + /* As struct device is a kobject, it's not safe to + * free the memory (including the reference counter itself) + * until it's release callback. */ + kfree(vp_dev); +} + /* the PCI probing function */ int virtio_pci_legacy_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) @@ -302,5 +313,4 @@ void virtio_pci_legacy_remove(struct pci_dev *pci_dev) pci_iounmap(pci_dev, vp_dev->ioaddr); pci_release_regions(pci_dev); pci_disable_device(pci_dev); - kfree(vp_dev); } diff --git a/drivers/watchdog/cadence_wdt.c b/drivers/watchdog/cadence_wdt.c index 5927c0a98a74..bcfd2a22208f 100644 --- a/drivers/watchdog/cadence_wdt.c +++ b/drivers/watchdog/cadence_wdt.c @@ -503,7 +503,6 @@ static struct platform_driver cdns_wdt_driver = { .shutdown = cdns_wdt_shutdown, .driver = { .name = "cdns-wdt", - .owner = THIS_MODULE, .of_match_table = cdns_wdt_of_match, .pm = &cdns_wdt_pm_ops, }, diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index d6add516a7a7..5142bbabe027 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -52,6 +52,8 @@ #define IMX2_WDT_WRSR 0x04 /* Reset Status Register */ #define IMX2_WDT_WRSR_TOUT (1 << 1) /* -> Reset due to Timeout */ +#define IMX2_WDT_WMCR 0x08 /* Misc Register */ + #define IMX2_WDT_MAX_TIME 128 #define IMX2_WDT_DEFAULT_TIME 60 /* in seconds */ @@ -274,6 +276,13 @@ static int __init imx2_wdt_probe(struct platform_device *pdev) imx2_wdt_ping_if_active(wdog); + /* + * Disable the watchdog power down counter at boot. Otherwise the power + * down counter will pull down the #WDOG interrupt line for one clock + * cycle. + */ + regmap_write(wdev->regmap, IMX2_WDT_WMCR, 0); + ret = watchdog_register_device(wdog); if (ret) { dev_err(&pdev->dev, "cannot register watchdog device\n"); @@ -327,18 +336,21 @@ static void imx2_wdt_shutdown(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP -/* Disable watchdog if it is active during suspend */ +/* Disable watchdog if it is active or non-active but still running */ static int imx2_wdt_suspend(struct device *dev) { struct watchdog_device *wdog = dev_get_drvdata(dev); struct imx2_wdt_device *wdev = watchdog_get_drvdata(wdog); - imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME); - imx2_wdt_ping(wdog); + /* The watchdog IP block is running */ + if (imx2_wdt_is_running(wdev)) { + imx2_wdt_set_timeout(wdog, IMX2_WDT_MAX_TIME); + imx2_wdt_ping(wdog); - /* Watchdog has been stopped but IP block is still running */ - if (!watchdog_active(wdog) && imx2_wdt_is_running(wdev)) - del_timer_sync(&wdev->timer); + /* The watchdog is not active */ + if (!watchdog_active(wdog)) + del_timer_sync(&wdev->timer); + } clk_disable_unprepare(wdev->clk); @@ -354,15 +366,25 @@ static int imx2_wdt_resume(struct device *dev) clk_prepare_enable(wdev->clk); if (watchdog_active(wdog) && !imx2_wdt_is_running(wdev)) { - /* Resumes from deep sleep we need restart - * the watchdog again. + /* + * If the watchdog is still active and resumes + * from deep sleep state, need to restart the + * watchdog again. */ imx2_wdt_setup(wdog); imx2_wdt_set_timeout(wdog, wdog->timeout); imx2_wdt_ping(wdog); } else if (imx2_wdt_is_running(wdev)) { + /* Resuming from non-deep sleep state. */ + imx2_wdt_set_timeout(wdog, wdog->timeout); imx2_wdt_ping(wdog); - mod_timer(&wdev->timer, jiffies + wdog->timeout * HZ / 2); + /* + * But the watchdog is not active, then start + * the timer again. + */ + if (!watchdog_active(wdog)) + mod_timer(&wdev->timer, + jiffies + wdog->timeout * HZ / 2); } return 0; diff --git a/drivers/watchdog/meson_wdt.c b/drivers/watchdog/meson_wdt.c index ef6a298e8c45..1f4155ee3404 100644 --- a/drivers/watchdog/meson_wdt.c +++ b/drivers/watchdog/meson_wdt.c @@ -215,7 +215,6 @@ static struct platform_driver meson_wdt_driver = { .remove = meson_wdt_remove, .shutdown = meson_wdt_shutdown, .driver = { - .owner = THIS_MODULE, .name = DRV_NAME, .of_match_table = meson_wdt_dt_ids, }, diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index 3860d02729dc..0b52d92cb2e5 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -92,7 +92,6 @@ EXPORT_SYMBOL_GPL(balloon_stats); /* We increase/decrease in batches which fit in a page */ static xen_pfn_t frame_list[PAGE_SIZE / sizeof(unsigned long)]; -static DEFINE_PER_CPU(struct page *, balloon_scratch_page); /* List of ballooned pages, threaded through the mem_map array. */ @@ -423,22 +422,12 @@ static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp) page = pfn_to_page(pfn); #ifdef CONFIG_XEN_HAVE_PVMMU - /* - * Ballooned out frames are effectively replaced with - * a scratch frame. Ensure direct mappings and the - * p2m are consistent. - */ if (!xen_feature(XENFEAT_auto_translated_physmap)) { if (!PageHighMem(page)) { - struct page *scratch_page = get_balloon_scratch_page(); - ret = HYPERVISOR_update_va_mapping( (unsigned long)__va(pfn << PAGE_SHIFT), - pfn_pte(page_to_pfn(scratch_page), - PAGE_KERNEL_RO), 0); + __pte_ma(0), 0); BUG_ON(ret); - - put_balloon_scratch_page(); } __set_phys_to_machine(pfn, INVALID_P2M_ENTRY); } @@ -500,18 +489,6 @@ static void balloon_process(struct work_struct *work) mutex_unlock(&balloon_mutex); } -struct page *get_balloon_scratch_page(void) -{ - struct page *ret = get_cpu_var(balloon_scratch_page); - BUG_ON(ret == NULL); - return ret; -} - -void put_balloon_scratch_page(void) -{ - put_cpu_var(balloon_scratch_page); -} - /* Resets the Xen limit, sets new target, and kicks off processing. */ void balloon_set_new_target(unsigned long target) { @@ -605,61 +582,13 @@ static void __init balloon_add_region(unsigned long start_pfn, } } -static int alloc_balloon_scratch_page(int cpu) -{ - if (per_cpu(balloon_scratch_page, cpu) != NULL) - return 0; - - per_cpu(balloon_scratch_page, cpu) = alloc_page(GFP_KERNEL); - if (per_cpu(balloon_scratch_page, cpu) == NULL) { - pr_warn("Failed to allocate balloon_scratch_page for cpu %d\n", cpu); - return -ENOMEM; - } - - return 0; -} - - -static int balloon_cpu_notify(struct notifier_block *self, - unsigned long action, void *hcpu) -{ - int cpu = (long)hcpu; - switch (action) { - case CPU_UP_PREPARE: - if (alloc_balloon_scratch_page(cpu)) - return NOTIFY_BAD; - break; - default: - break; - } - return NOTIFY_OK; -} - -static struct notifier_block balloon_cpu_notifier = { - .notifier_call = balloon_cpu_notify, -}; - static int __init balloon_init(void) { - int i, cpu; + int i; if (!xen_domain()) return -ENODEV; - if (!xen_feature(XENFEAT_auto_translated_physmap)) { - register_cpu_notifier(&balloon_cpu_notifier); - - get_online_cpus(); - for_each_online_cpu(cpu) { - if (alloc_balloon_scratch_page(cpu)) { - put_online_cpus(); - unregister_cpu_notifier(&balloon_cpu_notifier); - return -ENOMEM; - } - } - put_online_cpus(); - } - pr_info("Initialising balloon driver\n"); balloon_stats.current_pages = xen_pv_domain() @@ -696,15 +625,4 @@ static int __init balloon_init(void) subsys_initcall(balloon_init); -static int __init balloon_clear(void) -{ - int cpu; - - for_each_possible_cpu(cpu) - per_cpu(balloon_scratch_page, cpu) = NULL; - - return 0; -} -early_initcall(balloon_clear); - MODULE_LICENSE("GPL"); diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c index 073b4a19a8b0..d5bb1a33d0a3 100644 --- a/drivers/xen/gntdev.c +++ b/drivers/xen/gntdev.c @@ -67,7 +67,7 @@ struct gntdev_priv { * Only populated if populate_freeable_maps == 1 */ struct list_head freeable_maps; /* lock protects maps and freeable_maps */ - spinlock_t lock; + struct mutex lock; struct mm_struct *mm; struct mmu_notifier mn; }; @@ -91,7 +91,9 @@ struct grant_map { struct gnttab_map_grant_ref *map_ops; struct gnttab_unmap_grant_ref *unmap_ops; struct gnttab_map_grant_ref *kmap_ops; + struct gnttab_unmap_grant_ref *kunmap_ops; struct page **pages; + unsigned long pages_vm_start; }; static int unmap_grant_pages(struct grant_map *map, int offset, int pages); @@ -118,12 +120,13 @@ static void gntdev_free_map(struct grant_map *map) return; if (map->pages) - free_xenballooned_pages(map->count, map->pages); + gnttab_free_pages(map->count, map->pages); kfree(map->pages); kfree(map->grants); kfree(map->map_ops); kfree(map->unmap_ops); kfree(map->kmap_ops); + kfree(map->kunmap_ops); kfree(map); } @@ -140,21 +143,24 @@ static struct grant_map *gntdev_alloc_map(struct gntdev_priv *priv, int count) add->map_ops = kcalloc(count, sizeof(add->map_ops[0]), GFP_KERNEL); add->unmap_ops = kcalloc(count, sizeof(add->unmap_ops[0]), GFP_KERNEL); add->kmap_ops = kcalloc(count, sizeof(add->kmap_ops[0]), GFP_KERNEL); + add->kunmap_ops = kcalloc(count, sizeof(add->kunmap_ops[0]), GFP_KERNEL); add->pages = kcalloc(count, sizeof(add->pages[0]), GFP_KERNEL); if (NULL == add->grants || NULL == add->map_ops || NULL == add->unmap_ops || NULL == add->kmap_ops || + NULL == add->kunmap_ops || NULL == add->pages) goto err; - if (alloc_xenballooned_pages(count, add->pages, false /* lowmem */)) + if (gnttab_alloc_pages(count, add->pages)) goto err; for (i = 0; i < count; i++) { add->map_ops[i].handle = -1; add->unmap_ops[i].handle = -1; add->kmap_ops[i].handle = -1; + add->kunmap_ops[i].handle = -1; } add->index = 0; @@ -216,9 +222,9 @@ static void gntdev_put_map(struct gntdev_priv *priv, struct grant_map *map) } if (populate_freeable_maps && priv) { - spin_lock(&priv->lock); + mutex_lock(&priv->lock); list_del(&map->next); - spin_unlock(&priv->lock); + mutex_unlock(&priv->lock); } if (map->pages && !use_ptemod) @@ -239,6 +245,14 @@ static int find_grant_ptes(pte_t *pte, pgtable_t token, BUG_ON(pgnr >= map->count); pte_maddr = arbitrary_virt_to_machine(pte).maddr; + /* + * Set the PTE as special to force get_user_pages_fast() fall + * back to the slow path. If this is not supported as part of + * the grant map, it will be done afterwards. + */ + if (xen_feature(XENFEAT_gnttab_map_avail_bits)) + flags |= (1 << _GNTMAP_guest_avail0); + gnttab_set_map_op(&map->map_ops[pgnr], pte_maddr, flags, map->grants[pgnr].ref, map->grants[pgnr].domid); @@ -247,6 +261,15 @@ static int find_grant_ptes(pte_t *pte, pgtable_t token, return 0; } +#ifdef CONFIG_X86 +static int set_grant_ptes_as_special(pte_t *pte, pgtable_t token, + unsigned long addr, void *data) +{ + set_pte_at(current->mm, addr, pte, pte_mkspecial(*pte)); + return 0; +} +#endif + static int map_grant_pages(struct grant_map *map) { int i, err = 0; @@ -280,6 +303,8 @@ static int map_grant_pages(struct grant_map *map) map->flags | GNTMAP_host_map, map->grants[i].ref, map->grants[i].domid); + gnttab_set_unmap_op(&map->kunmap_ops[i], address, + map->flags | GNTMAP_host_map, -1); } } @@ -290,20 +315,42 @@ static int map_grant_pages(struct grant_map *map) return err; for (i = 0; i < map->count; i++) { - if (map->map_ops[i].status) + if (map->map_ops[i].status) { err = -EINVAL; - else { - BUG_ON(map->map_ops[i].handle == -1); - map->unmap_ops[i].handle = map->map_ops[i].handle; - pr_debug("map handle=%d\n", map->map_ops[i].handle); + continue; } + + map->unmap_ops[i].handle = map->map_ops[i].handle; + if (use_ptemod) + map->kunmap_ops[i].handle = map->kmap_ops[i].handle; } return err; } +struct unmap_grant_pages_callback_data +{ + struct completion completion; + int result; +}; + +static void unmap_grant_callback(int result, + struct gntab_unmap_queue_data *data) +{ + struct unmap_grant_pages_callback_data* d = data->data; + + d->result = result; + complete(&d->completion); +} + static int __unmap_grant_pages(struct grant_map *map, int offset, int pages) { int i, err = 0; + struct gntab_unmap_queue_data unmap_data; + struct unmap_grant_pages_callback_data data; + + init_completion(&data.completion); + unmap_data.data = &data; + unmap_data.done= &unmap_grant_callback; if (map->notify.flags & UNMAP_NOTIFY_CLEAR_BYTE) { int pgno = (map->notify.addr >> PAGE_SHIFT); @@ -315,11 +362,16 @@ static int __unmap_grant_pages(struct grant_map *map, int offset, int pages) } } - err = gnttab_unmap_refs(map->unmap_ops + offset, - use_ptemod ? map->kmap_ops + offset : NULL, map->pages + offset, - pages); - if (err) - return err; + unmap_data.unmap_ops = map->unmap_ops + offset; + unmap_data.kunmap_ops = use_ptemod ? map->kunmap_ops + offset : NULL; + unmap_data.pages = map->pages + offset; + unmap_data.count = pages; + + gnttab_unmap_refs_async(&unmap_data); + + wait_for_completion(&data.completion); + if (data.result) + return data.result; for (i = 0; i < pages; i++) { if (map->unmap_ops[offset+i].status) @@ -387,17 +439,26 @@ static void gntdev_vma_close(struct vm_area_struct *vma) * not do any unmapping, since that has been done prior to * closing the vma, but it may still iterate the unmap_ops list. */ - spin_lock(&priv->lock); + mutex_lock(&priv->lock); map->vma = NULL; - spin_unlock(&priv->lock); + mutex_unlock(&priv->lock); } vma->vm_private_data = NULL; gntdev_put_map(priv, map); } +static struct page *gntdev_vma_find_special_page(struct vm_area_struct *vma, + unsigned long addr) +{ + struct grant_map *map = vma->vm_private_data; + + return map->pages[(addr - map->pages_vm_start) >> PAGE_SHIFT]; +} + static struct vm_operations_struct gntdev_vmops = { .open = gntdev_vma_open, .close = gntdev_vma_close, + .find_special_page = gntdev_vma_find_special_page, }; /* ------------------------------------------------------------------ */ @@ -433,14 +494,14 @@ static void mn_invl_range_start(struct mmu_notifier *mn, struct gntdev_priv *priv = container_of(mn, struct gntdev_priv, mn); struct grant_map *map; - spin_lock(&priv->lock); + mutex_lock(&priv->lock); list_for_each_entry(map, &priv->maps, next) { unmap_if_in_range(map, start, end); } list_for_each_entry(map, &priv->freeable_maps, next) { unmap_if_in_range(map, start, end); } - spin_unlock(&priv->lock); + mutex_unlock(&priv->lock); } static void mn_invl_page(struct mmu_notifier *mn, @@ -457,7 +518,7 @@ static void mn_release(struct mmu_notifier *mn, struct grant_map *map; int err; - spin_lock(&priv->lock); + mutex_lock(&priv->lock); list_for_each_entry(map, &priv->maps, next) { if (!map->vma) continue; @@ -476,7 +537,7 @@ static void mn_release(struct mmu_notifier *mn, err = unmap_grant_pages(map, /* offset */ 0, map->count); WARN_ON(err); } - spin_unlock(&priv->lock); + mutex_unlock(&priv->lock); } static struct mmu_notifier_ops gntdev_mmu_ops = { @@ -498,7 +559,7 @@ static int gntdev_open(struct inode *inode, struct file *flip) INIT_LIST_HEAD(&priv->maps); INIT_LIST_HEAD(&priv->freeable_maps); - spin_lock_init(&priv->lock); + mutex_init(&priv->lock); if (use_ptemod) { priv->mm = get_task_mm(current); @@ -572,10 +633,10 @@ static long gntdev_ioctl_map_grant_ref(struct gntdev_priv *priv, return -EFAULT; } - spin_lock(&priv->lock); + mutex_lock(&priv->lock); gntdev_add_map(priv, map); op.index = map->index << PAGE_SHIFT; - spin_unlock(&priv->lock); + mutex_unlock(&priv->lock); if (copy_to_user(u, &op, sizeof(op)) != 0) return -EFAULT; @@ -594,7 +655,7 @@ static long gntdev_ioctl_unmap_grant_ref(struct gntdev_priv *priv, return -EFAULT; pr_debug("priv %p, del %d+%d\n", priv, (int)op.index, (int)op.count); - spin_lock(&priv->lock); + mutex_lock(&priv->lock); map = gntdev_find_map_index(priv, op.index >> PAGE_SHIFT, op.count); if (map) { list_del(&map->next); @@ -602,7 +663,7 @@ static long gntdev_ioctl_unmap_grant_ref(struct gntdev_priv *priv, list_add_tail(&map->next, &priv->freeable_maps); err = 0; } - spin_unlock(&priv->lock); + mutex_unlock(&priv->lock); if (map) gntdev_put_map(priv, map); return err; @@ -670,7 +731,7 @@ static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u) out_flags = op.action; out_event = op.event_channel_port; - spin_lock(&priv->lock); + mutex_lock(&priv->lock); list_for_each_entry(map, &priv->maps, next) { uint64_t begin = map->index << PAGE_SHIFT; @@ -698,7 +759,7 @@ static long gntdev_ioctl_notify(struct gntdev_priv *priv, void __user *u) rc = 0; unlock_out: - spin_unlock(&priv->lock); + mutex_unlock(&priv->lock); /* Drop the reference to the event channel we did not save in the map */ if (out_flags & UNMAP_NOTIFY_SEND_EVENT) @@ -748,7 +809,7 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma) pr_debug("map %d+%d at %lx (pgoff %lx)\n", index, count, vma->vm_start, vma->vm_pgoff); - spin_lock(&priv->lock); + mutex_lock(&priv->lock); map = gntdev_find_map_index(priv, index, count); if (!map) goto unlock_out; @@ -783,7 +844,7 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma) map->flags |= GNTMAP_readonly; } - spin_unlock(&priv->lock); + mutex_unlock(&priv->lock); if (use_ptemod) { err = apply_to_page_range(vma->vm_mm, vma->vm_start, @@ -806,16 +867,34 @@ static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma) if (err) goto out_put_map; } + } else { +#ifdef CONFIG_X86 + /* + * If the PTEs were not made special by the grant map + * hypercall, do so here. + * + * This is racy since the mapping is already visible + * to userspace but userspace should be well-behaved + * enough to not touch it until the mmap() call + * returns. + */ + if (!xen_feature(XENFEAT_gnttab_map_avail_bits)) { + apply_to_page_range(vma->vm_mm, vma->vm_start, + vma->vm_end - vma->vm_start, + set_grant_ptes_as_special, NULL); + } +#endif + map->pages_vm_start = vma->vm_start; } return 0; unlock_out: - spin_unlock(&priv->lock); + mutex_unlock(&priv->lock); return err; out_unlock_put: - spin_unlock(&priv->lock); + mutex_unlock(&priv->lock); out_put_map: if (use_ptemod) map->vma = NULL; diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c index 7786291ba229..17972fbacddc 100644 --- a/drivers/xen/grant-table.c +++ b/drivers/xen/grant-table.c @@ -42,6 +42,7 @@ #include <linux/io.h> #include <linux/delay.h> #include <linux/hardirq.h> +#include <linux/workqueue.h> #include <xen/xen.h> #include <xen/interface/xen.h> @@ -50,6 +51,7 @@ #include <xen/interface/memory.h> #include <xen/hvc-console.h> #include <xen/swiotlb-xen.h> +#include <xen/balloon.h> #include <asm/xen/hypercall.h> #include <asm/xen/interface.h> @@ -671,6 +673,59 @@ void gnttab_free_auto_xlat_frames(void) } EXPORT_SYMBOL_GPL(gnttab_free_auto_xlat_frames); +/** + * gnttab_alloc_pages - alloc pages suitable for grant mapping into + * @nr_pages: number of pages to alloc + * @pages: returns the pages + */ +int gnttab_alloc_pages(int nr_pages, struct page **pages) +{ + int i; + int ret; + + ret = alloc_xenballooned_pages(nr_pages, pages, false); + if (ret < 0) + return ret; + + for (i = 0; i < nr_pages; i++) { +#if BITS_PER_LONG < 64 + struct xen_page_foreign *foreign; + + foreign = kzalloc(sizeof(*foreign), GFP_KERNEL); + if (!foreign) { + gnttab_free_pages(nr_pages, pages); + return -ENOMEM; + } + set_page_private(pages[i], (unsigned long)foreign); +#endif + SetPagePrivate(pages[i]); + } + + return 0; +} +EXPORT_SYMBOL(gnttab_alloc_pages); + +/** + * gnttab_free_pages - free pages allocated by gnttab_alloc_pages() + * @nr_pages; number of pages to free + * @pages: the pages + */ +void gnttab_free_pages(int nr_pages, struct page **pages) +{ + int i; + + for (i = 0; i < nr_pages; i++) { + if (PagePrivate(pages[i])) { +#if BITS_PER_LONG < 64 + kfree((void *)page_private(pages[i])); +#endif + ClearPagePrivate(pages[i]); + } + } + free_xenballooned_pages(nr_pages, pages); +} +EXPORT_SYMBOL(gnttab_free_pages); + /* Handling of paged out grant targets (GNTST_eagain) */ #define MAX_DELAY 256 static inline void @@ -727,30 +782,87 @@ int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops, if (ret) return ret; - /* Retry eagain maps */ - for (i = 0; i < count; i++) + for (i = 0; i < count; i++) { + /* Retry eagain maps */ if (map_ops[i].status == GNTST_eagain) gnttab_retry_eagain_gop(GNTTABOP_map_grant_ref, map_ops + i, &map_ops[i].status, __func__); + if (map_ops[i].status == GNTST_okay) { + struct xen_page_foreign *foreign; + + SetPageForeign(pages[i]); + foreign = xen_page_foreign(pages[i]); + foreign->domid = map_ops[i].dom; + foreign->gref = map_ops[i].ref; + } + } + return set_foreign_p2m_mapping(map_ops, kmap_ops, pages, count); } EXPORT_SYMBOL_GPL(gnttab_map_refs); int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops, - struct gnttab_map_grant_ref *kmap_ops, + struct gnttab_unmap_grant_ref *kunmap_ops, struct page **pages, unsigned int count) { + unsigned int i; int ret; ret = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, unmap_ops, count); if (ret) return ret; - return clear_foreign_p2m_mapping(unmap_ops, kmap_ops, pages, count); + for (i = 0; i < count; i++) + ClearPageForeign(pages[i]); + + return clear_foreign_p2m_mapping(unmap_ops, kunmap_ops, pages, count); } EXPORT_SYMBOL_GPL(gnttab_unmap_refs); +#define GNTTAB_UNMAP_REFS_DELAY 5 + +static void __gnttab_unmap_refs_async(struct gntab_unmap_queue_data* item); + +static void gnttab_unmap_work(struct work_struct *work) +{ + struct gntab_unmap_queue_data + *unmap_data = container_of(work, + struct gntab_unmap_queue_data, + gnttab_work.work); + if (unmap_data->age != UINT_MAX) + unmap_data->age++; + __gnttab_unmap_refs_async(unmap_data); +} + +static void __gnttab_unmap_refs_async(struct gntab_unmap_queue_data* item) +{ + int ret; + int pc; + + for (pc = 0; pc < item->count; pc++) { + if (page_count(item->pages[pc]) > 1) { + unsigned long delay = GNTTAB_UNMAP_REFS_DELAY * (item->age + 1); + schedule_delayed_work(&item->gnttab_work, + msecs_to_jiffies(delay)); + return; + } + } + + ret = gnttab_unmap_refs(item->unmap_ops, item->kunmap_ops, + item->pages, item->count); + item->done(ret, item); +} + +void gnttab_unmap_refs_async(struct gntab_unmap_queue_data* item) +{ + INIT_DELAYED_WORK(&item->gnttab_work, gnttab_unmap_work); + item->age = 0; + + __gnttab_unmap_refs_async(item); +} +EXPORT_SYMBOL_GPL(gnttab_unmap_refs_async); + static int gnttab_map_frames_v1(xen_pfn_t *frames, unsigned int nr_gframes) { int rc; diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index f8bb36f9d9ce..bf1940706422 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -105,10 +105,16 @@ static void do_suspend(void) err = freeze_processes(); if (err) { - pr_err("%s: freeze failed %d\n", __func__, err); + pr_err("%s: freeze processes failed %d\n", __func__, err); goto out; } + err = freeze_kernel_threads(); + if (err) { + pr_err("%s: freeze kernel threads failed %d\n", __func__, err); + goto out_thaw; + } + err = dpm_suspend_start(PMSG_FREEZE); if (err) { pr_err("%s: dpm_suspend_start %d\n", __func__, err); diff --git a/drivers/xen/tmem.c b/drivers/xen/tmem.c index 83b5c53bec6b..8a65423bc696 100644 --- a/drivers/xen/tmem.c +++ b/drivers/xen/tmem.c @@ -374,7 +374,7 @@ static struct frontswap_ops tmem_frontswap_ops = { }; #endif -static int xen_tmem_init(void) +static int __init xen_tmem_init(void) { if (!xen_domain()) return 0; diff --git a/drivers/xen/xen-acpi-memhotplug.c b/drivers/xen/xen-acpi-memhotplug.c index 34e40b733f9a..4fc886cd5586 100644 --- a/drivers/xen/xen-acpi-memhotplug.c +++ b/drivers/xen/xen-acpi-memhotplug.c @@ -117,8 +117,8 @@ acpi_memory_get_resource(struct acpi_resource *resource, void *context) list_for_each_entry(info, &mem_device->res_list, list) { if ((info->caching == address64.info.mem.caching) && (info->write_protect == address64.info.mem.write_protect) && - (info->start_addr + info->length == address64.minimum)) { - info->length += address64.address_length; + (info->start_addr + info->length == address64.address.minimum)) { + info->length += address64.address.address_length; return AE_OK; } } @@ -130,8 +130,8 @@ acpi_memory_get_resource(struct acpi_resource *resource, void *context) INIT_LIST_HEAD(&new->list); new->caching = address64.info.mem.caching; new->write_protect = address64.info.mem.write_protect; - new->start_addr = address64.minimum; - new->length = address64.address_length; + new->start_addr = address64.address.minimum; + new->length = address64.address.address_length; list_add_tail(&new->list, &mem_device->res_list); return AE_OK; diff --git a/drivers/xen/xen-scsiback.c b/drivers/xen/xen-scsiback.c index 471ab0661696..61653a03a8f5 100644 --- a/drivers/xen/xen-scsiback.c +++ b/drivers/xen/xen-scsiback.c @@ -228,7 +228,7 @@ static void put_free_pages(struct page **page, int num) return; if (i > scsiback_max_buffer_pages) { n = min(num, i - scsiback_max_buffer_pages); - free_xenballooned_pages(n, page + num - n); + gnttab_free_pages(n, page + num - n); n = num - n; } spin_lock_irqsave(&free_pages_lock, flags); @@ -245,7 +245,7 @@ static int get_free_page(struct page **page) spin_lock_irqsave(&free_pages_lock, flags); if (list_empty(&scsiback_free_pages)) { spin_unlock_irqrestore(&free_pages_lock, flags); - return alloc_xenballooned_pages(1, page, false); + return gnttab_alloc_pages(1, page); } page[0] = list_first_entry(&scsiback_free_pages, struct page, lru); list_del(&page[0]->lru); @@ -2107,7 +2107,7 @@ static void __exit scsiback_exit(void) while (free_pages_num) { if (get_free_page(&page)) BUG(); - free_xenballooned_pages(1, &page); + gnttab_free_pages(1, &page); } scsiback_deregister_configfs(); xenbus_unregister_driver(&scsiback_driver); diff --git a/drivers/xen/xenbus/xenbus_dev_frontend.c b/drivers/xen/xenbus/xenbus_dev_frontend.c index 85534ea63555..9433e46518c8 100644 --- a/drivers/xen/xenbus/xenbus_dev_frontend.c +++ b/drivers/xen/xenbus/xenbus_dev_frontend.c @@ -326,10 +326,13 @@ static int xenbus_write_transaction(unsigned msg_type, } if (msg_type == XS_TRANSACTION_START) { - trans->handle.id = simple_strtoul(reply, NULL, 0); - - list_add(&trans->list, &u->transactions); - } else if (msg_type == XS_TRANSACTION_END) { + if (u->u.msg.type == XS_ERROR) + kfree(trans); + else { + trans->handle.id = simple_strtoul(reply, NULL, 0); + list_add(&trans->list, &u->transactions); + } + } else if (u->u.msg.type == XS_TRANSACTION_END) { list_for_each_entry(trans, &u->transactions, list) if (trans->handle.id == u->u.msg.tx_id) break; |